mirror of https://github.com/wolfSSL/wolfssl.git
RSA Update
1. Replaced MakeRsaKey() function wth a version that follows the NIST prescribed process closer. 2. Added an additional check to RSA key generation to ensure that |p-q| > 2^((nlen/2)-100) per NIST FIPS 186-4 sec B.3.1. 3. Added public API for checking a number being probably prime according to FIPS 186-4. 4. Added a large integer used to check the lower bound of a possible prime.pull/1311/head
parent
4afa7c7e22
commit
84f6093068
|
@ -443,6 +443,9 @@ const char* wc_GetErrorString(int error)
|
|||
case PSS_SALTLEN_E:
|
||||
return "PSS - Length of salt is too big for hash algorithm";
|
||||
|
||||
case PRIME_GEN_E:
|
||||
return "Unable to find a prime for RSA key";
|
||||
|
||||
default:
|
||||
return "unknown error number";
|
||||
|
||||
|
|
|
@ -2096,16 +2096,221 @@ int wc_RsaFlattenPublicKey(RsaKey* key, byte* e, word32* eSz, byte* n,
|
|||
}
|
||||
|
||||
#ifdef WOLFSSL_KEY_GEN
|
||||
|
||||
/* Check that |p-q| > 2^((size/2)-100) */
|
||||
static int wc_CompareDiffPQ(mp_int* p, mp_int* q, int size)
|
||||
{
|
||||
mp_int c, d;
|
||||
int ret;
|
||||
|
||||
if (p == NULL || q == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
ret = mp_init_multi(&c, &d, NULL, NULL, NULL, NULL);
|
||||
|
||||
/* c = 2^((size/2)-100) */
|
||||
if (ret == 0)
|
||||
ret = mp_2expt(&c, (size/2)-100);
|
||||
|
||||
/* d = |p-q| */
|
||||
if (ret == 0)
|
||||
ret = mp_sub(p, q, &d);
|
||||
|
||||
if (ret == 0)
|
||||
ret = mp_abs(&d, &d);
|
||||
|
||||
/* compare */
|
||||
if (ret == 0)
|
||||
ret = mp_cmp(&d, &c);
|
||||
|
||||
if (ret == MP_GT)
|
||||
ret = MP_OKAY;
|
||||
|
||||
mp_clear(&d);
|
||||
mp_clear(&c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* The lower_bound value is floor(2^(0.5) * 2^((nlen/2)-1)) where nlen is 4096.
|
||||
* This number was calculated using a small test tool written with a common
|
||||
* large number math library. Other values of nlen may be checked with a subset
|
||||
* of lower_bound. */
|
||||
static const byte lower_bound[] = {
|
||||
0xB5, 0x04, 0xF3, 0x33, 0xF9, 0xDE, 0x64, 0x84,
|
||||
0x59, 0x7D, 0x89, 0xB3, 0x75, 0x4A, 0xBE, 0x9F,
|
||||
0x1D, 0x6F, 0x60, 0xBA, 0x89, 0x3B, 0xA8, 0x4C,
|
||||
0xED, 0x17, 0xAC, 0x85, 0x83, 0x33, 0x99, 0x15,
|
||||
/* 512 */
|
||||
0x4A, 0xFC, 0x83, 0x04, 0x3A, 0xB8, 0xA2, 0xC3,
|
||||
0xA8, 0xB1, 0xFE, 0x6F, 0xDC, 0x83, 0xDB, 0x39,
|
||||
0x0F, 0x74, 0xA8, 0x5E, 0x43, 0x9C, 0x7B, 0x4A,
|
||||
0x78, 0x04, 0x87, 0x36, 0x3D, 0xFA, 0x27, 0x68,
|
||||
/* 1024 */
|
||||
0xD2, 0x20, 0x2E, 0x87, 0x42, 0xAF, 0x1F, 0x4E,
|
||||
0x53, 0x05, 0x9C, 0x60, 0x11, 0xBC, 0x33, 0x7B,
|
||||
0xCA, 0xB1, 0xBC, 0x91, 0x16, 0x88, 0x45, 0x8A,
|
||||
0x46, 0x0A, 0xBC, 0x72, 0x2F, 0x7C, 0x4E, 0x33,
|
||||
0xC6, 0xD5, 0xA8, 0xA3, 0x8B, 0xB7, 0xE9, 0xDC,
|
||||
0xCB, 0x2A, 0x63, 0x43, 0x31, 0xF3, 0xC8, 0x4D,
|
||||
0xF5, 0x2F, 0x12, 0x0F, 0x83, 0x6E, 0x58, 0x2E,
|
||||
0xEA, 0xA4, 0xA0, 0x89, 0x90, 0x40, 0xCA, 0x4A,
|
||||
/* 2048 */
|
||||
0x81, 0x39, 0x4A, 0xB6, 0xD8, 0xFD, 0x0E, 0xFD,
|
||||
0xF4, 0xD3, 0xA0, 0x2C, 0xEB, 0xC9, 0x3E, 0x0C,
|
||||
0x42, 0x64, 0xDA, 0xBC, 0xD5, 0x28, 0xB6, 0x51,
|
||||
0xB8, 0xCF, 0x34, 0x1B, 0x6F, 0x82, 0x36, 0xC7,
|
||||
0x01, 0x04, 0xDC, 0x01, 0xFE, 0x32, 0x35, 0x2F,
|
||||
0x33, 0x2A, 0x5E, 0x9F, 0x7B, 0xDA, 0x1E, 0xBF,
|
||||
0xF6, 0xA1, 0xBE, 0x3F, 0xCA, 0x22, 0x13, 0x07,
|
||||
0xDE, 0xA0, 0x62, 0x41, 0xF7, 0xAA, 0x81, 0xC2,
|
||||
/* 3072 */
|
||||
0xC1, 0xFC, 0xBD, 0xDE, 0xA2, 0xF7, 0xDC, 0x33,
|
||||
0x18, 0x83, 0x8A, 0x2E, 0xAF, 0xF5, 0xF3, 0xB2,
|
||||
0xD2, 0x4F, 0x4A, 0x76, 0x3F, 0xAC, 0xB8, 0x82,
|
||||
0xFD, 0xFE, 0x17, 0x0F, 0xD3, 0xB1, 0xF7, 0x80,
|
||||
0xF9, 0xAC, 0xCE, 0x41, 0x79, 0x7F, 0x28, 0x05,
|
||||
0xC2, 0x46, 0x78, 0x5E, 0x92, 0x95, 0x70, 0x23,
|
||||
0x5F, 0xCF, 0x8F, 0x7B, 0xCA, 0x3E, 0xA3, 0x3B,
|
||||
0x4D, 0x7C, 0x60, 0xA5, 0xE6, 0x33, 0xE3, 0xE1
|
||||
/* 4096 */
|
||||
};
|
||||
|
||||
|
||||
static INLINE int RsaSizeCheck(int size)
|
||||
{
|
||||
switch (size) {
|
||||
#ifndef HAVE_FIPS
|
||||
case 1024:
|
||||
#endif
|
||||
case 2048:
|
||||
case 3072:
|
||||
case 4096:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wc_CheckProbablePrime_ex(mp_int* p, mp_int* q, mp_int* e, int nlen,
|
||||
int* isPrime)
|
||||
{
|
||||
int ret;
|
||||
mp_int tmp1, tmp2;
|
||||
mp_int* prime;
|
||||
|
||||
if (p == NULL || e == NULL || isPrime == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
if (!RsaSizeCheck(nlen))
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
*isPrime = MP_NO;
|
||||
|
||||
if (q != NULL) {
|
||||
/* 5.4 - check that |p-q| <= (2^(1/2))(2^((nlen/2)-1)) */
|
||||
ret = wc_CompareDiffPQ(p, q, nlen);
|
||||
if (ret != MP_OKAY) goto notOkay;
|
||||
prime = q;
|
||||
}
|
||||
else
|
||||
prime = p;
|
||||
|
||||
ret = mp_init_multi(&tmp1, &tmp2, NULL, NULL, NULL, NULL);
|
||||
if (ret != MP_OKAY) goto notOkay;
|
||||
|
||||
/* 4.4,5.5 - Check that prime >= (2^(1/2))(2^((nlen/2)-1))
|
||||
* This is a comparison against lowerBound */
|
||||
ret = mp_read_unsigned_bin(&tmp1, lower_bound, nlen/16);
|
||||
if (ret != MP_OKAY) goto notOkay;
|
||||
ret = mp_cmp(prime, &tmp1);
|
||||
if (ret == MP_LT) goto exit;
|
||||
|
||||
/* 4.5,5.6 - Check that GCD(p-1, e) == 1 */
|
||||
ret = mp_sub_d(prime, 1, &tmp1); /* tmp1 = prime-1 */
|
||||
if (ret != MP_OKAY) goto notOkay;
|
||||
ret = mp_gcd(&tmp1, e, &tmp2); /* tmp2 = gcd(prime-1, e) */
|
||||
if (ret != MP_OKAY) goto notOkay;
|
||||
ret = mp_cmp_d(&tmp2, 1);
|
||||
if (ret != MP_EQ) goto exit; /* e divides p-1 */
|
||||
|
||||
/* 4.5.1,5.6.1 - Check primality of p with 8 iterations */
|
||||
ret = mp_prime_is_prime(prime, 8, isPrime);
|
||||
/* Performs some divides by a table of primes, and then does M-R,
|
||||
* it sets isPrime as a side-effect. */
|
||||
if (ret != MP_OKAY) goto notOkay;
|
||||
|
||||
exit:
|
||||
ret = MP_OKAY;
|
||||
notOkay:
|
||||
mp_clear(&tmp1);
|
||||
mp_clear(&tmp2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int wc_CheckProbablePrime(const byte* pRaw, word32 pRawSz,
|
||||
const byte* qRaw, word32 qRawSz,
|
||||
const byte* eRaw, word32 eRawSz,
|
||||
int nlen, int* isPrime)
|
||||
{
|
||||
mp_int p, q, e;
|
||||
mp_int* Q = NULL;
|
||||
int ret;
|
||||
|
||||
if (pRaw == NULL || pRawSz == 0 ||
|
||||
eRaw == NULL || eRawSz == 0 ||
|
||||
isPrime == NULL) {
|
||||
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if ((qRaw != NULL && qRawSz == 0) || (qRaw == NULL && qRawSz != 0))
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
ret = mp_init_multi(&p, &q, &e, NULL, NULL, NULL);
|
||||
|
||||
if (ret == MP_OKAY)
|
||||
ret = mp_read_unsigned_bin(&p, pRaw, pRawSz);
|
||||
|
||||
if (ret == MP_OKAY) {
|
||||
if (qRaw != NULL) {
|
||||
if (ret == MP_OKAY)
|
||||
ret = mp_read_unsigned_bin(&q, qRaw, qRawSz);
|
||||
if (ret == MP_OKAY)
|
||||
Q = &q;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == MP_OKAY)
|
||||
ret = mp_read_unsigned_bin(&e, eRaw, eRawSz);
|
||||
|
||||
if (ret == MP_OKAY)
|
||||
ret = wc_CheckProbablePrime_ex(&p, Q, &e, nlen, isPrime);
|
||||
|
||||
ret = (ret == MP_OKAY) ? 0 : PRIME_GEN_E;
|
||||
|
||||
mp_clear(&p);
|
||||
mp_clear(&q);
|
||||
mp_clear(&e);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Make an RSA key for size bits, with e specified, 65537 is a good e */
|
||||
int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng)
|
||||
{
|
||||
mp_int p, q, tmp1, tmp2, tmp3;
|
||||
int err;
|
||||
int err, i, failCount, primeSz, isPrime;
|
||||
byte* buf = NULL;
|
||||
|
||||
if (key == NULL || rng == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
if (size < RSA_MIN_SIZE || size > RSA_MAX_SIZE)
|
||||
if (!RsaSizeCheck(size))
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
if (e < 3 || (e & 1) == 0)
|
||||
|
@ -2130,35 +2335,87 @@ int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng)
|
|||
}
|
||||
#endif
|
||||
|
||||
if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != MP_OKAY)
|
||||
return err;
|
||||
err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL);
|
||||
|
||||
err = mp_set_int(&tmp3, e);
|
||||
if (err == MP_OKAY)
|
||||
err = mp_set_int(&tmp3, e);
|
||||
|
||||
failCount = 5 * (size / 2);
|
||||
primeSz = size / 16; /* size is the size of n in bits.
|
||||
primeSz is in bytes. */
|
||||
|
||||
/* allocate buffer to work with */
|
||||
if (err == MP_OKAY) {
|
||||
buf = (byte*)XMALLOC(primeSz, key->heap, DYNAMIC_TYPE_RSA);
|
||||
if (buf == NULL)
|
||||
err = MEMORY_E;
|
||||
}
|
||||
|
||||
/* make p */
|
||||
if (err == MP_OKAY) {
|
||||
isPrime = 0;
|
||||
i = 0;
|
||||
do {
|
||||
err = mp_rand_prime(&p, size/16, rng, key->heap); /* size in bytes/2 */
|
||||
#ifdef SHOW_GEN
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
/* generate value */
|
||||
err = wc_RNG_GenerateBlock(rng, buf, primeSz);
|
||||
|
||||
if (err == 0) {
|
||||
/* prime lower bound has the MSB set, set it in candidate */
|
||||
buf[0] |= 0x80;
|
||||
/* make candidate odd */
|
||||
buf[primeSz-1] |= 0x01;
|
||||
/* load value */
|
||||
err = mp_read_unsigned_bin(&p, buf, primeSz);
|
||||
}
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_sub_d(&p, 1, &tmp1); /* tmp1 = p-1 */
|
||||
err = wc_CheckProbablePrime_ex(&p, NULL, &tmp3, size, &isPrime);
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_gcd(&tmp1, &tmp3, &tmp2); /* tmp2 = gcd(p-1, e) */
|
||||
} while (err == MP_OKAY && mp_cmp_d(&tmp2, 1) != 0); /* e divides p-1 */
|
||||
i++;
|
||||
} while (err == MP_OKAY && !isPrime && i < failCount);
|
||||
}
|
||||
|
||||
if (err == MP_OKAY && !isPrime)
|
||||
err = PRIME_GEN_E;
|
||||
|
||||
/* make q */
|
||||
if (err == MP_OKAY) {
|
||||
isPrime = 0;
|
||||
i = 0;
|
||||
do {
|
||||
err = mp_rand_prime(&q, size/16, rng, key->heap); /* size in bytes/2 */
|
||||
#ifdef SHOW_GEN
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
/* generate value */
|
||||
err = wc_RNG_GenerateBlock(rng, buf, primeSz);
|
||||
|
||||
if (err == 0) {
|
||||
/* prime lower bound has the MSB set, set it in candidate */
|
||||
buf[0] |= 0x80;
|
||||
/* make candidate odd */
|
||||
buf[primeSz-1] |= 0x01;
|
||||
/* load value */
|
||||
err = mp_read_unsigned_bin(&q, buf, primeSz);
|
||||
}
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_sub_d(&q, 1, &tmp1); /* tmp1 = q-1 */
|
||||
err = wc_CheckProbablePrime_ex(&p, &q, &tmp3, size, &isPrime);
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_gcd(&tmp1, &tmp3, &tmp2); /* tmp2 = gcd(q-1, e) */
|
||||
} while (err == MP_OKAY && mp_cmp_d(&tmp2, 1) != 0); /* e divides q-1 */
|
||||
i++;
|
||||
} while (err == MP_OKAY && !isPrime && i < failCount);
|
||||
}
|
||||
|
||||
if (err == MP_OKAY && !isPrime)
|
||||
err = PRIME_GEN_E;
|
||||
|
||||
if (buf) {
|
||||
ForceZero(buf, primeSz);
|
||||
XFREE(buf, key->heap, DYNAMIC_TYPE_RSA);
|
||||
}
|
||||
|
||||
if (err == MP_OKAY)
|
||||
|
@ -2168,35 +2425,32 @@ int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng)
|
|||
err = mp_init_multi(&key->dP, &key->dQ, &key->u, NULL, NULL, NULL);
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_sub_d(&p, 1, &tmp2); /* tmp2 = p-1 */
|
||||
err = mp_sub_d(&p, 1, &tmp1); /* tmp1 = p-1 */
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_lcm(&tmp1, &tmp2, &tmp1); /* tmp1 = lcm(p-1, q-1),last loop */
|
||||
err = mp_sub_d(&q, 1, &tmp2); /* tmp2 = q-1 */
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_lcm(&tmp1, &tmp2, &tmp3); /* tmp3 = lcm(p-1, q-1),last loop */
|
||||
|
||||
/* make key */
|
||||
if (err == MP_OKAY)
|
||||
err = mp_set_int(&key->e, (mp_digit)e); /* key->e = e */
|
||||
|
||||
if (err == MP_OKAY) /* key->d = 1/e mod lcm(p-1, q-1) */
|
||||
err = mp_invmod(&key->e, &tmp1, &key->d);
|
||||
err = mp_invmod(&key->e, &tmp3, &key->d);
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_mul(&p, &q, &key->n); /* key->n = pq */
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_sub_d(&p, 1, &tmp1);
|
||||
err = mp_mod(&key->d, &tmp1, &key->dP); /* key->dP = d mod(p-1) */
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_sub_d(&q, 1, &tmp2);
|
||||
err = mp_mod(&key->d, &tmp2, &key->dQ); /* key->dQ = d mod(q-1) */
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_mod(&key->d, &tmp1, &key->dP);
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_mod(&key->d, &tmp2, &key->dQ);
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_invmod(&q, &p, &key->u);
|
||||
err = mp_invmod(&q, &p, &key->u); /* key->u = 1/q mod p */
|
||||
|
||||
if (err == MP_OKAY)
|
||||
err = mp_copy(&p, &key->p);
|
||||
|
@ -2207,11 +2461,11 @@ int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng)
|
|||
if (err == MP_OKAY)
|
||||
key->type = RSA_PRIVATE;
|
||||
|
||||
mp_clear(&tmp3);
|
||||
mp_clear(&tmp2);
|
||||
mp_clear(&tmp1);
|
||||
mp_clear(&q);
|
||||
mp_clear(&tmp2);
|
||||
mp_clear(&tmp3);
|
||||
mp_clear(&p);
|
||||
mp_clear(&q);
|
||||
|
||||
if (err != MP_OKAY) {
|
||||
wc_FreeRsaKey(key);
|
||||
|
|
|
@ -195,8 +195,9 @@ enum {
|
|||
WC_HW_WAIT_E = -249, /* Hardware waiting on resource */
|
||||
|
||||
PSS_SALTLEN_E = -250, /* PSS length of salt is to long for hash */
|
||||
PRIME_GEN_E = -251, /* Failure finding a prime. */
|
||||
|
||||
WC_LAST_E = -250, /* Update this to indicate last error */
|
||||
WC_LAST_E = -251, /* Update this to indicate last error */
|
||||
MIN_CODE_E = -300 /* errors -101 - -299 */
|
||||
|
||||
/* add new companion error id strings for any new error codes
|
||||
|
|
|
@ -226,6 +226,10 @@ WOLFSSL_API int wc_RsaFlattenPublicKey(RsaKey*, byte*, word32*, byte*,
|
|||
#ifdef WOLFSSL_KEY_GEN
|
||||
WOLFSSL_API int wc_RsaKeyToPublicDer(RsaKey*, byte* output, word32 inLen);
|
||||
WOLFSSL_API int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng);
|
||||
WOLFSSL_API int wc_CheckProbablePrime(const byte* p, word32 pSz,
|
||||
const byte* q, word32 qSz,
|
||||
const byte* e, word32 eSz,
|
||||
int nlen, int* isPrime);
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_USER_RSA */
|
||||
|
|
Loading…
Reference in New Issue