From 84f609306889c9710f11e82aa90a7248a832ca54 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 24 Oct 2017 11:51:22 -0700 Subject: [PATCH] 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. --- wolfcrypt/src/error.c | 3 + wolfcrypt/src/rsa.c | 314 +++++++++++++++++++++++++++++--- wolfssl/wolfcrypt/error-crypt.h | 3 +- wolfssl/wolfcrypt/rsa.h | 4 + 4 files changed, 293 insertions(+), 31 deletions(-) diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index 280f14d90..add4f6458 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -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"; diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 178b41e95..6de416ce6 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -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); diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index 4f6066272..9ca0c1a2b 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -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 diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index ee5a70366..d56a7d136 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -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 */