mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-04 18:27:36 +00:00
d2edcad66e
Thanks to Phil Zimmermann for the code and for the license exception we needed to include it. There remains some build system integration work to be done before this code will build properly in the FreeSWITCH tree.
472 lines
13 KiB
C
472 lines
13 KiB
C
/*
|
|
* Copyright (c) 1995 Colin Plumb. All rights reserved.
|
|
* For licensing and other legal details, see the file legal.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.
|
|
*/
|
|
|
|
#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;
|
|
}
|