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
John Safranek 2017-10-24 11:51:22 -07:00
parent 4afa7c7e22
commit 84f6093068
4 changed files with 293 additions and 31 deletions

View File

@ -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";

View File

@ -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);

View File

@ -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

View File

@ -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 */