mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-17 01:58:57 +02:00
Merge bitcoin/bitcoin#35254: crypto: cleanse HMAC stack buffers after use and ChainCode
21a1380c13key: cleanse ChainCode on destruction (Thomas)b3a3f88346crypto: cleanse HMAC stack buffers after use (Thomas) Pull request description: `CHMAC_SHA256` and `CHMAC_SHA512` leave two stack buffers populated on return: `rkey[]` holds `K' ⊕ ipad` after the constructor, and `temp[]` holds the inner-hash output after `Finalize()`. When the HMAC is keyed with sensitive material (chain code in `BIP32Hash()` in `hash.cpp` for BIP32 child key derivation; PRK in HKDF-Expand in `hkdf_sha256_32.cpp`, used for BIP324 transport keying), `rkey` is one constant XOR from that key, and `temp` is a one-way digest covering it. This PR cleanses both buffers with `memory_cleanse()`, matching the convention already used in `chacha20.cpp` and `chacha20poly1305.cpp`. No observable change for callers. Update: Cleansing the HMAC primitive's internal buffers still leaves a caller's `ChainCode` value populated in memory after use. The second commit promotes `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. `MUSIG_CHAINCODE` is retyped from `constexpr uint256` to `const ChainCode` to match its BIP328 semantic role; this also removes the GCC-14 consteval lambda workaround. ACKs for top commit: davidgumberg: crACK21a1380c13optout21: ACK21a1380c13achow101: ACK21a1380c13winterrdog: ACK21a1380c13Tree-SHA512: 022c8372da3e2c9c269ef55b695d8415241acf64be04692f30da0e682dd1d05178f95601a3bd208573fd0630656b3dedcf6de34a2a3cf794515c0268e710af75
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <crypto/hmac_sha256.h>
|
||||
|
||||
#include <crypto/sha256.h>
|
||||
#include <support/cleanse.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -26,6 +27,8 @@ CHMAC_SHA256::CHMAC_SHA256(const unsigned char* key, size_t keylen)
|
||||
for (int n = 0; n < 64; n++)
|
||||
rkey[n] ^= 0x5c ^ 0x36;
|
||||
inner.Write(rkey, 64);
|
||||
|
||||
memory_cleanse(rkey, sizeof(rkey));
|
||||
}
|
||||
|
||||
void CHMAC_SHA256::Finalize(unsigned char hash[OUTPUT_SIZE])
|
||||
@@ -33,4 +36,5 @@ void CHMAC_SHA256::Finalize(unsigned char hash[OUTPUT_SIZE])
|
||||
unsigned char temp[32];
|
||||
inner.Finalize(temp);
|
||||
outer.Write(temp, 32).Finalize(hash);
|
||||
memory_cleanse(temp, sizeof(temp));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <crypto/hmac_sha512.h>
|
||||
|
||||
#include <crypto/sha512.h>
|
||||
#include <support/cleanse.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -26,6 +27,8 @@ CHMAC_SHA512::CHMAC_SHA512(const unsigned char* key, size_t keylen)
|
||||
for (int n = 0; n < 128; n++)
|
||||
rkey[n] ^= 0x5c ^ 0x36;
|
||||
inner.Write(rkey, 128);
|
||||
|
||||
memory_cleanse(rkey, sizeof(rkey));
|
||||
}
|
||||
|
||||
void CHMAC_SHA512::Finalize(unsigned char hash[OUTPUT_SIZE])
|
||||
@@ -33,4 +36,5 @@ void CHMAC_SHA512::Finalize(unsigned char hash[OUTPUT_SIZE])
|
||||
unsigned char temp[64];
|
||||
inner.Finalize(temp);
|
||||
outer.Write(temp, 64).Finalize(hash);
|
||||
memory_cleanse(temp, sizeof(temp));
|
||||
}
|
||||
|
||||
10
src/hash.h
10
src/hash.h
@@ -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 {
|
||||
|
||||
@@ -11,10 +11,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)
|
||||
{
|
||||
|
||||
@@ -27,8 +27,6 @@ public:
|
||||
explicit CKeyID(const uint160& in) : uint160(in) {}
|
||||
};
|
||||
|
||||
typedef uint256 ChainCode;
|
||||
|
||||
/** An encapsulated public key. */
|
||||
class CPubKey
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user