key: cleanse ChainCode on destruction

HMAC primitives cleanse their internal stack buffers, but a caller's
ChainCode remains populated in memory after use. Promote ChainCode
from `typedef uint256` to a `base_blob<256>` subclass with a
memory_cleanse() destructor, so chain codes in CExtKey, CExtPubKey,
and local variables are cleansed on scope exit.

Retype MUSIG_CHAINCODE from `constexpr uint256` to `const ChainCode`
to match its BIP328 semantic role. Dropping `constexpr` (ChainCode is
no longer a literal type) also removes the GCC-14 consteval lambda
workaround.

Remove the duplicate typedef in pubkey.h (which includes hash.h
transitively). Two fuzz-test call sites in test/fuzz/key.cpp now
construct the chain-code argument explicitly rather than relying on
the typedef.
This commit is contained in:
Thomas
2026-05-12 21:24:19 +02:00
parent b3a3f88346
commit 21a1380c13
4 changed files with 12 additions and 9 deletions

View File

@@ -13,12 +13,20 @@
#include <prevector.h>
#include <serialize.h>
#include <span.h>
#include <support/cleanse.h>
#include <uint256.h>
#include <string>
#include <vector>
typedef uint256 ChainCode;
/** A BIP32 chain code. Cleansed on destruction. */
class ChainCode : public base_blob<256> {
public:
constexpr ChainCode() = default;
constexpr explicit ChainCode(std::span<const unsigned char> vch) : base_blob<256>(vch) {}
constexpr explicit ChainCode(const base_blob<256>& b) : base_blob<256>(b) {}
~ChainCode() { memory_cleanse(data(), size()); }
};
/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */
class CHash256 {

View File

@@ -9,10 +9,7 @@
//! MuSig2 chaincode as defined by BIP 328
using namespace util::hex_literals;
constexpr uint256 MUSIG_CHAINCODE{
// Use immediate lambda to work around GCC-14 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117966
[]() consteval { return uint256{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8}; }(),
};
const ChainCode MUSIG_CHAINCODE{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8};
static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
{

View File

@@ -27,8 +27,6 @@ public:
explicit CKeyID(const uint160& in) : uint160(in) {}
};
typedef uint256 ChainCode;
/** An encapsulated public key. */
class CPubKey
{

View File

@@ -85,7 +85,7 @@ FUZZ_TARGET(key, .init = initialize_key)
{
CKey child_key;
ChainCode child_chaincode;
const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
const bool ok = key.Derive(child_key, child_chaincode, 0, ChainCode{random_uint256});
assert(ok);
assert(child_key.IsValid());
assert(!(child_key == key));
@@ -275,7 +275,7 @@ FUZZ_TARGET(key, .init = initialize_key)
{
CPubKey child_pubkey;
ChainCode child_chaincode;
const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, ChainCode{random_uint256});
assert(ok);
assert(child_pubkey != pubkey);
assert(child_pubkey.IsCompressed());