walletdb: hash pubkey/privkey in one shot to avoid leaking secret data
What changed, and why it matters
This change is a defensive cleanup in Bitcoin Core's wallet code. Previously, when saving or loading private keys, the code temporarily copied the public and private keys together into an ordinary memory buffer (a std::vector) before hashing them. That buffer could remain in memory longer than necessary, creating a small window where sensitive private-key material might be exposed through memory dumps, core dumps, or swap. The patch removes that temporary buffer and instead hashes the keys directly in one step. It does not fix an active exploit or reported breach, but it reduces a real, if narrow, information-disclosure risk.
Treat as a worthwhile hardening patch. Users running affected versions should upgrade through normal release channels if this commit is included in a release. No emergency response is warranted because the exposure window is small and requires access to process memory or a crash dump. Developers should prefer direct multi-input hashing for sensitive material and avoid copying private keys into standard containers.
Security signals we found
Commit message states goal is to avoid leaking secret data
Private key material was previously copied into an unscrubbed std::vector during wallet load/save
Patch removes temporary buffer and hashes keys directly
No change to cryptographic output or database format
Defensive hardening rather than fix for a known exploit
Evidence from the diff
The commit modifies src/wallet/walletdb.cpp to replace four instances where a std::vector
Changed components
src/wallet/walletdb.cppWalletBatch::WriteKeyWalletBatch::WriteDescriptorKeyLoadKeyLoadDescriptorWalletRecordsInspect captured patch +8 / −20
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 4cb8c11e..6e6aab14 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -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;
Why this scored 37/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.