From b60019f75c6432e0288b5bb1251c56d7bfc4eecb Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 9 Sep 2026 14:51:02 -0600 Subject: [PATCH 01/74] Zeroize EdDSA scalars and KDF blocks; reject non-canonical Ed448 keys ISO/IEC 19790:2012 7.9.7 and FIPS 186-5 7.7 step 1. --- wolfcrypt/src/ed25519.c | 12 ++++++++++++ wolfcrypt/src/ed448.c | 14 +++++++++++++- wolfcrypt/src/kdf.c | 4 ++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 8fcdda78794..a4f5da41869 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -427,6 +427,11 @@ 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)); +#endif return ret; } @@ -493,6 +498,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; diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 0fc8bf5dc88..b64ac458033 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -448,6 +448,9 @@ 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). */ + ForceZero(az, sizeof(az)); + return ret; } @@ -527,6 +530,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 @@ -722,7 +732,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) { @@ -985,6 +996,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; diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 3f7aa7bcd28..5b8e2d40862 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -846,6 +846,8 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, 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)); } } else { @@ -891,6 +893,8 @@ 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)); } } } From b0ab8eb7a06d4c69a0acb81826ddbe0357cd66e9 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 9 Sep 2026 15:28:31 -0600 Subject: [PATCH 02/74] Zeroize temporary SSPs in the FIPS v7 boundary wc_Sha3Free and wc_ShaFree now clear their state; also DRBG, RSA MGF1, AES KW, XMSS, LMS, SLH-DSA, ML-KEM and X25519/X448 paths. --- wolfcrypt/src/aes.c | 32 ++++++++++++++++++++------------ wolfcrypt/src/curve25519.c | 6 ++++++ wolfcrypt/src/curve448.c | 3 +++ wolfcrypt/src/ecc.c | 10 ++++++++++ wolfcrypt/src/kdf.c | 2 ++ wolfcrypt/src/random.c | 4 ++++ wolfcrypt/src/rsa.c | 22 ++++++++++++++++++++++ wolfcrypt/src/sha.c | 4 ++++ wolfcrypt/src/sha3.c | 14 +++++++++++--- wolfcrypt/src/wc_lms_impl.c | 10 ++++++++++ wolfcrypt/src/wc_mlkem.c | 12 ++++++++++++ wolfcrypt/src/wc_slhdsa.c | 27 +++++++++++++++++++++++++++ wolfcrypt/src/wc_xmss.c | 21 +++++++++++++-------- wolfcrypt/src/wc_xmss_impl.c | 3 +++ 14 files changed, 147 insertions(+), 23 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 254d595fbbe..e676efc9b09 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; @@ -18241,13 +18245,14 @@ 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)); - return 0; + return ret; } int wc_AesKeyWrap_ex(Aes *aes, const byte* in, word32 inSz, byte* out, @@ -18409,13 +18414,16 @@ 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); + } + /* 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)); - return 0; + return ret; } int wc_AesKeyUnWrap_ex(Aes *aes, const byte* in, word32 inSz, byte* out, diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 93ff76d0cdd..9ba273338b1 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -792,6 +792,12 @@ int wc_curve25519_make_key(WC_RNG* rng, int keysize, curve25519_key* key) } #endif key->pubSet = (ret == 0); + if (ret != 0) { + /* Public half failed: drop the scalar too + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(key->k, sizeof(key->k)); + key->privSet = 0; + } } } #endif /* !WOLFSSL_SE050 */ diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index 851db3bdda3..89c912b713f 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -291,6 +291,9 @@ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) else { ForceZero(key->k, sizeof(key->k)); XMEMSET(key->p, 0, sizeof(key->p)); + /* A zeroised SSP shall not be reusable + * (ISO/IEC 19790:2012 7.9.7 [09.29]). */ + key->privSet = 0; } } #endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index ca0db6d251b..3adf96b7376 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -12476,6 +12476,16 @@ 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). */ + mp_forcezero(key->k); + #ifdef WOLFSSL_ECC_BLIND_K + mp_forcezero(key->kb); + mp_forcezero(key->ku); + #endif + } + #ifdef WOLFSSL_MAXQ10XX_CRYPTO if ((ret == 0) && (key->devId != INVALID_DEVID)) { ret = wc_MAXQ10XX_EccSetKey(key, key->dp->size); diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 5b8e2d40862..6574c2ce63d 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -900,6 +900,8 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, } _HashFree(enmhashId, &hash); + /* hash absorbed the shared secret K (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(&hash, sizeof(hash)); return ret; } diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index a30111f04a2..ccd0af53e3f 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1775,6 +1775,8 @@ static WARN_UNUSED_RESULT int Hash_gen(DRBG_internal* drbg, byte* out, wc_MemZero_Check(digest, WC_SHA256_DIGEST_SIZE); #endif #endif + /* digest holds the last output block (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(digest, WC_SHA256_DIGEST_SIZE); #ifndef WOLFSSL_SMALL_STACK_CACHE WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST); @@ -2445,6 +2447,8 @@ static WARN_UNUSED_RESULT int Hash512_gen(DRBG_SHA512_internal* drbg, wc_MemZero_Check(digest, WC_SHA512_DIGEST_SIZE); #endif #endif + /* See Hash_gen. */ + ForceZero(digest, WC_SHA512_DIGEST_SIZE); #ifndef WOLFSSL_SMALL_STACK_CACHE WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST); diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 8f5f10d4304..2e54ddafd35 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -1115,6 +1115,17 @@ 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 /* check for if dynamic memory was needed, then free */ #ifdef WOLFSSL_SMALL_STACK_CACHE wc_HashFree(hash, hType); @@ -1133,6 +1144,17 @@ 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 #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) /* check for if dynamic memory was needed, then free */ if (tmpF) { diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 95a81843d93..e6699e30960 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -1186,6 +1186,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 */ diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index cc25c207b81..2796d65955d 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1646,12 +1646,10 @@ static void wc_Sha3Free(wc_Sha3* sha3) 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 +1679,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). */ + ForceZero(sha3, sizeof(*sha3)); } /* Reset a SHA-3/SHAKE context to its freshly initialized state, reusing its @@ -2378,6 +2380,9 @@ int wc_Shake128_Absorb(wc_Shake* shake, const byte* data, word32 len) byte hash[1]; ret = Sha3Final(shake, 0x1f, hash, WC_SHA3_128_COUNT, 0); } + /* Sha3Final does not clear t; the absorbed seed would stay for the + * squeeze lifetime (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(shake->t, sizeof(shake->t)); /* No partial data. */ shake->i = 0; @@ -2731,6 +2736,9 @@ int wc_Shake256_Absorb(wc_Shake* shake, const byte* data, word32 len) byte hash[1]; ret = Sha3Final(shake, 0x1f, hash, WC_SHA3_256_COUNT, 0); } + /* Sha3Final does not clear t; the absorbed seed would stay for the + * squeeze lifetime (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(shake->t, sizeof(shake->t)); /* No partial data. */ shake->i = 0; diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index 841ebc35f51..a5b1c3c0637 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -507,6 +507,8 @@ static WC_INLINE int wc_lms_sha256_192_hash_block(wc_Sha256* sha256, 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)); return ret; } @@ -570,6 +572,8 @@ 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)); return ret; } @@ -614,6 +618,8 @@ 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)); return ret; #else @@ -624,6 +630,8 @@ static WC_INLINE int wc_lms_hash_sha256_192_final(wc_Sha256* sha256, byte* hash) 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)); return ret; #endif @@ -4459,6 +4467,8 @@ static int wc_hss_derive_seed_i(LmsState* state, const byte* id, /* Copy part of hash as new I into private key. */ XMEMCPY(seed_i, tmp, LMS_I_LEN); } + /* buffer held the parent SEED (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(buffer, sizeof(buffer)); return ret; } diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index afc097747c0..608c434e707 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -1045,6 +1045,18 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, key->flags |= MLKEM_FLAG_A_SET; #endif } + else if (key != NULL) { + /* Keygen failed after s and z were written; key is NULL on the + * argument-check path (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)); diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 03f62733d35..9892d1fe598 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -6660,6 +6660,8 @@ 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); WC_FREE_VAR_EX(state, heap, DYNAMIC_TYPE_SLHDSA); } @@ -7037,6 +7039,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; } @@ -7109,6 +7115,8 @@ 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)); return ret; } @@ -7242,6 +7250,10 @@ 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); + } WC_FREE_VAR_EX(nodes, key->heap, DYNAMIC_TYPE_SLHDSA); return ret; } @@ -7476,6 +7488,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 { @@ -7545,6 +7561,11 @@ 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); + } WC_FREE_VAR_EX(nodes, key->heap, DYNAMIC_TYPE_SLHDSA); } @@ -7600,6 +7621,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); + } } else { byte nodes[2 * SLHDSA_MAX_N]; @@ -7620,6 +7645,8 @@ 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)); } return ret; diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c index c69150ebb10..0ced5dc52f6 100644 --- a/wolfcrypt/src/wc_xmss.c +++ b/wolfcrypt/src/wc_xmss.c @@ -1347,6 +1347,7 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) if (ret != 0) { WOLFSSL_MSG("error: XMSS keygen failed"); key->state = WC_XMSS_STATE_BAD; + ForceZero(key->sk, key->sk_len); } /* Free state after use. */ wc_xmss_state_free(state); @@ -1684,15 +1685,19 @@ int wc_XmssKey_SigsLeft(XmssKey* key) WOLFSSL_MSG("error: XMSS secret key not held in software"); ret = 0; } - /* Read the current secret key from NV storage.*/ - else if (key->read_private_key(key->sk, key->sk_len, key->context) != - WC_XMSS_RC_READ_TO_MEMORY) { - WOLFSSL_MSG("error: XMSS read_private_key failed"); - ret = 0; - } else { - /* Ask implementation to check index in private key. */ - ret = wc_xmss_sigsleft(key->params, key->sk); + /* Read the current secret key from NV storage.*/ + if (key->read_private_key(key->sk, key->sk_len, key->context) != + WC_XMSS_RC_READ_TO_MEMORY) { + WOLFSSL_MSG("error: XMSS read_private_key failed"); + ret = 0; + } + else { + /* Ask implementation to check index in private key. */ + ret = wc_xmss_sigsleft(key->params, key->sk); + } + /* Only the index was needed (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(key->sk, key->sk_len); } #endif diff --git a/wolfcrypt/src/wc_xmss_impl.c b/wolfcrypt/src/wc_xmss_impl.c index 9aadbcc6313..1423183c578 100644 --- a/wolfcrypt/src/wc_xmss_impl.c +++ b/wolfcrypt/src/wc_xmss_impl.c @@ -680,6 +680,9 @@ 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)); } #endif else From 40b498105e8114f0f8c4d1f4e4321407757945bd Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 9 Sep 2026 16:18:27 -0600 Subject: [PATCH 03/74] Zeroize remaining temporaries and failure paths Hash Free callback returns, GetHash copies, RSA/ECC/DH/EdDSA/PQC error paths, AES KW/XTS/GCM scratch. --- wolfcrypt/src/aes.c | 17 ++++++++++-- wolfcrypt/src/cmac.c | 2 ++ wolfcrypt/src/curve25519.c | 13 +++++++++- wolfcrypt/src/dh.c | 20 +++++++++++++- wolfcrypt/src/ecc.c | 22 +++++++++++----- wolfcrypt/src/ed25519.c | 8 ++++++ wolfcrypt/src/ed448.c | 8 ++++++ wolfcrypt/src/hmac.c | 2 +- wolfcrypt/src/kdf.c | 3 +++ wolfcrypt/src/pwdbased.c | 3 +++ wolfcrypt/src/random.c | 1 + wolfcrypt/src/rsa.c | 52 ++++++++++++++++++++++++++++--------- wolfcrypt/src/sha.c | 5 +++- wolfcrypt/src/sha256.c | 21 ++++++++++++--- wolfcrypt/src/sha3.c | 7 ++++- wolfcrypt/src/sha512.c | 30 +++++++++++++++++---- wolfcrypt/src/wc_mldsa.c | 14 ++++++++++ wolfcrypt/src/wc_mlkem.c | 6 ++--- wolfcrypt/src/wc_slhdsa.c | 14 +++++++++- wolfcrypt/src/wolfentropy.c | 4 +++ 20 files changed, 213 insertions(+), 39 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index e676efc9b09..e89b83ff391 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -9587,6 +9587,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 @@ -9687,6 +9688,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 @@ -10070,6 +10072,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 @@ -10572,6 +10575,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 @@ -10748,6 +10752,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, ByteReverseWords64(x, x, WC_AES_BLOCK_SIZE); #endif XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); } #endif /* !FREESCALE_LTC_AES_GCM */ @@ -11055,6 +11060,7 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, ByteReverseWords(x, x, WC_AES_BLOCK_SIZE); #endif XMEMCPY(s, x, sSz); + ForceZero(x, sizeof(x)); } #ifdef WOLFSSL_AESGCM_STREAM @@ -13245,10 +13251,13 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( IncrementGcmCounter(AES_COUNTER(aes)); /* Encrypt counter into a buffer. */ ret = wc_AesEncrypt(aes, AES_COUNTER(aes), scratch); - if (ret != 0) + if (ret != 0) { + ForceZero(scratch, sizeof(scratch)); return ret; + } /* XOR plain text into encrypted counter into cipher text buffer. */ xorbufout(out, scratch, in, WC_AES_BLOCK_SIZE); + ForceZero(scratch, sizeof(scratch)); /* Data complete. */ in += WC_AES_BLOCK_SIZE; out += WC_AES_BLOCK_SIZE; @@ -16889,8 +16898,10 @@ void wc_AesFree(Aes* aes) aes->keyInstalled = 0; /* If callback wants standard free, it can set devId to INVALID_DEVID. * Otherwise assume the callback handled cleanup. */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(aes, sizeof(Aes)); return; + } /* fall-through when unavailable */ } #endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_FREE */ @@ -19309,6 +19320,7 @@ static int AesXtsEncrypt_sw(XtsAes* xaes, byte* out, const byte* in, word32 sz, if (ret == 0) ret = AesXtsEncryptUpdate_sw(xaes, out, in, sz, tweak_block); WC_AES_ARM64_SVR_END(); + ForceZero(tweak_block, sizeof(tweak_block)); return ret; } @@ -19917,6 +19929,7 @@ static int AesXtsDecrypt_sw(XtsAes* xaes, byte* out, const byte* in, word32 sz, if (ret == 0) ret = AesXtsDecryptUpdate_sw(xaes, out, in, sz, tweak_block); WC_AES_ARM64_SVR_END(); + ForceZero(tweak_block, sizeof(tweak_block)); return ret; } diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 1462ac71f16..c3ac738eb52 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -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)); @@ -750,6 +751,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/curve25519.c b/wolfcrypt/src/curve25519.c index 9ba273338b1..bbbb57bdf19 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -704,6 +704,12 @@ static int wc_curve25519_make_key_nb(WC_RNG* rng, int keysize, if (ret == 0) { key->pubSet = 1; } + else if (ret != FP_WOULDBLOCK) { + /* Public half failed: drop the scalar too + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(key->k, sizeof(key->k)); + key->privSet = 0; + } } return ret; @@ -717,7 +723,7 @@ int wc_curve25519_set_nonblock(curve25519_key* key, x25519_nb_ctx_t* ctx) /* If a different context is already set, clear it before replacing. * The caller is responsible for freeing any heap-allocated context. */ if (key->nb_ctx != NULL && key->nb_ctx != ctx) { - XMEMSET(key->nb_ctx, 0, sizeof(x25519_nb_ctx_t)); + ForceZero(key->nb_ctx, sizeof(x25519_nb_ctx_t)); } if (ctx != NULL) { XMEMSET(ctx, 0, sizeof(x25519_nb_ctx_t)); @@ -1436,6 +1442,11 @@ void wc_curve25519_free(curve25519_key* key) #ifdef WOLFSSL_SE050 se050_curve25519_free_key(key); #endif +#ifdef WC_X25519_NONBLOCK + if (key->nb_ctx != NULL) { + ForceZero(key->nb_ctx, sizeof(*key->nb_ctx)); + } +#endif ForceZero(key, sizeof(*key)); diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index d0099b82ead..d541dc235e5 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -1029,7 +1029,10 @@ int wc_DhSetNonBlock(DhKey* key, DhNb* nb) return BAD_FUNC_ARG; if (nb != NULL) { - XMEMSET(nb, 0, sizeof(DhNb)); + ForceZero(nb, sizeof(DhNb)); + } + if ((key->nb != NULL) && (key->nb != nb)) { + ForceZero(key->nb, sizeof(DhNb)); } /* Pass NULL to disable non-blocking mode. */ @@ -1057,6 +1060,11 @@ int wc_FreeDhKey(DhKey* key) #ifdef WOLFSSL_KCAPI_DH KcapiDh_Free(key); #endif + #ifdef WC_DH_NONBLOCK + if (key->nb != NULL) { + ForceZero(key->nb, sizeof(DhNb)); + } + #endif #ifdef WOLFSSL_CHECK_MEM_ZERO /* Deregister any mem-zero entries covering this key (e.g. key->priv * registered by wc_DhImportKeyPair) now that its fields are zeroed. @@ -1503,6 +1511,7 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng, byte* priv, word32* privSz, byte* pub, word32* pubSz) { int ret; + int privWritten; if (key == NULL || rng == NULL || priv == NULL || privSz == NULL || pub == NULL || pubSz == NULL) { @@ -1510,6 +1519,8 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng, } ret = GeneratePrivateDh(key, rng, priv, privSz); + /* From here *privSz is the length actually written. */ + privWritten = (ret == 0); if (ret == 0) ret = GeneratePublicDh(key, priv, *privSz, pub, pubSz); @@ -1519,6 +1530,11 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng, if (ret == 0) ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, *privSz); #endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */ + if (privWritten && (ret != 0)) { + /* A failed pair is not handed back (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(priv, *privSz); + *privSz = 0; + } return ret; } @@ -2620,6 +2636,8 @@ int wc_DhImportKeyPair(DhKey* key, const byte* priv, word32 privSz, if (priv[0] == 0) { privSz--; priv++; } + /* Never overwrite one SSP with another (ISO/IEC 19790:2012 [09.31]). */ + mp_forcezero(&key->priv); if (mp_init(&key->priv) != MP_OKAY) havePriv = 0; } diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 3adf96b7376..57f99da49a5 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -3453,6 +3453,8 @@ static int ecc_mulmod(const mp_int* k, ecc_point* P, ecc_point* Q, err = mp_cond_swap_ct_ex(R[0]->z, R[1]->z, (int)modulus->used, (int)b, tmp); } + /* tmp holds a scalar bit (ISO/IEC 19790:2012 7.9.7). */ + mp_forcezero(tmp); #endif } @@ -7198,6 +7200,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,6 +7219,7 @@ 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 { @@ -7466,12 +7470,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 +7484,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) { @@ -7896,6 +7897,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 +8299,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) { diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index a4f5da41869..b162ecc51a9 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -1840,10 +1840,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 b64ac458033..2c2b02a0396 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -1731,10 +1731,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..e3df88e8505 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -1788,7 +1788,7 @@ void wc_HmacFree(Hmac* hmac) byte finalHash[WC_HMAC_BLOCK_SIZE]; ret = wc_CryptoCb_Hmac(hmac, hmac->macType, NULL, 0, finalHash); (void)ret; /* must ignore return code here */ - (void)finalHash; + ForceZero(finalHash, sizeof(finalHash)); } #endif diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 6574c2ce63d..1af792e8847 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -282,6 +282,7 @@ int wc_PRF_TLSv1(byte* digest, word32 digLen, const byte* secret, } } + ForceZero(sha_result, MAX_PRF_DIG); #if defined(WOLFSSL_CHECK_MEM_ZERO) wc_MemZero_Check(sha_result, MAX_PRF_DIG); #endif @@ -1590,6 +1591,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; } @@ -1763,6 +1765,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/pwdbased.c b/wolfcrypt/src/pwdbased.c index 770b2e4c1b7..cd6ddcb5bc6 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -611,6 +611,7 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, byte tmp[WC_MAX_BLOCK_SIZE + 1]; ret = mp_to_unsigned_bin(res, tmp); XMEMCPY(I + i, tmp + 1, v); + ForceZero(tmp, sizeof(tmp)); } else if (outSz < (int)v) { XMEMSET(I + i, 0, v - (word32)outSz); @@ -918,6 +919,7 @@ static void scryptSalsa(word32* out, word32* in) for (i = 0; i < 16; i++) out[i] = ByteReverseWord32(ByteReverseWord32(in[i]) + x[i]); #endif + ForceZero(x, sizeof(x)); } /* Mix a block using Salsa20/8. @@ -971,6 +973,7 @@ static void scryptBlockMix(byte* b, byte* y, int r) } #endif } + ForceZero(x, sizeof(x)); } /* Random oracles mix. diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index ccd0af53e3f..8183bbe45ef 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3031,6 +3031,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 diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 2e54ddafd35..0bc6834ade4 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; @@ -604,8 +618,13 @@ int wc_FreeRsaKey(RsaKey* key) WC_PK_TYPE_RSA, 0, key); /* If callback wants standard free, it returns CRYPTOCB_UNAVAILABLE. * Otherwise assume the callback handled cleanup. */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + wc_RsaCleanup(key); + #ifndef WOLFSSL_RSA_PUBLIC_ONLY + RsaForceZeroPriv(key); + #endif return ret; + } /* fall-through to software cleanup */ ret = 0; } @@ -622,17 +641,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 */ @@ -1448,12 +1458,14 @@ static int RsaPad_OAEP(const byte* input, word32 inputLen, byte* pkcsBlock, } #else if (pkcsBlockLen - hLen - 1 > sizeof(dbMask)) { + ForceZero(seed, hLen); return MEMORY_E; } #endif XMEMSET(dbMask, 0, pkcsBlockLen - hLen - 1); /* help static analyzer */ ret = RsaMGF(mgf, seed, hLen, dbMask, pkcsBlockLen - hLen - 1, heap); if (ret != 0) { + ForceZero(dbMask, pkcsBlockLen - hLen - 1); WC_FREE_VAR_EX(dbMask, heap, DYNAMIC_TYPE_RSA); WC_FREE_VAR_EX(lHash, heap, DYNAMIC_TYPE_RSA_BUFFER); ForceZero(seed, hLen); @@ -1463,6 +1475,8 @@ static int RsaPad_OAEP(const byte* input, word32 inputLen, byte* pkcsBlock, 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); WC_FREE_VAR_EX(dbMask, heap, DYNAMIC_TYPE_RSA); /* create maskedSeed from seedMask */ @@ -3873,6 +3887,10 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, hash, mgf, label, labelSz, saltLen, RsaModulusBits(key), key->heap); if (ret < 0) { + if (rsa_type == RSA_PUBLIC_ENCRYPT) { + /* Padding failed with the secret already copied in. */ + ForceZero(out, (word32)sz); + } break; } @@ -3889,6 +3907,16 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, key->state = RSA_STATE_ENCRYPT_RES; } if (ret < 0) { + /* out holds the padded secret (ISO/IEC 19790:2012 7.9.7); a + * pending or would-block result still needs it. */ + if ((rsa_type == RSA_PUBLIC_ENCRYPT) && + (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) + #ifdef WC_RSA_NONBLOCK + && (ret != FP_WOULDBLOCK) + #endif + ) { + ForceZero(out, (word32)sz); + } break; } diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index e6699e30960..8d38987a866 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -854,6 +854,7 @@ 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)); #else XMEMCPY(hash, sha->digest, WC_SHA_DIGEST_SIZE); #endif @@ -1139,8 +1140,10 @@ void wc_ShaFree(wc_Sha* sha) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha, sizeof(*sha)); return; + } /* fall-through when unavailable */ } diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 2642838d279..d85cac34847 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -2395,6 +2395,7 @@ 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)); #else XMEMCPY(hash, sha256->digest, WC_SHA256_DIGEST_SIZE); #endif @@ -2473,13 +2474,16 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #endif { byte buffer[WC_SHA256_BLOCK_SIZE]; + int tret; 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)); + return tret; } #else return Transform_Sha256(sha256, data); @@ -3124,8 +3128,10 @@ int wc_Sha224Reset(wc_Sha224* sha224) { /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha224, sizeof(*sha224)); return; + } /* fall-through when unavailable */ } @@ -3201,8 +3207,10 @@ void wc_Sha256Free(wc_Sha256* sha256) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha256, sizeof(*sha256)); return; + } /* fall-through when unavailable */ } @@ -3413,6 +3421,8 @@ int wc_Sha224Reset(wc_Sha224* sha224) { wc_Sha224Free(tmpSha224); } + ForceZero(tmpSha224, sizeof(*tmpSha224)); + WC_FREE_VAR_EX(tmpSha224, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -3562,6 +3572,9 @@ int wc_Sha256GetHash(wc_Sha256* sha256, byte* hash) } + ForceZero(tmpSha256, sizeof(*tmpSha256)); + + WC_FREE_VAR_EX(tmpSha256, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 2796d65955d..8a7f1aa7701 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1659,8 +1659,11 @@ static void wc_Sha3Free(wc_Sha3* sha3) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha3->s, sizeof(sha3->s)); + ForceZero(sha3->t, sizeof(sha3->t)); return; + } /* fall-through when unavailable */ } @@ -1812,6 +1815,8 @@ static int wc_Sha3GetHash(wc_Sha3* sha3, byte* hash, word32 p, word32 len) ret = wc_Sha3Final(tmpSha3, hash, p, len); } + ForceZero(tmpSha3, sizeof(*tmpSha3)); + WC_FREE_VAR_EX(tmpSha3, sha3->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; } diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index f846016b9ac..2a42dfe1ba8 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -507,8 +507,10 @@ void wc_Sha512Free(wc_Sha512* sha512) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha512, sizeof(*sha512)); return; + } /* fall-through when unavailable */ } @@ -536,6 +538,8 @@ int wc_Sha512GetHash(wc_Sha512* sha512, byte* hash) wc_Sha512Free(tmpSha512); } + ForceZero(tmpSha512, sizeof(*tmpSha512)); + WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -649,6 +653,8 @@ int wc_Sha512_224GetHash(wc_Sha512* sha512, byte* hash) wc_Sha512_224Free(tmpSha512); } + ForceZero(tmpSha512, sizeof(*tmpSha512)); + WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -729,6 +735,8 @@ int wc_Sha512_256GetHash(wc_Sha512* sha512, byte* hash) wc_Sha512_256Free(tmpSha512); } + ForceZero(tmpSha512, sizeof(*tmpSha512)); + WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -829,8 +837,10 @@ void wc_Sha384Free(wc_Sha384* sha384) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha384, sizeof(*sha384)); return; + } /* fall-through when unavailable */ } @@ -858,6 +868,8 @@ int wc_Sha384GetHash(wc_Sha384* sha384, byte* hash) wc_Sha384Free(tmpSha384); } + ForceZero(tmpSha384, sizeof(*tmpSha384)); + WC_FREE_VAR_EX(tmpSha384, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -2711,8 +2723,10 @@ void wc_Sha512Free(wc_Sha512* sha512) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha512, sizeof(*sha512)); return; + } /* fall-through when unavailable */ } @@ -2821,8 +2835,8 @@ 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_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE) XFREE(buffer, sha->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; @@ -3208,8 +3222,10 @@ void wc_Sha384Free(wc_Sha384* sha384) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ForceZero(sha384, sizeof(*sha384)); return; + } /* fall-through when unavailable */ } @@ -3300,6 +3316,8 @@ static int Sha512_Family_GetHash(wc_Sha512* sha512, byte* hash, wc_Sha512Free(tmpSha512); } + ForceZero(tmpSha512, sizeof(*tmpSha512)); + WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -3750,6 +3768,8 @@ int wc_Sha384GetHash(wc_Sha384* sha384, byte* hash) wc_Sha384Free(tmpSha384); } + ForceZero(tmpSha384, sizeof(*tmpSha384)); + WC_FREE_VAR_EX(tmpSha384, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index bccae07132b..dcdd77a1e26 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -10494,6 +10494,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; @@ -11168,6 +11172,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)) { @@ -14396,6 +14404,12 @@ 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) + if (key->prvKeySet) { + 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; diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 608c434e707..1aa4973de5d 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -1045,9 +1045,9 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, key->flags |= MLKEM_FLAG_A_SET; #endif } - else if (key != NULL) { - /* Keygen failed after s and z were written; key is NULL on the - * argument-check path (ISO/IEC 19790:2012 7.9.7). */ + 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); diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 9892d1fe598..18b6f4088c4 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -9195,11 +9195,17 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, { /* The seeds are now staged in the key as the contiguous * SK.seed || SK.prf || PK.seed the callback expects. */ + key->flags &= (word16)~WC_SLHDSA_FLAG_BOTH_KEYS; ret = wc_CryptoCb_MakePqcSignatureKeyEx(NULL, WC_PQC_SIG_TYPE_SLHDSA, (int)key->params->param, key->sk, 3U * key->params->n, key); - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) { + /* Device owns the key (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(key->sk, 2U * key->params->n); + } return ret; + } /* fall-through when unavailable */ ret = 0; } @@ -9307,6 +9313,7 @@ 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; /* Steps 1, 7-13: Set address based on message digest. */ slhdsakey_set_ha_from_md(key, md, adrs, t, &l); @@ -9325,6 +9332,11 @@ 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, key->params->k * (1 + key->params->a) * n); + } return ret; } diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c index 3932d652247..f05a362bdc0 100644 --- a/wolfcrypt/src/wolfentropy.c +++ b/wolfcrypt/src/wolfentropy.c @@ -836,6 +836,7 @@ static int Entropy_Condition(byte* output, word32 len, byte* noise, if (ret == 0) { XMEMCPY(output, hash, len); } + ForceZero(hash, sizeof(hash)); } } @@ -945,6 +946,9 @@ int wc_Entropy_Get(int bits, unsigned char* entropy, word32 len) #endif if (ret != WC_NO_ERR_TRACE(BAD_MUTEX_E)) { + /* Raw samples were conditioned into the seed + * (ISO/IEC 19790:2012 7.9.7). */ + ForceZero(noise, sizeof(noise)); /* Unlock mutex now we are done. */ wc_UnLockMutex(&entropy_mutex); } From daa8e9bab2dc6464d3a97086c55d529104eed20f Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 10 Sep 2026 08:09:08 -0600 Subject: [PATCH 04/74] SHA Free: release msg and W before the callback-path wipe --- wolfcrypt/src/sha.c | 8 ++++++++ wolfcrypt/src/sha256.c | 32 ++++++++++++++++++++++++++++++++ wolfcrypt/src/sha512.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 8d38987a866..a36bfffe4d4 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -1141,6 +1141,14 @@ void wc_ShaFree(wc_Sha* sha) /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* Release heap state first; the wipe drops its pointers. */ + #ifdef WOLFSSL_HASH_KEEP + if (sha->msg != NULL) { + ForceZero(sha->msg, sha->len); + XFREE(sha->msg, sha->heap, DYNAMIC_TYPE_TMP_BUFFER); + sha->msg = NULL; + } + #endif ForceZero(sha, sizeof(*sha)); return; } diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index d85cac34847..ae4c1322acd 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -3129,6 +3129,22 @@ int wc_Sha224Reset(wc_Sha224* sha224) { /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* Release heap state first; the wipe drops its pointers. */ + #ifdef WOLFSSL_SMALL_STACK_CACHE + if (sha224->W != NULL) { + ForceZero(sha224->W, + sizeof(word32) * WC_SHA224_BLOCK_SIZE); + XFREE(sha224->W, sha224->heap, DYNAMIC_TYPE_DIGEST); + sha224->W = NULL; + } + #endif + #ifdef WOLFSSL_HASH_KEEP + if (sha224->msg != NULL) { + ForceZero(sha224->msg, sha224->len); + XFREE(sha224->msg, sha224->heap, DYNAMIC_TYPE_TMP_BUFFER); + sha224->msg = NULL; + } + #endif ForceZero(sha224, sizeof(*sha224)); return; } @@ -3208,6 +3224,22 @@ void wc_Sha256Free(wc_Sha256* sha256) /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* Release heap state first; the wipe drops its pointers. */ + #ifdef WOLFSSL_SMALL_STACK_CACHE + if (sha256->W != NULL) { + ForceZero(sha256->W, + sizeof(word32) * WC_SHA256_BLOCK_SIZE); + XFREE(sha256->W, sha256->heap, DYNAMIC_TYPE_DIGEST); + sha256->W = NULL; + } + #endif + #ifdef WOLFSSL_HASH_KEEP + if (sha256->msg != NULL) { + ForceZero(sha256->msg, sha256->len); + XFREE(sha256->msg, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); + sha256->msg = NULL; + } + #endif ForceZero(sha256, sizeof(*sha256)); return; } diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 2a42dfe1ba8..05982f63d63 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2724,6 +2724,22 @@ void wc_Sha512Free(wc_Sha512* sha512) /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* Release heap state first; the wipe drops its pointers. */ + #ifdef WOLFSSL_SMALL_STACK_CACHE + if (sha512->W != NULL) { + ForceZero(sha512->W, + (sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE); + XFREE(sha512->W, sha512->heap, DYNAMIC_TYPE_DIGEST); + sha512->W = NULL; + } + #endif + #ifdef WOLFSSL_HASH_KEEP + if (sha512->msg != NULL) { + ForceZero(sha512->msg, sha512->len); + XFREE(sha512->msg, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); + sha512->msg = NULL; + } + #endif ForceZero(sha512, sizeof(*sha512)); return; } @@ -3223,6 +3239,22 @@ void wc_Sha384Free(wc_Sha384* sha384) /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* Release heap state first; the wipe drops its pointers. */ + #ifdef WOLFSSL_SMALL_STACK_CACHE + if (sha384->W != NULL) { + ForceZero(sha384->W, + (sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE); + XFREE(sha384->W, sha384->heap, DYNAMIC_TYPE_DIGEST); + sha384->W = NULL; + } + #endif + #ifdef WOLFSSL_HASH_KEEP + if (sha384->msg != NULL) { + ForceZero(sha384->msg, sha384->len); + XFREE(sha384->msg, sha384->heap, DYNAMIC_TYPE_TMP_BUFFER); + sha384->msg = NULL; + } + #endif ForceZero(sha384, sizeof(*sha384)); return; } From 0be88ba0f30094fab77dc138048d8bbcbca2907b Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 10 Sep 2026 16:11:41 -0600 Subject: [PATCH 05/74] Address review: keep destructors off caller-owned contexts, fix entropy include wc_curve25519_free and wc_FreeDhKey no longer wipe the non-blocking context (TLS frees it first); wolfentropy.c pulls in misc.c; GCM scratch wiped once per call; KW wipe only after out is written; wc_AesFree releases streamData before the wipe; test_digest.h freed src instead of dst. --- tests/api/test_digest.h | 4 ++-- wolfcrypt/src/aes.c | 17 +++++++++++++++-- wolfcrypt/src/curve25519.c | 6 ------ wolfcrypt/src/dh.c | 5 ----- wolfcrypt/src/sha3.c | 3 +-- wolfcrypt/src/wc_slhdsa.c | 2 +- wolfcrypt/src/wolfentropy.c | 6 ++++++ 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/tests/api/test_digest.h b/tests/api/test_digest.h index 78ce499dd80..db458652422 100644 --- a/tests/api/test_digest.h +++ b/tests/api/test_digest.h @@ -574,7 +574,7 @@ do { \ ExpectIntEQ(wc_##name##_Final(&dst, hashDst, WC_##upper##_COUNT * 8), 0); \ ExpectBufEQ(hashSrc, emptyHash, WC_##upper##_COUNT * 8); \ ExpectBufEQ(hashDst, emptyHash, WC_##upper##_COUNT * 8); \ - wc_##name##_Free(&src); \ + wc_##name##_Free(&dst); \ \ /* Test buffered data is copied. */ \ ExpectIntEQ(wc_##name##_Update(&src, (byte*)"abc", 3), 0); \ @@ -583,7 +583,7 @@ do { \ ExpectIntEQ(wc_##name##_Final(&dst, hashDst, WC_##upper##_COUNT * 8), 0); \ ExpectBufEQ(hashSrc, abcHash, WC_##upper##_COUNT * 8); \ ExpectBufEQ(hashDst, abcHash, WC_##upper##_COUNT * 8); \ - wc_##name##_Free(&src); \ + wc_##name##_Free(&dst); \ \ /* Test count of length is copied. */ \ ExpectIntEQ(wc_##name##_Update(&src, data, sizeof(data)), 0); \ diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index e89b83ff391..5ab9ae1efd0 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -13245,9 +13245,9 @@ 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); @@ -13257,11 +13257,11 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( } /* XOR plain text into encrypted counter into cipher text buffer. */ xorbufout(out, scratch, in, WC_AES_BLOCK_SIZE); - ForceZero(scratch, sizeof(scratch)); /* Data complete. */ in += WC_AES_BLOCK_SIZE; out += WC_AES_BLOCK_SIZE; } + ForceZero(scratch, sizeof(scratch)); } if (partial != 0) { @@ -16899,6 +16899,15 @@ void wc_AesFree(Aes* aes) /* If callback wants standard free, it can set devId to INVALID_DEVID. * Otherwise assume the callback handled cleanup. */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* Release heap state first; the wipe drops its pointers. */ + #if defined(WOLFSSL_AESGCM_STREAM) && defined(WOLFSSL_SMALL_STACK) && \ + !defined(WOLFSSL_AESNI) + if (aes->streamData != NULL) { + ForceZero(aes->streamData, aes->streamData_sz); + XFREE(aes->streamData, aes->heap, DYNAMIC_TYPE_AES); + aes->streamData = NULL; + } + #endif ForceZero(aes, sizeof(Aes)); return; } @@ -18429,6 +18438,10 @@ static int AesKeyUnWrapRaw(Aes* aes, const byte* in, word32 inSz, byte* out, /* 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)); diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index bbbb57bdf19..301836d9b07 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -1442,12 +1442,6 @@ void wc_curve25519_free(curve25519_key* key) #ifdef WOLFSSL_SE050 se050_curve25519_free_key(key); #endif -#ifdef WC_X25519_NONBLOCK - if (key->nb_ctx != NULL) { - ForceZero(key->nb_ctx, sizeof(*key->nb_ctx)); - } -#endif - ForceZero(key, sizeof(*key)); #ifdef WOLFSSL_CHECK_MEM_ZERO diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index d541dc235e5..6c49fc6b4d6 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -1060,11 +1060,6 @@ int wc_FreeDhKey(DhKey* key) #ifdef WOLFSSL_KCAPI_DH KcapiDh_Free(key); #endif - #ifdef WC_DH_NONBLOCK - if (key->nb != NULL) { - ForceZero(key->nb, sizeof(DhNb)); - } - #endif #ifdef WOLFSSL_CHECK_MEM_ZERO /* Deregister any mem-zero entries covering this key (e.g. key->priv * registered by wc_DhImportKeyPair) now that its fields are zeroed. diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 8a7f1aa7701..ea48acb7c27 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1660,8 +1660,7 @@ static void wc_Sha3Free(wc_Sha3* sha3) /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - ForceZero(sha3->s, sizeof(sha3->s)); - ForceZero(sha3->t, sizeof(sha3->t)); + ForceZero(sha3, sizeof(*sha3)); return; } /* fall-through when unavailable */ diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 18b6f4088c4..084371b2372 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -9195,7 +9195,7 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, { /* The seeds are now staged in the key as the contiguous * SK.seed || SK.prf || PK.seed the callback expects. */ - key->flags &= (word16)~WC_SLHDSA_FLAG_BOTH_KEYS; + key->flags &= ~((int)WC_SLHDSA_FLAG_BOTH_KEYS); ret = wc_CryptoCb_MakePqcSignatureKeyEx(NULL, WC_PQC_SIG_TYPE_SLHDSA, (int)key->params->param, key->sk, 3U * key->params->n, key); diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c index f05a362bdc0..6f1c1590d30 100644 --- a/wolfcrypt/src/wolfentropy.c +++ b/wolfcrypt/src/wolfentropy.c @@ -46,6 +46,12 @@ data, use this implementation to seed and re-seed the DRBG. #endif #include +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif #if defined(__APPLE__) || defined(__MACH__) #include #endif From ff7a795968bce5309144f302409776e6bcfdbd71 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 10 Sep 2026 17:04:03 -0600 Subject: [PATCH 06/74] Use ForceZero where XMEMSET wiped sensitive data XMEMSET is a dead store the compiler may drop. Covers HMAC and SHA-2 Copy failure paths, X25519/ECC nonblocking contexts, ECC keyRaw, and CCM plaintext on tag mismatch. --- wolfcrypt/src/curve25519.c | 2 +- wolfcrypt/src/ecc.c | 2 +- wolfcrypt/src/hmac.c | 2 +- wolfcrypt/src/sha256.c | 4 ++-- wolfcrypt/src/sha512.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 301836d9b07..3683d166d60 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -879,7 +879,7 @@ static int wc_curve25519_shared_secret_nb(curve25519_key* privKey, } if (ret != FP_WOULDBLOCK) { - XMEMSET(privKey->nb_ctx, 0, sizeof(x25519_nb_ctx_t)); + ForceZero(privKey->nb_ctx, sizeof(x25519_nb_ctx_t)); } return ret; diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 57f99da49a5..9d86e66a02b 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -17469,7 +17469,7 @@ int wc_ecc_set_nonblock(ecc_key *key, ecc_nb_ctx_t* ctx) /* If a different context is already set, clear it before replacing. * The caller is responsible for freeing any heap-allocated context. */ if (key->nb_ctx != NULL && key->nb_ctx != ctx) { - XMEMSET(key->nb_ctx, 0, sizeof(ecc_nb_ctx_t)); + ForceZero(key->nb_ctx, sizeof(ecc_nb_ctx_t)); } if (ctx != NULL) { XMEMSET(ctx, 0, sizeof(ecc_nb_ctx_t)); diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index e3df88e8505..dd839367698 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; } diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index ae4c1322acd..ad0753bb542 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -3491,7 +3491,7 @@ int wc_Sha224Reset(wc_Sha224* sha224) { dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_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 @@ -3648,7 +3648,7 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_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/sha512.c b/wolfcrypt/src/sha512.c index 05982f63d63..10626d70ebc 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -3395,7 +3395,7 @@ int wc_Sha512Copy(wc_Sha512* src, wc_Sha512* dst) dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + 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 @@ -3843,7 +3843,7 @@ int wc_Sha384Copy(wc_Sha384* src, wc_Sha384* dst) dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + 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 From b1e61ded4f38c983f6f2e2a8eb3eaab4e7efde67 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 10 Sep 2026 18:05:45 -0600 Subject: [PATCH 07/74] NoiseSrc: ForceZero the health-test state on free --- wolfcrypt/src/random.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 8183bbe45ef..7e6d6910abe 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -9488,7 +9488,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; From 126044a6f00b71fb7204ef72a5f08eb31b2071e6 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 15 Sep 2026 15:07:08 -0400 Subject: [PATCH 08/74] SHA-3 Free: wipe state only; RSA even-mod test expects the wiped buffer Whole-object wipe nulled the per-object block function under WC_C_DYNAMIC_FALLBACK, which SLH-DSA reuses after HASH_T_FREE. --- wolfcrypt/src/sha3.c | 18 ++++++++++++++++-- wolfcrypt/test/test.c | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index ea48acb7c27..3749d9f883d 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1640,6 +1640,20 @@ 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)); + sha3->i = 0; +#endif +} + static void wc_Sha3Free(wc_Sha3* sha3) { #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) @@ -1660,7 +1674,7 @@ static void wc_Sha3Free(wc_Sha3* sha3) /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - ForceZero(sha3, sizeof(*sha3)); + wc_Sha3Wipe(sha3); return; } /* fall-through when unavailable */ @@ -1684,7 +1698,7 @@ static void wc_Sha3Free(wc_Sha3* sha3) /* 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). */ - ForceZero(sha3, sizeof(*sha3)); + wc_Sha3Wipe(sha3); } /* Reset a SHA-3/SHAKE context to its freshly initialized state, reusing its diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 2ad6034e8a0..e3adce54fb1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -41267,6 +41267,14 @@ static wc_test_ret_t rsa_even_mod_test(WC_RNG* rng, RsaKey* key) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_even_mod); } + /* The failed encrypt wipes out; check that, then give the decrypt an + * in-range value so it still reaches the even modulus private op. */ + for (idx = 0; idx < outSz; idx++) { + if (out[idx] != 0) { + ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa_even_mod); + } + } + XMEMSET(out, 0x01, outSz); #endif /* WOLFSSL_RSA_VERIFY_ONLY */ #ifndef WOLFSSL_RSA_PUBLIC_ONLY From 46bded111b0f9165575ffdde41e9c504c8647621 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 22 Sep 2026 11:26:08 -0600 Subject: [PATCH 09/74] RSA even-mod test: only expect the wiped buffer from FIPS v7 or non-FIPS builds --- wolfcrypt/test/test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e3adce54fb1..7f2abe10595 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -41267,14 +41267,16 @@ static wc_test_ret_t rsa_even_mod_test(WC_RNG* rng, RsaKey* key) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_even_mod); } - /* The failed encrypt wipes out; check that, then give the decrypt an - * in-range value so it still reaches the even modulus private op. */ +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) + /* v7+ wipes out on the failed encrypt; check, then refill in range so + * the decrypt still reaches the even modulus private op. */ for (idx = 0; idx < outSz; idx++) { if (out[idx] != 0) { ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa_even_mod); } } XMEMSET(out, 0x01, outSz); +#endif #endif /* WOLFSSL_RSA_VERIFY_ONLY */ #ifndef WOLFSSL_RSA_PUBLIC_ONLY From ab2239221eebe9fc3ecef3d433b900b43af4ad75 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 1 Oct 2026 14:16:55 -0600 Subject: [PATCH 10/74] SHA-3: keep the C Keccak round scratch in the context The C block function wrote its round scratch to its own stack frame on every block, where nothing could wipe it. The context now owns the scratch and wc_Sha3Free wipes it with the state (ISO/IEC 19790:2012 7.9.7). About 1% on the C lane; the asm lanes are unchanged. --- wolfcrypt/src/sha3.c | 131 ++++++++++++++++++++++---------- wolfcrypt/src/wc_frodokem_mat.c | 4 +- wolfcrypt/src/wc_mldsa.c | 16 ++-- wolfcrypt/src/wc_mlkem_poly.c | 36 ++++----- wolfcrypt/src/wc_slhdsa.c | 4 +- wolfssl/wolfcrypt/sha3.h | 45 ++++++++++- 6 files changed, 162 insertions(+), 74 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 3749d9f883d..cde05b890fd 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -125,7 +125,7 @@ #define SHA3_BLOCK (sha3->sha3_block) #define SHA3_BLOCK_N (sha3->sha3_block_n) #else - void (*sha3_block)(word64 *s) = NULL; + WC_SHA3_BLOCK_FN sha3_block = NULL; void (*sha3_block_n)(word64 *s, const byte* data, word32 n, word64 c) = NULL; #define SHA3_BLOCK sha3_block @@ -134,6 +134,25 @@ #endif #ifdef USE_INTEL_SPEEDUP +#ifdef WC_SHA3_SCRATCH_W +/* The asm block functions keep no round scratch; give them the C signature + * so one function pointer type serves both. */ +static void sha3_block_avx2_scr(word64* s, void* scratch) +{ + (void)scratch; + sha3_block_avx2(s); +} +static void sha3_block_bmi2_scr(word64* s, void* scratch) +{ + (void)scratch; + sha3_block_bmi2(s); +} + #define SHA3_BLOCK_AVX2_FN sha3_block_avx2_scr + #define SHA3_BLOCK_BMI2_FN sha3_block_bmi2_scr +#else + #define SHA3_BLOCK_AVX2_FN sha3_block_avx2 + #define SHA3_BLOCK_BMI2_FN sha3_block_bmi2 +#endif /* Block-function selection when USE_INTEL_SPEEDUP: BMI2, then AVX2, then * the C block. BMI2 is preferred because its block uses general * registers only and so needs no vector-register save; AVX2 goes first @@ -151,7 +170,7 @@ #if defined(WOLFSSL_SHA3_NO_AVX2) #define SHA3_BLOCK_VREGS(f) 0 #else - #define SHA3_BLOCK_VREGS(f) ((f) == sha3_block_avx2) + #define SHA3_BLOCK_VREGS(f) ((f) == SHA3_BLOCK_AVX2_FN) #endif #endif @@ -183,6 +202,19 @@ #if !defined(WOLFSSL_ARMASM) && !defined(WOLFSSL_RISCV_ASM) && \ !defined(WOLFSSL_PPC64_ASM) && !defined(WOLFSSL_PPC32_ASM) +/* The state and the scratch never overlap; tell the compiler so the lanes can + * stay in registers across the round macros as they did with local arrays. */ +#if defined(__GNUC__) || defined(__clang__) + #define WC_SHA3_RESTRICT __restrict__ +#elif defined(_MSC_VER) + #define WC_SHA3_RESTRICT __restrict +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define WC_SHA3_RESTRICT restrict +#else + #define WC_SHA3_RESTRICT +#endif + + #ifdef WOLFSSL_SHA3_SMALL /* Rotate a 64-bit value left. * @@ -348,11 +380,19 @@ while (0) * * s The state. */ +#ifdef WC_SHA3_SCRATCH_W +void BlockSha3(word64* WC_SHA3_RESTRICT s, void* scratch) +{ + byte i, x, y; + word64 t0, t1; + word64* WC_SHA3_RESTRICT b = (word64*)scratch; +#else void BlockSha3(word64* s) { byte i, x, y; word64 t0, t1; word64 b[5]; +#endif for (i = 0; i < 24; i++) { @@ -745,18 +785,30 @@ while (0) WC_SHA3_CHI(DL, DH, 20); \ } while (0) -void BlockSha3(word64* s) +#ifdef WC_SHA3_SCRATCH_W +void BlockSha3(word64* WC_SHA3_RESTRICT s, void* scratch) { /* Process the 25 little-endian lanes as 32-bit halves to avoid 64-bit * helper calls. XMEMCPY in/out (aliasing s through word32* is strict- * aliasing UB); st[2k] is lane k's low half, st[2k+1] the high half. * Round constants are split with shifts for the same reason. */ + word32* WC_SHA3_RESTRICT st = (word32*)scratch; + word32* WC_SHA3_RESTRICT sl = st + 50; + word32* WC_SHA3_RESTRICT sh = st + 75; + word32* WC_SHA3_RESTRICT nl = st + 100; + word32* WC_SHA3_RESTRICT nh = st + 125; + word32* WC_SHA3_RESTRICT bl = st + 150; + word32* WC_SHA3_RESTRICT bh = st + 155; +#else +void BlockSha3(word64* s) +{ word32 st[50]; word32 sl[25], sh[25], nl[25], nh[25], bl[5], bh[5]; +#endif word32 i, k; word64 rc; - XMEMCPY(st, s, sizeof(st)); + XMEMCPY(st, s, 25 * sizeof(word64)); for (k = 0; k < 25; k++) { sl[k] = st[2 * k]; sh[k] = st[2 * k + 1]; @@ -775,7 +827,7 @@ void BlockSha3(word64* s) st[2 * k] = sl[k]; st[2 * k + 1] = sh[k]; } - XMEMCPY(s, st, sizeof(st)); + XMEMCPY(s, st, 25 * sizeof(word64)); } #undef WC_SHA3_RL @@ -785,10 +837,17 @@ void BlockSha3(word64* s) #else /* !WC_SHA3_SPLIT64 */ +#ifdef WC_SHA3_SCRATCH_W +void BlockSha3(word64* WC_SHA3_RESTRICT s, void* scratch) +{ + word64* WC_SHA3_RESTRICT n = (word64*)scratch; + word64* WC_SHA3_RESTRICT b = n + 25; +#else void BlockSha3(word64* s) { word64 n[25]; word64 b[5]; +#endif word64 t0; #ifndef SHA3_BY_SPEC word64 t1; @@ -952,7 +1011,7 @@ static int InitSha3(wc_Sha3* sha3) #if defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) /* WOLFSSL_SHA3_AVX2 asks for AVX2 ahead of BMI2. */ if (SHA3_USE_AVX2(cpuid_flags)) { - SHA3_BLOCK = sha3_block_avx2; + SHA3_BLOCK = SHA3_BLOCK_AVX2_FN; SHA3_BLOCK_N = sha3_block_n_avx2; } else @@ -960,14 +1019,14 @@ static int InitSha3(wc_Sha3* sha3) /* BMI2 before AVX2: sha3_block_bmi2 uses general registers only, so * it needs no vector-register save. */ if (IS_INTEL_BMI1(cpuid_flags) && IS_INTEL_BMI2(cpuid_flags)) { - SHA3_BLOCK = sha3_block_bmi2; + SHA3_BLOCK = SHA3_BLOCK_BMI2_FN; SHA3_BLOCK_N = sha3_block_n_bmi2; } #if !defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) /* AVX2 without BMI2. Only live in the plain build: the overrides * either select AVX2 above or disable it outright. */ else if (SHA3_USE_AVX2(cpuid_flags)) { - SHA3_BLOCK = sha3_block_avx2; + SHA3_BLOCK = SHA3_BLOCK_AVX2_FN; SHA3_BLOCK_N = sha3_block_n_avx2; } #endif @@ -1009,6 +1068,15 @@ void BlockSha3(word64* s) } #endif +/* Run the block function on a context's state with the context's scratch. */ +#if defined(SHA3_FUNC_PTR) && defined(WC_SHA3_SCRATCH_W) + #define SHA3_BLOCK_RUN(o) (*sha3_block)((o)->s, (o)->scratch) +#elif defined(SHA3_FUNC_PTR) + #define SHA3_BLOCK_RUN(o) (*sha3_block)((o)->s) +#else + #define SHA3_BLOCK_RUN(o) WC_SHA3_BLOCK(o, (o)->s) +#endif + /* Update the SHA-3 hash state with message data. * * sha3 wc_Sha3 object holding state. @@ -1028,7 +1096,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) #endif #ifdef USE_INTEL_SPEEDUP #ifdef WC_C_DYNAMIC_FALLBACK - void (*sha3_block)(word64 *s) = SHA3_BLOCK; + WC_SHA3_BLOCK_FN sha3_block = SHA3_BLOCK; void (*sha3_block_n)(word64 *s, const byte* data, word32 n, word64 c) = SHA3_BLOCK_N; #endif @@ -1101,11 +1169,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) total_check += p; #endif #endif - #ifdef SHA3_FUNC_PTR - (*sha3_block)(sha3->s); - #else - BlockSha3(sha3->s); - #endif + SHA3_BLOCK_RUN(sha3); sha3->i = 0; } } @@ -1139,11 +1203,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) } #endif #endif - #ifdef SHA3_FUNC_PTR - (*sha3_block)(sha3->s); - #else - BlockSha3(sha3->s); - #endif + SHA3_BLOCK_RUN(sha3); len -= p * 8U; data += p * 8U; } @@ -1206,7 +1266,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l word32 check = 0; #endif #if defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) - void (*sha3_block)(word64 *s) = SHA3_BLOCK; + WC_SHA3_BLOCK_FN sha3_block = SHA3_BLOCK; #endif if ((p < WC_SHA3_512_COUNT) || (p > WC_SHA3_128_COUNT)) @@ -1272,11 +1332,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l #endif for (j = 0; l - j >= rate; j += rate) { - #ifdef SHA3_FUNC_PTR - (*sha3_block)(sha3->s); - #else - BlockSha3(sha3->s); - #endif + SHA3_BLOCK_RUN(sha3); #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64((word64*)(hash + j), sha3->s, rate); #elif defined(WOLFSSL_WIDE_BYTE) @@ -1286,11 +1342,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l #endif } if (j != l) { - #ifdef SHA3_FUNC_PTR - (*sha3_block)(sha3->s); - #else - BlockSha3(sha3->s); - #endif + SHA3_BLOCK_RUN(sha3); #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64(sha3->s, sha3->s, rate); XMEMCPY(hash + j, sha3->s, l - j); @@ -1650,6 +1702,9 @@ static void wc_Sha3Wipe(wc_Sha3* sha3) #else ForceZero(sha3->s, sizeof(sha3->s)); ForceZero(sha3->t, sizeof(sha3->t)); +#ifdef WC_SHA3_SCRATCH_W + ForceZero(sha3->scratch, sizeof(sha3->scratch)); +#endif sha3->i = 0; #endif } @@ -2424,7 +2479,7 @@ int wc_Shake128_Absorb(wc_Shake* shake, const byte* data, word32 len) int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) { #if defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) - void (*sha3_block)(word64 *s); + WC_SHA3_BLOCK_FN sha3_block; #endif if ((shake == NULL) || (out == NULL && blockCnt != 0)) { @@ -2465,11 +2520,7 @@ int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) #endif for (; (blockCnt > 0); blockCnt--) { - #ifdef SHA3_FUNC_PTR - (*sha3_block)(shake->s); - #else - BlockSha3(shake->s); - #endif + SHA3_BLOCK_RUN(shake); #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64((word64*)out, shake->s, WC_SHA3_128_COUNT * 8); #elif defined(WOLFSSL_WIDE_BYTE) @@ -2773,7 +2824,7 @@ int wc_Shake256_Absorb(wc_Shake* shake, const byte* data, word32 len) int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) { #if defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) - void (*sha3_block)(word64 *s); + WC_SHA3_BLOCK_FN sha3_block; #endif if ((shake == NULL) || (out == NULL && blockCnt != 0)) { @@ -2814,11 +2865,7 @@ int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) #endif for (; (blockCnt > 0); blockCnt--) { - #ifdef SHA3_FUNC_PTR - (*sha3_block)(shake->s); - #else - BlockSha3(shake->s); - #endif + SHA3_BLOCK_RUN(shake); #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64((word64*)out, shake->s, WC_SHA3_256_COUNT * 8); #elif defined(WOLFSSL_WIDE_BYTE) diff --git a/wolfcrypt/src/wc_frodokem_mat.c b/wolfcrypt/src/wc_frodokem_mat.c index fe560b944e5..e825a49b897 100644 --- a/wolfcrypt/src/wc_frodokem_mat.c +++ b/wolfcrypt/src/wc_frodokem_mat.c @@ -1327,10 +1327,10 @@ static int frodokem_gen_a_row_shake(wc_Shake* shake, word16* row, state8[WC_SHA3_128_COUNT * 8 - 1] = 0x80; for (l = 0; l + inc < 2 * p->n; l += inc) { - BlockSha3(state); + WC_SHA3_BLOCK(shake, state); XMEMCPY(rowBytes + l, state8, WC_SHA3_128_BLOCK_SIZE); } - BlockSha3(state); + WC_SHA3_BLOCK(shake, state); XMEMCPY(rowBytes + l, state8, (word32)(2 * p->n - l)); ret = 0; diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index dcdd77a1e26..17a9114d191 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -602,7 +602,7 @@ static int mldsa_shake256(wc_Shake* shake256, const byte* data, else #endif { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } if (dataLen >= WC_SHA3_256_COUNT * 8) { #ifndef WC_SHA3_NO_ASM @@ -630,7 +630,7 @@ static int mldsa_shake256(wc_Shake* shake256, const byte* data, xorbuf(state, data, WC_SHA3_256_COUNT * 8); dataLen -= WC_SHA3_256_COUNT * 8; data += WC_SHA3_256_COUNT * 8; - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } } } @@ -661,7 +661,7 @@ static int mldsa_shake256(wc_Shake* shake256, const byte* data, else #endif { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } if (hash != (byte*)shake256->s) { XMEMCPY(hash, shake256->s, hashLen); @@ -734,7 +734,7 @@ static int mldsa_hash256(wc_Shake* shake256, const byte* data1, else #endif { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } if (data2Len >= WC_SHA3_256_COUNT * 8) { @@ -763,7 +763,7 @@ static int mldsa_hash256(wc_Shake* shake256, const byte* data1, xorbuf(state, data2, WC_SHA3_256_COUNT * 8); data2Len -= WC_SHA3_256_COUNT * 8; data2 += WC_SHA3_256_COUNT * 8; - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } } } @@ -798,7 +798,7 @@ static int mldsa_hash256(wc_Shake* shake256, const byte* data1, else #endif { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } XMEMCPY(hash, shake256->s, hashLen); ret = 0; @@ -1136,7 +1136,7 @@ static int mldsa_squeeze256(wc_Shake* shake256, const byte* in, else #endif { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } XMEMCPY(out, shake256->s, WC_SHA3_256_COUNT * 8); out += WC_SHA3_256_COUNT * 8; @@ -5733,7 +5733,7 @@ static int mldsa_sample_in_ball_ex(int level, wc_Shake* shake256, else #endif { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } /* Restart hash block index. */ diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index ac3f3140103..25ed6acd181 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -2906,7 +2906,7 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); } XMEMCPY(rand + i, state, SHA3_128_BYTES); } @@ -2929,7 +2929,7 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); } XMEMCPY(rand, state, SHA3_128_BYTES); ctr0 += mlkem_rej_uniform_ins(a + ctr0, MLKEM_N - ctr0, rand, @@ -3049,7 +3049,7 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); } XMEMCPY(rand + i, state, SHA3_128_BYTES); } @@ -3072,7 +3072,7 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); } XMEMCPY(rand, state, SHA3_128_BYTES); ctr[0] += mlkem_rej_uniform_ins(a + ctr[0], MLKEM_N - ctr[0], rand, @@ -3364,11 +3364,11 @@ static int mlkem_gen_matrix_k2_aarch64(sword16* a, byte* seed, int transposed) state[4] = 0x1f0000 + (1 << 8) + 1; XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5)); state[20] = W64LIT(0x8000000000000000); - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); p = (byte*)state; ctr0 = mlkem_rej_uniform_neon(a, MLKEM_N, p, XOF_BLOCK_SIZE); while (ctr0 < MLKEM_N) { - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); ctr0 += mlkem_rej_uniform_neon(a + ctr0, MLKEM_N - ctr0, p, XOF_BLOCK_SIZE); } @@ -3518,11 +3518,11 @@ static int mlkem_gen_matrix_k4_aarch64(sword16* a, byte* seed, int transposed) state[4] = 0x1f0000 + (3 << 8) + 3; XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5)); state[20] = W64LIT(0x8000000000000000); - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); p = (byte*)state; ctr0 = mlkem_rej_uniform_neon(a, MLKEM_N, p, XOF_BLOCK_SIZE); while (ctr0 < MLKEM_N) { - BlockSha3(state); + WC_SHA3_BLOCK_SCR(state, state + 25); ctr0 += mlkem_rej_uniform_neon(a + ctr0, MLKEM_N - ctr0, p, XOF_BLOCK_SIZE); } @@ -3772,7 +3772,7 @@ static int mlkem_prf(wc_Shake* shake256, byte* out, unsigned int outLen, else #endif /* !WC_SHA3_NO_ASM */ { - BlockSha3(state); + WC_SHA3_BLOCK(shake256, state); } /* Copy the state as output. */ @@ -3820,7 +3820,7 @@ static int mlkem_prf(wc_Shake* shake256, byte* out, unsigned int outLen, */ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) { - word64 state[25]; + word64 state[WC_SHA3_STATE_W]; word32 len64 = seedLen / 8; readUnalignedWords64(state, seed, len64); @@ -3844,7 +3844,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) else #endif { - BlockSha3(state); + WC_SHA3_BLOCK_ST(state); } XMEMCPY(out, state, outLen); @@ -3871,7 +3871,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) */ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) { - word64 state[25]; + word64 state[WC_SHA3_STATE_W]; word32 len64 = seedLen / 8; readUnalignedWords64(state, seed, len64); @@ -3879,7 +3879,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) XMEMSET(state + len64 + 1, 0, (25 - len64 - 1) * sizeof(word64)); state[WC_SHA3_256_COUNT - 1] = W64LIT(0x8000000000000000); - BlockSha3(state); + WC_SHA3_BLOCK_ST(state); XMEMCPY(out, state, outLen); /* state holds secret KDF output. */ @@ -5065,7 +5065,7 @@ static int mlkem_get_noise_eta2_avx2(MLKEM_PRF_T* prf, sword16* p, else #endif /* !WC_SHA3_NO_ASM */ { - BlockSha3(state); + WC_SHA3_BLOCK(prf, state); } mlkem_cbd_eta2_ins(p, (byte*)state); @@ -5582,15 +5582,15 @@ static void mlkem_get_noise_eta3_aarch64(byte* rand, byte* seed, byte o) { /* ETA3_RAND_SIZE is larger than the SHAKE-256 rate - two squeezes are * needed, so the state cannot be squeezed in place over the output. */ - word64 state[25]; + word64 state[WC_SHA3_STATE_W]; readUnalignedWords64(state, seed, 4); state[4] = 0x1f00 + o; XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5)); state[16] = W64LIT(0x8000000000000000); - BlockSha3(state); + WC_SHA3_BLOCK_ST(state); XMEMCPY(rand , state, SHA3_256_BYTES); - BlockSha3(state); + WC_SHA3_BLOCK_ST(state); XMEMCPY(rand + SHA3_256_BYTES, state, ETA3_RAND_SIZE - SHA3_256_BYTES); /* state is secret-seeded; caller zeroizes rand. */ @@ -5676,7 +5676,7 @@ static void mlkem_get_noise_eta2_aarch64(word64* rand, byte* seed, byte o) rand[4] = 0x1f00 + o; XMEMSET(rand + 5, 0, sizeof(*rand) * (25 - 5)); rand[16] = W64LIT(0x8000000000000000); - BlockSha3(rand); + WC_SHA3_BLOCK_SCR(rand, rand + 25); } #endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || * !WOLFSSL_MLKEM_NO_DECAPSULATE */ diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 084371b2372..f9c09077c1e 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -576,7 +576,7 @@ static int slhdsakey_hash_shake_3(wc_Shake* shake, const byte* data1, #endif { /* Process the state using C code. */ - BlockSha3(state); + WC_SHA3_BLOCK(shake, state); } /* Copy hash result, of the required length, from the state into hash. */ XMEMCPY(hash, shake->s, hash_len); @@ -695,7 +695,7 @@ static int slhdsakey_hash_shake_4(wc_Shake* shake, const byte* data1, #endif { /* Process the state using C code. */ - BlockSha3(state); + WC_SHA3_BLOCK(shake, state); } /* Copy hash result, of the required length, from the state into hash. */ XMEMCPY(hash, shake->s, hash_len); diff --git a/wolfssl/wolfcrypt/sha3.h b/wolfssl/wolfcrypt/sha3.h index 6e5e2a912bb..6a6fbcb231b 100644 --- a/wolfssl/wolfcrypt/sha3.h +++ b/wolfssl/wolfcrypt/sha3.h @@ -141,6 +141,31 @@ enum { #include "cy_crypto_core.h" #endif +/* Round scratch of the C Keccak block function lives in the context so it is + * wiped once at Free: word64 lanes, or word32 halves for the split variant. */ +#if (defined(WC_SHA3_NO_ASM) || (!defined(WOLFSSL_ARMASM) && \ + !defined(WOLFSSL_RISCV_ASM) && !defined(WOLFSSL_PPC64_ASM) && \ + !defined(WOLFSSL_PPC32_ASM))) && !defined(PSOC6_HASH_SHA3) && \ + !defined(WOLFSSL_XILINX_CRYPT) && !defined(WOLFSSL_AFALG_XILINX_SHA3) + #if defined(WOLFSSL_SHA3_SMALL) + #define WC_SHA3_SCRATCH_W 5 + #elif defined(WC_SHA3_SPLIT64) || \ + (defined(WOLFSSL_WIDE_BYTE) && !defined(BIG_ENDIAN_ORDER)) + #define WC_SHA3_SCRATCH_W 80 + #elif !defined(STM32_HASH_SHA3) || defined(WOLFSSL_SHAKE128) || \ + defined(WOLFSSL_SHAKE256) + #define WC_SHA3_SCRATCH_W 30 + #endif +#endif +#ifdef WC_SHA3_SCRATCH_W + typedef void (*WC_SHA3_BLOCK_FN)(word64* s, void* scratch); + /* Lanes to declare for a bare state that carries its own scratch. */ + #define WC_SHA3_STATE_W (25 + WC_SHA3_SCRATCH_W) +#else + typedef void (*WC_SHA3_BLOCK_FN)(word64* s); + #define WC_SHA3_STATE_W 25 +#endif + /* Sha3 digest */ struct wc_Sha3 { #if defined(PSOC6_HASH_SHA3) @@ -152,6 +177,10 @@ struct wc_Sha3 { word64 s[25]; /* Unprocessed message data. */ byte t[200]; +#ifdef WC_SHA3_SCRATCH_W + /* Round scratch for the C block function, wiped at Free. */ + word64 scratch[WC_SHA3_SCRATCH_W]; +#endif /* Index into unprocessed data to place next message byte. */ word32 i; @@ -164,7 +193,7 @@ struct wc_Sha3 { #endif #ifdef WC_C_DYNAMIC_FALLBACK - void (*sha3_block)(word64 *s); + WC_SHA3_BLOCK_FN sha3_block; void (*sha3_block_n)(word64 *s, const byte* data, word32 n, word64 c); #endif @@ -394,7 +423,19 @@ WOLFSSL_API int wc_Cshake256(const byte* name, word32 nameLen, #endif #endif /* WOLFSSL_KMAC || WOLFSSL_CSHAKE */ -WOLFSSL_LOCAL void BlockSha3(word64 *s); +#ifdef WC_SHA3_SCRATCH_W +WOLFSSL_LOCAL void BlockSha3(word64* s, void* scratch); +/* obj is the wc_Sha3 or wc_Shake whose scratch the block function uses. */ +#define WC_SHA3_BLOCK(obj, s) BlockSha3((s), (obj)->scratch) +#define WC_SHA3_BLOCK_SCR(s, scr) BlockSha3((s), (scr)) +/* st is a word64[WC_SHA3_STATE_W] with the scratch after the state. */ +#define WC_SHA3_BLOCK_ST(st) BlockSha3((st), (st) + 25) +#else +WOLFSSL_LOCAL void BlockSha3(word64* s); +#define WC_SHA3_BLOCK(obj, s) BlockSha3(s) +#define WC_SHA3_BLOCK_SCR(s, scr) do { (void)(scr); BlockSha3(s); } while (0) +#define WC_SHA3_BLOCK_ST(st) BlockSha3(st) +#endif #ifdef WC_SHA3_NO_ASM /* asm speedups disabled */ From 09fa6f78c2922ace8ebc0a9ca39b1882b48fb394 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 1 Oct 2026 14:19:00 -0600 Subject: [PATCH 11/74] ML-KEM: add basemul products into r without staging them mlkem_basemul_mont_add staged each product pair of the secret vector in a stack array before adding it to r. Multiply and add in one step so the products stay in registers (FIPS 203 Section 3.3; ISO/IEC 19790:2012 7.9.7). restrict on r, a and b keeps the loop vectorised. --- wolfcrypt/src/wc_mlkem_poly.c | 130 +++++++++++++++++----------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index 25ed6acd181..6b3cbe5d129 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -1099,6 +1099,49 @@ static void mlkem_basemul(sword16* r, const sword16* a, const sword16* b, r[1] = MLKEM_MONT_RED(p1); } +/* r never aliases a or b; saying so keeps the accumulate loop vectorised. */ +#if defined(__GNUC__) || defined(__clang__) + #define MLKEM_RESTRICT __restrict__ +#elif defined(_MSC_VER) + #define MLKEM_RESTRICT __restrict +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define MLKEM_RESTRICT restrict +#else + #define MLKEM_RESTRICT +#endif + +/* 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). */ +static void mlkem_basemul_add(sword16* MLKEM_RESTRICT r, + const sword16* MLKEM_RESTRICT a, const sword16* MLKEM_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 +1211,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* MLKEM_RESTRICT r, + const sword16* MLKEM_RESTRICT a, const sword16* MLKEM_RESTRICT b) { const sword16* zeta = zetas + 64; @@ -1177,78 +1220,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 } From 10de57e66f76aa94ec11f4b20a967d21f53b9e4f Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 6 Oct 2026 10:36:30 -0600 Subject: [PATCH 12/74] ForceZero: use volatile stores where no data barrier backs the wipe Without GNU asm, or with WOLFSSL_NO_ASM, WC_BARRIER_DATA discards its pointer and the fence is a no-op on x86, so a wipe of a local whose address never escapes can be dropped as a dead store. Mark that fallback and let ForceZero write through volatile pointers there (ISO/IEC 19790:2012 7.9.7 [09.30]). The GNU asm path is unchanged. --- wolfcrypt/src/misc.c | 16 ++++++++++++---- wolfssl/wolfcrypt/wc_port.h | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/misc.c b/wolfcrypt/src/misc.c index f4b81fb8373..26e1fe52356 100644 --- a/wolfcrypt/src/misc.c +++ b/wolfcrypt/src/misc.c @@ -788,8 +788,16 @@ WC_MISC_STATIC WC_INLINE void xorbuf(void* buf, const void* mask, word32 count) with zeros. It ensures compiler optimization doesn't skip it. */ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) { - byte *zb = (byte *)mem; - unsigned long *zl; +#ifdef WC_BARRIER_DATA_WEAK + /* Volatile stores cannot be dropped as dead when no barrier backs them. */ + typedef volatile byte wc_wipe_byte; + typedef volatile unsigned long wc_wipe_word; +#else + typedef byte wc_wipe_byte; + typedef unsigned long wc_wipe_word; +#endif + wc_wipe_byte *zb = (wc_wipe_byte *)mem; + wc_wipe_word *zl; /* Make the compiler put the buffer's current contents at mem, so the * wipe below hits the memory that holds them and not a copy. */ @@ -803,14 +811,14 @@ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) --len; } - zl = (unsigned long *)zb; + zl = (wc_wipe_word *)zb; while (len >= sizeof(unsigned long)) { *zl++ = 0; len -= sizeof(unsigned long); } - zb = (byte *)zl; + zb = (wc_wipe_byte *)zl; while (len) { *zb++ = 0; diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 546b95f71c2..e1c979288c5 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -2161,6 +2161,8 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); #define WC_BARRIER_DATA(ptr) \ __asm__ __volatile__("" : : "r"(ptr) : "memory") #else + /* No real data barrier here: ForceZero falls back to volatile stores. */ + #define WC_BARRIER_DATA_WEAK #define WC_BARRIER_DATA(ptr) do { (void)(ptr); WC_BARRIER(); } while (0) #endif From 09bf95c4ccca3432480bdc4219e45868e189e8de Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 6 Oct 2026 10:37:46 -0600 Subject: [PATCH 13/74] SHA-1, SHA-256: keep the C transform message schedule in the context The C transforms built the message schedule W, whose first words are the raw input block, on their own stack frame where nothing could wipe it. The context now owns W and the destructor's whole-object wipe covers it (ISO/IEC 19790:2012 7.9.7 [09.32]). WC_RESTRICT is the shared restrict qualifier. Within noise of master on the no-asm benchmark. --- wolfcrypt/src/sha.c | 2 +- wolfcrypt/src/sha256.c | 6 ++++++ wolfssl/wolfcrypt/sha.h | 2 ++ wolfssl/wolfcrypt/sha256.h | 6 ++++++ wolfssl/wolfcrypt/wc_port.h | 13 +++++++++++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index a36bfffe4d4..161cb754e25 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -491,7 +491,7 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) static int Transform(wc_Sha* sha, const byte* data) { - word32 W[WC_SHA_BLOCK_SIZE / sizeof(word32)]; + word32* WC_RESTRICT W = sha->W; /* Copy context->state[] to working vars */ word32 a = sha->digest[0]; diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index ad0753bb542..26db04243aa 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -1822,6 +1822,8 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; + #elif defined(WC_SHA256_W_IN_CTX) + word32* WC_RESTRICT W = sha256->Wbuf; #else word32 W[WC_SHA256_BLOCK_SIZE]; #endif @@ -1897,7 +1899,11 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef USE_SLOW_SHA256 int j; #endif + #ifdef WC_SHA256_W_IN_CTX + word32* WC_RESTRICT W = sha256->Wbuf; + #else word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; + #endif /* Copy digest to working vars */ S[0] = sha256->digest[0]; diff --git a/wolfssl/wolfcrypt/sha.h b/wolfssl/wolfcrypt/sha.h index 7116d33c73c..48feff3b695 100644 --- a/wolfssl/wolfcrypt/sha.h +++ b/wolfssl/wolfcrypt/sha.h @@ -161,6 +161,8 @@ struct wc_Sha { #else word32 digest[WC_SHA_DIGEST_SIZE / sizeof(word32)]; #endif + /* Message schedule of the C transform; wiped with the context at Free. */ + word32 W[WC_SHA_BLOCK_SIZE / sizeof(word32)]; #endif /* end of the hardware-vs-software member chain */ /* The callback port keeps the software implementation compiled in for diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index e11bfd026ff..c1e6f24b0aa 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -195,6 +195,12 @@ struct wc_Sha256 { word32 buffLen; /* in bytes */ word32 loLen; /* length in bytes */ word32 hiLen; /* length in bytes */ +#if !((defined(WOLFSSL_SMALL_STACK_CACHE) || defined(WOLFSSL_SMALL_STACK)) && \ + !defined(WOLFSSL_NO_MALLOC)) + /* Message schedule of the C transform; wiped with the context at Free. */ + #define WC_SHA256_W_IN_CTX + word32 Wbuf[WC_SHA256_BLOCK_SIZE]; +#endif #endif /* end of the hardware-vs-software member chain */ diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index e1c979288c5..de46db54585 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -106,6 +106,19 @@ #endif #endif /* !WC_DEPRECATED */ +/* Pointer qualifier promising no aliasing, for scratch kept in a context. */ +#ifndef WC_RESTRICT + #if defined(__GNUC__) || defined(__clang__) + #define WC_RESTRICT __restrict__ + #elif defined(_MSC_VER) + #define WC_RESTRICT __restrict + #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define WC_RESTRICT restrict + #else + #define WC_RESTRICT + #endif +#endif + /* Use inlining if compiler allows -- omit the static attribute here, so that * WC_INLINE can be used on functions that are instantiated both inline in the * TU, and callable from outside the TU. From 85b9a441816ddb170d94c74054e8b106837b321d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:27:19 -0600 Subject: [PATCH 14/74] openssl compat: size the SHA and SHA-224 contexts from the native structs The in-context SHA-1 and SHA-256 message schedules grew wc_Sha and wc_Sha224; derive the holders from sizeof and bump the mcapi HMAC holder. --- mcapi/crypto.h | 4 ++-- wolfssl/openssl/sha.h | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/mcapi/crypto.h b/mcapi/crypto.h index ae287be745c..90f0edbd4e2 100644 --- a/mcapi/crypto.h +++ b/mcapi/crypto.h @@ -108,9 +108,9 @@ enum { typedef struct CRYPT_HMAC_CTX { /* big enough to hold internal, but check on init */ #ifdef WOLF_PRIVATE_KEY_ID - long long holder[108]; + long long holder[160]; #else - long long holder[98]; + long long holder[150]; #endif } CRYPT_HMAC_CTX; 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 From a8a55f746ab3bff9bd1fa13862096d0200aa1bd5 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:35:40 -0600 Subject: [PATCH 15/74] mcapi: grow the SHA and SHA-256 context holders --- mcapi/crypto.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mcapi/crypto.h b/mcapi/crypto.h index 90f0edbd4e2..73ca00b9443 100644 --- a/mcapi/crypto.h +++ b/mcapi/crypto.h @@ -48,7 +48,7 @@ enum { /* SHA */ typedef struct CRYPT_SHA_CTX { - int holder[29]; /* big enough to hold internal, but check on init */ + int holder[48]; /* big enough to hold internal, but check on init */ } CRYPT_SHA_CTX; int CRYPT_SHA_Initialize(CRYPT_SHA_CTX*); @@ -63,7 +63,7 @@ enum { /* SHA-256 */ typedef struct CRYPT_SHA256_CTX { - int holder[32]; /* big enough to hold internal, but check on init */ + int holder[112]; /* big enough to hold internal, but check on init */ } CRYPT_SHA256_CTX; int CRYPT_SHA256_Initialize(CRYPT_SHA256_CTX*); From c56f8a7668e775a2aa194d92b5fbc658e0505ed3 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 14:17:00 -0600 Subject: [PATCH 16/74] SHA-1: wipe the GetHash copy; SHA-256: wipe the many-registers stack W --- wolfcrypt/src/sha.c | 2 ++ wolfcrypt/src/sha256.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 161cb754e25..2b5a10cf9c0 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -1236,8 +1236,10 @@ int wc_ShaGetHash(wc_Sha* sha, byte* hash) ret = wc_ShaCopy(sha, tmpSha); if (ret == 0) { ret = wc_ShaFinal(tmpSha, hash); + wc_ShaFree(tmpSha); } + ForceZero(tmpSha, sizeof(*tmpSha)); 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 26db04243aa..c84b31c2fb9 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -1949,6 +1949,9 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->digest[6] += S[6]; sha256->digest[7] += S[7]; + #ifndef WC_SHA256_W_IN_CTX + ForceZero(W, sizeof(W)); + #endif return 0; } #endif /* SHA256_MANY_REGISTERS */ From fe9456df8078fb3e78929539761d280bf83ab8b6 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 14:17:00 -0600 Subject: [PATCH 17/74] SHA-3: ForceZero in InitSha3, wipe KMAC key staging and keyed sponge --- wolfcrypt/src/sha3.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index cde05b890fd..b02365d226b 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -973,11 +973,9 @@ static word64 Load64BitLittleEndian(const byte* a) static int InitSha3(wc_Sha3* sha3) { - int i; - - for (i = 0; i < 25; i++) - sha3->s[i] = 0; - XMEMSET(sha3->t, 0, sizeof(sha3->t)); + /* The sponge held the prior message. */ + ForceZero(sha3->s, sizeof(sha3->s)); + ForceZero(sha3->t, sizeof(sha3->t)); sha3->i = 0; #ifdef WOLFSSL_HASH_FLAGS sha3->flags = 0; @@ -1704,6 +1702,9 @@ static void wc_Sha3Wipe(wc_Sha3* sha3) ForceZero(sha3->t, sizeof(sha3->t)); #ifdef WC_SHA3_SCRATCH_W ForceZero(sha3->scratch, sizeof(sha3->scratch)); +#endif +#ifdef STM32_HASH_SHA3 + ForceZero(&sha3->stmCtx, sizeof(sha3->stmCtx)); #endif sha3->i = 0; #endif @@ -3199,6 +3200,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)); } } @@ -3284,6 +3287,10 @@ static int KmacFinal(wc_Kmac* kmac, byte* out, word32 outLen, int xof) /* cSHAKE domain separation pad (0x04), then squeeze outLen. */ ret = Sha3Final(&kmac->shake, 0x04, out, kmac->count, outLen); } + if (ret == 0) { + /* Keyed sponge is no longer needed (ISO/IEC 19790 7.9.7). */ + wc_Sha3Wipe(&kmac->shake); + } } return ret; } From 11220d4cd24363bda40c38e4cbadac085ee5e007 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:37:09 -0600 Subject: [PATCH 18/74] EdDSA and X25519/X448: wipe ladder, scalar and key temporaries Zeroise the radix-16 scalar, scalar limb copies, ladder and inversion field elements, reset the persistent hash on a failed sign, and clear priv on a failed raw export or import. --- wolfcrypt/src/curve25519.c | 26 ++++++++++++++++++++++-- wolfcrypt/src/curve448.c | 15 +++++++++++++- wolfcrypt/src/ed25519.c | 4 ++++ wolfcrypt/src/ed448.c | 5 +++++ wolfcrypt/src/fe_448.c | 33 ++++++++++++++++++++++++++++++ wolfcrypt/src/fe_operations.c | 21 ++++++++++++++++++- wolfcrypt/src/ge_448.c | 22 ++++++++++++++++++++ wolfcrypt/src/ge_operations.c | 38 ++++++++++++++++++++++++++--------- 8 files changed, 151 insertions(+), 13 deletions(-) diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 3683d166d60..5d84e36374e 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -1267,13 +1267,23 @@ int wc_curve25519_export_key_raw_ex(curve25519_key* key, { int ret; + /* Check the public arguments before anything is written to priv. */ + if ((pub == NULL) || (pubSz == NULL)) + return BAD_FUNC_ARG; + /* export private part */ ret = wc_curve25519_export_private_raw_ex(key, priv, privSz, endian); if (ret != 0) return ret; /* export public part */ - return wc_curve25519_export_public_ex(key, pub, pubSz, endian); + ret = wc_curve25519_export_public_ex(key, pub, pubSz, endian); + if (ret != 0) { + /* Public export failed: do not hand back the private key. */ + ForceZero(priv, *privSz); + } + + return ret; } #endif /* HAVE_CURVE25519_KEY_EXPORT */ @@ -1306,7 +1316,13 @@ int wc_curve25519_import_private_raw_ex(const byte* priv, word32 privSz, return ret; /* import public part */ - return wc_curve25519_import_public_ex(pub, pubSz, key, endian); + ret = wc_curve25519_import_public_ex(pub, pubSz, key, endian); + if (ret != 0) { + key->privSet = 0; + ForceZero(key->k, sizeof(key->k)); + } + + return ret; } /* curve25519 private key import only. (Big endian) @@ -1441,6 +1457,12 @@ void wc_curve25519_free(curve25519_key* key) #ifdef WOLFSSL_SE050 se050_curve25519_free_key(key); +#endif +#ifdef WC_X25519_NONBLOCK + /* An attached context may hold ladder state from an unfinished call. */ + if (key->nb_ctx != NULL) { + ForceZero(key->nb_ctx, sizeof(x25519_nb_ctx_t)); + } #endif ForceZero(key, sizeof(*key)); diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index 89c912b713f..69138a01ae3 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -812,11 +812,20 @@ int wc_curve448_export_key_raw_ex(curve448_key* key, byte* priv, word32 *privSz, { int ret; + /* Check the public arguments before anything is written to priv. */ + if ((pub == NULL) || (pubSz == NULL)) { + return BAD_FUNC_ARG; + } + /* export private part */ ret = wc_curve448_export_private_raw_ex(key, priv, privSz, endian); if (ret == 0) { /* export public part */ ret = wc_curve448_export_public_ex(key, pub, pubSz, endian); + if (ret != 0) { + /* Public export failed: do not hand back the private key. */ + ForceZero(priv, *privSz); + } } return ret; @@ -870,7 +879,11 @@ int wc_curve448_import_private_raw_ex(const byte* priv, word32 privSz, ret = wc_curve448_import_private_ex(priv, privSz, key, endian); if (ret == 0) { /* import public part */ - return wc_curve448_import_public_ex(pub, pubSz, key, endian); + ret = wc_curve448_import_public_ex(pub, pubSz, key, endian); + if (ret != 0) { + key->privSet = 0; + ForceZero(key->k, sizeof(key->k)); + } } return ret; diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index b162ecc51a9..d598d6d267c 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -697,6 +697,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) + (void)ed25519_hash_reset(key); #endif } diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 2c2b02a0396..913c5f859b4 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -684,6 +684,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) { + (void)ed448_hash_reset(key); + } #endif } if (ret == 0) { diff --git a/wolfcrypt/src/fe_448.c b/wolfcrypt/src/fe_448.c index dad8740ff0e..bbe79ee630e 100644 --- a/wolfcrypt/src/fe_448.c +++ b/wolfcrypt/src/fe_448.c @@ -336,6 +336,7 @@ void fe448_invert(word8* r, const word8* a) fe448_sqr(t, t); fe448_sqr(t, t); fe448_mul(r, t, a); + ForceZero(t, sizeof(t)); } /* Scalar multiply the point by a number. r = n.a @@ -395,6 +396,14 @@ int curve448(byte* r, const byte* n, const byte* a) fe448_mul(r, x2, z2); fe448_norm(r); + ForceZero(x1, sizeof(x1)); + ForceZero(x2, sizeof(x2)); + ForceZero(z2, sizeof(z2)); + ForceZero(x3, sizeof(x3)); + ForceZero(z3, sizeof(z3)); + ForceZero(t0, sizeof(t0)); + ForceZero(t1, sizeof(t1)); + return 0; } @@ -1072,6 +1081,10 @@ void fe448_invert(sword64* r, const sword64* a) /* t1 = fffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000 */ fe448_mul(r, t3, t1); /* r = fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd */ + ForceZero(t1, sizeof(t1)); + ForceZero(t2, sizeof(t2)); + ForceZero(t3, sizeof(t3)); + ForceZero(t4, sizeof(t4)); } /* Scalar multiply the point by a number. r = n.a @@ -1137,6 +1150,14 @@ int curve448(byte* r, const byte* n, const byte* a) fe448_mul(x2, x2, z2); fe448_to_bytes(r, x2); + ForceZero(x1, sizeof(x1)); + ForceZero(x2, sizeof(x2)); + ForceZero(z2, sizeof(z2)); + ForceZero(x3, sizeof(x3)); + ForceZero(z3, sizeof(z3)); + ForceZero(t0, sizeof(t0)); + ForceZero(t1, sizeof(t1)); + return 0; } @@ -2249,6 +2270,10 @@ void fe448_invert(sword32* r, const sword32* a) /* t1 = fffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000 */ fe448_mul(r, t3, t1); /* r = fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd */ + ForceZero(t1, sizeof(t1)); + ForceZero(t2, sizeof(t2)); + ForceZero(t3, sizeof(t3)); + ForceZero(t4, sizeof(t4)); } /* Scalar multiply the point by a number. r = n.a @@ -2314,6 +2339,14 @@ int curve448(byte* r, const byte* n, const byte* a) fe448_mul(x2, x2, z2); fe448_to_bytes(r, x2); + ForceZero(x1, sizeof(x1)); + ForceZero(x2, sizeof(x2)); + ForceZero(z2, sizeof(z2)); + ForceZero(x3, sizeof(x3)); + ForceZero(z3, sizeof(z3)); + ForceZero(t0, sizeof(t0)); + ForceZero(t1, sizeof(t1)); + return 0; } diff --git a/wolfcrypt/src/fe_operations.c b/wolfcrypt/src/fe_operations.c index 671ab364fdd..94b6d0935d5 100644 --- a/wolfcrypt/src/fe_operations.c +++ b/wolfcrypt/src/fe_operations.c @@ -180,6 +180,14 @@ int curve25519(byte* q, const byte* n, const byte* p) fe_mul(x2,x2,z2); fe_tobytes(q,x2); + ForceZero(x1, sizeof(x1)); + ForceZero(x2, sizeof(x2)); + ForceZero(z2, sizeof(z2)); + ForceZero(x3, sizeof(x3)); + ForceZero(z3, sizeof(z3)); + ForceZero(tmp0, sizeof(tmp0)); + ForceZero(tmp1, sizeof(tmp1)); + return 0; } #else @@ -247,6 +255,14 @@ int curve25519_blind(byte* q, const byte* n, const byte* mask, const byte* p, fe_mul(x2,x2,z2); fe_tobytes(q,x2); + ForceZero(x1, sizeof(x1)); + ForceZero(x2, sizeof(x2)); + ForceZero(z2, sizeof(z2)); + ForceZero(x3, sizeof(x3)); + ForceZero(z3, sizeof(z3)); + ForceZero(tmp0, sizeof(tmp0)); + ForceZero(tmp1, sizeof(tmp1)); + return 0; } #endif @@ -754,7 +770,10 @@ void fe_invert(fe out,const fe z) fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); fe_mul(out,t1,t0); - return; + ForceZero(t0, sizeof(t0)); + ForceZero(t1, sizeof(t1)); + ForceZero(t2, sizeof(t2)); + ForceZero(t3, sizeof(t3)); } diff --git a/wolfcrypt/src/ge_448.c b/wolfcrypt/src/ge_448.c index d2c77638dc0..7f1e2e7c754 100644 --- a/wolfcrypt/src/ge_448.c +++ b/wolfcrypt/src/ge_448.c @@ -148,6 +148,7 @@ void sc448_reduce(byte* b) } b[i] = t[i] & 0xff; b[i+1] = 0; + ForceZero(t, sizeof(t)); } /* Multiply a by b and add d. r = (a * b + d) mod order @@ -227,6 +228,7 @@ void sc448_muladd(byte* r, const byte* a, const byte* b, const byte* d) for (i = 0; i < 56; i++) { u += r[i] - (ed448_order[i] & o); r[i] = u & 0xff; u >>= 8; } + ForceZero(t, sizeof(t)); } /* Double the point on the Twisted Edwards curve. r = 2.p @@ -362,6 +364,8 @@ static void ge448_scalarmult(ge448_p2* h, const ge448_p2* p, const byte* a) } XMEMCPY(h, &r, sizeof(r)); + ForceZero(&r, sizeof(r)); + ForceZero(&s, sizeof(s)); } /* Perform a scalar multiplication of the base point. r = a * base @@ -788,6 +792,8 @@ void sc448_reduce(byte* b) b[55] = (byte)(d[7 ] >> 48); b[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ + ForceZero(d, sizeof(d)); + ForceZero(t, sizeof(t)); } /* Multiply a by b and add d. r = (a * b + d) mod order @@ -1295,6 +1301,11 @@ void sc448_muladd(byte* r, const byte* a, const byte* b, const byte* d) r[55] = (byte)(rd[7 ] >> 48); r[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ + ForceZero(ad, sizeof(ad)); + ForceZero(bd, sizeof(bd)); + ForceZero(dd, sizeof(dd)); + ForceZero(rd, sizeof(rd)); + ForceZero(t, sizeof(t)); } /* Precomputed multiples of the base point. */ @@ -5766,6 +5777,8 @@ void sc448_reduce(byte* b) b[55] = (byte)(d[15] >> 20); b[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ + ForceZero(d, sizeof(d)); + ForceZero(t, sizeof(t)); } /* Multiply a by b and add d. r = (a * b + d) mod order @@ -6677,6 +6690,11 @@ void sc448_muladd(byte* r, const byte* a, const byte* b, const byte* d) r[55] = (byte)(rd[15] >> 20); r[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ + ForceZero(ad, sizeof(ad)); + ForceZero(bd, sizeof(bd)); + ForceZero(dd, sizeof(dd)); + ForceZero(rd, sizeof(rd)); + ForceZero(t, sizeof(t)); } /* Precomputed multiples of the base point. */ @@ -10974,6 +10992,7 @@ static void ge448_select(ge448_precomp* r, int pos, byte b) cmov(r, &base[pos][7], babs, 8); fe448_neg(minusx, r->x); fe448_cmov(r->x, minusx, bnegative); + ForceZero(minusx, sizeof(minusx)); } /* Perform a scalar multiplication of the base point. r = a * base @@ -11040,6 +11059,9 @@ int ge448_scalarmult_base(ge448_p2* r, const byte* a) ge448_madd(r, r, t); } + /* e is the scalar in radix 16 and t the selected table entry. */ + ForceZero(e, 113); + ForceZero(t, sizeof(*t)); #ifdef WOLFSSL_SMALL_STACK XFREE(t, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(e, NULL, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/wolfcrypt/src/ge_operations.c b/wolfcrypt/src/ge_operations.c index fda375ba2e3..aa2615fba95 100644 --- a/wolfcrypt/src/ge_operations.c +++ b/wolfcrypt/src/ge_operations.c @@ -310,6 +310,7 @@ void sc_reduce(byte* s) for (i = 0; i < 32; i++) s[i] = WC_OCTET(s[i]); #endif + ForceZero(t, sizeof(t)); } /* @@ -638,6 +639,10 @@ void sc_muladd(byte* s, const byte* a, const byte* b, const byte* c) for (i = 0; i < 32; i++) s[i] = WC_OCTET(s[i]); #endif + ForceZero(ad, sizeof(ad)); + ForceZero(bd, sizeof(bd)); + ForceZero(cd, sizeof(cd)); + ForceZero(t, sizeof(t)); } #endif #else @@ -779,6 +784,7 @@ void sc_reduce(byte* s) s[29] = (byte)(t[ 5] >> 22); s[30] = (byte)(t[ 5] >> 30); s[31] = (byte)(t[ 5] >> 38); + ForceZero(t, sizeof(t)); } /* @@ -931,6 +937,10 @@ void sc_muladd(byte* s, const byte* a, const byte* b, const byte* c) s[29] = (byte)(t[ 5] >> 22); s[30] = (byte)(t[ 5] >> 30); s[31] = (byte)(t[ 5] >> 38); + ForceZero(ad, sizeof(ad)); + ForceZero(bd, sizeof(bd)); + ForceZero(cd, sizeof(cd)); + ForceZero(t, sizeof(t)); } #endif /* !CURVED25519_ASM */ #endif /* !HAVE___UINT128_T || NO_CURVED25519_128BIT */ @@ -9093,10 +9103,9 @@ static const ge_precomp base[32][8] = { } ; #endif -static void ge_select(ge_precomp *t,int pos,signed char b) +static void ge_select(ge_precomp *t,ge_precomp *minust,int pos,signed char b) { #ifndef CURVED25519_ASM - ge_precomp minust; unsigned char bnegative = negative(b); unsigned char babs = (unsigned char)(b - (signed char)((unsigned char)((-bnegative) & b) << 1)); @@ -9111,9 +9120,10 @@ static void ge_select(ge_precomp *t,int pos,signed char b) cmov(t,&base[pos][6],babs,7); cmov(t,&base[pos][7],babs,8); fe_cswap(t->yminusx, t->yplusx, bnegative); - fe_neg(minust.xy2d,t->xy2d); - fe_cmov(t->xy2d,minust.xy2d,bnegative); + fe_neg(minust->xy2d,t->xy2d); + fe_cmov(t->xy2d,minust->xy2d,bnegative); #else + (void)minust; /* (wc_ptr_t) needed to work around C array casting semantics. */ fe_cmov_table((fe*)t, (const fe*)(wc_ptr_t)base[pos], b); #endif @@ -9136,6 +9146,7 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) ge_p2 s; #endif ge_precomp t; + ge_precomp minust; int i; for (i = 0;i < 32;++i) { @@ -9156,7 +9167,7 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) /* each e[i] is between -8 and 8 */ #ifndef CURVED25519_ASM - ge_select(&t,0,e[1]); + ge_select(&t,&minust,0,e[1]); fe_sub(h->X, t.yplusx, t.yminusx); fe_add(h->Y, t.yplusx, t.yminusx); fe_0(h->Z); @@ -9166,7 +9177,7 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) fe_add(h->Y, h->Y, h->Y); for (i = 3;i < 64;i += 2) { - ge_select(&t,i / 2,e[i]); + ge_select(&t,&minust,i / 2,e[i]); ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); } @@ -9176,21 +9187,30 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) ge_p2_dbl(&r,&s); ge_p1p1_to_p3(h,&r); for (i = 0;i < 64;i += 2) { - ge_select(&t,i / 2,e[i]); + ge_select(&t,&minust,i / 2,e[i]); ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); } #else - ge_select(&t, 0, e[0]); + ge_select(&t, &minust, 0, e[0]); fe_sub(h->X, t.yplusx, t.yminusx); fe_add(h->Y, t.yplusx, t.yminusx); fe_0(h->Z); h->Z[0] = 2; fe_copy(h->T, t.xy2d); for (i = 1; i < 64; i++) { - ge_select(&t, i, e[i]); + ge_select(&t, &minust, i, e[i]); ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); } #endif + + /* e is the scalar in radix 16; r, s and t are secret dependent. */ + ForceZero(e, sizeof(e)); + ForceZero(&r, sizeof(r)); +#ifndef CURVED25519_ASM + ForceZero(&s, sizeof(s)); +#endif + ForceZero(&t, sizeof(t)); + ForceZero(&minust, sizeof(minust)); } #endif /* HAVE_ED25519_SIGN || HAVE_ED25519_MAKE_KEY || * WOLFSSL_CURVE25519_USE_ED25519 */ From 3dfc04c208e24903f24c902219a6cb9feadd4404 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:39:36 -0600 Subject: [PATCH 19/74] rsa: wipe the PSS salt, the OAEP unpad temp and the keygen blind The blind and the blinded e were staged in key->p, q and e and only partly covered by the later copies. Free also clears an attached non-blocking context. --- wolfcrypt/src/rsa.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 0bc6834ade4..ca0a771d0f3 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -609,6 +609,14 @@ int wc_FreeRsaKey(RsaKey* key) return BAD_FUNC_ARG; } +#ifdef WC_RSA_NONBLOCK + /* An abandoned operation leaves a copy of d in the context. */ + if (key->nb != NULL) { + ForceZero(key->nb, sizeof(RsaNb)); + key->nb = NULL; + } +#endif + #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) #ifndef WOLF_CRYPTO_CB_FIND if (key->devId != INVALID_DEVID) @@ -1685,9 +1693,16 @@ 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)); +#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; } @@ -1871,6 +1886,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, hLen); +#if defined(WOLFSSL_CHECK_MEM_ZERO) && \ + (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) + wc_MemZero_Check(tmp, hLen); +#endif WC_FREE_VAR_EX(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; } @@ -5984,6 +6004,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 @@ -6131,8 +6155,11 @@ int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb) if (key == NULL) return BAD_FUNC_ARG; - if (nb) { - XMEMSET(nb, 0, sizeof(RsaNb)); + if (nb != NULL) { + ForceZero(nb, sizeof(RsaNb)); + } + if ((key->nb != NULL) && (key->nb != nb)) { + ForceZero(key->nb, sizeof(RsaNb)); } /* Allow nb == NULL to clear non-block mode */ From afc37ca2fce61aa374733f5d502ae45286b04b44 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:39:36 -0600 Subject: [PATCH 20/74] dh: wipe the previous private key in wc_DhKeyCopy and the nb context --- wolfcrypt/src/dh.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index 6c49fc6b4d6..f4c38fac6cf 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -1046,6 +1046,13 @@ int wc_DhSetNonBlock(DhKey* key, DhNb* nb) int wc_FreeDhKey(DhKey* key) { if (key) { + #ifdef WC_DH_NONBLOCK + /* An abandoned operation leaves a copy of x in the context. */ + if (key->nb != NULL) { + ForceZero(key->nb, sizeof(DhNb)); + key->nb = NULL; + } + #endif mp_clear(&key->p); mp_clear(&key->g); mp_clear(&key->q); @@ -2593,6 +2600,8 @@ WOLFSSL_LOCAL int wc_DhKeyCopy(DhKey* src, DhKey* dst) return ret; } + /* mp_copy covers only the digits in use; wipe the previous value first. */ + mp_forcezero(&dst->priv); if ((ret = mp_copy(&src->priv, &dst->priv)) != MP_OKAY) { WOLFSSL_MSG("mp_copy error"); return ret; From 10e7278671ce230403209ca2030ac852aa2c714e Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:39:36 -0600 Subject: [PATCH 21/74] ecc: wipe the unblinded scalar copy, ladder state and nb context ecc_put_k() clears key->ku after each use under WOLFSSL_ECC_BLIND_K. The Joye ladders forcezero their scalar copy and wc_ecc_mulmod_ex2 forcezeros the ladder points instead of relying on mp_clear. --- wolfcrypt/src/ecc.c | 73 ++++++++++++++++++++++++++++++++++------- wolfssl/wolfcrypt/ecc.h | 2 ++ 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 9d86e66a02b..f4dce1bd97f 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -445,6 +445,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); @@ -3466,6 +3472,8 @@ 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); #if defined(WOLFSSL_SMALL_STACK) && !defined(WC_NO_CACHE_RESISTANT) XFREE(tmp, NULL, DYNAMIC_TYPE_ECC); #endif @@ -3654,6 +3662,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; } @@ -4131,9 +4142,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 @@ -5168,6 +5181,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; } @@ -5751,6 +5771,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; @@ -7226,6 +7247,7 @@ static int deterministic_sign_helper(const byte* in, word32 inlen, ecc_key* key) key->sign_k_set = 1; } #endif + ecc_put_k(key); } wc_ecc_curve_free(curve); @@ -7460,6 +7482,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) @@ -7723,17 +7746,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 @@ -8674,6 +8698,14 @@ int wc_ecc_free(ecc_key* key) key->dhuk_plain_priv_len = 0; #endif +#ifdef WC_ECC_NONBLOCK + /* An abandoned operation leaves a copy of k in the context. */ + if (key->nb_ctx != NULL) { + ForceZero(key->nb_ctx, sizeof(ecc_nb_ctx_t)); + key->nb_ctx = NULL; + } +#endif + mp_clear(key->pubkey.x); mp_clear(key->pubkey.y); mp_clear(key->pubkey.z); @@ -11156,6 +11188,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); @@ -11446,37 +11479,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 @@ -11563,6 +11606,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); @@ -12131,6 +12175,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 && @@ -12152,6 +12197,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; } @@ -12313,12 +12359,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; } @@ -12343,6 +12391,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; } @@ -12935,7 +12984,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 @@ -17472,7 +17521,7 @@ int wc_ecc_set_nonblock(ecc_key *key, ecc_nb_ctx_t* ctx) ForceZero(key->nb_ctx, sizeof(ecc_nb_ctx_t)); } if (ctx != NULL) { - XMEMSET(ctx, 0, sizeof(ecc_nb_ctx_t)); + ForceZero(ctx, sizeof(ecc_nb_ctx_t)); } key->nb_ctx = ctx; return 0; 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 From d1b776b546155ca0ad25e3f7f02aa7e06c802d9e Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:46:10 -0600 Subject: [PATCH 22/74] HMAC, HKDF: wipe key on SetKey failure, inner digest, partial OKM --- wolfcrypt/src/hmac.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index dd839367698..57780b2d33f 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -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 */ } @@ -1668,6 +1682,8 @@ int wc_HmacFinal(Hmac* hmac, byte* hash) break; } + /* The inner digest is keyed output (ISO/IEC 19790 7.9.7). */ + ForceZero(hmac->innerHash, sizeof(hmac->innerHash)); if (ret == 0) { hmac->innerHashKeyed = 0; } @@ -2014,6 +2030,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 From 6c06a1e1274d493e013d329ba38822cfcbf38185 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:46:10 -0600 Subject: [PATCH 23/74] KDF, PBKDF: wipe partial derived output on failure Also use mp_forcezero for the PKCS12 MP variant and wipe its mp_ints. --- wolfcrypt/src/kdf.c | 37 +++++++++++++++++++++++++++++++++++- wolfcrypt/src/pwdbased.c | 41 ++++++++++++++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 1af792e8847..ff38a241682 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)); @@ -280,6 +284,10 @@ int wc_PRF_TLSv1(byte* digest, word32 digLen, const byte* secret, xorbuf(digest, sha_result, digLen); ForceZero(sha_result, digLen); } + else { + /* digest holds the unmasked MD5 half (ISO/IEC 19790 7.9.7). */ + ForceZero(digest, digLen); + } } ForceZero(sha_result, MAX_PRF_DIG); @@ -857,7 +865,7 @@ 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++) { + for (curBlock = 1; (ret == 0) && (curBlock < blocks); curBlock++) { ret = _HashInit(enmhashId, &hash); if (ret != 0) break; ret = _HashUpdate(enmhashId, &hash, kSzFlat, LENGTH_SZ); @@ -903,6 +911,9 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, _HashFree(enmhashId, &hash); /* hash absorbed the shared secret K (ISO/IEC 19790:2012 7.9.7). */ ForceZero(&hash, sizeof(hash)); + if (ret != 0) { + ForceZero(key, keySz); + } return ret; } @@ -1088,6 +1099,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); @@ -1182,6 +1202,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); @@ -1263,6 +1292,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); @@ -1336,6 +1368,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); diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index cd6ddcb5bc6..b19d796cfac 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -192,8 +192,13 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, wc_MemZero_Check(digest, sizeof(digest)); #endif - if (err != 0) + if (err != 0) { + /* Partial output is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(key, (word32)keyLen); + if (iv != NULL) + ForceZero(iv, (word32)ivLen); return err; + } if (keyOutput != (keyLen + ivLen)) return BUFFER_E; @@ -227,6 +232,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 +369,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) @@ -461,6 +472,8 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, mp_int res[1]; #endif enum wc_HashType hashT; + byte* outStart = output; + int outLen = kLen; (void)heap; @@ -588,7 +601,7 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, ret = MP_ADD_E; if (ret != 0) { - mp_clear(B1); + mp_forcezero(B1); break; } @@ -621,13 +634,13 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, ret = mp_to_unsigned_bin(res, I + i); } - mp_clear(i1); - mp_clear(res); + mp_forcezero(i1); + mp_forcezero(res); if (ret < 0) break; } if (ret < 0) { - mp_clear(B1); + mp_forcezero(B1); break; } @@ -635,7 +648,11 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, XMEMCPY(output, Ai, currentLen); output += currentLen; kLen -= (int)currentLen; - mp_clear(B1); + mp_forcezero(B1); + } + if (ret != 0) { + /* Partial output is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(outStart, (word32)outLen); } #ifdef WOLFSSL_SMALL_STACK @@ -645,8 +662,14 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, XFREE(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); ForceZero(B, WC_MAX_BLOCK_SIZE); XFREE(B, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (B1 != NULL) + ForceZero(B1, sizeof(*B1)); XFREE(B1, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (i1 != NULL) + ForceZero(i1, sizeof(*i1)); XFREE(i1, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (res != NULL) + ForceZero(res, sizeof(*res)); XFREE(res, heap, DYNAMIC_TYPE_TMP_BUFFER); #else ForceZero(Ai, WC_MAX_DIGEST_SIZE); @@ -710,6 +733,8 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, #endif byte* buffer = staticBuffer; enum wc_HashType hashT; + byte* outStart = output; + int outLen = kLen; (void)heap; @@ -838,6 +863,10 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, PKCS12_ByteReverseWords((PKCS12_WORD*)I, (PKCS12_WORD*)I, nBlocks * v); #endif } + if (ret != 0) { + /* Partial output is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(outStart, (word32)outLen); + } ForceZero(B, WC_MAX_BLOCK_SIZE); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); From 28c39b9e96fe27184d117d8e3bf84c4443288622 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:46:10 -0600 Subject: [PATCH 24/74] SE050 hash: wipe the buffered message and the SHA-512 context --- wolfcrypt/src/port/nxp/se050_port.c | 4 ++++ wolfcrypt/src/sha512.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 6201b3d183f..6d86a2d77b3 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1296,6 +1296,10 @@ int se050_hash_final(SE050_HASH_Context* se050Ctx, byte* hash, size_t digestLen, void se050_hash_free(SE050_HASH_Context* se050Ctx) { + if (se050Ctx->msg != NULL) { + /* The buffered message may be keyed (ISO/IEC 19790 7.9.7). */ + ForceZero(se050Ctx->msg, se050Ctx->len); + } XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); se050Ctx->msg = NULL; se050Ctx->len = 0; diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 10626d70ebc..e0bc974d9b7 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -304,7 +304,11 @@ } void wc_Sha512Free(wc_Sha512* sha512) { + if (sha512 == NULL) { + return; + } se050_hash_free(&sha512->se050Ctx); + ForceZero(sha512, sizeof(*sha512)); } #elif defined(STM32_HASH_SHA512) From cd41592e160d36ac59087ccbf1407035df644960 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:46:48 -0600 Subject: [PATCH 25/74] ML-KEM: wipe hash state on failure and the old key before re-keying Also clear s_hat from a failed private key encode and the small compress temporaries. --- wolfcrypt/src/wc_mlkem.c | 48 +++++++++++++++++++++++++++++++++++ wolfcrypt/src/wc_mlkem_poly.c | 22 ++++++++++++++-- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 1aa4973de5d..aa0d95f7d11 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -796,6 +796,21 @@ int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng) #endif /* WC_NO_RNG */ } +/* Wipe a SHA-3 object that a failed step left mid-operation. */ +static void mlkemkey_hash_wipe(wc_Sha3* h) +{ +#ifndef PSOC6_HASH_SHA3 + ForceZero(h->s, sizeof(h->s)); + ForceZero(h->t, sizeof(h->t)); +#ifdef WC_SHA3_SCRATCH_W + ForceZero(h->scratch, sizeof(h->scratch)); +#endif + h->i = 0; +#else + (void)wc_Sha3_256_Reset(h); +#endif +} + /** * Make a ML-KEM key object using random data. * @@ -886,6 +901,8 @@ 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; @@ -894,6 +911,13 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, 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 @@ -1056,6 +1080,8 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, ForceZero(key->priv, sizeof(key->priv)); #endif ForceZero(key->z, sizeof(key->z)); + mlkemkey_hash_wipe(&key->hash); + mlkemkey_hash_wipe(&key->prf); } /* Zeroize the secret seed material in rho||sigma (sigma) before return. */ @@ -1891,6 +1917,10 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, /* FIPS 203 3.3: a failed encapsulation leaves no ciphertext. */ ForceZero(ct, ctSz); } + if ((ret != 0) && (key != NULL)) { + mlkemkey_hash_wipe(&key->hash); + mlkemkey_hash_wipe(&key->prf); + } #ifdef WOLFSSL_MLKEM_KYBER /* msg holds the secret message H(rand) used for Kyber encapsulation; @@ -2340,6 +2370,10 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, wc_MemZero_Check(msg, sizeof(msg)); wc_MemZero_Check(kr, sizeof(kr)); #endif + if ((ret != 0) && (key != NULL)) { + mlkemkey_hash_wipe(&key->hash); + mlkemkey_hash_wipe(&key->prf); + } #endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ return ret; @@ -2491,6 +2525,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); } @@ -2927,6 +2968,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. */ @@ -3244,6 +3289,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 6b3cbe5d129..cd1a7d70a65 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -6275,6 +6275,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. */ @@ -6290,7 +6295,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); @@ -6363,6 +6367,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. @@ -6427,6 +6436,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++) { @@ -6440,7 +6450,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); @@ -6487,6 +6496,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. @@ -6914,6 +6926,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. @@ -7017,6 +7032,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. From a8c90d35fb56986b7db4dbe9e70dab5516ac5148 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:46:48 -0600 Subject: [PATCH 26/74] ML-DSA: wipe rho', rejected candidates and keys left by failures Clear the old key before writing a new one, the t region in CheckKey, and the SHAKE state when key generation or signing fails. --- wolfcrypt/src/wc_mldsa.c | 118 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index 17a9114d191..5ebf059046c 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -9367,6 +9367,57 @@ static int mldsa_calc_t(sword32* t, const sword32* a, sword32* s1, #endif /* !WOLFSSL_MLDSA_VERIFY_ONLY */ +#ifndef WOLFSSL_MLDSA_VERIFY_ONLY +#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; +} + +/* 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)); +#ifdef WC_SHA3_SCRATCH_W + ForceZero(shake->scratch, sizeof(shake->scratch)); +#endif + shake->i = 0; +#else + (void)wc_Shake256_Reset(shake); +#endif +} +#endif /* !WOLFSSL_MLDSA_VERIFY_ONLY */ + /******************************************************************************/ #ifndef WOLFSSL_MLDSA_NO_MAKE_KEY @@ -9598,6 +9649,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 +9756,13 @@ 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); + /* 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); @@ -9761,6 +9820,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 +9848,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 +9922,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 +9930,11 @@ 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); + /* 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); @@ -9927,6 +9998,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 +10257,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 +10326,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; @@ -10478,10 +10555,16 @@ static int mldsa_sign_with_seed_mu(wc_MlDsaKey* key, } } + if (WC_VAR_OK(w1e)) { + ForceZero(w1e, MLDSA_MAX_W1_ENC_SZ); + } 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; @@ -10525,6 +10608,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; @@ -11160,6 +11246,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; @@ -11190,6 +11281,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; @@ -14406,9 +14500,7 @@ int wc_MlDsaKey_SetParams(wc_MlDsaKey* key, byte level) #if !defined(WOLFSSL_MLDSA_DYNAMIC_KEYS) && \ !defined(WOLFSSL_MLDSA_ASSIGN_KEY) && !defined(WOLFSSL_MLDSA_VERIFY_ONLY) - if (key->prvKeySet) { - ForceZero(key->k, sizeof(key->k)); - } + 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; @@ -14963,9 +15055,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); @@ -15284,6 +15376,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) @@ -15330,8 +15423,11 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, #ifdef WOLFSSL_MLDSA_ASSIGN_KEY key->k = priv; #else + /* Do not write the new key over the old one. */ + ForceZero(key->k, privSz); XMEMCPY(key->k, priv, privSz); #endif + copied = 1; } /* Allocate and create cached values. The caches only feed the @@ -15382,6 +15478,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); } @@ -15390,6 +15487,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; } From 397d67b0318bd42b1adcbf2a5592ff783bf2653d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:50:04 -0600 Subject: [PATCH 27/74] AES: wipe GCM, CFB and XTS temporaries, finished streams and failed keys GCM C and asm-wrapper lanes clear the keystream block, E(J0) and the GHASH accumulator at one exit; streaming Final drops streamData. CFB decrypt wipes its keystream buffers, XTS wipes the masked blocks, a failed SetKey keeps no key copy, and wc_GmacFree zeroises a Gmac. --- doc/dox_comments/header_files/aes.h | 25 +++++ wolfcrypt/src/aes.c | 153 +++++++++++++++++++--------- wolfcrypt/src/cmac.c | 2 +- wolfssl/wolfcrypt/aes.h | 1 + 4 files changed, 132 insertions(+), 49 deletions(-) diff --git a/doc/dox_comments/header_files/aes.h b/doc/dox_comments/header_files/aes.h index f39d1c5ee68..9d25e019639 100644 --- a/doc/dox_comments/header_files/aes.h +++ b/doc/dox_comments/header_files/aes.h @@ -645,6 +645,31 @@ int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, byte* authTag, word32 authTagSz); +/*! + \ingroup AES + \brief This function zeroizes the key schedule and GHASH state held by a + Gmac object. Call it when the object is no longer needed. + + \return 0 on success + \return BAD_FUNC_ARG if gmac is NULL + + \param gmac pointer to the Gmac object to clear + + _Example_ + \code + Gmac gmac; + wc_AesInit(&gmac.aes, NULL, INVALID_DEVID); + wc_GmacSetKey(&gmac, key, sizeof(key)); + wc_GmacUpdate(&gmac, iv, sizeof(iv), authIn, sizeof(authIn), tag, + sizeof(tag)); + wc_GmacFree(&gmac); + \endcode + + \sa wc_GmacSetKey + \sa wc_GmacUpdate +*/ +int wc_GmacFree(Gmac* gmac); + /*! \ingroup AES \brief This function sets the key for an AES object using CCM diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 5ab9ae1efd0..020db833fdc 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -3966,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 @@ -5963,7 +5964,22 @@ 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 { + /* A failed setup keeps no copy of the key. */ + ForceZero(aes->key, sizeof(aes->key)); + #ifdef WC_C_DYNAMIC_FALLBACK + ForceZero(aes->key_C_fallback, sizeof(aes->key_C_fallback)); + #endif + #if defined(WOLF_CRYPTO_CB) || (defined(WOLFSSL_DEVCRYPTO) && \ + (defined(WOLFSSL_DEVCRYPTO_AES) || defined(WOLFSSL_DEVCRYPTO_CBC))) || \ + (defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES)) || \ + defined(WOLFSSL_KCAPI_AES) || defined(WOLFSSL_NXP_HASHCRYPT_AES) + ForceZero(aes->devKey, sizeof(aes->devKey)); + #endif + } #ifndef WOLF_CRYPTO_CB_ONLY_AES /* A claimed key runs in software only if the callback built a * schedule for it. */ @@ -8901,6 +8917,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 @@ -9257,9 +9274,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) { @@ -11764,7 +11779,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 @@ -11784,7 +11799,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; @@ -11798,7 +11813,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; @@ -11810,7 +11825,7 @@ 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) { @@ -11818,7 +11833,7 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( ret = AesEncrypt_preFetchOpt(aes, initialCounter, scratch, &did_prefetches); if (ret != 0) - return ret; + goto done; xorbuf(authTag, scratch, authTagSz); #ifdef OPENSSL_EXTRA if (!in && !sz) @@ -11827,6 +11842,9 @@ 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); return ret; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -11944,6 +11962,9 @@ 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); return 0; } @@ -12609,7 +12630,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 done; xorbuf(Tprime, EKY0, sizeof(Tprime)); #ifdef WC_AES_GCM_DEC_AUTH_EARLY /* ConstantCompare returns the cumulative bitwise or of the bitwise xor of @@ -12622,7 +12643,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 @@ -12643,7 +12664,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 done; } /* process remainder using partial handling */ #endif @@ -12664,7 +12685,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 done; } xorbuf(out, c, WC_AES_BLOCK_SIZE * blocks); c += WC_AES_BLOCK_SIZE * blocks; @@ -12677,7 +12698,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 done; xorbufout(p, scratch, c, WC_AES_BLOCK_SIZE); #endif p += WC_AES_BLOCK_SIZE; @@ -12689,7 +12710,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( IncrementGcmCounter(counter); ret = wc_AesEncrypt(aes, counter, scratch); if (ret != 0) - return ret; + goto done; xorbuf(scratch, c, partial); XMEMCPY(p, scratch, partial); } @@ -12718,6 +12739,10 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( out[i] &= (byte)~mask; } #endif +done: + /* Tag mask E(J0) and the last keystream block. */ + ForceZero(EKY0, WC_AES_BLOCK_SIZE); + ForceZero(scratch, WC_AES_BLOCK_SIZE); return ret; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -12727,6 +12752,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]; @@ -12827,11 +12853,13 @@ 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; + return ret; } #endif @@ -13297,8 +13325,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; } @@ -15405,6 +15431,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)) { @@ -15433,6 +15460,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) { @@ -15463,6 +15491,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; } @@ -15579,6 +15612,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)) { @@ -15602,6 +15636,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) { @@ -15637,6 +15672,12 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz) } } + 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; @@ -15884,6 +15925,16 @@ int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, authTag, authTagSz, authIn, authInSz); } +int wc_GmacFree(Gmac* gmac) +{ + if (gmac == NULL) { + return BAD_FUNC_ARG; + } + + wc_AesFree(&gmac->aes); + return 0; +} + #endif /* HAVE_AESGCM */ #ifdef HAVE_AESCCM @@ -17669,7 +17720,11 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, in += nbytes; sz -= nbytes; } + ForceZero(ks, WC_AES_BLOCK_SIZE); } +#ifndef WOLFSSL_SMALL_STACK + ForceZero(tmp, sizeof(tmp)); +#endif } #endif while (sz >= WC_AES_BLOCK_SIZE) { @@ -17700,6 +17755,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 @@ -19257,6 +19315,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; @@ -19285,19 +19344,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 */ @@ -19389,6 +19453,7 @@ 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(); @@ -19398,8 +19463,7 @@ static int AesXtsEncryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, 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 @@ -19412,14 +19476,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); @@ -19444,12 +19505,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); @@ -19469,7 +19528,9 @@ 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); return ret; } @@ -19981,6 +20042,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 */ @@ -19996,8 +20059,7 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, 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 @@ -20007,14 +20069,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); @@ -20040,9 +20099,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; @@ -20059,8 +20115,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); @@ -20073,8 +20128,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); @@ -20082,14 +20137,16 @@ 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); return ret; } diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index c3ac738eb52..035ac42868f 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 diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 4122374c757..8a3cd5881ed 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -791,6 +791,7 @@ WOLFSSL_API WARN_UNUSED_RESULT int wc_AesGcmDecryptFinal(Aes* aes, WOLFSSL_API int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, byte* authTag, word32 authTagSz); + WOLFSSL_API int wc_GmacFree(Gmac* gmac); #ifndef WC_NO_RNG WOLFSSL_API int wc_Gmac(const byte* key, word32 keySz, byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, From 07ff724c68b3aa735275e935d3d804abfbf8d1ab Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:37:11 -0600 Subject: [PATCH 28/74] random: uninstantiate the DRBG after any failed instantiate Hash_DRBG_Init derives V before C, so a failure in the second Hash_df left V resident. In the WOLFSSL_NO_MALLOC layout the state lives in the WC_RNG and wc_FreeRng skipped it once rng->drbg was NULL. --- wolfcrypt/src/random.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 7e6d6910abe..6b86656fbdb 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -3930,6 +3930,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, @@ -3953,8 +3956,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 */ @@ -7928,6 +7929,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 @@ -7977,6 +7982,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. */ From 5e7dac3625846bd5bbba9f275ff7913b55a5d832 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 13:46:20 -0600 Subject: [PATCH 29/74] random, wolfentropy: wipe seed, stir and output temporaries Rejected seed banks and purged stir accumulators are wiped under a claim instead of being overwritten by the next fill, a purged consumer claim stays closed until its own wipe, and the pool reader, the RDSEED self-check, the PIC32MZ staging word, the entropy startup samples and the memuse state are cleared when done. --- wolfcrypt/src/random.c | 78 +++++++++++++++++++++---------------- wolfcrypt/src/rng_bank.c | 4 +- wolfcrypt/src/wolfentropy.c | 19 ++++++--- 3 files changed, 59 insertions(+), 42 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 6b86656fbdb..d2b6a74d63b 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,27 @@ 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. 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) + 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) + return; + ForceZero(rng->nextStir, (word32)sizeof(rng->nextStir)); + 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 +1289,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 @@ -5120,8 +5136,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); @@ -5829,6 +5844,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 @@ -6825,16 +6842,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); @@ -9118,28 +9130,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; } @@ -10019,6 +10028,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/rng_bank.c b/wolfcrypt/src/rng_bank.c index 5ef2b6867f6..25b1b4b8ab1 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -3745,9 +3745,9 @@ WOLFSSL_TEST_VIS int wc_RNG_DRBG_Reseed_Now( * minimal generate at the forced counter performs the module's own * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on * success the fresh reseed counter is 2 rather than 1. scratch holds - * only discarded output bytes; XMEMSET suffices for it here. */ + * discarded output bytes. */ ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); - XMEMSET(scratch, 0, sizeof(scratch)); + ForceZero(scratch, sizeof(scratch)); if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { /* On the legacy boundary, nonce incorporation is a separate diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c index 6f1c1590d30..f4639f8e4cf 100644 --- a/wolfcrypt/src/wolfentropy.c +++ b/wolfcrypt/src/wolfentropy.c @@ -402,6 +402,8 @@ static void Entropy_StopThread(void) /* State to update that is multiple cache lines long. */ static word64 entropy_state[ENTROPY_NUM_WORDS + EXTRA_ENTROPY_WORDS] = {0}; +/* Digest of the state, used as the next update indices. */ +static byte entropy_idx[WC_SHA3_256_DIGEST_SIZE]; /* Using memory will take different amount of times depending on the CPU's * caches and business. @@ -422,7 +424,6 @@ static word64 entropy_state[ENTROPY_NUM_WORDS + EXTRA_ENTROPY_WORDS] = {0}; static int Entropy_MemUse(void) { int i; - static byte d[WC_SHA3_256_DIGEST_SIZE]; int j; int ret; @@ -433,13 +434,13 @@ static int Entropy_MemUse(void) if (ret != 0) return ret; /* Get pseudo-random indices. */ - ret = wc_Sha3_256_Final(&entropyHash, d); + ret = wc_Sha3_256_Final(&entropyHash, entropy_idx); if (ret != 0) return ret; for (i = 0; i < ENTROPY_NUM_64BIT_WORDS; i++) { /* Choose a 64-bit word from a pseudo-random block.*/ - int idx = ((int)d[i] << ENTROPY_BLOCK_SZ) + + int idx = ((int)entropy_idx[i] << ENTROPY_BLOCK_SZ) + (j << ENTROPY_OFFSET_SHIFTING); /* Update a pseudo-random 64-bit word with a pseudo-random value. */ entropy_state[idx] += Entropy_TimeHiRes(); @@ -608,7 +609,7 @@ static void Entropy_HealthTest_Repetition_Reset(void) /* No previous stored. */ rep_have_prev = 0; /* Clear previous. */ - rep_prev_noise = 0; + ForceZero(&rep_prev_noise, sizeof(rep_prev_noise)); } /* Test sample value with repetition test. @@ -687,9 +688,9 @@ static word16 prop_samples[PROP_WINDOW_SIZE]; static void Entropy_HealthTest_Proportion_Reset(void) { /* Clear out samples. */ - XMEMSET(prop_samples, 0, sizeof(prop_samples)); + ForceZero(prop_samples, sizeof(prop_samples)); /* Clear out counts. */ - XMEMSET(prop_cnt, 0, sizeof(prop_cnt)); + ForceZero(prop_cnt, sizeof(prop_cnt)); /* Clear stored count. */ prop_total = 0; /* Reset first and last index for samples. */ @@ -802,6 +803,8 @@ static int Entropy_HealthTest_Startup(void) /* Failing test only resets its own data. */ Entropy_HealthTest_Reset(); } + /* Raw samples of the live source. */ + ForceZero(initial, sizeof(initial)); return ret; } @@ -1061,6 +1064,10 @@ void Entropy_Final(void) #endif /* Clear health test data. */ Entropy_HealthTest_Reset(); + /* Raw timing state, its digest and the last sample time. */ + ForceZero(entropy_state, sizeof(entropy_state)); + ForceZero(entropy_idx, sizeof(entropy_idx)); + ForceZero(&entropy_last_time, sizeof(entropy_last_time)); /* No longer initialized. */ entropy_memuse_initialized = 0; } From b17a4bffc434e89fab8d1e8954302f41e60e3c46 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 14:02:34 -0600 Subject: [PATCH 30/74] SLH-DSA: wipe secret hash state and scratch once per operation The key's SHAKE or SHA-256 PRF object, md, the FORS indices and the WOTS+ message are wiped at the end of keygen, the PCT and every sign path; a failed import or root computation clears the seeds. --- wolfcrypt/src/wc_slhdsa.c | 53 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index f9c09077c1e..cd57f4e6368 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -1385,6 +1385,7 @@ static int slhdsakey_prf_msg_sha2(SlhDsaKey* key, const byte* sk_prf, if (ret == 0) { XMEMCPY(hash, digest, n); } + ForceZero(digest, sizeof(digest)); return ret; } @@ -5275,8 +5276,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 +5340,7 @@ static int slhdsakey_wots_sign(SlhDsaKey* key, const byte* m, wc_MemZero_Check(sk, n); #endif } + ForceZero(msg, sizeof(msg)); return ret; } @@ -7771,6 +7775,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; } @@ -9081,6 +9086,31 @@ 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)) { + if (key->hash.sha2.sha256_inited) { + wc_Sha256Free(&key->hash.sha2.sha256); + key->hash.sha2.sha256_inited = 0; + } + } + else +#endif + { + wc_Shake* shake = &key->hash.shk.shake; + + ForceZero(shake->s, sizeof(shake->s)); + ForceZero(shake->t, sizeof(shake->t)); +#ifdef WC_SHA3_SCRATCH_W + ForceZero(shake->scratch, sizeof(shake->scratch)); +#endif + shake->i = 0; + } +} + /* Compute the public key root from the seeds already staged in the key. * * FIPS 205, Section 9.1, Algorithm 18, steps 1 to 3. @@ -9121,6 +9151,7 @@ static int slhdsakey_compute_root(SlhDsaKey* key) if (ret == 0) { key->flags = WC_SLHDSA_FLAG_BOTH_KEYS; } + slhdsakey_wipe_secret_hash(key); return ret; } @@ -9221,6 +9252,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 */ @@ -9260,6 +9294,7 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, } ForceZero(pct_root, sizeof(pct_root)); } + slhdsakey_wipe_secret_hash(key); /* 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 @@ -9337,6 +9372,7 @@ static int slhdsakey_sign(SlhDsaKey* key, byte* md, byte* sig) * (ISO/IEC 19790:2012 7.9.7). */ ForceZero(sigFors, key->params->k * (1 + key->params->a) * n); } + slhdsakey_wipe_secret_hash(key); return ret; } @@ -9447,6 +9483,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; @@ -9591,6 +9629,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; @@ -10717,6 +10757,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; @@ -11216,7 +11258,6 @@ int wc_SlhDsaKey_ImportPrivate(SlhDsaKey* key, const byte* priv, word32 privLen) else { /* 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; /* 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) @@ -11224,6 +11265,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; From 447da79655c9dcbf3e18df9fbf50bde31fed7b0f Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 14:02:34 -0600 Subject: [PATCH 31/74] LMS: wipe the private key on failed keygen, reload and sign A failed sign also clears sig, and a SEED or I slot is wiped before the next value is derived into it. --- wolfcrypt/src/wc_lms.c | 49 +++++++++++++++++++++++++++++++++++++ wolfcrypt/src/wc_lms_impl.c | 5 ++++ 2 files changed, 54 insertions(+) diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c index d63aa89630b..9314ee36cb8 100644 --- a/wolfcrypt/src/wc_lms.c +++ b/wolfcrypt/src/wc_lms.c @@ -1090,6 +1090,34 @@ void wc_LmsKey_Free(LmsKey* key) } #ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Length of the expanded private key data. */ +static word32 wc_lmskey_priv_data_len(const LmsParams* params) +{ + word32 len = LMS_PRIV_DATA_LEN(params->levels, params->height, params->p, + params->rootLevels, params->cacheBits, params->hash_len); + +#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE + len += HSS_PRIVATE_KEY_LEN(params->hash_len); +#endif + return len; +} + +#ifndef WOLF_CRYPTO_CB_ONLY_LMS +/* A failed generation, reload or sign leaves no private key resident + * (ISO/IEC 19790:2012 7.9.7). */ +static void wc_lmskey_wipe_priv(LmsKey* key) +{ + if (key->priv_data != NULL) { + ForceZero(key->priv_data, wc_lmskey_priv_data_len(key->params)); + } + ForceZero(key->priv_raw, HSS_MAX_PRIVATE_KEY_LEN); + ForceZero(&key->priv, sizeof(HssPrivKey)); + if (key->state != WC_LMS_STATE_NOSIGS) { + key->state = WC_LMS_STATE_BAD; + } +} +#endif /* !WOLF_CRYPTO_CB_ONLY_LMS */ + /* Set the write private key callback to the LMS key structure. * * The callback must be able to write/update the private key to @@ -1213,6 +1241,7 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) #endif #ifndef WOLF_CRYPTO_CB_ONLY_LMS word32 priv_data_len = 0; + int started = 0; #endif /* Validate parameters. */ @@ -1314,6 +1343,7 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) /* Initialize working state for use. */ ret = wc_lmskey_state_init(state, key->params); if (ret == 0) { + started = 1; /* Make the HSS key. */ ret = wc_hss_make_key(state, rng, key->priv_raw, &key->priv, key->priv_data, key->pub); @@ -1360,6 +1390,9 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) key->state = WC_LMS_STATE_OK; key->pubSet = 1; } + else if (started) { + wc_lmskey_wipe_priv(key); + } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; @@ -1391,6 +1424,7 @@ int wc_LmsKey_Reload(LmsKey* key) int ret = 0; #ifndef WOLF_CRYPTO_CB_ONLY_LMS word32 priv_data_len = 0; + int started = 0; #endif /* Validate parameter. */ @@ -1456,7 +1490,10 @@ int wc_LmsKey_Reload(LmsKey* key) /* Load private key. */ #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE const LmsParams* params = key->params; + #endif + started = 1; + #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE rv = key->read_private_key(key->priv_data, priv_data_len, key->context); #else rv = key->read_private_key(key->priv_raw, @@ -1513,6 +1550,9 @@ int wc_LmsKey_Reload(LmsKey* key) /* Update state. */ key->state = WC_LMS_STATE_OK; } + else if (started) { + wc_lmskey_wipe_priv(key); + } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; @@ -1568,6 +1608,9 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, int msgSz) { int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_LMS + int started = 0; +#endif /* Validate parameters. A NULL msg is valid for the empty message * (msgSz == 0), per RFC 8554 which permits empty messages. */ @@ -1661,6 +1704,7 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, /* Set the key state to bad by default. State is presumed bad * unless a correct sign and write operation happen together. */ key->state = WC_LMS_STATE_BAD; + started = 1; /* Sign message. */ ret = wc_hss_sign(state, key->priv_raw, &key->priv, key->priv_data, msg, (word32)msgSz, sig); @@ -1710,6 +1754,11 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, * to sign with again. */ key->state = WC_LMS_STATE_OK; } + else if (started) { + /* A one-time key may be in sig; the NV copy is the key now. */ + ForceZero(sig, key->params->sig_len); + wc_lmskey_wipe_priv(key); + } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index a5b1c3c0637..f817fe494ea 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -3289,6 +3289,8 @@ static void wc_lms_priv_state_store(const LmsParams* params, static void wc_lms_priv_state_copy(const LmsParams* params, LmsPrivState* dst, const LmsPrivState* src) { + ForceZero(dst->auth_path, LMS_PRIV_STATE_LEN(params->height, + params->rootLevels, params->cacheBits, params->hash_len)); XMEMCPY(dst->auth_path, src->auth_path, LMS_PRIV_STATE_LEN(params->height, params->rootLevels, params->cacheBits, params->hash_len)); dst->stack.offset = src->stack.offset; @@ -4549,6 +4551,8 @@ static int wc_hss_expand_private_key(LmsState* state, byte* priv, c32toa(q32, priv); if (!skip) { + /* Never destroy a SEED by writing the next one over it. */ + ForceZero(priv + LMS_Q_LEN, params->hash_len + LMS_I_LEN); /* Derive SEED and I into private key. */ ret = wc_hss_derive_seed_i(state, priv_seed_i + params->hash_len, priv_seed_i, priv_q, priv + LMS_Q_LEN); @@ -4597,6 +4601,7 @@ static int wc_lms_next_subtree_init(LmsState* state, LmsPrivState* privState, privState->leaf.offset = 0; /* Derive SEED and I for next tree. */ + ForceZero(priv + LMS_Q_LEN, params->hash_len + LMS_I_LEN); ret = wc_hss_derive_seed_i(state, priv_i, priv_seed, priv_q, priv + LMS_Q_LEN); if (ret == 0) { From da225caacc5232112f597f00bd774318474d7c20 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 7 Oct 2026 15:02:42 -0600 Subject: [PATCH 32/74] ML-KEM: wipe the implicit-rejection flag in decapsulation Reported against master: FIPS 203 6.3 requires the flag destroyed before Decaps_internal terminates; cmp, msg and kr were already wiped. --- wolfcrypt/src/wc_mlkem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index aa0d95f7d11..b8d488451ee 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -2366,6 +2366,8 @@ 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)); From 6b5101ceb5bffef88e9e52a7df3c310b4fb5163f Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 12:04:05 -0600 Subject: [PATCH 33/74] HMAC: clear the key when async hardware key setup fails --- wolfcrypt/src/hmac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index 57780b2d33f..c646c934b05 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -1131,7 +1131,9 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, /* update key length */ hmac->keyLen = (word16)length; - return ret; + /* A failure falls through to the key cleanup below. */ + if (ret == 0) + return ret; } /* no need to pad below */ #endif From 3d7a33989375c9e33d92738f97f57ed85f0f616a Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 12:23:13 -0600 Subject: [PATCH 34/74] AES-GCM, CMAC, ECIES, X9.63: wipe expected tags, H copies and KDF blocks --- wolfcrypt/src/aes.c | 9 ++++++++- wolfcrypt/src/cmac.c | 2 ++ wolfcrypt/src/ecc.c | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 020db833fdc..def0c3a96c4 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -10768,6 +10768,8 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #endif XMEMCPY(s, x, sSz); ForceZero(x, sizeof(x)); + /* bigH is a copy of the hash subkey H. */ + ForceZero(bigH, sizeof(bigH)); } #endif /* !FREESCALE_LTC_AES_GCM */ @@ -11076,6 +11078,8 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #endif XMEMCPY(s, x, sSz); ForceZero(x, sizeof(x)); + /* bigH is a copy of the hash subkey H. */ + ForceZero(bigH, sizeof(bigH)); } #ifdef WOLFSSL_AESGCM_STREAM @@ -12740,9 +12744,10 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( } #endif done: - /* Tag mask E(J0) and the last keystream block. */ + /* 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); return ret; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -15669,6 +15674,8 @@ 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)); } } diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index 035ac42868f..527a7077a95 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -710,6 +710,8 @@ 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)); return ret; } diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index f4dce1bd97f..8c575cd7b64 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -16600,6 +16600,8 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, ret = HASH_TYPE_E; WOLFSSL_MSG("ECC Decrypt HMAC Check failed!"); } + /* On a mismatch this is a valid tag for the input. */ + ForceZero(verify, sizeof(verify)); wc_HmacFree(hmac); } @@ -17461,6 +17463,12 @@ int wc_X963_KDF(enum wc_HashType type, const byte* secret, word32 secretSz, remaining -= copySz; outIdx += copySz; } + /* Derived key material, including the unused tail of the last block. */ + ForceZero(tmp, sizeof(tmp)); + if (ret != 0) { + /* Partial output is not released (ISO/IEC 19790 7.9.7). */ + ForceZero(out, outSz); + } wc_HashFree(hash, type); From 77215354551e61322ae47df1a535d508328ceada Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 12:23:13 -0600 Subject: [PATCH 35/74] EdDSA, X25519: wipe the projective points of the scalar multiplication --- wolfcrypt/src/curve25519.c | 2 ++ wolfcrypt/src/ed25519.c | 4 ++++ wolfcrypt/src/ed448.c | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 5d84e36374e..48e9a563b6e 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -223,6 +223,8 @@ static int curve25519_make_pub_ex(int public_size, byte* pub, int private_size, lm_invert(A.T, A.T); lm_mul(pub, A.X, A.T); #endif + /* A is the private scalar times the base point. */ + ForceZero(&A, sizeof(A)); ret = 0; } #elif defined(CURVED25519_X64) || (defined(WOLFSSL_ARMASM) && \ diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index d598d6d267c..f25447f5730 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -419,6 +419,8 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, #else ge_scalarmult_base(&A, az); ge_p3_tobytes(pubKey, &A); + /* Projective A leaks bits of the private scalar. */ + ForceZero(&A, sizeof(A)); #endif if (storePub) @@ -720,6 +722,8 @@ int wc_ed25519_sign_msg_ex(const byte* in, word32 inLen, byte* out, r and B */ ge_scalarmult_base(&R,nonce); ge_p3_tobytes(out,&R); + /* Projective R leaks bits of the nonce. */ + ForceZero(&R, sizeof(R)); #endif } diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 913c5f859b4..cc524392e94 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -449,6 +449,8 @@ int wc_ed448_make_public(ed448_key* key, unsigned char* pubKey, word32 pubKeySz) } /* 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)); return ret; @@ -763,6 +765,8 @@ 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(nonce, sizeof(nonce)); wc_MemZero_Check(az, sizeof(az)); From 0982e40aea0c537dda9e0831587548b2989a630c Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 12:23:13 -0600 Subject: [PATCH 36/74] RSA, ML-DSA, entropy: clear padding flags, rejected hint count, raw sample --- wolfcrypt/src/rsa.c | 7 +++++++ wolfcrypt/src/wc_mldsa.c | 4 ++++ wolfcrypt/src/wolfentropy.c | 6 ++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index ca0a771d0f3..e1a8668633f 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -1950,6 +1950,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 */ @@ -2199,6 +2201,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 diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index 5ebf059046c..c716f880579 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -6420,6 +6420,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 +6440,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 diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c index f4639f8e4cf..91c14138d60 100644 --- a/wolfcrypt/src/wolfentropy.c +++ b/wolfcrypt/src/wolfentropy.c @@ -522,11 +522,13 @@ static int Entropy_GetNoise(unsigned char* noise, int samples) for (i = 0; i < samples; i++) { ret = Entropy_GetSample(&sample); if (ret != 0) - return ret; + break; noise[i] = (byte)sample; } + /* Raw noise sample (ISO/IEC 19790 7.9.7). */ + ForceZero(&sample, sizeof(sample)); - return 0; + return ret; } /* Mutex to prevent multiple callers requesting entropy operations at the From 5dad1d180656e943913e3093ec520e2e8ee4bebe Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 14:35:16 -0600 Subject: [PATCH 37/74] Drop changes outside the FIPS boundary --- doc/dox_comments/header_files/aes.h | 25 ------------------------- tests/api/test_digest.h | 4 ++-- wolfcrypt/src/misc.c | 4 ++-- wolfcrypt/src/port/nxp/se050_port.c | 4 ---- wolfcrypt/src/sha.c | 2 +- wolfcrypt/src/sha256.c | 4 ++-- wolfcrypt/test/test.c | 10 ---------- wolfssl/wolfcrypt/wc_port.h | 15 --------------- 8 files changed, 7 insertions(+), 61 deletions(-) diff --git a/doc/dox_comments/header_files/aes.h b/doc/dox_comments/header_files/aes.h index 9d25e019639..f39d1c5ee68 100644 --- a/doc/dox_comments/header_files/aes.h +++ b/doc/dox_comments/header_files/aes.h @@ -645,31 +645,6 @@ int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, byte* authTag, word32 authTagSz); -/*! - \ingroup AES - \brief This function zeroizes the key schedule and GHASH state held by a - Gmac object. Call it when the object is no longer needed. - - \return 0 on success - \return BAD_FUNC_ARG if gmac is NULL - - \param gmac pointer to the Gmac object to clear - - _Example_ - \code - Gmac gmac; - wc_AesInit(&gmac.aes, NULL, INVALID_DEVID); - wc_GmacSetKey(&gmac, key, sizeof(key)); - wc_GmacUpdate(&gmac, iv, sizeof(iv), authIn, sizeof(authIn), tag, - sizeof(tag)); - wc_GmacFree(&gmac); - \endcode - - \sa wc_GmacSetKey - \sa wc_GmacUpdate -*/ -int wc_GmacFree(Gmac* gmac); - /*! \ingroup AES \brief This function sets the key for an AES object using CCM diff --git a/tests/api/test_digest.h b/tests/api/test_digest.h index db458652422..78ce499dd80 100644 --- a/tests/api/test_digest.h +++ b/tests/api/test_digest.h @@ -574,7 +574,7 @@ do { \ ExpectIntEQ(wc_##name##_Final(&dst, hashDst, WC_##upper##_COUNT * 8), 0); \ ExpectBufEQ(hashSrc, emptyHash, WC_##upper##_COUNT * 8); \ ExpectBufEQ(hashDst, emptyHash, WC_##upper##_COUNT * 8); \ - wc_##name##_Free(&dst); \ + wc_##name##_Free(&src); \ \ /* Test buffered data is copied. */ \ ExpectIntEQ(wc_##name##_Update(&src, (byte*)"abc", 3), 0); \ @@ -583,7 +583,7 @@ do { \ ExpectIntEQ(wc_##name##_Final(&dst, hashDst, WC_##upper##_COUNT * 8), 0); \ ExpectBufEQ(hashSrc, abcHash, WC_##upper##_COUNT * 8); \ ExpectBufEQ(hashDst, abcHash, WC_##upper##_COUNT * 8); \ - wc_##name##_Free(&dst); \ + wc_##name##_Free(&src); \ \ /* Test count of length is copied. */ \ ExpectIntEQ(wc_##name##_Update(&src, data, sizeof(data)), 0); \ diff --git a/wolfcrypt/src/misc.c b/wolfcrypt/src/misc.c index 26e1fe52356..3edf2e18e50 100644 --- a/wolfcrypt/src/misc.c +++ b/wolfcrypt/src/misc.c @@ -788,8 +788,8 @@ WC_MISC_STATIC WC_INLINE void xorbuf(void* buf, const void* mask, word32 count) with zeros. It ensures compiler optimization doesn't skip it. */ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) { -#ifdef WC_BARRIER_DATA_WEAK - /* Volatile stores cannot be dropped as dead when no barrier backs them. */ +#if !defined(__CC_ARM) && (!defined(__GNUC__) || defined(WOLFSSL_NO_ASM)) + /* No data barrier backs the wipe; volatile stores cannot be dropped. */ typedef volatile byte wc_wipe_byte; typedef volatile unsigned long wc_wipe_word; #else diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 6d86a2d77b3..6201b3d183f 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1296,10 +1296,6 @@ int se050_hash_final(SE050_HASH_Context* se050Ctx, byte* hash, size_t digestLen, void se050_hash_free(SE050_HASH_Context* se050Ctx) { - if (se050Ctx->msg != NULL) { - /* The buffered message may be keyed (ISO/IEC 19790 7.9.7). */ - ForceZero(se050Ctx->msg, se050Ctx->len); - } XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); se050Ctx->msg = NULL; se050Ctx->len = 0; diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 2b5a10cf9c0..2aae9853e82 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -491,7 +491,7 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) static int Transform(wc_Sha* sha, const byte* data) { - word32* WC_RESTRICT W = sha->W; + word32* W = sha->W; /* Copy context->state[] to working vars */ word32 a = sha->digest[0]; diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index c84b31c2fb9..d37f159fa5b 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -1823,7 +1823,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, if (W == NULL) return MEMORY_E; #elif defined(WC_SHA256_W_IN_CTX) - word32* WC_RESTRICT W = sha256->Wbuf; + word32* W = sha256->Wbuf; #else word32 W[WC_SHA256_BLOCK_SIZE]; #endif @@ -1900,7 +1900,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, int j; #endif #ifdef WC_SHA256_W_IN_CTX - word32* WC_RESTRICT W = sha256->Wbuf; + word32* W = sha256->Wbuf; #else word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; #endif diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 7f2abe10595..2ad6034e8a0 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -41267,16 +41267,6 @@ static wc_test_ret_t rsa_even_mod_test(WC_RNG* rng, RsaKey* key) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_even_mod); } -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) - /* v7+ wipes out on the failed encrypt; check, then refill in range so - * the decrypt still reaches the even modulus private op. */ - for (idx = 0; idx < outSz; idx++) { - if (out[idx] != 0) { - ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa_even_mod); - } - } - XMEMSET(out, 0x01, outSz); -#endif #endif /* WOLFSSL_RSA_VERIFY_ONLY */ #ifndef WOLFSSL_RSA_PUBLIC_ONLY diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index de46db54585..546b95f71c2 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -106,19 +106,6 @@ #endif #endif /* !WC_DEPRECATED */ -/* Pointer qualifier promising no aliasing, for scratch kept in a context. */ -#ifndef WC_RESTRICT - #if defined(__GNUC__) || defined(__clang__) - #define WC_RESTRICT __restrict__ - #elif defined(_MSC_VER) - #define WC_RESTRICT __restrict - #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - #define WC_RESTRICT restrict - #else - #define WC_RESTRICT - #endif -#endif - /* Use inlining if compiler allows -- omit the static attribute here, so that * WC_INLINE can be used on functions that are instantiated both inline in the * TU, and callable from outside the TU. @@ -2174,8 +2161,6 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); #define WC_BARRIER_DATA(ptr) \ __asm__ __volatile__("" : : "r"(ptr) : "memory") #else - /* No real data barrier here: ForceZero falls back to volatile stores. */ - #define WC_BARRIER_DATA_WEAK #define WC_BARRIER_DATA(ptr) do { (void)(ptr); WC_BARRIER(); } while (0) #endif From 5d7372008a4be97017d27a983a11de1c1d685ab1 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 14:47:59 -0600 Subject: [PATCH 38/74] AES: drop wc_GmacFree --- wolfcrypt/src/aes.c | 10 ---------- wolfssl/wolfcrypt/aes.h | 1 - 2 files changed, 11 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index def0c3a96c4..a88ef522d5f 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -15932,16 +15932,6 @@ int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, authTag, authTagSz, authIn, authInSz); } -int wc_GmacFree(Gmac* gmac) -{ - if (gmac == NULL) { - return BAD_FUNC_ARG; - } - - wc_AesFree(&gmac->aes); - return 0; -} - #endif /* HAVE_AESGCM */ #ifdef HAVE_AESCCM diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 8a3cd5881ed..4122374c757 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -791,7 +791,6 @@ WOLFSSL_API WARN_UNUSED_RESULT int wc_AesGcmDecryptFinal(Aes* aes, WOLFSSL_API int wc_GmacUpdate(Gmac* gmac, const byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, byte* authTag, word32 authTagSz); - WOLFSSL_API int wc_GmacFree(Gmac* gmac); #ifndef WC_NO_RNG WOLFSSL_API int wc_Gmac(const byte* key, word32 keySz, byte* iv, word32 ivSz, const byte* authIn, word32 authInSz, From 8850f5741a67a2cd6f545d34a02f038066b5eb7b Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 15:35:36 -0600 Subject: [PATCH 39/74] ECC, X25519, DH, RSA: leave the caller's non-blocking context alone at free --- wolfcrypt/src/curve25519.c | 6 ------ wolfcrypt/src/dh.c | 7 ------- wolfcrypt/src/ecc.c | 8 -------- wolfcrypt/src/rsa.c | 8 -------- 4 files changed, 29 deletions(-) diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 48e9a563b6e..be6c4fd75ec 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -1459,12 +1459,6 @@ void wc_curve25519_free(curve25519_key* key) #ifdef WOLFSSL_SE050 se050_curve25519_free_key(key); -#endif -#ifdef WC_X25519_NONBLOCK - /* An attached context may hold ladder state from an unfinished call. */ - if (key->nb_ctx != NULL) { - ForceZero(key->nb_ctx, sizeof(x25519_nb_ctx_t)); - } #endif ForceZero(key, sizeof(*key)); diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index f4c38fac6cf..ba7ca213775 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -1046,13 +1046,6 @@ int wc_DhSetNonBlock(DhKey* key, DhNb* nb) int wc_FreeDhKey(DhKey* key) { if (key) { - #ifdef WC_DH_NONBLOCK - /* An abandoned operation leaves a copy of x in the context. */ - if (key->nb != NULL) { - ForceZero(key->nb, sizeof(DhNb)); - key->nb = NULL; - } - #endif mp_clear(&key->p); mp_clear(&key->g); mp_clear(&key->q); diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 8c575cd7b64..eac3df67b78 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -8698,14 +8698,6 @@ int wc_ecc_free(ecc_key* key) key->dhuk_plain_priv_len = 0; #endif -#ifdef WC_ECC_NONBLOCK - /* An abandoned operation leaves a copy of k in the context. */ - if (key->nb_ctx != NULL) { - ForceZero(key->nb_ctx, sizeof(ecc_nb_ctx_t)); - key->nb_ctx = NULL; - } -#endif - mp_clear(key->pubkey.x); mp_clear(key->pubkey.y); mp_clear(key->pubkey.z); diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index e1a8668633f..57577254a8f 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -609,14 +609,6 @@ int wc_FreeRsaKey(RsaKey* key) return BAD_FUNC_ARG; } -#ifdef WC_RSA_NONBLOCK - /* An abandoned operation leaves a copy of d in the context. */ - if (key->nb != NULL) { - ForceZero(key->nb, sizeof(RsaNb)); - key->nb = NULL; - } -#endif - #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) #ifndef WOLF_CRYPTO_CB_FIND if (key->devId != INVALID_DEVID) From c7f70dd7148bca01ea172cb380ecd2de14eb17e3 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 15:35:36 -0600 Subject: [PATCH 40/74] ML-KEM: read the key type only after encapsulate's checks pass --- wolfcrypt/src/wc_mlkem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index b8d488451ee..64290dd5371 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -1886,7 +1886,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 { From 9306081baae76648f33eac86b5618a6a369499df Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 15:35:36 -0600 Subject: [PATCH 41/74] SHA-256: keep one context layout under WC_SHA2_NO_SMALL_STACK --- wolfssl/wolfcrypt/sha256.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index c1e6f24b0aa..3529783e3cb 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -195,7 +195,10 @@ struct wc_Sha256 { word32 buffLen; /* in bytes */ word32 loLen; /* length in bytes */ word32 hiLen; /* length in bytes */ -#if !((defined(WOLFSSL_SMALL_STACK_CACHE) || defined(WOLFSSL_SMALL_STACK)) && \ +/* settings.h drops SMALL_STACK only inside sha256.c under + * WC_SHA2_NO_SMALL_STACK, so test that too to keep one layout. */ +#if defined(WC_SHA2_NO_SMALL_STACK) || \ + !((defined(WOLFSSL_SMALL_STACK_CACHE) || defined(WOLFSSL_SMALL_STACK)) && \ !defined(WOLFSSL_NO_MALLOC)) /* Message schedule of the C transform; wiped with the context at Free. */ #define WC_SHA256_W_IN_CTX From afccfc766b4fcf5fef8b3654add6159636dca222 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 16:31:44 -0600 Subject: [PATCH 42/74] wolfentropy: drop the out-of-boundary entropy source changes --- wolfcrypt/src/wolfentropy.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/wolfcrypt/src/wolfentropy.c b/wolfcrypt/src/wolfentropy.c index 91c14138d60..3932d652247 100644 --- a/wolfcrypt/src/wolfentropy.c +++ b/wolfcrypt/src/wolfentropy.c @@ -46,12 +46,6 @@ data, use this implementation to seed and re-seed the DRBG. #endif #include -#ifdef NO_INLINE - #include -#else - #define WOLFSSL_MISC_INCLUDED - #include -#endif #if defined(__APPLE__) || defined(__MACH__) #include #endif @@ -402,8 +396,6 @@ static void Entropy_StopThread(void) /* State to update that is multiple cache lines long. */ static word64 entropy_state[ENTROPY_NUM_WORDS + EXTRA_ENTROPY_WORDS] = {0}; -/* Digest of the state, used as the next update indices. */ -static byte entropy_idx[WC_SHA3_256_DIGEST_SIZE]; /* Using memory will take different amount of times depending on the CPU's * caches and business. @@ -424,6 +416,7 @@ static byte entropy_idx[WC_SHA3_256_DIGEST_SIZE]; static int Entropy_MemUse(void) { int i; + static byte d[WC_SHA3_256_DIGEST_SIZE]; int j; int ret; @@ -434,13 +427,13 @@ static int Entropy_MemUse(void) if (ret != 0) return ret; /* Get pseudo-random indices. */ - ret = wc_Sha3_256_Final(&entropyHash, entropy_idx); + ret = wc_Sha3_256_Final(&entropyHash, d); if (ret != 0) return ret; for (i = 0; i < ENTROPY_NUM_64BIT_WORDS; i++) { /* Choose a 64-bit word from a pseudo-random block.*/ - int idx = ((int)entropy_idx[i] << ENTROPY_BLOCK_SZ) + + int idx = ((int)d[i] << ENTROPY_BLOCK_SZ) + (j << ENTROPY_OFFSET_SHIFTING); /* Update a pseudo-random 64-bit word with a pseudo-random value. */ entropy_state[idx] += Entropy_TimeHiRes(); @@ -522,13 +515,11 @@ static int Entropy_GetNoise(unsigned char* noise, int samples) for (i = 0; i < samples; i++) { ret = Entropy_GetSample(&sample); if (ret != 0) - break; + return ret; noise[i] = (byte)sample; } - /* Raw noise sample (ISO/IEC 19790 7.9.7). */ - ForceZero(&sample, sizeof(sample)); - return ret; + return 0; } /* Mutex to prevent multiple callers requesting entropy operations at the @@ -611,7 +602,7 @@ static void Entropy_HealthTest_Repetition_Reset(void) /* No previous stored. */ rep_have_prev = 0; /* Clear previous. */ - ForceZero(&rep_prev_noise, sizeof(rep_prev_noise)); + rep_prev_noise = 0; } /* Test sample value with repetition test. @@ -690,9 +681,9 @@ static word16 prop_samples[PROP_WINDOW_SIZE]; static void Entropy_HealthTest_Proportion_Reset(void) { /* Clear out samples. */ - ForceZero(prop_samples, sizeof(prop_samples)); + XMEMSET(prop_samples, 0, sizeof(prop_samples)); /* Clear out counts. */ - ForceZero(prop_cnt, sizeof(prop_cnt)); + XMEMSET(prop_cnt, 0, sizeof(prop_cnt)); /* Clear stored count. */ prop_total = 0; /* Reset first and last index for samples. */ @@ -805,8 +796,6 @@ static int Entropy_HealthTest_Startup(void) /* Failing test only resets its own data. */ Entropy_HealthTest_Reset(); } - /* Raw samples of the live source. */ - ForceZero(initial, sizeof(initial)); return ret; } @@ -847,7 +836,6 @@ static int Entropy_Condition(byte* output, word32 len, byte* noise, if (ret == 0) { XMEMCPY(output, hash, len); } - ForceZero(hash, sizeof(hash)); } } @@ -957,9 +945,6 @@ int wc_Entropy_Get(int bits, unsigned char* entropy, word32 len) #endif if (ret != WC_NO_ERR_TRACE(BAD_MUTEX_E)) { - /* Raw samples were conditioned into the seed - * (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(noise, sizeof(noise)); /* Unlock mutex now we are done. */ wc_UnLockMutex(&entropy_mutex); } @@ -1066,10 +1051,6 @@ void Entropy_Final(void) #endif /* Clear health test data. */ Entropy_HealthTest_Reset(); - /* Raw timing state, its digest and the last sample time. */ - ForceZero(entropy_state, sizeof(entropy_state)); - ForceZero(entropy_idx, sizeof(entropy_idx)); - ForceZero(&entropy_last_time, sizeof(entropy_last_time)); /* No longer initialized. */ entropy_memuse_initialized = 0; } From 6acca4fc98fe70bb07803a998dcf2fa44d146fd3 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 8 Oct 2026 17:11:53 -0600 Subject: [PATCH 43/74] ML-KEM, ML-DSA: fix wipe helper guards and SHA-3 scratch layout Wipe helpers are built wherever their callers are. sha3.h is read before the per-file asm undefs so wc_Sha3 matches sha3.c, and buffers whose tail is Keccak scratch are sized to WC_SHA3_STATE_W. --- wolfcrypt/src/wc_mldsa.c | 75 +++++++++++++++++++---------------- wolfcrypt/src/wc_mlkem.c | 37 ++++++++++------- wolfcrypt/src/wc_mlkem_poly.c | 15 ++++--- 3 files changed, 73 insertions(+), 54 deletions(-) diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index c716f880579..689d8464911 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -206,6 +206,8 @@ } #endif +/* Before the asm undefs, so wc_Sha3 has the layout sha3.c built. */ +#include #if defined(WC_MLDSA_NO_ASM) || defined(WC_SHA3_NO_ASM) #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -557,6 +559,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 @@ -9372,40 +9413,6 @@ static int mldsa_calc_t(sword32* t, const sword32* a, sword32* s1, #endif /* !WOLFSSL_MLDSA_VERIFY_ONLY */ #ifndef WOLFSSL_MLDSA_VERIFY_ONLY -#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; -} - /* Wipe a SHAKE object that a failed step left mid-operation. */ static void mldsa_shake_wipe(wc_Shake* shake) { diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 64290dd5371..1e2ec84f184 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -84,6 +84,8 @@ #endif #endif +/* Before the asm undefs, so wc_Sha3 has the layout sha3.c built. */ +#include #ifdef WC_MLKEM_NO_ASM #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -682,6 +684,26 @@ 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)) +/* Wipe a SHA-3 object that a failed step left mid-operation. */ +static void mlkemkey_hash_wipe(wc_Sha3* h) +{ +#ifndef PSOC6_HASH_SHA3 + ForceZero(h->s, sizeof(h->s)); + ForceZero(h->t, sizeof(h->t)); +#ifdef WC_SHA3_SCRATCH_W + ForceZero(h->scratch, sizeof(h->scratch)); +#endif + h->i = 0; +#else + (void)wc_Sha3_256_Reset(h); +#endif +} +#endif + /******************************************************************************/ #ifndef WOLFSSL_MLKEM_NO_MAKE_KEY @@ -796,21 +818,6 @@ int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng) #endif /* WC_NO_RNG */ } -/* Wipe a SHA-3 object that a failed step left mid-operation. */ -static void mlkemkey_hash_wipe(wc_Sha3* h) -{ -#ifndef PSOC6_HASH_SHA3 - ForceZero(h->s, sizeof(h->s)); - ForceZero(h->t, sizeof(h->t)); -#ifdef WC_SHA3_SCRATCH_W - ForceZero(h->scratch, sizeof(h->scratch)); -#endif - h->i = 0; -#else - (void)wc_Sha3_256_Reset(h); -#endif -} - /** * Make a ML-KEM key object using random data. * diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index cd1a7d70a65..3d6a0ae3609 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -101,6 +101,11 @@ #endif #endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ +/* Before the asm undefs, so wc_Sha3 has the layout sha3.c built. */ +#include +/* Words for a multi-state buffer whose tail is also Keccak scratch. */ +#define MLKEM_SHA3_BUF_W(n) \ + (((n) > WC_SHA3_STATE_W) ? (n) : WC_SHA3_STATE_W) #ifdef WC_MLKEM_NO_ASM #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -2797,7 +2802,7 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) word64 *state = NULL; #else byte rand[4 * GEN_MATRIX_SIZE + 4]; - word64 state[25 * 4]; + word64 state[MLKEM_SHA3_BUF_W(25 * 4)]; #endif unsigned int ctr0; unsigned int ctr1; @@ -2969,7 +2974,7 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) word64 *state = NULL; #else byte rand[8 * GEN_MATRIX_SIZE + 4]; - word64 state[25 * 8]; + word64 state[MLKEM_SHA3_BUF_W(25 * 8)]; #endif unsigned int ctr[8]; @@ -3319,7 +3324,7 @@ static int mlkem_gen_matrix_k4_avx512(sword16* a, byte* seed, int transposed) */ static int mlkem_gen_matrix_k2_aarch64(sword16* a, byte* seed, int transposed) { - word64 state[3 * 25]; + word64 state[MLKEM_SHA3_BUF_W(3 * 25)]; word64* st = (word64*)state; unsigned int ctr0; unsigned int ctr1; @@ -3467,7 +3472,7 @@ static int mlkem_gen_matrix_k4_aarch64(sword16* a, byte* seed, int transposed) { int i; int k; - word64 state[3 * 25]; + word64 state[MLKEM_SHA3_BUF_W(3 * 25)]; word64* st = (word64*)state; unsigned int ctr0; unsigned int ctr1; @@ -5698,7 +5703,7 @@ static void mlkem_get_noise_eta2_aarch64(word64* rand, byte* seed, byte o) static int mlkem_get_noise_k3_aarch64(sword16* vec1, sword16* vec2, sword16* poly, byte* seed) { - word64 rand[3 * 25]; + word64 rand[MLKEM_SHA3_BUF_W(3 * 25)]; mlkem_get_noise_x3_eta2_aarch64(rand, seed, 0); mlkem_cbd_eta2(vec1 , (byte*)rand + 0 * 25 * 8); From ca21dd5f2e7802c58cd1a09b448c69b93391a498 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Fri, 9 Oct 2026 09:17:15 -0600 Subject: [PATCH 44/74] SHA-256: keep W on the stack in kernel builds The kernel hash descriptor cannot hold W; wipe it per transform there, as SHA-512 does. --- wolfcrypt/src/sha256.c | 4 ++++ wolfssl/wolfcrypt/sha256.h | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index d37f159fa5b..a39b43af9de 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -1826,6 +1826,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, word32* W = sha256->Wbuf; #else word32 W[WC_SHA256_BLOCK_SIZE]; + #define SHA256_W_ON_STACK #endif /* Copy context->state[] to working vars */ @@ -1868,6 +1869,9 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, !defined(WOLFSSL_NO_MALLOC) ForceZero(W, sizeof(word32) * WC_SHA256_BLOCK_SIZE); XFREE(W, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); + #elif defined(SHA256_W_ON_STACK) + ForceZero(W, sizeof(W)); + #undef SHA256_W_ON_STACK #endif return 0; } diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index 3529783e3cb..2addd3f64f6 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -195,9 +195,9 @@ struct wc_Sha256 { word32 buffLen; /* in bytes */ word32 loLen; /* length in bytes */ word32 hiLen; /* length in bytes */ -/* settings.h drops SMALL_STACK only inside sha256.c under - * WC_SHA2_NO_SMALL_STACK, so test that too to keep one layout. */ -#if defined(WC_SHA2_NO_SMALL_STACK) || \ +/* Kernel builds (WC_SHA2_NO_SMALL_STACK) keep W on the stack so the + * context fits the kernel hash descriptor. */ +#if !defined(WC_SHA2_NO_SMALL_STACK) && \ !((defined(WOLFSSL_SMALL_STACK_CACHE) || defined(WOLFSSL_SMALL_STACK)) && \ !defined(WOLFSSL_NO_MALLOC)) /* Message schedule of the C transform; wiped with the context at Free. */ From 834aa369965d54ca2e7b0940048e20809c4ab6f2 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Fri, 9 Oct 2026 09:37:11 -0600 Subject: [PATCH 45/74] RSA: leave the caller's output alone when public encrypt fails --- wolfcrypt/src/rsa.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 57577254a8f..02f2dd77cda 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -3906,10 +3906,6 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, hash, mgf, label, labelSz, saltLen, RsaModulusBits(key), key->heap); if (ret < 0) { - if (rsa_type == RSA_PUBLIC_ENCRYPT) { - /* Padding failed with the secret already copied in. */ - ForceZero(out, (word32)sz); - } break; } @@ -3926,16 +3922,6 @@ static int RsaPublicEncryptEx(const byte* in, word32 inLen, byte* out, key->state = RSA_STATE_ENCRYPT_RES; } if (ret < 0) { - /* out holds the padded secret (ISO/IEC 19790:2012 7.9.7); a - * pending or would-block result still needs it. */ - if ((rsa_type == RSA_PUBLIC_ENCRYPT) && - (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) - #ifdef WC_RSA_NONBLOCK - && (ret != FP_WOULDBLOCK) - #endif - ) { - ForceZero(out, (word32)sz); - } break; } From 1af671711d041ab63dcbf62a446b7282109634ae Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Fri, 9 Oct 2026 15:02:17 -0600 Subject: [PATCH 46/74] RSA: wipe the OAEP block in out when public encrypt fails --- wolfcrypt/src/rsa.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 02f2dd77cda..28380ca4ff0 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -3906,6 +3906,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; } @@ -3922,6 +3928,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; } From 827807d2efdbe33081c0ea92d7ddad9245611870 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Mon, 12 Oct 2026 11:49:20 -0600 Subject: [PATCH 47/74] Fix remaining FIPS v7 C zeroization gaps --- tests/api/test_kdf.c | 86 ++++++++ tests/api/test_kdf.h | 4 +- tests/api/test_slhdsa.c | 46 ++++ wolfcrypt/src/aes.c | 397 ++++++++++++++++++++++------------ wolfcrypt/src/kdf.c | 7 +- wolfcrypt/src/sha256.c | 11 +- wolfcrypt/src/wc_slhdsa.c | 67 ++++-- wolfssl/wolfcrypt/ecc.h | 7 + wolfssl/wolfcrypt/rsa.h | 8 + wolfssl/wolfcrypt/wc_slhdsa.h | 4 + 10 files changed, 466 insertions(+), 171 deletions(-) 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_slhdsa.c b/tests/api/test_slhdsa.c index 336c02ab9bb..a3892c4c5a9 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]; +#ifdef 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 a88ef522d5f..79d84671ac2 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -8906,6 +8906,36 @@ static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) #ifdef HAVE_AESGCM +/* 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. */ +#if defined(__GNUC__) || defined(__clang__) + #define GCM_RESTRICT __restrict__ +#elif defined(_MSC_VER) + #define GCM_RESTRICT __restrict +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define GCM_RESTRICT restrict +#else + #define GCM_RESTRICT +#endif + +/* Keep the 4-bit multiply in its block loop after adding a scratch pointer. + * Some compilers otherwise outline it despite WC_INLINE, adding a call for + * every block. Honor NO_INLINE builds. */ +#if defined(__GNUC__) && !defined(NO_INLINE) + #define GCM_4BIT_INLINE WC_INLINE __attribute__((always_inline)) +#else + #define GCM_4BIT_INLINE WC_INLINE +#endif + +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) @@ -9625,10 +9655,11 @@ 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) +#define GCM_C_GMULT +static void GMULT(byte* X, byte* Y, GcmScratch* GCM_RESTRICT ws) { - byte Z[WC_AES_BLOCK_SIZE]; - byte V[WC_AES_BLOCK_SIZE]; + byte* Z = ws->b; + byte* V = ws->b + WC_AES_BLOCK_SIZE; int i, j; XMEMSET(Z, 0, WC_AES_BLOCK_SIZE); @@ -9653,6 +9684,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; @@ -9667,14 +9701,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); } } @@ -9684,14 +9718,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); } } @@ -9699,11 +9733,14 @@ 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_AESGCM_STREAM @@ -9720,11 +9757,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 */ @@ -9758,6 +9795,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) \ @@ -9792,6 +9831,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) \ @@ -9816,6 +9857,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}, @@ -9883,14 +9925,15 @@ 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]) +static void GMULT(byte *x, byte m[256][WC_AES_BLOCK_SIZE], + GcmScratch* GCM_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); @@ -9908,7 +9951,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; @@ -9940,7 +9983,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; @@ -9958,7 +10001,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; @@ -9996,12 +10039,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) \ @@ -10011,6 +10060,9 @@ 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; @@ -10034,14 +10086,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 } @@ -10063,14 +10115,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 } @@ -10082,12 +10134,15 @@ 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_AESGCM_STREAM @@ -10097,7 +10152,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 @@ -10116,11 +10171,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 */ @@ -10133,12 +10188,12 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #if !defined(WOLFSSL_ARMASM_NO_NEON) && defined(__aarch64__) #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len_NEON(x, (const byte*)((gcm)->H), a, len) -#define GMULT(x, m) \ +#define GMULT(x, m, ws) \ GCM_gmult_NEON(x, (const byte**)m) #else #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len(x, (const byte**)((gcm)->M0), a, len) -#define GMULT(x, m) \ +#define GMULT(x, m, ws) \ GCM_gmult(x, (const byte**)m) #endif @@ -10146,10 +10201,11 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #elif (defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM)) #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len(x, (const byte**)((gcm)->M0), a, len) -#define GMULT(x, m) \ +#define GMULT(x, m, ws) \ GCM_gmult(x, (const byte**)m) #else +#define GCM_C_GMULT /* remainder = x^7 + x^2 + x^1 + 1 => 0xe1 * R shifts right a reverse bit pair of bytes such that: * R(b0, b1) => b1 = (b1 >> 1) | (b0 << 7); b0 >>= 1 @@ -10207,13 +10263,14 @@ static const word16 R[32] = { * [0..15] * H */ #if defined(WC_16BIT_CPU) -static void GMULT(byte *x, byte m[16][WC_AES_BLOCK_SIZE]) +static void GMULT(byte *x, byte m[16][WC_AES_BLOCK_SIZE], + GcmScratch* GCM_RESTRICT ws) { int i, j, n; - 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--) { for (n = 0; n < 2; n++) { @@ -10238,15 +10295,18 @@ static void GMULT(byte *x, byte m[16][WC_AES_BLOCK_SIZE]) XMEMCPY(x, Z, WC_AES_BLOCK_SIZE); } #elif defined(WC_32BIT_CPU) && defined(BIG_ENDIAN_ORDER) -static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) +static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], + GcmScratch* GCM_RESTRICT ws) { int i; - word32 z8[4] = {0, 0, 0, 0}; + word32* z8 = ws->w32; byte a; word32* x8 = (word32*)x; word32* m8; byte xi; + XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); + for (i = 15; i > 0; i--) { xi = x[i]; @@ -10303,15 +10363,17 @@ static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) x8[0] = z8[0]; x8[1] = z8[1]; x8[2] = z8[2]; x8[3] = z8[3]; } #elif defined(WC_32BIT_CPU) -static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) +static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], + GcmScratch* GCM_RESTRICT ws) { int i; - word32 z8[4] = {0, 0, 0, 0}; + word32* z8 = ws->w32; byte a; word32* x8 = (word32*)x; word32* m8; byte xi; - word32 n7, n6, n5, n4, n3, n2, n1, n0; + + XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); for (i = 15; i > 0; i--) { xi = x[i]; @@ -10354,18 +10416,17 @@ static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) a = (z8[3] >> 24) & 0xf; /* Rotate z by 4-bits */ - n7 = z8[3] & 0xf0f0f0f0ULL; - n6 = z8[3] & 0x0f0f0f0fULL; - n5 = z8[2] & 0xf0f0f0f0ULL; - n4 = z8[2] & 0x0f0f0f0fULL; - n3 = z8[1] & 0xf0f0f0f0ULL; - n2 = z8[1] & 0x0f0f0f0fULL; - n1 = z8[0] & 0xf0f0f0f0ULL; - n0 = z8[0] & 0x0f0f0f0fULL; - z8[3] = (n7 >> 4) | (n6 << 12) | (n4 >> 20); - z8[2] = (n5 >> 4) | (n4 << 12) | (n2 >> 20); - z8[1] = (n3 >> 4) | (n2 << 12) | (n0 >> 20); - z8[0] = (n1 >> 4) | (n0 << 12); + z8[3] = ((z8[3] & 0xf0f0f0f0U) >> 4) | + ((z8[3] & 0x0f0f0f0fU) << 12) | + ((z8[2] & 0x0f0f0f0fU) >> 20); + z8[2] = ((z8[2] & 0xf0f0f0f0U) >> 4) | + ((z8[2] & 0x0f0f0f0fU) << 12) | + ((z8[1] & 0x0f0f0f0fU) >> 20); + z8[1] = ((z8[1] & 0xf0f0f0f0U) >> 4) | + ((z8[1] & 0x0f0f0f0fU) << 12) | + ((z8[0] & 0x0f0f0f0fU) >> 20); + z8[0] = ((z8[0] & 0xf0f0f0f0U) >> 4) | + ((z8[0] & 0x0f0f0f0fU) << 12); /* XOR in most significant nibble * remainder */ z8[0] ^= (word32)R[a]; @@ -10377,15 +10438,18 @@ static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) x8[0] = z8[0]; x8[1] = z8[1]; x8[2] = z8[2]; x8[3] = z8[3]; } #elif defined(WC_64BIT_CPU) && defined(BIG_ENDIAN_ORDER) -static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) +static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], + GcmScratch* GCM_RESTRICT ws) { int i; - word64 z8[2] = {0, 0}; + word64* z8 = ws->w64; byte a; word64* x8 = (word64*)x; word64* m8; byte xi; + XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); + for (i = 15; i > 0; i--) { xi = x[i]; @@ -10441,16 +10505,18 @@ static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) x8[1] = z8[1]; } #else -static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) +static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], + GcmScratch* GCM_RESTRICT ws) { int i; - word64 z8[2] = {0, 0}; + word64* z8 = ws->w64; byte a; word64* x8 = (word64*)x; word64* m8; - word64 n0, n1, n2, n3; byte xi; + XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); + for (i = 15; i > 0; i--) { xi = x[i]; @@ -10490,12 +10556,11 @@ static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) a = (z8[1] >> 56) & 0xf; /* Rotate z by 4-bits */ - n3 = z8[1] & W64LIT(0xf0f0f0f0f0f0f0f0); - n2 = z8[1] & W64LIT(0x0f0f0f0f0f0f0f0f); - n1 = z8[0] & W64LIT(0xf0f0f0f0f0f0f0f0); - n0 = z8[0] & W64LIT(0x0f0f0f0f0f0f0f0f); - z8[1] = (n3 >> 4) | (n2 << 12) | (n0 >> 52); - z8[0] = (n1 >> 4) | (n0 << 12); + z8[1] = ((z8[1] & W64LIT(0xf0f0f0f0f0f0f0f0)) >> 4) | + ((z8[1] & W64LIT(0x0f0f0f0f0f0f0f0f)) << 12) | + ((z8[0] & W64LIT(0x0f0f0f0f0f0f0f0f)) >> 52); + z8[0] = ((z8[0] & W64LIT(0xf0f0f0f0f0f0f0f0)) >> 4) | + ((z8[0] & W64LIT(0x0f0f0f0f0f0f0f0f)) << 12); /* XOR in next significant nibble * H */ m8 = (word64*)m[xi >> 4]; @@ -10514,6 +10579,9 @@ static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) 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; @@ -10537,14 +10605,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 } @@ -10566,14 +10634,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 } @@ -10585,12 +10653,15 @@ 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)); +#ifdef GCM_C_GMULT + ForceZero(&gmultScratch, sizeof(gmultScratch)); +#endif } #ifdef WOLFSSL_AESGCM_STREAM @@ -10619,23 +10690,25 @@ 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 */ #elif defined(WORD64_AVAILABLE) && !defined(GCM_WORD32) #if !defined(FREESCALE_LTC_AES_GCM) -static void GMULT(word64* X, word64* Y) +#define GCM_C_GMULT +static void GMULT(word64* X, word64* Y, GcmScratch* GCM_RESTRICT ws) { - word64 Z[2] = {0,0}; - word64 V[2]; + 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++) @@ -10670,6 +10743,9 @@ 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]; @@ -10691,7 +10767,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) { @@ -10702,7 +10778,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 */ @@ -10732,7 +10808,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) { @@ -10743,7 +10819,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); } } @@ -10761,7 +10837,7 @@ 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); @@ -10770,6 +10846,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, 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 } #endif /* !FREESCALE_LTC_AES_GCM */ @@ -10791,17 +10870,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 @@ -10825,7 +10904,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) @@ -10848,7 +10927,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) @@ -10869,16 +10948,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 @@ -10902,7 +10981,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 @@ -10924,7 +11003,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 @@ -10935,12 +11014,14 @@ 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 +static void GMULT(word32* X, word32* Y, GcmScratch* GCM_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++) @@ -10986,6 +11067,9 @@ 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]; @@ -11009,7 +11093,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) { @@ -11022,7 +11106,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); } } @@ -11040,7 +11124,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) { @@ -11053,7 +11137,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); } } @@ -11071,7 +11155,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, bigH); + GMULT(x, bigH, &gmultScratch); } #ifdef LITTLE_ENDIAN_ORDER ByteReverseWords(x, x, WC_AES_BLOCK_SIZE); @@ -11080,6 +11164,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, 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_AESGCM_STREAM @@ -11100,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. @@ -11134,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) @@ -11152,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. @@ -11183,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 */ @@ -11243,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; @@ -11334,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. @@ -11346,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; @@ -11366,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 */ @@ -11849,6 +11948,9 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( 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)); return ret; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -11970,6 +12072,9 @@ static int AES_GCM_encrypt_ASM(Aes* aes, byte* out, const byte* in, 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)); return 0; } #endif @@ -12748,6 +12853,8 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( 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)); return ret; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -12864,6 +12971,9 @@ static int AES_GCM_decrypt_ASM(Aes* aes, byte* out, const byte* in, 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)); return ret; } #endif @@ -13202,6 +13312,7 @@ 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)); if (ret != 0) return ret; /* Reset state fields. */ diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index ff38a241682..f4cd4d7f137 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -866,8 +866,8 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, ret = _HashFinal(enmhashId, &hash, key); for (curBlock = 1; (ret == 0) && (curBlock < blocks); curBlock++) { - ret = _HashInit(enmhashId, &hash); - if (ret != 0) break; + /* 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) @@ -886,8 +886,7 @@ 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); + /* Reuse the reset context from the preceding Final. */ if (ret == 0) ret = _HashUpdate(enmhashId, &hash, kSzFlat, LENGTH_SZ); if (ret == 0 && kPad) diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index a39b43af9de..bd4d41ac8c1 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -1379,13 +1379,14 @@ static int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, int ret = 0; while (len >= WC_SHA256_BLOCK_SIZE) { - word32 buffer[WC_SHA256_BLOCK_SIZE / sizeof(word32)]; - - XMEMCPY(buffer, data, WC_SHA256_BLOCK_SIZE); + /* Keep the endian-conversion scratch in the context, where Free + * wipes it. Update and Final can already pass this same buffer. */ + if (data != (const byte*)sha256->buffer) + XMEMCPY(sha256->buffer, data, WC_SHA256_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER - ByteReverseWords(buffer, buffer, WC_SHA256_BLOCK_SIZE); + ByteReverseWords(sha256->buffer, sha256->buffer, WC_SHA256_BLOCK_SIZE); #endif - ret = Transform_Sha256(sha256, (const byte*)buffer); + ret = Transform_Sha256(sha256, (const byte*)sha256->buffer); if (ret != 0) break; data += WC_SHA256_BLOCK_SIZE; diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index cd57f4e6368..dfb5799ab53 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; @@ -8908,6 +8908,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; @@ -9070,15 +9074,18 @@ 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); + /* 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)); } #endif /* WOLF_CRYPTO_CB_ONLY_SLHDSA */ @@ -9092,6 +9099,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; @@ -9208,14 +9216,20 @@ 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); - } + /* 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 WOLF_CRYPTO_CB @@ -9231,10 +9245,15 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, WC_PQC_SIG_TYPE_SLHDSA, (int)key->params->param, key->sk, 3U * key->params->n, key); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) { - /* Device owns the key (ISO/IEC 19790:2012 7.9.7). */ + if ((ret != 0) || + ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0)) { + /* A failed generation or a device-owned key leaves no + * software private key (ISO/IEC 19790:2012 7.9.7). */ ForceZero(key->sk, 2U * key->params->n); } + if (ret != 0) { + key->flags = 0; + } return ret; } /* fall-through when unavailable */ @@ -9246,6 +9265,7 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, #ifdef WOLF_CRYPTO_CB_ONLY_SLHDSA if (ret == 0) { ret = NO_VALID_DEVID; + ForceZero(key->sk, 2U * key->params->n); } #else if (ret == 0) { @@ -11256,8 +11276,18 @@ int wc_SlhDsaKey_ImportPrivate(SlhDsaKey* key, const byte* priv, word32 privLen) ret = BAD_LENGTH_E; } else { + byte encoded[4 * SLHDSA_MAX_N]; + + /* 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); + XMEMCPY(key->sk, encoded, privLen); + ForceZero(encoded, sizeof(encoded)); /* 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) @@ -11390,6 +11420,7 @@ int wc_SlhDsaKey_CheckKey(SlhDsaKey* key) ret = WC_KEY_MISMATCH_E; } ForceZero(root, sizeof(root)); + slhdsakey_wipe_secret_hash(key); } #endif /* WOLF_CRYPTO_CB_ONLY_SLHDSA */ diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index bd69a436cba..a7ac6c94a79 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -658,6 +658,8 @@ struct ecc_key { WC_RNG* rng; #endif #ifdef WC_ECC_NONBLOCK + /* Caller-owned. Detach with wc_ecc_set_nonblock(key, NULL) before this + * storage expires so it is wiped; wc_ecc_free does not dereference it. */ ecc_nb_ctx_t* nb_ctx; #endif #ifdef WOLFSSL_DHUK @@ -1227,6 +1229,11 @@ int wc_ecc_get_key_id(ecc_key* key, word32* keyId); #endif #ifdef WC_ECC_NONBLOCK + /* Attaching resets ctx; replacing/detaching wipes the previous live ctx. + * The caller owns its storage and must serialize access. Detach on + * completion or cancellation before releasing ctx. After cancelling an + * unfinished operation, free/reinitialize the key before starting another. + * wc_ecc_free alone does not wipe caller-owned ctx storage. */ WOLFSSL_API int wc_ecc_set_nonblock(ecc_key *key, ecc_nb_ctx_t* ctx); #endif diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index 5a9346632c6..55abe288357 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -286,6 +286,9 @@ struct RsaKey { byte dataIsAlloc; #endif #ifdef WC_RSA_NONBLOCK + /* Caller-owned. Detach with wc_RsaSetNonBlock(key, NULL) while this + * storage is still live to wipe it before release; FreeRsaKey does not + * dereference it. See the setter's lifetime contract below. */ RsaNb* nb; #endif #ifdef WOLFSSL_AFALG_XILINX_RSA @@ -451,6 +454,11 @@ WOLFSSL_API int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, WOLFSSL_API int wc_RsaClearRNG(RsaKey* key); #endif #ifdef WC_RSA_NONBLOCK + /* Attaching resets nb; replacing/detaching wipes the previous live nb. + * The caller owns both allocations and must serialize access. Detach + * before nb expires, on completion or cancellation. After cancelling an + * unfinished operation, free/reinitialize the key before starting another. + * FreeRsaKey alone does not wipe caller-owned nb storage. */ WOLFSSL_API int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb); /* maxBlockInst budget lives on the TFM exptModNb_t; only available * with USE_FAST_MATH. SP-only WC_RSA_NONBLOCK builds do not expose 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 From b2fd4660dffcb680b75421d08300a2968652a218 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 14 Oct 2026 10:00:42 -0600 Subject: [PATCH 48/74] Drop zeroization outside the FIPS v7 services Callback and async paths, dynamic fallback, non-blocking contexts, SAKKE, DH, X25519/X448 and the fe/ge arithmetic, PBKDF1, PKCS#12, scrypt, TLS 1.0 PRF, X9.63 KDF, ECIES and the LMS/XMSS private paths. They return in a non-FIPS follow-up. --- wolfcrypt/src/aes.c | 24 ++------------ wolfcrypt/src/curve25519.c | 39 +++-------------------- wolfcrypt/src/curve448.c | 18 +---------- wolfcrypt/src/dh.c | 17 +--------- wolfcrypt/src/ecc.c | 12 ++----- wolfcrypt/src/fe_448.c | 33 ------------------- wolfcrypt/src/fe_operations.c | 21 +----------- wolfcrypt/src/ge_448.c | 22 ------------- wolfcrypt/src/ge_operations.c | 38 ++++++---------------- wolfcrypt/src/hmac.c | 6 ++-- wolfcrypt/src/kdf.c | 5 --- wolfcrypt/src/pwdbased.c | 38 ++++------------------ wolfcrypt/src/rsa.c | 14 ++------ wolfcrypt/src/sha.c | 12 +------ wolfcrypt/src/sha256.c | 40 ++--------------------- wolfcrypt/src/sha3.c | 4 +-- wolfcrypt/src/sha512.c | 60 +++-------------------------------- wolfcrypt/src/wc_lms.c | 49 ---------------------------- wolfcrypt/src/wc_lms_impl.c | 7 ---- wolfcrypt/src/wc_slhdsa.c | 14 +------- wolfcrypt/src/wc_xmss.c | 21 +++++------- wolfssl/wolfcrypt/ecc.h | 7 ---- wolfssl/wolfcrypt/rsa.h | 8 ----- 23 files changed, 49 insertions(+), 460 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 79d84671ac2..92116e774ca 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -5968,17 +5968,8 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) aes->keyInstalled = 1; } else { - /* A failed setup keeps no copy of the key. */ + /* Erase the software key schedule after a failed setup. */ ForceZero(aes->key, sizeof(aes->key)); - #ifdef WC_C_DYNAMIC_FALLBACK - ForceZero(aes->key_C_fallback, sizeof(aes->key_C_fallback)); - #endif - #if defined(WOLF_CRYPTO_CB) || (defined(WOLFSSL_DEVCRYPTO) && \ - (defined(WOLFSSL_DEVCRYPTO_AES) || defined(WOLFSSL_DEVCRYPTO_CBC))) || \ - (defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES)) || \ - defined(WOLFSSL_KCAPI_AES) || defined(WOLFSSL_NXP_HASHCRYPT_AES) - ForceZero(aes->devKey, sizeof(aes->devKey)); - #endif } #ifndef WOLF_CRYPTO_CB_ONLY_AES /* A claimed key runs in software only if the callback built a @@ -17057,19 +17048,8 @@ void wc_AesFree(Aes* aes) aes->keyInstalled = 0; /* If callback wants standard free, it can set devId to INVALID_DEVID. * Otherwise assume the callback handled cleanup. */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - /* Release heap state first; the wipe drops its pointers. */ - #if defined(WOLFSSL_AESGCM_STREAM) && defined(WOLFSSL_SMALL_STACK) && \ - !defined(WOLFSSL_AESNI) - if (aes->streamData != NULL) { - ForceZero(aes->streamData, aes->streamData_sz); - XFREE(aes->streamData, aes->heap, DYNAMIC_TYPE_AES); - aes->streamData = NULL; - } - #endif - ForceZero(aes, sizeof(Aes)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } #endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_FREE */ diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index be6c4fd75ec..93ff76d0cdd 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -223,8 +223,6 @@ static int curve25519_make_pub_ex(int public_size, byte* pub, int private_size, lm_invert(A.T, A.T); lm_mul(pub, A.X, A.T); #endif - /* A is the private scalar times the base point. */ - ForceZero(&A, sizeof(A)); ret = 0; } #elif defined(CURVED25519_X64) || (defined(WOLFSSL_ARMASM) && \ @@ -706,12 +704,6 @@ static int wc_curve25519_make_key_nb(WC_RNG* rng, int keysize, if (ret == 0) { key->pubSet = 1; } - else if (ret != FP_WOULDBLOCK) { - /* Public half failed: drop the scalar too - * (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(key->k, sizeof(key->k)); - key->privSet = 0; - } } return ret; @@ -725,7 +717,7 @@ int wc_curve25519_set_nonblock(curve25519_key* key, x25519_nb_ctx_t* ctx) /* If a different context is already set, clear it before replacing. * The caller is responsible for freeing any heap-allocated context. */ if (key->nb_ctx != NULL && key->nb_ctx != ctx) { - ForceZero(key->nb_ctx, sizeof(x25519_nb_ctx_t)); + XMEMSET(key->nb_ctx, 0, sizeof(x25519_nb_ctx_t)); } if (ctx != NULL) { XMEMSET(ctx, 0, sizeof(x25519_nb_ctx_t)); @@ -800,12 +792,6 @@ int wc_curve25519_make_key(WC_RNG* rng, int keysize, curve25519_key* key) } #endif key->pubSet = (ret == 0); - if (ret != 0) { - /* Public half failed: drop the scalar too - * (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(key->k, sizeof(key->k)); - key->privSet = 0; - } } } #endif /* !WOLFSSL_SE050 */ @@ -881,7 +867,7 @@ static int wc_curve25519_shared_secret_nb(curve25519_key* privKey, } if (ret != FP_WOULDBLOCK) { - ForceZero(privKey->nb_ctx, sizeof(x25519_nb_ctx_t)); + XMEMSET(privKey->nb_ctx, 0, sizeof(x25519_nb_ctx_t)); } return ret; @@ -1269,23 +1255,13 @@ int wc_curve25519_export_key_raw_ex(curve25519_key* key, { int ret; - /* Check the public arguments before anything is written to priv. */ - if ((pub == NULL) || (pubSz == NULL)) - return BAD_FUNC_ARG; - /* export private part */ ret = wc_curve25519_export_private_raw_ex(key, priv, privSz, endian); if (ret != 0) return ret; /* export public part */ - ret = wc_curve25519_export_public_ex(key, pub, pubSz, endian); - if (ret != 0) { - /* Public export failed: do not hand back the private key. */ - ForceZero(priv, *privSz); - } - - return ret; + return wc_curve25519_export_public_ex(key, pub, pubSz, endian); } #endif /* HAVE_CURVE25519_KEY_EXPORT */ @@ -1318,13 +1294,7 @@ int wc_curve25519_import_private_raw_ex(const byte* priv, word32 privSz, return ret; /* import public part */ - ret = wc_curve25519_import_public_ex(pub, pubSz, key, endian); - if (ret != 0) { - key->privSet = 0; - ForceZero(key->k, sizeof(key->k)); - } - - return ret; + return wc_curve25519_import_public_ex(pub, pubSz, key, endian); } /* curve25519 private key import only. (Big endian) @@ -1460,6 +1430,7 @@ void wc_curve25519_free(curve25519_key* key) #ifdef WOLFSSL_SE050 se050_curve25519_free_key(key); #endif + ForceZero(key, sizeof(*key)); #ifdef WOLFSSL_CHECK_MEM_ZERO diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index 69138a01ae3..851db3bdda3 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -291,9 +291,6 @@ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) else { ForceZero(key->k, sizeof(key->k)); XMEMSET(key->p, 0, sizeof(key->p)); - /* A zeroised SSP shall not be reusable - * (ISO/IEC 19790:2012 7.9.7 [09.29]). */ - key->privSet = 0; } } #endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ @@ -812,20 +809,11 @@ int wc_curve448_export_key_raw_ex(curve448_key* key, byte* priv, word32 *privSz, { int ret; - /* Check the public arguments before anything is written to priv. */ - if ((pub == NULL) || (pubSz == NULL)) { - return BAD_FUNC_ARG; - } - /* export private part */ ret = wc_curve448_export_private_raw_ex(key, priv, privSz, endian); if (ret == 0) { /* export public part */ ret = wc_curve448_export_public_ex(key, pub, pubSz, endian); - if (ret != 0) { - /* Public export failed: do not hand back the private key. */ - ForceZero(priv, *privSz); - } } return ret; @@ -879,11 +867,7 @@ int wc_curve448_import_private_raw_ex(const byte* priv, word32 privSz, ret = wc_curve448_import_private_ex(priv, privSz, key, endian); if (ret == 0) { /* import public part */ - ret = wc_curve448_import_public_ex(pub, pubSz, key, endian); - if (ret != 0) { - key->privSet = 0; - ForceZero(key->k, sizeof(key->k)); - } + return wc_curve448_import_public_ex(pub, pubSz, key, endian); } return ret; diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index ba7ca213775..d0099b82ead 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -1029,10 +1029,7 @@ int wc_DhSetNonBlock(DhKey* key, DhNb* nb) return BAD_FUNC_ARG; if (nb != NULL) { - ForceZero(nb, sizeof(DhNb)); - } - if ((key->nb != NULL) && (key->nb != nb)) { - ForceZero(key->nb, sizeof(DhNb)); + XMEMSET(nb, 0, sizeof(DhNb)); } /* Pass NULL to disable non-blocking mode. */ @@ -1506,7 +1503,6 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng, byte* priv, word32* privSz, byte* pub, word32* pubSz) { int ret; - int privWritten; if (key == NULL || rng == NULL || priv == NULL || privSz == NULL || pub == NULL || pubSz == NULL) { @@ -1514,8 +1510,6 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng, } ret = GeneratePrivateDh(key, rng, priv, privSz); - /* From here *privSz is the length actually written. */ - privWritten = (ret == 0); if (ret == 0) ret = GeneratePublicDh(key, priv, *privSz, pub, pubSz); @@ -1525,11 +1519,6 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng, if (ret == 0) ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, *privSz); #endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */ - if (privWritten && (ret != 0)) { - /* A failed pair is not handed back (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(priv, *privSz); - *privSz = 0; - } return ret; } @@ -2593,8 +2582,6 @@ WOLFSSL_LOCAL int wc_DhKeyCopy(DhKey* src, DhKey* dst) return ret; } - /* mp_copy covers only the digits in use; wipe the previous value first. */ - mp_forcezero(&dst->priv); if ((ret = mp_copy(&src->priv, &dst->priv)) != MP_OKAY) { WOLFSSL_MSG("mp_copy error"); return ret; @@ -2633,8 +2620,6 @@ int wc_DhImportKeyPair(DhKey* key, const byte* priv, word32 privSz, if (priv[0] == 0) { privSz--; priv++; } - /* Never overwrite one SSP with another (ISO/IEC 19790:2012 [09.31]). */ - mp_forcezero(&key->priv); if (mp_init(&key->priv) != MP_OKAY) havePriv = 0; } diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index eac3df67b78..67c86f3e8fc 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -16592,8 +16592,6 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, ret = HASH_TYPE_E; WOLFSSL_MSG("ECC Decrypt HMAC Check failed!"); } - /* On a mismatch this is a valid tag for the input. */ - ForceZero(verify, sizeof(verify)); wc_HmacFree(hmac); } @@ -17455,12 +17453,6 @@ int wc_X963_KDF(enum wc_HashType type, const byte* secret, word32 secretSz, remaining -= copySz; outIdx += copySz; } - /* Derived key material, including the unused tail of the last block. */ - ForceZero(tmp, sizeof(tmp)); - if (ret != 0) { - /* Partial output is not released (ISO/IEC 19790 7.9.7). */ - ForceZero(out, outSz); - } wc_HashFree(hash, type); @@ -17518,10 +17510,10 @@ int wc_ecc_set_nonblock(ecc_key *key, ecc_nb_ctx_t* ctx) /* If a different context is already set, clear it before replacing. * The caller is responsible for freeing any heap-allocated context. */ if (key->nb_ctx != NULL && key->nb_ctx != ctx) { - ForceZero(key->nb_ctx, sizeof(ecc_nb_ctx_t)); + XMEMSET(key->nb_ctx, 0, sizeof(ecc_nb_ctx_t)); } if (ctx != NULL) { - ForceZero(ctx, sizeof(ecc_nb_ctx_t)); + XMEMSET(ctx, 0, sizeof(ecc_nb_ctx_t)); } key->nb_ctx = ctx; return 0; diff --git a/wolfcrypt/src/fe_448.c b/wolfcrypt/src/fe_448.c index bbe79ee630e..dad8740ff0e 100644 --- a/wolfcrypt/src/fe_448.c +++ b/wolfcrypt/src/fe_448.c @@ -336,7 +336,6 @@ void fe448_invert(word8* r, const word8* a) fe448_sqr(t, t); fe448_sqr(t, t); fe448_mul(r, t, a); - ForceZero(t, sizeof(t)); } /* Scalar multiply the point by a number. r = n.a @@ -396,14 +395,6 @@ int curve448(byte* r, const byte* n, const byte* a) fe448_mul(r, x2, z2); fe448_norm(r); - ForceZero(x1, sizeof(x1)); - ForceZero(x2, sizeof(x2)); - ForceZero(z2, sizeof(z2)); - ForceZero(x3, sizeof(x3)); - ForceZero(z3, sizeof(z3)); - ForceZero(t0, sizeof(t0)); - ForceZero(t1, sizeof(t1)); - return 0; } @@ -1081,10 +1072,6 @@ void fe448_invert(sword64* r, const sword64* a) /* t1 = fffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000 */ fe448_mul(r, t3, t1); /* r = fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd */ - ForceZero(t1, sizeof(t1)); - ForceZero(t2, sizeof(t2)); - ForceZero(t3, sizeof(t3)); - ForceZero(t4, sizeof(t4)); } /* Scalar multiply the point by a number. r = n.a @@ -1150,14 +1137,6 @@ int curve448(byte* r, const byte* n, const byte* a) fe448_mul(x2, x2, z2); fe448_to_bytes(r, x2); - ForceZero(x1, sizeof(x1)); - ForceZero(x2, sizeof(x2)); - ForceZero(z2, sizeof(z2)); - ForceZero(x3, sizeof(x3)); - ForceZero(z3, sizeof(z3)); - ForceZero(t0, sizeof(t0)); - ForceZero(t1, sizeof(t1)); - return 0; } @@ -2270,10 +2249,6 @@ void fe448_invert(sword32* r, const sword32* a) /* t1 = fffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000 */ fe448_mul(r, t3, t1); /* r = fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd */ - ForceZero(t1, sizeof(t1)); - ForceZero(t2, sizeof(t2)); - ForceZero(t3, sizeof(t3)); - ForceZero(t4, sizeof(t4)); } /* Scalar multiply the point by a number. r = n.a @@ -2339,14 +2314,6 @@ int curve448(byte* r, const byte* n, const byte* a) fe448_mul(x2, x2, z2); fe448_to_bytes(r, x2); - ForceZero(x1, sizeof(x1)); - ForceZero(x2, sizeof(x2)); - ForceZero(z2, sizeof(z2)); - ForceZero(x3, sizeof(x3)); - ForceZero(z3, sizeof(z3)); - ForceZero(t0, sizeof(t0)); - ForceZero(t1, sizeof(t1)); - return 0; } diff --git a/wolfcrypt/src/fe_operations.c b/wolfcrypt/src/fe_operations.c index 94b6d0935d5..671ab364fdd 100644 --- a/wolfcrypt/src/fe_operations.c +++ b/wolfcrypt/src/fe_operations.c @@ -180,14 +180,6 @@ int curve25519(byte* q, const byte* n, const byte* p) fe_mul(x2,x2,z2); fe_tobytes(q,x2); - ForceZero(x1, sizeof(x1)); - ForceZero(x2, sizeof(x2)); - ForceZero(z2, sizeof(z2)); - ForceZero(x3, sizeof(x3)); - ForceZero(z3, sizeof(z3)); - ForceZero(tmp0, sizeof(tmp0)); - ForceZero(tmp1, sizeof(tmp1)); - return 0; } #else @@ -255,14 +247,6 @@ int curve25519_blind(byte* q, const byte* n, const byte* mask, const byte* p, fe_mul(x2,x2,z2); fe_tobytes(q,x2); - ForceZero(x1, sizeof(x1)); - ForceZero(x2, sizeof(x2)); - ForceZero(z2, sizeof(z2)); - ForceZero(x3, sizeof(x3)); - ForceZero(z3, sizeof(z3)); - ForceZero(tmp0, sizeof(tmp0)); - ForceZero(tmp1, sizeof(tmp1)); - return 0; } #endif @@ -770,10 +754,7 @@ void fe_invert(fe out,const fe z) fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); fe_mul(out,t1,t0); - ForceZero(t0, sizeof(t0)); - ForceZero(t1, sizeof(t1)); - ForceZero(t2, sizeof(t2)); - ForceZero(t3, sizeof(t3)); + return; } diff --git a/wolfcrypt/src/ge_448.c b/wolfcrypt/src/ge_448.c index 7f1e2e7c754..d2c77638dc0 100644 --- a/wolfcrypt/src/ge_448.c +++ b/wolfcrypt/src/ge_448.c @@ -148,7 +148,6 @@ void sc448_reduce(byte* b) } b[i] = t[i] & 0xff; b[i+1] = 0; - ForceZero(t, sizeof(t)); } /* Multiply a by b and add d. r = (a * b + d) mod order @@ -228,7 +227,6 @@ void sc448_muladd(byte* r, const byte* a, const byte* b, const byte* d) for (i = 0; i < 56; i++) { u += r[i] - (ed448_order[i] & o); r[i] = u & 0xff; u >>= 8; } - ForceZero(t, sizeof(t)); } /* Double the point on the Twisted Edwards curve. r = 2.p @@ -364,8 +362,6 @@ static void ge448_scalarmult(ge448_p2* h, const ge448_p2* p, const byte* a) } XMEMCPY(h, &r, sizeof(r)); - ForceZero(&r, sizeof(r)); - ForceZero(&s, sizeof(s)); } /* Perform a scalar multiplication of the base point. r = a * base @@ -792,8 +788,6 @@ void sc448_reduce(byte* b) b[55] = (byte)(d[7 ] >> 48); b[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ - ForceZero(d, sizeof(d)); - ForceZero(t, sizeof(t)); } /* Multiply a by b and add d. r = (a * b + d) mod order @@ -1301,11 +1295,6 @@ void sc448_muladd(byte* r, const byte* a, const byte* b, const byte* d) r[55] = (byte)(rd[7 ] >> 48); r[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ - ForceZero(ad, sizeof(ad)); - ForceZero(bd, sizeof(bd)); - ForceZero(dd, sizeof(dd)); - ForceZero(rd, sizeof(rd)); - ForceZero(t, sizeof(t)); } /* Precomputed multiples of the base point. */ @@ -5777,8 +5766,6 @@ void sc448_reduce(byte* b) b[55] = (byte)(d[15] >> 20); b[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ - ForceZero(d, sizeof(d)); - ForceZero(t, sizeof(t)); } /* Multiply a by b and add d. r = (a * b + d) mod order @@ -6690,11 +6677,6 @@ void sc448_muladd(byte* r, const byte* a, const byte* b, const byte* d) r[55] = (byte)(rd[15] >> 20); r[56] = 0; #endif /* WOLFSSL_ED448_NO_LARGE_CODE */ - ForceZero(ad, sizeof(ad)); - ForceZero(bd, sizeof(bd)); - ForceZero(dd, sizeof(dd)); - ForceZero(rd, sizeof(rd)); - ForceZero(t, sizeof(t)); } /* Precomputed multiples of the base point. */ @@ -10992,7 +10974,6 @@ static void ge448_select(ge448_precomp* r, int pos, byte b) cmov(r, &base[pos][7], babs, 8); fe448_neg(minusx, r->x); fe448_cmov(r->x, minusx, bnegative); - ForceZero(minusx, sizeof(minusx)); } /* Perform a scalar multiplication of the base point. r = a * base @@ -11059,9 +11040,6 @@ int ge448_scalarmult_base(ge448_p2* r, const byte* a) ge448_madd(r, r, t); } - /* e is the scalar in radix 16 and t the selected table entry. */ - ForceZero(e, 113); - ForceZero(t, sizeof(*t)); #ifdef WOLFSSL_SMALL_STACK XFREE(t, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(e, NULL, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/wolfcrypt/src/ge_operations.c b/wolfcrypt/src/ge_operations.c index aa2615fba95..fda375ba2e3 100644 --- a/wolfcrypt/src/ge_operations.c +++ b/wolfcrypt/src/ge_operations.c @@ -310,7 +310,6 @@ void sc_reduce(byte* s) for (i = 0; i < 32; i++) s[i] = WC_OCTET(s[i]); #endif - ForceZero(t, sizeof(t)); } /* @@ -639,10 +638,6 @@ void sc_muladd(byte* s, const byte* a, const byte* b, const byte* c) for (i = 0; i < 32; i++) s[i] = WC_OCTET(s[i]); #endif - ForceZero(ad, sizeof(ad)); - ForceZero(bd, sizeof(bd)); - ForceZero(cd, sizeof(cd)); - ForceZero(t, sizeof(t)); } #endif #else @@ -784,7 +779,6 @@ void sc_reduce(byte* s) s[29] = (byte)(t[ 5] >> 22); s[30] = (byte)(t[ 5] >> 30); s[31] = (byte)(t[ 5] >> 38); - ForceZero(t, sizeof(t)); } /* @@ -937,10 +931,6 @@ void sc_muladd(byte* s, const byte* a, const byte* b, const byte* c) s[29] = (byte)(t[ 5] >> 22); s[30] = (byte)(t[ 5] >> 30); s[31] = (byte)(t[ 5] >> 38); - ForceZero(ad, sizeof(ad)); - ForceZero(bd, sizeof(bd)); - ForceZero(cd, sizeof(cd)); - ForceZero(t, sizeof(t)); } #endif /* !CURVED25519_ASM */ #endif /* !HAVE___UINT128_T || NO_CURVED25519_128BIT */ @@ -9103,9 +9093,10 @@ static const ge_precomp base[32][8] = { } ; #endif -static void ge_select(ge_precomp *t,ge_precomp *minust,int pos,signed char b) +static void ge_select(ge_precomp *t,int pos,signed char b) { #ifndef CURVED25519_ASM + ge_precomp minust; unsigned char bnegative = negative(b); unsigned char babs = (unsigned char)(b - (signed char)((unsigned char)((-bnegative) & b) << 1)); @@ -9120,10 +9111,9 @@ static void ge_select(ge_precomp *t,ge_precomp *minust,int pos,signed char b) cmov(t,&base[pos][6],babs,7); cmov(t,&base[pos][7],babs,8); fe_cswap(t->yminusx, t->yplusx, bnegative); - fe_neg(minust->xy2d,t->xy2d); - fe_cmov(t->xy2d,minust->xy2d,bnegative); + fe_neg(minust.xy2d,t->xy2d); + fe_cmov(t->xy2d,minust.xy2d,bnegative); #else - (void)minust; /* (wc_ptr_t) needed to work around C array casting semantics. */ fe_cmov_table((fe*)t, (const fe*)(wc_ptr_t)base[pos], b); #endif @@ -9146,7 +9136,6 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) ge_p2 s; #endif ge_precomp t; - ge_precomp minust; int i; for (i = 0;i < 32;++i) { @@ -9167,7 +9156,7 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) /* each e[i] is between -8 and 8 */ #ifndef CURVED25519_ASM - ge_select(&t,&minust,0,e[1]); + ge_select(&t,0,e[1]); fe_sub(h->X, t.yplusx, t.yminusx); fe_add(h->Y, t.yplusx, t.yminusx); fe_0(h->Z); @@ -9177,7 +9166,7 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) fe_add(h->Y, h->Y, h->Y); for (i = 3;i < 64;i += 2) { - ge_select(&t,&minust,i / 2,e[i]); + ge_select(&t,i / 2,e[i]); ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); } @@ -9187,30 +9176,21 @@ void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) ge_p2_dbl(&r,&s); ge_p1p1_to_p3(h,&r); for (i = 0;i < 64;i += 2) { - ge_select(&t,&minust,i / 2,e[i]); + ge_select(&t,i / 2,e[i]); ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); } #else - ge_select(&t, &minust, 0, e[0]); + ge_select(&t, 0, e[0]); fe_sub(h->X, t.yplusx, t.yminusx); fe_add(h->Y, t.yplusx, t.yminusx); fe_0(h->Z); h->Z[0] = 2; fe_copy(h->T, t.xy2d); for (i = 1; i < 64; i++) { - ge_select(&t, &minust, i, e[i]); + ge_select(&t, i, e[i]); ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); } #endif - - /* e is the scalar in radix 16; r, s and t are secret dependent. */ - ForceZero(e, sizeof(e)); - ForceZero(&r, sizeof(r)); -#ifndef CURVED25519_ASM - ForceZero(&s, sizeof(s)); -#endif - ForceZero(&t, sizeof(t)); - ForceZero(&minust, sizeof(minust)); } #endif /* HAVE_ED25519_SIGN || HAVE_ED25519_MAKE_KEY || * WOLFSSL_CURVE25519_USE_ED25519 */ diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index c646c934b05..6d1a94a2b05 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -1131,9 +1131,7 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, /* update key length */ hmac->keyLen = (word16)length; - /* A failure falls through to the key cleanup below. */ - if (ret == 0) - return ret; + return ret; } /* no need to pad below */ #endif @@ -1806,7 +1804,7 @@ void wc_HmacFree(Hmac* hmac) byte finalHash[WC_HMAC_BLOCK_SIZE]; ret = wc_CryptoCb_Hmac(hmac, hmac->macType, NULL, 0, finalHash); (void)ret; /* must ignore return code here */ - ForceZero(finalHash, sizeof(finalHash)); + (void)finalHash; } #endif diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index f4cd4d7f137..3c2c5c58d4f 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -284,13 +284,8 @@ int wc_PRF_TLSv1(byte* digest, word32 digLen, const byte* secret, xorbuf(digest, sha_result, digLen); ForceZero(sha_result, digLen); } - else { - /* digest holds the unmasked MD5 half (ISO/IEC 19790 7.9.7). */ - ForceZero(digest, digLen); - } } - ForceZero(sha_result, MAX_PRF_DIG); #if defined(WOLFSSL_CHECK_MEM_ZERO) wc_MemZero_Check(sha_result, MAX_PRF_DIG); #endif diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index b19d796cfac..206da4b079c 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -192,13 +192,8 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, wc_MemZero_Check(digest, sizeof(digest)); #endif - if (err != 0) { - /* Partial output is not released (ISO/IEC 19790 7.9.7). */ - ForceZero(key, (word32)keyLen); - if (iv != NULL) - ForceZero(iv, (word32)ivLen); + if (err != 0) return err; - } if (keyOutput != (keyLen + ivLen)) return BUFFER_E; @@ -472,8 +467,6 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, mp_int res[1]; #endif enum wc_HashType hashT; - byte* outStart = output; - int outLen = kLen; (void)heap; @@ -601,7 +594,7 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, ret = MP_ADD_E; if (ret != 0) { - mp_forcezero(B1); + mp_clear(B1); break; } @@ -624,7 +617,6 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, byte tmp[WC_MAX_BLOCK_SIZE + 1]; ret = mp_to_unsigned_bin(res, tmp); XMEMCPY(I + i, tmp + 1, v); - ForceZero(tmp, sizeof(tmp)); } else if (outSz < (int)v) { XMEMSET(I + i, 0, v - (word32)outSz); @@ -634,13 +626,13 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, ret = mp_to_unsigned_bin(res, I + i); } - mp_forcezero(i1); - mp_forcezero(res); + mp_clear(i1); + mp_clear(res); if (ret < 0) break; } if (ret < 0) { - mp_forcezero(B1); + mp_clear(B1); break; } @@ -648,11 +640,7 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, XMEMCPY(output, Ai, currentLen); output += currentLen; kLen -= (int)currentLen; - mp_forcezero(B1); - } - if (ret != 0) { - /* Partial output is not released (ISO/IEC 19790 7.9.7). */ - ForceZero(outStart, (word32)outLen); + mp_clear(B1); } #ifdef WOLFSSL_SMALL_STACK @@ -662,14 +650,8 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, XFREE(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); ForceZero(B, WC_MAX_BLOCK_SIZE); XFREE(B, heap, DYNAMIC_TYPE_TMP_BUFFER); - if (B1 != NULL) - ForceZero(B1, sizeof(*B1)); XFREE(B1, heap, DYNAMIC_TYPE_TMP_BUFFER); - if (i1 != NULL) - ForceZero(i1, sizeof(*i1)); XFREE(i1, heap, DYNAMIC_TYPE_TMP_BUFFER); - if (res != NULL) - ForceZero(res, sizeof(*res)); XFREE(res, heap, DYNAMIC_TYPE_TMP_BUFFER); #else ForceZero(Ai, WC_MAX_DIGEST_SIZE); @@ -733,8 +715,6 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, #endif byte* buffer = staticBuffer; enum wc_HashType hashT; - byte* outStart = output; - int outLen = kLen; (void)heap; @@ -863,10 +843,6 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, PKCS12_ByteReverseWords((PKCS12_WORD*)I, (PKCS12_WORD*)I, nBlocks * v); #endif } - if (ret != 0) { - /* Partial output is not released (ISO/IEC 19790 7.9.7). */ - ForceZero(outStart, (word32)outLen); - } ForceZero(B, WC_MAX_BLOCK_SIZE); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -948,7 +924,6 @@ static void scryptSalsa(word32* out, word32* in) for (i = 0; i < 16; i++) out[i] = ByteReverseWord32(ByteReverseWord32(in[i]) + x[i]); #endif - ForceZero(x, sizeof(x)); } /* Mix a block using Salsa20/8. @@ -1002,7 +977,6 @@ static void scryptBlockMix(byte* b, byte* y, int r) } #endif } - ForceZero(x, sizeof(x)); } /* Random oracles mix. diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 28380ca4ff0..a97f986e4ab 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -618,13 +618,8 @@ int wc_FreeRsaKey(RsaKey* key) WC_PK_TYPE_RSA, 0, key); /* If callback wants standard free, it returns CRYPTOCB_UNAVAILABLE. * Otherwise assume the callback handled cleanup. */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - wc_RsaCleanup(key); - #ifndef WOLFSSL_RSA_PUBLIC_ONLY - RsaForceZeroPriv(key); - #endif + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; - } /* fall-through to software cleanup */ ret = 0; } @@ -6156,11 +6151,8 @@ int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb) if (key == NULL) return BAD_FUNC_ARG; - if (nb != NULL) { - ForceZero(nb, sizeof(RsaNb)); - } - if ((key->nb != NULL) && (key->nb != nb)) { - ForceZero(key->nb, sizeof(RsaNb)); + if (nb) { + XMEMSET(nb, 0, sizeof(RsaNb)); } /* Allow nb == NULL to clear non-block mode */ diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 2aae9853e82..3ebb043768c 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -1140,18 +1140,8 @@ void wc_ShaFree(wc_Sha* sha) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - /* Release heap state first; the wipe drops its pointers. */ - #ifdef WOLFSSL_HASH_KEEP - if (sha->msg != NULL) { - ForceZero(sha->msg, sha->len); - XFREE(sha->msg, sha->heap, DYNAMIC_TYPE_TMP_BUFFER); - sha->msg = NULL; - } - #endif - ForceZero(sha, sizeof(*sha)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index bd4d41ac8c1..06b1ebf32a4 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -3142,26 +3142,8 @@ int wc_Sha224Reset(wc_Sha224* sha224) { /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - /* Release heap state first; the wipe drops its pointers. */ - #ifdef WOLFSSL_SMALL_STACK_CACHE - if (sha224->W != NULL) { - ForceZero(sha224->W, - sizeof(word32) * WC_SHA224_BLOCK_SIZE); - XFREE(sha224->W, sha224->heap, DYNAMIC_TYPE_DIGEST); - sha224->W = NULL; - } - #endif - #ifdef WOLFSSL_HASH_KEEP - if (sha224->msg != NULL) { - ForceZero(sha224->msg, sha224->len); - XFREE(sha224->msg, sha224->heap, DYNAMIC_TYPE_TMP_BUFFER); - sha224->msg = NULL; - } - #endif - ForceZero(sha224, sizeof(*sha224)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } @@ -3237,26 +3219,8 @@ void wc_Sha256Free(wc_Sha256* sha256) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - /* Release heap state first; the wipe drops its pointers. */ - #ifdef WOLFSSL_SMALL_STACK_CACHE - if (sha256->W != NULL) { - ForceZero(sha256->W, - sizeof(word32) * WC_SHA256_BLOCK_SIZE); - XFREE(sha256->W, sha256->heap, DYNAMIC_TYPE_DIGEST); - sha256->W = NULL; - } - #endif - #ifdef WOLFSSL_HASH_KEEP - if (sha256->msg != NULL) { - ForceZero(sha256->msg, sha256->len); - XFREE(sha256->msg, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); - sha256->msg = NULL; - } - #endif - ForceZero(sha256, sizeof(*sha256)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index b02365d226b..7879bb80fce 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1729,10 +1729,8 @@ static void wc_Sha3Free(wc_Sha3* sha3) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - wc_Sha3Wipe(sha3); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index e0bc974d9b7..1b48212e6ca 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -304,11 +304,7 @@ } void wc_Sha512Free(wc_Sha512* sha512) { - if (sha512 == NULL) { - return; - } se050_hash_free(&sha512->se050Ctx); - ForceZero(sha512, sizeof(*sha512)); } #elif defined(STM32_HASH_SHA512) @@ -511,10 +507,8 @@ void wc_Sha512Free(wc_Sha512* sha512) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - ForceZero(sha512, sizeof(*sha512)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } @@ -542,8 +536,6 @@ int wc_Sha512GetHash(wc_Sha512* sha512, byte* hash) wc_Sha512Free(tmpSha512); } - ForceZero(tmpSha512, sizeof(*tmpSha512)); - WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -657,8 +649,6 @@ int wc_Sha512_224GetHash(wc_Sha512* sha512, byte* hash) wc_Sha512_224Free(tmpSha512); } - ForceZero(tmpSha512, sizeof(*tmpSha512)); - WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -739,8 +729,6 @@ int wc_Sha512_256GetHash(wc_Sha512* sha512, byte* hash) wc_Sha512_256Free(tmpSha512); } - ForceZero(tmpSha512, sizeof(*tmpSha512)); - WC_FREE_VAR_EX(tmpSha512, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -841,10 +829,8 @@ void wc_Sha384Free(wc_Sha384* sha384) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - ForceZero(sha384, sizeof(*sha384)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } @@ -872,8 +858,6 @@ int wc_Sha384GetHash(wc_Sha384* sha384, byte* hash) wc_Sha384Free(tmpSha384); } - ForceZero(tmpSha384, sizeof(*tmpSha384)); - WC_FREE_VAR_EX(tmpSha384, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -2727,26 +2711,8 @@ void wc_Sha512Free(wc_Sha512* sha512) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - /* Release heap state first; the wipe drops its pointers. */ - #ifdef WOLFSSL_SMALL_STACK_CACHE - if (sha512->W != NULL) { - ForceZero(sha512->W, - (sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE); - XFREE(sha512->W, sha512->heap, DYNAMIC_TYPE_DIGEST); - sha512->W = NULL; - } - #endif - #ifdef WOLFSSL_HASH_KEEP - if (sha512->msg != NULL) { - ForceZero(sha512->msg, sha512->len); - XFREE(sha512->msg, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); - sha512->msg = NULL; - } - #endif - ForceZero(sha512, sizeof(*sha512)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } @@ -3242,26 +3208,8 @@ void wc_Sha384Free(wc_Sha384* sha384) /* If they want the standard free, they can call it themselves */ /* via their callback setting devId to INVALID_DEVID */ /* otherwise assume the callback handled it */ - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - /* Release heap state first; the wipe drops its pointers. */ - #ifdef WOLFSSL_SMALL_STACK_CACHE - if (sha384->W != NULL) { - ForceZero(sha384->W, - (sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE); - XFREE(sha384->W, sha384->heap, DYNAMIC_TYPE_DIGEST); - sha384->W = NULL; - } - #endif - #ifdef WOLFSSL_HASH_KEEP - if (sha384->msg != NULL) { - ForceZero(sha384->msg, sha384->len); - XFREE(sha384->msg, sha384->heap, DYNAMIC_TYPE_TMP_BUFFER); - sha384->msg = NULL; - } - #endif - ForceZero(sha384, sizeof(*sha384)); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return; - } /* fall-through when unavailable */ } diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c index 9314ee36cb8..d63aa89630b 100644 --- a/wolfcrypt/src/wc_lms.c +++ b/wolfcrypt/src/wc_lms.c @@ -1090,34 +1090,6 @@ void wc_LmsKey_Free(LmsKey* key) } #ifndef WOLFSSL_LMS_VERIFY_ONLY -/* Length of the expanded private key data. */ -static word32 wc_lmskey_priv_data_len(const LmsParams* params) -{ - word32 len = LMS_PRIV_DATA_LEN(params->levels, params->height, params->p, - params->rootLevels, params->cacheBits, params->hash_len); - -#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE - len += HSS_PRIVATE_KEY_LEN(params->hash_len); -#endif - return len; -} - -#ifndef WOLF_CRYPTO_CB_ONLY_LMS -/* A failed generation, reload or sign leaves no private key resident - * (ISO/IEC 19790:2012 7.9.7). */ -static void wc_lmskey_wipe_priv(LmsKey* key) -{ - if (key->priv_data != NULL) { - ForceZero(key->priv_data, wc_lmskey_priv_data_len(key->params)); - } - ForceZero(key->priv_raw, HSS_MAX_PRIVATE_KEY_LEN); - ForceZero(&key->priv, sizeof(HssPrivKey)); - if (key->state != WC_LMS_STATE_NOSIGS) { - key->state = WC_LMS_STATE_BAD; - } -} -#endif /* !WOLF_CRYPTO_CB_ONLY_LMS */ - /* Set the write private key callback to the LMS key structure. * * The callback must be able to write/update the private key to @@ -1241,7 +1213,6 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) #endif #ifndef WOLF_CRYPTO_CB_ONLY_LMS word32 priv_data_len = 0; - int started = 0; #endif /* Validate parameters. */ @@ -1343,7 +1314,6 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) /* Initialize working state for use. */ ret = wc_lmskey_state_init(state, key->params); if (ret == 0) { - started = 1; /* Make the HSS key. */ ret = wc_hss_make_key(state, rng, key->priv_raw, &key->priv, key->priv_data, key->pub); @@ -1390,9 +1360,6 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) key->state = WC_LMS_STATE_OK; key->pubSet = 1; } - else if (started) { - wc_lmskey_wipe_priv(key); - } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; @@ -1424,7 +1391,6 @@ int wc_LmsKey_Reload(LmsKey* key) int ret = 0; #ifndef WOLF_CRYPTO_CB_ONLY_LMS word32 priv_data_len = 0; - int started = 0; #endif /* Validate parameter. */ @@ -1490,10 +1456,7 @@ int wc_LmsKey_Reload(LmsKey* key) /* Load private key. */ #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE const LmsParams* params = key->params; - #endif - started = 1; - #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE rv = key->read_private_key(key->priv_data, priv_data_len, key->context); #else rv = key->read_private_key(key->priv_raw, @@ -1550,9 +1513,6 @@ int wc_LmsKey_Reload(LmsKey* key) /* Update state. */ key->state = WC_LMS_STATE_OK; } - else if (started) { - wc_lmskey_wipe_priv(key); - } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; @@ -1608,9 +1568,6 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, int msgSz) { int ret = 0; -#ifndef WOLF_CRYPTO_CB_ONLY_LMS - int started = 0; -#endif /* Validate parameters. A NULL msg is valid for the empty message * (msgSz == 0), per RFC 8554 which permits empty messages. */ @@ -1704,7 +1661,6 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, /* Set the key state to bad by default. State is presumed bad * unless a correct sign and write operation happen together. */ key->state = WC_LMS_STATE_BAD; - started = 1; /* Sign message. */ ret = wc_hss_sign(state, key->priv_raw, &key->priv, key->priv_data, msg, (word32)msgSz, sig); @@ -1754,11 +1710,6 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, * to sign with again. */ key->state = WC_LMS_STATE_OK; } - else if (started) { - /* A one-time key may be in sig; the NV copy is the key now. */ - ForceZero(sig, key->params->sig_len); - wc_lmskey_wipe_priv(key); - } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index f817fe494ea..f123d9849bc 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -3289,8 +3289,6 @@ static void wc_lms_priv_state_store(const LmsParams* params, static void wc_lms_priv_state_copy(const LmsParams* params, LmsPrivState* dst, const LmsPrivState* src) { - ForceZero(dst->auth_path, LMS_PRIV_STATE_LEN(params->height, - params->rootLevels, params->cacheBits, params->hash_len)); XMEMCPY(dst->auth_path, src->auth_path, LMS_PRIV_STATE_LEN(params->height, params->rootLevels, params->cacheBits, params->hash_len)); dst->stack.offset = src->stack.offset; @@ -4469,8 +4467,6 @@ static int wc_hss_derive_seed_i(LmsState* state, const byte* id, /* Copy part of hash as new I into private key. */ XMEMCPY(seed_i, tmp, LMS_I_LEN); } - /* buffer held the parent SEED (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(buffer, sizeof(buffer)); return ret; } @@ -4551,8 +4547,6 @@ static int wc_hss_expand_private_key(LmsState* state, byte* priv, c32toa(q32, priv); if (!skip) { - /* Never destroy a SEED by writing the next one over it. */ - ForceZero(priv + LMS_Q_LEN, params->hash_len + LMS_I_LEN); /* Derive SEED and I into private key. */ ret = wc_hss_derive_seed_i(state, priv_seed_i + params->hash_len, priv_seed_i, priv_q, priv + LMS_Q_LEN); @@ -4601,7 +4595,6 @@ static int wc_lms_next_subtree_init(LmsState* state, LmsPrivState* privState, privState->leaf.offset = 0; /* Derive SEED and I for next tree. */ - ForceZero(priv + LMS_Q_LEN, params->hash_len + LMS_I_LEN); ret = wc_hss_derive_seed_i(state, priv_i, priv_seed, priv_q, priv + LMS_Q_LEN); if (ret == 0) { diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index dfb5799ab53..81f7ed29731 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -9240,22 +9240,11 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, { /* The seeds are now staged in the key as the contiguous * SK.seed || SK.prf || PK.seed the callback expects. */ - key->flags &= ~((int)WC_SLHDSA_FLAG_BOTH_KEYS); ret = wc_CryptoCb_MakePqcSignatureKeyEx(NULL, WC_PQC_SIG_TYPE_SLHDSA, (int)key->params->param, key->sk, 3U * key->params->n, key); - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { - if ((ret != 0) || - ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0)) { - /* A failed generation or a device-owned key leaves no - * software private key (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(key->sk, 2U * key->params->n); - } - if (ret != 0) { - key->flags = 0; - } + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; - } /* fall-through when unavailable */ ret = 0; } @@ -9265,7 +9254,6 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, #ifdef WOLF_CRYPTO_CB_ONLY_SLHDSA if (ret == 0) { ret = NO_VALID_DEVID; - ForceZero(key->sk, 2U * key->params->n); } #else if (ret == 0) { diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c index 0ced5dc52f6..c69150ebb10 100644 --- a/wolfcrypt/src/wc_xmss.c +++ b/wolfcrypt/src/wc_xmss.c @@ -1347,7 +1347,6 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) if (ret != 0) { WOLFSSL_MSG("error: XMSS keygen failed"); key->state = WC_XMSS_STATE_BAD; - ForceZero(key->sk, key->sk_len); } /* Free state after use. */ wc_xmss_state_free(state); @@ -1685,19 +1684,15 @@ int wc_XmssKey_SigsLeft(XmssKey* key) WOLFSSL_MSG("error: XMSS secret key not held in software"); ret = 0; } + /* Read the current secret key from NV storage.*/ + else if (key->read_private_key(key->sk, key->sk_len, key->context) != + WC_XMSS_RC_READ_TO_MEMORY) { + WOLFSSL_MSG("error: XMSS read_private_key failed"); + ret = 0; + } else { - /* Read the current secret key from NV storage.*/ - if (key->read_private_key(key->sk, key->sk_len, key->context) != - WC_XMSS_RC_READ_TO_MEMORY) { - WOLFSSL_MSG("error: XMSS read_private_key failed"); - ret = 0; - } - else { - /* Ask implementation to check index in private key. */ - ret = wc_xmss_sigsleft(key->params, key->sk); - } - /* Only the index was needed (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(key->sk, key->sk_len); + /* Ask implementation to check index in private key. */ + ret = wc_xmss_sigsleft(key->params, key->sk); } #endif diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index a7ac6c94a79..bd69a436cba 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -658,8 +658,6 @@ struct ecc_key { WC_RNG* rng; #endif #ifdef WC_ECC_NONBLOCK - /* Caller-owned. Detach with wc_ecc_set_nonblock(key, NULL) before this - * storage expires so it is wiped; wc_ecc_free does not dereference it. */ ecc_nb_ctx_t* nb_ctx; #endif #ifdef WOLFSSL_DHUK @@ -1229,11 +1227,6 @@ int wc_ecc_get_key_id(ecc_key* key, word32* keyId); #endif #ifdef WC_ECC_NONBLOCK - /* Attaching resets ctx; replacing/detaching wipes the previous live ctx. - * The caller owns its storage and must serialize access. Detach on - * completion or cancellation before releasing ctx. After cancelling an - * unfinished operation, free/reinitialize the key before starting another. - * wc_ecc_free alone does not wipe caller-owned ctx storage. */ WOLFSSL_API int wc_ecc_set_nonblock(ecc_key *key, ecc_nb_ctx_t* ctx); #endif diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index 55abe288357..5a9346632c6 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -286,9 +286,6 @@ struct RsaKey { byte dataIsAlloc; #endif #ifdef WC_RSA_NONBLOCK - /* Caller-owned. Detach with wc_RsaSetNonBlock(key, NULL) while this - * storage is still live to wipe it before release; FreeRsaKey does not - * dereference it. See the setter's lifetime contract below. */ RsaNb* nb; #endif #ifdef WOLFSSL_AFALG_XILINX_RSA @@ -454,11 +451,6 @@ WOLFSSL_API int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, WOLFSSL_API int wc_RsaClearRNG(RsaKey* key); #endif #ifdef WC_RSA_NONBLOCK - /* Attaching resets nb; replacing/detaching wipes the previous live nb. - * The caller owns both allocations and must serialize access. Detach - * before nb expires, on completion or cancellation. After cancelling an - * unfinished operation, free/reinitialize the key before starting another. - * FreeRsaKey alone does not wipe caller-owned nb storage. */ WOLFSSL_API int wc_RsaSetNonBlock(RsaKey* key, RsaNb* nb); /* maxBlockInst budget lives on the TFM exptModNb_t; only available * with USE_FAST_MATH. SP-only WC_RSA_NONBLOCK builds do not expose From 387faebfb59b41e897f9fae9b17b91631b3e0701 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 14 Oct 2026 10:00:43 -0600 Subject: [PATCH 49/74] Fix the zeroization gaps from the round-2 review ML-KEM wipes its hash objects at every exit (FIPS 203 3.3). GCM wipes the raw GHASH, the stale H table, the stream state and partial plaintext on a primitive failure. OAEP unpad wipes its whole scratch. The ECC ladder tmp is wiped on every exit and a rejected private import clears the key type. EdDSA frees the persistent hash on failure. The stir purge keeps the old EMPTY store when its claim fails. ecc.c reads the shared struct layouts before its local small-stack override. --- wolfcrypt/src/aes.c | 45 +++++++++++++++++++++++++++++++--------- wolfcrypt/src/ecc.c | 30 +++++++++++++++++++-------- wolfcrypt/src/ed25519.c | 2 +- wolfcrypt/src/ed448.c | 2 +- wolfcrypt/src/random.c | 5 ++++- wolfcrypt/src/rsa.c | 21 +++++++++---------- wolfcrypt/src/wc_mlkem.c | 14 ++++++++----- 7 files changed, 81 insertions(+), 38 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 92116e774ca..613280e279c 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -9464,6 +9464,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; } @@ -11926,8 +11933,11 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( GHASH(&aes->gcm, authIn, authInSz, out, sz, authTag, authTagSz); ret = AesEncrypt_preFetchOpt(aes, initialCounter, scratch, &did_prefetches); - if (ret != 0) + 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) @@ -12730,7 +12740,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) - goto done; + 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 @@ -12764,7 +12774,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) - goto done; + goto error; } /* process remainder using partial handling */ #endif @@ -12784,8 +12794,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); - goto done; + goto error; } xorbuf(out, c, WC_AES_BLOCK_SIZE * blocks); c += WC_AES_BLOCK_SIZE * blocks; @@ -12798,7 +12807,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( #if !defined(WOLFSSL_PIC32MZ_CRYPT) ret = wc_AesEncrypt(aes, counter, scratch); if (ret != 0) - goto done; + goto error; xorbufout(p, scratch, c, WC_AES_BLOCK_SIZE); #endif p += WC_AES_BLOCK_SIZE; @@ -12810,7 +12819,7 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( IncrementGcmCounter(counter); ret = wc_AesEncrypt(aes, counter, scratch); if (ret != 0) - goto done; + goto error; xorbuf(scratch, c, partial); XMEMCPY(p, scratch, partial); } @@ -12839,6 +12848,12 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( 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); @@ -13369,7 +13384,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); @@ -13388,7 +13403,7 @@ static WARN_UNUSED_RESULT int AesGcmCryptUpdate_C( ret = wc_AesEncrypt(aes, AES_COUNTER(aes), scratch); if (ret != 0) { ForceZero(scratch, sizeof(scratch)); - return ret; + goto error; } /* XOR plain text into encrypted counter into cipher text buffer. */ xorbufout(out, scratch, in, WC_AES_BLOCK_SIZE); @@ -13405,7 +13420,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. */ @@ -13413,6 +13428,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. diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 67c86f3e8fc..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 @@ -3316,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 */ @@ -3459,8 +3468,6 @@ static int ecc_mulmod(const mp_int* k, ecc_point* P, ecc_point* Q, err = mp_cond_swap_ct_ex(R[0]->z, R[1]->z, (int)modulus->used, (int)b, tmp); } - /* tmp holds a scalar bit (ISO/IEC 19790:2012 7.9.7). */ - mp_forcezero(tmp); #endif } @@ -3474,6 +3481,11 @@ static int ecc_mulmod(const mp_int* k, ecc_point* P, ecc_point* Q, /* 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 @@ -12423,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) { @@ -12528,11 +12543,8 @@ static int _ecc_import_private_key_ex(const byte* priv, word32 privSz, if (ret != 0) { /* Rejected scalar must not stay in the key * (ISO/IEC 19790:2012 7.9.7). */ - mp_forcezero(key->k); - #ifdef WOLFSSL_ECC_BLIND_K - mp_forcezero(key->kb); - mp_forcezero(key->ku); - #endif + ecc_forcezero_k(key); + key->type = (pub != NULL) ? ECC_PUBLICKEY : 0; } #ifdef WOLFSSL_MAXQ10XX_CRYPTO diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index f25447f5730..93ddfdabc16 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -702,7 +702,7 @@ int wc_ed25519_sign_msg_ex(const byte* in, word32 inLen, byte* out, #else /* On failure the absorbed prefix must not stay in the key's hash. */ if (ret != 0) - (void)ed25519_hash_reset(key); + ed25519_hash_free(key, sha); #endif } diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index cc524392e94..d2cc4096f86 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -689,7 +689,7 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, #else /* On failure the absorbed prefix must not stay in the key's hash. */ if (ret != 0) { - (void)ed448_hash_reset(key); + ed448_hash_free(key, sha); } #endif } diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d2b6a74d63b..5a100b7cfa5 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1157,8 +1157,11 @@ static WC_MAYBE_UNUSED void NextStirPurge(WC_RNG* rng) &rng->nextStirLen, cur, WC_DRBG_NEXT_SEED_CONSUMING, ret); } WC_CAS_WITH_RETRY_END; - if (ret != 0) + if (ret != 0) { + /* No claim, so no wipe; still drop the material as before. */ + WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); return; + } ForceZero(rng->nextStir, (word32)sizeof(rng->nextStir)); WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index a97f986e4ab..6a85eab2256 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -1873,10 +1873,10 @@ 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, hLen); + ForceZero(tmp, pkcsBlockLen); #if defined(WOLFSSL_CHECK_MEM_ZERO) && \ (!defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)) - wc_MemZero_Check(tmp, hLen); + wc_MemZero_Check(tmp, pkcsBlockLen); #endif WC_FREE_VAR_EX(tmp, heap, DYNAMIC_TYPE_RSA_BUFFER); return ret; @@ -1888,12 +1888,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; } @@ -1901,12 +1901,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; diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 1e2ec84f184..24f6a609d9d 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -688,7 +688,8 @@ int wc_MlKemKey_Free(MlKemKey* key) (!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)) -/* Wipe a SHA-3 object that a failed step left mid-operation. */ +/* 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 @@ -1087,8 +1088,6 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, ForceZero(key->priv, sizeof(key->priv)); #endif ForceZero(key->z, sizeof(key->z)); - mlkemkey_hash_wipe(&key->hash); - mlkemkey_hash_wipe(&key->prf); } /* Zeroize the secret seed material in rho||sigma (sigma) before return. */ @@ -1210,6 +1209,11 @@ 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); + } + return ret; #endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ } @@ -1925,7 +1929,7 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, /* FIPS 203 3.3: a failed encapsulation leaves no ciphertext. */ ForceZero(ct, ctSz); } - if ((ret != 0) && (key != NULL)) { + if (key != NULL) { mlkemkey_hash_wipe(&key->hash); mlkemkey_hash_wipe(&key->prf); } @@ -2380,7 +2384,7 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, wc_MemZero_Check(msg, sizeof(msg)); wc_MemZero_Check(kr, sizeof(kr)); #endif - if ((ret != 0) && (key != NULL)) { + if (key != NULL) { mlkemkey_hash_wipe(&key->hash); mlkemkey_hash_wipe(&key->prf); } From 4b427b27a4125226c31f1944107d14725f1b2db1 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 14 Oct 2026 10:00:43 -0600 Subject: [PATCH 50/74] tests: cover the round-2 zeroization fixes --- tests/api/test_aes.c | 55 +++++++++++++++++++++++++++ tests/api/test_aes.h | 2 + tests/api/test_ecc.c | 13 +++++++ tests/api/test_mlkem.c | 81 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_mlkem.h | 4 +- tests/api/test_rsa.c | 83 +++++++++++++++++++++++++++++++++++++++++ tests/api/test_rsa.h | 2 + tests/api/test_slhdsa.c | 2 +- 8 files changed, 240 insertions(+), 2 deletions(-) diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c index a4167fc9c7b..34925097389 100644 --- a/tests/api/test_aes.c +++ b/tests/api/test_aes.c @@ -14667,3 +14667,58 @@ 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) + 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..7c27a23524b 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -1419,6 +1419,19 @@ 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) + /* Rejecting a private scalar must also revoke private-key capability. */ + 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_mlkem.c b/tests/api/test_mlkem.c index d2fc6df953d..0a6486bc8b9 100644 --- a/tests/api/test_mlkem.c +++ b/tests/api/test_mlkem.c @@ -5599,3 +5599,84 @@ 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)); +#ifdef WC_SHA3_SCRATCH_W + ExpectBufEQ(hash->scratch, zero, sizeof(hash->scratch)); +#endif + 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..f2efc977634 100644 --- a/tests/api/test_rsa.c +++ b/tests/api/test_rsa.c @@ -2630,3 +2630,86 @@ 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) + 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 a3892c4c5a9..45d2e0a40ad 100644 --- a/tests/api/test_slhdsa.c +++ b/tests/api/test_slhdsa.c @@ -954,7 +954,7 @@ int test_wc_slhdsa_sign_vfy(void) byte* sig = NULL; word32 sigLen; byte ctx[10]; -#ifdef WOLFSSL_SLHDSA_PARAM_SHA2_128F +#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F) const byte zeroDigest[WC_SHA256_DIGEST_SIZE] = {0}; #endif From 2c0e1cfbb6fd35a9f73904b6ec192c0f4f070d26 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 14 Oct 2026 10:48:41 -0600 Subject: [PATCH 51/74] tests: guard the new zeroization tests for builds that swap the sources The ECC import type check is skipped on the device set-key path: with WOLF_CRYPTO_CB_SETKEY and WOLF_CRYPTO_CB_FIND the import is staged in a temporary key, so a rejected scalar leaves the caller's key as it was. The RSA OAEP and AES-GCM failure-wipe tests are skipped under HAVE_SELFTEST, where the CAVP build replaces rsa.c and aes.c with the tagged selftest sources. --- tests/api/test_aes.c | 3 ++- tests/api/test_ecc.c | 7 +++++-- tests/api/test_rsa.c | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c index 34925097389..7aeeceb83b4 100644 --- a/tests/api/test_aes.c +++ b/tests/api/test_aes.c @@ -14674,7 +14674,8 @@ 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(WOLFSSL_NO_ASM) && !defined(WOLF_CRYPTO_CB_ONLY_AES) && \ + !defined(HAVE_SELFTEST) Aes aes; const byte key[16] = {0}; const byte iv[12] = {0}; diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index 7c27a23524b..a6ebf92ac3e 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -1422,8 +1422,11 @@ int test_wc_ecc_import_private_key(void) #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) - /* Rejecting a private scalar must also revoke private-key capability. */ + !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 the old one stays valid. */ XMEMSET(privKey, 0, privKeySz); ExpectIntEQ(wc_ecc_import_private_key(privKey, privKeySz, x963Key, x963KeySz, &keyImp), WC_NO_ERR_TRACE(ECC_PRIV_KEY_E)); diff --git a/tests/api/test_rsa.c b/tests/api/test_rsa.c index f2efc977634..6c3e803571c 100644 --- a/tests/api/test_rsa.c +++ b/tests/api/test_rsa.c @@ -2657,7 +2657,8 @@ int test_wc_RsaOaep_EncryptFailureWipe(void) #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(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST) RsaKey key; WC_RNG rng; byte out[TEST_RSA_BYTES + 8]; From c228159f183fac39f960a7382e3157863e121306 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 14 Oct 2026 14:37:16 -0600 Subject: [PATCH 52/74] ecc: free the old key contents before an import re-inits them Importing over a live key dropped the previous scalar and ordinates without a wipe or a free. Caught by the async valgrind run. --- wolfcrypt/src/ecc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index ae2d85618c5..390213bc2ba 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -11684,6 +11684,11 @@ static int _ecc_import_x963_ex2(const byte* in, word32 inLen, ecc_key* key, alt_fp_init(key->ku); #endif #else + /* Free the old key contents: re-init alone would leak them unwiped. */ + mp_clear(key->pubkey.x); + mp_clear(key->pubkey.y); + mp_clear(key->pubkey.z); + ecc_forcezero_k(key); err = mp_init_multi(key->k, key->pubkey.x, key->pubkey.y, key->pubkey.z, #ifndef WOLFSSL_ECC_BLIND_K NULL, NULL @@ -12792,6 +12797,11 @@ static int _ecc_import_raw_private(ecc_key* key, const char* qx, alt_fp_init(key->ku); #endif #else + /* Free the old key contents: re-init alone would leak them unwiped. */ + mp_clear(key->pubkey.x); + mp_clear(key->pubkey.y); + mp_clear(key->pubkey.z); + ecc_forcezero_k(key); err = mp_init_multi(key->k, key->pubkey.x, key->pubkey.y, key->pubkey.z, #ifndef WOLFSSL_ECC_BLIND_K NULL, NULL From b4e0e1b7f817d74d2b762efe1cd59acb6b17324a Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 15 Oct 2026 12:19:47 -0600 Subject: [PATCH 53/74] ecc: do not read the key before an import re-inits it wolfBoot's keytools imports into a key that was never initialised, so the cleanup from 63d8a614a read garbage and crashed. The test now rejects the scalar into a fresh key instead of re-importing over a live one. --- tests/api/test_ecc.c | 5 ++++- wolfcrypt/src/ecc.c | 10 ---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index a6ebf92ac3e..ac8287685e9 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -1426,7 +1426,10 @@ int test_wc_ecc_import_private_key(void) !(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 the old one stays valid. */ + * 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)); diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 390213bc2ba..ae2d85618c5 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -11684,11 +11684,6 @@ static int _ecc_import_x963_ex2(const byte* in, word32 inLen, ecc_key* key, alt_fp_init(key->ku); #endif #else - /* Free the old key contents: re-init alone would leak them unwiped. */ - mp_clear(key->pubkey.x); - mp_clear(key->pubkey.y); - mp_clear(key->pubkey.z); - ecc_forcezero_k(key); err = mp_init_multi(key->k, key->pubkey.x, key->pubkey.y, key->pubkey.z, #ifndef WOLFSSL_ECC_BLIND_K NULL, NULL @@ -12797,11 +12792,6 @@ static int _ecc_import_raw_private(ecc_key* key, const char* qx, alt_fp_init(key->ku); #endif #else - /* Free the old key contents: re-init alone would leak them unwiped. */ - mp_clear(key->pubkey.x); - mp_clear(key->pubkey.y); - mp_clear(key->pubkey.z); - ecc_forcezero_k(key); err = mp_init_multi(key->k, key->pubkey.x, key->pubkey.y, key->pubkey.z, #ifndef WOLFSSL_ECC_BLIND_K NULL, NULL From 920170cfd11fd36706de817b199a3af8dd80ea9e Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Mon, 19 Oct 2026 12:42:14 -0600 Subject: [PATCH 54/74] Wipe HMAC and SHAKE object state at Free, not per Final or absorb --- wolfcrypt/src/hmac.c | 2 -- wolfcrypt/src/sha3.c | 6 ------ 2 files changed, 8 deletions(-) diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index 6d1a94a2b05..b1a24dc6752 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -1682,8 +1682,6 @@ int wc_HmacFinal(Hmac* hmac, byte* hash) break; } - /* The inner digest is keyed output (ISO/IEC 19790 7.9.7). */ - ForceZero(hmac->innerHash, sizeof(hmac->innerHash)); if (ret == 0) { hmac->innerHashKeyed = 0; } diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 7879bb80fce..28bacd854a3 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -2452,9 +2452,6 @@ int wc_Shake128_Absorb(wc_Shake* shake, const byte* data, word32 len) byte hash[1]; ret = Sha3Final(shake, 0x1f, hash, WC_SHA3_128_COUNT, 0); } - /* Sha3Final does not clear t; the absorbed seed would stay for the - * squeeze lifetime (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(shake->t, sizeof(shake->t)); /* No partial data. */ shake->i = 0; @@ -2804,9 +2801,6 @@ int wc_Shake256_Absorb(wc_Shake* shake, const byte* data, word32 len) byte hash[1]; ret = Sha3Final(shake, 0x1f, hash, WC_SHA3_256_COUNT, 0); } - /* Sha3Final does not clear t; the absorbed seed would stay for the - * squeeze lifetime (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(shake->t, sizeof(shake->t)); /* No partial data. */ shake->i = 0; From 29502540620b04dc57758bff0abd9a3cb602bdc5 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 20 Oct 2026 09:35:06 -0600 Subject: [PATCH 55/74] random: drop the Hash_gen digest wipes duplicated by the rebase --- wolfcrypt/src/random.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 5a100b7cfa5..d755231a79c 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1794,8 +1794,6 @@ static WARN_UNUSED_RESULT int Hash_gen(DRBG_internal* drbg, byte* out, wc_MemZero_Check(digest, WC_SHA256_DIGEST_SIZE); #endif #endif - /* digest holds the last output block (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(digest, WC_SHA256_DIGEST_SIZE); #ifndef WOLFSSL_SMALL_STACK_CACHE WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST); @@ -2466,8 +2464,6 @@ static WARN_UNUSED_RESULT int Hash512_gen(DRBG_SHA512_internal* drbg, wc_MemZero_Check(digest, WC_SHA512_DIGEST_SIZE); #endif #endif - /* See Hash_gen. */ - ForceZero(digest, WC_SHA512_DIGEST_SIZE); #ifndef WOLFSSL_SMALL_STACK_CACHE WC_FREE_VAR_EX(digest, drbg->heap, DYNAMIC_TYPE_DIGEST); From 1708a7facae282b09beb9116c0753f25907ebc0f Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 20 Oct 2026 12:01:15 -0600 Subject: [PATCH 56/74] aes: restore the 4-bit GHASH multiply from master --- wolfcrypt/src/aes.c | 113 +++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 69 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 613280e279c..c81bccbf428 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -8910,15 +8910,6 @@ static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) #define GCM_RESTRICT #endif -/* Keep the 4-bit multiply in its block loop after adding a scratch pointer. - * Some compilers otherwise outline it despite WC_INLINE, adding a call for - * every block. Honor NO_INLINE builds. */ -#if defined(__GNUC__) && !defined(NO_INLINE) - #define GCM_4BIT_INLINE WC_INLINE __attribute__((always_inline)) -#else - #define GCM_4BIT_INLINE WC_INLINE -#endif - typedef union GcmScratch { byte b[2 * WC_AES_BLOCK_SIZE]; word32 w32[2 * WC_AES_BLOCK_SIZE / sizeof(word32)]; @@ -10186,12 +10177,12 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #if !defined(WOLFSSL_ARMASM_NO_NEON) && defined(__aarch64__) #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len_NEON(x, (const byte*)((gcm)->H), a, len) -#define GMULT(x, m, ws) \ +#define GMULT(x, m) \ GCM_gmult_NEON(x, (const byte**)m) #else #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len(x, (const byte**)((gcm)->M0), a, len) -#define GMULT(x, m, ws) \ +#define GMULT(x, m) \ GCM_gmult(x, (const byte**)m) #endif @@ -10199,11 +10190,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #elif (defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM)) #define GCM_GMULT_LEN(gcm, x, a, len) \ GCM_gmult_len(x, (const byte**)((gcm)->M0), a, len) -#define GMULT(x, m, ws) \ +#define GMULT(x, m) \ GCM_gmult(x, (const byte**)m) #else -#define GCM_C_GMULT /* remainder = x^7 + x^2 + x^1 + 1 => 0xe1 * R shifts right a reverse bit pair of bytes such that: * R(b0, b1) => b1 = (b1 >> 1) | (b0 << 7); b0 >>= 1 @@ -10261,14 +10251,13 @@ static const word16 R[32] = { * [0..15] * H */ #if defined(WC_16BIT_CPU) -static void GMULT(byte *x, byte m[16][WC_AES_BLOCK_SIZE], - GcmScratch* GCM_RESTRICT ws) +static void GMULT(byte *x, byte m[16][WC_AES_BLOCK_SIZE]) { int i, j, n; - byte* Z = ws->b; + byte Z[WC_AES_BLOCK_SIZE]; byte a; - XMEMSET(Z, 0, WC_AES_BLOCK_SIZE); + XMEMSET(Z, 0, sizeof(Z)); for (i = 15; i >= 0; i--) { for (n = 0; n < 2; n++) { @@ -10293,18 +10282,15 @@ static void GMULT(byte *x, byte m[16][WC_AES_BLOCK_SIZE], XMEMCPY(x, Z, WC_AES_BLOCK_SIZE); } #elif defined(WC_32BIT_CPU) && defined(BIG_ENDIAN_ORDER) -static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], - GcmScratch* GCM_RESTRICT ws) +static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) { int i; - word32* z8 = ws->w32; + word32 z8[4] = {0, 0, 0, 0}; byte a; word32* x8 = (word32*)x; word32* m8; byte xi; - XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); - for (i = 15; i > 0; i--) { xi = x[i]; @@ -10361,17 +10347,15 @@ static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], x8[0] = z8[0]; x8[1] = z8[1]; x8[2] = z8[2]; x8[3] = z8[3]; } #elif defined(WC_32BIT_CPU) -static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], - GcmScratch* GCM_RESTRICT ws) +static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) { int i; - word32* z8 = ws->w32; + word32 z8[4] = {0, 0, 0, 0}; byte a; word32* x8 = (word32*)x; word32* m8; byte xi; - - XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); + word32 n7, n6, n5, n4, n3, n2, n1, n0; for (i = 15; i > 0; i--) { xi = x[i]; @@ -10414,17 +10398,18 @@ static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], a = (z8[3] >> 24) & 0xf; /* Rotate z by 4-bits */ - z8[3] = ((z8[3] & 0xf0f0f0f0U) >> 4) | - ((z8[3] & 0x0f0f0f0fU) << 12) | - ((z8[2] & 0x0f0f0f0fU) >> 20); - z8[2] = ((z8[2] & 0xf0f0f0f0U) >> 4) | - ((z8[2] & 0x0f0f0f0fU) << 12) | - ((z8[1] & 0x0f0f0f0fU) >> 20); - z8[1] = ((z8[1] & 0xf0f0f0f0U) >> 4) | - ((z8[1] & 0x0f0f0f0fU) << 12) | - ((z8[0] & 0x0f0f0f0fU) >> 20); - z8[0] = ((z8[0] & 0xf0f0f0f0U) >> 4) | - ((z8[0] & 0x0f0f0f0fU) << 12); + n7 = z8[3] & 0xf0f0f0f0ULL; + n6 = z8[3] & 0x0f0f0f0fULL; + n5 = z8[2] & 0xf0f0f0f0ULL; + n4 = z8[2] & 0x0f0f0f0fULL; + n3 = z8[1] & 0xf0f0f0f0ULL; + n2 = z8[1] & 0x0f0f0f0fULL; + n1 = z8[0] & 0xf0f0f0f0ULL; + n0 = z8[0] & 0x0f0f0f0fULL; + z8[3] = (n7 >> 4) | (n6 << 12) | (n4 >> 20); + z8[2] = (n5 >> 4) | (n4 << 12) | (n2 >> 20); + z8[1] = (n3 >> 4) | (n2 << 12) | (n0 >> 20); + z8[0] = (n1 >> 4) | (n0 << 12); /* XOR in most significant nibble * remainder */ z8[0] ^= (word32)R[a]; @@ -10436,18 +10421,15 @@ static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], x8[0] = z8[0]; x8[1] = z8[1]; x8[2] = z8[2]; x8[3] = z8[3]; } #elif defined(WC_64BIT_CPU) && defined(BIG_ENDIAN_ORDER) -static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], - GcmScratch* GCM_RESTRICT ws) +static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) { int i; - word64* z8 = ws->w64; + word64 z8[2] = {0, 0}; byte a; word64* x8 = (word64*)x; word64* m8; byte xi; - XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); - for (i = 15; i > 0; i--) { xi = x[i]; @@ -10503,18 +10485,16 @@ static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], x8[1] = z8[1]; } #else -static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], - GcmScratch* GCM_RESTRICT ws) +static WC_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE]) { int i; - word64* z8 = ws->w64; + word64 z8[2] = {0, 0}; byte a; word64* x8 = (word64*)x; word64* m8; + word64 n0, n1, n2, n3; byte xi; - XMEMSET(z8, 0, WC_AES_BLOCK_SIZE); - for (i = 15; i > 0; i--) { xi = x[i]; @@ -10554,11 +10534,12 @@ static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], a = (z8[1] >> 56) & 0xf; /* Rotate z by 4-bits */ - z8[1] = ((z8[1] & W64LIT(0xf0f0f0f0f0f0f0f0)) >> 4) | - ((z8[1] & W64LIT(0x0f0f0f0f0f0f0f0f)) << 12) | - ((z8[0] & W64LIT(0x0f0f0f0f0f0f0f0f)) >> 52); - z8[0] = ((z8[0] & W64LIT(0xf0f0f0f0f0f0f0f0)) >> 4) | - ((z8[0] & W64LIT(0x0f0f0f0f0f0f0f0f)) << 12); + n3 = z8[1] & W64LIT(0xf0f0f0f0f0f0f0f0); + n2 = z8[1] & W64LIT(0x0f0f0f0f0f0f0f0f); + n1 = z8[0] & W64LIT(0xf0f0f0f0f0f0f0f0); + n0 = z8[0] & W64LIT(0x0f0f0f0f0f0f0f0f); + z8[1] = (n3 >> 4) | (n2 << 12) | (n0 >> 52); + z8[0] = (n1 >> 4) | (n0 << 12); /* XOR in next significant nibble * H */ m8 = (word64*)m[xi >> 4]; @@ -10577,9 +10558,6 @@ static GCM_4BIT_INLINE void GMULT(byte *x, byte m[32][WC_AES_BLOCK_SIZE], 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; @@ -10603,14 +10581,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, &gmultScratch); + GMULT(x, gcm->M0); 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, &gmultScratch); + GMULT(x, gcm->M0); } #endif } @@ -10632,14 +10610,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, &gmultScratch); + GMULT(x, gcm->M0); 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, &gmultScratch); + GMULT(x, gcm->M0); } #endif } @@ -10651,15 +10629,12 @@ 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, &gmultScratch); + GMULT(x, gcm->M0); #endif /* Copy the result into s. */ XMEMCPY(s, x, sSz); ForceZero(x, sizeof(x)); -#ifdef GCM_C_GMULT - ForceZero(&gmultScratch, sizeof(gmultScratch)); -#endif } #ifdef WOLFSSL_AESGCM_STREAM @@ -10688,11 +10663,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, &gmultScratch); \ - } \ +#define GHASH_ONE_BLOCK_SW(aes, block) \ + do { \ + xorbuf(AES_TAG(aes), block, WC_AES_BLOCK_SIZE); \ + GMULT(AES_TAG(aes), (aes)->gcm.M0); \ + } \ while (0) #endif #endif /* WOLFSSL_AESGCM_STREAM */ From aadd47a3ece65311f6e0b069015012af7a519ba6 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 20 Oct 2026 12:37:43 -0600 Subject: [PATCH 57/74] Add WOLFSSL_CHECK_MEM_ZERO coverage for the new SSP wipes --- wolfcrypt/src/aes.c | 146 +++++++++++++++++++++++++++++++++++ wolfcrypt/src/cmac.c | 6 ++ wolfcrypt/src/ed25519.c | 18 +++++ wolfcrypt/src/ed448.c | 10 +++ wolfcrypt/src/kdf.c | 30 +++++++ wolfcrypt/src/rng_bank.c | 7 ++ wolfcrypt/src/rsa.c | 78 ++++++++++++++++++- wolfcrypt/src/sha.c | 13 ++++ wolfcrypt/src/sha256.c | 28 +++++++ wolfcrypt/src/sha3.c | 6 ++ wolfcrypt/src/sha512.c | 20 +++++ wolfcrypt/src/wc_lms_impl.c | 28 +++++++ wolfcrypt/src/wc_mldsa.c | 22 ++++++ wolfcrypt/src/wc_mlkem.c | 59 ++++++++++++++ wolfcrypt/src/wc_slhdsa.c | 120 ++++++++++++++++++++++++++++ wolfcrypt/src/wc_xmss_impl.c | 6 ++ 16 files changed, 593 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index c81bccbf428..d5552684745 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -9683,6 +9683,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) { @@ -9730,6 +9733,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #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 @@ -10057,6 +10063,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, 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) { @@ -10132,6 +10141,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #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 @@ -10723,6 +10735,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, 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); @@ -10822,6 +10838,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #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 */ @@ -11047,6 +11067,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, 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); @@ -11140,6 +11164,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #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 @@ -11825,6 +11853,13 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( int did_prefetches = 0; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + 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); @@ -11927,6 +11962,11 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( /* 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)) || \ @@ -11943,6 +11983,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); @@ -12051,6 +12098,12 @@ static int AES_GCM_encrypt_ASM(Aes* aes, byte* out, const byte* in, /* 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 @@ -12692,6 +12745,13 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( word32 i; #endif +#ifdef WOLFSSL_CHECK_MEM_ZERO + 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); @@ -12836,6 +12896,12 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( 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; } #elif (defined(__aarch64__) || defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) || \ @@ -12853,6 +12919,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); @@ -12955,6 +13028,12 @@ static int AES_GCM_decrypt_ASM(Aes* aes, byte* out, const byte* in, /* 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 @@ -13270,6 +13349,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); @@ -13294,6 +13377,9 @@ static WARN_UNUSED_RESULT int AesGcmInit_C(Aes* aes, const byte* iv, word32 ivSz /* 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. */ @@ -15767,6 +15853,10 @@ 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 + 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(); @@ -15778,6 +15868,9 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz) } /* 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 } } @@ -17762,6 +17855,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 @@ -17771,6 +17868,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)) { @@ -17809,9 +17909,15 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, 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 @@ -18380,6 +18486,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++) { @@ -18417,6 +18526,9 @@ static int AesKeyWrapRaw(Aes* aes, word32 inSz, byte* out, const byte* aiv) } /* 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 ret; } @@ -18546,6 +18658,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; @@ -18592,6 +18707,9 @@ static int AesKeyUnWrapRaw(Aes* aes, const byte* in, word32 inSz, byte* out, * (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 ret; } @@ -19481,11 +19599,18 @@ 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 + 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; } @@ -19545,6 +19670,9 @@ static int AesXtsEncryptUpdate_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 + wc_MemZero_Add("AesXtsEncryptUpdate_sw buf", buf, sizeof(buf)); +#endif #ifdef HAVE_AES_ECB /* encrypt all of buffer at once when possible */ @@ -19619,6 +19747,9 @@ static int AesXtsEncryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, 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; } @@ -20087,11 +20218,18 @@ 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 + 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; } @@ -20141,6 +20279,10 @@ 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 + 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 */ @@ -20235,6 +20377,10 @@ static int AesXtsDecryptUpdate_sw(XtsAes* xaes, byte* out, const byte* in, 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 527a7077a95..e14dd4b0316 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -693,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, @@ -712,6 +715,9 @@ int wc_AesCmacVerify_ex(Cmac* cmac, } /* 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; } diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 93ddfdabc16..6f0d171fba9 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -401,6 +401,9 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, if (ret == 0) ret = NO_VALID_DEVID; #else +#ifdef WOLFSSL_CHECK_MEM_ZERO + 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,10 +420,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) @@ -433,6 +442,9 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, /* 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; } @@ -720,10 +732,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 } diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index d2cc4096f86..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)); @@ -452,6 +456,10 @@ int wc_ed448_make_public(ed448_key* key, unsigned char* pubKey, word32 pubKeySz) /* 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; } @@ -619,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)); @@ -768,6 +777,7 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out, /* 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 diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 3c2c5c58d4f..9fe011a10c2 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -830,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) @@ -847,11 +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 { @@ -881,6 +891,10 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, 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 /* Reuse the reset context from the preceding Final. */ if (ret == 0) ret = _HashUpdate(enmhashId, &hash, kSzFlat, LENGTH_SZ); @@ -898,6 +912,9 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, 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 } } } @@ -905,6 +922,9 @@ int wc_SSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, _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); } @@ -1604,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 */ @@ -1703,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) { diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 25b1b4b8ab1..dab76cb4e5b 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -3746,8 +3746,15 @@ WOLFSSL_TEST_VIS int wc_RNG_DRBG_Reseed_Now( * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on * success the fresh reseed counter is 2 rather than 1. scratch holds * discarded output bytes. */ +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wc_RNG_DRBG_Reseed_Now scratch", scratch, + sizeof(scratch)); +#endif ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); ForceZero(scratch, sizeof(scratch)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(scratch, sizeof(scratch)); +#endif if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { /* On the legacy boundary, nonce incorporation is a separate diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 6a85eab2256..f59104af1c8 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -1100,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); @@ -1130,6 +1143,15 @@ static int RsaMGF1(enum wc_HashType hType, byte* seed, word32 seedSz, } #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 @@ -1160,6 +1182,15 @@ static int RsaMGF1(enum wc_HashType hType, byte* seed, word32 seedSz, #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) { @@ -1433,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; } @@ -1454,16 +1492,30 @@ 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; } @@ -1472,6 +1524,10 @@ static int RsaPad_OAEP(const byte* input, word32 inputLen, byte* pkcsBlock, /* 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 */ @@ -1481,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); @@ -1617,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); @@ -1639,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; } @@ -1651,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) { @@ -1682,6 +1749,9 @@ static int RsaPad_PSS(const byte* input, word32 inputLen, byte* pkcsBlock, #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) if (msg != NULL) { diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 3ebb043768c..077ea7e69be 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -834,6 +834,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. */ @@ -855,6 +859,9 @@ int wc_ShaFinalRaw(wc_Sha* sha, byte* hash) } 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 @@ -1222,6 +1229,9 @@ 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) { @@ -1230,6 +1240,9 @@ int wc_ShaGetHash(wc_Sha* sha, byte* hash) } 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 06b1ebf32a4..4dce7dc5671 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -2399,6 +2399,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. */ @@ -2410,6 +2414,9 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, } 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 @@ -2489,6 +2496,10 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, { 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__ @@ -2497,6 +2508,9 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, 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 @@ -3424,6 +3438,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) { @@ -3432,6 +3450,9 @@ int wc_Sha224Reset(wc_Sha224* sha224) { } 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; @@ -3574,6 +3595,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) { @@ -3583,6 +3608,9 @@ 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); diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 28bacd854a3..99e3360ab77 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1877,12 +1877,18 @@ 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; diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 1b48212e6ca..01551b99c48 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2793,6 +2793,9 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) if (buffer == NULL) return MEMORY_E; #endif +#if defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_SMALL_STACK_CACHE) + 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) && \ @@ -2822,6 +2825,9 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) XMEMCPY(sha->buffer, buffer, WC_SHA512_BLOCK_SIZE); #endif 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 @@ -3292,6 +3298,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); @@ -3301,6 +3311,9 @@ static int Sha512_Family_GetHash(wc_Sha512* sha512, byte* hash, } 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); @@ -3744,6 +3757,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); @@ -3753,6 +3770,9 @@ int wc_Sha384GetHash(wc_Sha384* sha384, byte* hash) } 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); diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index f123d9849bc..6fee0b72f46 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -502,6 +502,10 @@ 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) { @@ -509,6 +513,9 @@ static WC_INLINE int wc_lms_sha256_192_hash_block(wc_Sha256* sha256, } /* 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; } @@ -528,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. */ @@ -574,6 +585,9 @@ 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; } @@ -591,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, @@ -620,18 +638,28 @@ static WC_INLINE int wc_lms_hash_sha256_192_final(wc_Sha256* sha256, byte* 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; #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 689d8464911..d4fdde8270f 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -9769,6 +9769,9 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) byte* t1 = key->p + MLDSA_PUB_SEED_SZ; byte kBuf[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); @@ -9803,6 +9806,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. */ @@ -9941,6 +9947,9 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed) const byte* s2pt = s2p; word32 s2Stride = (word32)params->s2EncSz / params->k; + #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); @@ -9982,6 +9991,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. */ @@ -10473,6 +10485,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); @@ -10568,6 +10583,9 @@ 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); } @@ -14952,6 +14970,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); diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 24f6a609d9d..de58b7294dd 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -703,6 +703,43 @@ static void mlkemkey_hash_wipe(wc_Sha3* h) (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)); +#ifdef WC_SHA3_SCRATCH_W + wc_MemZero_Add("mlkem key hash scratch", key->hash.scratch, + sizeof(key->hash.scratch)); + wc_MemZero_Add("mlkem key prf scratch", key->prf.scratch, + sizeof(key->prf.scratch)); +#endif +#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)); +#ifdef WC_SHA3_SCRATCH_W + wc_MemZero_Check(key->hash.scratch, sizeof(key->hash.scratch)); + wc_MemZero_Check(key->prf.scratch, sizeof(key->prf.scratch)); +#endif +#else + (void)key; +#endif +} +#endif #endif /******************************************************************************/ @@ -916,6 +953,11 @@ int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, /* 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; } @@ -1212,6 +1254,9 @@ key-pair test required by ISO/IEC 19790:2012 sec 7.10.3.3" 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; @@ -1785,6 +1830,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)) { @@ -1932,6 +1980,9 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, 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 @@ -2304,6 +2355,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. */ @@ -2383,10 +2438,14 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, #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 */ diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 81f7ed29731..8d24a0ef586 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -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; } @@ -1386,6 +1389,9 @@ static int slhdsakey_prf_msg_sha2(SlhDsaKey* key, const byte* sk_prf, XMEMCPY(hash, digest, n); } ForceZero(digest, sizeof(digest)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(digest, sizeof(digest)); +#endif return ret; } @@ -6647,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++) { @@ -6666,6 +6676,9 @@ static int slhdsakey_hash_f_ti_x4(const byte* pk_seed, byte* addr, byte* node, /* 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); } @@ -7087,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); @@ -7121,6 +7137,9 @@ static int slhdsakey_fors_node_x4_z1(SlhDsaKey* key, const byte* sk_seed, /* 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; } @@ -7174,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); @@ -7257,6 +7280,10 @@ static int slhdsakey_fors_node_x4_low(SlhDsaKey* key, const byte* sk_seed, /* 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; @@ -7508,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; @@ -7569,6 +7600,9 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, * (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); } @@ -7633,6 +7667,9 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, 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); @@ -7651,6 +7688,9 @@ static int slhdsakey_fors_node_c(SlhDsaKey* key, const byte* sk_seed, word32 i, } /* 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; @@ -9077,6 +9117,9 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng) byte seeds[3 * SLHDSA_MAX_N]; byte n = key->params->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) { @@ -9086,6 +9129,9 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng) 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 */ @@ -9119,6 +9165,42 @@ static void slhdsakey_wipe_secret_hash(SlhDsaKey* key) } } +#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)); + } + else +#endif + { + 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)); + } +} + +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)); + } + else +#endif + { + wc_Shake* shake = &key->hash.shk.shake; + + wc_MemZero_Check(shake->s, sizeof(shake->s)); + wc_MemZero_Check(shake->t, sizeof(shake->t)); + } +} +#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. @@ -9149,6 +9231,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. */ @@ -9160,6 +9245,9 @@ static int slhdsakey_compute_root(SlhDsaKey* key) 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; } @@ -9218,6 +9306,10 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, byte n = key->params->n; byte seeds[3 * SLHDSA_MAX_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); @@ -9230,6 +9322,9 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed, 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 @@ -9285,6 +9380,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)) { @@ -9303,6 +9401,9 @@ 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 @@ -9358,6 +9459,9 @@ static int slhdsakey_sign(SlhDsaKey* key, byte* md, byte* sig) 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); @@ -9381,6 +9485,9 @@ static int slhdsakey_sign(SlhDsaKey* key, byte* md, byte* sig) ForceZero(sigFors, 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; } @@ -11266,6 +11373,10 @@ int wc_SlhDsaKey_ImportPrivate(SlhDsaKey* key, const byte* priv, word32 privLen) 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 @@ -11276,6 +11387,9 @@ int wc_SlhDsaKey_ImportPrivate(SlhDsaKey* key, const byte* priv, word32 privLen) /* Copy private and public key data into SLH-DSA key object. */ 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) @@ -11396,6 +11510,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 @@ -11409,6 +11526,9 @@ int wc_SlhDsaKey_CheckKey(SlhDsaKey* key) } 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 1423183c578..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); @@ -683,6 +686,9 @@ static WC_INLINE void wc_xmss_hash(XmssState* state, const byte* in, /* 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 From f09453eabdcbc52ab79da853591ebd941ec23bd9 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 21 Oct 2026 15:18:51 -0600 Subject: [PATCH 58/74] sha3: drop the KMAC Final wipe so a reused object stays keyed --- wolfcrypt/src/sha3.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 99e3360ab77..c79fc2f09e1 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -3285,10 +3285,6 @@ static int KmacFinal(wc_Kmac* kmac, byte* out, word32 outLen, int xof) /* cSHAKE domain separation pad (0x04), then squeeze outLen. */ ret = Sha3Final(&kmac->shake, 0x04, out, kmac->count, outLen); } - if (ret == 0) { - /* Keyed sponge is no longer needed (ISO/IEC 19790 7.9.7). */ - wc_Sha3Wipe(&kmac->shake); - } } return ret; } From d464f6a753e9cf2ef85b691cbcd0383d0f35fd7c Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 21 Oct 2026 15:18:51 -0600 Subject: [PATCH 59/74] sha3: restore plain zeroing in InitSha3 --- wolfcrypt/src/sha3.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index c79fc2f09e1..4e14815f812 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -973,9 +973,11 @@ static word64 Load64BitLittleEndian(const byte* a) static int InitSha3(wc_Sha3* sha3) { - /* The sponge held the prior message. */ - ForceZero(sha3->s, sizeof(sha3->s)); - ForceZero(sha3->t, sizeof(sha3->t)); + int i; + + for (i = 0; i < 25; i++) + sha3->s[i] = 0; + XMEMSET(sha3->t, 0, sizeof(sha3->t)); sha3->i = 0; #ifdef WOLFSSL_HASH_FLAGS sha3->flags = 0; From 7f261ddb3cbd71cffbe77467b22f367276c5ce6d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 21 Oct 2026 15:24:06 -0600 Subject: [PATCH 60/74] rng_bank: restore master's scratch clear in the pre-v7 reseed path --- wolfcrypt/src/rng_bank.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index dab76cb4e5b..5ef2b6867f6 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -3745,16 +3745,9 @@ WOLFSSL_TEST_VIS int wc_RNG_DRBG_Reseed_Now( * minimal generate at the forced counter performs the module's own * PollAndReSeed() in-boundary. This consumes 4 bytes of output, so on * success the fresh reseed counter is 2 rather than 1. scratch holds - * discarded output bytes. */ -#ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Add("wc_RNG_DRBG_Reseed_Now scratch", scratch, - sizeof(scratch)); -#endif + * only discarded output bytes; XMEMSET suffices for it here. */ ret = wc_RNG_GenerateBlock(rng, scratch, (word32)sizeof(scratch)); - ForceZero(scratch, sizeof(scratch)); -#ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(scratch, sizeof(scratch)); -#endif + XMEMSET(scratch, 0, sizeof(scratch)); if ((ret == 0) && (nonce != NULL) && (nonceSz > 0)) { /* On the legacy boundary, nonce incorporation is a separate From 517eb2bc36f4d27c460777685b692ec3388e1d8d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 21 Oct 2026 15:36:19 -0600 Subject: [PATCH 61/74] SHA-3: drop the Keccak round scratch from the context Keeping the C round scratch in the context cost 1 to 3 percent on the C lane (SHA-3, SHAKE, KMAC, ML-KEM). The block function and its callers go back to master's local scratch; the state wipes at Free stay. --- tests/api/test_mlkem.c | 3 - wolfcrypt/src/sha3.c | 131 ++++++++++---------------------- wolfcrypt/src/wc_frodokem_mat.c | 4 +- wolfcrypt/src/wc_mldsa.c | 21 ++--- wolfcrypt/src/wc_mlkem.c | 15 ---- wolfcrypt/src/wc_mlkem_poly.c | 51 ++++++------- wolfcrypt/src/wc_slhdsa.c | 7 +- wolfssl/wolfcrypt/sha3.h | 45 +---------- 8 files changed, 79 insertions(+), 198 deletions(-) diff --git a/tests/api/test_mlkem.c b/tests/api/test_mlkem.c index 0a6486bc8b9..ba335cb6393 100644 --- a/tests/api/test_mlkem.c +++ b/tests/api/test_mlkem.c @@ -5666,9 +5666,6 @@ int test_wc_mlkem_operation_cleanup(void) const wc_Sha3* hash = h == 0 ? &key->hash : &key->prf; ExpectBufEQ(hash->s, zero, sizeof(hash->s)); ExpectBufEQ(hash->t, zero, sizeof(hash->t)); -#ifdef WC_SHA3_SCRATCH_W - ExpectBufEQ(hash->scratch, zero, sizeof(hash->scratch)); -#endif ExpectIntEQ(hash->i, 0); } } diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 4e14815f812..c5f315cb17d 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -125,7 +125,7 @@ #define SHA3_BLOCK (sha3->sha3_block) #define SHA3_BLOCK_N (sha3->sha3_block_n) #else - WC_SHA3_BLOCK_FN sha3_block = NULL; + void (*sha3_block)(word64 *s) = NULL; void (*sha3_block_n)(word64 *s, const byte* data, word32 n, word64 c) = NULL; #define SHA3_BLOCK sha3_block @@ -134,25 +134,6 @@ #endif #ifdef USE_INTEL_SPEEDUP -#ifdef WC_SHA3_SCRATCH_W -/* The asm block functions keep no round scratch; give them the C signature - * so one function pointer type serves both. */ -static void sha3_block_avx2_scr(word64* s, void* scratch) -{ - (void)scratch; - sha3_block_avx2(s); -} -static void sha3_block_bmi2_scr(word64* s, void* scratch) -{ - (void)scratch; - sha3_block_bmi2(s); -} - #define SHA3_BLOCK_AVX2_FN sha3_block_avx2_scr - #define SHA3_BLOCK_BMI2_FN sha3_block_bmi2_scr -#else - #define SHA3_BLOCK_AVX2_FN sha3_block_avx2 - #define SHA3_BLOCK_BMI2_FN sha3_block_bmi2 -#endif /* Block-function selection when USE_INTEL_SPEEDUP: BMI2, then AVX2, then * the C block. BMI2 is preferred because its block uses general * registers only and so needs no vector-register save; AVX2 goes first @@ -170,7 +151,7 @@ static void sha3_block_bmi2_scr(word64* s, void* scratch) #if defined(WOLFSSL_SHA3_NO_AVX2) #define SHA3_BLOCK_VREGS(f) 0 #else - #define SHA3_BLOCK_VREGS(f) ((f) == SHA3_BLOCK_AVX2_FN) + #define SHA3_BLOCK_VREGS(f) ((f) == sha3_block_avx2) #endif #endif @@ -202,19 +183,6 @@ static void sha3_block_bmi2_scr(word64* s, void* scratch) #if !defined(WOLFSSL_ARMASM) && !defined(WOLFSSL_RISCV_ASM) && \ !defined(WOLFSSL_PPC64_ASM) && !defined(WOLFSSL_PPC32_ASM) -/* The state and the scratch never overlap; tell the compiler so the lanes can - * stay in registers across the round macros as they did with local arrays. */ -#if defined(__GNUC__) || defined(__clang__) - #define WC_SHA3_RESTRICT __restrict__ -#elif defined(_MSC_VER) - #define WC_SHA3_RESTRICT __restrict -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - #define WC_SHA3_RESTRICT restrict -#else - #define WC_SHA3_RESTRICT -#endif - - #ifdef WOLFSSL_SHA3_SMALL /* Rotate a 64-bit value left. * @@ -380,19 +348,11 @@ while (0) * * s The state. */ -#ifdef WC_SHA3_SCRATCH_W -void BlockSha3(word64* WC_SHA3_RESTRICT s, void* scratch) -{ - byte i, x, y; - word64 t0, t1; - word64* WC_SHA3_RESTRICT b = (word64*)scratch; -#else void BlockSha3(word64* s) { byte i, x, y; word64 t0, t1; word64 b[5]; -#endif for (i = 0; i < 24; i++) { @@ -785,30 +745,18 @@ while (0) WC_SHA3_CHI(DL, DH, 20); \ } while (0) -#ifdef WC_SHA3_SCRATCH_W -void BlockSha3(word64* WC_SHA3_RESTRICT s, void* scratch) +void BlockSha3(word64* s) { /* Process the 25 little-endian lanes as 32-bit halves to avoid 64-bit * helper calls. XMEMCPY in/out (aliasing s through word32* is strict- * aliasing UB); st[2k] is lane k's low half, st[2k+1] the high half. * Round constants are split with shifts for the same reason. */ - word32* WC_SHA3_RESTRICT st = (word32*)scratch; - word32* WC_SHA3_RESTRICT sl = st + 50; - word32* WC_SHA3_RESTRICT sh = st + 75; - word32* WC_SHA3_RESTRICT nl = st + 100; - word32* WC_SHA3_RESTRICT nh = st + 125; - word32* WC_SHA3_RESTRICT bl = st + 150; - word32* WC_SHA3_RESTRICT bh = st + 155; -#else -void BlockSha3(word64* s) -{ word32 st[50]; word32 sl[25], sh[25], nl[25], nh[25], bl[5], bh[5]; -#endif word32 i, k; word64 rc; - XMEMCPY(st, s, 25 * sizeof(word64)); + XMEMCPY(st, s, sizeof(st)); for (k = 0; k < 25; k++) { sl[k] = st[2 * k]; sh[k] = st[2 * k + 1]; @@ -827,7 +775,7 @@ void BlockSha3(word64* s) st[2 * k] = sl[k]; st[2 * k + 1] = sh[k]; } - XMEMCPY(s, st, 25 * sizeof(word64)); + XMEMCPY(s, st, sizeof(st)); } #undef WC_SHA3_RL @@ -837,17 +785,10 @@ void BlockSha3(word64* s) #else /* !WC_SHA3_SPLIT64 */ -#ifdef WC_SHA3_SCRATCH_W -void BlockSha3(word64* WC_SHA3_RESTRICT s, void* scratch) -{ - word64* WC_SHA3_RESTRICT n = (word64*)scratch; - word64* WC_SHA3_RESTRICT b = n + 25; -#else void BlockSha3(word64* s) { word64 n[25]; word64 b[5]; -#endif word64 t0; #ifndef SHA3_BY_SPEC word64 t1; @@ -1011,7 +952,7 @@ static int InitSha3(wc_Sha3* sha3) #if defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) /* WOLFSSL_SHA3_AVX2 asks for AVX2 ahead of BMI2. */ if (SHA3_USE_AVX2(cpuid_flags)) { - SHA3_BLOCK = SHA3_BLOCK_AVX2_FN; + SHA3_BLOCK = sha3_block_avx2; SHA3_BLOCK_N = sha3_block_n_avx2; } else @@ -1019,14 +960,14 @@ static int InitSha3(wc_Sha3* sha3) /* BMI2 before AVX2: sha3_block_bmi2 uses general registers only, so * it needs no vector-register save. */ if (IS_INTEL_BMI1(cpuid_flags) && IS_INTEL_BMI2(cpuid_flags)) { - SHA3_BLOCK = SHA3_BLOCK_BMI2_FN; + SHA3_BLOCK = sha3_block_bmi2; SHA3_BLOCK_N = sha3_block_n_bmi2; } #if !defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) /* AVX2 without BMI2. Only live in the plain build: the overrides * either select AVX2 above or disable it outright. */ else if (SHA3_USE_AVX2(cpuid_flags)) { - SHA3_BLOCK = SHA3_BLOCK_AVX2_FN; + SHA3_BLOCK = sha3_block_avx2; SHA3_BLOCK_N = sha3_block_n_avx2; } #endif @@ -1068,15 +1009,6 @@ void BlockSha3(word64* s) } #endif -/* Run the block function on a context's state with the context's scratch. */ -#if defined(SHA3_FUNC_PTR) && defined(WC_SHA3_SCRATCH_W) - #define SHA3_BLOCK_RUN(o) (*sha3_block)((o)->s, (o)->scratch) -#elif defined(SHA3_FUNC_PTR) - #define SHA3_BLOCK_RUN(o) (*sha3_block)((o)->s) -#else - #define SHA3_BLOCK_RUN(o) WC_SHA3_BLOCK(o, (o)->s) -#endif - /* Update the SHA-3 hash state with message data. * * sha3 wc_Sha3 object holding state. @@ -1096,7 +1028,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) #endif #ifdef USE_INTEL_SPEEDUP #ifdef WC_C_DYNAMIC_FALLBACK - WC_SHA3_BLOCK_FN sha3_block = SHA3_BLOCK; + void (*sha3_block)(word64 *s) = SHA3_BLOCK; void (*sha3_block_n)(word64 *s, const byte* data, word32 n, word64 c) = SHA3_BLOCK_N; #endif @@ -1169,7 +1101,11 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) total_check += p; #endif #endif - SHA3_BLOCK_RUN(sha3); + #ifdef SHA3_FUNC_PTR + (*sha3_block)(sha3->s); + #else + BlockSha3(sha3->s); + #endif sha3->i = 0; } } @@ -1203,7 +1139,11 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) } #endif #endif - SHA3_BLOCK_RUN(sha3); + #ifdef SHA3_FUNC_PTR + (*sha3_block)(sha3->s); + #else + BlockSha3(sha3->s); + #endif len -= p * 8U; data += p * 8U; } @@ -1266,7 +1206,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l word32 check = 0; #endif #if defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) - WC_SHA3_BLOCK_FN sha3_block = SHA3_BLOCK; + void (*sha3_block)(word64 *s) = SHA3_BLOCK; #endif if ((p < WC_SHA3_512_COUNT) || (p > WC_SHA3_128_COUNT)) @@ -1332,7 +1272,11 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l #endif for (j = 0; l - j >= rate; j += rate) { - SHA3_BLOCK_RUN(sha3); + #ifdef SHA3_FUNC_PTR + (*sha3_block)(sha3->s); + #else + BlockSha3(sha3->s); + #endif #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64((word64*)(hash + j), sha3->s, rate); #elif defined(WOLFSSL_WIDE_BYTE) @@ -1342,7 +1286,11 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l #endif } if (j != l) { - SHA3_BLOCK_RUN(sha3); + #ifdef SHA3_FUNC_PTR + (*sha3_block)(sha3->s); + #else + BlockSha3(sha3->s); + #endif #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64(sha3->s, sha3->s, rate); XMEMCPY(hash + j, sha3->s, l - j); @@ -1702,9 +1650,6 @@ static void wc_Sha3Wipe(wc_Sha3* sha3) #else ForceZero(sha3->s, sizeof(sha3->s)); ForceZero(sha3->t, sizeof(sha3->t)); -#ifdef WC_SHA3_SCRATCH_W - ForceZero(sha3->scratch, sizeof(sha3->scratch)); -#endif #ifdef STM32_HASH_SHA3 ForceZero(&sha3->stmCtx, sizeof(sha3->stmCtx)); #endif @@ -2483,7 +2428,7 @@ int wc_Shake128_Absorb(wc_Shake* shake, const byte* data, word32 len) int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) { #if defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) - WC_SHA3_BLOCK_FN sha3_block; + void (*sha3_block)(word64 *s); #endif if ((shake == NULL) || (out == NULL && blockCnt != 0)) { @@ -2524,7 +2469,11 @@ int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) #endif for (; (blockCnt > 0); blockCnt--) { - SHA3_BLOCK_RUN(shake); + #ifdef SHA3_FUNC_PTR + (*sha3_block)(shake->s); + #else + BlockSha3(shake->s); + #endif #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64((word64*)out, shake->s, WC_SHA3_128_COUNT * 8); #elif defined(WOLFSSL_WIDE_BYTE) @@ -2825,7 +2774,7 @@ int wc_Shake256_Absorb(wc_Shake* shake, const byte* data, word32 len) int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) { #if defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) - WC_SHA3_BLOCK_FN sha3_block; + void (*sha3_block)(word64 *s); #endif if ((shake == NULL) || (out == NULL && blockCnt != 0)) { @@ -2866,7 +2815,11 @@ int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) #endif for (; (blockCnt > 0); blockCnt--) { - SHA3_BLOCK_RUN(shake); + #ifdef SHA3_FUNC_PTR + (*sha3_block)(shake->s); + #else + BlockSha3(shake->s); + #endif #if defined(BIG_ENDIAN_ORDER) ByteReverseWords64((word64*)out, shake->s, WC_SHA3_256_COUNT * 8); #elif defined(WOLFSSL_WIDE_BYTE) diff --git a/wolfcrypt/src/wc_frodokem_mat.c b/wolfcrypt/src/wc_frodokem_mat.c index e825a49b897..fe560b944e5 100644 --- a/wolfcrypt/src/wc_frodokem_mat.c +++ b/wolfcrypt/src/wc_frodokem_mat.c @@ -1327,10 +1327,10 @@ static int frodokem_gen_a_row_shake(wc_Shake* shake, word16* row, state8[WC_SHA3_128_COUNT * 8 - 1] = 0x80; for (l = 0; l + inc < 2 * p->n; l += inc) { - WC_SHA3_BLOCK(shake, state); + BlockSha3(state); XMEMCPY(rowBytes + l, state8, WC_SHA3_128_BLOCK_SIZE); } - WC_SHA3_BLOCK(shake, state); + BlockSha3(state); XMEMCPY(rowBytes + l, state8, (word32)(2 * p->n - l)); ret = 0; diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index d4fdde8270f..f0c8e664b81 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -206,8 +206,6 @@ } #endif -/* Before the asm undefs, so wc_Sha3 has the layout sha3.c built. */ -#include #if defined(WC_MLDSA_NO_ASM) || defined(WC_SHA3_NO_ASM) #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -643,7 +641,7 @@ static int mldsa_shake256(wc_Shake* shake256, const byte* data, else #endif { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } if (dataLen >= WC_SHA3_256_COUNT * 8) { #ifndef WC_SHA3_NO_ASM @@ -671,7 +669,7 @@ static int mldsa_shake256(wc_Shake* shake256, const byte* data, xorbuf(state, data, WC_SHA3_256_COUNT * 8); dataLen -= WC_SHA3_256_COUNT * 8; data += WC_SHA3_256_COUNT * 8; - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } } } @@ -702,7 +700,7 @@ static int mldsa_shake256(wc_Shake* shake256, const byte* data, else #endif { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } if (hash != (byte*)shake256->s) { XMEMCPY(hash, shake256->s, hashLen); @@ -775,7 +773,7 @@ static int mldsa_hash256(wc_Shake* shake256, const byte* data1, else #endif { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } if (data2Len >= WC_SHA3_256_COUNT * 8) { @@ -804,7 +802,7 @@ static int mldsa_hash256(wc_Shake* shake256, const byte* data1, xorbuf(state, data2, WC_SHA3_256_COUNT * 8); data2Len -= WC_SHA3_256_COUNT * 8; data2 += WC_SHA3_256_COUNT * 8; - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } } } @@ -839,7 +837,7 @@ static int mldsa_hash256(wc_Shake* shake256, const byte* data1, else #endif { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } XMEMCPY(hash, shake256->s, hashLen); ret = 0; @@ -1177,7 +1175,7 @@ static int mldsa_squeeze256(wc_Shake* shake256, const byte* in, else #endif { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } XMEMCPY(out, shake256->s, WC_SHA3_256_COUNT * 8); out += WC_SHA3_256_COUNT * 8; @@ -5774,7 +5772,7 @@ static int mldsa_sample_in_ball_ex(int level, wc_Shake* shake256, else #endif { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } /* Restart hash block index. */ @@ -9419,9 +9417,6 @@ static void mldsa_shake_wipe(wc_Shake* shake) #ifndef PSOC6_HASH_SHA3 ForceZero(shake->s, sizeof(shake->s)); ForceZero(shake->t, sizeof(shake->t)); -#ifdef WC_SHA3_SCRATCH_W - ForceZero(shake->scratch, sizeof(shake->scratch)); -#endif shake->i = 0; #else (void)wc_Shake256_Reset(shake); diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index de58b7294dd..1192caffe63 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -84,8 +84,6 @@ #endif #endif -/* Before the asm undefs, so wc_Sha3 has the layout sha3.c built. */ -#include #ifdef WC_MLKEM_NO_ASM #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -695,9 +693,6 @@ static void mlkemkey_hash_wipe(wc_Sha3* h) #ifndef PSOC6_HASH_SHA3 ForceZero(h->s, sizeof(h->s)); ForceZero(h->t, sizeof(h->t)); -#ifdef WC_SHA3_SCRATCH_W - ForceZero(h->scratch, sizeof(h->scratch)); -#endif h->i = 0; #else (void)wc_Sha3_256_Reset(h); @@ -713,12 +708,6 @@ static void mlkemkey_hash_memzero_add(MlKemKey* key) 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)); -#ifdef WC_SHA3_SCRATCH_W - wc_MemZero_Add("mlkem key hash scratch", key->hash.scratch, - sizeof(key->hash.scratch)); - wc_MemZero_Add("mlkem key prf scratch", key->prf.scratch, - sizeof(key->prf.scratch)); -#endif #else (void)key; #endif @@ -731,10 +720,6 @@ static void mlkemkey_hash_memzero_check(MlKemKey* key) 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)); -#ifdef WC_SHA3_SCRATCH_W - wc_MemZero_Check(key->hash.scratch, sizeof(key->hash.scratch)); - wc_MemZero_Check(key->prf.scratch, sizeof(key->prf.scratch)); -#endif #else (void)key; #endif diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index 3d6a0ae3609..aae11352ecd 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -101,11 +101,6 @@ #endif #endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ -/* Before the asm undefs, so wc_Sha3 has the layout sha3.c built. */ -#include -/* Words for a multi-state buffer whose tail is also Keccak scratch. */ -#define MLKEM_SHA3_BUF_W(n) \ - (((n) > WC_SHA3_STATE_W) ? (n) : WC_SHA3_STATE_W) #ifdef WC_MLKEM_NO_ASM #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -2802,7 +2797,7 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) word64 *state = NULL; #else byte rand[4 * GEN_MATRIX_SIZE + 4]; - word64 state[MLKEM_SHA3_BUF_W(25 * 4)]; + word64 state[25 * 4]; #endif unsigned int ctr0; unsigned int ctr1; @@ -2913,7 +2908,7 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); } XMEMCPY(rand + i, state, SHA3_128_BYTES); } @@ -2936,7 +2931,7 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); } XMEMCPY(rand, state, SHA3_128_BYTES); ctr0 += mlkem_rej_uniform_ins(a + ctr0, MLKEM_N - ctr0, rand, @@ -2974,7 +2969,7 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) word64 *state = NULL; #else byte rand[8 * GEN_MATRIX_SIZE + 4]; - word64 state[MLKEM_SHA3_BUF_W(25 * 8)]; + word64 state[25 * 8]; #endif unsigned int ctr[8]; @@ -3056,7 +3051,7 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); } XMEMCPY(rand + i, state, SHA3_128_BYTES); } @@ -3079,7 +3074,7 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) else #endif /* !WC_SHA3_NO_ASM */ { - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); } XMEMCPY(rand, state, SHA3_128_BYTES); ctr[0] += mlkem_rej_uniform_ins(a + ctr[0], MLKEM_N - ctr[0], rand, @@ -3324,7 +3319,7 @@ static int mlkem_gen_matrix_k4_avx512(sword16* a, byte* seed, int transposed) */ static int mlkem_gen_matrix_k2_aarch64(sword16* a, byte* seed, int transposed) { - word64 state[MLKEM_SHA3_BUF_W(3 * 25)]; + word64 state[3 * 25]; word64* st = (word64*)state; unsigned int ctr0; unsigned int ctr1; @@ -3371,11 +3366,11 @@ static int mlkem_gen_matrix_k2_aarch64(sword16* a, byte* seed, int transposed) state[4] = 0x1f0000 + (1 << 8) + 1; XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5)); state[20] = W64LIT(0x8000000000000000); - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); p = (byte*)state; ctr0 = mlkem_rej_uniform_neon(a, MLKEM_N, p, XOF_BLOCK_SIZE); while (ctr0 < MLKEM_N) { - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); ctr0 += mlkem_rej_uniform_neon(a + ctr0, MLKEM_N - ctr0, p, XOF_BLOCK_SIZE); } @@ -3472,7 +3467,7 @@ static int mlkem_gen_matrix_k4_aarch64(sword16* a, byte* seed, int transposed) { int i; int k; - word64 state[MLKEM_SHA3_BUF_W(3 * 25)]; + word64 state[3 * 25]; word64* st = (word64*)state; unsigned int ctr0; unsigned int ctr1; @@ -3525,11 +3520,11 @@ static int mlkem_gen_matrix_k4_aarch64(sword16* a, byte* seed, int transposed) state[4] = 0x1f0000 + (3 << 8) + 3; XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5)); state[20] = W64LIT(0x8000000000000000); - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); p = (byte*)state; ctr0 = mlkem_rej_uniform_neon(a, MLKEM_N, p, XOF_BLOCK_SIZE); while (ctr0 < MLKEM_N) { - WC_SHA3_BLOCK_SCR(state, state + 25); + BlockSha3(state); ctr0 += mlkem_rej_uniform_neon(a + ctr0, MLKEM_N - ctr0, p, XOF_BLOCK_SIZE); } @@ -3779,7 +3774,7 @@ static int mlkem_prf(wc_Shake* shake256, byte* out, unsigned int outLen, else #endif /* !WC_SHA3_NO_ASM */ { - WC_SHA3_BLOCK(shake256, state); + BlockSha3(state); } /* Copy the state as output. */ @@ -3827,7 +3822,7 @@ static int mlkem_prf(wc_Shake* shake256, byte* out, unsigned int outLen, */ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) { - word64 state[WC_SHA3_STATE_W]; + word64 state[25]; word32 len64 = seedLen / 8; readUnalignedWords64(state, seed, len64); @@ -3851,7 +3846,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) else #endif { - WC_SHA3_BLOCK_ST(state); + BlockSha3(state); } XMEMCPY(out, state, outLen); @@ -3878,7 +3873,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) */ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) { - word64 state[WC_SHA3_STATE_W]; + word64 state[25]; word32 len64 = seedLen / 8; readUnalignedWords64(state, seed, len64); @@ -3886,7 +3881,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) XMEMSET(state + len64 + 1, 0, (25 - len64 - 1) * sizeof(word64)); state[WC_SHA3_256_COUNT - 1] = W64LIT(0x8000000000000000); - WC_SHA3_BLOCK_ST(state); + BlockSha3(state); XMEMCPY(out, state, outLen); /* state holds secret KDF output. */ @@ -5072,7 +5067,7 @@ static int mlkem_get_noise_eta2_avx2(MLKEM_PRF_T* prf, sword16* p, else #endif /* !WC_SHA3_NO_ASM */ { - WC_SHA3_BLOCK(prf, state); + BlockSha3(state); } mlkem_cbd_eta2_ins(p, (byte*)state); @@ -5589,15 +5584,15 @@ static void mlkem_get_noise_eta3_aarch64(byte* rand, byte* seed, byte o) { /* ETA3_RAND_SIZE is larger than the SHAKE-256 rate - two squeezes are * needed, so the state cannot be squeezed in place over the output. */ - word64 state[WC_SHA3_STATE_W]; + word64 state[25]; readUnalignedWords64(state, seed, 4); state[4] = 0x1f00 + o; XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5)); state[16] = W64LIT(0x8000000000000000); - WC_SHA3_BLOCK_ST(state); + BlockSha3(state); XMEMCPY(rand , state, SHA3_256_BYTES); - WC_SHA3_BLOCK_ST(state); + BlockSha3(state); XMEMCPY(rand + SHA3_256_BYTES, state, ETA3_RAND_SIZE - SHA3_256_BYTES); /* state is secret-seeded; caller zeroizes rand. */ @@ -5683,7 +5678,7 @@ static void mlkem_get_noise_eta2_aarch64(word64* rand, byte* seed, byte o) rand[4] = 0x1f00 + o; XMEMSET(rand + 5, 0, sizeof(*rand) * (25 - 5)); rand[16] = W64LIT(0x8000000000000000); - WC_SHA3_BLOCK_SCR(rand, rand + 25); + BlockSha3(rand); } #endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || * !WOLFSSL_MLKEM_NO_DECAPSULATE */ @@ -5703,7 +5698,7 @@ static void mlkem_get_noise_eta2_aarch64(word64* rand, byte* seed, byte o) static int mlkem_get_noise_k3_aarch64(sword16* vec1, sword16* vec2, sword16* poly, byte* seed) { - word64 rand[MLKEM_SHA3_BUF_W(3 * 25)]; + word64 rand[3 * 25]; mlkem_get_noise_x3_eta2_aarch64(rand, seed, 0); mlkem_cbd_eta2(vec1 , (byte*)rand + 0 * 25 * 8); diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index 8d24a0ef586..b5e788c43ee 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -576,7 +576,7 @@ static int slhdsakey_hash_shake_3(wc_Shake* shake, const byte* data1, #endif { /* Process the state using C code. */ - WC_SHA3_BLOCK(shake, state); + BlockSha3(state); } /* Copy hash result, of the required length, from the state into hash. */ XMEMCPY(hash, shake->s, hash_len); @@ -695,7 +695,7 @@ static int slhdsakey_hash_shake_4(wc_Shake* shake, const byte* data1, #endif { /* Process the state using C code. */ - WC_SHA3_BLOCK(shake, state); + BlockSha3(state); } /* Copy hash result, of the required length, from the state into hash. */ XMEMCPY(hash, shake->s, hash_len); @@ -9158,9 +9158,6 @@ static void slhdsakey_wipe_secret_hash(SlhDsaKey* key) ForceZero(shake->s, sizeof(shake->s)); ForceZero(shake->t, sizeof(shake->t)); -#ifdef WC_SHA3_SCRATCH_W - ForceZero(shake->scratch, sizeof(shake->scratch)); -#endif shake->i = 0; } } diff --git a/wolfssl/wolfcrypt/sha3.h b/wolfssl/wolfcrypt/sha3.h index 6a6fbcb231b..6e5e2a912bb 100644 --- a/wolfssl/wolfcrypt/sha3.h +++ b/wolfssl/wolfcrypt/sha3.h @@ -141,31 +141,6 @@ enum { #include "cy_crypto_core.h" #endif -/* Round scratch of the C Keccak block function lives in the context so it is - * wiped once at Free: word64 lanes, or word32 halves for the split variant. */ -#if (defined(WC_SHA3_NO_ASM) || (!defined(WOLFSSL_ARMASM) && \ - !defined(WOLFSSL_RISCV_ASM) && !defined(WOLFSSL_PPC64_ASM) && \ - !defined(WOLFSSL_PPC32_ASM))) && !defined(PSOC6_HASH_SHA3) && \ - !defined(WOLFSSL_XILINX_CRYPT) && !defined(WOLFSSL_AFALG_XILINX_SHA3) - #if defined(WOLFSSL_SHA3_SMALL) - #define WC_SHA3_SCRATCH_W 5 - #elif defined(WC_SHA3_SPLIT64) || \ - (defined(WOLFSSL_WIDE_BYTE) && !defined(BIG_ENDIAN_ORDER)) - #define WC_SHA3_SCRATCH_W 80 - #elif !defined(STM32_HASH_SHA3) || defined(WOLFSSL_SHAKE128) || \ - defined(WOLFSSL_SHAKE256) - #define WC_SHA3_SCRATCH_W 30 - #endif -#endif -#ifdef WC_SHA3_SCRATCH_W - typedef void (*WC_SHA3_BLOCK_FN)(word64* s, void* scratch); - /* Lanes to declare for a bare state that carries its own scratch. */ - #define WC_SHA3_STATE_W (25 + WC_SHA3_SCRATCH_W) -#else - typedef void (*WC_SHA3_BLOCK_FN)(word64* s); - #define WC_SHA3_STATE_W 25 -#endif - /* Sha3 digest */ struct wc_Sha3 { #if defined(PSOC6_HASH_SHA3) @@ -177,10 +152,6 @@ struct wc_Sha3 { word64 s[25]; /* Unprocessed message data. */ byte t[200]; -#ifdef WC_SHA3_SCRATCH_W - /* Round scratch for the C block function, wiped at Free. */ - word64 scratch[WC_SHA3_SCRATCH_W]; -#endif /* Index into unprocessed data to place next message byte. */ word32 i; @@ -193,7 +164,7 @@ struct wc_Sha3 { #endif #ifdef WC_C_DYNAMIC_FALLBACK - WC_SHA3_BLOCK_FN sha3_block; + void (*sha3_block)(word64 *s); void (*sha3_block_n)(word64 *s, const byte* data, word32 n, word64 c); #endif @@ -423,19 +394,7 @@ WOLFSSL_API int wc_Cshake256(const byte* name, word32 nameLen, #endif #endif /* WOLFSSL_KMAC || WOLFSSL_CSHAKE */ -#ifdef WC_SHA3_SCRATCH_W -WOLFSSL_LOCAL void BlockSha3(word64* s, void* scratch); -/* obj is the wc_Sha3 or wc_Shake whose scratch the block function uses. */ -#define WC_SHA3_BLOCK(obj, s) BlockSha3((s), (obj)->scratch) -#define WC_SHA3_BLOCK_SCR(s, scr) BlockSha3((s), (scr)) -/* st is a word64[WC_SHA3_STATE_W] with the scratch after the state. */ -#define WC_SHA3_BLOCK_ST(st) BlockSha3((st), (st) + 25) -#else -WOLFSSL_LOCAL void BlockSha3(word64* s); -#define WC_SHA3_BLOCK(obj, s) BlockSha3(s) -#define WC_SHA3_BLOCK_SCR(s, scr) do { (void)(scr); BlockSha3(s); } while (0) -#define WC_SHA3_BLOCK_ST(st) BlockSha3(st) -#endif +WOLFSSL_LOCAL void BlockSha3(word64 *s); #ifdef WC_SHA3_NO_ASM /* asm speedups disabled */ From 5b9dee903d00b31fbe7dd5e1803b102a6d466f85 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Mon, 26 Oct 2026 09:26:47 -0600 Subject: [PATCH 62/74] SHA-1 and SHA-256: keep the message schedule on the stack and wipe it per block Move W back to the stack as on master. Holding it in the context made the C transform alias W with the input block, which cost SHA-256 and HMAC-SHA256 about 3% on default builds. W is now wiped before the transform returns, the same way sha512.c already handles its schedule, so a keyed block never outlives the frame. The Transform_Sha256_Len copy buffer is hoisted out of the loop and wiped once per call. WOLFSSL_CHECK_MEM_ZERO covers all of them. --- wolfcrypt/src/misc.c | 16 ++++---------- wolfcrypt/src/sha.c | 12 +++++++++- wolfcrypt/src/sha256.c | 45 +++++++++++++++++++++++++++----------- wolfssl/wolfcrypt/sha.h | 2 -- wolfssl/wolfcrypt/sha256.h | 9 -------- 5 files changed, 47 insertions(+), 37 deletions(-) diff --git a/wolfcrypt/src/misc.c b/wolfcrypt/src/misc.c index 3edf2e18e50..f4b81fb8373 100644 --- a/wolfcrypt/src/misc.c +++ b/wolfcrypt/src/misc.c @@ -788,16 +788,8 @@ WC_MISC_STATIC WC_INLINE void xorbuf(void* buf, const void* mask, word32 count) with zeros. It ensures compiler optimization doesn't skip it. */ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) { -#if !defined(__CC_ARM) && (!defined(__GNUC__) || defined(WOLFSSL_NO_ASM)) - /* No data barrier backs the wipe; volatile stores cannot be dropped. */ - typedef volatile byte wc_wipe_byte; - typedef volatile unsigned long wc_wipe_word; -#else - typedef byte wc_wipe_byte; - typedef unsigned long wc_wipe_word; -#endif - wc_wipe_byte *zb = (wc_wipe_byte *)mem; - wc_wipe_word *zl; + byte *zb = (byte *)mem; + unsigned long *zl; /* Make the compiler put the buffer's current contents at mem, so the * wipe below hits the memory that holds them and not a copy. */ @@ -811,14 +803,14 @@ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) --len; } - zl = (wc_wipe_word *)zb; + zl = (unsigned long *)zb; while (len >= sizeof(unsigned long)) { *zl++ = 0; len -= sizeof(unsigned long); } - zb = (wc_wipe_byte *)zl; + zb = (byte *)zl; while (len) { *zb++ = 0; diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index 077ea7e69be..d32e9ddcbe1 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -491,7 +491,7 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) static int Transform(wc_Sha* sha, const byte* data) { - word32* W = sha->W; + word32 W[WC_SHA_BLOCK_SIZE / sizeof(word32)]; /* Copy context->state[] to working vars */ word32 a = sha->digest[0]; @@ -503,6 +503,9 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) #ifdef USE_SLOW_SHA word32 t, i; + #ifdef WOLFSSL_CHECK_MEM_ZERO + 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 +533,9 @@ 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 + 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 +571,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 */ diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 4dce7dc5671..a9f4761c848 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -1377,21 +1377,26 @@ static int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, word32 len) { int ret = 0; + word32 buffer[WC_SHA256_BLOCK_SIZE / sizeof(word32)]; +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Transform_Sha256_Len buffer", buffer, sizeof(buffer)); +#endif while (len >= WC_SHA256_BLOCK_SIZE) { - /* Keep the endian-conversion scratch in the context, where Free - * wipes it. Update and Final can already pass this same buffer. */ - if (data != (const byte*)sha256->buffer) - XMEMCPY(sha256->buffer, data, WC_SHA256_BLOCK_SIZE); + XMEMCPY(buffer, data, WC_SHA256_BLOCK_SIZE); #ifdef LITTLE_ENDIAN_ORDER - ByteReverseWords(sha256->buffer, sha256->buffer, WC_SHA256_BLOCK_SIZE); + ByteReverseWords(buffer, buffer, WC_SHA256_BLOCK_SIZE); #endif - ret = Transform_Sha256(sha256, (const byte*)sha256->buffer); + ret = Transform_Sha256(sha256, (const byte*)buffer); if (ret != 0) break; data += WC_SHA256_BLOCK_SIZE; len -= WC_SHA256_BLOCK_SIZE; } + ForceZero(buffer, sizeof(buffer)); +#ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(buffer, sizeof(buffer)); +#endif return ret; } @@ -1823,12 +1828,16 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; - #elif defined(WC_SHA256_W_IN_CTX) - word32* W = sha256->Wbuf; #else word32 W[WC_SHA256_BLOCK_SIZE]; #define SHA256_W_ON_STACK #endif + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Transform_Sha256 S", S, sizeof(S)); + #ifdef SHA256_W_ON_STACK + wc_MemZero_Add("Transform_Sha256 W", W, sizeof(W)); + #endif + #endif /* Copy context->state[] to working vars */ for (i = 0; i < 8; i++) @@ -1872,7 +1881,14 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, XFREE(W, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); #elif defined(SHA256_W_ON_STACK) ForceZero(W, sizeof(W)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(W, sizeof(W)); + #endif #undef SHA256_W_ON_STACK + #endif + ForceZero(S, sizeof(S)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(S, sizeof(S)); #endif return 0; } @@ -1904,12 +1920,12 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef USE_SLOW_SHA256 int j; #endif - #ifdef WC_SHA256_W_IN_CTX - word32* W = sha256->Wbuf; - #else word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; - #endif + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Transform_Sha256 S", S, sizeof(S)); + wc_MemZero_Add("Transform_Sha256 W", W, sizeof(W)); + #endif /* Copy digest to working vars */ S[0] = sha256->digest[0]; S[1] = sha256->digest[1]; @@ -1954,8 +1970,11 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->digest[6] += S[6]; sha256->digest[7] += S[7]; - #ifndef WC_SHA256_W_IN_CTX ForceZero(W, sizeof(W)); + ForceZero(S, sizeof(S)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(W, sizeof(W)); + wc_MemZero_Check(S, sizeof(S)); #endif return 0; } diff --git a/wolfssl/wolfcrypt/sha.h b/wolfssl/wolfcrypt/sha.h index 48feff3b695..7116d33c73c 100644 --- a/wolfssl/wolfcrypt/sha.h +++ b/wolfssl/wolfcrypt/sha.h @@ -161,8 +161,6 @@ struct wc_Sha { #else word32 digest[WC_SHA_DIGEST_SIZE / sizeof(word32)]; #endif - /* Message schedule of the C transform; wiped with the context at Free. */ - word32 W[WC_SHA_BLOCK_SIZE / sizeof(word32)]; #endif /* end of the hardware-vs-software member chain */ /* The callback port keeps the software implementation compiled in for diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index 2addd3f64f6..e11bfd026ff 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -195,15 +195,6 @@ struct wc_Sha256 { word32 buffLen; /* in bytes */ word32 loLen; /* length in bytes */ word32 hiLen; /* length in bytes */ -/* Kernel builds (WC_SHA2_NO_SMALL_STACK) keep W on the stack so the - * context fits the kernel hash descriptor. */ -#if !defined(WC_SHA2_NO_SMALL_STACK) && \ - !((defined(WOLFSSL_SMALL_STACK_CACHE) || defined(WOLFSSL_SMALL_STACK)) && \ - !defined(WOLFSSL_NO_MALLOC)) - /* Message schedule of the C transform; wiped with the context at Free. */ - #define WC_SHA256_W_IN_CTX - word32 Wbuf[WC_SHA256_BLOCK_SIZE]; -#endif #endif /* end of the hardware-vs-software member chain */ From ac30b2b713c08002d688bca10ba2d055ed2d1860 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Mon, 26 Oct 2026 09:26:47 -0600 Subject: [PATCH 63/74] ForceZero: escape the buffer through an out-of-line sink instead of volatile stores Without GNU inline asm, or with WOLFSSL_NO_ASM, WC_BARRIER_DATA() used to fall back to WC_BARRIER(), which does not cover a local whose address never escaped, so the wipe could be dropped as a dead store. The volatile typedefs in ForceZero worked around that at the cost of a byte-by-byte wipe and two fences per call. The fallback now passes the pointer to wc_BarrierDataSink() in wc_port.c, reached through a volatile load of a function pointer so link time optimization cannot see the empty body, and ForceZero goes back to the plain stores on master. --- wolfcrypt/src/wc_port.c | 18 ++++++++++++++++++ wolfssl/wolfcrypt/wc_port.h | 9 +++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) 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/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 546b95f71c2..9f2c24b86a4 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -2150,7 +2150,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 +2163,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 From eae3ac911c9bb756bc84c63f55def28e37f62a23 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Mon, 26 Oct 2026 12:10:24 -0600 Subject: [PATCH 64/74] SLH-DSA: cast the FORS signature wipe length to size_t gcc with -Wsign-conversion rejects the int product of the byte parameters being passed as a size_t. --- wolfcrypt/src/wc_slhdsa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index b5e788c43ee..e109c02d684 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -9479,7 +9479,8 @@ static int slhdsakey_sign(SlhDsaKey* key, byte* md, byte* sig) if (ret != 0) { /* Unreleased FORS secrets may be in sig * (ISO/IEC 19790:2012 7.9.7). */ - ForceZero(sigFors, key->params->k * (1 + key->params->a) * n); + ForceZero(sigFors, + (size_t)(key->params->k * (1 + key->params->a) * n)); } slhdsakey_wipe_secret_hash(key); #ifdef WOLFSSL_CHECK_MEM_ZERO From be71ea1345f4480b69afdea4a4fc043c97d24d44 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Mon, 26 Oct 2026 14:03:54 -0600 Subject: [PATCH 65/74] SLH-DSA: compile the secret hash wipe helpers without SHAKE --- wolfcrypt/src/wc_slhdsa.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wolfcrypt/src/wc_slhdsa.c b/wolfcrypt/src/wc_slhdsa.c index e109c02d684..a634a3c7157 100644 --- a/wolfcrypt/src/wc_slhdsa.c +++ b/wolfcrypt/src/wc_slhdsa.c @@ -9151,8 +9151,11 @@ static void slhdsakey_wipe_secret_hash(SlhDsaKey* key) 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; @@ -9160,6 +9163,7 @@ static void slhdsakey_wipe_secret_hash(SlhDsaKey* key) ForceZero(shake->t, sizeof(shake->t)); shake->i = 0; } +#endif } #ifdef WOLFSSL_CHECK_MEM_ZERO @@ -9171,14 +9175,18 @@ static void slhdsakey_memzero_add_hash(SlhDsaKey* key) 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) @@ -9187,14 +9195,18 @@ static void slhdsakey_memzero_check_hash(SlhDsaKey* key) 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 From 6199ab18bd37186472869dd471e4f5ae6513b99b Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 15:08:29 -0500 Subject: [PATCH 66/74] wolfcrypt/src/random.c: harden NextStirPurge() sentinel handling, complete zeroization coverage in wc_RNG_TestSeed() and _InitRng(). * NextStirPurge(): never claim or repaint a constructor's _TORNDOWN -- the reopen is the constructor's to make. The claim loop skips _TORNDOWN alongside _CONSUMING (re-checked on every retry), and the reopen is now a CAS retry loop rather than a plain store, so a depositor's fill length stored over the claim is discarded (length only -- its bytes lie in place until the next wipe or full-buffer fold, benign for the uncredited stir) while a _TORNDOWN painted at any point is left standing. Both abort arms (a port's retry clause) get explicit dispositions: an aborted claim now stores nothing, since any store could repaint a live claim or a _TORNDOWN, and the stale fill's resurrection is benign by the three-no-ops doctrine; an aborted reopen stores EMPTY, since stranding _CONSUMING would silence the stir aperture on the instance, and the repaint risk on that already-exotic path is accepted. * wc_RNG_TestSeed(): zeroize the APT reference byte and the all-values test's majority candidate at the end of their window scopes, matching the RCT arm's prevByte -- all three hold a byte of raw seed. * _InitRng(): set drbg_scratch_instantiated before the scratch instantiate calls (both boundaries), as for drbg_instantiated: a failed instantiate still leaves V in the state, and the failure arm must uninstantiate it. The scratch is NULL-seeded, so the stranded state would be a public constant -- wiped anyway, for uniformity of the invariant. --- wolfcrypt/src/random.c | 52 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d755231a79c..de31b38d82f 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1144,26 +1144,53 @@ static int NextSeedProducerRelease(wolfSSL_Atomic_Int *lenp, /* Purge the uncredited stir accumulator. Claimed CONSUMING first so the wipe * runs under ownership; a holder's burn-before-release covers the CONSUMING - * case. Blind depositors can tear the wipe, which is harmless. */ + * 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) + 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) { - /* No claim, so no wipe; still drop the material as before. */ - WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); + /* 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)); - WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); + /* 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 */ @@ -3081,6 +3108,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz) } aptFailed |= (matches >= cutoff); + ForceZero(&refByte, sizeof(refByte)); } } } @@ -3121,6 +3149,7 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz) } aptFailed |= (count >= cutoff); + ForceZero(&cand, sizeof(cand)); } } } @@ -3721,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) { @@ -3770,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) { From 3e235aee4c33fd7a587b0d4a4bc56f25670e0549 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 15:27:58 -0500 Subject: [PATCH 67/74] wolfssl/wolfcrypt/wc_port.h: add WC_RESTRICT. Follows the WC_INLINE pattern: C99 restrict where available, __restrict on GNU-compatible compilers and MSVC (including C++ translation units), and nothing on compilers with no support. WC_RESTRICT applies the C99 restrict contract: it tells the compiler that in each call, objects modified through the qualified pointer are accessed only through it (or through pointers derived from it), and that objects it accesses are modified only through the qualified pointer. A caller-supplied buffer therefore compiles like a local array. The practical benefit is that, after a store through the qualified pointer, the compiler can keep using a value previously loaded through an unrelated pointer without reloading it. --- wolfssl/wolfcrypt/wc_port.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 9f2c24b86a4..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__) From 166c7c86f046b35c439fe17d71379c860c4a27a3 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 22:51:51 +0000 Subject: [PATCH 68/74] ML-DSA: don't destroy an exact-alias source in raw private import wc_mldsa.c mldsa_import_priv_raw: the pre-copy ForceZero of key->k runs before XMEMCPY(key->k, priv, privSz), so a caller re-importing from key->k itself (an exact alias) has the source zeroed and imports an all-zero key -- and because mldsa_check_eta_range() validates priv BEFORE the wipe, the zeroed import passes validation, rebuilds the caches from zeros, and returns success with prvKeySet set. Skip both the wipe and the copy for an exact alias: the new key is already in place, and exact-alias XMEMCPY is undefined besides. The sibling import sites (SLH-DSA seed and encoded imports, ML-DSA keygen's k move) preserve aliased input in a stack bounce buffer per ISO 19790 7.9.7; at ML-DSA private-key sizes (2560-4896 bytes) a stack bounce is not an option, and in the non-aliased case the same-size overwrite leaves no residue of the old key, so the explicit wipe is kept only on that path. --- wolfcrypt/src/wc_mldsa.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index f0c8e664b81..b96a74acf64 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -15451,9 +15451,17 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, #ifdef WOLFSSL_MLDSA_ASSIGN_KEY key->k = priv; #else - /* Do not write the new key over the old one. */ - ForceZero(key->k, privSz); - 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; } From bc84b6710f060a7396cfa2f10add3e4da4589509 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 21:54:12 -0500 Subject: [PATCH 69/74] SHA-2: caller-owned transform scratch buffer, wiped once per public call Refactor to eliminate the per-block ForceZero of the C transforms' message schedule (and, for SHA-256, the working variables): Move the scratch to the caller so the wipe runs once per public call instead of once per block, without changing any context layout, public signature, dispatch arm, or function-pointer table: - Sha256TScratch{W,S} / Sha512TScratch{W}, provisioned by per-config DECL/BEGIN/WIPE macros built on the WC_DECLARE_VAR / WC_ALLOC_VAR_EX / WC_FREE_VAR_EX family: a local in big-stack builds, a per-call heap block under WOLFSSL_SMALL_STACK (previously allocated and freed PER BLOCK), and the per-context cache under WOLFSSL_SMALL_STACK_CACHE, which keeps its zero per-call cost and wipe-at-Free semantics (cache allocations are resized to the scratch struct; the wc_Sha512Transform() backup block now sits after it). - External providers of wc_Sha256.W / wc_Sha512.W (linuxkm's w_buf, the Nuvoton copy callback, mpfs_athena's SHA-384 copy) size their buffers by new WC_SHA256_TSCRATCH_SIZE / WC_SHA512_TSCRATCH_SIZE constants in the public headers, and each translation unit statically asserts its scratch struct matches its constant, so providers and the transform cannot drift apart. - Transform_Sha256_scr() / _Transform_Sha512_scr() take the scratch as a WC_RESTRICT parameter, so values loaded through other pointers stay in registers across schedule stores, avoiding aliasing cost otherwise associated with placing W on the stack. The old-signature transforms remain as compatibility shims with per-call scratch, keeping every dispatch arm and function-pointer type untouched. - The bulk C paths hoist one scratch across their block loop: Transform_Sha256_Len(), both *_Len_C_from_raw(), and both of Sha512Update()'s block loops -- including the intel arm's runtime pre-AVX fallback loop -- so every multi-block C path wipes once per call. In the loop shared with the ESP32 port, the hoist is compiled only for the software-transform configuration; hardware-port arms are untouched. - The *_C_from_raw() adapters become thin wrappers over new *_C_from_raw_scr() variants: the block is staged (when not already there) in the aligned context buffer and byte-reversed in place, so the bulk loops share one scratch while raw input of any alignment stays safe on strict-alignment targets. - SHA-512's working variables T stay a local with their existing per-block wipe: behind a pointer they cannot be register-allocated across the 80 rounds, measured at ~9% on the C transform, while their wipe is one cache line. - WOLFSSL_CHECK_MEM_ZERO coverage moves with the scratch: registered at BEGIN, checked at WIPE, with the scratch zeroed at registration (a const void* registration argument pointing at an uninitialized object trips -Wmaybe-uninitialized under -Werror); wc_Sha512Transform()'s existing backup-buffer registration gets the same initialization. Zeroization lifetime: transient schedule and working variables are wiped when the public call returns; the pending input block in ctx->buffer already persists longer, so no new exposure class is introduced. The chaining state the working variables derive from persists in the context until Free, as before. Performance scope (noasm C transforms; ForceZero never auto-vectorizes, so the wipe is scalar word stores in userspace and kernel alike): recovers the ~2.3% SHA-256 regression from the per-block W+S wipes, and improves SHA-512 8% over master itself by amortizing the per-block W wipe out of Sha512Update()'s block loop. Asm-accelerated paths (intelasm, armasm) carry their own schedules and are untouched in both directions. --- linuxkm/lkcapi_sha_glue.c | 14 +- wolfcrypt/src/port/microchip/mpfs_athena.c | 5 + wolfcrypt/src/port/nuvoton/nuvoton_cb_hash.c | 10 + wolfcrypt/src/sha256.c | 198 +++++++++++++------ wolfcrypt/src/sha512.c | 180 +++++++++++++---- wolfssl/wolfcrypt/sha256.h | 20 ++ wolfssl/wolfcrypt/sha512.h | 7 + 7 files changed, 333 insertions(+), 101 deletions(-) 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/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/sha256.c b/wolfcrypt/src/sha256.c index a9f4761c848..42511ea81e2 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); @@ -1378,7 +1466,9 @@ static int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, { 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 @@ -1387,12 +1477,13 @@ static int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #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)); @@ -1450,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; @@ -1736,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; @@ -1813,31 +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]; - #define SHA256_W_ON_STACK - #endif - #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Add("Transform_Sha256 S", S, sizeof(S)); - #ifdef SHA256_W_ON_STACK - wc_MemZero_Add("Transform_Sha256 W", W, sizeof(W)); - #endif - #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++) @@ -1875,21 +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); - #elif defined(SHA256_W_ON_STACK) - ForceZero(W, sizeof(W)); - #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(W, sizeof(W)); - #endif - #undef SHA256_W_ON_STACK - #endif - ForceZero(S, sizeof(S)); - #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(S, sizeof(S)); - #endif + /* W and S are the caller's Sha256TScratch, wiped with one ForceZero + * when the public call returns. */ return 0; } #else @@ -1913,19 +1973,17 @@ 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; - #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Add("Transform_Sha256 S", S, sizeof(S)); - wc_MemZero_Add("Transform_Sha256 W", W, sizeof(W)); - #endif /* Copy digest to working vars */ S[0] = sha256->digest[0]; S[1] = sha256->digest[1]; @@ -1970,15 +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]; - ForceZero(W, sizeof(W)); - ForceZero(S, sizeof(S)); - #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(W, sizeof(W)); - wc_MemZero_Check(S, sizeof(S)); - #endif + /* 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 */ @@ -2858,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; @@ -3187,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; } @@ -3270,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; } @@ -3506,7 +3576,7 @@ 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(sizeof(Sha256TScratch), dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { ForceZero(dst, sizeof(wc_Sha224)); @@ -3670,7 +3740,7 @@ 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(sizeof(Sha256TScratch), dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { ForceZero(dst, sizeof(wc_Sha256)); diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 01551b99c48..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,7 +2893,7 @@ 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); @@ -2794,6 +2901,9 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) 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 @@ -2967,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; @@ -3231,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; } @@ -3357,7 +3467,7 @@ 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) { ForceZero(dst, sizeof(wc_Sha512)); @@ -3812,7 +3922,7 @@ 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) { ForceZero(dst, sizeof(wc_Sha384)); diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index e11bfd026ff..c5b92f8259c 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -128,6 +128,22 @@ #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. */ +#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 +228,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..62c17d7fd7d 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -121,6 +121,13 @@ #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. */ + #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 From 78896746b7e9cac4330a8bdfd833ee10c1e1fae7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 22:20:11 -0500 Subject: [PATCH 70/74] tests/api/test_frodokem.c: in frodokem_kats[] definition, use WC_STATIC_STRUCT_INITIALIZER, not ad hoc { 0 }, for warning-free build in C++ mode. --- tests/api/test_frodokem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 \ From cf64372e267622c1ec7a8d2c9d1a7017959bbfe8 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 22:20:29 -0500 Subject: [PATCH 71/74] wolfcrypt/src/aes.c, wolfcrypt/src/ed25519.c, wolfcrypt/src/sha.c: zero stack buffers before WOLFSSL_CHECK_MEM_ZERO registration. wc_MemZero_Add() takes const void*, so gcc assumes a read: registering a not-yet-written stack buffer trips -Wmaybe-uninitialized under -Werror. Zero each registered buffer first -- cost confined to WOLFSSL_CHECK_MEM_ZERO builds. --- wolfcrypt/src/aes.c | 28 ++++++++++++++++++++++++++++ wolfcrypt/src/ed25519.c | 3 +++ wolfcrypt/src/sha.c | 8 ++++++++ 3 files changed, 39 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index d5552684745..888a00a81bd 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -11854,6 +11854,11 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( #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)); @@ -12746,6 +12751,12 @@ int WARN_UNUSED_RESULT AES_GCM_decrypt_C( #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)); @@ -15854,6 +15865,10 @@ 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 @@ -19600,6 +19615,9 @@ 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 @@ -19671,6 +19689,9 @@ static int AesXtsEncryptUpdate_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)); wc_MemZero_Add("AesXtsEncryptUpdate_sw buf", buf, sizeof(buf)); #endif @@ -20219,6 +20240,9 @@ 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 @@ -20280,6 +20304,10 @@ 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 diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 6f0d171fba9..4e852f9c394 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -402,6 +402,9 @@ int wc_ed25519_make_public(ed25519_key* key, unsigned char* pubKey, 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) diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index d32e9ddcbe1..4bb59a17e2d 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -504,6 +504,10 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) 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++) { @@ -534,6 +538,10 @@ static WC_INLINE void AddLength(wc_Sha* sha, word32 len) /* 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); From 1b015097b517d74090da928809ef1b6572a90875 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 28 Oct 2026 09:00:42 -0500 Subject: [PATCH 72/74] wolfcrypt/src/sha256.c: use WC_SHA256_TSCRATCH_SIZE. not sizeof(Sha256TScratch), as allocation operand in smallstack wc_Sha{224,256}Copy(). wolfssl/wolfcrypt/sha{256,512}.h: add note that WC_SHA{256,512}_TSCRATCH_SIZE are precious names that wolfBoot depends on. --- wolfcrypt/src/sha256.c | 4 ++-- wolfssl/wolfcrypt/sha256.h | 4 +++- wolfssl/wolfcrypt/sha512.h | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 42511ea81e2..07ddeb60a5f 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -3576,7 +3576,7 @@ int wc_Sha224Reset(wc_Sha224* sha224) { XMEMCPY(dst, src, sizeof(wc_Sha224)); #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(Sha256TScratch), + dst->W = (word32*)XMALLOC(WC_SHA256_TSCRATCH_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { ForceZero(dst, sizeof(wc_Sha224)); @@ -3740,7 +3740,7 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(Sha256TScratch), + dst->W = (word32*)XMALLOC(WC_SHA256_TSCRATCH_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { ForceZero(dst, sizeof(wc_Sha256)); diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index c5b92f8259c..4bd22ff2df0 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -135,7 +135,9 @@ * 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. */ + * 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)) diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index 62c17d7fd7d..ad7eef309ea 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -125,7 +125,10 @@ * (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. */ + * 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 From 2911a85bf14ace537e37c0846d648bb033563b80 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 28 Oct 2026 10:06:42 -0500 Subject: [PATCH 73/74] wolfcrypt/src/aes.c, wolfcrypt/src/wc_mlkem_poly.c, wolfcrypt/src/wc_frodokem_mat.c: refactor ad-hoc __restrict macros using WC_RESTRICT (no functional changes). --- wolfcrypt/src/aes.c | 33 ++++++++++++++++----------------- wolfcrypt/src/wc_frodokem_mat.c | 24 ++++++++---------------- wolfcrypt/src/wc_mlkem_poly.c | 24 ++++++++---------------- 3 files changed, 32 insertions(+), 49 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 888a00a81bd..893d9871f06 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -8897,19 +8897,6 @@ static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) #ifdef HAVE_AESGCM -/* 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. */ -#if defined(__GNUC__) || defined(__clang__) - #define GCM_RESTRICT __restrict__ -#elif defined(_MSC_VER) - #define GCM_RESTRICT __restrict -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - #define GCM_RESTRICT restrict -#else - #define GCM_RESTRICT -#endif - typedef union GcmScratch { byte b[2 * WC_AES_BLOCK_SIZE]; word32 w32[2 * WC_AES_BLOCK_SIZE / sizeof(word32)]; @@ -9645,7 +9632,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #define HAVE_GHASH #elif defined(GCM_SMALL) #define GCM_C_GMULT -static void GMULT(byte* X, byte* Y, GcmScratch* GCM_RESTRICT ws) +/* 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; @@ -9920,8 +9910,11 @@ ALIGN16 static const byte R[256][2] = { {0xbc, 0xf8}, {0xbd, 0x3a}, {0xbf, 0x7c}, {0xbe, 0xbe} }; +/* 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* GCM_RESTRICT ws) + GcmScratch* WC_RESTRICT ws) { #if !defined(WORD64_AVAILABLE) || defined(BIG_ENDIAN_ORDER) int i, j; @@ -10687,7 +10680,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #if !defined(FREESCALE_LTC_AES_GCM) #define GCM_C_GMULT -static void GMULT(word64* X, word64* Y, GcmScratch* GCM_RESTRICT ws) +/* 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; @@ -11008,7 +11004,10 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, #else /* GCM_WORD32 */ #define GCM_C_GMULT -static void GMULT(word32* X, word32* Y, GcmScratch* GCM_RESTRICT ws) +/* 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 = ws->w32; word32* V = ws->w32 + 4; 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_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index aae11352ecd..3ac34bf6071 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -1099,21 +1099,13 @@ static void mlkem_basemul(sword16* r, const sword16* a, const sword16* b, r[1] = MLKEM_MONT_RED(p1); } -/* r never aliases a or b; saying so keeps the accumulate loop vectorised. */ -#if defined(__GNUC__) || defined(__clang__) - #define MLKEM_RESTRICT __restrict__ -#elif defined(_MSC_VER) - #define MLKEM_RESTRICT __restrict -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - #define MLKEM_RESTRICT restrict -#else - #define MLKEM_RESTRICT -#endif - /* 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). */ -static void mlkem_basemul_add(sword16* MLKEM_RESTRICT r, - const sword16* MLKEM_RESTRICT a, const sword16* MLKEM_RESTRICT b, + * 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; @@ -1211,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* MLKEM_RESTRICT r, - const sword16* MLKEM_RESTRICT a, const sword16* MLKEM_RESTRICT 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; From 4ec705f76facf5663cd02ac780b97a5e4284fd41 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 28 Oct 2026 11:01:37 -0500 Subject: [PATCH 74/74] mcapi/crypto.h: revert changes (none needed versus the branch base). --- mcapi/crypto.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mcapi/crypto.h b/mcapi/crypto.h index 73ca00b9443..ae287be745c 100644 --- a/mcapi/crypto.h +++ b/mcapi/crypto.h @@ -48,7 +48,7 @@ enum { /* SHA */ typedef struct CRYPT_SHA_CTX { - int holder[48]; /* big enough to hold internal, but check on init */ + int holder[29]; /* big enough to hold internal, but check on init */ } CRYPT_SHA_CTX; int CRYPT_SHA_Initialize(CRYPT_SHA_CTX*); @@ -63,7 +63,7 @@ enum { /* SHA-256 */ typedef struct CRYPT_SHA256_CTX { - int holder[112]; /* big enough to hold internal, but check on init */ + int holder[32]; /* big enough to hold internal, but check on init */ } CRYPT_SHA256_CTX; int CRYPT_SHA256_Initialize(CRYPT_SHA256_CTX*); @@ -108,9 +108,9 @@ enum { typedef struct CRYPT_HMAC_CTX { /* big enough to hold internal, but check on init */ #ifdef WOLF_PRIVATE_KEY_ID - long long holder[160]; + long long holder[108]; #else - long long holder[150]; + long long holder[98]; #endif } CRYPT_HMAC_CTX;