musig: always clear out secret key in `secp256k1_musig_nonce_gen_counter`
What changed, and why it matters
This is a small defensive cleanup in Bitcoin Core's libsecp256k1 MuSig code. The function that generates a cryptographic nonce could, in rare error cases, leave a temporary copy of the user's secret key in memory instead of wiping it. The patch makes sure the secret key buffer is always cleared, even when the internal function fails. The actual failure path can only be reached if the API is misused (bad keypair or cache), so this is best-practice hardening rather than an active exploit.
Treat as a low-severity hardening fix. Backport if the project maintains stable branches, but no urgent security response is required. Review other MuSig functions for similar patterns where secret-key buffers may not be cleared on all exit paths.
Security signals we found
secret-key material left uncleared on an error path
use of explicit memory clearing (`secp256k1_memclear_explicit`) added to failure path
MuSig nonce generation function
reported by external finder (l0rinc)
Evidence from the diff
In secp256k1_musig_nonce_gen_counter, a local seckey buffer was populated from the keypair and passed to secp256k1_musig_nonce_gen_internal. Previously, if that internal call returned 0 (failure), the function returned immediately without calling secp256k1_memclear_explicit on seckey. The patch stores the return value, always clears seckey, then returns the stored result. The only known failure conditions for secp256k1_musig_nonce_gen_internal are invalid keypair or keyagg_cache arguments, so the uncleared secret-key exposure is limited to API-misuse scenarios.
Changed components
src/modules/musig/session_impl.hsecp256k1_musig_nonce_gen_counterInspect captured patch +2 / −4
diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h
index 6a37bfd..510ee89 100644
--- a/src/modules/musig/session_impl.h
+++ b/src/modules/musig/session_impl.h
@@ -483,11 +483,9 @@ int secp256k1_musig_nonce_gen_counter(const secp256k1_context* ctx, secp256k1_mu
(void) ret;
#endif
- if (!secp256k1_musig_nonce_gen_internal(ctx, secnonce, pubnonce, buf, seckey, &pubkey, msg32, keyagg_cache, extra_input32)) {
- return 0;
- }
+ ret = secp256k1_musig_nonce_gen_internal(ctx, secnonce, pubnonce, buf, seckey, &pubkey, msg32, keyagg_cache, extra_input32);
secp256k1_memclear_explicit(seckey, sizeof(seckey));
- return 1;
+ return ret;
}
static int secp256k1_musig_sum_pubnonces(const secp256k1_context* ctx, secp256k1_gej *summed_pubnonces, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) {
Why this scored 34/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.