RSA Decrypt Bounds Checking

1. Added some bounds checking on the ciphertext passed into the RSA decrypt function. NIST SP 800-56B specifies that the ciphertext shouldn't be a number larger than the modulus.
2. Added an API test to check that the direct RSA decrypt function returns an error with a "bad" message.
3. Added an ifndef guard to disable the bounds check. Default is to keep the bounds check.
4. RSA Decrypt bounds check only checked the first time into wc_RsaFunction().
This commit is contained in:
John Safranek
2018-02-22 17:17:54 -08:00
parent 6500c40015
commit a49553df6a
4 changed files with 116 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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