Skip to content

Commit 9abed46

Browse files
committed
Merge #16946: wallet: include a checksum of encrypted private keys
d67055e Upgrade or rewrite encrypted key checksums (Andrew Chow) c9a9ddb Set fDecryptionThoroughlyChecked based on whether crypted key checksums are valid (Andrew Chow) a8334f7 Read and write a checksum for encrypted keys (Andrew Chow) Pull request description: Adds a checksum to the encrypted key record in the wallet database so that encrypted keys can be checked for corruption on wallet loading, in the same way that unencrypted keys are. This allows for us to skip the full decryption of keys upon the first unlocking of the wallet in that session as any key corruption will have already been detected. The checksum is just the double SHA256 of the encrypted key and it is appended to the record after the encrypted key itself. This is backwards compatible as old wallets will be able to read the encrypted key and ignore that there is more data in the stream. Additionally, old wallets will be upgraded upon their first unlocking (so that key decryption is checked before we commit to a checksum of the encrypted key) and a wallet flag set indicating that. The presence of the wallet flag lets us skip the full decryption as if `fDecryptionThoroughlyChecked` were true. This does mean that the first time an old wallet is unlocked in a new version will take much longer, but subsequent unlocks will be instantaneous. Furthermore, corruption will be detected upon loading rather than on trying to send so wallet corruption will be detected sooner. Fixes #12423 ACKs for top commit: laanwj: code review ACK d67055e jonatack: Code review ACK d67055e meshcollider: Code review ACK d67055e Tree-SHA512: d5c1c10cfcb5db9e10dcf2326423565a9f499290b81f3155ec72254ed5bd7491e2ff5c50e98590eb07842c20d7797b4efa1c3475bae64971d500aad3b4e711d4
2 parents 4479eb0 + d67055e commit 9abed46

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

src/wallet/scriptpubkeyman.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key
220220
bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
221221
bool keyFail = false;
222222
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
223+
WalletBatch batch(m_storage.GetDatabase());
223224
for (; mi != mapCryptedKeys.end(); ++mi)
224225
{
225226
const CPubKey &vchPubKey = (*mi).second.first;
@@ -233,6 +234,10 @@ bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key
233234
keyPass = true;
234235
if (fDecryptionThoroughlyChecked)
235236
break;
237+
else {
238+
// Rewrite these encrypted keys with checksums
239+
batch.WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
240+
}
236241
}
237242
if (keyPass && keyFail)
238243
{
@@ -713,8 +718,13 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
713718
return true;
714719
}
715720

716-
bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
721+
bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid)
717722
{
723+
// Set fDecryptionThoroughlyChecked to false when the checksum is invalid
724+
if (!checksum_valid) {
725+
fDecryptionThoroughlyChecked = false;
726+
}
727+
718728
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
719729
}
720730

src/wallet/scriptpubkeyman.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
243243
{
244244
private:
245245
//! keeps track of whether Unlock has run a thorough check before
246-
bool fDecryptionThoroughlyChecked = false;
246+
bool fDecryptionThoroughlyChecked = true;
247247

248248
using WatchOnlySet = std::set<CScript>;
249249
using WatchKeyMap = std::map<CKeyID, CPubKey>;
@@ -385,7 +385,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
385385
//! Adds an encrypted key to the store, and saves it to disk.
386386
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
387387
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
388-
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
388+
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
389389
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
390390
//! Adds a CScript to the store
391391
bool LoadCScript(const CScript& redeemScript);

src/wallet/walletdb.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,19 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
115115
return false;
116116
}
117117

118-
if (!WriteIC(std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey), vchCryptedSecret, false)) {
119-
return false;
118+
// Compute a checksum of the encrypted key
119+
uint256 checksum = Hash(vchCryptedSecret.begin(), vchCryptedSecret.end());
120+
121+
const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
122+
if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
123+
// It may already exist, so try writing just the checksum
124+
std::vector<unsigned char> val;
125+
if (!m_batch.Read(key, val)) {
126+
return false;
127+
}
128+
if (!WriteIC(key, std::make_pair(val, checksum), true)) {
129+
return false;
130+
}
120131
}
121132
EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
122133
return true;
@@ -397,9 +408,21 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
397408
}
398409
std::vector<unsigned char> vchPrivKey;
399410
ssValue >> vchPrivKey;
411+
412+
// Get the checksum and check it
413+
bool checksum_valid = false;
414+
if (!ssValue.eof()) {
415+
uint256 checksum;
416+
ssValue >> checksum;
417+
if ((checksum_valid = Hash(vchPrivKey.begin(), vchPrivKey.end()) != checksum)) {
418+
strErr = "Error reading wallet database: Crypted key corrupt";
419+
return false;
420+
}
421+
}
422+
400423
wss.nCKeys++;
401424

402-
if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey))
425+
if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
403426
{
404427
strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
405428
return false;

0 commit comments

Comments
 (0)