diff --git a/tests/api.c b/tests/api.c index fb6bb62a2..b84e3065c 100644 --- a/tests/api.c +++ b/tests/api.c @@ -9495,6 +9495,83 @@ static int test_wc_MakeRsaKey (void) } /* END test_wc_MakeRsaKey */ +/* + * Test the bounds checking on the cipher text versus the key modulus. + * 1. Make a new RSA key. + * 2. Set c to 1. + * 3. Decrypt c into k. (error) + * 4. Copy the key modulus to c and sub 1 from the copy. + * 5. Decrypt c into k. (error) + * Valid bounds test cases are covered by all the other RSA tests. + */ +static int test_RsaDecryptBoundsCheck(void) +{ + int ret = 0; +#if !defined(NO_RSA) && defined(WC_RSA_NO_PADDING) && \ + (defined(USE_CERT_BUFFERS_1024) || defined(USE_CERT_BUFFERS_2048)) && \ + defined(WOLFSSL_PUBLIC_MP) && !defined(NO_RSA_BOUNDS_CHECK) + RsaKey key; + byte flatC[256]; + word32 flatCSz; + byte out[256]; + word32 outSz = sizeof(out); + WC_RNG rng; + + printf(testingFmt, "RSA decrypt bounds check"); + + ret = wc_InitRng(&rng); + + if (ret == 0) + ret = wc_InitRsaKey(&key, NULL); + + if (ret == 0) { + const byte* derKey; + word32 derKeySz; + word32 idx = 0; + + #ifdef USE_CERT_BUFFERS_1024 + derKey = server_key_der_1024; + derKeySz = (word32)sizeof_server_key_der_1024; + flatCSz = 128; + #else + derKey = server_key_der_2048; + derKeySz = (word32)sizeof_server_key_der_2048; + flatCSz = 256; + #endif + + ret = wc_RsaPrivateKeyDecode(derKey, &idx, &key, derKeySz); + } + + if (ret == 0) { + XMEMSET(flatC, 0, flatCSz); + flatC[flatCSz-1] = 1; + + ret = wc_RsaDirect(flatC, flatCSz, out, &outSz, &key, + RSA_PRIVATE_DECRYPT, &rng); + } + if (ret == RSA_OUT_OF_RANGE_E) { + mp_int c; + mp_init_copy(&c, &key.n); + mp_sub_d(&c, 1, &c); + mp_to_unsigned_bin(&c, flatC); + ret = wc_RsaDirect(flatC, sizeof(flatC), out, &outSz, &key, + RSA_PRIVATE_DECRYPT, NULL); + mp_clear(&c); + } + if (ret == RSA_OUT_OF_RANGE_E) + ret = 0; + + if (wc_FreeRsaKey(&key) || wc_FreeRng(&rng) || ret != 0) + ret = WOLFSSL_FATAL_ERROR; + + printf(resultFmt, ret == 0 ? passed : failed); + + +#endif + return ret; + +} /* END test_wc_RsaDecryptBoundsCheck */ + /* * Testing wc_SetKeyUsage() */ @@ -17586,6 +17663,7 @@ void ApiTest(void) AssertIntEQ(test_wc_RsaEncryptSize(), 0); AssertIntEQ(test_wc_RsaSSL_SignVerify(), 0); AssertIntEQ(test_wc_RsaFlattenPublicKey(), 0); + AssertIntEQ(test_RsaDecryptBoundsCheck(), 0); AssertIntEQ(test_wc_AesCcmSetKey(), 0); AssertIntEQ(test_wc_AesCcmEncryptDecrypt(), 0); AssertIntEQ(test_wc_Hc128_SetKey(), 0); diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index 414b77a3f..33d9d6efe 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -452,6 +452,9 @@ const char* wc_GetErrorString(int error) case BER_INDEF_E: return "Unable to decode an indefinite length encoded message"; + case RSA_OUT_OF_RANGE_E: + return "Ciphertext to decrypt is out of range"; + default: return "unknown error number"; diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 27aeed722..949cdd072 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -1326,7 +1326,7 @@ static int wc_RsaFunctionSync(const byte* in, word32 inLen, byte* out, /* tmp = tmp*rnd mod n */ if (mp_mulmod(&tmp, &rnd, &key->n, &tmp) != MP_OKAY) ERROR_OUT(MP_MULMOD_E); - #endif /* WC_RSA_BLINGING */ + #endif /* WC_RSA_BLINDING */ #ifdef RSA_LOW_MEM /* half as much memory but twice as slow */ if (mp_exptmod(&tmp, &key->d, &key->n, &tmp) != MP_OKAY) @@ -1609,13 +1609,44 @@ int wc_RsaDirect(byte* in, word32 inLen, byte* out, word32* outSz, int wc_RsaFunction(const byte* in, word32 inLen, byte* out, word32* outLen, int type, RsaKey* key, WC_RNG* rng) { - int ret; + int ret = 0; if (key == NULL || in == NULL || inLen == 0 || out == NULL || outLen == NULL || *outLen == 0 || type == RSA_TYPE_UNKNOWN) { return BAD_FUNC_ARG; } +#ifndef NO_RSA_BOUNDS_CHECK + if (type == RSA_PRIVATE_DECRYPT && + key->state == RSA_STATE_DECRYPT_EXPTMOD) { + + /* Check that 1 < in < n-1. (Requirement of 800-56B.) */ + mp_int c; + + if (mp_init(&c) != MP_OKAY) + ret = MEMORY_E; + if (ret == 0) { + if (mp_read_unsigned_bin(&c, in, inLen) != 0) + ret = MP_READ_E; + } + if (ret == 0) { + /* check c > 1 */ + if (mp_cmp_d(&c, 1) != MP_GT) + ret = RSA_OUT_OF_RANGE_E; + } + if (ret == 0) { + /* check c+1 < n */ + mp_add_d(&c, 1, &c); + if (mp_cmp(&c, &key->n) != MP_LT) + ret = RSA_OUT_OF_RANGE_E; + } + mp_clear(&c); + + if (ret != 0) + return ret; + } +#endif /* NO_RSA_BOUNDS_CHECK */ + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_RSA) if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_RSA && key->n.raw.len > 0) { diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index 6088bd9cb..9a4a8076c 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -197,10 +197,10 @@ enum { PSS_SALTLEN_E = -250, /* PSS length of salt is to long for hash */ PRIME_GEN_E = -251, /* Failure finding a prime. */ - BER_INDEF_E = -252, /* Cannot decode indefinite length BER. */ + RSA_OUT_OF_RANGE_E = -253, /* Ciphertext to decrypt out of range. */ - WC_LAST_E = -252, /* Update this to indicate last error */ + WC_LAST_E = -253, /* Update this to indicate last error */ MIN_CODE_E = -300 /* errors -101 - -299 */ /* add new companion error id strings for any new error codes