From 8ba46594f0acbfe470f1b328bb65681d9d2999f1 Mon Sep 17 00:00:00 2001 From: Arpan Sharma Date: Thu, 3 Sep 2026 23:54:09 -0500 Subject: [PATCH] XMSS/Falcon: zeroize secret working state and key buffers before free Several secret buffers were freed without being zeroized first, leaving key material resident in freed heap or on the stack: - wc_xmss.c: the XmssState working buffer holds the XMSS private seed S_XMSS and transient WOTS secret keys during keygen and sign. It is now ForceZero'd before WC_FREE_VAR_EX at the keygen, sign, and verify sites, matching wc_lms.c (ForceZero(state, sizeof(LmsState))). - wc_xmss.c: the keygen seed buffer (SK_seed || SK_PRF) is zeroized before free; the first 2*n bytes are secret. The adjacent key->sk was already zeroized. - falcon.c: wc_Falcon_PrivateKeyDecode zeroizes the decoded private-key temporary before freeing it, matching every other secret temporary in the file. No functional change. --- wolfcrypt/src/falcon.c | 3 +++ wolfcrypt/src/wc_xmss.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/wolfcrypt/src/falcon.c b/wolfcrypt/src/falcon.c index 07e06f29f77..046ce52bce1 100644 --- a/wolfcrypt/src/falcon.c +++ b/wolfcrypt/src/falcon.c @@ -9540,6 +9540,9 @@ int wc_Falcon_PrivateKeyDecode(const byte* input, word32* inOutIdx, } } + /* Zeroize the decoded private key before free (matches every other secret + * temporary in this file); pubKey is public and needs no scrubbing. */ + ForceZero(privKey, FALCON_MAX_PRV_KEY_SIZE); XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c index 2ec27000347..4184438e307 100644 --- a/wolfcrypt/src/wc_xmss.c +++ b/wolfcrypt/src/wc_xmss.c @@ -787,6 +787,8 @@ static WC_INLINE int wc_xmsskey_signupdate(XmssKey* key, byte* sig, /* Free state after use. */ wc_xmss_state_free(state); } + /* Zeroize working state before free. */ + ForceZero(state, sizeof(XmssState)); WC_FREE_VAR_EX(state, key->heap, DYNAMIC_TYPE_TMP_BUFFER); } } @@ -1284,6 +1286,8 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) /* Free state after use. */ wc_xmss_state_free(state); } + /* Zeroize working state before free. */ + ForceZero(state, sizeof(XmssState)); WC_FREE_VAR_EX(state, key->heap, DYNAMIC_TYPE_TMP_BUFFER); } } @@ -1305,6 +1309,14 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) key->state = WC_XMSS_STATE_OK; } + /* Zeroize the secret seed (SK_seed || SK_PRF) before freeing the buffer. */ +#ifdef WOLFSSL_SMALL_STACK + if (seed != NULL) { + ForceZero(seed, 3U * key->params->n); + } +#else + ForceZero(seed, sizeof(seed)); +#endif WC_FREE_VAR_EX(seed, key->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -2015,6 +2027,8 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen, /* Free state after use. */ wc_xmss_state_free(state); } + /* Zeroize working state before free. */ + ForceZero(state, sizeof(XmssState)); WC_FREE_VAR_EX(state, key->heap, DYNAMIC_TYPE_TMP_BUFFER); } }