diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 46aad8c45b1..b6071e7ed69 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -932,10 +932,20 @@ struct wc_swallow_the_semicolon * substantively implemented. */ #ifndef WC_SHA256_W_SIZE - #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) + #ifdef WC_SHA256_TSCRATCH_SIZE + /* The transform reads W as its full caller-owned scratch. */ + #define WC_SHA256_W_SIZE WC_SHA256_TSCRATCH_SIZE + #else /* old FIPS */ + #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) + #endif #endif #ifndef WC_SHA512_W_SIZE - #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) + #ifdef WC_SHA512_TSCRATCH_SIZE + #define WC_SHA512_W_SIZE \ + (WC_SHA512_TSCRATCH_SIZE + WC_SHA512_BLOCK_SIZE) + #else /* old FIPS */ + #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) + #endif #endif #define WC_LINUXKM_SHA2_IMPLEMENT(name, s_name, digest_size, block_size, \ diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c index a4167fc9c7b..7aeeceb83b4 100644 --- a/tests/api/test_aes.c +++ b/tests/api/test_aes.c @@ -14667,3 +14667,59 @@ int test_wc_AesEcb_RetCodeChecked(void) #endif /* WOLF_CRYPTO_CB && !NO_AES && HAVE_AES_ECB && WOLFSSL_AES_128 && * !WOLF_CRYPTO_CB_ONLY_AES && (WOLFSSL_AES_COUNTER || HAVE_AESGCM) */ + +/* Exercise the portable block primitive's real KEYUSAGE_E path after GCM + * setup. No callback or test-only failure hook is needed. */ +int test_wc_AesGcm_PrimitiveFailureCleanup(void) +{ + EXPECT_DECLS; +#if !defined(NO_AES) && defined(HAVE_AESGCM) && defined(WOLFSSL_AES_128) && \ + defined(WOLFSSL_NO_ASM) && !defined(WOLF_CRYPTO_CB_ONLY_AES) && \ + !defined(HAVE_SELFTEST) + Aes aes; + const byte key[16] = {0}; + const byte iv[12] = {0}; + const byte aad[16] = {0}; + const byte zeros[5 * WC_AES_BLOCK_SIZE] = {0}; + byte tag[WC_AES_BLOCK_SIZE]; + int ret; +#ifdef WOLFSSL_AESGCM_STREAM + byte in[17] = {0}; + byte out[17]; +#endif + + XMEMSET(&aes, 0, sizeof(aes)); + ExpectIntEQ(wc_AesInit(&aes, NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_AesGcmSetKey(&aes, key, sizeof(key)), 0); + XMEMSET(tag, 0xa5, sizeof(tag)); + aes.rounds = 0; + /* Empty plaintext reaches GHASH before the first AES block operation. */ + ret = wc_AesGcmEncrypt(&aes, NULL, NULL, 0, iv, sizeof(iv), tag, + sizeof(tag), aad, sizeof(aad)); + if (ret != WC_NO_ERR_TRACE(KEYUSAGE_E)) { + wc_AesFree(&aes); + return TEST_SKIPPED; /* backend does not use the portable primitive */ + } + ExpectBufEQ(tag, zeros, sizeof(tag)); +#ifdef WOLFSSL_AESGCM_STREAM + ExpectIntEQ(wc_AesGcmEncryptInit(&aes, key, sizeof(key), iv, sizeof(iv)), 0); + /* Retain fifteen bytes of keystream, then fail generating the next block. */ + ExpectIntEQ(wc_AesGcmEncryptUpdate(&aes, out, in, 1, NULL, 0), 0); + aes.rounds = 0; + ExpectIntEQ(wc_AesGcmEncryptUpdate(&aes, out, in, sizeof(in), NULL, 0), + WC_NO_ERR_TRACE(KEYUSAGE_E)); + ExpectBufEQ(aes.streamData, zeros, sizeof(zeros)); + ExpectIntEQ(aes.nonceSet, 0); + ExpectIntEQ(aes.over, 0); + /* A failed stream must require reinitialization. */ + ExpectIntLT(wc_AesGcmEncryptUpdate(&aes, out, in, 1, NULL, 0), 0); + ExpectIntEQ(wc_AesGcmEncryptInit(&aes, key, sizeof(key), iv, sizeof(iv)), 0); + ExpectIntEQ(wc_AesGcmEncryptUpdate(&aes, out, in, sizeof(in), NULL, 0), 0); + ExpectIntEQ(wc_AesGcmEncryptFinal(&aes, tag, sizeof(tag)), 0); +#endif + wc_AesFree(&aes); +#else + return TEST_SKIPPED; +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_aes.h b/tests/api/test_aes.h index e23560a39ad..9690eece664 100644 --- a/tests/api/test_aes.h +++ b/tests/api/test_aes.h @@ -63,6 +63,7 @@ int test_wc_AesGcmStream_MidStreamState(void); int test_wc_AesGcmStream_ReinitAfterFinal(void); int test_wc_AesGcmStream_BadAuthTag(void); int test_wc_AesGcmDecrypt_WipeOnAuthFail(void); +int test_wc_AesGcm_PrimitiveFailureCleanup(void); int test_wc_AesKeyWrapVectors(void); int test_wc_AesKeyWrapDecisionCoverage(void); int test_wc_AesGcmDecisionCoverage(void); @@ -241,6 +242,7 @@ int test_wc_CryptoCb_AesDirect(void); TEST_DECL_GROUP("aes", test_wc_AesGcmStream_ReinitAfterFinal), \ TEST_DECL_GROUP("aes", test_wc_AesGcmStream_BadAuthTag), \ TEST_DECL_GROUP("aes", test_wc_AesGcmDecrypt_WipeOnAuthFail), \ + TEST_DECL_GROUP("aes", test_wc_AesGcm_PrimitiveFailureCleanup), \ TEST_DECL_GROUP("aes", test_wc_AesKeyWrapVectors), \ TEST_DECL_GROUP("aes", test_wc_AesKeyWrapDecisionCoverage), \ TEST_DECL_GROUP("aes", test_wc_AesGcmDecisionCoverage), \ diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index 60717cdae5b..ac8287685e9 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -1419,6 +1419,25 @@ int test_wc_ecc_import_private_key(void) x963KeySz, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_ecc_import_private_key(NULL, privKeySz, x963Key, x963KeySz, &keyImp), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \ + !defined(HAVE_SELFTEST) && !defined(WOLFSSL_CRYPTOCELL) && \ + !defined(WOLFSSL_SE050) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \ + !defined(WOLFSSL_KCAPI_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) && \ + !(defined(WOLF_CRYPTO_CB_SETKEY) && defined(WOLF_CRYPTO_CB_FIND)) + /* Rejecting a private scalar must also revoke private-key capability. + * The device set-key path imports into a temporary key first, so a + * rejected scalar never touches this key and its type stays as it was. + * Use a fresh key: importing over a live one is not a supported path. */ + wc_ecc_free(&keyImp); + ExpectIntEQ(wc_ecc_init(&keyImp), 0); + XMEMSET(privKey, 0, privKeySz); + ExpectIntEQ(wc_ecc_import_private_key(privKey, privKeySz, x963Key, + x963KeySz, &keyImp), WC_NO_ERR_TRACE(ECC_PRIV_KEY_E)); + ExpectIntEQ(keyImp.type, ECC_PUBLICKEY); + ExpectIntEQ(wc_ecc_import_private_key(privKey, privKeySz, NULL, 0, + &keyImp), WC_NO_ERR_TRACE(ECC_PRIV_KEY_E)); + ExpectIntEQ(keyImp.type, 0); +#endif PRIVATE_KEY_LOCK(); DoExpectIntEQ(wc_FreeRng(&rng), 0); diff --git a/tests/api/test_frodokem.c b/tests/api/test_frodokem.c index 349eb2ccaab..c8754093a50 100644 --- a/tests/api/test_frodokem.c +++ b/tests/api/test_frodokem.c @@ -742,7 +742,7 @@ static const FrodoKemKat frodokem_kats[] = { FKAT_640_AES FKAT_976_AES FKAT_1344_AES FKAT_E640_SHAKE FKAT_E976_SHAKE FKAT_E1344_SHAKE FKAT_E640_AES FKAT_E976_AES FKAT_E1344_AES - { 0 } /* sentinel so the array is never empty */ + WC_STATIC_STRUCT_INITIALIZER /* sentinel so the array is never empty */ }; #define FRODOKEM_KAT_CNT \ diff --git a/tests/api/test_kdf.c b/tests/api/test_kdf.c index 68a09be92d7..86ce87f6d36 100644 --- a/tests/api/test_kdf.c +++ b/tests/api/test_kdf.c @@ -929,3 +929,89 @@ int test_wc_KdfFeatureCoverage(void) #endif /* !HAVE_FIPS && !HAVE_SELFTEST */ return EXPECT_RESULT(); } /* END test_wc_KdfFeatureCoverage */ + +/* RFC 4253 section 7.2 fixtures computed independently with Python hashlib: + * K = a5 * 32 (positive mpint with a leading zero), H = 39 * 32, + * session_id = 17 * 32, key ID = A. K1 = HASH(mpint(K)||H||A||session_id), + * Kn = HASH(mpint(K)||H||K1||...||K(n-1)). The 133-byte output exercises + * repeated Final/reuse and the partial last block for every supported hash. + */ +int test_wc_SshKdfReuse(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_WOLFSSH) && !defined(NO_HMAC) + static const struct { + byte hashId; + const char* expected; + } vectors[] = { +#if !defined(NO_SHA) + { WC_SHA, + "\x1c\x9f\x61\x4a\x3b\xa5\x27\x49\x39\xc0\x53\x1d\x22\xe5\xf3\xa2" + "\xe4\x07\xd3\x28\x19\xe0\xc7\xa4\x82\x83\x16\x15\x5c\x0c\x31\xe5" + "\x78\xe4\xe8\x24\x70\x0e\xf8\x66\xe2\x66\x9b\x3a\xac\x26\xa1\x2c" + "\xd4\x5e\xb7\x58\xeb\xbf\xaf\x6f\xb9\x09\xa2\x25\xe8\xf9\x3c\x1f" + "\x23\x3c\x08\xfc\x4e\x78\x5c\x35\x10\x51\x4c\x0a\x6e\x85\xf5\x94" + "\xcc\xaf\x3d\xf8\xb5\x61\x79\xd4\x3c\x9e\x3c\x18\xa0\x19\x55\x54" + "\xce\x85\x86\xbc\x6e\x74\x30\x4a\xf6\x00\x5a\x08\x15\x70\xeb\x35" + "\xb1\x20\xc4\xca\xd7\xc3\x66\xe9\xe0\x83\x38\x61\x34\x39\x36\x2a" + "\xda\xaa\x28\xbb\x22" + }, +#endif +#if !defined(NO_SHA256) + { WC_SHA256, + "\x34\xb2\x16\x40\xeb\x59\x97\x50\x70\x19\x97\x5c\x8c\xc0\x0c\xfb" + "\x1a\xef\x39\xcc\x7a\x25\x8a\x6c\x33\xb0\x7b\xe4\xe9\x0f\x81\xfa" + "\x20\xed\x94\xb1\xe1\x26\x97\x58\x1d\xe8\x94\x04\xb9\x7e\x52\x03" + "\x79\x8a\xf1\x91\x38\x62\x15\x65\xd2\x5e\x9e\xa7\x96\x70\x51\x75" + "\x3d\x8a\x11\x41\x77\xdd\x7a\x33\x32\x12\x78\x69\xcd\xfc\x69\x55" + "\xed\xa9\xdd\x87\xea\xef\xe1\x0b\x28\xb6\x71\x72\xdb\xc8\x9d\x08" + "\x45\xbc\x2d\x85\x9c\x4c\xa0\x33\x80\x99\xcc\x1f\x9f\x23\x82\x68" + "\x32\xbf\xad\x27\x1d\x0c\xfe\x43\xa4\xf9\xbc\xb6\xd6\xee\x6a\x70" + "\xf9\xdf\x8f\xa1\xe3" + }, +#endif +#if defined(WOLFSSL_SHA384) + { WC_SHA384, + "\x82\x6a\xb6\x8b\x6d\x81\x42\xa2\x85\x12\x32\x61\x8c\xaf\xf4\x3b" + "\xad\xad\x88\x2d\x7f\x9d\xea\x63\x75\x40\x66\xe9\x9f\xb3\x42\x1e" + "\x26\x30\x4e\xb2\x99\xda\xe3\x56\x2e\x5a\xde\x44\xc7\x8d\x3d\x59" + "\x45\xf9\xe1\x5c\xe8\x8b\x01\x55\xc9\x87\xb7\x95\x74\x32\x9b\xe8" + "\xcc\x12\x69\x25\xba\x88\x43\x5f\x27\x78\xc3\x35\x18\x6e\x3d\x32" + "\x36\x74\xa0\x44\xd8\x84\xb6\x2a\x1b\xdf\x01\x23\x7f\x62\x38\xb8" + "\xca\x9e\x55\x08\xf6\x3b\x60\xce\x1b\x2b\xe1\x0c\xdb\x15\x90\x54" + "\x16\x3b\x04\xbb\x71\x98\x6d\x20\xc9\x9e\xd6\x3b\xf2\xd1\x8f\xf4" + "\xbb\x0d\xaf\x6a\xf1" + }, +#endif +#if defined(WOLFSSL_SHA512) + { WC_SHA512, + "\xb9\xcb\x49\xf0\x5f\xac\x25\xca\x6d\x9a\xfa\x68\x87\x57\xa2\x17" + "\x2a\x61\xcc\x56\xea\x71\x0f\xef\x07\xac\xb1\x7e\x7e\xaf\x54\x8f" + "\x15\x3b\x12\x9f\xbd\x6f\x79\xc9\x89\x14\xe6\x40\x43\x07\x27\x01" + "\x93\x1d\xda\xc8\x4b\x8b\xeb\xdf\x6a\xa3\xf8\xed\x8a\x8d\x9d\x49" + "\xc4\x5d\x90\xdd\x56\xd3\x64\x83\x5a\x63\x47\x3a\x69\xf9\x11\xaf" + "\x50\x7f\x3b\x18\xbc\x37\xec\xf9\xdd\xad\x98\x80\xab\x58\xb5\xd3" + "\x72\x6e\x2c\xce\xae\x7f\xde\x5e\x4f\xb9\xf5\x57\x5a\x65\x2d\x88" + "\xb0\xc8\xc9\x74\x9e\x75\xec\x51\xb9\x23\x64\x26\xb8\x7c\x05\x74" + "\xa2\xe7\x3d\x30\xf8" + }, +#endif + { 0, NULL } + }; + static const word32 lengths[] = { 1, 32, 64, 96, 128, 133 }; + byte k[32], h[32], sessionId[32], key[133]; + word32 i, j; + + XMEMSET(k, 0xa5, sizeof(k)); + XMEMSET(h, 0x39, sizeof(h)); + XMEMSET(sessionId, 0x17, sizeof(sessionId)); + for (i = 0; vectors[i].expected != NULL; i++) { + for (j = 0; j < sizeof(lengths) / sizeof(lengths[0]); j++) { + ExpectIntEQ(wc_SSH_KDF(vectors[i].hashId, 'A', key, lengths[j], + k, sizeof(k), h, sizeof(h), sessionId, sizeof(sessionId)), 0); + ExpectBufEQ(key, vectors[i].expected, lengths[j]); + } + } +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_kdf.h b/tests/api/test_kdf.h index 3adf4ce0b6f..8ea13b43cca 100644 --- a/tests/api/test_kdf.h +++ b/tests/api/test_kdf.h @@ -26,9 +26,11 @@ int test_wc_KdfDecisionCoverage(void); int test_wc_KdfFeatureCoverage(void); +int test_wc_SshKdfReuse(void); #define TEST_KDF_DECLS \ TEST_DECL_GROUP("kdf", test_wc_KdfDecisionCoverage), \ - TEST_DECL_GROUP("kdf", test_wc_KdfFeatureCoverage) + TEST_DECL_GROUP("kdf", test_wc_KdfFeatureCoverage), \ + TEST_DECL_GROUP("kdf", test_wc_SshKdfReuse) #endif /* WOLFCRYPT_TEST_KDF_H */ diff --git a/tests/api/test_mlkem.c b/tests/api/test_mlkem.c index d2fc6df953d..ba335cb6393 100644 --- a/tests/api/test_mlkem.c +++ b/tests/api/test_mlkem.c @@ -5599,3 +5599,81 @@ int test_wc_mlkem_make_key_id_dev(void) #endif return EXPECT_RESULT(); } + +/* FIPS 203 3.3: retain the key and designated output, but no hash or PRF + * intermediates, when a complete ML-KEM operation returns. */ +int test_wc_mlkem_operation_cleanup(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_MLKEM) && defined(WC_MLKEM_HAVE_NATIVE) && \ + !defined(WOLFSSL_NO_ML_KEM) && !defined(PSOC6_HASH_SHA3) && \ + !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) + static const int types[] = { +#ifndef WOLFSSL_NO_ML_KEM_512 + WC_ML_KEM_512, +#endif +#ifndef WOLFSSL_NO_ML_KEM_768 + WC_ML_KEM_768, +#endif +#ifndef WOLFSSL_NO_ML_KEM_1024 + WC_ML_KEM_1024, +#endif + -1 + }; + static const byte zero[sizeof(wc_Sha3)] = {0}; + MlKemKey* key = NULL; + byte seeds[WC_ML_KEM_MAKEKEY_RAND_SZ]; + byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE]; + byte ss[WC_ML_KEM_SS_SZ]; + byte recovered[WC_ML_KEM_SS_SZ]; + word32 ctSz = 0; + int t, repeat, op, h; + + key = (MlKemKey*)XMALLOC(sizeof(*key), NULL, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(key); + XMEMSET(seeds, 0x71, sizeof(seeds)); + PRIVATE_KEY_UNLOCK(); + for (t = 0; EXPECT_SUCCESS() && types[t] != -1; t++) { + ExpectIntEQ(wc_MlKemKey_Init(key, types[t], NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_MlKemKey_CipherTextSize(key, &ctSz), 0); + for (repeat = 0; EXPECT_SUCCESS() && repeat < 2; repeat++) { + for (op = 0; EXPECT_SUCCESS() && op < 5; op++) { + if (op == 0) { + ExpectIntEQ(wc_MlKemKey_MakeKeyWithRandom(key, seeds, + sizeof(seeds)), SEED_OK); + } + else if (op == 1) { + ExpectIntEQ(wc_MlKemKey_EncapsulateWithRandom(key, ct, ss, + seeds, WC_ML_KEM_ENC_RAND_SZ), SEED_OK); + } + else if (op == 2) { + ExpectIntEQ(wc_MlKemKey_Decapsulate(key, recovered, ct, + ctSz), 0); + ExpectBufEQ(ss, recovered, sizeof(ss)); + } + else if (op == 3) { + ExpectIntEQ(wc_MlKemKey_Decapsulate(key, recovered, ct, + ctSz - 1), WC_NO_ERR_TRACE(BUFFER_E)); + } + else { + ct[32] ^= 1; + ExpectIntEQ(wc_MlKemKey_Decapsulate(key, recovered, ct, + ctSz), 0); + } + for (h = 0; h < 2; h++) { + const wc_Sha3* hash = h == 0 ? &key->hash : &key->prf; + ExpectBufEQ(hash->s, zero, sizeof(hash->s)); + ExpectBufEQ(hash->t, zero, sizeof(hash->t)); + ExpectIntEQ(hash->i, 0); + } + } + } + wc_MlKemKey_Free(key); + } + PRIVATE_KEY_LOCK(); + XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_mlkem.h b/tests/api/test_mlkem.h index 41e19de21f7..5c42ecbca7c 100644 --- a/tests/api/test_mlkem.h +++ b/tests/api/test_mlkem.h @@ -45,6 +45,7 @@ int test_wc_mlkem_decode_pubkey_refused_save(void); int test_wc_mlkem_decode_reused_key(void); int test_wc_mlkem_decode_alloc_fail(void); int test_wc_mlkem_make_key_id_dev(void); +int test_wc_mlkem_operation_cleanup(void); #define TEST_MLKEM_DECLS \ TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_kats), \ @@ -67,6 +68,7 @@ int test_wc_mlkem_make_key_id_dev(void); TEST_DECL_GROUP("mlkem", test_wc_mlkem_decode_pubkey_refused_save), \ TEST_DECL_GROUP("mlkem", test_wc_mlkem_decode_reused_key), \ TEST_DECL_GROUP("mlkem", test_wc_mlkem_decode_alloc_fail), \ - TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_id_dev) + TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_id_dev), \ + TEST_DECL_GROUP("mlkem", test_wc_mlkem_operation_cleanup) #endif /* WOLFCRYPT_TEST_MLKEM_H */ diff --git a/tests/api/test_rsa.c b/tests/api/test_rsa.c index 9ac9b5dae94..6c3e803571c 100644 --- a/tests/api/test_rsa.c +++ b/tests/api/test_rsa.c @@ -2630,3 +2630,87 @@ int test_wc_MakeRsaKey_id_dev(void) #endif return EXPECT_RESULT(); } + + +#if defined(WOLF_CRYPTO_CB) && !defined(NO_RSA) && \ + !defined(WC_NO_RSA_OAEP) && !defined(WOLFSSL_NO_RSA_SW_PAD) && \ + defined(WOLFSSL_KEY_GEN) && !defined(WC_NO_RNG) && !defined(NO_SHA256) && \ + !defined(WOLFSSL_RSA_VERIFY_ONLY) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \ + !defined(HAVE_FIPS) +/* Fail RSAEP only after software OAEP has placed EM in the output buffer. */ +static int rsa_oaep_fail_cb(int devId, wc_CryptoInfo* info, void* ctx) +{ + (void)devId; + if (info->algo_type == WC_ALGO_TYPE_PK && + info->pk.type == WC_PK_TYPE_RSA && + info->pk.rsa.type == RSA_PUBLIC_ENCRYPT) { + (*(int*)ctx)++; + return WC_HW_E; + } + return CRYPTOCB_UNAVAILABLE; +} +#endif + +int test_wc_RsaOaep_EncryptFailureWipe(void) +{ + EXPECT_DECLS; +#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) && !defined(WC_NO_RNG) && \ + !defined(WC_NO_RSA_OAEP) && !defined(WOLFSSL_NO_RSA_SW_PAD) && \ + !defined(NO_SHA256) && !defined(WOLFSSL_RSA_VERIFY_ONLY) && \ + !defined(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST) + RsaKey key; + WC_RNG rng; + byte out[TEST_RSA_BYTES + 8]; + const byte msg[16] = {1}; + int i; +#ifdef WOLF_CRYPTO_CB + int calls = 0; + int registered = 0; + const int devId = 23; +#endif + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + ExpectIntEQ(wc_InitRsaKey(&key, HEAP_HINT), 0); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(MAKE_RSA_KEY(&key, TEST_RSA_BITS, WC_RSA_EXPONENT, &rng), 0); + XMEMSET(out, 0xa5, sizeof(out)); + /* Invalid MGF fails after the message has been copied into DB. */ + ExpectIntLT(wc_RsaPublicEncrypt_ex(msg, sizeof(msg), out, sizeof(out), + &key, &rng, WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA256, -1, NULL, 0), 0); + for (i = 0; i < TEST_RSA_BYTES; i++) + ExpectIntEQ(out[i], 0); + for (; i < (int)sizeof(out); i++) + ExpectIntEQ(out[i], 0xa5); + +#ifdef WOLF_CRYPTO_CB + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, rsa_oaep_fail_cb, &calls), 0); + if (EXPECT_SUCCESS()) { + registered = 1; + key.devId = devId; + XMEMSET(out, 0xa5, sizeof(out)); + ExpectIntEQ(wc_RsaPublicEncrypt_ex(msg, sizeof(msg), out, sizeof(out), + &key, &rng, WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, + NULL, 0), WC_NO_ERR_TRACE(WC_HW_E)); + ExpectIntEQ(calls, 1); + for (i = 0; i < TEST_RSA_BYTES; i++) + ExpectIntEQ(out[i], 0); + for (; i < (int)sizeof(out); i++) + ExpectIntEQ(out[i], 0xa5); + key.devId = INVALID_DEVID; + } + if (registered) + wc_CryptoCb_UnRegisterDevice(devId); +#endif + /* Cleanup must also reset the operation state so the key remains usable. */ + ExpectIntEQ(wc_RsaPublicEncrypt_ex(msg, sizeof(msg), out, sizeof(out), + &key, &rng, WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, NULL, 0), + TEST_RSA_BYTES); + DoExpectIntEQ(wc_FreeRsaKey(&key), 0); + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#else + return TEST_SKIPPED; +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_rsa.h b/tests/api/test_rsa.h index d3fc75f3385..2c950c30fa3 100644 --- a/tests/api/test_rsa.h +++ b/tests/api/test_rsa.h @@ -41,6 +41,7 @@ int test_wc_RsaKeyToDer(void); int test_wc_RsaKeyToPublicDer(void); int test_wc_RsaPublicEncryptDecrypt(void); int test_wc_RsaPublicEncryptDecrypt_ex(void); +int test_wc_RsaOaep_EncryptFailureWipe(void); int test_wc_RsaEncryptSize(void); int test_wc_RsaSSL_SignVerify(void); int test_wc_RsaFlattenPublicKey(void); @@ -71,6 +72,7 @@ int test_wc_MakeRsaKey_id_dev(void); TEST_DECL_GROUP("rsa", test_wc_RsaKeyToPublicDer), \ TEST_DECL_GROUP("rsa", test_wc_RsaPublicEncryptDecrypt), \ TEST_DECL_GROUP("rsa", test_wc_RsaPublicEncryptDecrypt_ex), \ + TEST_DECL_GROUP("rsa", test_wc_RsaOaep_EncryptFailureWipe), \ TEST_DECL_GROUP("rsa", test_wc_RsaEncryptSize), \ TEST_DECL_GROUP("rsa", test_wc_RsaSSL_SignVerify), \ TEST_DECL_GROUP("rsa", test_wc_RsaFlattenPublicKey), \ diff --git a/tests/api/test_slhdsa.c b/tests/api/test_slhdsa.c index 336c02ab9bb..45d2e0a40ad 100644 --- a/tests/api/test_slhdsa.c +++ b/tests/api/test_slhdsa.c @@ -632,6 +632,8 @@ int test_wc_slhdsa_make_key(void) byte sk_seed[TEST_SLHDSA_DEFAULT_SEED_LEN]; byte sk_prf[TEST_SLHDSA_DEFAULT_SEED_LEN]; byte pk_seed[TEST_SLHDSA_DEFAULT_SEED_LEN]; + byte expected[TEST_SLHDSA_DEFAULT_PRIV_LEN]; + word32 n = TEST_SLHDSA_DEFAULT_SEED_LEN; XMEMSET(sk_seed, 0x01, sizeof(sk_seed)); XMEMSET(sk_prf, 0x02, sizeof(sk_prf)); @@ -661,6 +663,30 @@ int test_wc_slhdsa_make_key(void) ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, sk_seed, sizeof(sk_seed), sk_prf, sizeof(sk_prf), pk_seed, sizeof(pk_seed)), SEED_OK); + + /* Replacement must stage aliased seeds before erasing the old key. */ + XMEMCPY(expected, key.sk, sizeof(expected)); + ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, key.sk, n, + key.sk + n, n, key.sk + 2 * n, n), SEED_OK); + ExpectBufEQ(key.sk, expected, sizeof(expected)); + + /* The first seed may alias even when the other two are new. */ + XMEMSET(sk_prf, 0x04, sizeof(sk_prf)); + XMEMSET(pk_seed, 0x05, sizeof(pk_seed)); + ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, key.sk, n, + sk_prf, n, pk_seed, n), SEED_OK); + ExpectBufEQ(key.sk, sk_seed, n); + ExpectBufEQ(key.sk + n, sk_prf, n); + ExpectBufEQ(key.sk + 2 * n, pk_seed, n); + ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0); + + /* Reordered in-key aliases must all be read before any write. */ + ExpectIntEQ(wc_SlhDsaKey_MakeKeyWithRandom(&key, key.sk + n, n, + key.sk + 2 * n, n, key.sk, n), SEED_OK); + ExpectBufEQ(key.sk, sk_prf, n); + ExpectBufEQ(key.sk + n, pk_seed, n); + ExpectBufEQ(key.sk + 2 * n, sk_seed, n); + ExpectIntEQ(wc_SlhDsaKey_CheckKey(&key), 0); wc_SlhDsaKey_Free(&key); } #endif @@ -928,6 +954,9 @@ int test_wc_slhdsa_sign_vfy(void) byte* sig = NULL; word32 sigLen; byte ctx[10]; +#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F) + const byte zeroDigest[WC_SHA256_DIGEST_SIZE] = {0}; +#endif sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER); ExpectNotNull(sig); @@ -1045,13 +1074,16 @@ int test_wc_slhdsa_sign_vfy(void) ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_128F, NULL, INVALID_DEVID), 0); ExpectIntEQ(wc_SlhDsaKey_MakeKey(&key, &rng), 0); + ExpectBufEQ(key.sha2Digest, zeroDigest, sizeof(zeroDigest)); sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_Sign(&key, ctx, sizeof(ctx), msg, sizeof(msg), sig, &sigLen, &rng), 0); + ExpectBufEQ(key.sha2Digest, zeroDigest, sizeof(zeroDigest)); ExpectIntEQ(sigLen, WC_SLHDSA_SHA2_128F_SIG_LEN); ExpectIntEQ(wc_SlhDsaKey_Verify(&key, ctx, sizeof(ctx), msg, sizeof(msg), sig, sigLen), 0); wc_SlhDsaKey_Free(&key); + ExpectBufEQ(key.sha2Digest, zeroDigest, sizeof(zeroDigest)); #endif #ifdef WOLFSSL_SLHDSA_PARAM_SHA2_192S ExpectIntEQ(wc_SlhDsaKey_Init(&key, SLHDSA_SHA2_192S, NULL, INVALID_DEVID), @@ -1794,6 +1826,20 @@ int test_wc_slhdsa_export_import(void) ExpectIntEQ(wc_SlhDsaKey_Init(&key2, TEST_SLHDSA_DEFAULT_PARAM, NULL, INVALID_DEVID), 0); ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, privKey, privKeyLen), 0); + /* Exact and partial aliasing of a replacement encoding must survive the + * old-key wipe. The fixed key storage has room for the shifted input. */ + ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, key2.sk, privKeyLen), 0); + ExpectBufEQ(key2.sk, privKey, privKeyLen); + if (privKeyLen < sizeof(key2.sk)) { + XMEMMOVE(key2.sk + 1, key2.sk, privKeyLen); + ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, key2.sk + 1, privKeyLen), + 0); + ExpectBufEQ(key2.sk, privKey, privKeyLen); + } + /* Validation errors leave the existing key usable. */ + ExpectIntEQ(wc_SlhDsaKey_ImportPrivate(&key2, privKey, privKeyLen - 1), + WC_NO_ERR_TRACE(BAD_LENGTH_E)); + ExpectBufEQ(key2.sk, privKey, privKeyLen); /* Sign with imported key. */ sigLen = WC_SLHDSA_MAX_SIG_LEN; ExpectIntEQ(wc_SlhDsaKey_Sign(&key2, ctx, sizeof(ctx), msg, sizeof(msg), diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 254d595fbbe..893d9871f06 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -1256,6 +1256,7 @@ static WC_INLINE void wc_Stm32_CrypAesBlock(const byte* in, byte* out) if (AES_set_encrypt_key_AESNI(userKey,bits,temp_key) == WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { + ForceZero(temp_key, sizeof(Aes)); WC_FREE_VAR_EX(temp_key, aes->heap, DYNAMIC_TYPE_AES); return BAD_FUNC_ARG; } @@ -1289,6 +1290,9 @@ static WC_INLINE void wc_Stm32_CrypAesBlock(const byte* in, byte* out) Key_Schedule[0] = Temp_Key_Schedule[nr]; + /* temp_key holds the expanded key schedule + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(temp_key, sizeof(Aes)); WC_FREE_VAR_EX(temp_key, aes->heap, DYNAMIC_TYPE_AES); return 0; @@ -3962,6 +3966,7 @@ WC_ALL_ARGS_NOT_NULL static WARN_UNUSED_RESULT int wc_AesEncrypt( AES_ECB_encrypt_AESNI(tmp_align, tmp_align, WC_AES_BLOCK_SIZE, (byte*)aes->key, (int)aes->rounds); XMEMCPY(outBlock, tmp_align, WC_AES_BLOCK_SIZE); + ForceZero(tmp, WC_AES_BLOCK_SIZE + AESNI_ALIGN); XFREE(tmp, aes->heap, DYNAMIC_TYPE_TMP_BUFFER); return 0; #else @@ -5959,7 +5964,13 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) aes->keyInstalled = 0; ret = AesSetKeyLocal_body(aes, userKey, keylen, iv, dir, checkKeyLen, &devOwned); - aes->keyInstalled = (ret == 0) ? 1 : 0; + if (ret == 0) { + aes->keyInstalled = 1; + } + else { + /* Erase the software key schedule after a failed setup. */ + ForceZero(aes->key, sizeof(aes->key)); + } #ifndef WOLF_CRYPTO_CB_ONLY_AES /* A claimed key runs in software only if the callback built a * schedule for it. */ @@ -8886,6 +8897,14 @@ static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) #ifdef HAVE_AESGCM +typedef union GcmScratch { + byte b[2 * WC_AES_BLOCK_SIZE]; + word32 w32[2 * WC_AES_BLOCK_SIZE / sizeof(word32)]; +#ifdef WORD64_AVAILABLE + word64 w64[2 * WC_AES_BLOCK_SIZE / sizeof(word64)]; +#endif +} GcmScratch; + #ifdef WOLFSSL_AESGCM_STREAM /* Access initialization counter data. */ #define AES_INITCTR(aes) ((aes)->streamData + 0 * WC_AES_BLOCK_SIZE) @@ -8897,6 +8916,7 @@ static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) #define AES_LASTGBLOCK(aes) ((aes)->streamData + 3 * WC_AES_BLOCK_SIZE) /* Access last encrypted block. */ #define AES_LASTBLOCK(aes) ((aes)->streamData + 4 * WC_AES_BLOCK_SIZE) + #define AES_STREAM_DATA_SZ (5 * WC_AES_BLOCK_SIZE) #define GHASH_ONE_BLOCK GHASH_ONE_BLOCK_SW #endif @@ -9253,9 +9273,7 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) } #endif #ifdef WOLFSSL_AESGCM_STREAM - if (ret == 0) { - aes->gcmKeySet = 1; - } + aes->gcmKeySet = (ret == 0) ? 1 : 0; #endif #if defined(WOLFSSL_SECO_CAAM) if (aes->devId == WOLFSSL_SECO_DEVID) { @@ -9424,6 +9442,13 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) wc_MemZero_Check(local, sizeof(local)); #endif #endif + if (ret != 0) { + /* A failed H derivation must not leave the previous key's GHASH + * state usable with the newly installed AES key. */ + WC_AES_GCM_UNKEY(aes); + aes->keyInstalled = 0; + ForceZero(&aes->gcm, sizeof(aes->gcm)); + } return ret; } @@ -9583,6 +9608,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, /* Copy the result into s. */ XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); } #ifdef WOLFSSL_AESGCM_STREAM @@ -9605,10 +9631,14 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #define HAVE_GHASH #elif defined(GCM_SMALL) -static void GMULT(byte* X, byte* Y) -{ - byte Z[WC_AES_BLOCK_SIZE]; - byte V[WC_AES_BLOCK_SIZE]; +#define GCM_C_GMULT +/* C multiplication scratch is owned by the surrounding GHASH operation. + * Reuse it for every block and erase once when that operation returns. Keeping + * it off Gcm also preserves concurrent read-only use of a keyed Gcm object. */ +static void GMULT(byte* X, byte* Y, GcmScratch* WC_RESTRICT ws) +{ + byte* Z = ws->b; + byte* V = ws->b + WC_AES_BLOCK_SIZE; int i, j; XMEMSET(Z, 0, WC_AES_BLOCK_SIZE); @@ -9633,6 +9663,9 @@ static void GMULT(byte* X, byte* Y) void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 cSz, byte* s, word32 sSz) { +#ifdef GCM_C_GMULT + GcmScratch gmultScratch; +#endif ALIGN_GCM_TAG byte x[WC_AES_BLOCK_SIZE]; byte scratch[WC_AES_BLOCK_SIZE]; word32 blocks, partial; @@ -9640,6 +9673,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, h = gcm->H; XMEMSET(x, 0, WC_AES_BLOCK_SIZE); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("GHASH x", x, sizeof(x)); +#endif /* Hash in A, the Additional Authentication Data */ if (aSz != 0 && a != NULL) { @@ -9647,14 +9683,14 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, partial = aSz % WC_AES_BLOCK_SIZE; while (blocks--) { xorbuf(x, a, WC_AES_BLOCK_SIZE); - GMULT(x, h); + GMULT(x, h, &gmultScratch); a += WC_AES_BLOCK_SIZE; } if (partial != 0) { XMEMSET(scratch, 0, WC_AES_BLOCK_SIZE); XMEMCPY(scratch, a, partial); xorbuf(x, scratch, WC_AES_BLOCK_SIZE); - GMULT(x, h); + GMULT(x, h, &gmultScratch); } } @@ -9664,14 +9700,14 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, partial = cSz % WC_AES_BLOCK_SIZE; while (blocks--) { xorbuf(x, c, WC_AES_BLOCK_SIZE); - GMULT(x, h); + GMULT(x, h, &gmultScratch); c += WC_AES_BLOCK_SIZE; } if (partial != 0) { XMEMSET(scratch, 0, WC_AES_BLOCK_SIZE); XMEMCPY(scratch, c, partial); xorbuf(x, scratch, WC_AES_BLOCK_SIZE); - GMULT(x, h); + GMULT(x, h, &gmultScratch); } } @@ -9679,10 +9715,17 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, FlattenSzInBits(&scratch[0], aSz); FlattenSzInBits(&scratch[8], cSz); xorbuf(x, scratch, WC_AES_BLOCK_SIZE); - GMULT(x, h); + GMULT(x, h, &gmultScratch); /* Copy the result into s. */ XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(x, sizeof(x)); +#endif } #ifdef WOLFSSL_AESGCM_STREAM @@ -9699,11 +9742,11 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, * @param [in, out] aes AES GCM object. * @param [in] block Block of AAD or cipher text. */ -#define GHASH_ONE_BLOCK_SW(aes, block) \ - do { \ - xorbuf(AES_TAG(aes), block, WC_AES_BLOCK_SIZE); \ - GMULT(AES_TAG(aes), (aes)->gcm.H); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + xorbuf(AES_TAG(aes), block, WC_AES_BLOCK_SIZE); \ + GMULT(AES_TAG(aes), (aes)->gcm.H, &gmultScratch); \ + } \ while (0) #endif /* WOLFSSL_AESGCM_STREAM */ @@ -9737,6 +9780,8 @@ static WC_MAYBE_UNUSED void GCM_gmult_len_armasm_C( len -= AES_BLOCK_SIZE; a += AES_BLOCK_SIZE; } + ForceZero(Z, sizeof(Z)); + ForceZero(V, sizeof(V)); } #define GCM_GMULT_LEN(gcm, x, a, len) \ @@ -9771,6 +9816,8 @@ static void GCM_gmult_len_armasm_C( len -= AES_BLOCK_SIZE; a += AES_BLOCK_SIZE; } + ForceZero(Z, sizeof(Z)); + ForceZero(V, sizeof(V)); } #define GCM_GMULT_LEN(gcm, x, a, len) \ @@ -9795,6 +9842,7 @@ static void GCM_gmult_len_armasm_C( #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len(x, (const byte**)((gcm)->M0), a, len) #else +#define GCM_C_GMULT ALIGN16 static const byte R[256][2] = { {0x00, 0x00}, {0x01, 0xc2}, {0x03, 0x84}, {0x02, 0x46}, {0x07, 0x08}, {0x06, 0xca}, {0x04, 0x8c}, {0x05, 0x4e}, @@ -9862,14 +9910,18 @@ ALIGN16 static const byte R[256][2] = { {0xbc, 0xf8}, {0xbd, 0x3a}, {0xbf, 0x7c}, {0xbe, 0xbe} }; -static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE]) +/* C multiplication scratch is owned by the surrounding GHASH operation. + * Reuse it for every block and erase once when that operation returns. Keeping + * it off Gcm also preserves concurrent read-only use of a keyed Gcm object. */ +static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE], + GcmScratch* WC_RESTRICT ws) { #if !defined(WORD64_AVAILABLE) || defined(BIG_ENDIAN_ORDER) int i, j; - byte Z[WC_AES_BLOCK_SIZE]; + byte* Z = ws->b; byte a; - XMEMSET(Z, 0, sizeof(Z)); + XMEMSET(Z, 0, WC_AES_BLOCK_SIZE); for (i = 15; i > 0; i--) { xorbuf(Z, m[x[i]], WC_AES_BLOCK_SIZE); @@ -9887,7 +9939,7 @@ static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE]) XMEMCPY(x, Z, WC_AES_BLOCK_SIZE); #elif defined(WC_32BIT_CPU) #ifndef WOLFSSL_USE_ALIGN - byte Z[WC_AES_BLOCK_SIZE + WC_AES_BLOCK_SIZE]; + byte* Z = ws->b; byte a; word32* pZ; word32* pm; @@ -9919,7 +9971,7 @@ static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE]) px[0] = pZ[0] ^ pm[0]; px[1] = pZ[1] ^ pm[1]; px[2] = pZ[2] ^ pm[2]; px[3] = pZ[3] ^ pm[3]; #else - byte Z[WC_AES_BLOCK_SIZE + WC_AES_BLOCK_SIZE]; + byte* Z = ws->b; byte a; int i; @@ -9937,7 +9989,7 @@ static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE]) XMEMCPY(x, Z + 1, WC_AES_BLOCK_SIZE); #endif #else - byte Z[WC_AES_BLOCK_SIZE + WC_AES_BLOCK_SIZE]; + byte* Z = ws->b; byte a; word64* pZ; word64* pm; @@ -9975,12 +10027,18 @@ static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE]) static void GCM_gmult_len_table_C(byte* x, byte m[256][WC_AES_BLOCK_SIZE], const unsigned char* a, unsigned long len) { +#ifdef GCM_C_GMULT + GcmScratch gmultScratch; +#endif while (len >= WC_AES_BLOCK_SIZE) { xorbuf(x, a, WC_AES_BLOCK_SIZE); - GMULT(x, m); + GMULT(x, m, &gmultScratch); len -= WC_AES_BLOCK_SIZE; a += WC_AES_BLOCK_SIZE; } +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif } #define GCM_GMULT_LEN(gcm, x, a, len) \ @@ -9990,11 +10048,17 @@ static void GCM_gmult_len_table_C(byte* x, byte m[256][WC_AES_BLOCK_SIZE], void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 cSz, byte* s, word32 sSz) { +#if defined(GCM_C_GMULT) && !defined(GCM_GMULT_LEN) + GcmScratch gmultScratch; +#endif ALIGN_GCM_TAG byte x[WC_AES_BLOCK_SIZE]; byte scratch[WC_AES_BLOCK_SIZE]; word32 blocks, partial; XMEMSET(x, 0, WC_AES_BLOCK_SIZE); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("GHASH x", x, sizeof(x)); +#endif /* Hash in A, the Additional Authentication Data */ if (aSz != 0 && a != NULL) { @@ -10013,14 +10077,14 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #else while (blocks--) { xorbuf(x, a, WC_AES_BLOCK_SIZE); - GMULT(x, gcm->M0); + GMULT(x, gcm->M0, &gmultScratch); a += WC_AES_BLOCK_SIZE; } if (partial != 0) { XMEMSET(scratch, 0, WC_AES_BLOCK_SIZE); XMEMCPY(scratch, a, partial); xorbuf(x, scratch, WC_AES_BLOCK_SIZE); - GMULT(x, gcm->M0); + GMULT(x, gcm->M0, &gmultScratch); } #endif } @@ -10042,14 +10106,14 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #else while (blocks--) { xorbuf(x, c, WC_AES_BLOCK_SIZE); - GMULT(x, gcm->M0); + GMULT(x, gcm->M0, &gmultScratch); c += WC_AES_BLOCK_SIZE; } if (partial != 0) { XMEMSET(scratch, 0, WC_AES_BLOCK_SIZE); XMEMCPY(scratch, c, partial); xorbuf(x, scratch, WC_AES_BLOCK_SIZE); - GMULT(x, gcm->M0); + GMULT(x, gcm->M0, &gmultScratch); } #endif } @@ -10061,11 +10125,18 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, GCM_GMULT_LEN(gcm, x, scratch, WC_AES_BLOCK_SIZE); #else xorbuf(x, scratch, WC_AES_BLOCK_SIZE); - GMULT(x, gcm->M0); + GMULT(x, gcm->M0, &gmultScratch); #endif /* Copy the result into s. */ XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); +#if defined(GCM_C_GMULT) && !defined(GCM_GMULT_LEN) + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(x, sizeof(x)); +#endif } #ifdef WOLFSSL_AESGCM_STREAM @@ -10075,7 +10146,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, */ #define GHASH_INIT_EXTRA(aes) WC_DO_NOTHING -#ifdef GCM_GMULT_LEN +#if defined(GCM_GMULT_LEN) && !defined(WOLFSSL_ARM32_AES_DISPATCH) /* GHASH one block of data. * * Defer to the length-based implementation with a length of one block - it @@ -10094,11 +10165,11 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, * @param [in, out] aes AES GCM object. * @param [in] block Block of AAD or cipher text. */ -#define GHASH_ONE_BLOCK_SW(aes, block) \ - do { \ - xorbuf(AES_TAG(aes), block, WC_AES_BLOCK_SIZE); \ - GMULT(AES_TAG(aes), aes->gcm.M0); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + xorbuf(AES_TAG(aes), block, WC_AES_BLOCK_SIZE); \ + GMULT(AES_TAG(aes), aes->gcm.M0, &gmultScratch); \ + } \ while (0) #endif #endif /* WOLFSSL_AESGCM_STREAM */ @@ -10568,6 +10639,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, /* Copy the result into s. */ XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); } #ifdef WOLFSSL_AESGCM_STREAM @@ -10607,12 +10679,17 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #elif defined(WORD64_AVAILABLE) && !defined(GCM_WORD32) #if !defined(FREESCALE_LTC_AES_GCM) -static void GMULT(word64* X, word64* Y) -{ - word64 Z[2] = {0,0}; - word64 V[2]; +#define GCM_C_GMULT +/* C multiplication scratch is owned by the surrounding GHASH operation. + * Reuse it for every block and erase once when that operation returns. Keeping + * it off Gcm also preserves concurrent read-only use of a keyed Gcm object. */ +static void GMULT(word64* X, word64* Y, GcmScratch* WC_RESTRICT ws) +{ + word64* Z = ws->w64; + word64* V = ws->w64 + 2; int i, j; word64 v1; + XMEMSET(Z, 0, WC_AES_BLOCK_SIZE); V[0] = X[0]; V[1] = X[1]; for (i = 0; i < 2; i++) @@ -10647,10 +10724,17 @@ static void GMULT(word64* X, word64* Y) void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 cSz, byte* s, word32 sSz) { +#ifdef GCM_C_GMULT + GcmScratch gmultScratch; +#endif word64 x[2] = {0,0}; word32 blocks, partial; word64 bigH[2]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("GHASH x", x, sizeof(x)); + wc_MemZero_Add("GHASH bigH", bigH, sizeof(bigH)); +#endif XMEMCPY(bigH, gcm->H, WC_AES_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords64(bigH, bigH, WC_AES_BLOCK_SIZE); @@ -10668,7 +10752,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #endif x[0] ^= bigA[0]; x[1] ^= bigA[1]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); a += WC_AES_BLOCK_SIZE; } if (partial != 0) { @@ -10679,7 +10763,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #endif x[0] ^= bigA[0]; x[1] ^= bigA[1]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); } #ifdef OPENSSL_EXTRA /* store AAD partial tag for next call */ @@ -10709,7 +10793,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #endif x[0] ^= bigC[0]; x[1] ^= bigC[1]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); c += WC_AES_BLOCK_SIZE; } if (partial != 0) { @@ -10720,7 +10804,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #endif x[0] ^= bigC[0]; x[1] ^= bigC[1]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); } } @@ -10738,12 +10822,22 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[0] ^= len[0]; x[1] ^= len[1]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); } #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords64(x, x, WC_AES_BLOCK_SIZE); #endif XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); + /* bigH is a copy of the hash subkey H. */ + ForceZero(bigH, sizeof(bigH)); +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(x, sizeof(x)); + wc_MemZero_Check(bigH, sizeof(bigH)); +#endif } #endif /* !FREESCALE_LTC_AES_GCM */ @@ -10765,17 +10859,17 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, * @param [in, out] aes AES GCM object. * @param [in] block Block of AAD or cipher text. */ -#define GHASH_ONE_BLOCK_SW(aes, block) \ - do { \ - word64* x = (word64*)AES_TAG(aes); \ - word64* h = (word64*)aes->gcm.H; \ - word64 block64[2]; \ - XMEMCPY(block64, block, WC_AES_BLOCK_SIZE); \ - ByteReverseWords64(block64, block64, WC_AES_BLOCK_SIZE); \ - x[0] ^= block64[0]; \ - x[1] ^= block64[1]; \ - GMULT(x, h); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + word64* x = (word64*)AES_TAG(aes); \ + word64* h = (word64*)aes->gcm.H; \ + word64 block64[2]; \ + XMEMCPY(block64, block, WC_AES_BLOCK_SIZE); \ + ByteReverseWords64(block64, block64, WC_AES_BLOCK_SIZE); \ + x[0] ^= block64[0]; \ + x[1] ^= block64[1]; \ + GMULT(x, h, &gmultScratch); \ + } \ while (0) #ifdef OPENSSL_EXTRA @@ -10799,7 +10893,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, \ x[0] ^= len[0]; \ x[1] ^= len[1]; \ - GMULT(x, h); \ + GMULT(x, h, &gmultScratch); \ ByteReverseWords64(x, x, WC_AES_BLOCK_SIZE); \ } \ while (0) @@ -10822,7 +10916,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, \ x[0] ^= len[0]; \ x[1] ^= len[1]; \ - GMULT(x, h); \ + GMULT(x, h, &gmultScratch); \ ByteReverseWords64(x, x, WC_AES_BLOCK_SIZE); \ } \ while (0) @@ -10843,16 +10937,16 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, * @param [in, out] aes AES GCM object. * @param [in] block Block of AAD or cipher text. */ -#define GHASH_ONE_BLOCK_SW(aes, block) \ - do { \ - word64* x = (word64*)AES_TAG(aes); \ - word64* h = (word64*)aes->gcm.H; \ - word64 block64[2]; \ - XMEMCPY(block64, block, WC_AES_BLOCK_SIZE); \ - x[0] ^= block64[0]; \ - x[1] ^= block64[1]; \ - GMULT(x, h); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + word64* x = (word64*)AES_TAG(aes); \ + word64* h = (word64*)aes->gcm.H; \ + word64 block64[2]; \ + XMEMCPY(block64, block, WC_AES_BLOCK_SIZE); \ + x[0] ^= block64[0]; \ + x[1] ^= block64[1]; \ + GMULT(x, h, &gmultScratch); \ + } \ while (0) #ifdef OPENSSL_EXTRA @@ -10876,7 +10970,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, \ x[0] ^= len[0]; \ x[1] ^= len[1]; \ - GMULT(x, h); \ + GMULT(x, h, &gmultScratch); \ } \ while (0) #else @@ -10898,7 +10992,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, \ x[0] ^= len[0]; \ x[1] ^= len[1]; \ - GMULT(x, h); \ + GMULT(x, h, &gmultScratch); \ } \ while (0) #endif @@ -10909,12 +11003,17 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, /* end defined(WORD64_AVAILABLE) && !defined(GCM_WORD32) */ #else /* GCM_WORD32 */ -static void GMULT(word32* X, word32* Y) +#define GCM_C_GMULT +/* C multiplication scratch is owned by the surrounding GHASH operation. + * Reuse it for every block and erase once when that operation returns. Keeping + * it off Gcm also preserves concurrent read-only use of a keyed Gcm object. */ +static void GMULT(word32* X, word32* Y, GcmScratch* WC_RESTRICT ws) { - word32 Z[4] = {0,0,0,0}; - word32 V[4]; + word32* Z = ws->w32; + word32* V = ws->w32 + 4; int i, j; + XMEMSET(Z, 0, WC_AES_BLOCK_SIZE); V[0] = X[0]; V[1] = X[1]; V[2] = X[2]; V[3] = X[3]; for (i = 0; i < 4; i++) @@ -10960,10 +11059,17 @@ static void GMULT(word32* X, word32* Y) void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 cSz, byte* s, word32 sSz) { +#ifdef GCM_C_GMULT + GcmScratch gmultScratch; +#endif word32 x[4] = {0,0,0,0}; word32 blocks, partial; word32 bigH[4]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("GHASH x", x, sizeof(x)); + wc_MemZero_Add("GHASH bigH", bigH, sizeof(bigH)); +#endif XMEMCPY(bigH, gcm->H, WC_AES_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords(bigH, bigH, WC_AES_BLOCK_SIZE); @@ -10983,7 +11089,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= bigA[1]; x[2] ^= bigA[2]; x[3] ^= bigA[3]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); a += WC_AES_BLOCK_SIZE; } if (partial != 0) { @@ -10996,7 +11102,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= bigA[1]; x[2] ^= bigA[2]; x[3] ^= bigA[3]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); } } @@ -11014,7 +11120,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= bigC[1]; x[2] ^= bigC[2]; x[3] ^= bigC[3]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); c += WC_AES_BLOCK_SIZE; } if (partial != 0) { @@ -11027,7 +11133,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= bigC[1]; x[2] ^= bigC[2]; x[3] ^= bigC[3]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); } } @@ -11045,12 +11151,22 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= len[1]; x[2] ^= len[2]; x[3] ^= len[3]; - GMULT(x, bigH); + GMULT(x, bigH, &gmultScratch); } #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords(x, x, WC_AES_BLOCK_SIZE); #endif XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); + /* bigH is a copy of the hash subkey H. */ + ForceZero(bigH, sizeof(bigH)); +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(x, sizeof(x)); + wc_MemZero_Check(bigH, sizeof(bigH)); +#endif } #ifdef WOLFSSL_AESGCM_STREAM @@ -11071,19 +11187,19 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, * @param [in, out] aes AES GCM object. * @param [in] block Block of AAD or cipher text. */ -#define GHASH_ONE_BLOCK_SW(aes, block) \ - do { \ - word32* x = (word32*)AES_TAG(aes); \ - word32* h = (word32*)aes->gcm.H; \ - word32 bigEnd[4]; \ - XMEMCPY(bigEnd, block, WC_AES_BLOCK_SIZE); \ - ByteReverseWords(bigEnd, bigEnd, WC_AES_BLOCK_SIZE); \ - x[0] ^= bigEnd[0]; \ - x[1] ^= bigEnd[1]; \ - x[2] ^= bigEnd[2]; \ - x[3] ^= bigEnd[3]; \ - GMULT(x, h); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + word32* x = (word32*)AES_TAG(aes); \ + word32* h = (word32*)aes->gcm.H; \ + word32 bigEnd[4]; \ + XMEMCPY(bigEnd, block, WC_AES_BLOCK_SIZE); \ + ByteReverseWords(bigEnd, bigEnd, WC_AES_BLOCK_SIZE); \ + x[0] ^= bigEnd[0]; \ + x[1] ^= bigEnd[1]; \ + x[2] ^= bigEnd[2]; \ + x[3] ^= bigEnd[3]; \ + GMULT(x, h, &gmultScratch); \ + } \ while (0) /* GHASH in AAD and cipher text lengths in bits. @@ -11105,7 +11221,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= len[1]; \ x[2] ^= len[2]; \ x[3] ^= len[3]; \ - GMULT(x, h); \ + GMULT(x, h, &gmultScratch); \ ByteReverseWords(x, x, WC_AES_BLOCK_SIZE); \ } \ while (0) @@ -11123,18 +11239,18 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, * @param [in, out] aes AES GCM object. * @param [in] block Block of AAD or cipher text. */ -#define GHASH_ONE_BLOCK_SW(aes, block) \ - do { \ - word32* x = (word32*)AES_TAG(aes); \ - word32* h = (word32*)aes->gcm.H; \ - word32 block32[4]; \ - XMEMCPY(block32, block, WC_AES_BLOCK_SIZE); \ - x[0] ^= block32[0]; \ - x[1] ^= block32[1]; \ - x[2] ^= block32[2]; \ - x[3] ^= block32[3]; \ - GMULT(x, h); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + word32* x = (word32*)AES_TAG(aes); \ + word32* h = (word32*)aes->gcm.H; \ + word32 block32[4]; \ + XMEMCPY(block32, block, WC_AES_BLOCK_SIZE); \ + x[0] ^= block32[0]; \ + x[1] ^= block32[1]; \ + x[2] ^= block32[2]; \ + x[3] ^= block32[3]; \ + GMULT(x, h, &gmultScratch); \ + } \ while (0) /* GHASH in AAD and cipher text lengths in bits. @@ -11154,7 +11270,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, x[1] ^= len[1]; \ x[2] ^= len[2]; \ x[3] ^= len[3]; \ - GMULT(x, h); \ + GMULT(x, h, &gmultScratch); \ } \ while (0) #endif /* LITTLE_ENDIAN_ORDER */ @@ -11214,6 +11330,9 @@ static void GHASH_INIT(Aes* aes) { static void GHASH_UPDATE(Aes* aes, const byte* a, word32 aSz, const byte* c, word32 cSz) { +#ifdef GCM_C_GMULT + GcmScratch gmultScratch; +#endif word32 blocks; word32 partial; @@ -11305,6 +11424,9 @@ static void GHASH_UPDATE(Aes* aes, const byte* a, word32 aSz, const byte* c, aes->cOver = (byte)partial; } } +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif } /* Finalize the GHASH calculation. @@ -11317,6 +11439,9 @@ static void GHASH_UPDATE(Aes* aes, const byte* a, word32 aSz, const byte* c, */ static void GHASH_FINAL(Aes* aes, byte* s, word32 sSz) { +#ifdef GCM_C_GMULT + GcmScratch gmultScratch; +#endif /* AAD block incomplete when > 0 */ byte over = aes->aOver; @@ -11337,6 +11462,9 @@ static void GHASH_FINAL(Aes* aes, byte* s, word32 sSz) XMEMCPY(s, AES_TAG(aes), sSz); /* reset aes->gcm.H in case of reuse */ GHASH_INIT_EXTRA(aes); +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif } #endif /* WOLFSSL_AESGCM_STREAM */ @@ -11724,6 +11852,18 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( int did_prefetches = 0; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(counter, 0, sizeof(counter)); + XMEMSET(initialCounter, 0, sizeof(initialCounter)); + XMEMSET(scratch, 0, sizeof(scratch)); + wc_MemZero_Add("AES_GCM_encrypt_C counter", counter, sizeof(counter)); + wc_MemZero_Add("AES_GCM_encrypt_C initialCounter", initialCounter, + sizeof(initialCounter)); + wc_MemZero_Add("AES_GCM_encrypt_C scratch", scratch, sizeof(scratch)); +#endif + if (ivSz == GCM_NONCE_MID_SZ) { /* Counter is IV with bottom 4 bytes set to: 0x00,0x00,0x00,0x01. */ XMEMCPY(counter, iv, ivSz); @@ -11754,7 +11894,7 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( out, in, (blocks * WC_AES_BLOCK_SIZE), PIC32_ENCRYPTION, PIC32_ALGO_AES, PIC32_CRYPTOALGO_AES_GCM); if (ret != 0) - return ret; + goto done; } /* process remainder using partial handling */ #endif @@ -11774,7 +11914,7 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( ret = wc_AesEcbEncrypt(aes, out, out, WC_AES_BLOCK_SIZE * blocks); if (ret != 0) { ForceZero(out, WC_AES_BLOCK_SIZE * blocks); - return ret; + goto done; } xorbuf(out, p, WC_AES_BLOCK_SIZE * blocks); p += WC_AES_BLOCK_SIZE * blocks; @@ -11788,7 +11928,7 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( ret = AesEncrypt_preFetchOpt(aes, counter, scratch, &did_prefetches); if (ret != 0) - return ret; + goto done; xorbufout(c, scratch, p, WC_AES_BLOCK_SIZE); #endif p += WC_AES_BLOCK_SIZE; @@ -11800,15 +11940,18 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( IncrementGcmCounter(counter); ret = AesEncrypt_preFetchOpt(aes, counter, scratch, &did_prefetches); if (ret != 0) - return ret; + goto done; xorbufout(c, scratch, p, partial); } if (authTag) { GHASH(&aes->gcm, authIn, authInSz, out, sz, authTag, authTagSz); ret = AesEncrypt_preFetchOpt(aes, initialCounter, scratch, &did_prefetches); - if (ret != 0) - return ret; + if (ret != 0) { + /* authTag still contains the secret, unmasked GHASH result. */ + ForceZero(authTag, authTagSz); + goto done; + } xorbuf(authTag, scratch, authTagSz); #ifdef OPENSSL_EXTRA if (!in && !sz) @@ -11817,6 +11960,17 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( #endif } +done: + /* Last keystream block or the tag mask E(J0). */ + ForceZero(scratch, WC_AES_BLOCK_SIZE); + /* Non-96-bit IVs produce a secret GHASH-derived counter. */ + ForceZero(counter, sizeof(counter)); + ForceZero(initialCounter, sizeof(initialCounter)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(counter, sizeof(counter)); + wc_MemZero_Check(initialCounter, sizeof(initialCounter)); + wc_MemZero_Check(scratch, sizeof(scratch)); +#endif return ret; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -11833,6 +11987,13 @@ static int AES_GCM_encrypt_ASM(Aes* aes, byte* out, const byte* in, ALIGN_GCM_TAG byte x[WC_AES_BLOCK_SIZE]; byte scratch[WC_AES_BLOCK_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AES_GCM_encrypt_ASM counter", counter, sizeof(counter)); + wc_MemZero_Add("AES_GCM_encrypt_ASM initialCounter", initialCounter, + sizeof(initialCounter)); + wc_MemZero_Add("AES_GCM_encrypt_ASM x", x, sizeof(x)); + wc_MemZero_Add("AES_GCM_encrypt_ASM scratch", scratch, sizeof(scratch)); +#endif XMEMSET(initialCounter, 0, WC_AES_BLOCK_SIZE); if (ivSz == GCM_NONCE_MID_SZ) { XMEMCPY(initialCounter, iv, ivSz); @@ -11934,7 +12095,19 @@ static int AES_GCM_encrypt_ASM(Aes* aes, byte* out, const byte* in, } #endif xorbuf(authTag, scratch, authTagSz); + /* Unmasked GHASH and the tag mask E(J0). */ + ForceZero(x, WC_AES_BLOCK_SIZE); + ForceZero(scratch, WC_AES_BLOCK_SIZE); + /* Non-96-bit IVs produce a secret GHASH-derived counter. */ + ForceZero(counter, sizeof(counter)); + ForceZero(initialCounter, sizeof(initialCounter)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(counter, sizeof(counter)); + wc_MemZero_Check(initialCounter, sizeof(initialCounter)); + wc_MemZero_Check(x, sizeof(x)); + wc_MemZero_Check(scratch, sizeof(scratch)); +#endif return 0; } #endif @@ -12576,6 +12749,19 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( word32 i; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(counter, 0, sizeof(counter)); + XMEMSET(scratch, 0, sizeof(scratch)); + XMEMSET(Tprime, 0, sizeof(Tprime)); + XMEMSET(EKY0, 0, sizeof(EKY0)); + wc_MemZero_Add("AES_GCM_decrypt_C counter", counter, sizeof(counter)); + wc_MemZero_Add("AES_GCM_decrypt_C scratch", scratch, sizeof(scratch)); + wc_MemZero_Add("AES_GCM_decrypt_C Tprime", Tprime, sizeof(Tprime)); + wc_MemZero_Add("AES_GCM_decrypt_C EKY0", EKY0, sizeof(EKY0)); +#endif + if (ivSz == GCM_NONCE_MID_SZ) { /* Counter is IV with bottom 4 bytes set to: 0x00,0x00,0x00,0x01. */ XMEMCPY(counter, iv, ivSz); @@ -12599,7 +12785,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( GHASH(&aes->gcm, authIn, authInSz, in, sz, Tprime, sizeof(Tprime)); ret = wc_AesEncrypt(aes, counter, EKY0); if (ret != 0) - return ret; + goto error; xorbuf(Tprime, EKY0, sizeof(Tprime)); #ifdef WC_AES_GCM_DEC_AUTH_EARLY /* ConstantCompare returns the cumulative bitwise or of the bitwise xor of @@ -12612,7 +12798,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( res = 0 - (sword32)(((word32)(0 - res)) >> 31U); ret = res & AES_GCM_AUTH_E; if (ret != 0) - return ret; + goto done; #endif #ifdef OPENSSL_EXTRA @@ -12633,7 +12819,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( out, in, (blocks * WC_AES_BLOCK_SIZE), PIC32_DECRYPTION, PIC32_ALGO_AES, PIC32_CRYPTOALGO_AES_GCM); if (ret != 0) - return ret; + goto error; } /* process remainder using partial handling */ #endif @@ -12653,8 +12839,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( ret = wc_AesEcbEncrypt(aes, out, out, WC_AES_BLOCK_SIZE * blocks); if (ret != 0) { - ForceZero(out, WC_AES_BLOCK_SIZE * blocks); - return ret; + goto error; } xorbuf(out, c, WC_AES_BLOCK_SIZE * blocks); c += WC_AES_BLOCK_SIZE * blocks; @@ -12667,7 +12852,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( #if !defined(WOLFSSL_PIC32MZ_CRYPT) ret = wc_AesEncrypt(aes, counter, scratch); if (ret != 0) - return ret; + goto error; xorbufout(p, scratch, c, WC_AES_BLOCK_SIZE); #endif p += WC_AES_BLOCK_SIZE; @@ -12679,7 +12864,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( IncrementGcmCounter(counter); ret = wc_AesEncrypt(aes, counter, scratch); if (ret != 0) - return ret; + goto error; xorbuf(scratch, c, partial); XMEMCPY(p, scratch, partial); } @@ -12707,6 +12892,25 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( for (i = 0; i < sz; i++) { out[i] &= (byte)~mask; } +#endif + goto done; +error: + /* No unauthenticated plaintext or generated counter/keystream may escape + * after a primitive failure, including an in-place operation. */ + if (out != NULL) + ForceZero(out, sz); +done: + /* Tag mask E(J0), the last keystream block and the expected tag. */ + ForceZero(EKY0, WC_AES_BLOCK_SIZE); + ForceZero(scratch, WC_AES_BLOCK_SIZE); + ForceZero(Tprime, WC_AES_BLOCK_SIZE); + /* Non-96-bit IVs produce a secret GHASH-derived counter. */ + ForceZero(counter, sizeof(counter)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(counter, sizeof(counter)); + wc_MemZero_Check(scratch, sizeof(scratch)); + wc_MemZero_Check(Tprime, sizeof(Tprime)); + wc_MemZero_Check(EKY0, sizeof(EKY0)); #endif return ret; } @@ -12717,6 +12921,7 @@ static int AES_GCM_decrypt_ASM(Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) { + int ret = 0; word32 blocks; word32 partial; byte counter[WC_AES_BLOCK_SIZE]; @@ -12724,6 +12929,13 @@ static int AES_GCM_decrypt_ASM(Aes* aes, byte* out, const byte* in, byte scratch[WC_AES_BLOCK_SIZE]; ALIGN_GCM_TAG byte x[WC_AES_BLOCK_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AES_GCM_decrypt_ASM counter", counter, sizeof(counter)); + wc_MemZero_Add("AES_GCM_decrypt_ASM initialCounter", initialCounter, + sizeof(initialCounter)); + wc_MemZero_Add("AES_GCM_decrypt_ASM scratch", scratch, sizeof(scratch)); + wc_MemZero_Add("AES_GCM_decrypt_ASM x", x, sizeof(x)); +#endif XMEMSET(initialCounter, 0, WC_AES_BLOCK_SIZE); if (ivSz == GCM_NONCE_MID_SZ) { XMEMCPY(initialCounter, iv, ivSz); @@ -12817,11 +13029,22 @@ static int AES_GCM_decrypt_ASM(Aes* aes, byte* out, const byte* in, xorbuf(x, scratch, authTagSz); if (authTag != NULL) { if (ConstantCompare(authTag, x, authTagSz) != 0) { - return AES_GCM_AUTH_E; + ret = AES_GCM_AUTH_E; } } + ForceZero(x, WC_AES_BLOCK_SIZE); + ForceZero(scratch, WC_AES_BLOCK_SIZE); - return 0; + /* Non-96-bit IVs produce a secret GHASH-derived counter. */ + ForceZero(counter, sizeof(counter)); + ForceZero(initialCounter, sizeof(initialCounter)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(counter, sizeof(counter)); + wc_MemZero_Check(initialCounter, sizeof(initialCounter)); + wc_MemZero_Check(scratch, sizeof(scratch)); + wc_MemZero_Check(x, sizeof(x)); +#endif + return ret; } #endif @@ -13136,6 +13359,10 @@ static WARN_UNUSED_RESULT int AesGcmInit_C(Aes* aes, const byte* iv, word32 ivSz ALIGN32 byte counter[WC_AES_BLOCK_SIZE]; int ret; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AesGcmInit_C counter", counter, sizeof(counter)); +#endif + if (ivSz == GCM_NONCE_MID_SZ) { /* Counter is IV with bottom 4 bytes set to: 0x00,0x00,0x00,0x01. */ XMEMCPY(counter, iv, ivSz); @@ -13159,6 +13386,10 @@ static WARN_UNUSED_RESULT int AesGcmInit_C(Aes* aes, const byte* iv, word32 ivSz XMEMCPY(AES_COUNTER(aes), counter, WC_AES_BLOCK_SIZE); /* Encrypt initial counter into a buffer for GCM. */ ret = wc_AesEncrypt(aes, counter, AES_INITCTR(aes)); + ForceZero(counter, sizeof(counter)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(counter, sizeof(counter)); +#endif if (ret != 0) return ret; /* Reset state fields. */ @@ -13224,7 +13455,7 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( ret = wc_AesEcbEncrypt(aes, out, out, WC_AES_BLOCK_SIZE * blocks); if (ret != 0) { ForceZero(out, WC_AES_BLOCK_SIZE * blocks); - return ret; + goto error; } /* XOR in plaintext. */ xorbuf(out, in, WC_AES_BLOCK_SIZE * blocks); @@ -13235,20 +13466,23 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( else #endif /* HAVE_AES_ECB */ { + ALIGN32 byte scratch[WC_AES_BLOCK_SIZE]; /* Encrypt block by block. */ while (blocks--) { - ALIGN32 byte scratch[WC_AES_BLOCK_SIZE]; IncrementGcmCounter(AES_COUNTER(aes)); /* Encrypt counter into a buffer. */ ret = wc_AesEncrypt(aes, AES_COUNTER(aes), scratch); - if (ret != 0) - return ret; + if (ret != 0) { + ForceZero(scratch, sizeof(scratch)); + goto error; + } /* XOR plain text into encrypted counter into cipher text buffer. */ xorbufout(out, scratch, in, WC_AES_BLOCK_SIZE); /* Data complete. */ in += WC_AES_BLOCK_SIZE; out += WC_AES_BLOCK_SIZE; } + ForceZero(scratch, sizeof(scratch)); } if (partial != 0) { @@ -13257,7 +13491,7 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( /* Encrypt counter into cache. */ ret = wc_AesEncrypt(aes, AES_COUNTER(aes), AES_LASTBLOCK(aes)); if (ret != 0) - return ret; + goto error; /* XOR plain text into encrypted counter into cipher text buffer. */ xorbufout(out, AES_LASTBLOCK(aes), in, partial); /* Keep amount of encrypted block used. */ @@ -13265,6 +13499,16 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( } return 0; + +error: + /* The counter may already have advanced and earlier output may have been + * produced. This stream cannot be resumed after a primitive failure. */ + ForceZero(aes->streamData, AES_STREAM_DATA_SZ); + aes->nonceSet = 0; + aes->over = 0; + aes->aOver = 0; + aes->cOver = 0; + return ret; } /* Calculates authentication tag for AES GCM. C implementation. @@ -13284,8 +13528,6 @@ static WARN_UNUSED_RESULT int AesGcmFinal_C( /* store AAD size for next call */ aes->gcm.aadLen = aes->aSz; #endif - /* Zeroize last block to protect sensitive data. */ - ForceZero(AES_LASTBLOCK(aes), WC_AES_BLOCK_SIZE); return 0; } @@ -15392,6 +15634,7 @@ int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag, word32 authTagSz) { int ret = 0; + int wipe = 0; /* Check validity of parameters. */ if ((aes == NULL) || (authTag == NULL)) { @@ -15420,6 +15663,7 @@ int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag, word32 authTagSz) } if (ret == 0) { + wipe = 1; /* Calculate authentication tag. */ #ifdef WC_AESNI_GCM if (aes->use_aesni) { @@ -15450,6 +15694,11 @@ int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag, word32 authTagSz) if ((ret == 0) && aes->ctrSet) { IncCtr((byte*)aes->reg, aes->nonceSz); } + if (wipe) { + /* The stream is finished: drop E(J0), counter, tag and keystream. */ + ForceZero(aes->streamData, AES_STREAM_DATA_SZ); + aes->nonceSet = 0; + } return ret; } @@ -15566,6 +15815,7 @@ int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz) { int ret = 0; + int wipe = 0; /* Check validity of parameters. */ if ((aes == NULL) || (authTag == NULL)) { @@ -15589,6 +15839,7 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz) } if (ret == 0) { + wipe = 1; /* Calculate authentication tag and compare with one passed in.. */ #ifdef WC_AESNI_GCM if (aes->use_aesni) { @@ -15612,6 +15863,14 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz) { ALIGN32 byte calcTag[WC_AES_BLOCK_SIZE]; WC_AES_GCM_C_SVR_BEGIN(); + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a + * pointer to an uninitialized object trips -Wmaybe-uninitialized + * under -Werror. */ + XMEMSET(calcTag, 0, sizeof(calcTag)); + wc_MemZero_Add("wc_AesGcmDecryptFinal calcTag", calcTag, + sizeof(calcTag)); + #endif /* Calculate authentication tag. */ ret = AesGcmFinal_C(aes, calcTag, WC_AES_BLOCK_SIZE); WC_AES_GCM_C_SVR_END(); @@ -15621,9 +15880,20 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz) ret = AES_GCM_AUTH_E; } } + /* On a mismatch this is a valid tag for the input. */ + ForceZero(calcTag, sizeof(calcTag)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(calcTag, sizeof(calcTag)); + #endif } } + if (wipe) { + /* The stream is finished: drop E(J0), counter, tag and keystream. */ + ForceZero(aes->streamData, AES_STREAM_DATA_SZ); + aes->nonceSet = 0; + } + /* Final cannot see earlier Update output; on AES_GCM_AUTH_E the caller * must discard it (Security Policy operational rules). */ return ret; @@ -17599,6 +17869,10 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, { #ifndef WOLFSSL_SMALL_STACK ALIGN16 byte tmp[WC_AES_CFB_DEC_BUF_BLOCKS * WC_AES_BLOCK_SIZE]; +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AesCfbDecrypt_C tmp", tmp, + WC_AES_CFB_DEC_BUF_BLOCKS * WC_AES_BLOCK_SIZE); #endif if (sz >= 2 * WC_AES_BLOCK_SIZE) { /* CFB-decrypt keystream block i is E(C_{i-1}): block 0 uses the @@ -17608,6 +17882,9 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, * carry E(C_{n-1}) as the next chunk's block-0 keystream - E(reg) * is computed only once here. */ ALIGN16 byte ks[WC_AES_BLOCK_SIZE]; + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AesCfbDecrypt_C ks", ks, sizeof(ks)); + #endif ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, ks, &did_prefetches); while ((ret == 0) && (sz >= 2 * WC_AES_BLOCK_SIZE)) { @@ -17645,7 +17922,17 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, in += nbytes; sz -= nbytes; } + ForceZero(ks, WC_AES_BLOCK_SIZE); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(ks, sizeof(ks)); + #endif } +#ifndef WOLFSSL_SMALL_STACK + ForceZero(tmp, sizeof(tmp)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(tmp, sizeof(tmp)); + #endif +#endif } #endif while (sz >= WC_AES_BLOCK_SIZE) { @@ -17676,6 +17963,9 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, #ifdef WOLFSSL_SMALL_STACK /* Free tmp after restoring interrupts, so that GFP_KERNEL is usable. */ + if (tmp != NULL) { + ForceZero(tmp, WC_AES_CFB_DEC_BUF_BLOCKS * WC_AES_BLOCK_SIZE); + } XFREE(tmp, NULL, DYNAMIC_TYPE_AES); #endif @@ -18210,6 +18500,9 @@ static int AesKeyWrapRaw(Aes* aes, word32 inSz, byte* out, const byte* aiv) * HAVE_AES_ECB wc_AesEcbEncrypt saves them and can route to an ECB cb. */ VECTOR_REGISTERS_PUSH; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AesKeyWrapRaw tmp", tmp, sizeof(tmp)); +#endif for (j = 0; j <= 5; j++) { for (i = 1; i <= inSz / KEYWRAP_BLOCK_SIZE; i++) { @@ -18241,13 +18534,17 @@ static int AesKeyWrapRaw(Aes* aes, word32 inSz, byte* out, const byte* aiv) VECTOR_REGISTERS_POP; #endif - if (ret != 0) - return ret; - - /* C[0] = A */ - XMEMCPY(out, tmp, KEYWRAP_BLOCK_SIZE); + if (ret == 0) { + /* C[0] = A */ + XMEMCPY(out, tmp, KEYWRAP_BLOCK_SIZE); + } + /* tmp holds A || P[i] on an encrypt failure (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(tmp, sizeof(tmp)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(tmp, sizeof(tmp)); +#endif - return 0; + return ret; } int wc_AesKeyWrap_ex(Aes *aes, const byte* in, word32 inSz, byte* out, @@ -18375,6 +18672,9 @@ static int AesKeyUnWrapRaw(Aes* aes, const byte* in, word32 inSz, byte* out, * (saves registers + ECB cb); otherwise save vector registers here. */ VECTOR_REGISTERS_PUSH; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("AesKeyUnWrapRaw tmp", tmp, sizeof(tmp)); +#endif /* initialize counter to 6n */ n = (inSz - 1) / KEYWRAP_BLOCK_SIZE; @@ -18409,13 +18709,23 @@ static int AesKeyUnWrapRaw(Aes* aes, const byte* in, word32 inSz, byte* out, VECTOR_REGISTERS_POP; #endif - if (ret != 0) - return ret; - - /* return recovered A */ - XMEMCPY(aOut, tmp, KEYWRAP_BLOCK_SIZE); + if (ret == 0) { + /* return recovered A */ + XMEMCPY(aOut, tmp, KEYWRAP_BLOCK_SIZE); + } + else { + /* Partially recovered plaintext (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(out, inSz - KEYWRAP_BLOCK_SIZE); + } + /* tmp ends holding the first 8 bytes of the recovered key + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(tmp, sizeof(tmp)); + ForceZero(t, sizeof(t)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(tmp, sizeof(tmp)); +#endif - return 0; + return ret; } int wc_AesKeyUnWrap_ex(Aes *aes, const byte* in, word32 inSz, byte* out, @@ -19225,6 +19535,7 @@ void AES_XTS_decrypt_update_avx512(const unsigned char *in, unsigned char *out, static WARN_UNUSED_RESULT int _AesXtsHelper( Aes* aes, byte* out, const byte* in, word32 sz, int dir) { + int ret; word32 outSz = sz; word32 totalSz = (sz / WC_AES_BLOCK_SIZE) * WC_AES_BLOCK_SIZE; /* total bytes */ byte* pt = out; @@ -19253,19 +19564,24 @@ static WARN_UNUSED_RESULT int _AesXtsHelper( xorbuf(out, in, totalSz); #ifndef WOLFSSL_RISCV_ASM if (dir == AES_ENCRYPTION) { - return _AesEcbEncrypt(aes, out, out, totalSz); + ret = _AesEcbEncrypt(aes, out, out, totalSz); } else { - return _AesEcbDecrypt(aes, out, out, totalSz); + ret = _AesEcbDecrypt(aes, out, out, totalSz); } #else if (dir == AES_ENCRYPTION) { - return wc_AesEcbEncrypt(aes, out, out, totalSz); + ret = wc_AesEcbEncrypt(aes, out, out, totalSz); } else { - return wc_AesEcbDecrypt(aes, out, out, totalSz); + ret = wc_AesEcbDecrypt(aes, out, out, totalSz); } #endif + if (ret != 0) { + /* out holds the tweak-masked input until the block call succeeds. */ + ForceZero(out, totalSz); + } + return ret; } #endif #endif /* HAVE_AES_ECB */ @@ -19297,10 +19613,21 @@ static int AesXtsEncrypt_sw(XtsAes* xaes, byte* out, const byte* in, word32 sz, /* One claim for the tweak and the data below it. */ WC_AES_ARM64_SVR_BEGIN(); +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(tweak_block, 0, sizeof(tweak_block)); + wc_MemZero_Add("AesXtsEncrypt_sw tweak_block", tweak_block, + sizeof(tweak_block)); +#endif ret = wc_AesEncryptDirect(&xaes->tweak, tweak_block, i); if (ret == 0) ret = AesXtsEncryptUpdate_sw(xaes, out, in, sz, tweak_block); WC_AES_ARM64_SVR_END(); + ForceZero(tweak_block, sizeof(tweak_block)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(tweak_block, sizeof(tweak_block)); +#endif return ret; } @@ -19356,17 +19683,23 @@ static int AesXtsEncryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, int ret = 0; word32 blocks = (sz / WC_AES_BLOCK_SIZE); Aes *aes = &xaes->aes; + byte buf[WC_AES_BLOCK_SIZE]; /* One claim for the call: the block routine below is vector code. */ WC_AES_ARM64_SVR_BEGIN(); +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(buf, 0, sizeof(buf)); + wc_MemZero_Add("AesXtsEncryptUpdate_sw buf", buf, sizeof(buf)); +#endif #ifdef HAVE_AES_ECB /* encrypt all of buffer at once when possible */ if (in != out) { /* can not handle inline */ XMEMCPY(out, i, WC_AES_BLOCK_SIZE); if ((ret = _AesXtsHelper(aes, out, in, sz, AES_ENCRYPTION)) != 0) { - WC_AES_ARM64_SVR_END(); - return ret; + goto done; } } #endif @@ -19379,14 +19712,11 @@ static int AesXtsEncryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, if (in == out) #endif { /* check for if inline */ - byte buf[WC_AES_BLOCK_SIZE]; - XMEMCPY(buf, in, WC_AES_BLOCK_SIZE); xorbuf(buf, i, WC_AES_BLOCK_SIZE); ret = wc_AesEncryptDirect(aes, out, buf); if (ret != 0) { - WC_AES_ARM64_SVR_END(); - return ret; + goto done; } } xorbuf(out, i, WC_AES_BLOCK_SIZE); @@ -19411,12 +19741,10 @@ static int AesXtsEncryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, /* stealing operation of XTS to handle left overs */ if (sz > 0) { - byte buf[WC_AES_BLOCK_SIZE]; - XMEMCPY(buf, out - WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); if (sz >= WC_AES_BLOCK_SIZE) { /* extra sanity check before copy */ - WC_AES_ARM64_SVR_END(); - return BUFFER_E; + ret = BUFFER_E; + goto done; } if (in != out) { XMEMCPY(out, buf, sz); @@ -19436,7 +19764,12 @@ static int AesXtsEncryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, xorbuf(out - WC_AES_BLOCK_SIZE, i, WC_AES_BLOCK_SIZE); } +done: WC_AES_ARM64_SVR_END(); + ForceZero(buf, WC_AES_BLOCK_SIZE); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(buf, sizeof(buf)); +#endif return ret; } @@ -19905,10 +20238,21 @@ static int AesXtsDecrypt_sw(XtsAes* xaes, byte* out, const byte* in, word32 sz, /* One claim for the tweak and the data below it. */ WC_AES_ARM64_SVR_BEGIN(); +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(tweak_block, 0, sizeof(tweak_block)); + wc_MemZero_Add("AesXtsDecrypt_sw tweak_block", tweak_block, + sizeof(tweak_block)); +#endif ret = wc_AesEncryptDirect(&xaes->tweak, tweak_block, i); if (ret == 0) ret = AesXtsDecryptUpdate_sw(xaes, out, in, sz, tweak_block); WC_AES_ARM64_SVR_END(); + ForceZero(tweak_block, sizeof(tweak_block)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(tweak_block, sizeof(tweak_block)); +#endif return ret; } @@ -19947,6 +20291,8 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, word32 j; byte carry = 0; byte stl = (sz % WC_AES_BLOCK_SIZE); + byte buf[WC_AES_BLOCK_SIZE]; + byte tmp2[WC_AES_BLOCK_SIZE]; /* if Stealing then break out of loop one block early to handle special * case */ @@ -19956,14 +20302,21 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, /* One claim for the call: the block routine below is vector code. */ WC_AES_ARM64_SVR_BEGIN(); +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(tmp2, 0, sizeof(tmp2)); + wc_MemZero_Add("AesXtsDecryptUpdate_sw buf", buf, sizeof(buf)); + wc_MemZero_Add("AesXtsDecryptUpdate_sw tmp2", tmp2, sizeof(tmp2)); +#endif #ifdef HAVE_AES_ECB /* decrypt all of buffer at once when possible */ if (in != out) { /* can not handle inline */ XMEMCPY(out, i, WC_AES_BLOCK_SIZE); if ((ret = _AesXtsHelper(aes, out, in, sz, AES_DECRYPTION)) != 0) { - WC_AES_ARM64_SVR_END(); - return ret; + goto done; } } #endif @@ -19973,14 +20326,11 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, if (in == out) #endif { /* check for if inline */ - byte buf[WC_AES_BLOCK_SIZE]; - XMEMCPY(buf, in, WC_AES_BLOCK_SIZE); xorbuf(buf, i, WC_AES_BLOCK_SIZE); ret = wc_AesDecryptDirect(aes, out, buf); if (ret != 0) { - WC_AES_ARM64_SVR_END(); - return ret; + goto done; } } xorbuf(out, i, WC_AES_BLOCK_SIZE); @@ -20006,9 +20356,6 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, /* stealing operation of XTS to handle left overs */ if (sz >= WC_AES_BLOCK_SIZE) { - byte buf[WC_AES_BLOCK_SIZE]; - byte tmp2[WC_AES_BLOCK_SIZE]; - /* multiply by shift left and propagate carry */ for (j = 0; j < WC_AES_BLOCK_SIZE; j++) { byte tmpC; @@ -20025,8 +20372,7 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, xorbuf(buf, tmp2, WC_AES_BLOCK_SIZE); ret = wc_AesDecryptDirect(aes, out, buf); if (ret != 0) { - WC_AES_ARM64_SVR_END(); - return ret; + goto done; } xorbuf(out, tmp2, WC_AES_BLOCK_SIZE); @@ -20039,8 +20385,8 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, /* Make buffer with end of cipher text | last */ XMEMCPY(buf, tmp2, WC_AES_BLOCK_SIZE); if (sz >= WC_AES_BLOCK_SIZE) { /* extra sanity check before copy */ - WC_AES_ARM64_SVR_END(); - return BUFFER_E; + ret = BUFFER_E; + goto done; } XMEMCPY(buf, in, sz); XMEMCPY(out, tmp2, sz); @@ -20048,14 +20394,20 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, xorbuf(buf, i, WC_AES_BLOCK_SIZE); ret = wc_AesDecryptDirect(aes, tmp2, buf); if (ret != 0) { - WC_AES_ARM64_SVR_END(); - return ret; + goto done; } xorbuf(tmp2, i, WC_AES_BLOCK_SIZE); XMEMCPY(out - WC_AES_BLOCK_SIZE, tmp2, WC_AES_BLOCK_SIZE); } +done: WC_AES_ARM64_SVR_END(); + ForceZero(buf, WC_AES_BLOCK_SIZE); + ForceZero(tmp2, WC_AES_BLOCK_SIZE); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(buf, sizeof(buf)); + wc_MemZero_Check(tmp2, sizeof(tmp2)); +#endif return ret; } diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 1462ac71f16..e14dd4b0316 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -235,7 +235,7 @@ static int _InitCmac_common(Cmac* cmac, const byte* key, word32 keySz, } if (ret == 0) { - byte l[WC_AES_BLOCK_SIZE]; + ALIGN16 byte l[WC_AES_BLOCK_SIZE]; XMEMSET(l, 0, WC_AES_BLOCK_SIZE); #ifdef WOLFSSL_CHECK_MEM_ZERO @@ -657,6 +657,7 @@ int wc_AesCmacGenerate(byte* out, word32* outSz, #ifdef WOLFSSL_SMALL_STACK + ForceZero(cmac, sizeof(Cmac)); XFREE(cmac, NULL, DYNAMIC_TYPE_CMAC); #elif defined(WOLFSSL_CHECK_MEM_ZERO) wc_MemZero_Check(cmac, sizeof(Cmac)); @@ -692,6 +693,9 @@ int wc_AesCmacVerify_ex(Cmac* cmac, aSz = checkSz; XMEMSET(a, 0, sizeof(a)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_AesCmacVerify_ex a", a, sizeof(a)); +#endif ret = wc_AesCmacGenerate_ex(cmac, a, &aSz, in, inSz, @@ -709,6 +713,11 @@ int wc_AesCmacVerify_ex(Cmac* cmac, compareRet = ConstantCompare(check, a, (int)aSz); ret = compareRet ? MAC_CMP_FAILED_E : 0; } + /* On a mismatch this is a valid tag for the message. */ + ForceZero(a, sizeof(a)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(a, sizeof(a)); +#endif return ret; } @@ -750,6 +759,7 @@ int wc_AesCmacVerify(const byte* check, word32 checkSz, INVALID_DEVID); #ifdef WOLFSSL_SMALL_STACK + ForceZero(cmac, sizeof(Cmac)); XFREE(cmac, NULL, DYNAMIC_TYPE_CMAC); #elif defined(WOLFSSL_CHECK_MEM_ZERO) wc_MemZero_Check(cmac, sizeof(Cmac)); diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index ca0db6d251b..ae2d85618c5 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -25,6 +25,12 @@ #include #ifdef WOLFSSL_ECC_NO_SMALL_STACK +/* Shared contexts must retain the library-wide allocation configuration. */ +#include +#include +#ifdef HAVE_ECC_ENCRYPT +#include +#endif #undef WOLFSSL_SMALL_STACK #undef WOLFSSL_SMALL_STACK_CACHE #endif @@ -445,6 +451,12 @@ void ecc_forcezero_k(ecc_key* key) mp_forcezero(key->ku); } +/* Drop the unblinded copy once the caller is done with it. */ +void ecc_put_k(ecc_key* key) +{ + mp_forcezero(key->ku); +} + mp_int* wc_ecc_key_get_priv(ecc_key* key) { return ecc_get_k(key); @@ -3310,13 +3322,16 @@ static int ecc_mulmod(const mp_int* k, ecc_point* P, ecc_point* Q, /* First bit always 1 (fix at end) and swap equals first bit */ int swap = 1; WC_DECLARE_VAR(tmp, mp_int, 1, 0); + int tmpInit = 0; #endif int infinity; #ifndef WC_NO_CACHE_RESISTANT WC_ALLOC_VAR_EX(tmp, mp_int, 1, NULL, DYNAMIC_TYPE_ECC, err=MEMORY_E); - if (err == MP_OKAY) + if (err == MP_OKAY) { err = mp_init(tmp); + tmpInit = (err == MP_OKAY); + } #endif /* Step 1: R[0] = P; R[1] = P */ @@ -3464,6 +3479,13 @@ static int ecc_mulmod(const mp_int* k, ecc_point* P, ecc_point* Q, if (err == MP_OKAY) err = mp_copy(R[0]->z, Q->z); + /* kt holds a copy of the scalar (ISO/IEC 19790:2012 7.9.7). */ + mp_forcezero(kt); +#ifndef WC_NO_CACHE_RESISTANT + /* tmp can hold scalar-dependent coordinates even after a ladder error. */ + if (tmpInit) + mp_forcezero(tmp); +#endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WC_NO_CACHE_RESISTANT) XFREE(tmp, NULL, DYNAMIC_TYPE_ECC); #endif @@ -3652,6 +3674,9 @@ static int ecc_mulmod(const mp_int* k, ecc_point* P, ecc_point* Q, if (err == MP_OKAY) err = mp_copy(R[set + 0]->z, Q->z); + /* kt holds a copy of the scalar (ISO/IEC 19790:2012 7.9.7). */ + mp_forcezero(kt); + return err; } @@ -4129,9 +4154,11 @@ int wc_ecc_mulmod_ex2(const mp_int* k, ecc_point* G, ecc_point* R, mp_int* a, exit: - /* done */ + /* Ladder points are scalar dependent; mp_clear only wipes under FIPS. */ + wc_ecc_forcezero_point(tG); wc_ecc_del_point_ex(tG, heap); for (i = 0; i < M_POINTS; i++) { + wc_ecc_forcezero_point(M[i]); wc_ecc_del_point_ex(M[i], heap); } #ifdef WOLFSSL_SMALL_STACK_CACHE @@ -5166,6 +5193,13 @@ int wc_ecc_shared_secret_gen_sync(ecc_key* private_key, ecc_point* point, mp_forcezero(k); WC_FREE_VAR_EX(k_lcl, private_key->heap, DYNAMIC_TYPE_ECC_BUFFER); #endif +#ifdef WC_ECC_NONBLOCK + /* An incomplete operation still needs k. */ + if (err != FP_WOULDBLOCK) +#endif + { + ecc_put_k(private_key); + } return err; } @@ -5749,6 +5783,7 @@ static int ecc_make_pub_sw(ecc_key* key, ecc_curve_spec* curve, wc_ecc_del_point_ex(base, key->heap); } #endif /* WOLFSSL_SP_MATH */ + ecc_put_k(key); } /* END: Software Crypto */ return err; @@ -7198,6 +7233,7 @@ static int deterministic_sign_helper(const byte* in, word32 inlen, ecc_key* key) if (wc_ecc_gen_deterministic_k(in, inlen, key->hashType, ecc_get_k(key), key->sign_k, curve->order, key->heap) != 0) { + mp_forcezero(key->sign_k); mp_free(key->sign_k); XFREE(key->sign_k, key->heap, DYNAMIC_TYPE_ECC); key->sign_k = NULL; @@ -7216,12 +7252,14 @@ static int deterministic_sign_helper(const byte* in, word32 inlen, ecc_key* key) key->sign_k_set = 0; if (wc_ecc_gen_deterministic_k(in, inlen, key->hashType, ecc_get_k(key), key->sign_k, curve->order, key->heap) != 0) { + mp_forcezero(key->sign_k); err = ECC_PRIV_KEY_E; } else { key->sign_k_set = 1; } #endif + ecc_put_k(key); } wc_ecc_curve_free(curve); @@ -7456,6 +7494,7 @@ static int ecc_sign_hash_sw(ecc_key* key, ecc_key* pubkey, WC_RNG* rng, break; WC_RELAX_LONG_LOOP(); } + ecc_put_k(key); mp_forcezero(b); FREE_MP_INT_SIZE(b, key->heap, DYNAMIC_TYPE_ECC); #if !defined(WOLFSSL_SMALL_STACK) && defined(WOLFSSL_CHECK_MEM_ZERO) @@ -7466,12 +7505,10 @@ static int ecc_sign_hash_sw(ecc_key* key, ecc_key* pubkey, WC_RNG* rng, } #endif -#ifdef WOLFSSL_HAVE_SP_ECC #if defined(WOLFSSL_ECDSA_SET_K) || defined(WOLFSSL_ECDSA_SET_K_ONE_LOOP) || \ defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) -/* SP only resets the logical length of k, leaving its digits in the backing - * store. Clear it the way the software path does. */ +/* The nonce is consumed by every sign result; SP only resets its length. */ static void ecc_sign_k_forcezero(ecc_key* key) { #ifndef WOLFSSL_NO_MALLOC @@ -7482,14 +7519,13 @@ static void ecc_sign_k_forcezero(ecc_key* key) key->sign_k = NULL; } #else - if (key->sign_k_set) { - mp_forcezero(key->sign_k); - key->sign_k_set = 0; - } + mp_forcezero(key->sign_k); + key->sign_k_set = 0; #endif } #endif +#ifdef WOLFSSL_HAVE_SP_ECC static int ecc_sign_hash_sp(const byte* in, word32 inlen, WC_RNG* rng, ecc_key* key, mp_int *r, mp_int *s) { @@ -7722,17 +7758,18 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, /* WC_KEY_SIZE_E only means SP left this curve to the software path below, * which needs k and clears it itself. Every other result consumed k. */ if (err != WC_NO_ERR_TRACE(WC_KEY_SIZE_E)) { - #if defined(WOLFSSL_ECDSA_SET_K) || defined(WOLFSSL_ECDSA_SET_K_ONE_LOOP) || \ - defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ - defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) #ifdef WC_ECC_NONBLOCK /* An incomplete operation still needs k. */ if (err != FP_WOULDBLOCK) #endif { + ecc_put_k(key); + #if defined(WOLFSSL_ECDSA_SET_K) || defined(WOLFSSL_ECDSA_SET_K_ONE_LOOP) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) ecc_sign_k_forcezero(key); - } #endif + } return err; } #else @@ -7896,6 +7933,12 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, } } } +#if defined(WOLFSSL_ECDSA_SET_K) || defined(WOLFSSL_ECDSA_SET_K_ONE_LOOP) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) + /* The nonce is consumed whatever the result. */ + ecc_sign_k_forcezero(key); +#endif mp_clear(e); wc_ecc_curve_free(curve); @@ -8292,6 +8335,7 @@ int wc_ecc_sign_set_k(const byte* k, word32 klen, ecc_key* key) } if (ret == 0 && mp_cmp(key->sign_k, curve->order) != MP_LT) { ret = MP_VAL; + ecc_sign_k_forcezero(key); } #ifdef WOLFSSL_NO_MALLOC if (ret == 0) { @@ -11148,6 +11192,7 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime) } } + ecc_put_k(key); wc_ecc_curve_free(curve); wc_ecc_del_point_ex(res, key->heap); wc_ecc_del_point_ex(base, key->heap); @@ -11438,37 +11483,47 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv) #ifdef WOLFSSL_HAVE_SP_ECC #ifndef WOLFSSL_SP_NO_256 if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SECP256R1) { - return sp_ecc_check_key_256(key->pubkey.x, key->pubkey.y, + err = sp_ecc_check_key_256(key->pubkey.x, key->pubkey.y, (priv && (key->type == ECC_PRIVATEKEY)) ? ecc_get_k(key) : NULL, key->heap); + ecc_put_k(key); + return err; } #endif #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SP_SM2) if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SM2P256V1) { - return sp_ecc_check_key_sm2_256(key->pubkey.x, key->pubkey.y, + err = sp_ecc_check_key_sm2_256(key->pubkey.x, key->pubkey.y, (priv && (key->type == ECC_PRIVATEKEY)) ? ecc_get_k(key) : NULL, key->heap); + ecc_put_k(key); + return err; } #endif #ifdef WOLFSSL_SP_384 if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SECP384R1) { - return sp_ecc_check_key_384(key->pubkey.x, key->pubkey.y, + err = sp_ecc_check_key_384(key->pubkey.x, key->pubkey.y, (priv && (key->type == ECC_PRIVATEKEY)) ? ecc_get_k(key) : NULL, key->heap); + ecc_put_k(key); + return err; } #endif #ifdef WOLFSSL_SP_521 if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SECP521R1) { - return sp_ecc_check_key_521(key->pubkey.x, key->pubkey.y, + err = sp_ecc_check_key_521(key->pubkey.x, key->pubkey.y, (priv && (key->type == ECC_PRIVATEKEY)) ? ecc_get_k(key) : NULL, key->heap); + ecc_put_k(key); + return err; } #endif #if defined(WOLFSSL_SP_1024) && defined(WOLFCRYPT_HAVE_SAKKE) if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SAKKE_1) { - return sp_ecc_check_key_1024(key->pubkey.x, key->pubkey.y, + err = sp_ecc_check_key_1024(key->pubkey.x, key->pubkey.y, (priv && (key->type == ECC_PRIVATEKEY)) ? ecc_get_k(key) : NULL, key->heap); + ecc_put_k(key); + return err; } #endif #endif @@ -11555,6 +11610,7 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv) if (err == MP_OKAY && key->type == ECC_PRIVATEKEY) err = ecc_check_privkey_gen(key, curve->Af, curve->prime); #endif + ecc_put_k(key); } wc_ecc_curve_free(curve); @@ -12123,6 +12179,7 @@ static int _ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, err = wc_export_int(ecc_get_k(key), d, dLen, keySz + WC_CAAM_MAC_SZ, encType); + ecc_put_k(key); *dLen = keySz + WC_CAAM_MAC_SZ; } else if (encType == WC_TYPE_BLACK_KEY && @@ -12144,6 +12201,7 @@ static int _ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, #endif { err = wc_export_int(ecc_get_k(key), d, dLen, keySz, encType); + ecc_put_k(key); if (err != MP_OKAY) return err; } @@ -12305,12 +12363,14 @@ static int ecc_check_privkey_range(ecc_key* key) * DER it round-trips through carries a zero private value. Range * checking still applies to a scalar that is actually present. */ if (mp_iszero(k)) { + ecc_put_k(key); return 0; } #endif /* SP 800-56Ar3, section 5.6.2.1.2 - private keys are in [1, n-1]. */ if (mp_iszero(k) || mp_isneg(k)) { + ecc_put_k(key); return ECC_PRIV_KEY_E; } @@ -12335,6 +12395,7 @@ static int ecc_check_privkey_range(ecc_key* key) mp_clear(order); } WC_FREE_VAR_EX(order, key->heap, DYNAMIC_TYPE_ECC); + ecc_put_k(key); return ret; } @@ -12374,8 +12435,11 @@ static int _ecc_import_private_key_ex(const byte* priv, word32 privSz, key->type = ECC_PRIVATEKEY_ONLY; } - if (ret != 0) + if (ret != 0) { + ecc_forcezero_k(key); + key->type = 0; return ret; + } #if defined(WOLFSSL_QNX_CAAM) || defined(WOLFSSL_IMXRT1170_CAAM) if ((wc_ecc_size(key) + WC_CAAM_MAC_SZ) == (int)privSz) { @@ -12476,6 +12540,13 @@ static int _ecc_import_private_key_ex(const byte* priv, word32 privSz, #endif + if (ret != 0) { + /* Rejected scalar must not stay in the key + * (ISO/IEC 19790:2012 7.9.7). */ + ecc_forcezero_k(key); + key->type = (pub != NULL) ? ECC_PUBLICKEY : 0; + } + #ifdef WOLFSSL_MAXQ10XX_CRYPTO if ((ret == 0) && (key->devId != INVALID_DEVID)) { ret = wc_MAXQ10XX_EccSetKey(key, key->dp->size); @@ -12917,7 +12988,7 @@ static int _ecc_import_raw_private(ecc_key* key, const char* qx, mp_clear(key->pubkey.x); mp_clear(key->pubkey.y); mp_clear(key->pubkey.z); - mp_forcezero(key->k); + ecc_forcezero_k(key); #if defined(WOLFSSL_XILINX_CRYPT_VERSAL) ForceZero(key->keyRaw, sizeof(key->keyRaw)); #endif diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 8fcdda78794..4e852f9c394 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -401,6 +401,12 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, if (ret == 0) ret = NO_VALID_DEVID; #else +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(az, 0, sizeof(az)); + wc_MemZero_Add("wc_ed25519_make_public az", az, sizeof(az)); +#endif if (ret == 0) ret = ed25519_hash(key, key->k, ED25519_KEY_SIZE, az); if (ret == 0) { @@ -417,8 +423,16 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, ED25519_KEY_SIZE, &publicKey, kLTC_Ed25519 /* result on Ed25519 */); LTC_PKHA_Ed25519_Compress(&publicKey, pubKey); #else + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_ed25519_make_public A", &A, sizeof(A)); + #endif ge_scalarmult_base(&A, az); ge_p3_tobytes(pubKey, &A); + /* Projective A leaks bits of the private scalar. */ + ForceZero(&A, sizeof(A)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(&A, sizeof(A)); + #endif #endif if (storePub) @@ -427,6 +441,14 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, } #endif /* WOLF_CRYPTO_CB_ONLY_ED25519 */ +#ifndef WOLF_CRYPTO_CB_ONLY_ED25519 + /* az holds the clamped secret scalar (ISO/IEC 19790:2012 7.9.7). The + * crypto-callback return above happens before az is written. */ + ForceZero(az, sizeof(az)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(az, sizeof(az)); +#endif +#endif return ret; } @@ -493,6 +515,13 @@ int wc_ed25519_make_key(WC_RNG* rng, int keySz, ed25519_key* key) if (ret == 0) { ret = ed25519_pairwise_consistency_test(key, rng); } + if (ret != 0) { + /* Do not hand back a key that failed its check or PCT. */ + key->privKeySet = 0; + key->pubKeySet = 0; + ForceZero(key->k, ED25519_PRV_KEY_SIZE); + ForceZero(key->p, ED25519_PUB_KEY_SIZE); + } #endif return ret; @@ -685,6 +714,10 @@ int wc_ed25519_sign_msg_ex(const byte* in, word32 inLen, byte* out, #ifndef WOLFSSL_ED25519_PERSISTENT_SHA ed25519_hash_free(key, sha); WC_FREE_VAR_EX(sha, key->heap, DYNAMIC_TYPE_HASHES); +#else + /* On failure the absorbed prefix must not stay in the key's hash. */ + if (ret != 0) + ed25519_hash_free(key, sha); #endif } @@ -702,8 +735,16 @@ int wc_ed25519_sign_msg_ex(const byte* in, word32 inLen, byte* out, /* step 2: computing R = rB where rB is the scalar multiplication of r and B */ + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_ed25519_sign_msg_ex R", &R, sizeof(R)); + #endif ge_scalarmult_base(&R,nonce); ge_p3_tobytes(out,&R); + /* Projective R leaks bits of the nonce. */ + ForceZero(&R, sizeof(R)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(&R, sizeof(R)); + #endif #endif } @@ -1828,10 +1869,18 @@ int wc_ed25519_export_key(const ed25519_key* key, int ret; /* export 'full' private part */ + /* Check the public arguments before anything is written to priv. */ + if ((pub == NULL) || (pubSz == NULL)) { + return BAD_FUNC_ARG; + } ret = wc_ed25519_export_private(key, priv, privSz); if (ret == 0) { /* export public part */ ret = wc_ed25519_export_public(key, pub, pubSz); + if (ret != 0) { + /* Public export failed: do not hand back the private key. */ + ForceZero(priv, *privSz); + } } return ret; diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 0fc8bf5dc88..596285c5915 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -428,6 +428,10 @@ int wc_ed448_make_public(ed448_key* key, unsigned char* pubKey, word32 pubKeySz) ret = ECC_PRIV_KEY_E; } +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_ed448_make_public az", az, sizeof(az)); + wc_MemZero_Add("wc_ed448_make_public A", &A, sizeof(A)); +#endif if (ret == 0) ret = ed448_hash(key, key->k, ED448_KEY_SIZE, az, sizeof(az)); @@ -448,6 +452,15 @@ int wc_ed448_make_public(ed448_key* key, unsigned char* pubKey, word32 pubKeySz) key->pubKeySet = 1; } + /* az holds the clamped secret scalar (ISO/IEC 19790:2012 7.9.7). */ + /* Projective A leaks bits of the private scalar. */ + ForceZero(&A, sizeof(A)); + ForceZero(az, sizeof(az)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(&A, sizeof(A)); + wc_MemZero_Check(az, sizeof(az)); +#endif + return ret; } @@ -527,6 +540,13 @@ int wc_ed448_make_key(WC_RNG* rng, int keySz, ed448_key* key) if (ret == 0) { ret = ed448_pairwise_consistency_test(key, rng); } + if (ret != 0) { + /* Do not hand back a key that failed its check or PCT. */ + key->privKeySet = 0; + key->pubKeySet = 0; + ForceZero(key->k, ED448_PRV_KEY_SIZE); + ForceZero(key->p, ED448_PUB_KEY_SIZE); + } } #endif @@ -607,6 +627,7 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, XMEMSET(nonce, 0, sizeof(nonce)); wc_MemZero_Add("wc_ed448_sign_msg_ex az", az, sizeof(az)); wc_MemZero_Add("wc_ed448_sign_msg_ex nonce", nonce, sizeof(nonce)); + wc_MemZero_Add("wc_ed448_sign_msg_ex R", &R, sizeof(R)); #ifdef WOLFSSL_EDDSA_CHECK_PRIV_ON_SIGN XMEMSET(orig_k, 0, sizeof(orig_k)); wc_MemZero_Add("wc_ed448_sign_msg_ex orig_k", orig_k, sizeof(orig_k)); @@ -674,6 +695,11 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, } #ifndef WOLFSSL_ED448_PERSISTENT_SHA ed448_hash_free(key, sha); +#else + /* On failure the absorbed prefix must not stay in the key's hash. */ + if (ret != 0) { + ed448_hash_free(key, sha); + } #endif } if (ret == 0) { @@ -722,7 +748,8 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, #endif } #ifndef WOLFSSL_ED448_PERSISTENT_SHA - WC_FREE_VAR_EX(sha, key->heap, DYNAMIC_TYPE_HASHES); + /* key may be NULL here and XFREE evaluates its heap argument. */ + WC_FREE_VAR_EX(sha, key ? key->heap : NULL, DYNAMIC_TYPE_HASHES); #endif if (ret == 0) { @@ -747,7 +774,10 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, ForceZero(az, sizeof(az)); ForceZero(nonce, sizeof(nonce)); + /* Projective R leaks bits of the nonce. */ + ForceZero(&R, sizeof(R)); #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(&R, sizeof(R)); wc_MemZero_Check(nonce, sizeof(nonce)); wc_MemZero_Check(az, sizeof(az)); #endif @@ -985,6 +1015,7 @@ static int ed448_verify_msg_final_with_sha(const byte* sig, word32 sigLen, return BAD_FUNC_ARG; } + /* uncompress A (public key), test if valid, and negate it */ if (ge448_from_bytes_negate_vartime(&A, key->p) != 0) return BAD_FUNC_ARG; @@ -1719,10 +1750,18 @@ int wc_ed448_export_key(const ed448_key* key, byte* priv, word32 *privSz, int ret = 0; /* export 'full' private part */ + /* Check the public arguments before anything is written to priv. */ + if ((pub == NULL) || (pubSz == NULL)) { + return BAD_FUNC_ARG; + } ret = wc_ed448_export_private(key, priv, privSz); if (ret == 0) { /* export public part */ ret = wc_ed448_export_public(key, pub, pubSz); + if (ret != 0) { + /* Public export failed: do not hand back the private key. */ + ForceZero(priv, *privSz); + } } return ret; diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index fb1c447bfc2..b1a24dc6752 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -536,7 +536,7 @@ int wc_HmacCopy(Hmac* src, Hmac* dst) { if (hashes_copied >= 3) HmacKeyFreeHash(src->macType, &dst->o_hash); #endif - XMEMSET(dst, 0, sizeof(*dst)); + ForceZero(dst, sizeof(*dst)); } return ret; } @@ -1156,6 +1156,20 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, } #endif +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(6,0,0) + if (ret != 0) { + #ifdef WOLF_CRYPTO_CB + int devId = hmac->devId; + #endif + /* The key entered the object; clear it (ISO/IEC 19790 7.9.7). */ + wc_HmacFree(hmac); + hmac->heap = heap; + #ifdef WOLF_CRYPTO_CB + hmac->devId = devId; + #endif + } +#endif + return ret; #endif /* WOLFSSL_MAXQ108X */ } @@ -2014,6 +2028,10 @@ int wolfSSL_GetHmacMaxSize(void) outIdx += left; n++; } + if (ret != 0) { + /* Partial OKM is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(out, outSz); + } ForceZero(tmp, WC_MAX_DIGEST_SIZE); #ifdef WOLFSSL_CHECK_MEM_ZERO diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 3f7aa7bcd28..9fe011a10c2 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -207,6 +207,10 @@ int wc_PRF(byte* result, word32 resLen, const byte* secret, } wc_HmacFree(hmac); } + if (ret != 0) { + /* Partial PRF output is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(result, resLen); + } ForceZero(current, P_HASH_MAX_SIZE); ForceZero(hmac, sizeof(Hmac)); @@ -826,6 +830,9 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, ret = _HashInit(enmhashId, &hash); if (ret != 0) return ret; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_SSH_KDF hash", &hash, sizeof(hash)); +#endif ret = _HashUpdate(enmhashId, &hash, kSzFlat, LENGTH_SZ); if (ret == 0 && kPad) @@ -843,9 +850,18 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, if (blocks == 0) { if (remainder > 0) { byte lastBlock[WC_MAX_DIGEST_SIZE]; + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_SSH_KDF lastBlock", lastBlock, + sizeof(lastBlock)); + #endif ret = _HashFinal(enmhashId, &hash, lastBlock); if (ret == 0) XMEMCPY(key, lastBlock, remainder); + /* lastBlock held derived key material (ISO/IEC 19790 7.9). */ + ForceZero(lastBlock, sizeof(lastBlock)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(lastBlock, sizeof(lastBlock)); + #endif } } else { @@ -854,9 +870,9 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, runningKeySz = digestSz; ret = _HashFinal(enmhashId, &hash, key); - for (curBlock = 1; curBlock < blocks; curBlock++) { - ret = _HashInit(enmhashId, &hash); - if (ret != 0) break; + for (curBlock = 1; (ret == 0) && (curBlock < blocks); curBlock++) { + /* Final resets the hash for reuse. Reinitializing here would + * lose owned resources such as the cached SHA-256 schedule. */ ret = _HashUpdate(enmhashId, &hash, kSzFlat, LENGTH_SZ); if (ret != 0) break; if (kPad) @@ -875,8 +891,11 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, if (remainder > 0) { byte lastBlock[WC_MAX_DIGEST_SIZE]; - if (ret == 0) - ret = _HashInit(enmhashId, &hash); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_SSH_KDF lastBlock", lastBlock, + sizeof(lastBlock)); + #endif + /* Reuse the reset context from the preceding Final. */ if (ret == 0) ret = _HashUpdate(enmhashId, &hash, kSzFlat, LENGTH_SZ); if (ret == 0 && kPad) @@ -891,11 +910,24 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, ret = _HashFinal(enmhashId, &hash, lastBlock); if (ret == 0) XMEMCPY(key + runningKeySz, lastBlock, remainder); + /* lastBlock held derived key material (ISO/IEC 19790 7.9). */ + ForceZero(lastBlock, sizeof(lastBlock)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(lastBlock, sizeof(lastBlock)); + #endif } } } _HashFree(enmhashId, &hash); + /* hash absorbed the shared secret K (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(&hash, sizeof(hash)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(&hash, sizeof(hash)); +#endif + if (ret != 0) { + ForceZero(key, keySz); + } return ret; } @@ -1081,6 +1113,15 @@ int wc_SRTP_KDF(const byte* key, word32 keySz, const byte* salt, word32 saltSz, ret = wc_srtp_kdf_derive_key(block, WC_SRTP_INDEX_LEN, WC_SRTP_LABEL_SALT, key3, key3Sz, aes); } + if ((ret != 0) && aes_inited) { + /* Derived keys are not released (ISO/IEC 19790 7.9.7). */ + if (key1 != NULL) + ForceZero(key1, key1Sz); + if (key2 != NULL) + ForceZero(key2, key2Sz); + if (key3 != NULL) + ForceZero(key3, key3Sz); + } if (aes_inited) wc_AesFree(aes); @@ -1175,6 +1216,15 @@ int wc_SRTCP_KDF_ex(const byte* key, word32 keySz, const byte* salt, word32 salt ret = wc_srtp_kdf_derive_key(block, idxLen, WC_SRTCP_LABEL_SALT, key3, key3Sz, aes); } + if ((ret != 0) && aes_inited) { + /* Derived keys are not released (ISO/IEC 19790 7.9.7). */ + if (key1 != NULL) + ForceZero(key1, key1Sz); + if (key2 != NULL) + ForceZero(key2, key2Sz); + if (key3 != NULL) + ForceZero(key3, key3Sz); + } if (aes_inited) wc_AesFree(aes); @@ -1256,6 +1306,9 @@ int wc_SRTP_KDF_label(const byte* key, word32 keySz, const byte* salt, ret = wc_srtp_kdf_derive_key(block, WC_SRTP_INDEX_LEN, label, outKey, outKeySz, aes); } + if ((ret != 0) && aes_inited) { + ForceZero(outKey, outKeySz); + } if (aes_inited) wc_AesFree(aes); @@ -1329,6 +1382,9 @@ int wc_SRTCP_KDF_label(const byte* key, word32 keySz, const byte* salt, ret = wc_srtp_kdf_derive_key(block, WC_SRTCP_INDEX_LEN, label, outKey, outKeySz, aes); } + if ((ret != 0) && aes_inited) { + ForceZero(outKey, outKeySz); + } if (aes_inited) wc_AesFree(aes); @@ -1568,6 +1624,11 @@ int wc_KDA_KDF_twostep_cmac(const byte * salt, word32 salt_len, #endif return MEMORY_E; } + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Aes part is checked by wc_AesFree. */ + wc_MemZero_Add("wc_KDA_KDF_twostep_cmac cmac", + ((unsigned char *)cmac) + sizeof(Aes), sizeof(Cmac) - sizeof(Aes)); + #endif #endif /* step 1: cmac extract */ @@ -1584,6 +1645,7 @@ int wc_KDA_KDF_twostep_cmac(const byte * salt, word32 salt_len, #ifdef WOLFSSL_SMALL_STACK if (cmac) { + ForceZero(cmac, sizeof(Cmac)); XFREE(cmac, heap, DYNAMIC_TYPE_CMAC); cmac = NULL; } @@ -1666,6 +1728,11 @@ int wc_KDA_KDF_PRF_cmac(const byte* Kin, word32 KinSz, if (cmac == NULL) { return MEMORY_E; } + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Aes part is checked by wc_AesFree. */ + wc_MemZero_Add("wc_KDA_KDF_PRF_cmac cmac", + ((unsigned char *)cmac) + sizeof(Aes), sizeof(Cmac) - sizeof(Aes)); + #endif #endif while (len_rem >= WC_AES_BLOCK_SIZE) { @@ -1757,6 +1824,7 @@ int wc_KDA_KDF_PRF_cmac(const byte* Kin, word32 KinSz, #ifdef WOLFSSL_SMALL_STACK if (cmac) { + ForceZero(cmac, sizeof(Cmac)); XFREE(cmac, heap, DYNAMIC_TYPE_CMAC); cmac = NULL; } diff --git a/wolfcrypt/src/port/microchip/mpfs_athena.c b/wolfcrypt/src/port/microchip/mpfs_athena.c index 44c325d041d..e3d66df0c48 100644 --- a/wolfcrypt/src/port/microchip/mpfs_athena.c +++ b/wolfcrypt/src/port/microchip/mpfs_athena.c @@ -562,8 +562,13 @@ static int wc_MpfsAthenaShaCopyDev(wc_CryptoInfo* info) * _Transform_Sha512() and wc_Sha512Transform() and is allocated rather than * copied, and its size carries that second buffer. */ #if defined(WOLFSSL_SMALL_STACK_CACHE) && !defined(WC_SHA2_NO_SMALL_STACK) + #ifdef WC_SHA512_TSCRATCH_SIZE + dst->W = (word64*)XMALLOC(WC_SHA512_TSCRATCH_SIZE + WC_SHA384_BLOCK_SIZE, + dst->heap, DYNAMIC_TYPE_DIGEST); + #else /* old FIPS */ dst->W = (word64*)XMALLOC((sizeof(word64) * 16) + WC_SHA384_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); + #endif if (dst->W == NULL) { /* msg still aliases the source here, so it is dropped, not freed. */ ForceZero(dst, sizeof(wc_Sha384)); diff --git a/wolfcrypt/src/port/nuvoton/nuvoton_cb_hash.c b/wolfcrypt/src/port/nuvoton/nuvoton_cb_hash.c index 3fa9b2bb181..f95d7ea7c5a 100644 --- a/wolfcrypt/src/port/nuvoton/nuvoton_cb_hash.c +++ b/wolfcrypt/src/port/nuvoton/nuvoton_cb_hash.c @@ -479,8 +479,13 @@ static int nuvoton_hash_dup_owned(int type, void* src, void* dst) wc_Sha256* d = (wc_Sha256*)dst; (void)s; (void)d; #ifdef NUVOTON_HASH_OWNS_W + #ifdef WC_SHA256_TSCRATCH_SIZE + d->W = (word32*)XMALLOC(WC_SHA256_TSCRATCH_SIZE, + d->heap, DYNAMIC_TYPE_DIGEST); + #else /* old FIPS */ d->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, d->heap, DYNAMIC_TYPE_DIGEST); + #endif if (d->W == NULL) { ret = MEMORY_E; } @@ -517,8 +522,13 @@ static int nuvoton_hash_dup_owned(int type, void* src, void* dst) wc_Sha512* d = (wc_Sha512*)dst; (void)s; (void)d; #ifdef NUVOTON_HASH_OWNS_W + #ifdef WC_SHA512_TSCRATCH_SIZE + d->W = (word64*)XMALLOC(WC_SHA512_TSCRATCH_SIZE + + WC_SHA512_BLOCK_SIZE, d->heap, DYNAMIC_TYPE_DIGEST); + #else /* old FIPS */ d->W = (word64*)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, d->heap, DYNAMIC_TYPE_DIGEST); + #endif if (d->W == NULL) { ret = MEMORY_E; } diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index 770b2e4c1b7..206da4b079c 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -227,6 +227,8 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, Hmac hmac[1]; #endif enum wc_HashType hashT; + byte* outStart = output; + int outLen = kLen; if (output == NULL || pLen < 0 || sLen < 0 || kLen < 0) { return BAD_FUNC_ARG; @@ -362,6 +364,10 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, } wc_HmacFree(hmac); } + if (ret != 0) { + /* Partial output is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(outStart, (word32)outLen); + } ForceZero(buffer, (word32)hLen); #if !defined(WOLFSSL_SMALL_STACK) && defined(WOLFSSL_CHECK_MEM_ZERO) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index a30111f04a2..de31b38d82f 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1040,8 +1040,8 @@ static WARN_UNUSED_RESULT int Hash256_DRBG_Reseed(DRBG_internal* drbg, * NextSeedProducerRelease()) -- the sentinel alone suppresses the pre-event * material; abandoned seed-aperture buffers are zeroized as explained below. * All other states purge directly to EMPTY. (The uncredited stir aperture - * keeps its plain-store purge: stirs carry no claims, so resurrection there is - * benign by the three-no-ops doctrine.) + * is purged by NextStirPurge(): stirs carry no claims, so resurrection there + * is benign by the three-no-ops doctrine.) * * Zeroization doctrine for purges: abandonment here is event-driven (fork, * VM clone/resume, credited reseed), and the event that abandons bytes in @@ -1057,9 +1057,7 @@ static WARN_UNUSED_RESULT int Hash256_DRBG_Reseed(DRBG_internal* drbg, * wc_RNG_DRBG_NextSeedNow_Nonce_local()'s failed-release arm. Reopening * EMPTY from under a live holder would let a producer claim and begin a * fill that the holder's own trailing wipe then tears. - * Contrast the health-test burn arm, which stays sentinel-only: RCT/APT - * are deterministic on the bytes, so every sibling rejects the same - * material identically and no lineage can have consumed it. + * The health-test burn arm wipes under its own PRODUCING claim. */ static WARN_UNUSED_RESULT int NextSeedPurge(WC_RNG *rng) { @@ -1143,6 +1141,57 @@ static int NextSeedProducerRelease(wolfSSL_Atomic_Int *lenp, WC_DRBG_NEXT_SEED_EMPTY); return BUSY_E; } + +/* Purge the uncredited stir accumulator. Claimed CONSUMING first so the wipe + * runs under ownership; a holder's burn-before-release covers the CONSUMING + * case, and a constructor's _TORNDOWN is never claimed or repainted (the + * reopen is its to make). Blind depositors can tear the wipe, which is + * harmless. */ +static WC_MAYBE_UNUSED void NextStirPurge(WC_RNG* rng) +{ + WC_ATOMIC_INT_ARG cur; + int ret; + + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->nextStirLen, cur, ret) { + if ((cur == WC_DRBG_NEXT_SEED_CONSUMING) || + (cur == WC_DRBG_NEXT_SEED_TORNDOWN)) + { + return; + } + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, + &rng->nextStirLen, cur, + WC_DRBG_NEXT_SEED_CONSUMING, ret); + } WC_CAS_WITH_RETRY_END; + if (ret != 0) { + /* Aborted claim (a port's retry clause): no claim, so no wipe -- + * and no store either, which could repaint a live claim or a + * constructor's _TORNDOWN. The stale fill stands; resurrection + * of uncredited stir material is benign by the three-no-ops + * doctrine, and the next purge or teardown collects it. */ + return; + } + ForceZero(rng->nextStir, (word32)sizeof(rng->nextStir)); + /* Reopen by CAS, never by plain store: a blind depositor can have + * stored a fill length over the claim (discarded, as before -- + * length only; its bytes lie in place until the next wipe or + * full-buffer fold, benign for the uncredited stir), and a + * constructor can have painted the word _TORNDOWN at any point, the + * reopen then being its to make, not ours. */ + WC_CAS_WITH_RETRY_BEGIN_INIT_CUR(&rng->nextStirLen, cur, ret) { + if (cur == WC_DRBG_NEXT_SEED_TORNDOWN) + return; + WC_CAS_WITH_RETRY_LOOP_FOREVER(wolfSSL_Atomic_Int_CompareExchange, + &rng->nextStirLen, cur, + WC_DRBG_NEXT_SEED_EMPTY, ret); + } WC_CAS_WITH_RETRY_END; + if (ret != 0) { + /* Aborted reopen: leaving the word CONSUMING would silence the + * stir aperture on this instance, so store EMPTY -- the + * _TORNDOWN-repaint risk on this already-exotic path is accepted, + * and teardown's own wipe and sentinel follow regardless. */ + WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); + } +} #endif /* WC_RNG_HAVE_NEXT_SEED */ /* in_bracketed_consume: nonzero when the caller feeds this reseed from its own @@ -1270,11 +1319,8 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, /* the uncredited stir aperture is purged too, for provenance * uniformity; best-effort (an in-flight depositor may * resurrect a partial fill -- benign, stirs carry no - * divergence burden), and never zeroized (racy, and - * interleaved entropy of compatible provenance is harmless). - */ - WOLFSSL_ATOMIC_STORE(rng->nextStirLen, - WC_DRBG_NEXT_SEED_EMPTY); + * divergence burden). */ + NextStirPurge(rng); } } #else @@ -3027,6 +3073,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz) /* Accumulate failure flag - once set, stays set */ rctFailed |= (repCount >= WC_RNG_SEED_RCT_CUTOFF); } + ForceZero(&prevByte, sizeof(prevByte)); } /* SP800-90B 4.4.2 Adaptive Proportion Test: the first byte of each window @@ -3061,6 +3108,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz) } aptFailed |= (matches >= cutoff); + ForceZero(&refByte, sizeof(refByte)); } } } @@ -3101,6 +3149,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz) } aptFailed |= (count >= cutoff); + ForceZero(&cand, sizeof(cand)); } } } @@ -3701,10 +3750,15 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, } if (ret == 0) { + /* Set before the call, as for drbg_instantiated: a failed + * instantiate still leaves V in the state, and the failure + * arm below must uninstantiate it. (The scratch is + * NULL-seeded, so the stranded state would be a public + * constant -- wiped anyway, for uniformity of the + * invariant.) */ + drbg_scratch_instantiated = 1; ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg_scratch, NULL, NULL, 0, NULL, 0, NULL, 0, rng->heap, devId); - if (ret == 0) - drbg_scratch_instantiated = 1; } if (ret == 0) { @@ -3750,10 +3804,10 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, } if (ret == 0) { + /* Set before the call -- see the SHA-256 scratch arm. */ + drbg_scratch_instantiated = 1; ret = Hash512_DRBG_Instantiate(rng->drbg512_scratch, NULL, NULL, 0, NULL, 0, NULL, 0, rng->heap, devId); - if (ret == 0) - drbg_scratch_instantiated = 1; } if (ret == 0) { @@ -3925,6 +3979,9 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, /* Instantiate the DRBG */ if (ret == DRBG_SUCCESS) { + /* Set before the call: a failed instantiate still leaves V in + * the state, and the failure arm below must uninstantiate it. */ + drbg_instantiated = 1; #ifndef NO_SHA256 if (rng->drbgType == WC_DRBG_SHA256) ret = Hash_DRBG_Instantiate((DRBG_internal *)rng->drbg, @@ -3948,8 +4005,6 @@ static WARN_UNUSED_RESULT int _InitRng(WC_RNG* rng, #endif nonce, nonceSz, perso, persoSz, rng->heap, devId); #endif - if (ret == 0) - drbg_instantiated = 1; } } /* ret == 0 */ @@ -5114,8 +5169,7 @@ WOLFSSL_API int wc_RNG_invalidate_entropy(WC_RNG* rng) { #endif #ifdef WC_RNG_HAVE_NEXT_SEED - WOLFSSL_ATOMIC_STORE(rng->nextStirLen, - WC_DRBG_NEXT_SEED_EMPTY); + NextStirPurge(rng); { int ret2 = NextSeedPurge(rng); @@ -5823,6 +5877,8 @@ int wc_RNG_Pool_Extract(WC_RNG* rng, byte* out, word32* n) WOLFSSL_ATOMIC_STORE(rng->poolTail, WC_RNG_POOL_PACK(WC_RNG_POOL_POS(w2), WC_RNG_POOL_EPOCH(w2))); + /* The copied bytes predate the event too. */ + ForceZero(out, done); #ifdef WC_RNG_DEBUG_STATS rng->_stats_pool_bytes_missed += *n; #endif @@ -6819,16 +6875,11 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( #ifdef WC_RNG_DEBUG_STATS ++rng->_stats_seed_failures; #endif - /* Use-once on a failed test is enforced by the sentinel - * alone: an EMPTY aperture is never consumed, and the next - * fill overwrites from offset zero. The buffer is never - * zeroized (house rule for the seed apertures). Burn and - * purge-discard converge on EMPTY; the release handles - * both, and the health-test failure is the more + /* Wiped under the PRODUCING claim: a purge only repaints the + * sentinel, so the buffer is still ours. Burn and purge-discard + * converge on EMPTY, and the health-test failure is the more * informative code and wins over the release's BUSY_E. */ - /* Sentinel-only by doctrine: rejection here is deterministic - * on the bytes (RCT/APT), so every sibling lineage rejects the - * identical material -- no copy is ever consumed anywhere. */ + ForceZero(rng->nextSeed, nextSeedSz); (void)NextSeedProducerRelease(&rng->nextSeedLen, rng->nextSeed, nextSeedSz, WC_DRBG_NEXT_SEED_EMPTY); @@ -7923,6 +7974,10 @@ int wc_FreeRng(WC_RNG* rng) rng->drbg = NULL; #endif } + #if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) + /* A failed instantiate nulls rng->drbg with the state still in here. */ + ForceZero(&rng->drbg_data, sizeof(rng->drbg_data)); + #endif #ifdef WOLFSSL_SMALL_STACK_CACHE /* Scratch buffers are tracked independently of rng->drbg so that a @@ -7972,6 +8027,9 @@ int wc_FreeRng(WC_RNG* rng) rng->drbg512 = NULL; #endif } + #if defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) + ForceZero(&rng->drbg512_data, sizeof(rng->drbg512_data)); + #endif #ifdef WOLFSSL_SMALL_STACK_CACHE /* Same independence rationale as the SHA-256 scratch above. */ @@ -9105,28 +9163,25 @@ static WC_MAYBE_UNUSED int wc_GenerateSeed_IntelRD(OS_Seed* os, byte* output, word64 sanity_word1 = 0, sanity_word2 = 0; ret = IntelRDseed64_r(&sanity_word1); - if (ret != 0) - return ret; - - ret = IntelRDseed64_r(&sanity_word2); - if (ret != 0) - return ret; - - if (sanity_word1 == sanity_word2) { + if (ret == 0) + ret = IntelRDseed64_r(&sanity_word2); + if ((ret == 0) && (sanity_word1 == sanity_word2)) { ret = IntelRDseed64_r(&sanity_word1); - if (ret != 0) - return ret; - - if (sanity_word1 == sanity_word2) { + if ((ret == 0) && (sanity_word1 == sanity_word2)) { #ifdef WC_VERBOSE_RNG WOLFSSL_DEBUG_PRINTF( "WARNING: disabling RDSEED due to repeating word 0x%lx -- " "check CPU microcode version.", sanity_word2); #endif rdseed_sanity_status = -1; - return WC_HW_E; + ret = WC_HW_E; } } + /* Raw source output, used only for the self-check. */ + ForceZero(&sanity_word1, sizeof(sanity_word1)); + ForceZero(&sanity_word2, sizeof(sanity_word2)); + if (ret != 0) + return ret; rdseed_sanity_status = 1; } @@ -9483,7 +9538,7 @@ void wc_NoiseSrc_Free(wc_NoiseSrc* src) if (src->work != NULL && src->workSz > 0) { ForceZero(src->work, src->workSz); } - XMEMSET(src->health, 0, sizeof(src->health)); + ForceZero(src->health, sizeof(src->health)); src->chunkCtr = 0; src->failed = 0; src->degraded = 0; @@ -10006,6 +10061,7 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) if(size==0)break; } } while(size); + ForceZero(rnd, sizeof(rnd)); return 0; } #else /* WOLFSSL_PIC32MZ_RNG */ diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 8f5f10d4304..f59104af1c8 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -587,6 +587,20 @@ int wc_RsaGetKeyId(RsaKey* key, word32* keyId) } #endif /* WOLFSSL_SE050 */ +#ifndef WOLFSSL_RSA_PUBLIC_ONLY +static void RsaForceZeroPriv(RsaKey* key) +{ +#if defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA) || !defined(RSA_LOW_MEM) + mp_forcezero(&key->u); + mp_forcezero(&key->dQ); + mp_forcezero(&key->dP); +#endif + mp_forcezero(&key->q); + mp_forcezero(&key->p); + mp_forcezero(&key->d); +} +#endif + int wc_FreeRsaKey(RsaKey* key) { int ret = 0; @@ -622,17 +636,8 @@ int wc_FreeRsaKey(RsaKey* key) #endif #ifndef WOLFSSL_RSA_PUBLIC_ONLY - /* Forcezero all private key fields that are present in this build - * configuration, since they may contain residual sensitive data even when - * key->type is not RSA_PRIVATE (e.g., after a partial key decode failure). */ -#if defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA) || !defined(RSA_LOW_MEM) - mp_forcezero(&key->u); - mp_forcezero(&key->dQ); - mp_forcezero(&key->dP); -#endif - mp_forcezero(&key->q); - mp_forcezero(&key->p); - mp_forcezero(&key->d); + /* Private fields may hold residue even when type is not RSA_PRIVATE. */ + RsaForceZeroPriv(key); #endif /* WOLFSSL_RSA_PUBLIC_ONLY */ /* public part */ @@ -1095,6 +1100,19 @@ static int RsaMGF1(enum wc_HashType hType, byte* seed, word32 seedSz, } #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + if (tmpF) { + wc_MemZero_Add("RsaMGF1 tmp", tmp, tmpSz); + } + else { + wc_MemZero_Add("RsaMGF1 tmpA", tmpA, sizeof(tmpA)); + } +#else + wc_MemZero_Add("RsaMGF1 tmp", tmp, sizeof(tmp)); +#endif +#endif + do { int i = 0; XMEMCPY(tmp, seed, seedSz); @@ -1115,6 +1133,26 @@ static int RsaMGF1(enum wc_HashType hType, byte* seed, word32 seedSz, ret = wc_Hash(hType, tmp, (seedSz + 4), tmp, tmpSz); #endif if (ret != 0) { + /* tmp holds the OAEP seed (ISO/IEC 19790:2012 7.9.7). */ +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + if (tmpF) { + ForceZero(tmp, tmpSz); + } + else { + ForceZero(tmpA, sizeof(tmpA)); + } +#else + ForceZero(tmp, sizeof(tmp)); +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + if (!tmpF) { + wc_MemZero_Check(tmpA, sizeof(tmpA)); + } +#else + wc_MemZero_Check(tmp, sizeof(tmp)); +#endif +#endif /* check for if dynamic memory was needed, then free */ #ifdef WOLFSSL_SMALL_STACK_CACHE wc_HashFree(hash, hType); @@ -1133,6 +1171,26 @@ static int RsaMGF1(enum wc_HashType hType, byte* seed, word32 seedSz, } counter++; } while (idx < outSz); + /* tmp holds the OAEP seed (ISO/IEC 19790:2012 7.9.7). */ +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + if (tmpF) { + ForceZero(tmp, tmpSz); + } + else { + ForceZero(tmpA, sizeof(tmpA)); + } +#else + ForceZero(tmp, sizeof(tmp)); +#endif +#ifdef WOLFSSL_CHECK_MEM_ZERO +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + if (!tmpF) { + wc_MemZero_Check(tmpA, sizeof(tmpA)); + } +#else + wc_MemZero_Check(tmp, sizeof(tmp)); +#endif +#endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) /* check for if dynamic memory was needed, then free */ if (tmpF) { @@ -1406,10 +1464,17 @@ static int RsaPad_OAEP(const byte* input, word32 inputLen, byte* pkcsBlock, idx = idx - hLen + 1; XMEMCPY(pkcsBlock + idx, lHash, hLen); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Pad OAEP seed", seed, hLen); +#endif /* generate random seed */ if ((ret = wc_RNG_GenerateBlock(rng, seed, hLen)) != 0) { WC_FREE_VAR_EX(lHash, heap, DYNAMIC_TYPE_RSA_BUFFER); ForceZero(seed, hLen); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(seed, hLen); +#endif WC_FREE_VAR_EX(seed, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; } @@ -1426,21 +1491,43 @@ static int RsaPad_OAEP(const byte* input, word32 inputLen, byte* pkcsBlock, } #else if (pkcsBlockLen - hLen - 1 > sizeof(dbMask)) { + ForceZero(seed, hLen); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(seed, hLen); + #endif return MEMORY_E; } #endif XMEMSET(dbMask, 0, pkcsBlockLen - hLen - 1); /* help static analyzer */ +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Pad OAEP dbMask", dbMask, pkcsBlockLen - hLen - 1); +#endif ret = RsaMGF(mgf, seed, hLen, dbMask, pkcsBlockLen - hLen - 1, heap); if (ret != 0) { + ForceZero(dbMask, pkcsBlockLen - hLen - 1); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(dbMask, pkcsBlockLen - hLen - 1); +#endif WC_FREE_VAR_EX(dbMask, heap, DYNAMIC_TYPE_RSA); WC_FREE_VAR_EX(lHash, heap, DYNAMIC_TYPE_RSA_BUFFER); ForceZero(seed, hLen); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(seed, hLen); +#endif WC_FREE_VAR_EX(seed, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; } xorbuf(pkcsBlock + hLen + 1, dbMask,pkcsBlockLen - hLen - 1); + /* dbMask is derived from the seed (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(dbMask, pkcsBlockLen - hLen - 1); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(dbMask, pkcsBlockLen - hLen - 1); +#endif WC_FREE_VAR_EX(dbMask, heap, DYNAMIC_TYPE_RSA); /* create maskedSeed from seedMask */ @@ -1450,16 +1537,16 @@ static int RsaPad_OAEP(const byte* input, word32 inputLen, byte* pkcsBlock, pkcsBlock + 1, hLen, heap)) != 0) { WC_FREE_VAR_EX(lHash, heap, DYNAMIC_TYPE_RSA_BUFFER); ForceZero(seed, hLen); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(seed, hLen); +#endif WC_FREE_VAR_EX(seed, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; } /* xor created seedMask with seed to make maskedSeed */ xorbuf(pkcsBlock + 1, seed, hLen); -#ifdef WOLFSSL_CHECK_MEM_ZERO - /* Seed must be zeroized now that it has been used. */ - wc_MemZero_Add("Pad OAEP seed", seed, hLen); -#endif /* Zeroize masking bytes so that padding can't be unmasked. */ ForceZero(seed, hLen); @@ -1586,6 +1673,10 @@ static int RsaPad_PSS(const byte* input, word32 inputLen, byte* pkcsBlock, if (msg == NULL) { return MEMORY_E; } + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("RsaPad_PSS msg", msg, + RSA_PSS_PAD_SZ + inputLen + (word32)saltLen); + #endif #endif salt = s = m = msg; XMEMSET(m, 0, RSA_PSS_PAD_SZ); @@ -1608,6 +1699,10 @@ static int RsaPad_PSS(const byte* input, word32 inputLen, byte* pkcsBlock, if (msg == NULL) { return MEMORY_E; } + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("RsaPad_PSS msg", msg, + RSA_PSS_PAD_SZ + inputLen + (word32)saltLen); + #endif #endif m = msg; } @@ -1620,6 +1715,9 @@ static int RsaPad_PSS(const byte* input, word32 inputLen, byte* pkcsBlock, XMEMCPY(m, input, inputLen); m += inputLen; o = 0; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("RsaPad_PSS salt", salt, sizeof(salt)); +#endif if (saltLen > 0) { ret = wc_RNG_GenerateBlock(rng, salt, (word32)saltLen); if (ret == 0) { @@ -1649,9 +1747,19 @@ static int RsaPad_PSS(const byte* input, word32 inputLen, byte* pkcsBlock, xorbuf(m, salt + o, (word32)saltLen); } +#if !defined(WOLFSSL_PSS_LONG_SALT) && !defined(WOLFSSL_PSS_SALT_LEN_DISCOVER) + ForceZero(salt, sizeof(salt)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(salt, sizeof(salt)); +#endif +#endif #if !defined(WOLFSSL_NO_MALLOC) || defined(WOLFSSL_STATIC_MEMORY) - /* msg is always not NULL as we bail on allocation failure */ - XFREE(msg, heap, DYNAMIC_TYPE_RSA_BUFFER); + if (msg != NULL) { + ForceZero(msg, RSA_PSS_PAD_SZ + inputLen + (word32)saltLen); + XFREE(msg, heap, DYNAMIC_TYPE_RSA_BUFFER); + } +#else + ForceZero(msg, RSA_PSS_PAD_SZ + inputLen + (word32)saltLen); #endif return ret; } @@ -1835,6 +1943,11 @@ static int RsaUnPad_OAEP(byte *pkcsBlock, unsigned int pkcsBlockLen, ret = RsaMGF(mgf, (byte*)(pkcsBlock + (hLen + 1)), pkcsBlockLen - hLen - 1, tmp, hLen, heap); if (ret != 0) { + ForceZero(tmp, pkcsBlockLen); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(tmp, pkcsBlockLen); +#endif WC_FREE_VAR_EX(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; } @@ -1845,12 +1958,12 @@ static int RsaUnPad_OAEP(byte *pkcsBlock, unsigned int pkcsBlockLen, /* get dbMask value */ ret = RsaMGF(mgf, tmp, hLen, tmp + hLen, pkcsBlockLen - hLen - 1, heap); if (ret != 0) { - ForceZero(tmp, hLen); -#ifdef WOLFSSL_SMALL_STACK - XFREE(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); -#elif defined(WOLFSSL_CHECK_MEM_ZERO) - wc_MemZero_Check(tmp, hLen); + ForceZero(tmp, pkcsBlockLen); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(tmp, pkcsBlockLen); #endif + WC_FREE_VAR_EX(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; } @@ -1858,12 +1971,11 @@ static int RsaUnPad_OAEP(byte *pkcsBlock, unsigned int pkcsBlockLen, xorbuf(pkcsBlock + hLen + 1, tmp + hLen, pkcsBlockLen - hLen - 1); ForceZero(tmp, pkcsBlockLen); -#ifdef WOLFSSL_SMALL_STACK - /* done with use of tmp buffer */ - XFREE(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); -#elif defined(WOLFSSL_CHECK_MEM_ZERO) +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) wc_MemZero_Check(tmp, pkcsBlockLen); #endif + WC_FREE_VAR_EX(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); /* advance idx to index of PS and msg separator, account for PS size of 0*/ idx = hLen + 1 + hLen; @@ -1894,6 +2006,8 @@ static int RsaUnPad_OAEP(byte *pkcsBlock, unsigned int pkcsBlockLen, /* Return 0 data length on error. */ idx = ctMaskSelWord32(ctMaskEq(c, 0), idx, pkcsBlockLen); + /* c mixed in the decrypted Y byte. */ + c = 0; } /* adjust pointer to correct location in array and return size of M */ @@ -2143,6 +2257,11 @@ static int RsaUnPad(const byte *pkcsBlock, unsigned int pkcsBlockLen, *output = (const byte *)(pkcsBlock + i); invalidMask = (int)-1 + (int)(inv >> 7); ret = invalidMask & ((int)pkcsBlockLen - i); + /* Which padding check failed is not left behind. */ + pastSep = 0; + invalid = 0; + minPad = 0; + invalidMask = 0; } #endif @@ -3851,6 +3970,12 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, hash, mgf, label, labelSz, saltLen, RsaModulusBits(key), key->heap); if (ret < 0) { + /* SP 800-56Br2 7.2.2.3: destroy DB, EM and em on any exit, + * including an early error exit; the OAEP block is built in + * out (ISO/IEC 19790:2012 7.9.7). */ + if (pad_type == WC_RSA_OAEP_PAD) { + ForceZero(out, (word32)sz); + } break; } @@ -3867,6 +3992,16 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, key->state = RSA_STATE_ENCRYPT_RES; } if (ret < 0) { + /* SP 800-56Br2 7.2.2.3: EM is still in out after a failed RSAEP. + * A pending or would-block call resumes from it. */ + if ((pad_type == WC_RSA_OAEP_PAD) && + (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) + #ifdef WC_RSA_NONBLOCK + && (ret != FP_WOULDBLOCK) + #endif + ) { + ForceZero(out, (word32)sz); + } break; } @@ -5934,6 +6069,10 @@ int wc_MakeRsaKey(RsaKey* key, int size, long e, WC_RNG* rng) /* Take off blinding from d and reset e */ if (err == MP_OKAY) err = mp_mulmod(&key->d, &key->p, tmp3, &key->d); + /* The blind, its gcd and the blinded e were staged in p, q and e. */ + mp_forcezero(&key->p); + mp_forcezero(&key->q); + mp_forcezero(&key->e); if (err == MP_OKAY) err = mp_set_int(&key->e, (unsigned long)e); #endif diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 95a81843d93..4bb59a17e2d 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -503,6 +503,13 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) #ifdef USE_SLOW_SHA word32 t, i; + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer + * to an uninitialized object trips -Wmaybe-uninitialized under + * -Werror. */ + XMEMSET(W, 0, sizeof(W)); + wc_MemZero_Add("Sha Transform W", W, sizeof(W)); + #endif for (i = 0; i < 16; i++) { R0(a, b, c, d, e, i); t = e; e = d; d = c; c = b; b = a; a = t; @@ -530,6 +537,13 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) #else /* nearly 1 K bigger in code size but 25% faster */ /* 4 rounds of 20 operations each. Loop unrolled. */ + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a pointer + * to an uninitialized object trips -Wmaybe-uninitialized under + * -Werror. */ + XMEMSET(W, 0, sizeof(W)); + wc_MemZero_Add("Sha Transform W", W, sizeof(W)); + #endif R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); @@ -565,6 +579,10 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) (void)data; /* Not used */ + ForceZero(W, sizeof(W)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(W, sizeof(W)); + #endif return 0; } #endif /* XTRANSFORM when USE_SHA_SOFTWARE_IMPL is enabled */ @@ -834,6 +852,10 @@ int wc_ShaFinalRaw(wc_Sha* sha, byte* hash) if (sha == NULL || hash == NULL) { return BAD_FUNC_ARG; } +#if defined(WOLFSSL_CHECK_MEM_ZERO) && defined(LITTLE_ENDIAN_ORDER) && \ + !defined(WOLFSSL_WIDE_BYTE) + wc_MemZero_Add("wc_ShaFinalRaw digest", digest, sizeof(digest)); +#endif #if defined(WOLFSSL_WIDE_BYTE) /* CHAR_BIT != 8: write the digest words as big-endian octets. */ @@ -854,6 +876,10 @@ int wc_ShaFinalRaw(wc_Sha* sha, byte* hash) ByteReverseWords((word32*)digest, (word32*)sha->digest, WC_SHA_DIGEST_SIZE); } XMEMCPY(hash, (byte *)&digest[0], WC_SHA_DIGEST_SIZE); + ForceZero(digest, sizeof(digest)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(digest, sizeof(digest)); +#endif #else XMEMCPY(hash, sha->digest, WC_SHA_DIGEST_SIZE); #endif @@ -1186,6 +1212,10 @@ void wc_ShaFree(wc_Sha* sha) #if defined(PSOC6_HASH_SHA1) wc_Psoc6_Sha_Free(); #endif + + /* digest and buffer hold keyed material for HMAC-SHA1 and the SSH KDF + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(sha, sizeof(*sha)); } #endif /* !MAX3266X_SHA */ @@ -1217,12 +1247,20 @@ int wc_ShaGetHash(wc_Sha* sha, byte* hash) WC_CALLOC_VAR_EX(tmpSha, wc_Sha, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_ShaGetHash tmpSha", tmpSha, sizeof(*tmpSha)); +#endif ret = wc_ShaCopy(sha, tmpSha); if (ret == 0) { ret = wc_ShaFinal(tmpSha, hash); + wc_ShaFree(tmpSha); } + ForceZero(tmpSha, sizeof(*tmpSha)); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(tmpSha, sizeof(*tmpSha)); +#endif WC_FREE_VAR_EX(tmpSha, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 2642838d279..07ddeb60a5f 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -381,6 +381,72 @@ int wc_Sha256Reset(wc_Sha256* sha256) { #endif /* Hardware Acceleration */ +/* Caller-owned transient scratch for the C transform: the message schedule + * and the working variables, so one ForceZero when the public call returns + * wipes all of it at once, instead of per-block wipes inside the transform. + * Natural alignment suffices; only the C transform uses it. */ +typedef struct Sha256TScratch { +#ifdef SHA256_MANY_REGISTERS + /* The MANY_REGISTERS transform rolls a 16-word window. */ + word32 W[WC_SHA256_BLOCK_SIZE / sizeof(word32)]; +#else + word32 W[WC_SHA256_BLOCK_SIZE]; +#endif + word32 S[8]; +} Sha256TScratch; + +/* External W providers size by the header constant; keep them in lockstep. */ +wc_static_assert(sizeof(Sha256TScratch) == WC_SHA256_TSCRATCH_SIZE); + +/* Scratch provisioning for the callers that drive the C transform. The + * declared pointer is named sha256TScr. BEGIN validates it (and registers it + * with WOLFSSL_CHECK_MEM_ZERO); WIPE is the single ForceZero (and the + * mem-zero check). Under WOLFSSL_SMALL_STACK_CACHE the scratch is the + * per-context cache allocated at init and wiped at Free, so BEGIN checks for + * it and WIPE is a no-op, preserving that configuration's zero per-call + * cost. */ +#if defined(WOLFSSL_SMALL_STACK_CACHE) && !defined(WOLFSSL_NO_MALLOC) + #define WC_SHA256_TSCRATCH_DECL(s) \ + Sha256TScratch* sha256TScr = (Sha256TScratch*)(s)->W + #define WC_SHA256_TSCRATCH_BEGIN(s) \ + do { if (sha256TScr == NULL) return BAD_FUNC_ARG; } while (0) + #define WC_SHA256_TSCRATCH_WIPE(s) WC_DO_NOTHING /* wiped at Free */ +#else + /* Stack in big-stack builds, heap under WOLFSSL_SMALL_STACK, per the + * WC_DECLARE_VAR family (one allocation per public call -- the previous + * code allocated and freed per block under WOLFSSL_SMALL_STACK). */ + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a + * pointer to an uninitialized object trips -Wmaybe-uninitialized + * under -Werror; a deterministic start also suits the checker. */ + #define WC_SHA256_TSCRATCH_MZ_ADD() \ + do { \ + XMEMSET(sha256TScr, 0, sizeof(Sha256TScratch)); \ + wc_MemZero_Add("Sha256 transform scratch", sha256TScr, \ + sizeof(Sha256TScratch)); \ + } while (0) + #define WC_SHA256_TSCRATCH_MZ_CHECK() \ + wc_MemZero_Check(sha256TScr, sizeof(Sha256TScratch)) + #else + #define WC_SHA256_TSCRATCH_MZ_ADD() WC_DO_NOTHING + #define WC_SHA256_TSCRATCH_MZ_CHECK() WC_DO_NOTHING + #endif + #define WC_SHA256_TSCRATCH_DECL(s) \ + WC_DECLARE_VAR(sha256TScr, Sha256TScratch, 1, (s)->heap) + #define WC_SHA256_TSCRATCH_BEGIN(s) \ + do { \ + WC_ALLOC_VAR_EX(sha256TScr, Sha256TScratch, 1, (s)->heap, \ + DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); \ + WC_SHA256_TSCRATCH_MZ_ADD(); \ + } while (0) + #define WC_SHA256_TSCRATCH_WIPE(s) \ + do { \ + ForceZero(sha256TScr, sizeof(Sha256TScratch)); \ + WC_SHA256_TSCRATCH_MZ_CHECK(); \ + WC_FREE_VAR_EX(sha256TScr, (s)->heap, DYNAMIC_TYPE_TMP_BUFFER); \ + } while (0) +#endif + #if defined(WOLFSSL_X86_64_BUILD) && defined(USE_INTEL_SPEEDUP) && \ (defined(HAVE_INTEL_AVX1) || defined(HAVE_INTEL_AVX2)) && \ !defined(WOLF_CRYPTO_CB_ONLY_SHA256) @@ -446,6 +512,8 @@ int wc_Sha256Reset(wc_Sha256* sha256) { /* #if defined(HAVE_INTEL_AVX1/2) at the tail of sha256 */ static int Transform_Sha256(wc_Sha256* sha256, const byte* data); + static int Transform_Sha256_scr(wc_Sha256* sha256, const byte* data, + Sha256TScratch* WC_RESTRICT scr); #ifdef __cplusplus extern "C" { @@ -557,15 +625,30 @@ int wc_Sha256Reset(wc_Sha256* sha256) { #ifdef WC_SHA256_RAW_BE_BUFFER - static WC_INLINE int Transform_Sha256_C_from_raw(wc_Sha256* S, - const byte* D) + static WC_INLINE int Transform_Sha256_C_from_raw_scr(wc_Sha256* S, + const byte* D, + Sha256TScratch* scr) { + /* D may be unaligned caller data: stage it in the aligned context + * buffer before word-wise byte reversal, and feed the transform + * from there on both endiannesses. */ if (D != (const byte*)S->buffer) XMEMCPY(S->buffer, D, WC_SHA256_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords(S->buffer, S->buffer, WC_SHA256_BLOCK_SIZE); #endif - return Transform_Sha256(S, (const byte*)S->buffer); + return Transform_Sha256_scr(S, (const byte*)S->buffer, scr); + } + + static WC_INLINE int Transform_Sha256_C_from_raw(wc_Sha256* S, + const byte* D) + { + int ret; + WC_SHA256_TSCRATCH_DECL(S); + WC_SHA256_TSCRATCH_BEGIN(S); + ret = Transform_Sha256_C_from_raw_scr(S, D, sha256TScr); + WC_SHA256_TSCRATCH_WIPE(S); + return ret; } static WC_INLINE int Transform_Sha256_Len_C_from_raw(wc_Sha256* S, @@ -573,14 +656,17 @@ int wc_Sha256Reset(wc_Sha256* sha256) { word32 L) { int ret = 0; + WC_SHA256_TSCRATCH_DECL(S); + WC_SHA256_TSCRATCH_BEGIN(S); while (L >= WC_SHA256_BLOCK_SIZE) { - ret = Transform_Sha256_C_from_raw(S, D); + ret = Transform_Sha256_C_from_raw_scr(S, D, sha256TScr); if (ret != 0) break; D += WC_SHA256_BLOCK_SIZE; L -= WC_SHA256_BLOCK_SIZE; } + WC_SHA256_TSCRATCH_WIPE(S); return ret; } @@ -829,7 +915,7 @@ int wc_Sha256Reset(wc_Sha256* sha256) { sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(sizeof(Sha256TScratch), sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1319,6 +1405,8 @@ static int transform_check = 0; static cpuid_flags_atomic_t cpuid_flags = WC_CPUID_ATOMIC_INITIALIZER; static int Transform_Sha256(wc_Sha256* sha256, const byte* data); +static int Transform_Sha256_scr(wc_Sha256* sha256, const byte* data, + Sha256TScratch* WC_RESTRICT scr); static int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, word32 len); @@ -1377,20 +1465,29 @@ static int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, word32 len) { int ret = 0; + word32 buffer[WC_SHA256_BLOCK_SIZE / sizeof(word32)]; + WC_SHA256_TSCRATCH_DECL(sha256); + WC_SHA256_TSCRATCH_BEGIN(sha256); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Transform_Sha256_Len buffer", buffer, sizeof(buffer)); +#endif while (len >= WC_SHA256_BLOCK_SIZE) { - word32 buffer[WC_SHA256_BLOCK_SIZE / sizeof(word32)]; - XMEMCPY(buffer, data, WC_SHA256_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords(buffer, buffer, WC_SHA256_BLOCK_SIZE); #endif - ret = Transform_Sha256(sha256, (const byte*)buffer); + ret = Transform_Sha256_scr(sha256, (const byte*)buffer, sha256TScr); if (ret != 0) break; data += WC_SHA256_BLOCK_SIZE; len -= WC_SHA256_BLOCK_SIZE; } + WC_SHA256_TSCRATCH_WIPE(sha256); + ForceZero(buffer, sizeof(buffer)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(buffer, sizeof(buffer)); +#endif return ret; } @@ -1444,7 +1541,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(sizeof(Sha256TScratch), sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1730,7 +1827,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(sizeof(Sha256TScratch), sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1807,24 +1904,13 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, d(j) += t0; \ h(j) = t0 + t1 - static int Transform_Sha256(wc_Sha256* sha256, const byte* data) + static int Transform_Sha256_scr(wc_Sha256* sha256, const byte* data, + Sha256TScratch* WC_RESTRICT scr) { - word32 S[8], t0, t1; + word32 t0, t1; int i; - - #if defined(WOLFSSL_SMALL_STACK_CACHE) && !defined(WOLFSSL_NO_MALLOC) - word32* W = sha256->W; - if (W == NULL) - return BAD_FUNC_ARG; - #elif defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) - word32* W; - W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, - sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (W == NULL) - return MEMORY_E; - #else - word32 W[WC_SHA256_BLOCK_SIZE]; - #endif + word32* WC_RESTRICT W = scr->W; + word32* WC_RESTRICT S = scr->S; /* Copy context->state[] to working vars */ for (i = 0; i < 8; i++) @@ -1862,11 +1948,8 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->digest[i] += S[i]; } - #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) &&\ - !defined(WOLFSSL_NO_MALLOC) - ForceZero(W, sizeof(word32) * WC_SHA256_BLOCK_SIZE); - XFREE(W, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); - #endif + /* W and S are the caller's Sha256TScratch, wiped with one ForceZero + * when the public call returns. */ return 0; } #else @@ -1890,14 +1973,16 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, d(j) += t0; \ h(j) = t0 + t1 - static int Transform_Sha256(wc_Sha256* sha256, const byte* data) + static int Transform_Sha256_scr(wc_Sha256* sha256, const byte* data, + Sha256TScratch* WC_RESTRICT scr) { - word32 S[8], t0, t1; + word32 t0, t1; int i; #ifdef USE_SLOW_SHA256 int j; #endif - word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; + word32* WC_RESTRICT W = scr->W; + word32* WC_RESTRICT S = scr->S; /* Copy digest to working vars */ S[0] = sha256->digest[0]; @@ -1943,9 +2028,27 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->digest[6] += S[6]; sha256->digest[7] += S[7]; + /* W and S are the caller's Sha256TScratch, wiped with one ForceZero + * when the public call returns. */ return 0; } #endif /* SHA256_MANY_REGISTERS */ + + /* Compatibility entry with per-call scratch, for the dispatch arms, + * function-pointer tables, and single-block callers, all of whose + * signatures stay unchanged. Bulk callers hoist one scratch across + * their block loop and call Transform_Sha256_scr() directly, so the + * wipe runs once per public call rather than once per block. */ + static WC_MAYBE_UNUSED int Transform_Sha256(wc_Sha256* sha256, + const byte* data) + { + int ret; + WC_SHA256_TSCRATCH_DECL(sha256); + WC_SHA256_TSCRATCH_BEGIN(sha256); + ret = Transform_Sha256_scr(sha256, data, sha256TScr); + WC_SHA256_TSCRATCH_WIPE(sha256); + return ret; + } #endif /* End wc_ software implementation */ @@ -2385,6 +2488,10 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, if (sha256 == NULL || hash == NULL) { return BAD_FUNC_ARG; } + #if defined(WOLFSSL_CHECK_MEM_ZERO) && defined(LITTLE_ENDIAN_ORDER) && \ + !defined(WOLFSSL_WIDE_BYTE) + wc_MemZero_Add("wc_Sha256FinalRaw digest", digest, sizeof(digest)); + #endif #if defined(WOLFSSL_WIDE_BYTE) /* CHAR_BIT != 8: store digest words as big-endian octets. */ @@ -2395,6 +2502,10 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, WC_SHA256_DIGEST_SIZE); } XMEMCPY(hash, digest, WC_SHA256_DIGEST_SIZE); + ForceZero(digest, sizeof(digest)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(digest, sizeof(digest)); + #endif #else XMEMCPY(hash, sha256->digest, WC_SHA256_DIGEST_SIZE); #endif @@ -2473,13 +2584,23 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #endif { byte buffer[WC_SHA256_BLOCK_SIZE]; + int tret; + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_Sha256Transform buffer", buffer, + sizeof(buffer)); + #endif ByteReverseWords((word32*)buffer, (word32*)data, WC_SHA256_BLOCK_SIZE); #ifdef __aarch64__ - return Transform_Sha256_aarch64(sha256, buffer); + tret = Transform_Sha256_aarch64(sha256, buffer); #else - return Transform_Sha256(sha256, buffer); + tret = Transform_Sha256(sha256, buffer); + #endif + ForceZero(buffer, sizeof(buffer)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(buffer, sizeof(buffer)); #endif + return tret; } #else return Transform_Sha256(sha256, data); @@ -2807,7 +2928,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha224->W == NULL) { - sha224->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha224->W = (word32*)XMALLOC(sizeof(Sha256TScratch), sha224->heap, DYNAMIC_TYPE_DIGEST); if (sha224->W == NULL) return MEMORY_E; @@ -3136,7 +3257,7 @@ int wc_Sha224Reset(wc_Sha224* sha224) { #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha224->W != NULL) { - ForceZero(sha224->W, sizeof(word32) * WC_SHA224_BLOCK_SIZE); + ForceZero(sha224->W, sizeof(Sha256TScratch)); XFREE(sha224->W, sha224->heap, DYNAMIC_TYPE_DIGEST); sha224->W = NULL; } @@ -3219,7 +3340,7 @@ void wc_Sha256Free(wc_Sha256* sha256) #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha256->W != NULL) { - ForceZero(sha256->W, sizeof(word32) * WC_SHA256_BLOCK_SIZE); + ForceZero(sha256->W, sizeof(Sha256TScratch)); XFREE(sha256->W, sha256->heap, DYNAMIC_TYPE_DIGEST); sha256->W = NULL; } @@ -3406,6 +3527,10 @@ int wc_Sha224Reset(wc_Sha224* sha224) { WC_CALLOC_VAR_EX(tmpSha224, wc_Sha224, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_Sha224GetHash tmpSha224", tmpSha224, + sizeof(*tmpSha224)); + #endif ret = wc_Sha224Copy(sha224, tmpSha224); if (ret == 0) { @@ -3413,6 +3538,11 @@ int wc_Sha224Reset(wc_Sha224* sha224) { wc_Sha224Free(tmpSha224); } + ForceZero(tmpSha224, sizeof(*tmpSha224)); + #if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(tmpSha224, sizeof(*tmpSha224)); + #endif + WC_FREE_VAR_EX(tmpSha224, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -3446,10 +3576,10 @@ int wc_Sha224Reset(wc_Sha224* sha224) { XMEMCPY(dst, src, sizeof(wc_Sha224)); #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + dst->W = (word32*)XMALLOC(WC_SHA256_TSCRATCH_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { - XMEMSET(dst, 0, sizeof(wc_Sha224)); + ForceZero(dst, sizeof(wc_Sha224)); return MEMORY_E; } #endif @@ -3554,6 +3684,10 @@ int wc_Sha256GetHash(wc_Sha256* sha256, byte* hash) WC_CALLOC_VAR_EX(tmpSha256, wc_Sha256, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_Sha256GetHash tmpSha256", tmpSha256, + sizeof(*tmpSha256)); +#endif ret = wc_Sha256Copy(sha256, tmpSha256); if (ret == 0) { @@ -3562,6 +3696,12 @@ int wc_Sha256GetHash(wc_Sha256* sha256, byte* hash) } + ForceZero(tmpSha256, sizeof(*tmpSha256)); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(tmpSha256, sizeof(*tmpSha256)); +#endif + + WC_FREE_VAR_EX(tmpSha256, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -3600,10 +3740,10 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + dst->W = (word32*)XMALLOC(WC_SHA256_TSCRATCH_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { - XMEMSET(dst, 0, sizeof(wc_Sha256)); + ForceZero(dst, sizeof(wc_Sha256)); return MEMORY_E; } #endif diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index cc25c207b81..c5f315cb17d 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1640,18 +1640,33 @@ static int wc_Sha3Final(wc_Sha3* sha3, byte* hash, word32 p, word32 len) * sha3 wc_Sha3 object holding state. * returns 0 on success. */ +/* Wipe the SSP-bearing state but keep the object usable: SLH-DSA reuses it + * after Free and the block function pointers live in it under + * WC_C_DYNAMIC_FALLBACK. */ +static void wc_Sha3Wipe(wc_Sha3* sha3) +{ +#ifdef PSOC6_HASH_SHA3 + ForceZero(sha3, sizeof(*sha3)); +#else + ForceZero(sha3->s, sizeof(sha3->s)); + ForceZero(sha3->t, sizeof(sha3->t)); +#ifdef STM32_HASH_SHA3 + ForceZero(&sha3->stmCtx, sizeof(sha3->stmCtx)); +#endif + sha3->i = 0; +#endif +} + static void wc_Sha3Free(wc_Sha3* sha3) { #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) int ret = 0; #endif - (void)sha3; - -#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) if (sha3 == NULL) return; +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) #ifndef WOLF_CRYPTO_CB_FIND if (sha3->devId != INVALID_DEVID) #endif @@ -1681,6 +1696,10 @@ static void wc_Sha3Free(wc_Sha3* sha3) #if defined(PSOC6_HASH_SHA3) wc_Psoc6_Sha_Free(); #endif + + /* s and t hold absorbed keys and seeds for Ed448, ML-KEM, ML-DSA, + * SLH-DSA, LMS, XMSS and HMAC-SHA3 (ISO/IEC 19790:2012 7.9.7). */ + wc_Sha3Wipe(sha3); } /* Reset a SHA-3/SHAKE context to its freshly initialized state, reusing its @@ -1805,11 +1824,19 @@ static int wc_Sha3GetHash(wc_Sha3* sha3, byte* hash, word32 p, word32 len) return MEMORY_E); XMEMSET(tmpSha3, 0, sizeof(*tmpSha3)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_Sha3GetHash tmpSha3", tmpSha3, sizeof(*tmpSha3)); +#endif ret = wc_Sha3Copy(sha3, tmpSha3); if (ret == 0) { ret = wc_Sha3Final(tmpSha3, hash, p, len); } + ForceZero(tmpSha3, sizeof(*tmpSha3)); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(tmpSha3, sizeof(*tmpSha3)); +#endif + WC_FREE_VAR_EX(tmpSha3, sha3->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -3126,6 +3153,8 @@ static int KmacInit(wc_Kmac* kmac, word32 count, const byte* key, word32 keyLen, if (ret == 0) { ret = CshakeBytePad(&kmac->shake, count, rate); } + /* The block buffer staged the key (ISO/IEC 19790 7.9.7). */ + ForceZero(kmac->shake.t, sizeof(kmac->shake.t)); } } diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index f846016b9ac..2e048f0e72a 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -145,6 +145,68 @@ #undef WC_C_DYNAMIC_FALLBACK #endif +/* Caller-owned transient scratch for the C transform: the 16-entry message + * schedule window, so one ForceZero when the public call returns wipes it, + * instead of a per-block wipe inside the transform. The working variables T + * stay a local of the transform: through a pointer they cannot be kept in + * registers across the 80 rounds (measured ~9% on the C transform), so they + * keep their small per-block wipe. Natural alignment suffices; only the C + * transform uses this. */ +typedef struct Sha512TScratch { + word64 W[16]; +} Sha512TScratch; + +/* External W providers size by the header constant; keep them in lockstep. */ +wc_static_assert(sizeof(Sha512TScratch) == WC_SHA512_TSCRATCH_SIZE); + +/* Scratch provisioning for the callers that drive the C transform. The + * declared pointer is named sha512TScr. Under WOLFSSL_SMALL_STACK_CACHE the + * scratch is the front of the per-context cache allocated at init (the + * wc_Sha512Transform() backup block sits after it) and is wiped at Free, so + * WIPE is a no-op there, preserving that configuration's zero per-call + * cost. */ +#if defined(WOLFSSL_SMALL_STACK_CACHE) + #define WC_SHA512_TSCRATCH_DECL(s) \ + Sha512TScratch* sha512TScr = (Sha512TScratch*)(s)->W + #define WC_SHA512_TSCRATCH_BEGIN(s) \ + do { if (sha512TScr == NULL) return BAD_FUNC_ARG; } while (0) + #define WC_SHA512_TSCRATCH_WIPE(s) WC_DO_NOTHING /* wiped at Free */ +#else + /* Stack in big-stack builds, heap under WOLFSSL_SMALL_STACK, per the + * WC_DECLARE_VAR family (one allocation per public call -- the previous + * code allocated and freed per block under WOLFSSL_SMALL_STACK). */ + #ifdef WOLFSSL_CHECK_MEM_ZERO + /* Zero first: the registration call takes const void*, and a + * pointer to an uninitialized object trips -Wmaybe-uninitialized + * under -Werror; a deterministic start also suits the checker. */ + #define WC_SHA512_TSCRATCH_MZ_ADD() \ + do { \ + XMEMSET(sha512TScr, 0, sizeof(Sha512TScratch)); \ + wc_MemZero_Add("Sha512 transform scratch", sha512TScr, \ + sizeof(Sha512TScratch)); \ + } while (0) + #define WC_SHA512_TSCRATCH_MZ_CHECK() \ + wc_MemZero_Check(sha512TScr, sizeof(Sha512TScratch)) + #else + #define WC_SHA512_TSCRATCH_MZ_ADD() WC_DO_NOTHING + #define WC_SHA512_TSCRATCH_MZ_CHECK() WC_DO_NOTHING + #endif + #define WC_SHA512_TSCRATCH_DECL(s) \ + WC_DECLARE_VAR(sha512TScr, Sha512TScratch, 1, (s)->heap) + #define WC_SHA512_TSCRATCH_BEGIN(s) \ + do { \ + WC_ALLOC_VAR_EX(sha512TScr, Sha512TScratch, 1, (s)->heap, \ + DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); \ + WC_SHA512_TSCRATCH_MZ_ADD(); \ + } while (0) + #define WC_SHA512_TSCRATCH_WIPE(s) \ + do { \ + ForceZero(sha512TScr, sizeof(Sha512TScratch)); \ + WC_SHA512_TSCRATCH_MZ_CHECK(); \ + WC_FREE_VAR_EX(sha512TScr, (s)->heap, DYNAMIC_TYPE_TMP_BUFFER); \ + } while (0) +#endif + #if defined(WOLFSSL_X86_64_BUILD) && defined(USE_INTEL_SPEEDUP) #if defined(__GNUC__) && ((__GNUC__ < 4) || \ (__GNUC__ == 4 && __GNUC_MINOR__ <= 8)) @@ -1248,6 +1310,8 @@ int wc_Sha512_256Reset(wc_Sha512* sha512) { #endif static int _Transform_Sha512(wc_Sha512 *sha512); + static int _Transform_Sha512_scr(wc_Sha512 *sha512, + Sha512TScratch* WC_RESTRICT scr); #ifdef WC_NO_INTERNAL_FUNCTION_POINTERS @@ -1280,13 +1344,29 @@ int wc_Sha512_256Reset(wc_Sha512* sha512) { #ifdef WC_SHA512_RAW_BE_BUFFER - static WC_INLINE int Transform_Sha512_C_from_raw(wc_Sha512 *sha512) + static WC_INLINE int Transform_Sha512_C_from_raw_scr(wc_Sha512 *sha512, + const byte* data, Sha512TScratch* WC_RESTRICT scr) { + /* data may be unaligned caller input: stage it in the aligned + * context buffer before word-wise byte reversal. */ + if (data != (const byte*)sha512->buffer) + XMEMCPY(sha512->buffer, data, WC_SHA512_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords64(sha512->buffer, sha512->buffer, WC_SHA512_BLOCK_SIZE); #endif - return _Transform_Sha512(sha512); + return _Transform_Sha512_scr(sha512, scr); + } + + static WC_INLINE int Transform_Sha512_C_from_raw(wc_Sha512 *sha512) + { + int ret; + WC_SHA512_TSCRATCH_DECL(sha512); + WC_SHA512_TSCRATCH_BEGIN(sha512); + ret = Transform_Sha512_C_from_raw_scr(sha512, + (const byte*)sha512->buffer, sha512TScr); + WC_SHA512_TSCRATCH_WIPE(sha512); + return ret; } static WC_INLINE int Transform_Sha512_Len_C_from_raw(wc_Sha512 *sha512, @@ -1294,15 +1374,17 @@ int wc_Sha512_256Reset(wc_Sha512* sha512) { { const byte* data = sha512->data; int ret = 0; + WC_SHA512_TSCRATCH_DECL(sha512); + WC_SHA512_TSCRATCH_BEGIN(sha512); while (len >= WC_SHA512_BLOCK_SIZE) { - XMEMCPY(sha512->buffer, data, WC_SHA512_BLOCK_SIZE); - ret = Transform_Sha512_C_from_raw(sha512); + ret = Transform_Sha512_C_from_raw_scr(sha512, data, sha512TScr); if (ret != 0) break; data += WC_SHA512_BLOCK_SIZE; len -= WC_SHA512_BLOCK_SIZE; } + WC_SHA512_TSCRATCH_WIPE(sha512); return ret; } @@ -1556,6 +1638,8 @@ static int transform_check = 0; static cpuid_flags_atomic_t cpuid_flags = WC_CPUID_ATOMIC_INITIALIZER; static int _Transform_Sha512(wc_Sha512* sha512); +static int _Transform_Sha512_scr(wc_Sha512* sha512, + Sha512TScratch* WC_RESTRICT scr); static int Transform_Sha512_C(wc_Sha512* sha512, const byte* data); static int Transform_Sha512_Len_C(wc_Sha512* sha512, const byte* data, word32 len); @@ -1885,7 +1969,7 @@ static int InitSha512_Family(wc_Sha512* sha512, void* heap, int devId, * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha512->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + sha512->W = (word64 *)XMALLOC(sizeof(Sha512TScratch) + WC_SHA512_BLOCK_SIZE, sha512->heap, DYNAMIC_TYPE_DIGEST); if (sha512->W == NULL) return MEMORY_E; @@ -2049,24 +2133,13 @@ static const word64 K512[80] = { d(i) += h(i); \ h(i) += S0(a(i)) + Maj(a(i),b(i),c(i)) -static int _Transform_Sha512(wc_Sha512* sha512) +static int _Transform_Sha512_scr(wc_Sha512* sha512, + Sha512TScratch* WC_RESTRICT scr) { const word64* K = K512; word32 j; word64 T[8]; - -#if defined(WOLFSSL_SMALL_STACK_CACHE) - word64* W = sha512->W; - if (W == NULL) - return BAD_FUNC_ARG; -#elif defined(WOLFSSL_SMALL_STACK) - word64* W; - W = (word64*) XMALLOC(sizeof(word64) * 16, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (W == NULL) - return MEMORY_E; -#else - word64 W[16]; -#endif + word64* WC_RESTRICT W = scr->W; /* Copy digest to working vars */ XMEMCPY(T, sha512->digest, sizeof(T)); @@ -2100,16 +2173,27 @@ static int _Transform_Sha512(wc_Sha512* sha512) sha512->digest[6] += g(0); sha512->digest[7] += h(0); - /* Wipe variables */ - ForceZero(W, sizeof(word64) * 16); + /* W is the caller's Sha512TScratch, wiped with one ForceZero when the + * public call returns. T is wiped here: keeping it register-resident + * requires it to be a local, and its wipe is one cache line. */ ForceZero(T, sizeof(T)); - -#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) - XFREE(W, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return 0; } + +/* Compatibility entry with per-call scratch, for the dispatch arms, + * function-pointer tables, and single-block callers, all of whose signatures + * stay unchanged. Bulk callers hoist one scratch across their block loop and + * call _Transform_Sha512_scr() directly, so the wipe runs once per public + * call rather than once per block. */ +static WC_MAYBE_UNUSED int _Transform_Sha512(wc_Sha512* sha512) +{ + int ret; + WC_SHA512_TSCRATCH_DECL(sha512); + WC_SHA512_TSCRATCH_BEGIN(sha512); + ret = _Transform_Sha512_scr(sha512, sha512TScr); + WC_SHA512_TSCRATCH_WIPE(sha512); + return ret; +} #endif @@ -2238,6 +2322,9 @@ static WC_INLINE int Sha512Update(wc_Sha512* sha512, const byte* data, word32 le defined(USE_INTEL_SPEEDUP) && (defined(HAVE_INTEL_AVX1) || \ defined(HAVE_INTEL_AVX2))) { + WC_SHA512_TSCRATCH_DECL(sha512); + + WC_SHA512_TSCRATCH_BEGIN(sha512); while (len >= WC_SHA512_BLOCK_SIZE) { XMEMCPY(local, data, WC_SHA512_BLOCK_SIZE); @@ -2253,14 +2340,29 @@ static WC_INLINE int Sha512Update(wc_Sha512* sha512, const byte* data, word32 le WC_SHA512_BLOCK_SIZE); } #endif - /* Byte reversal performed in function if required. */ - ret = Transform_Sha512(sha512); + /* Byte reversal performed in function if required: under + * WC_SHA512_RAW_BE_BUFFER the in-loop reversal above is compiled + * out and the from_raw adapter owns it. */ + #ifdef WC_SHA512_RAW_BE_BUFFER + ret = Transform_Sha512_C_from_raw_scr(sha512, + (const byte*)sha512->buffer, sha512TScr); + #else + ret = _Transform_Sha512_scr(sha512, sha512TScr); + #endif if (ret != 0) break; } + WC_SHA512_TSCRATCH_WIPE(sha512); } #else { + #if !defined(WOLFSSL_ESP32_CRYPT) || \ + defined(NO_WOLFSSL_ESP32_CRYPT_HASH) || \ + defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) + WC_SHA512_TSCRATCH_DECL(sha512); + + WC_SHA512_TSCRATCH_BEGIN(sha512); + #endif while (len >= WC_SHA512_BLOCK_SIZE) { XMEMCPY(local, data, WC_SHA512_BLOCK_SIZE); @@ -2280,7 +2382,7 @@ static WC_INLINE int Sha512Update(wc_Sha512* sha512, const byte* data, word32 le #if !defined(WOLFSSL_ESP32_CRYPT) || \ defined(NO_WOLFSSL_ESP32_CRYPT_HASH) || \ defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) - ret = Transform_Sha512(sha512); + ret = _Transform_Sha512_scr(sha512, sha512TScr); #else if(sha512->ctx.mode == ESP32_SHA_INIT) { esp_sha_try_hw_lock(&sha512->ctx); @@ -2297,6 +2399,11 @@ static WC_INLINE int Sha512Update(wc_Sha512* sha512, const byte* data, word32 le if (ret != 0) break; } /* while (len >= WC_SHA512_BLOCK_SIZE) */ + #if !defined(WOLFSSL_ESP32_CRYPT) || \ + defined(NO_WOLFSSL_ESP32_CRYPT_HASH) || \ + defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) + WC_SHA512_TSCRATCH_WIPE(sha512); + #endif } #endif #endif @@ -2729,7 +2836,7 @@ void wc_Sha512Free(wc_Sha512* sha512) #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha512->W != NULL) { - ForceZero(sha512->W, (sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE); + ForceZero(sha512->W, sizeof(Sha512TScratch) + WC_SHA512_BLOCK_SIZE); XFREE(sha512->W, sha512->heap, DYNAMIC_TYPE_DIGEST); sha512->W = NULL; } @@ -2786,13 +2893,19 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) /* Skip over the initial `W' buffer at the start (used by * _Transform_Sha512()). */ - buffer = sha->W + 16; + buffer = (word64*)((byte*)sha->W + sizeof(Sha512TScratch)); #elif defined(WOLFSSL_SMALL_STACK) buffer = (word64*)XMALLOC(WC_SHA512_BLOCK_SIZE, sha->heap, DYNAMIC_TYPE_TMP_BUFFER); if (buffer == NULL) return MEMORY_E; #endif +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK_CACHE) + /* Zero first: the registration call takes const void*, and a pointer to + * an uninitialized object trips -Wmaybe-uninitialized under -Werror. */ + XMEMSET(buffer, 0, WC_SHA512_BLOCK_SIZE); + wc_MemZero_Add("wc_Sha512Transform buffer", buffer, WC_SHA512_BLOCK_SIZE); +#endif #if defined(LITTLE_ENDIAN_ORDER) && !defined(WC_SHA512_RAW_BE_BUFFER) #if defined(WOLFSSL_X86_64_BUILD) && defined(USE_INTEL_SPEEDUP) && \ @@ -2821,8 +2934,11 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) XMEMCPY(sha->buffer, buffer, WC_SHA512_BLOCK_SIZE); #endif -#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) ForceZero(buffer, WC_SHA512_BLOCK_SIZE); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(buffer, WC_SHA512_BLOCK_SIZE); +#endif +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) XFREE(buffer, sha->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; @@ -2961,7 +3077,7 @@ static int InitSha384(wc_Sha384* sha384) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha384->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + sha384->W = (word64 *)XMALLOC(sizeof(Sha512TScratch) + WC_SHA512_BLOCK_SIZE, sha384->heap, DYNAMIC_TYPE_DIGEST); if (sha384->W == NULL) return MEMORY_E; @@ -3225,7 +3341,7 @@ void wc_Sha384Free(wc_Sha384* sha384) #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha384->W != NULL) { - ForceZero(sha384->W, (sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE); + ForceZero(sha384->W, sizeof(Sha512TScratch) + WC_SHA512_BLOCK_SIZE); XFREE(sha384->W, sha384->heap, DYNAMIC_TYPE_DIGEST); sha384->W = NULL; } @@ -3292,6 +3408,10 @@ static int Sha512_Family_GetHash(wc_Sha512* sha512, byte* hash, WC_CALLOC_VAR_EX(tmpSha512, wc_Sha512, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Sha512_Family_GetHash tmpSha512", tmpSha512, + sizeof(*tmpSha512)); +#endif /* copy this sha512 into tmpSha */ ret = wc_Sha512Copy(sha512, tmpSha512); @@ -3300,6 +3420,11 @@ static int Sha512_Family_GetHash(wc_Sha512* sha512, byte* hash, wc_Sha512Free(tmpSha512); } + ForceZero(tmpSha512, sizeof(*tmpSha512)); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(tmpSha512, sizeof(*tmpSha512)); +#endif + WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -3342,10 +3467,10 @@ int wc_Sha512Copy(wc_Sha512* src, wc_Sha512* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + dst->W = (word64 *)XMALLOC(sizeof(Sha512TScratch) + WC_SHA512_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { - XMEMSET(dst, 0, sizeof(wc_Sha512)); + ForceZero(dst, sizeof(wc_Sha512)); return MEMORY_E; } #endif @@ -3742,6 +3867,10 @@ int wc_Sha384GetHash(wc_Sha384* sha384, byte* hash) WC_CALLOC_VAR_EX(tmpSha384, wc_Sha384, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_Sha384GetHash tmpSha384", tmpSha384, + sizeof(*tmpSha384)); +#endif /* copy this sha384 into tmpSha */ ret = wc_Sha384Copy(sha384, tmpSha384); @@ -3750,6 +3879,11 @@ int wc_Sha384GetHash(wc_Sha384* sha384, byte* hash) wc_Sha384Free(tmpSha384); } + ForceZero(tmpSha384, sizeof(*tmpSha384)); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(tmpSha384, sizeof(*tmpSha384)); +#endif + WC_FREE_VAR_EX(tmpSha384, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -3788,10 +3922,10 @@ int wc_Sha384Copy(wc_Sha384* src, wc_Sha384* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA384_BLOCK_SIZE, + dst->W = (word64 *)XMALLOC(sizeof(Sha512TScratch) + WC_SHA384_BLOCK_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { - XMEMSET(dst, 0, sizeof(wc_Sha384)); + ForceZero(dst, sizeof(wc_Sha384)); return MEMORY_E; } #endif diff --git a/wolfcrypt/src/wc_frodokem_mat.c b/wolfcrypt/src/wc_frodokem_mat.c index fe560b944e5..685ebee1185 100644 --- a/wolfcrypt/src/wc_frodokem_mat.c +++ b/wolfcrypt/src/wc_frodokem_mat.c @@ -184,19 +184,6 @@ #define FRODOKEM_D15_SHAKE128 #endif -/* restrict qualifier for the matrix multiply inner loops. The A-row scratch - * and the output-row slices never overlap, so flagging them lets the compiler - * vectorize each contiguous accumulation stream (without it the aliasing - * analysis gives up and the loops stay scalar). Falls back to nothing on - * compilers without a restrict keyword - the code stays correct either way. */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - #define FRODOKEM_RESTRICT restrict -#elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) - #define FRODOKEM_RESTRICT __restrict -#else - #define FRODOKEM_RESTRICT -#endif - #if (defined(USE_INTEL_SPEEDUP) || defined(FRODOKEM_HAVE_SVE) || \ defined(FRODOKEM_HAVE_NEON_RUNTIME)) && \ !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) @@ -1565,8 +1552,13 @@ static int frodokem_gen_a_row_shake_x2(word16* rows, const byte* seedA, * @param [in] j Index of the generated A row. * @param [in] n Matrix dimension n. */ +/* restrict qualifier for the matrix multiply inner loops. The A-row scratch + * and the output-row slices never overlap, so flagging them lets the compiler + * vectorize each contiguous accumulation stream (without it the aliasing + * analysis gives up and the loops stay scalar). Falls back to nothing on + * compilers without a restrict keyword - the code stays correct either way. */ static void frodokem_sa_accum(word16* out, const word16* s, - const word16* FRODOKEM_RESTRICT row, int j, int n) + const word16* WC_RESTRICT row, int j, int n) { int k; #ifdef WOLFSSL_FRODOKEM_SMALL @@ -1574,7 +1566,7 @@ static void frodokem_sa_accum(word16* out, const word16* s, for (i = 0; i < FRODOKEM_NBAR; i++) { word16 sij = s[i * n + j]; - word16* FRODOKEM_RESTRICT o = out + i * n; + word16* WC_RESTRICT o = out + i * n; for (k = 0; k < n; k++) { o[k] = (word16)(o[k] + sij * row[k]); @@ -1590,7 +1582,7 @@ static void frodokem_sa_accum(word16* out, const word16* s, * with each S coefficient read just before its loop, keeps only one stream * live at a time (low register pressure). The output slice, wherever o * points, never overlaps row. */ - word16* FRODOKEM_RESTRICT o = out; + word16* WC_RESTRICT o = out; word16 sij; sij = s[0 * n + j]; diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index 841ebc35f51..6fee0b72f46 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -502,11 +502,20 @@ static WC_INLINE int wc_lms_sha256_192_hash_block(wc_Sha256* sha256, int ret; unsigned char output[WC_SHA256_DIGEST_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_lms_sha256_192_hash_block output", output, + sizeof(output)); +#endif /* Hash the block and reset SHA-256 state. */ ret = wc_Sha256HashBlock(sha256, data, output); if (ret == 0) { XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE); } + /* Prefix is x_q[i] or a child SEED (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(output, sizeof(output)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(output, sizeof(output)); +#endif return ret; } @@ -526,6 +535,10 @@ static WC_INLINE int wc_lms_hash_sha256_192(wc_Sha256* sha256, byte* data, int ret; unsigned char output[WC_SHA256_DIGEST_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_lms_hash_sha256_192 output", output, + sizeof(output)); +#endif #ifndef WC_LMS_FULL_HASH if (len < WC_SHA256_BLOCK_SIZE) { /* Store data into SHA-256 object's buffer. */ @@ -570,6 +583,11 @@ static WC_INLINE int wc_lms_hash_sha256_192(wc_Sha256* sha256, byte* data, } } #endif /* !WC_LMS_FULL_HASH */ + /* Prefix is x_q[i] or a child SEED (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(output, sizeof(output)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(output, sizeof(output)); +#endif return ret; } @@ -587,6 +605,10 @@ static WC_INLINE int wc_lms_hash_sha256_192_final(wc_Sha256* sha256, byte* hash) byte* buffer = (byte*)sha256->buffer; unsigned char output[WC_SHA256_DIGEST_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_lms_hash_sha256_192_final output", output, + sizeof(output)); +#endif buffer[sha256->buffLen++] = 0x80; if (sha256->buffLen > WC_SHA256_PAD_SIZE) { XMEMSET(buffer + sha256->buffLen, 0, @@ -614,16 +636,30 @@ static WC_INLINE int wc_lms_hash_sha256_192_final(wc_Sha256* sha256, byte* hash) sha256->hiLen = 0; sha256->loLen = 0; } + /* Prefix is x_q[i] or a child SEED (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(output, sizeof(output)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(output, sizeof(output)); +#endif return ret; #else int ret; unsigned char output[WC_SHA256_DIGEST_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_lms_hash_sha256_192_final output", output, + sizeof(output)); +#endif ret = wc_Sha256Final(sha256, output); if (ret == 0) { XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE); } + /* Prefix is x_q[i] or a child SEED (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(output, sizeof(output)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(output, sizeof(output)); +#endif return ret; #endif diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index bccae07132b..b96a74acf64 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -557,6 +557,45 @@ static int mldsa_alloc_pub_buf(wc_MlDsaKey* key) } #endif +/* Private key import also runs in callback-only builds. */ +#if defined(WOLFSSL_MLDSA_PRIVATE_KEY) || \ + (!defined(WOLFSSL_MLDSA_VERIFY_ONLY) && \ + !defined(WOLF_CRYPTO_CB_ONLY_MLDSA)) +#ifdef WC_MLDSA_CACHE_PRIV_VECTORS +/* Wipe the cached private vectors s1, s2 and t0. */ +static void mldsa_wipe_priv_vecs(wc_MlDsaKey* key) +{ +#ifndef WC_MLDSA_FIXED_ARRAY + if ((key->s1 != NULL) && (key->params != NULL)) { + ForceZero(key->s1, (word32)key->params->s1Sz + + 2U * (word32)key->params->s2Sz); + } +#else + ForceZero(key->s1, sizeof(key->s1)); + ForceZero(key->s2, sizeof(key->s2)); + ForceZero(key->t0, sizeof(key->t0)); +#endif + key->privVecsSet = 0; +} +#endif + +/* Wipe the private key and anything cached from it. */ +static void mldsa_wipe_priv(wc_MlDsaKey* key) +{ +#if defined(WOLFSSL_MLDSA_DYNAMIC_KEYS) + if (key->k != NULL) { + ForceZero(key->k, key->kSz); + } +#elif !defined(WOLFSSL_MLDSA_ASSIGN_KEY) + ForceZero(key->k, sizeof(key->k)); +#endif +#ifdef WC_MLDSA_CACHE_PRIV_VECTORS + mldsa_wipe_priv_vecs(key); +#endif + key->prvKeySet = 0; +} +#endif + #ifndef WOLF_CRYPTO_CB_ONLY_MLDSA /****************************************************************************** * Hash operations @@ -6420,6 +6459,8 @@ static int mldsa_make_hint(const sword32* s, const sword32* w1, byte k, for (i = 0; i < PARAMS_ML_DSA_44_K; i++) { ret = mldsa_make_hint_88(s, w1, h, &idx, valid); if ((ret != 0) || (!*valid)) { + /* Hint count of a rejected candidate. */ + ForceZero(&idx, sizeof(idx)); return ret; } /* Alg 14, Step 10: Store count of hints for polynomial at end of @@ -6438,6 +6479,8 @@ static int mldsa_make_hint(const sword32* s, const sword32* w1, byte k, for (i = 0; i < k; i++) { ret = mldsa_make_hint_32(s, w1, omega, h, &idx, valid); if ((ret != 0) || (!*valid)) { + /* Hint count of a rejected candidate. */ + ForceZero(&idx, sizeof(idx)); return ret; } /* Alg 14, Step 10: Store count of hints for polynomial at end of @@ -9367,6 +9410,20 @@ static int mldsa_calc_t(sword32* t, const sword32* a, sword32* s1, #endif /* !WOLFSSL_MLDSA_VERIFY_ONLY */ +#ifndef WOLFSSL_MLDSA_VERIFY_ONLY +/* Wipe a SHAKE object that a failed step left mid-operation. */ +static void mldsa_shake_wipe(wc_Shake* shake) +{ +#ifndef PSOC6_HASH_SHA3 + ForceZero(shake->s, sizeof(shake->s)); + ForceZero(shake->t, sizeof(shake->t)); + shake->i = 0; +#else + (void)wc_Shake256_Reset(shake); +#endif +} +#endif /* !WOLFSSL_MLDSA_VERIFY_ONLY */ + /******************************************************************************/ #ifndef WOLFSSL_MLDSA_NO_MAKE_KEY @@ -9598,6 +9655,10 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) if (ret == 0) { pub_seed = key->k; + if (key->prvKeySet) { + /* Do not write the new key over the old one. */ + mldsa_wipe_priv(key); + } } /* Allocate memory for large intermediates. */ @@ -9701,9 +9762,16 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) byte* s2p = s1p + params->s1EncSz; byte* t0 = s2p + params->s2EncSz; byte* t1 = key->p + MLDSA_PUB_SEED_SZ; + byte kBuf[MLDSA_K_SZ]; - /* Step 9: Move k down to after public seed. */ - XMEMCPY(k, k + MLDSA_PRIV_SEED_SZ, MLDSA_K_SZ); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("mldsa make_key kBuf", kBuf, sizeof(kBuf)); + #endif + /* Step 9: Move k down to after public seed; rho' is done with. */ + XMEMCPY(kBuf, k + MLDSA_PRIV_SEED_SZ, MLDSA_K_SZ); + ForceZero(k, MLDSA_PRIV_SEED_SZ + MLDSA_K_SZ); + XMEMCPY(k, kBuf, MLDSA_K_SZ); + ForceZero(kBuf, sizeof(kBuf)); /* Step 9. Alg 24 Steps 2-4: Encode s1 into private key. */ if (ret == 0) { ret = mldsa_vec_encode_eta_bits(s1, params->l, params->eta, s1p); @@ -9733,6 +9801,9 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) ret = mldsa_shake256(&key->shake, key->p, params->pkSz, tr, MLDSA_TR_SZ); } + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(kBuf, sizeof(kBuf)); + #endif } if (ret == 0) { /* Public key and private key are available. */ @@ -9761,6 +9832,10 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) } XFREE(s1, key->heap, DYNAMIC_TYPE_MLDSA); #endif + if (ret != 0) { + mldsa_wipe_priv(key); + mldsa_shake_wipe(&key->shake); + } return ret; #else int ret = 0; @@ -9785,6 +9860,10 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) if (ret == 0) { pub_seed = key->k; + if (key->prvKeySet) { + /* Do not write the new key over the old one. */ + mldsa_wipe_priv(key); + } } /* Allocate memory for large intermediates. */ @@ -9855,6 +9934,7 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) byte* s2p = s1p + params->s1EncSz; byte* t0 = s2p + params->s2EncSz; byte* t1 = key->p + MLDSA_PUB_SEED_SZ; + byte kBuf[MLDSA_K_SZ]; byte aseed[MLDSA_GEN_A_SEED_SZ]; /* One decoded s2 polynomial, held after t in the dead s2 vector. */ sword32* s2t = t + MLDSA_N; @@ -9862,8 +9942,14 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) const byte* s2pt = s2p; word32 s2Stride = (word32)params->s2EncSz / params->k; - /* Step 9: Move k down to after public seed. */ - XMEMCPY(k, k + MLDSA_PRIV_SEED_SZ, MLDSA_K_SZ); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("mldsa make_key kBuf", kBuf, sizeof(kBuf)); + #endif + /* Step 9: Move k down to after public seed; rho' is done with. */ + XMEMCPY(kBuf, k + MLDSA_PRIV_SEED_SZ, MLDSA_K_SZ); + ForceZero(k, MLDSA_PRIV_SEED_SZ + MLDSA_K_SZ); + XMEMCPY(k, kBuf, MLDSA_K_SZ); + ForceZero(kBuf, sizeof(kBuf)); /* Step 9. Alg 24 Steps 2-4: Encode s1 into private key. */ if (ret == 0) { ret = mldsa_vec_encode_eta_bits(s1, params->l, params->eta, s1p); @@ -9900,6 +9986,9 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) ret = mldsa_shake256(&key->shake, key->p, params->pkSz, tr, MLDSA_TR_SZ); } + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(kBuf, sizeof(kBuf)); + #endif } if (ret == 0) { /* Public key and private key are available. */ @@ -9927,6 +10016,10 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) ForceZero(s1, allocSz); } XFREE(s1, key->heap, DYNAMIC_TYPE_MLDSA); + if (ret != 0) { + mldsa_wipe_priv(key); + mldsa_shake_wipe(&key->shake); + } return ret; #endif } @@ -10182,6 +10275,7 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, byte priv_rand_seed[MLDSA_Y_SEED_SZ]; byte* h = sig + params->lambda / 4 + params->zEncSz; unsigned int allocSz = 0; + unsigned int rejSz = 0; #ifdef WC_MLDSA_FAULT_HARDEN sword32* y_check; #endif @@ -10250,6 +10344,7 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, /* y-l, w0-k, w1-k, c-1, z-l, ct0-k */ allocSz = (unsigned int)params->s1Sz + params->s2Sz + params->s2Sz + (unsigned int)MLDSA_POLY_SIZE + params->s1Sz + params->s2Sz; + rejSz = allocSz; #ifndef WC_MLDSA_CACHE_PRIV_VECTORS /* s1-l, s2-k, t0-k */ allocSz += (unsigned int)params->s1Sz + params->s2Sz + params->s2Sz; @@ -10385,6 +10480,9 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, DYNAMIC_TYPE_MLDSA, ret=MEMORY_E); if (WC_VAR_OK(w1e)) { + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("mldsa sign w1e", w1e, MLDSA_MAX_W1_ENC_SZ); + #endif if (ret == 0) { ret = mldsa_vec_encode_w1(w1, params->k, params->gamma2, w1e); @@ -10478,10 +10576,19 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, } } + if (WC_VAR_OK(w1e)) { + ForceZero(w1e, MLDSA_MAX_W1_ENC_SZ); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(w1e, MLDSA_MAX_W1_ENC_SZ); + #endif + } WC_FREE_VAR_EX(w1e, key->heap, DYNAMIC_TYPE_MLDSA); } if (!valid) { + /* Wipe the rejected candidate rather than overwrite it. */ + ForceZero(y, rejSz); + ForceZero(sig, params->sigSz); /* Too many attempts - something wrong with implementation. */ if ((kappa > (word16)(kappa + params->l))) { ret = BAD_COND_E; @@ -10494,6 +10601,10 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, } /* Step 11: Check we have a valid signature. */ while ((ret == 0) && (!valid)); + if (ret != 0) { + /* sig holds a rejected candidate (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(sig, params->sigSz); + } } if (ret == 0) { byte* ze = sig + params->lambda / 4; @@ -10521,6 +10632,9 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, ForceZero(y, zeroSz); } XFREE(y, key->heap, DYNAMIC_TYPE_MLDSA); + if (ret != 0) { + mldsa_shake_wipe(&key->shake); + } return ret; #elif !defined(WOLFSSL_MLDSA_SIGN_SMALLEST_MEM) int ret = 0; @@ -11156,6 +11270,11 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, } if (!valid) { + /* Wipe the rejected candidate; A and cached keys stay. */ + ForceZero(y, (size_t)((byte*)a - (byte*)y)); + ForceZero(w1e, (size_t)params->w1EncSz + + MLDSA_REJ_NTT_POLY_H_SIZE); + ForceZero(sig, params->sigSz); /* Too many attempts - something wrong with implementation. */ if ((kappa > (word16)(kappa + params->l))) { ret = BAD_COND_E; @@ -11168,6 +11287,10 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, } /* Step 11: Check we have a valid signature. */ while ((ret == 0) && (!valid)); + if (ret != 0) { + /* sig holds a rejected candidate (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(sig, params->sigSz); + } } if ((ret != 0) && (*sigLen == params->sigSz)) { @@ -11182,6 +11305,9 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, ForceZero(y, allocSz); } XFREE(y, key->heap, DYNAMIC_TYPE_MLDSA); + if (ret != 0) { + mldsa_shake_wipe(&key->shake); + } return ret; #else int ret = 0; @@ -14396,6 +14522,10 @@ int wc_MlDsaKey_SetParams(wc_MlDsaKey* key, byte level) } #endif +#if !defined(WOLFSSL_MLDSA_DYNAMIC_KEYS) && \ + !defined(WOLFSSL_MLDSA_ASSIGN_KEY) && !defined(WOLFSSL_MLDSA_VERIFY_ONLY) + ForceZero(key->k, sizeof(key->k)); +#endif /* Store level and indicate public and private key are not set. */ key->level = level % WC_ML_DSA_DRAFT; key->pubKeySet = 0; @@ -14835,6 +14965,10 @@ int wc_MlDsaKey_CheckKey(wc_MlDsaKey* key) } else { XMEMSET(s1, 0, allocSz); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("mldsa check_key s1 s2 t0 t", s1, + (size_t)params->s1Sz + 3U * (size_t)params->s2Sz); + #endif s2 = s1 + params->s1Sz / sizeof(*s1); t0 = s2 + params->s2Sz / sizeof(*s2); t = t0 + params->s2Sz / sizeof(*t0); @@ -14949,9 +15083,9 @@ int wc_MlDsaKey_CheckKey(wc_MlDsaKey* key) } if (key != NULL) { - /* Zeroize secret s1/s2/t0 at the front (trailing t/t1/A are public). */ + /* s1, s2, t0 and t, which held t0; t1 and A are public. */ if ((s1 != NULL) && (params != NULL)) { - ForceZero(s1, (word32)params->s1Sz + 2U * (word32)params->s2Sz); + ForceZero(s1, (word32)params->s1Sz + 3U * (word32)params->s2Sz); } /* Dispose of allocated memory. */ XFREE(s1, key->heap, DYNAMIC_TYPE_MLDSA); @@ -15270,6 +15404,7 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, { int ret = 0; int expPrivSz; + int copied = 0; #if (defined(WC_MLDSA_CACHE_MATRIX_A) || \ defined(WC_MLDSA_CACHE_PRIV_VECTORS)) && \ !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) @@ -15316,8 +15451,19 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, #ifdef WOLFSSL_MLDSA_ASSIGN_KEY key->k = priv; #else - XMEMCPY(key->k, priv, privSz); + /* An exact-alias re-import (priv == key->k) already has the new key + * in place: skip the wipe, which would destroy the source before the + * copy, and the copy itself, which is undefined for an exact alias. + * The sibling import sites preserve aliased input in a stack bounce + * buffer instead; at ML-DSA private-key sizes that is not an option, + * and in the non-aliased case the same-size overwrite leaves no + * residue of the old key. */ + if (priv != (const byte*)key->k) { + ForceZero(key->k, privSz); + XMEMCPY(key->k, priv, privSz); + } #endif + copied = 1; } /* Allocate and create cached values. The caches only feed the @@ -15368,6 +15514,7 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, } #endif if (ret == 0) { + mldsa_wipe_priv_vecs(key); /* Compute vectors from private key. */ ret = mldsa_make_priv_vecs(key, key->s1, key->s2, key->t0); } @@ -15376,6 +15523,9 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, /* Private key is set. */ key->prvKeySet = 1; } + else if (copied) { + mldsa_wipe_priv(key); + } return ret; } diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index afc097747c0..1192caffe63 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -682,6 +682,51 @@ int wc_MlKemKey_Free(MlKemKey* key) return 0; } +#if !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) && \ + (!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)) +/* Destroy operation intermediates without losing the reusable hash object + * configuration (FIPS 203 section 3.3). */ +static void mlkemkey_hash_wipe(wc_Sha3* h) +{ +#ifndef PSOC6_HASH_SHA3 + ForceZero(h->s, sizeof(h->s)); + ForceZero(h->t, sizeof(h->t)); + h->i = 0; +#else + (void)wc_Sha3_256_Reset(h); +#endif +} + +#ifdef WOLFSSL_CHECK_MEM_ZERO +/* Register the hash and PRF state that mlkemkey_hash_wipe() must clear. */ +static void mlkemkey_hash_memzero_add(MlKemKey* key) +{ +#ifndef PSOC6_HASH_SHA3 + wc_MemZero_Add("mlkem key hash s", key->hash.s, sizeof(key->hash.s)); + wc_MemZero_Add("mlkem key hash t", key->hash.t, sizeof(key->hash.t)); + wc_MemZero_Add("mlkem key prf s", key->prf.s, sizeof(key->prf.s)); + wc_MemZero_Add("mlkem key prf t", key->prf.t, sizeof(key->prf.t)); +#else + (void)key; +#endif +} + +static void mlkemkey_hash_memzero_check(MlKemKey* key) +{ +#ifndef PSOC6_HASH_SHA3 + wc_MemZero_Check(key->hash.s, sizeof(key->hash.s)); + wc_MemZero_Check(key->hash.t, sizeof(key->hash.t)); + wc_MemZero_Check(key->prf.s, sizeof(key->prf.s)); + wc_MemZero_Check(key->prf.t, sizeof(key->prf.t)); +#else + (void)key; +#endif +} +#endif +#endif + /******************************************************************************/ #ifndef WOLFSSL_MLKEM_NO_MAKE_KEY @@ -886,14 +931,28 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, } if (ret == 0) { + int hadPriv = (key->flags & MLKEM_FLAG_PRIV_SET) != 0; + /* Discards the key material, not the parameter set. */ key->flags &= MLKEM_FLAG_TYPE_SET; /* Establish parameters based on key type. */ k = mlkemkey_get_k(key); +#ifdef WOLFSSL_CHECK_MEM_ZERO + if (k != 0) { + mlkemkey_hash_memzero_add(key); + } +#endif if (k == 0) { ret = NOT_COMPILED_IN; } + else if (hadPriv) { + /* Do not write the new key over the old one. */ +#ifndef WOLFSSL_MLKEM_DYNAMIC_KEYS + ForceZero(key->priv, (size_t)k * MLKEM_N * sizeof(sword16)); +#endif + ForceZero(key->z, sizeof(key->z)); + } } #ifndef WOLFSSL_NO_MALLOC @@ -1045,6 +1104,18 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, key->flags |= MLKEM_FLAG_A_SET; #endif } + else if ((key != NULL) && (k != 0)) { + /* Keygen failed after s and z were written; k is 0 until the + * argument checks passed (ISO/IEC 19790:2012 7.9.7). */ +#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS + if (key->priv != NULL) { + ForceZero(key->priv, key->privAllocSz); + } +#else + ForceZero(key->priv, sizeof(key->priv)); +#endif + ForceZero(key->z, sizeof(key->z)); + } /* Zeroize the secret seed material in rho||sigma (sigma) before return. */ ForceZero(buf, sizeof(buf)); @@ -1165,6 +1236,14 @@ key-pair test required by ISO/IEC 19790:2012 sec 7.10.3.3" } #endif /* FIPS v7 or WOLFSSL_VALIDATE_MLKEM_KEYGEN */ + if ((key != NULL) && (k != 0)) { + mlkemkey_hash_wipe(&key->hash); + mlkemkey_hash_wipe(&key->prf); + #ifdef WOLFSSL_CHECK_MEM_ZERO + mlkemkey_hash_memzero_check(key); + #endif + } + return ret; #endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ } @@ -1736,6 +1815,9 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, #ifdef WOLFSSL_CHECK_MEM_ZERO XMEMSET(kr, 0, sizeof(kr)); wc_MemZero_Add("mlkem encapsulate kr", kr, sizeof(kr)); + if (key != NULL) { + mlkemkey_hash_memzero_add(key); + } #endif /* Validate parameters. */ if ((key == NULL) || (ct == NULL) || (ss == NULL) || (rand == NULL)) { @@ -1848,7 +1930,8 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, } #if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM) - if (key->type & MLKEM_KYBER) + /* key may be NULL here; reading it lets the compiler drop later checks. */ + if ((ret == 0) && (key->type & MLKEM_KYBER)) #endif #ifdef WOLFSSL_MLKEM_KYBER { @@ -1879,6 +1962,13 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, /* FIPS 203 3.3: a failed encapsulation leaves no ciphertext. */ ForceZero(ct, ctSz); } + if (key != NULL) { + mlkemkey_hash_wipe(&key->hash); + mlkemkey_hash_wipe(&key->prf); + #ifdef WOLFSSL_CHECK_MEM_ZERO + mlkemkey_hash_memzero_check(key); + #endif + } #ifdef WOLFSSL_MLKEM_KYBER /* msg holds the secret message H(rand) used for Kyber encapsulation; @@ -2250,6 +2340,10 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, XMEMSET(kr, 0, sizeof(kr)); wc_MemZero_Add("mlkem decapsulate msg", msg, sizeof(msg)); wc_MemZero_Add("mlkem decapsulate kr", kr, sizeof(kr)); + wc_MemZero_Add("mlkem decapsulate fail", &fail, sizeof(fail)); + if (key != NULL) { + mlkemkey_hash_memzero_add(key); + } #endif if (ret == 0) { /* Decapsulate the cipher text. */ @@ -2324,10 +2418,20 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, ForceZero(msg, sizeof(msg)); ForceZero(kr, sizeof(kr)); + /* The implicit-rejection flag is destroyed too (FIPS 203 6.3). */ + ForceZero(&fail, sizeof(fail)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(msg, sizeof(msg)); wc_MemZero_Check(kr, sizeof(kr)); + wc_MemZero_Check(&fail, sizeof(fail)); #endif + if (key != NULL) { + mlkemkey_hash_wipe(&key->hash); + mlkemkey_hash_wipe(&key->prf); + #ifdef WOLFSSL_CHECK_MEM_ZERO + mlkemkey_hash_memzero_check(key); + #endif + } #endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ return ret; @@ -2479,6 +2583,13 @@ int wc_MlKemKey_DecodePrivateKey(MlKemKey* key, const unsigned char* in, if (ret == 0) { /* Forget the old key before its buffers are replaced, so a failure * from here on leaves the key unusable. */ + if (key->flags & MLKEM_FLAG_PRIV_SET) { + /* Do not write the new key over the old one. */ +#ifndef WOLFSSL_MLKEM_DYNAMIC_KEYS + ForceZero(key->priv, (size_t)k * MLKEM_N * sizeof(sword16)); +#endif + ForceZero(key->z, sizeof(key->z)); + } key->flags &= ~(MLKEM_FLAG_BOTH_SET | MLKEM_FLAG_H_SET | MLKEM_FLAG_A_SET); } @@ -2915,6 +3026,10 @@ int wc_MlKemKey_EncodePrivateKey(MlKemKey* key, unsigned char* out, word32 len) /* Encode public key - calculates hash of public key. */ ret = wc_MlKemKey_EncodePublicKey(key, p, pubLen); p += pubLen; + if (ret != 0) { + /* s_hat is already in the buffer. */ + ForceZero(out, privLen); + } } if (ret == 0) { /* Append public hash. */ @@ -3232,6 +3347,9 @@ int wc_MlKemKey_PrivateKeyToDer(MlKemKey* key, byte* output, word32 len) if ((ret == 0) && (output != NULL)) { ret = SetAsymKeyDer(output + ((word32)sz - privLen), privLen, NULL, 0, output, len, oidSum); + if (ret < 0) { + ForceZero(output + ((word32)sz - privLen), privLen); + } } else if (ret == 0) { ret = sz; diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index ac3f3140103..3ac34bf6071 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -1099,6 +1099,41 @@ static void mlkem_basemul(sword16* r, const sword16* a, const sword16* b, r[1] = MLKEM_MONT_RED(p1); } +/* Base case multiply added into r: the products of the secret vector stay in + * registers instead of a stack array (ISO/IEC 19790:2012 7.9.7). + * + * r never aliases a or b; saying so keeps the accumulate loop vectorised. + */ +static void mlkem_basemul_add(sword16* WC_RESTRICT r, + const sword16* WC_RESTRICT a, const sword16* WC_RESTRICT b, + sword16 zeta) +{ + sword16 r0; + sword16 t; + sword16 a0 = a[0]; + sword16 a1 = a[1]; + sword16 b0 = b[0]; + sword16 b1 = b[1]; + sword32 p1; + sword32 p2; + + /* Step 1 */ + p1 = (sword32)a0 * b0; + p2 = (sword32)a1 * b1; + r0 = MLKEM_MONT_RED(p2); + p2 = (sword32)zeta * r0; + p2 += p1; + t = MLKEM_MONT_RED(p2); + r[0] = (sword16)(r[0] + t); + + /* Step 2 */ + p1 = (sword32)a0 * b1; + p2 = (sword32)a1 * b0; + p1 += p2; + t = MLKEM_MONT_RED(p1); + r[1] = (sword16)(r[1] + t); +} + /* Multiply two polynomials in NTT domain. r = a * b. * * FIPS 203, Algorithm 11: MultiplyNTTs(f_hat, g_hat) @@ -1168,8 +1203,8 @@ static void mlkem_basemul_mont(sword16* r, const sword16* a, const sword16* b) * @param [in] a First polynomial multiplier. * @param [in] b Second polynomial multiplier. */ -static void mlkem_basemul_mont_add(sword16* r, const sword16* a, - const sword16* b) +static void mlkem_basemul_mont_add(sword16* WC_RESTRICT r, + const sword16* WC_RESTRICT a, const sword16* WC_RESTRICT b) { const sword16* zeta = zetas + 64; @@ -1177,78 +1212,37 @@ static void mlkem_basemul_mont_add(sword16* r, const sword16* a, /* Two multiplications per loop. */ unsigned int i; for (i = 0; i < MLKEM_N; i += 4, zeta++) { - sword16 t0[2]; - sword16 t2[2]; - - mlkem_basemul(t0, a + i + 0, b + i + 0, zeta[0]); - mlkem_basemul(t2, a + i + 2, b + i + 2, (sword16)(-zeta[0])); - - r[i + 0] = (sword16)(r[i + 0] + t0[0]); - r[i + 1] = (sword16)(r[i + 1] + t0[1]); - r[i + 2] = (sword16)(r[i + 2] + t2[0]); - r[i + 3] = (sword16)(r[i + 3] + t2[1]); + mlkem_basemul_add(r + i + 0, a + i + 0, b + i + 0, zeta[0]); + mlkem_basemul_add(r + i + 2, a + i + 2, b + i + 2, + (sword16)(-zeta[0])); } #elif defined(WOLFSSL_MLKEM_NO_LARGE_CODE) /* Four multiplications per loop. */ unsigned int i; for (i = 0; i < MLKEM_N; i += 8, zeta += 2) { - sword16 t0[2]; - sword16 t2[2]; - sword16 t4[2]; - sword16 t6[2]; - - mlkem_basemul(t0, a + i + 0, b + i + 0, zeta[0]); - mlkem_basemul(t2, a + i + 2, b + i + 2, (sword16)(-zeta[0])); - mlkem_basemul(t4, a + i + 4, b + i + 4, zeta[1]); - mlkem_basemul(t6, a + i + 6, b + i + 6, (sword16)(-zeta[1])); - - r[i + 0] = (sword16)(r[i + 0] + t0[0]); - r[i + 1] = (sword16)(r[i + 1] + t0[1]); - r[i + 2] = (sword16)(r[i + 2] + t2[0]); - r[i + 3] = (sword16)(r[i + 3] + t2[1]); - r[i + 4] = (sword16)(r[i + 4] + t4[0]); - r[i + 5] = (sword16)(r[i + 5] + t4[1]); - r[i + 6] = (sword16)(r[i + 6] + t6[0]); - r[i + 7] = (sword16)(r[i + 7] + t6[1]); + mlkem_basemul_add(r + i + 0, a + i + 0, b + i + 0, zeta[0]); + mlkem_basemul_add(r + i + 2, a + i + 2, b + i + 2, + (sword16)(-zeta[0])); + mlkem_basemul_add(r + i + 4, a + i + 4, b + i + 4, zeta[1]); + mlkem_basemul_add(r + i + 6, a + i + 6, b + i + 6, + (sword16)(-zeta[1])); } #else /* Eight multiplications per loop. */ unsigned int i; for (i = 0; i < MLKEM_N; i += 16, zeta += 4) { - sword16 t0[2]; - sword16 t2[2]; - sword16 t4[2]; - sword16 t6[2]; - sword16 t8[2]; - sword16 t10[2]; - sword16 t12[2]; - sword16 t14[2]; - - mlkem_basemul(t0, a + i + 0, b + i + 0, zeta[0]); - mlkem_basemul(t2, a + i + 2, b + i + 2, (sword16)(-zeta[0])); - mlkem_basemul(t4, a + i + 4, b + i + 4, zeta[1]); - mlkem_basemul(t6, a + i + 6, b + i + 6, (sword16)(-zeta[1])); - mlkem_basemul(t8, a + i + 8, b + i + 8, zeta[2]); - mlkem_basemul(t10, a + i + 10, b + i + 10, (sword16)(-zeta[2])); - mlkem_basemul(t12, a + i + 12, b + i + 12, zeta[3]); - mlkem_basemul(t14, a + i + 14, b + i + 14, (sword16)(-zeta[3])); - - r[i + 0] = (sword16)(r[i + 0] + t0[0]); - r[i + 1] = (sword16)(r[i + 1] + t0[1]); - r[i + 2] = (sword16)(r[i + 2] + t2[0]); - r[i + 3] = (sword16)(r[i + 3] + t2[1]); - r[i + 4] = (sword16)(r[i + 4] + t4[0]); - r[i + 5] = (sword16)(r[i + 5] + t4[1]); - r[i + 6] = (sword16)(r[i + 6] + t6[0]); - r[i + 7] = (sword16)(r[i + 7] + t6[1]); - r[i + 8] = (sword16)(r[i + 8] + t8[0]); - r[i + 9] = (sword16)(r[i + 9] + t8[1]); - r[i + 10] = (sword16)(r[i + 10] + t10[0]); - r[i + 11] = (sword16)(r[i + 11] + t10[1]); - r[i + 12] = (sword16)(r[i + 12] + t12[0]); - r[i + 13] = (sword16)(r[i + 13] + t12[1]); - r[i + 14] = (sword16)(r[i + 14] + t14[0]); - r[i + 15] = (sword16)(r[i + 15] + t14[1]); + mlkem_basemul_add(r + i + 0, a + i + 0, b + i + 0, zeta[0]); + mlkem_basemul_add(r + i + 2, a + i + 2, b + i + 2, + (sword16)(-zeta[0])); + mlkem_basemul_add(r + i + 4, a + i + 4, b + i + 4, zeta[1]); + mlkem_basemul_add(r + i + 6, a + i + 6, b + i + 6, + (sword16)(-zeta[1])); + mlkem_basemul_add(r + i + 8, a + i + 8, b + i + 8, zeta[2]); + mlkem_basemul_add(r + i + 10, a + i + 10, b + i + 10, + (sword16)(-zeta[2])); + mlkem_basemul_add(r + i + 12, a + i + 12, b + i + 12, zeta[3]); + mlkem_basemul_add(r + i + 14, a + i + 14, b + i + 14, + (sword16)(-zeta[3])); } #endif } @@ -6273,6 +6267,11 @@ static void mlkem_vec_compress_10_c(byte* r, sword16* v, unsigned int k) { unsigned int i; unsigned int j; +#if defined(WOLFSSL_MLKEM_SMALL) && (defined(WOLFSSL_SMALL_STACK) || \ + defined(WOLFSSL_MLKEM_NO_LARGE_CODE) || defined(BIG_ENDIAN_ORDER) || \ + defined(WOLFSSL_WIDE_BYTE)) + sword16 t[4]; +#endif for (i = 0; i < k; i++) { /* Reduce each coefficient to mod q. */ @@ -6288,7 +6287,6 @@ static void mlkem_vec_compress_10_c(byte* r, sword16* v, unsigned int k) for (j = 0; j < MLKEM_N; j += 4) { #ifdef WOLFSSL_MLKEM_SMALL unsigned int l; - sword16 t[4]; /* Compress four polynomial values to 10 bits each. */ for (l = 0; l < 4; l++) { t[l] = TO_COMP_WORD_10(v, i, j, l); @@ -6361,6 +6359,11 @@ static void mlkem_vec_compress_10_c(byte* r, sword16* v, unsigned int k) } #endif } +#if defined(WOLFSSL_MLKEM_SMALL) && (defined(WOLFSSL_SMALL_STACK) || \ + defined(WOLFSSL_MLKEM_NO_LARGE_CODE) || defined(BIG_ENDIAN_ORDER) || \ + defined(WOLFSSL_WIDE_BYTE)) + ForceZero(t, sizeof(t)); +#endif } /* Compress the vector of polynomials into a byte array with 10 bits each. @@ -6425,6 +6428,7 @@ static void mlkem_vec_compress_11_c(byte* r, sword16* v) unsigned int j; #ifdef WOLFSSL_MLKEM_SMALL unsigned int k; + sword16 t[8]; #endif for (i = 0; i < 4; i++) { @@ -6438,7 +6442,6 @@ static void mlkem_vec_compress_11_c(byte* r, sword16* v) /* Each 8 polynomial coefficients. */ for (j = 0; j < MLKEM_N; j += 8) { #ifdef WOLFSSL_MLKEM_SMALL - sword16 t[8]; /* Compress eight polynomial values to 11 bits each. */ for (k = 0; k < 8; k++) { t[k] = TO_COMP_WORD_11(v, i, j, k); @@ -6485,6 +6488,9 @@ static void mlkem_vec_compress_11_c(byte* r, sword16* v) r += 11; } } +#ifdef WOLFSSL_MLKEM_SMALL + ForceZero(t, sizeof(t)); +#endif } /* Compress the vector of polynomials into a byte array with 11 bits each. @@ -6912,6 +6918,9 @@ static void mlkem_compress_4_c(byte* b, sword16* p) /* Move over set bytes. */ b += 4; } +#ifdef WOLFSSL_MLKEM_SMALL + ForceZero(t, sizeof(t)); +#endif } /* Compress a polynomial into byte array with coefficients of 4 bits. @@ -7015,6 +7024,9 @@ static void mlkem_compress_5_c(byte* b, sword16* p) /* Move over set bytes. */ b += 5; } +#ifdef WOLFSSL_MLKEM_SMALL + ForceZero(t, sizeof(t)); +#endif } /* Compress a polynomial into byte array with coefficients of 5 bits. diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 27fbc7ad197..003350d9944 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -785,6 +785,24 @@ WOLFSSL_API void wc_ForkLock_SetBroken(wc_ForkLock* lock, int broken) } #endif +#ifdef WC_BARRIER_DATA_USES_SINK +/* Callee of the portable WC_BARRIER_DATA() arm. It is reached through a + * pointer read with a volatile load, so link time optimization cannot see + * that the body is empty and drop the escape of the buffer address. */ +static void wc_BarrierDataSinkImpl(void* p) +{ + (void)p; +} +static void (* const wc_BarrierDataSinkPtr)(void*) = wc_BarrierDataSinkImpl; + +WC_NO_INLINE void wc_BarrierDataSink(void* p) +{ + void (*fn)(void*) = + *(void (* const volatile *)(void*))&wc_BarrierDataSinkPtr; + fn(p); +} +#endif /* WC_BARRIER_DATA_USES_SINK */ + /* Used to initialize state for wolfcrypt return 0 on success */ diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 03f62733d35..a634a3c7157 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -844,7 +844,7 @@ static int slhdsakey_hash_f_sha2(SlhDsaKey* key, const byte* pk_seed, { int ret; byte address[SLHDSA_HAC_SZ]; - byte digest[WC_SHA256_DIGEST_SIZE]; + byte* digest = key->sha2Digest; (void)pk_seed; @@ -1352,6 +1352,9 @@ static int slhdsakey_prf_msg_sha2(SlhDsaKey* key, const byte* sk_prf, byte digest[WC_SHA256_DIGEST_SIZE]; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa prf_msg digest", digest, sizeof(digest)); +#endif if (SLHDSA_SHA2_USE_SHA256(n)) { hmacType = WC_SHA256; } @@ -1385,6 +1388,10 @@ static int slhdsakey_prf_msg_sha2(SlhDsaKey* key, const byte* sk_prf, if (ret == 0) { XMEMCPY(hash, digest, n); } + ForceZero(digest, sizeof(digest)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(digest, sizeof(digest)); +#endif return ret; } @@ -5275,8 +5282,10 @@ static int slhdsakey_wots_sign(SlhDsaKey* key, const byte* m, if (!SLHDSA_IS_SHA2(key->params->param) && IS_INTEL_AVX2(cpuid_flags)) { int svr_ret = SAVE_VECTOR_REGISTERS2(); - if (svr_ret != 0) + if (svr_ret != 0) { + ForceZero(msg, sizeof(msg)); return svr_ret; + } ret = slhdsakey_wots_sign_chain_x4(key, msg, sk_seed, pk_seed, adrs, sk_adrs, sig); RESTORE_VECTOR_REGISTERS(); @@ -5337,6 +5346,7 @@ static int slhdsakey_wots_sign(SlhDsaKey* key, const byte* m, wc_MemZero_Check(sk, n); #endif } + ForceZero(msg, sizeof(msg)); return ret; } @@ -6643,6 +6653,10 @@ static int slhdsakey_hash_f_ti_x4(const byte* pk_seed, byte* addr, byte* node, WC_ALLOC_VAR_EX(state, word64, SLHDSA_SHAKE_X4_STATE_W, heap, DYNAMIC_TYPE_SLHDSA, ret = MEMORY_E); if (ret == 0) { +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa hash_f_ti_x4 state", state, + sizeof(word64) * SLHDSA_SHAKE_X4_STATE_W); +#endif o = slhdsakey_shake256_set_seed_ha_x4(state, pk_seed, addr, n); SHAKE256_SET_TREE_INDEX(state, o, ti); for (i = 0; i < n / 8; i++) { @@ -6660,6 +6674,11 @@ static int slhdsakey_hash_f_ti_x4(const byte* pk_seed, byte* addr, byte* node, slhdsakey_shake256_get_hash_x4(state, node, n); } + /* state holds four FORS secret leaves (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(state, sizeof(word64) * SLHDSA_SHAKE_X4_STATE_W); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(state, sizeof(word64) * SLHDSA_SHAKE_X4_STATE_W); +#endif WC_FREE_VAR_EX(state, heap, DYNAMIC_TYPE_SLHDSA); } @@ -7037,6 +7056,10 @@ static int slhdsakey_fors_node_x4_z0(SlhDsaKey* key, const byte* sk_seed, ret = HASH_F(key, pk_seed, adrs, node, n, node); } + if (ret != 0) { + /* node may still hold the FORS secret leaf. */ + ForceZero(node, n); + } return ret; } @@ -7077,6 +7100,9 @@ static int slhdsakey_fors_node_x4_z1(SlhDsaKey* key, const byte* sk_seed, byte n = key->params->n; byte nodes[2 * SLHDSA_MAX_N]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa fors_node_x4_z1 nodes", nodes, sizeof(nodes)); +#endif /* Step 7: Compute left node. */ /* Step 2: Generate private key value for index. */ ret = slhdsakey_fors_sk_gen(key, sk_seed, pk_seed, adrs, 2 * i + 0, nodes); @@ -7109,6 +7135,11 @@ static int slhdsakey_fors_node_x4_z1(SlhDsaKey* key, const byte* sk_seed, ret = HASH_H(key, pk_seed, adrs, nodes, n, node); } + /* nodes held two FORS secret leaves (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(nodes, sizeof(nodes)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(nodes, sizeof(nodes)); +#endif return ret; } @@ -7162,6 +7193,10 @@ static int slhdsakey_fors_node_x4_low(SlhDsaKey* key, const byte* sk_seed, if (ret == 0) { byte sk_addr[SLHDSA_HA_SZ]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa fors_node_x4_low nodes", nodes, + (1 << SLHDSA_MAX_FORS_NODE_DEPTH) * SLHDSA_MAX_N); +#endif HA_SetTreeHeight(adrs, 0); /* Copy address for FORS PRF. */ HA_Copy(sk_adrs, adrs); @@ -7242,6 +7277,14 @@ static int slhdsakey_fors_node_x4_low(SlhDsaKey* key, const byte* sk_seed, ret = HASH_H(key, pk_seed, adrs, nodes, n, node); } + /* nodes may still hold FORS secret leaves (ISO/IEC 19790:2012 7.9.7). */ + if (WC_VAR_OK(nodes)) { + ForceZero(nodes, (1 << SLHDSA_MAX_FORS_NODE_DEPTH) * SLHDSA_MAX_N); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(nodes, (1 << SLHDSA_MAX_FORS_NODE_DEPTH) * + SLHDSA_MAX_N); +#endif + } WC_FREE_VAR_EX(nodes, key->heap, DYNAMIC_TYPE_SLHDSA); return ret; } @@ -7476,6 +7519,10 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, /* Step 5: Compute node from public key seed, address and value. */ ret = HASH_F(key, pk_seed, adrs, node, n, node); } + if (ret != 0) { + /* node may still hold the FORS secret leaf. */ + ForceZero(node, n); + } } /* Step 6: Non leaf node. */ else { @@ -7488,6 +7535,10 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, WC_ALLOC_VAR_EX(nodes, byte, (SLHDSA_MAX_A + 1) * SLHDSA_MAX_N, key->heap, DYNAMIC_TYPE_SLHDSA, ret = MEMORY_E); if (ret == 0) { +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa fors_node_c nodes", nodes, + (SLHDSA_MAX_A + 1) * SLHDSA_MAX_N); +#endif /* For all leaf nodes. */ for (j = 0; j < m; j++) { word32 o = ((word32)z - 1U + (j & 1U)) * n; @@ -7545,6 +7596,14 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, } } + /* nodes may still hold FORS secret leaves + * (ISO/IEC 19790:2012 7.9.7). */ + if (WC_VAR_OK(nodes)) { + ForceZero(nodes, (SLHDSA_MAX_A + 1) * SLHDSA_MAX_N); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK) + wc_MemZero_Check(nodes, (SLHDSA_MAX_A + 1) * SLHDSA_MAX_N); +#endif + } WC_FREE_VAR_EX(nodes, key->heap, DYNAMIC_TYPE_SLHDSA); } @@ -7600,10 +7659,17 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, /* Step 5: Compute node from public key seed, address and value. */ ret = HASH_F(key, pk_seed, adrs, node, n, node); } + if (ret != 0) { + /* node may still hold the FORS secret leaf. */ + ForceZero(node, n); + } } else { byte nodes[2 * SLHDSA_MAX_N]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa fors_node_c nodes", nodes, sizeof(nodes)); +#endif /* Step 7: Compute left node. */ ret = slhdsakey_fors_node_c(key, sk_seed, 2 * i + 0, z - 1, pk_seed, adrs, nodes); @@ -7620,6 +7686,11 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, /* Step 11: Compute node from public key seed, address and nodes. */ ret = HASH_H(key, pk_seed, adrs, nodes, n, node); } + /* nodes held two FORS secret leaves (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(nodes, sizeof(nodes)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(nodes, sizeof(nodes)); +#endif } return ret; @@ -7744,6 +7815,7 @@ static int slhdsakey_fors_sign(SlhDsaKey* key, const byte* md, /* Private key values reached the caller's buffer; do not leave them. */ ForceZero(sig_start, (size_t)(sig_fors - sig_start)); } + ForceZero(indices, sizeof(indices)); return ret; } @@ -8876,6 +8948,10 @@ void wc_SlhDsaKey_Free(SlhDsaKey* key) #endif } +#ifdef WOLFSSL_SLHDSA_SHA2 + ForceZero(key->sha2Digest, sizeof(key->sha2Digest)); +#endif + #ifdef WOLF_PRIVATE_KEY_ID key->idLen = 0; key->labelLen = 0; @@ -9038,15 +9114,24 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng) } #else if (ret == 0) { - /* Steps 1-5: Generate the 3 random hashes. */ - ret = wc_RNG_GenerateBlock(rng, key->sk, 3U * key->params->n); - } - if (ret == 0) { + byte seeds[3 * SLHDSA_MAX_N]; byte n = key->params->n; - /* Step 7: Make the key with the random */ - ret = wc_SlhDsaKey_MakeKeyWithRandom(key, key->sk, n, key->sk + n, n, - key->sk + 2 * n, n); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa make_key seeds", seeds, sizeof(seeds)); +#endif + /* Steps 1-5: Generate the 3 random hashes. */ + ret = wc_RNG_GenerateBlock(rng, seeds, 3U * n); + if (ret == 0) { + /* Stage new seeds separately: an RNG failure preserves the old + * key, and MakeKeyWithRandom zeroizes it before replacement. */ + ret = wc_SlhDsaKey_MakeKeyWithRandom(key, seeds, n, seeds + n, n, + seeds + 2 * n, n); + } + ForceZero(seeds, sizeof(seeds)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(seeds, sizeof(seeds)); +#endif } #endif /* WOLF_CRYPTO_CB_ONLY_SLHDSA */ @@ -9054,6 +9139,77 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng) } #ifndef WOLF_CRYPTO_CB_ONLY_SLHDSA +/* Wipe the hash object that absorbed SK.seed or SK.prf, once per operation + * (ISO/IEC 19790:2012 7.9.7). */ +static void slhdsakey_wipe_secret_hash(SlhDsaKey* key) +{ +#ifdef WOLFSSL_SLHDSA_SHA2 + if (SLHDSA_IS_SHA2(key->params->param)) { + ForceZero(key->sha2Digest, sizeof(key->sha2Digest)); + if (key->hash.sha2.sha256_inited) { + wc_Sha256Free(&key->hash.sha2.sha256); + key->hash.sha2.sha256_inited = 0; + } + } + #ifndef WOLFSSL_SLHDSA_NO_SHAKE + else + #endif +#endif +#ifndef WOLFSSL_SLHDSA_NO_SHAKE + { + wc_Shake* shake = &key->hash.shk.shake; + + ForceZero(shake->s, sizeof(shake->s)); + ForceZero(shake->t, sizeof(shake->t)); + shake->i = 0; + } +#endif +} + +#ifdef WOLFSSL_CHECK_MEM_ZERO +/* Register the hash state that slhdsakey_wipe_secret_hash must clear. */ +static void slhdsakey_memzero_add_hash(SlhDsaKey* key) +{ +#ifdef WOLFSSL_SLHDSA_SHA2 + if (SLHDSA_IS_SHA2(key->params->param)) { + wc_MemZero_Add("slhdsa key sha2Digest", key->sha2Digest, + sizeof(key->sha2Digest)); + } + #ifndef WOLFSSL_SLHDSA_NO_SHAKE + else + #endif +#endif +#ifndef WOLFSSL_SLHDSA_NO_SHAKE + { + wc_Shake* shake = &key->hash.shk.shake; + + wc_MemZero_Add("slhdsa key shake s", shake->s, sizeof(shake->s)); + wc_MemZero_Add("slhdsa key shake t", shake->t, sizeof(shake->t)); + } +#endif +} + +static void slhdsakey_memzero_check_hash(SlhDsaKey* key) +{ +#ifdef WOLFSSL_SLHDSA_SHA2 + if (SLHDSA_IS_SHA2(key->params->param)) { + wc_MemZero_Check(key->sha2Digest, sizeof(key->sha2Digest)); + } + #ifndef WOLFSSL_SLHDSA_NO_SHAKE + else + #endif +#endif +#ifndef WOLFSSL_SLHDSA_NO_SHAKE + { + wc_Shake* shake = &key->hash.shk.shake; + + wc_MemZero_Check(shake->s, sizeof(shake->s)); + wc_MemZero_Check(shake->t, sizeof(shake->t)); + } +#endif +} +#endif + /* Compute the public key root from the seeds already staged in the key. * * FIPS 205, Section 9.1, Algorithm 18, steps 1 to 3. @@ -9084,6 +9240,9 @@ static int slhdsakey_compute_root(SlhDsaKey* key) } #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_add_hash(key); +#endif /* Step 1: Set address to all zeroes. */ HA_Init(adrs); /* Step 2: Set the address layer to the top of the subtree. */ @@ -9094,6 +9253,10 @@ static int slhdsakey_compute_root(SlhDsaKey* key) if (ret == 0) { key->flags = WC_SLHDSA_FLAG_BOTH_KEYS; } + slhdsakey_wipe_secret_hash(key); +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_check_hash(key); +#endif return ret; } @@ -9150,14 +9313,27 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, } else { byte n = key->params->n; + byte seeds[3 * SLHDSA_MAX_N]; - /* Step 4: Copy the seeds into the key if they didn't come from the key. - */ - if (sk_seed != key->sk) { - XMEMCPY(key->sk , sk_seed, n); - XMEMCPY(key->sk + n, sk_prf , n); - XMEMCPY(key->sk + 2 * n, pk_seed, n); - } +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa make_key_with_random seeds", seeds, + sizeof(seeds)); +#endif + /* Any of the three inputs may alias the key, in any order. Preserve + * all of them before destroying the old SSPs (ISO 19790 7.9.7). */ + XMEMCPY(seeds, sk_seed, n); + XMEMCPY(seeds + n, sk_prf, n); + XMEMCPY(seeds + 2 * n, pk_seed, n); +#ifndef WOLF_CRYPTO_CB_ONLY_SLHDSA + slhdsakey_wipe_secret_hash(key); +#endif + key->flags = 0; + ForceZero(key->sk, sizeof(key->sk)); + XMEMCPY(key->sk, seeds, 3U * n); + ForceZero(seeds, sizeof(seeds)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(seeds, sizeof(seeds)); +#endif } #ifdef WOLF_CRYPTO_CB @@ -9188,6 +9364,9 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, /* The seeds are staged in the key above, so the shared helper works * from key->sk for both this caller and the CheckKey fallback. */ ret = slhdsakey_compute_root(key); + if (ret != 0) { + ForceZero(key->sk, 2U * key->params->n); + } } #endif /* WOLF_CRYPTO_CB_ONLY_SLHDSA */ @@ -9210,6 +9389,9 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, word32 pct_pubLen = (word32)(n * 2); HashAddress pct_adrs; +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_add_hash(key); +#endif /* The key identifier the IG names. */ ret = wc_SlhDsaKey_ExportPublic(key, pct_pub, &pct_pubLen); if ((ret == 0) && (XMEMCMP(pct_pub, key->sk + 2 * n, n) != 0)) { @@ -9227,6 +9409,10 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, } ForceZero(pct_root, sizeof(pct_root)); } + slhdsakey_wipe_secret_hash(key); +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_check_hash(key); +#endif /* Free a key that failed the test, so a caller ignoring the return * value cannot sign with it. ISO/IEC 19790:2012 sec 7.10.1 forbids using @@ -9280,7 +9466,11 @@ static int slhdsakey_sign(SlhDsaKey* key, byte* md, byte* sig) word32 l; byte pk_fors[SLHDSA_MAX_N]; byte n = key->params->n; + byte* sigFors = sig; +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_add_hash(key); +#endif /* Steps 1, 7-13: Set address based on message digest. */ slhdsakey_set_ha_from_md(key, md, adrs, t, &l); @@ -9298,6 +9488,16 @@ static int slhdsakey_sign(SlhDsaKey* key, byte* md, byte* sig) ret = slhdsakey_ht_sign(key, pk_fors, key->sk, key->sk + 2 * n, t, l, sig); } + if (ret != 0) { + /* Unreleased FORS secrets may be in sig + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(sigFors, + (size_t)(key->params->k * (1 + key->params->a) * n)); + } + slhdsakey_wipe_secret_hash(key); +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_check_hash(key); +#endif return ret; } @@ -9408,6 +9608,8 @@ static int slhdsakey_sign_internal_msg(SlhDsaKey* key, const byte* m, /* FIPS 205 3.1: a failed sign leaves no part of a signature. */ ForceZero(sigOut, key->params->sigLen); } + ForceZero(md, sizeof(md)); + slhdsakey_wipe_secret_hash(key); } return ret; @@ -9552,6 +9754,8 @@ static int slhdsakey_sign_external(SlhDsaKey* key, const byte* ctx, byte ctxSz, /* FIPS 205 3.1: a failed sign leaves no part of a signature. */ ForceZero(sigOut, key->params->sigLen); } + ForceZero(md, sizeof(md)); + slhdsakey_wipe_secret_hash(key); } return ret; @@ -10678,6 +10882,8 @@ static int slhdsakey_signhash_external(SlhDsaKey* key, const byte* ctx, /* FIPS 205 3.1: a failed sign leaves no part of a signature. */ ForceZero(sigOut, key->params->sigLen); } + ForceZero(md, sizeof(md)); + slhdsakey_wipe_secret_hash(key); } return ret; @@ -11175,9 +11381,25 @@ int wc_SlhDsaKey_ImportPrivate(SlhDsaKey* key, const byte* priv, word32 privLen) ret = BAD_LENGTH_E; } else { + byte encoded[4 * SLHDSA_MAX_N]; + +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("slhdsa import_private encoded", encoded, + sizeof(encoded)); +#endif + /* Preserve aliased input before destroying the previous key. */ + XMEMCPY(encoded, priv, privLen); +#ifndef WOLF_CRYPTO_CB_ONLY_SLHDSA + slhdsakey_wipe_secret_hash(key); +#endif + key->flags = 0; + ForceZero(key->sk, sizeof(key->sk)); /* Copy private and public key data into SLH-DSA key object. */ - XMEMCPY(key->sk, priv, 4U * key->params->n); - key->flags = WC_SLHDSA_FLAG_BOTH_KEYS; + XMEMCPY(key->sk, encoded, privLen); + ForceZero(encoded, sizeof(encoded)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(encoded, sizeof(encoded)); +#endif /* Under crypto callback only the device performs every hash, so it derives * the SHA2 midstates for the imported PK.seed itself. */ #if defined(WOLFSSL_SLHDSA_SHA2) && !defined(WOLF_CRYPTO_CB_ONLY_SLHDSA) @@ -11185,6 +11407,14 @@ int wc_SlhDsaKey_ImportPrivate(SlhDsaKey* key, const byte* priv, word32 privLen) ret = slhdsakey_precompute_sha2_midstates(key); } #endif + if (ret == 0) { + key->flags = WC_SLHDSA_FLAG_BOTH_KEYS; + } + else { + /* The seeds are unusable without the midstates. */ + key->flags = 0; + ForceZero(key->sk, 4U * key->params->n); + } } return ret; @@ -11290,6 +11520,9 @@ int wc_SlhDsaKey_CheckKey(SlhDsaKey* key) byte root[SLHDSA_MAX_N]; HashAddress adrs; +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_add_hash(key); +#endif /* Recompute the public root from the private seed and compare. * Done directly rather than by regenerating the key: regeneration * overwrites the key being checked, and its key-pair test frees the @@ -11302,6 +11535,10 @@ int wc_SlhDsaKey_CheckKey(SlhDsaKey* key) ret = WC_KEY_MISMATCH_E; } ForceZero(root, sizeof(root)); + slhdsakey_wipe_secret_hash(key); +#ifdef WOLFSSL_CHECK_MEM_ZERO + slhdsakey_memzero_check_hash(key); +#endif } #endif /* WOLF_CRYPTO_CB_ONLY_SLHDSA */ diff --git a/wolfcrypt/src/wc_xmss_impl.c b/wolfcrypt/src/wc_xmss_impl.c index 9aadbcc6313..2b38d1e332c 100644 --- a/wolfcrypt/src/wc_xmss_impl.c +++ b/wolfcrypt/src/wc_xmss_impl.c @@ -673,6 +673,9 @@ static WC_INLINE void wc_xmss_hash(XmssState* state, const byte* in, /* Partial SHA-256 digest. */ else if (params->hash == WC_HASH_TYPE_SHA256) { byte buf[WC_SHA256_DIGEST_SIZE]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_xmss_hash buf", buf, sizeof(buf)); +#endif ret = wc_Sha256Update(&state->digest.sha256, in, inlen); if (ret == 0) { ret = wc_Sha256Final(&state->digest.sha256, buf); @@ -680,6 +683,12 @@ static WC_INLINE void wc_xmss_hash(XmssState* state, const byte* in, if (ret == 0) { XMEMCPY(out, buf, params->n); } + /* Prefix may be a WOTS+ secret element + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(buf, sizeof(buf)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(buf, sizeof(buf)); +#endif } #endif else diff --git a/wolfssl/openssl/sha.h b/wolfssl/openssl/sha.h index e8a1678a143..5ca533fecb8 100644 --- a/wolfssl/openssl/sha.h +++ b/wolfssl/openssl/sha.h @@ -27,6 +27,7 @@ #include #include +#include #include #ifdef WOLFSSL_PREFIX #include "prefix_sha.h" @@ -52,11 +53,7 @@ #ifndef NO_SHA typedef struct WOLFSSL_SHA_CTX { /* big enough to hold wolfcrypt Sha, but check on init */ -#ifdef WC_NO_PTR_INT_CAST - void* holder[(160 + WC_ASYNC_DEV_SIZE + CTX_SHA_HW_ADDER) / sizeof(void*)]; -#else - void* holder[(112 + WC_ASYNC_DEV_SIZE + CTX_SHA_HW_ADDER) / sizeof(void*)]; -#endif + void* holder[sizeof(wc_Sha) / sizeof(void*)]; #if defined(WOLFSSL_DEVCRYPTO_HASH) || defined(WOLFSSL_HASH_KEEP) void* keephash_holder[sizeof(void*) + (2 * sizeof(unsigned int))]; #endif @@ -114,8 +111,7 @@ typedef WOLFSSL_SHA_CTX SHA_CTX; * to Sha224, is expected to also be 16 byte aligned addresses. */ typedef struct WOLFSSL_SHA224_CTX { /* big enough to hold wolfcrypt Sha224, but check on init */ - ALIGN16 void* holder[(274 + CTX_SHA_HW_ADDER + WC_ASYNC_DEV_SIZE) / - sizeof(void*)]; + ALIGN16 void* holder[sizeof(wc_Sha224) / sizeof(void*)]; #if defined(WOLFSSL_DEVCRYPTO_HASH) || defined(WOLFSSL_HASH_KEEP) ALIGN16 void* keephash_holder[sizeof(void*) + (2 * sizeof(unsigned int))]; #endif diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index fdc61e091e4..bd69a436cba 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -699,6 +699,7 @@ struct ecc_key { #define ecc_blind_k(key, b) (void)b #define ecc_blind_k_rng(key, rng) 0 #define ecc_forcezero_k(key) mp_forcezero((key)->k) +#define ecc_put_k(key) WC_DO_NOTHING #define wc_ecc_key_get_priv(key) (key)->k #else @@ -706,6 +707,7 @@ mp_int* ecc_get_k(ecc_key* key); void ecc_blind_k(ecc_key* key, mp_int* b); int ecc_blind_k_rng(ecc_key* key, WC_RNG* rng); WOLFSSL_LOCAL void ecc_forcezero_k(ecc_key* key); +WOLFSSL_LOCAL void ecc_put_k(ecc_key* key); WOLFSSL_API mp_int* wc_ecc_key_get_priv(ecc_key* key); #endif diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index e11bfd026ff..4bd22ff2df0 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -128,6 +128,24 @@ #define WC_SHA256_DIGEST_SIZE 32 #define WC_SHA256_PAD_SIZE 56 +/* Size, in bytes, of the C transform's caller-owned scratch (Sha256TScratch + * in sha256.c): the message schedule plus the eight working variables. + * External providers of wc_Sha256.W under WOLFSSL_SMALL_STACK_CACHE (the + * init and copy paths, the Nuvoton copy callback, linuxkm's w_buf) must + * supply at least this much; sha256.c statically asserts this matches the + * struct. SHA256_MANY_REGISTERS must be defined globally (settings.h or + * user_settings.h), never per-TU, or providers and the transform disagree + * on this size. The macro's name is itself interface: external + * integrators -- notably wolfBoot's static allocator -- size their + * buffers by it and gate on its presence, so do not rename it. */ +#ifdef SHA256_MANY_REGISTERS + #define WC_SHA256_TSCRATCH_SIZE \ + (WC_SHA256_BLOCK_SIZE + 8 * sizeof(word32)) +#else + #define WC_SHA256_TSCRATCH_SIZE \ + ((WC_SHA256_BLOCK_SIZE + 8) * sizeof(word32)) +#endif + #ifdef WOLFSSL_TI_HASH #include "wolfssl/wolfcrypt/port/ti/ti-hash.h" @@ -212,6 +230,10 @@ struct wc_Sha256 { WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ #if defined(WOLFSSL_SMALL_STACK_CACHE) && !defined(WC_SHA2_NO_SMALL_STACK) + /* Every provider of this buffer (InitSha256, the copy paths, the + * Nuvoton callback, linuxkm's w_buf) must size it + * WC_SHA256_TSCRATCH_SIZE: the C transform uses it as its full + * caller-owned scratch, not just the message schedule. */ word32* W; #endif /* !FREESCALE_LTC_SHA && !STM32_HASH_SHA2 */ #ifdef WOLFSSL_DEVCRYPTO_HASH diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index 441904360db..ad7eef309ea 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -121,6 +121,16 @@ #define WC_SHA512_DIGEST_SIZE 64 #define WC_SHA512_PAD_SIZE 112 + /* Size, in bytes, of the C transform's caller-owned scratch + * (Sha512TScratch in sha512.c): the 16-entry message schedule window. + * External providers of wc_Sha512.W under WOLFSSL_SMALL_STACK_CACHE + * size their allocation as this plus the wc_Sha512Transform() backup + * block; sha512.c statically asserts this matches the struct. The + * macro's name is itself interface: external integrators -- notably + * wolfBoot's static allocator -- size their buffers by it and gate + * on its presence, so do not rename it. */ + #define WC_SHA512_TSCRATCH_SIZE (16 * sizeof(word64)) + #define WC_SHA512_224_BLOCK_SIZE WC_SHA512_BLOCK_SIZE #define WC_SHA512_224_DIGEST_SIZE 28 #define WC_SHA512_224_PAD_SIZE WC_SHA512_PAD_SIZE diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 546b95f71c2..d61ee49f6cb 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -146,6 +146,30 @@ #endif #endif +/* Restrict-qualified pointer parameter: a promise to the compiler that, within + * the callee, no object modified through this pointer is accessed through any + * other pointer. Lets the callee keep values loaded through other pointers in + * registers across stores through this one, recovering the codegen of a local + * array for caller-supplied scratch. On compilers with no restrict support it + * expands to nothing, which only forfeits the optimization. */ +#ifndef WC_RESTRICT + #ifdef __cplusplus + /* C++ has no standard restrict; the major compilers accept + * __restrict. */ + #if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) + #define WC_RESTRICT __restrict + #else + #define WC_RESTRICT + #endif + #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define WC_RESTRICT restrict + #elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) + #define WC_RESTRICT __restrict + #else + #define WC_RESTRICT + #endif +#endif + #if (defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)) || defined(HAVE_SELFTEST) #define INLINE WC_INLINE #if defined(__GNUC__) || defined(__clang__) @@ -2150,7 +2174,9 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); /* Compiler barrier that also treats the memory at ptr as read, so a wipe of * that memory cannot be dropped as a dead store. The GNU form emits no CPU * fence; cross-thread ordering is the caller's job. Without GNU asm (other - * compilers, or WOLFSSL_NO_ASM) it falls back to WC_BARRIER(). */ + * compilers, or WOLFSSL_NO_ASM) ptr escapes to an out-of-line call in + * wc_port.c, since a bare compiler barrier does not cover a local whose + * address never escaped. */ #ifdef WC_BARRIER_DATA /* use user-supplied WC_BARRIER_DATA() definition. */ #elif defined(__CC_ARM) @@ -2161,7 +2187,10 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); #define WC_BARRIER_DATA(ptr) \ __asm__ __volatile__("" : : "r"(ptr) : "memory") #else - #define WC_BARRIER_DATA(ptr) do { (void)(ptr); WC_BARRIER(); } while (0) + #define WC_BARRIER_DATA_USES_SINK + /* Internal to WC_BARRIER_DATA(). ptr is never dereferenced. */ + WOLFSSL_API void wc_BarrierDataSink(void* ptr); + #define WC_BARRIER_DATA(ptr) do { wc_BarrierDataSink((ptr)); } while (0) #endif diff --git a/wolfssl/wolfcrypt/wc_slhdsa.h b/wolfssl/wolfcrypt/wc_slhdsa.h index 6e4f034bb0c..70d48f3f4a2 100644 --- a/wolfssl/wolfcrypt/wc_slhdsa.h +++ b/wolfssl/wolfcrypt/wc_slhdsa.h @@ -850,6 +850,10 @@ struct SlhDsaKey { } sha2; #endif } hash; +#ifdef WOLFSSL_SLHDSA_SHA2 + /* Reusable F-function output, cleared after private operations and Free. */ + byte sha2Digest[WC_SHA256_DIGEST_SIZE]; +#endif }; #ifndef WC_SLHDSAKEY_TYPE_DEFINED