472 lines
13 KiB
C
472 lines
13 KiB
C
|
/*
|
||
|
* rsaglue.c - The interface between bignum math and RSA operations.
|
||
|
* This layer's primary reason for existence is to allow adaptation
|
||
|
* to other RSA math libraries for legal reasons.
|
||
|
*
|
||
|
* Copyright (c) 1995 Colin Plumb. All rights reserved.
|
||
|
* For licensing and other legal details, see the file legal.c.
|
||
|
*/
|
||
|
|
||
|
#include "first.h"
|
||
|
|
||
|
#include "bn.h"
|
||
|
|
||
|
#include "keys.h"
|
||
|
#include "random.h"
|
||
|
#include "rsaglue.h"
|
||
|
#include "usuals.h"
|
||
|
|
||
|
/*#define BNDEBUG 1*/
|
||
|
|
||
|
#if BNDEBUG
|
||
|
/* Some debugging hooks which have been left in for now. */
|
||
|
#include "bn/bnprint.h"
|
||
|
#define bndPut(prompt, bn) bnPrint(stdout, prompt, bn, "\n")
|
||
|
#define bndPrintf printf
|
||
|
#else
|
||
|
#define bndPut(prompt, bn) ((void)(prompt),(void)(bn))
|
||
|
#define bndPrintf(x) (void)0
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* This returns TRUE if the key is too big, returning the
|
||
|
* maximum number of bits that the library can accept. It
|
||
|
* is used if you want to use something icky from RSADSI, whose
|
||
|
* code is known to have satatic limits on key sizes. (BSAFE 2.1
|
||
|
* advertises 2048-bit key sizes. It lies. It's talking about
|
||
|
* conventional RC4 keys, whicah are useless to make anything like
|
||
|
* that large. RSA keys are limited to 1024 bits.
|
||
|
*/
|
||
|
int
|
||
|
rsaKeyTooBig(struct PubKey const *pub, struct SecKey const *sec)
|
||
|
{
|
||
|
(void)pub;
|
||
|
(void)sec;
|
||
|
return 0; /* Never too big! */
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fill the given bignum, from bytes high-1 through low (where 0 is
|
||
|
* the least significant byte), with non-zero random data.
|
||
|
*/
|
||
|
static int
|
||
|
randomPad(struct BigNum *bn, unsigned high, unsigned low)
|
||
|
{
|
||
|
unsigned i, l;
|
||
|
byte padding[64]; /* This can be any size (>0) whatsoever */
|
||
|
|
||
|
high -= low;
|
||
|
while (high) {
|
||
|
l = high < sizeof(padding) ? high : sizeof(padding);
|
||
|
randBytes(padding, l);
|
||
|
for (i = 0; i < l; i++) { /* Replace all zero bytes */
|
||
|
while(padding[i] == 0)
|
||
|
randBytes(padding+i, 1);
|
||
|
}
|
||
|
high -= l;
|
||
|
if (bnInsertBigBytes(bn, padding, high+low, l) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
}
|
||
|
|
||
|
memset(padding, 0, sizeof(padding));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fill the given bignum, from bytes high-1 through low (where 0 is
|
||
|
* the least significant byte), with all ones (0xFF) data.
|
||
|
*/
|
||
|
static int
|
||
|
onesPad(struct BigNum *bn, unsigned high, unsigned low)
|
||
|
{
|
||
|
unsigned l;
|
||
|
static byte const padding[] = {
|
||
|
255,255,255,255,255,255,255,255,
|
||
|
255,255,255,255,255,255,255,255
|
||
|
};
|
||
|
|
||
|
high -= low;
|
||
|
while (high) {
|
||
|
l = high < sizeof(padding) ? high : sizeof(padding);
|
||
|
high -= l;
|
||
|
if (bnInsertBigBytes(bn, padding, high+low, l) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Wrap a PKCS type 2 wrapper around some data and RSA encrypt it.
|
||
|
* If the modulus is n bytes long, with the most significant byte
|
||
|
* being n-1 and the least significant, 0, the wrapper looks like:
|
||
|
*
|
||
|
* Position Value Function
|
||
|
* n-1 0 This is needed to ensure that the padded number
|
||
|
* is less than the modulus.
|
||
|
* n-2 2 The padding type (non-zero random).
|
||
|
* n-3..len+1 ??? Non-zero random padding bytes to "salt" the
|
||
|
* output and prevent duplicate plaintext attacks.
|
||
|
* len 0 Zero byte to mark the end of the padding
|
||
|
* len-1..0 data Supplied payload data.
|
||
|
*
|
||
|
* There really should be several bytes of padding, although this
|
||
|
* routine will not fail to encrypt unless it will not fit, even
|
||
|
* with no padding bytes.
|
||
|
*/
|
||
|
|
||
|
static byte const encryptedType = 2;
|
||
|
|
||
|
int
|
||
|
rsaPublicEncrypt(struct BigNum *bn, byte const *in, unsigned len,
|
||
|
struct PubKey const *pub)
|
||
|
{
|
||
|
unsigned bytes = (bnBits(&pub->n)+7)/8;
|
||
|
|
||
|
if (len+3 > bytes)
|
||
|
return RSAGLUE_TOOSMALL; /* Won't fit! */
|
||
|
|
||
|
/* Set the entire number to 0 to start */
|
||
|
(void)bnSetQ(bn, 0);
|
||
|
|
||
|
if (bnInsertBigBytes(bn, &encryptedType, bytes-2, 1) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
if (randomPad(bn, bytes-2, len+1) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
|
||
|
if (bnInsertBigBytes(bn, in, 0, len) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
bndPrintf("RSA encrypting.\n");
|
||
|
bndPut("plaintext = ", bn);
|
||
|
return bnExpMod(bn, bn, &pub->e, &pub->n);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This performs a modular exponentiation using the Chinese Remainder
|
||
|
* Algorithm when the modulus is known to have two relatively prime
|
||
|
* factors n = p * q, and u = p^-1 (mod q) has been precomputed.
|
||
|
*
|
||
|
* The chinese remainder algorithm lets a computation mod n be performed
|
||
|
* mod p and mod q, and the results combined. Since it takes
|
||
|
* (considerably) more than twice as long to perform modular exponentiation
|
||
|
* mod n as it does to perform it mod p and mod q, time is saved.
|
||
|
*
|
||
|
* If x is the desired result, let xp and xq be the values of x mod p
|
||
|
* and mod q, respectively. Obviously, x = xp + p * k for some k.
|
||
|
* Taking this mod q, xq == xp + p*k (mod q), so p*k == xq-xp (mod q)
|
||
|
* and k == p^-1 * (xq-xp) (mod q), so k = u * (xq-xp mod q) mod q.
|
||
|
* After that, x = xp + p * k.
|
||
|
*
|
||
|
* Another savings comes from reducing the exponent d modulo phi(p)
|
||
|
* and phi(q). Here, we assume that p and q are prime, so phi(p) = p-1
|
||
|
* and phi(q) = q-1.
|
||
|
*/
|
||
|
static int
|
||
|
bnExpModCRA(struct BigNum *x, struct BigNum const *d,
|
||
|
struct BigNum const *p, struct BigNum const *q, struct BigNum const *u)
|
||
|
{
|
||
|
struct BigNum xp, xq, k;
|
||
|
int i;
|
||
|
|
||
|
bndPrintf("Performing CRA\n");
|
||
|
bndPut("x = ", x);
|
||
|
bndPut("p = ", p);
|
||
|
bndPut("q = ", q);
|
||
|
bndPut("d = ", d);
|
||
|
bndPut("u = ", u);
|
||
|
|
||
|
bnBegin(&xp);
|
||
|
bnBegin(&xq);
|
||
|
bnBegin(&k);
|
||
|
|
||
|
/* Compute xp = (x mod p) ^ (d mod p-1) mod p */
|
||
|
if (bnCopy(&xp, p) < 0) /* First, use xp to hold p-1 */
|
||
|
goto fail;
|
||
|
(void)bnSubQ(&xp, 1); /* p > 1, so subtracting is safe. */
|
||
|
if (bnMod(&k, d, &xp) < 0) /* Use k to hold the exponent */
|
||
|
goto fail;
|
||
|
bndPut("d mod p-1 = ", &k);
|
||
|
if (bnMod(&xp, x, p) < 0) /* Now xp = (x mod p) */
|
||
|
goto fail;
|
||
|
bndPut("x mod p = ", &xp);
|
||
|
if (bnExpMod(&xp, &xp, &k, p) < 0) /* xp = (x mod p)^k mod p */
|
||
|
goto fail;
|
||
|
bndPut("xp = x^d mod p = ", &xp);
|
||
|
|
||
|
/* Compute xq = (x mod q) ^ (d mod q-1) mod q */
|
||
|
if (bnCopy(&xq, q) < 0) /* First, use xq to hold q-1 */
|
||
|
goto fail;
|
||
|
(void)bnSubQ(&xq, 1); /* q > 1, so subtracting is safe. */
|
||
|
if (bnMod(&k, d, &xq) < 0) /* Use k to hold the exponent */
|
||
|
goto fail;
|
||
|
bndPut("d mod q-1 = ", &k);
|
||
|
if (bnMod(&xq, x, q) < 0) /* Now xq = (x mod q) */
|
||
|
goto fail;
|
||
|
bndPut("x mod q = ", &xq);
|
||
|
if (bnExpMod(&xq, &xq, &k, q) < 0) /* xq = (x mod q)^k mod q */
|
||
|
goto fail;
|
||
|
bndPut("xq = x^d mod q = ", &xq);
|
||
|
|
||
|
i = bnSub(&xq, &xp);
|
||
|
bndPut("xq - xp = ", &xq);
|
||
|
bndPrintf(("With sign %d\n", i));
|
||
|
if (i < 0)
|
||
|
goto fail;
|
||
|
if (i) {
|
||
|
/*
|
||
|
* Borrow out - xq-xp is negative, so bnSub returned
|
||
|
* xp-xq instead, the negative of the true answer.
|
||
|
* Add q back (which is subtracting from the negative)
|
||
|
* until the sign flips again. If p is much greater
|
||
|
* than q, this step could take annoyingly long.
|
||
|
* PGP requires that p < q, so it'll only happen once.
|
||
|
* You could get this stuck in a very lengthy loop by
|
||
|
* feeding this function a p >> q, but it seems fair
|
||
|
* to assume that secret keys are not constructed
|
||
|
* maliciously.
|
||
|
*
|
||
|
* If this becomes a concern, you can fix it up with a
|
||
|
* bnMod. (But watch out for the case that the correct
|
||
|
* answer is zero!)
|
||
|
*/
|
||
|
do {
|
||
|
i = bnSub(&xq, q);
|
||
|
bndPut("xq - xp mod q = ", &xq);
|
||
|
if (i < 0)
|
||
|
goto fail;
|
||
|
} while (!i);
|
||
|
}
|
||
|
|
||
|
/* Compute k = xq * u mod q */
|
||
|
if (bnMul(&k, u, &xq) < 0)
|
||
|
goto fail;
|
||
|
bndPut("(xq-xp) * u = ", &k);
|
||
|
if (bnMod(&k, &k, q) < 0)
|
||
|
goto fail;
|
||
|
bndPut("k = (xq-xp)*u % q = ", &k);
|
||
|
|
||
|
#if BNDEBUG /* @@@ DEBUG - do it the slow way for comparison */
|
||
|
if (bnMul(&xq, p, q) < 0)
|
||
|
goto fail;
|
||
|
bndPut("n = p*q = ", &xq);
|
||
|
if (bnExpMod(x, x, d, &xq) < 0)
|
||
|
goto fail;
|
||
|
if (bnCopy(&xq, x) < 0)
|
||
|
goto fail;
|
||
|
bndPut("x^d mod n = ", &xq);
|
||
|
#endif
|
||
|
|
||
|
/* Now x = k * p + xp is the final answer */
|
||
|
if (bnMul(x, &k, p) < 0)
|
||
|
goto fail;
|
||
|
bndPut("k * p = ", x);
|
||
|
if (bnAdd(x, &xp) < 0)
|
||
|
goto fail;
|
||
|
bndPut("k*p + xp = ", x);
|
||
|
#if BNDEBUG
|
||
|
if (bnCmp(x, &xq) != 0) {
|
||
|
bndPrintf(("Nasty!!!\n"));
|
||
|
goto fail;
|
||
|
}
|
||
|
bnSetQ(&k, 17);
|
||
|
bnMul(&xp, p, q);
|
||
|
bnExpMod(&xq, &xq, &k, &xp);
|
||
|
bndPut("x^17 mod n = ", &xq);
|
||
|
#endif
|
||
|
bnEnd(&xp);
|
||
|
bnEnd(&xq);
|
||
|
bnEnd(&k);
|
||
|
return 0;
|
||
|
|
||
|
fail:
|
||
|
bnEnd(&xp);
|
||
|
bnEnd(&xq);
|
||
|
bnEnd(&k);
|
||
|
return RSAGLUE_NOMEM;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This does an RSA signing operation, which is very similar, except
|
||
|
* that the padding differs. The type is 1, and the padding is all 1's
|
||
|
* (hex 0xFF).
|
||
|
*
|
||
|
* To summarize, the format is:
|
||
|
*
|
||
|
* Position Value Function
|
||
|
* n-1 0 This is needed to ensure that the padded number
|
||
|
* is less than the modulus.
|
||
|
* n-2 1 The padding type (all ones).
|
||
|
* n-3..len+1 255 All ones padding to ensure signatures are rare.
|
||
|
* len 0 Zero byte to mark the end of the padding
|
||
|
* len-1..0 data The payload
|
||
|
*
|
||
|
*
|
||
|
* The reason for the all 1's padding is an extra consistency check.
|
||
|
* A randomly invented signature will not decrypt to have the long
|
||
|
* run of ones necessary for acceptance.
|
||
|
*
|
||
|
* Oh... the public key isn't needed to decrypt, but it's passed in
|
||
|
* because a different glue library may need it for some reason.
|
||
|
*/
|
||
|
static const byte signedType = 1;
|
||
|
|
||
|
int
|
||
|
rsaPrivateEncrypt(struct BigNum *bn, byte const *in, unsigned len,
|
||
|
struct PubKey const *pub, struct SecKey const *sec)
|
||
|
{
|
||
|
unsigned bytes = (bnBits(&pub->n)+7)/8;
|
||
|
|
||
|
/* Set the entire number to 0 to start */
|
||
|
(void)bnSetQ(bn, 0);
|
||
|
|
||
|
if (len+3 > bytes)
|
||
|
return RSAGLUE_TOOSMALL; /* Won't fit */
|
||
|
if (bnInsertBigBytes(bn, &signedType, bytes-2, 1) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
if (onesPad(bn, bytes-2, len+1) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
if (bnInsertBigBytes(bn, in, 0, len) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
|
||
|
bndPrintf(("RSA signing.\n"));
|
||
|
bndPut("plaintext = ", bn);
|
||
|
return bnExpModCRA(bn, &sec->d, &sec->p, &sec->q, &sec->u);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Searches bytes, beginning with start-1 and progressing to 0,
|
||
|
* until one that is not 0xff is found. The idex of the last 0xff
|
||
|
* byte is returned (or start if start-1 is not 0xff.)
|
||
|
*/
|
||
|
static unsigned
|
||
|
bnSearchNonOneFromHigh(struct BigNum const *bn, unsigned start)
|
||
|
{
|
||
|
byte buf[16]; /* Size is arbitrary */
|
||
|
unsigned l;
|
||
|
unsigned i;
|
||
|
|
||
|
while (start) {
|
||
|
l = start < sizeof(buf) ? start : sizeof(buf);
|
||
|
start -= l;
|
||
|
bnExtractBigBytes(bn, buf, start, l);
|
||
|
for (i = 0; i < l; i++) {
|
||
|
if (buf[i] != 0xff) {
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
return start + l - i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* Nothing found */
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Decrypt a message with a public key.
|
||
|
* These destroy (actually, replace with a decrypted version) the
|
||
|
* input bignum bn.
|
||
|
*
|
||
|
* Performs an RSA signature check. Returns a prefix of the unwrapped
|
||
|
* data in the given buf. Returns the length of the untruncated
|
||
|
* data, which may exceed "len". Returns <0 on error.
|
||
|
*/
|
||
|
int
|
||
|
rsaPublicDecrypt(byte *buf, unsigned len, struct BigNum *bn,
|
||
|
struct PubKey const *pub)
|
||
|
{
|
||
|
byte tmp[1];
|
||
|
unsigned bytes;
|
||
|
|
||
|
bndPrintf(("RSA signature checking.\n"));
|
||
|
if (bnExpMod(bn, bn, &pub->e, &pub->n) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
bndPut("decrypted = ", bn);
|
||
|
bytes = (bnBits(&pub->n)+7)/8;
|
||
|
|
||
|
bnExtractBigBytes(bn, tmp, bytes-2, 2);
|
||
|
if (tmp[0] != 0 || tmp[1] != signedType) {
|
||
|
memset(tmp, 0, 2);
|
||
|
return RSAGLUE_CORRUPT;
|
||
|
}
|
||
|
|
||
|
bytes = bnSearchNonOneFromHigh(bn, bytes-2);
|
||
|
if (bytes < 1)
|
||
|
return RSAGLUE_CORRUPT;
|
||
|
bytes--;
|
||
|
bnExtractBigBytes(bn, tmp, bytes, 1);
|
||
|
if (tmp[0] != 0) {
|
||
|
tmp[0] = 0;
|
||
|
return RSAGLUE_CORRUPT;
|
||
|
}
|
||
|
/* Note: tmp isn't sensitive any more because its a constant! */
|
||
|
/* Success! Return the data */
|
||
|
if (len > bytes)
|
||
|
len = bytes;
|
||
|
bnExtractBigBytes(bn, buf, bytes-len, len);
|
||
|
return bytes;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Searches bytes, beginning with start-1 and progressing to 0,
|
||
|
* until finding one that is zero, or the end of the array.
|
||
|
* The index of the last non-zero byte is returned (0 if the array
|
||
|
* is all non-zero, or start if start-1 is zero).
|
||
|
*/
|
||
|
static unsigned
|
||
|
bnSearchZeroFromHigh(struct BigNum const *bn, unsigned start)
|
||
|
{
|
||
|
byte buf[16]; /* Size is arbitrary */
|
||
|
unsigned l;
|
||
|
unsigned i;
|
||
|
|
||
|
while (start) {
|
||
|
l = start < sizeof(buf) ? start : sizeof(buf);
|
||
|
start -= l;
|
||
|
bnExtractBigBytes(bn, buf, start, l);
|
||
|
for (i = 0; i < l; i++) {
|
||
|
if (buf[i] == 0) {
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
return start + l - i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* Nothing found */
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Performs an RSA decryption. Returns a prefix of the unwrapped
|
||
|
* data in the given buf. Returns the length of the untruncated
|
||
|
* data, which may exceed "len". Returns <0 on error.
|
||
|
*/
|
||
|
int
|
||
|
rsaPrivateDecrypt(byte *buf, unsigned len, struct BigNum *bn,
|
||
|
struct PubKey const *pub, struct SecKey const *sec)
|
||
|
{
|
||
|
unsigned bytes;
|
||
|
byte tmp[2];
|
||
|
|
||
|
bndPrintf(("RSA decrypting\n"));
|
||
|
if (bnExpModCRA(bn, &sec->d, &sec->p, &sec->q, &sec->u) < 0)
|
||
|
return RSAGLUE_NOMEM;
|
||
|
bndPut("decrypted = ", bn);
|
||
|
bytes = (bnBits(&pub->n)+7)/8;
|
||
|
|
||
|
bnExtractBigBytes(bn, tmp, bytes-2, 2);
|
||
|
if (tmp[0] != 0 || tmp[1] != 2) {
|
||
|
memset(tmp, 0, 2);
|
||
|
return RSAGLUE_CORRUPT;
|
||
|
}
|
||
|
|
||
|
bytes = bnSearchZeroFromHigh(bn, bytes-2);
|
||
|
if (bytes-- == 0)
|
||
|
return RSAGLUE_CORRUPT;
|
||
|
|
||
|
if (len > bytes)
|
||
|
len = bytes;
|
||
|
bnExtractBigBytes(bn, buf, bytes-len, len);
|
||
|
return bytes;
|
||
|
}
|