diff --git a/tests/api/test_curve25519.c b/tests/api/test_curve25519.c index 36cf643f2c6..c46ea40194f 100644 --- a/tests/api/test_curve25519.c +++ b/tests/api/test_curve25519.c @@ -444,14 +444,17 @@ int test_wc_curve25519_export_private_raw_ex(void) EXPECT_DECLS; #if defined(HAVE_CURVE25519) curve25519_key key; + WC_RNG rng; byte out[CURVE25519_KEYSIZE]; word32 outLen = sizeof(out); int endian = EC25519_BIG_ENDIAN; + XMEMSET(&rng, 0, sizeof(WC_RNG)); ExpectIntEQ(wc_curve25519_init(&key), 0); + /* Reject export when private key not set (privSet == 0). */ ExpectIntEQ(wc_curve25519_export_private_raw_ex(&key, out, &outLen, endian), - 0); + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); /* test bad cases */ ExpectIntEQ(wc_curve25519_export_private_raw_ex(NULL, NULL, NULL, endian), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); @@ -461,12 +464,15 @@ int test_wc_curve25519_export_private_raw_ex(void) endian), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_curve25519_export_private_raw_ex(&key, out, NULL, endian), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - ExpectIntEQ(wc_curve25519_export_private_raw_ex(&key, out, &outLen, - EC25519_LITTLE_ENDIAN), 0); - outLen = outLen - 2; + + /* Populate the key, then exercise the buffer-too-small path. */ + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_curve25519_make_key(&rng, CURVE25519_KEYSIZE, &key), 0); + outLen = CURVE25519_KEYSIZE - 1; ExpectIntEQ(wc_curve25519_export_private_raw_ex(&key, out, &outLen, endian), WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_curve25519_free(&key); #endif return EXPECT_RESULT(); diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index 653af5eabc2..384ede374f0 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -164,13 +164,16 @@ int test_wc_curve448_export_private_raw_ex(void) EXPECT_DECLS; #if defined(HAVE_CURVE448) curve448_key key; + WC_RNG rng; byte out[CURVE448_KEY_SIZE]; word32 outLen = sizeof(out); int endian = EC448_BIG_ENDIAN; + XMEMSET(&rng, 0, sizeof(WC_RNG)); ExpectIntEQ(wc_curve448_init(&key), 0); + /* Reject export when private key not set (privSet == 0). */ ExpectIntEQ(wc_curve448_export_private_raw_ex(&key, out, &outLen, endian), - 0); + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); /* test bad cases */ ExpectIntEQ(wc_curve448_export_private_raw_ex(NULL, NULL, NULL, endian), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); @@ -180,12 +183,15 @@ int test_wc_curve448_export_private_raw_ex(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_curve448_export_private_raw_ex(&key, out, NULL, endian), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - ExpectIntEQ(wc_curve448_export_private_raw_ex(&key, out, &outLen, - EC448_LITTLE_ENDIAN), 0); - outLen = outLen - 2; + + /* Populate the key, then exercise the buffer-too-small path. */ + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &key), 0); + outLen = CURVE448_KEY_SIZE - 1; ExpectIntEQ(wc_curve448_export_private_raw_ex(&key, out, &outLen, endian), WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_curve448_free(&key); #endif return EXPECT_RESULT(); diff --git a/tests/api/test_ed25519.c b/tests/api/test_ed25519.c index 34d39099d33..5eb9c13c4a2 100644 --- a/tests/api/test_ed25519.c +++ b/tests/api/test_ed25519.c @@ -347,6 +347,13 @@ int test_wc_ed25519_export(void) XMEMSET(&rng, 0, sizeof(WC_RNG)); ExpectIntEQ(wc_ed25519_init(&key), 0); + /* Reject export when private key not set. */ + PRIVATE_KEY_UNLOCK(); + ExpectIntEQ(wc_ed25519_export_private_only(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_export_private(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + PRIVATE_KEY_LOCK(); ExpectIntEQ(wc_InitRng(&rng), 0); #ifdef HAVE_ED25519_MAKE_KEY ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0); @@ -379,6 +386,20 @@ int test_wc_ed25519_export(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); PRIVATE_KEY_LOCK(); +#ifdef HAVE_ED25519_KEY_IMPORT + /* Public-only key: re-init and import just the public part; private + * exports must still fail with privKeySet == 0. */ + wc_ed25519_free(&key); + ExpectIntEQ(wc_ed25519_init(&key), 0); + ExpectIntEQ(wc_ed25519_import_public(pub, pubSz, &key), 0); + PRIVATE_KEY_UNLOCK(); + ExpectIntEQ(wc_ed25519_export_private_only(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_export_private(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + PRIVATE_KEY_LOCK(); +#endif + DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_ed25519_free(&key); #endif diff --git a/tests/api/test_ed448.c b/tests/api/test_ed448.c index fd2b2393bc0..0303e167cfa 100644 --- a/tests/api/test_ed448.c +++ b/tests/api/test_ed448.c @@ -324,6 +324,13 @@ int test_wc_ed448_export(void) XMEMSET(&rng, 0, sizeof(WC_RNG)); ExpectIntEQ(wc_ed448_init(&key), 0); + /* Reject export when private key not set. */ + PRIVATE_KEY_UNLOCK(); + ExpectIntEQ(wc_ed448_export_private_only(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed448_export_private(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + PRIVATE_KEY_LOCK(); ExpectIntEQ(wc_InitRng(&rng), 0); ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &key), 0); @@ -351,6 +358,20 @@ int test_wc_ed448_export(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); PRIVATE_KEY_LOCK(); +#ifdef HAVE_ED448_KEY_IMPORT + /* Public-only key: re-init and import just the public part; private + * exports must still fail with privKeySet == 0. */ + wc_ed448_free(&key); + ExpectIntEQ(wc_ed448_init(&key), 0); + ExpectIntEQ(wc_ed448_import_public(pub, pubSz, &key), 0); + PRIVATE_KEY_UNLOCK(); + ExpectIntEQ(wc_ed448_export_private_only(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed448_export_private(&key, priv, &privSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + PRIVATE_KEY_LOCK(); +#endif + DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_ed448_free(&key); #endif diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 2835c556cdf..b5e23f85b1a 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -71,6 +71,8 @@ */ int wc_CMAC_Grow(Cmac* cmac, const byte* in, int inSz) { + if ((cmac == NULL) || (in == NULL && inSz != 0)) + return BAD_FUNC_ARG; return _wc_Hash_Grow(&cmac->msg, &cmac->used, &cmac->len, in, inSz, cmac->aes.heap); } #endif /* WOLFSSL_HASH_KEEP */ diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index f8ca74255c9..a7d0ec7ff52 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -971,6 +971,9 @@ int wc_curve25519_export_private_raw_ex(curve25519_key* key, byte* out, if (key == NULL || out == NULL || outLen == NULL) return BAD_FUNC_ARG; + if (!key->privSet) + return ECC_BAD_ARG_E; + /* check size of outgoing buffer */ if (*outLen < CURVE25519_KEYSIZE) { *outLen = CURVE25519_KEYSIZE; diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index cbf34863646..8f1ded41dcf 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -478,6 +478,10 @@ int wc_curve448_export_private_raw_ex(curve448_key* key, byte* out, ret = BAD_FUNC_ARG; } + if ((ret == 0) && (!key->privSet)) { + ret = ECC_BAD_ARG_E; + } + /* check size of outgoing buffer */ if ((ret == 0) && (*outLen < CURVE448_KEY_SIZE)) { *outLen = CURVE448_KEY_SIZE; diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index e741f32d0a5..8b375191263 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -1414,7 +1414,7 @@ int wc_ed25519_import_private_key(const byte* priv, word32 privSz, int wc_ed25519_export_private_only(const ed25519_key* key, byte* out, word32* outLen) { /* sanity checks on arguments */ - if (key == NULL || out == NULL || outLen == NULL) + if (key == NULL || !key->privKeySet || out == NULL || outLen == NULL) return BAD_FUNC_ARG; if (*outLen < ED25519_KEY_SIZE) { diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index a3f931f5c53..0b6b1e41084 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -1301,6 +1301,10 @@ int wc_ed448_export_private_only(const ed448_key* key, byte* out, word32* outLen ret = BAD_FUNC_ARG; } + if ((ret == 0) && (!key->privKeySet)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && (*outLen < ED448_KEY_SIZE)) { *outLen = ED448_KEY_SIZE; ret = BUFFER_E; @@ -1333,6 +1337,10 @@ int wc_ed448_export_private(const ed448_key* key, byte* out, word32* outLen) ret = BAD_FUNC_ARG; } + if ((ret == 0) && (!key->privKeySet)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && (*outLen < ED448_PRV_KEY_SIZE)) { *outLen = ED448_PRV_KEY_SIZE; ret = BUFFER_E; diff --git a/wolfcrypt/src/integer.c b/wolfcrypt/src/integer.c index 91e457cdee1..65789c41c75 100644 --- a/wolfcrypt/src/integer.c +++ b/wolfcrypt/src/integer.c @@ -549,21 +549,48 @@ int mp_exch (mp_int * a, mp_int * b) return MP_OKAY; } +/* Constant-time conditional swap: must not branch on m (leaks scalar bit). + * m must be 0 or 1. The t parameter is unused; XOR is performed in place + * with a single-digit stack scratch so callers don't need to clear t->dp. */ int mp_cond_swap_ct_ex (mp_int * a, mp_int * b, int c, int m, mp_int * t) { - (void)c; + int i; + int err; + int imask; + int idiff; + mp_digit mask; + mp_digit d; + (void)t; - if (m == 1) - mp_exch(a, b); + + m &= 1; + imask = -m; + mask = (mp_digit)0 - (mp_digit)m; + + if ((err = mp_grow(a, c)) != MP_OKAY) + return err; + if ((err = mp_grow(b, c)) != MP_OKAY) + return err; + + idiff = (a->used ^ b->used) & imask; + a->used ^= idiff; + b->used ^= idiff; + idiff = (a->sign ^ b->sign) & imask; + a->sign ^= idiff; + b->sign ^= idiff; + + for (i = 0; i < c; i++) { + d = (a->dp[i] ^ b->dp[i]) & mask; + a->dp[i] ^= d; + b->dp[i] ^= d; + } + return MP_OKAY; } int mp_cond_swap_ct (mp_int * a, mp_int * b, int c, int m) { - (void)c; - if (m == 1) - mp_exch(a, b); - return MP_OKAY; + return mp_cond_swap_ct_ex(a, b, c, m, NULL); } diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 729c17f091c..f50c9436be5 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -12009,6 +12009,9 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz, int ret = 0, length, saltSz, iterations, blockSz, kekKeySz; int hashOID = WC_SHA; /* default to SHA1 */ + int keyLen = 0; + int keyLenPresent = 0; + word32 pbkdf2End = 0; word32 kdfAlgoId, pwriEncAlgoId, keyEncAlgoId, cekSz; byte* pkiMsg = in; word32 pkiMsgSz = inSz; @@ -12055,6 +12058,7 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz, /* get KDF params SEQ */ if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; + pbkdf2End = *idx + (word32)length; /* get KDF salt OCTET STRING */ if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) @@ -12080,6 +12084,44 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz, return ASN_PARSE_E; } + /* optional keyLength - validated below once kekKeySz is known */ + if (*idx < pbkdf2End && pkiMsg[*idx] == ASN_INTEGER) { + if (GetShortInt(pkiMsg, idx, &keyLen, pbkdf2End) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + keyLenPresent = 1; + } + + /* optional prf; default hmacWithSHA1 keeps hashOID at WC_SHA */ + if (*idx < pbkdf2End && + pkiMsg[*idx] == (ASN_SEQUENCE | ASN_CONSTRUCTED)) { + word32 prfOid = 0; + if (GetAlgoId(pkiMsg, idx, &prfOid, oidHmacType, + pbkdf2End) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + switch ((int)prfOid) { + #ifdef WOLFSSL_SHA224 + case HMAC_SHA224_OID: hashOID = WC_SHA224; break; + #endif + #ifndef NO_SHA256 + case HMAC_SHA256_OID: hashOID = WC_SHA256; break; + #endif + #ifdef WOLFSSL_SHA384 + case HMAC_SHA384_OID: hashOID = WC_SHA384; break; + #endif + #ifdef WOLFSSL_SHA512 + case HMAC_SHA512_OID: hashOID = WC_SHA512; break; + #endif + default: + /* unknown id (incl. explicit hmacWithSHA1) - keep + * default WC_SHA; MAC unwrap fails if mismatched */ + break; + } + } + /* get KeyEncAlgoId SEQ */ if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) { XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); @@ -12112,6 +12154,13 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz, return kekKeySz; } + /* RFC 8018: when present, PBKDF2 keyLength must equal the + * derived key length expected by the encryption algorithm */ + if (keyLenPresent && keyLen != kekKeySz) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index a57b1a2edc9..3aff7a821ef 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -928,6 +928,8 @@ WOLFSSL_API int wc_BankRef_Release(WC_RNG *rng) { int isZero = 0; int ret = 0; + if (rng == NULL) + return BAD_FUNC_ARG; if (rng->bankref == NULL) return BAD_FUNC_ARG; wolfSSL_RefDec(&rng->bankref->refcount, &isZero, &ret); diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index 8c387eec6ff..5094df0d828 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -2633,7 +2633,7 @@ int wc_hash2sz(int hType) case WC_HASH_TYPE_SHA: return 20; case WC_HASH_TYPE_SHA224: - return 24; + return 28; case WC_HASH_TYPE_SHA256: return 32; case WC_HASH_TYPE_SHA384: diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c index 30c67d167e8..e7e673a3b7b 100644 --- a/wolfcrypt/src/wolfentropy.c +++ b/wolfcrypt/src/wolfentropy.c @@ -477,6 +477,11 @@ static int Entropy_GetNoise(unsigned char* noise, int samples) return 0; } +/* Mutex to prevent multiple callers requesting entropy operations at the + * same time. + */ +static wolfSSL_Mutex entropy_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(entropy_mutex); + /* Generate raw entropy for performing assessment. * * @param [out] raw Buffer to hold raw entropy data. @@ -488,13 +493,31 @@ static int Entropy_GetNoise(unsigned char* noise, int samples) int wc_Entropy_GetRawEntropy(unsigned char* raw, int cnt) { int ret = 0; + int locked = 0; + +#ifdef HAVE_FIPS + if (!entropy_memuse_initialized) { + ret = Entropy_Init(); + } +#endif + + /* Lock the mutex as collection uses globals. */ + if (ret == 0) { + if (wc_LockMutex(&entropy_mutex) != 0) { + ret = BAD_MUTEX_E; + } + else { + locked = 1; + } + } #ifdef ENTROPY_MEMUSE_THREADED - /* Start the counter thread as a proxy for time counter. */ - ret = Entropy_StartThread(); - if (ret == 0) + if (ret == 0) { + /* Start the counter thread as a proxy for time counter. */ + ret = Entropy_StartThread(); + } #endif - { + if (ret == 0) { ret = Entropy_GetNoise(raw, cnt); } #ifdef ENTROPY_MEMUSE_THREADED @@ -502,6 +525,10 @@ int wc_Entropy_GetRawEntropy(unsigned char* raw, int cnt) Entropy_StopThread(); #endif + if (locked) { + wc_UnLockMutex(&entropy_mutex); + } + return ret; } @@ -766,11 +793,6 @@ static int Entropy_Condition(byte* output, word32 len, byte* noise, return ret; } -/* Mutex to prevent multiple callers requesting entropy operations at the - * same time. - */ -static wolfSSL_Mutex entropy_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(entropy_mutex); - /* Get entropy of specified strength. * * SP800-90b 2.3.1 - GetEntropy: An Interface to the Entropy Source diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 361f16d6319..28a3975090f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -66406,6 +66406,52 @@ static wc_test_ret_t mp_test_mont(mp_int* a, mp_int* m, mp_int* n, mp_int* r, WC } #endif +/* mp_cond_swap_ct resolves to sp_cond_swap_ct in SP-math builds, which is only + * compiled when HAVE_ECC && ECC_TIMING_RESISTANT && !WC_NO_CACHE_RESISTANT. + * Gate the test to match. */ +#if !defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_SP_MATH_ALL) + #define MP_TEST_COND_SWAP_AVAILABLE +#elif defined(HAVE_ECC) && defined(ECC_TIMING_RESISTANT) && \ + !defined(WC_NO_CACHE_RESISTANT) + #define MP_TEST_COND_SWAP_AVAILABLE +#endif + +#ifdef MP_TEST_COND_SWAP_AVAILABLE +static wc_test_ret_t mp_test_cond_swap(mp_int* a, mp_int* b) +{ + int ret; + int c; + + mp_zero(a); + mp_zero(b); + ret = mp_set_int(a, 0x12345678); + if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + ret = mp_set_int(b, 0x9abcdef0); + if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + c = (a->used > b->used) ? a->used : b->used; + + /* m == 0: no swap. */ + ret = mp_cond_swap_ct(a, b, c, 0); + if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + if (a->dp[0] != 0x12345678 || b->dp[0] != 0x9abcdef0) + return WC_TEST_RET_ENC_NC; + + /* m == 1: swap. */ + ret = mp_cond_swap_ct(a, b, c, 1); + if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + if (a->dp[0] != 0x9abcdef0 || b->dp[0] != 0x12345678) + return WC_TEST_RET_ENC_NC; + + /* m == 1 again: swap back. */ + ret = mp_cond_swap_ct(a, b, c, 1); + if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + if (a->dp[0] != 0x12345678 || b->dp[0] != 0x9abcdef0) + return WC_TEST_RET_ENC_NC; + + return 0; +} +#endif /* MP_TEST_COND_SWAP_AVAILABLE */ + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mp_test(void) { WC_RNG rng; @@ -66719,6 +66765,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mp_test(void) if ((ret = mp_test_mont(a, b, r1, r2, &rng)) != 0) goto done; #endif +#ifdef MP_TEST_COND_SWAP_AVAILABLE + if ((ret = mp_test_cond_swap(a, b)) != 0) + goto done; +#endif done: