Merge bitcoin/bitcoin#34759: walletdb: hash pubkey/privkey in one shot to avoid leaking secret data

501a3dd4ad4a545a05663a78cec61575966045c7 walletdb: hash pubkey/privkey in one shot to avoid leaking secret data (Sebastian Falbesoner)

Pull request description:

  In several places in the wallet DB module, byte strings containing serialized public keys and secret keys are created in order to be hashed. To avoid sensitive data lingering in memory (and potentially leaking), don't store the preimage, but hash both public key and secret key in one shot, using the overloaded `Hash` function:
  d198635fa2/src/hash.h (L82-L88)

  See e.g. #31166 and #31774 for similarly themed PRs (Note that in #31166 we used the explicit `memory_cleanse` approach though, as changing the allocator was not possible.)

ACKs for top commit:
  davidgumberg:
    crACK 501a3dd4ad
  furszy:
    ACK 501a3dd4ad4a545a05663a78cec61575966045c7
  rkrux:
    ACK 501a3dd
  theuni:
    ACK 501a3dd4ad4a545a05663a78cec61575966045c7

Tree-SHA512: 8a71685b26bf89fca181aed6512a8db843b6d1dc740a468bb33fb2a629a23167a9676c228d1077ad8db2df9db80f47e32ec013737e93df8ee6f4ba505d3d50c9
This commit is contained in:
merge-script 2026-03-10 11:19:40 +00:00
commit 544c15ff4e
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1

View File

@ -117,12 +117,9 @@ bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey,
}
// hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> vchKey;
vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
const auto keypair_hash = Hash(vchPubKey, vchPrivKey);
return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey)), false);
return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, keypair_hash), false);
}
bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
@ -220,12 +217,9 @@ bool WalletBatch::EraseActiveScriptPubKeyMan(uint8_t type, bool internal)
bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
{
// hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> key;
key.reserve(pubkey.size() + privkey.size());
key.insert(key.end(), pubkey.begin(), pubkey.end());
key.insert(key.end(), privkey.begin(), privkey.end());
const auto keypair_hash = Hash(pubkey, privkey);
return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(privkey, Hash(key)), false);
return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(privkey, keypair_hash), false);
}
bool WalletBatch::WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret)
@ -328,12 +322,9 @@ bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::stri
if (!hash.IsNull())
{
// hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> vchKey;
vchKey.reserve(vchPubKey.size() + pkey.size());
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
const auto keypair_hash = Hash(vchPubKey, pkey);
if (Hash(vchKey) != hash)
if (keypair_hash != hash)
{
strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
return false;
@ -875,12 +866,9 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
value >> hash;
// hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> to_hash;
to_hash.reserve(pubkey.size() + pkey.size());
to_hash.insert(to_hash.end(), pubkey.begin(), pubkey.end());
to_hash.insert(to_hash.end(), pkey.begin(), pkey.end());
const auto keypair_hash = Hash(pubkey, pkey);
if (Hash(to_hash) != hash)
if (keypair_hash != hash)
{
strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt";
return DBErrors::CORRUPT;