walletdb: hash pubkey/privkey in one shot to avoid leaking secret data

Avoid storing the privkey in a vector, which could linger in memory
and potentially leak sensitive data. An alternative approach is to
use `secure_allocator` for the `std::vector` instances, but this
commit has the advantage of also deduplicating code at the same shot.

Thanks to @theuni for suggesting this.
This commit is contained in:
Sebastian Falbesoner
2026-03-06 16:58:49 +01:00
parent 8b70ed6996
commit 501a3dd4ad

View File

@@ -117,12 +117,9 @@ bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey,
} }
// hash pubkey/privkey to accelerate wallet load // hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> vchKey; const auto keypair_hash = Hash(vchPubKey, vchPrivKey);
vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
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, 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) bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
{ {
// hash pubkey/privkey to accelerate wallet load // hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> key; const auto keypair_hash = Hash(pubkey, privkey);
key.reserve(pubkey.size() + privkey.size());
key.insert(key.end(), pubkey.begin(), pubkey.end());
key.insert(key.end(), privkey.begin(), privkey.end());
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) 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()) if (!hash.IsNull())
{ {
// hash pubkey/privkey to accelerate wallet load // hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> vchKey; const auto keypair_hash = Hash(vchPubKey, pkey);
vchKey.reserve(vchPubKey.size() + pkey.size());
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
if (Hash(vchKey) != hash) if (keypair_hash != hash)
{ {
strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
return false; return false;
@@ -875,12 +866,9 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
value >> hash; value >> hash;
// hash pubkey/privkey to accelerate wallet load // hash pubkey/privkey to accelerate wallet load
std::vector<unsigned char> to_hash; const auto keypair_hash = Hash(pubkey, pkey);
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());
if (Hash(to_hash) != hash) if (keypair_hash != hash)
{ {
strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt"; strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt";
return DBErrors::CORRUPT; return DBErrors::CORRUPT;