diff options
| author | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-07 10:55:50 -0300 |
|---|---|---|
| committer | Bitflash <admin@bitflash.network> | 2026-08-07 11:05:09 -0300 |
| commit | d925be57e22213554b0ab2a64d7618611fedf81d (patch) | |
| tree | 4d43fe71e94849255e259aaca0d197453cb24a20 | |
| parent | 09ff5751264de6101f1bf0981accac0123351a7a (diff) | |
wallet: cleanse unlocked secrets before clearing them (#2)
| -rw-r--r-- | src/db.cpp | 19 | ||||
| -rw-r--r-- | src/main.cpp | 56 |
2 files changed, 64 insertions, 11 deletions
| @@ -1404,6 +1404,13 @@ static bool EncryptPrivateKeyForWallet(const CKeyingMaterial& vchMasterKey, | |||
| 1404 | return true; | 1404 | return true; |
| 1405 | } | 1405 | } |
| 1406 | 1406 | ||
| 1407 | static void SecureClearBytes(vector<unsigned char>& vch) | ||
| 1408 | { | ||
| 1409 | if (!vch.empty()) | ||
| 1410 | OPENSSL_cleanse(&vch[0], vch.size()); | ||
| 1411 | vch.clear(); | ||
| 1412 | } | ||
| 1413 | |||
| 1407 | bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& strErrorRet) | 1414 | bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& strErrorRet) |
| 1408 | { | 1415 | { |
| 1409 | strBackupRet.clear(); | 1416 | strBackupRet.clear(); |
| @@ -1423,6 +1430,7 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st | |||
| 1423 | map<vector<unsigned char>, CPrivKey> mapPlainKeys; | 1430 | map<vector<unsigned char>, CPrivKey> mapPlainKeys; |
| 1424 | vector<unsigned char> vchPlainHDMaster; | 1431 | vector<unsigned char> vchPlainHDMaster; |
| 1425 | vector<unsigned char> vchPlainHDChainCode; | 1432 | vector<unsigned char> vchPlainHDChainCode; |
| 1433 | bool fHadHDSeed = false; | ||
| 1426 | unsigned int nHDNextSnapshot = 0; | 1434 | unsigned int nHDNextSnapshot = 0; |
| 1427 | { | 1435 | { |
| 1428 | CRITICAL_BLOCK(cs_mapKeys) | 1436 | CRITICAL_BLOCK(cs_mapKeys) |
| @@ -1433,6 +1441,7 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st | |||
| 1433 | { | 1441 | { |
| 1434 | vchPlainHDMaster = vchHDMaster; | 1442 | vchPlainHDMaster = vchHDMaster; |
| 1435 | vchPlainHDChainCode = vchHDChainCode; | 1443 | vchPlainHDChainCode = vchHDChainCode; |
| 1444 | fHadHDSeed = !vchPlainHDMaster.empty() || !vchPlainHDChainCode.empty(); | ||
| 1436 | nHDNextSnapshot = nHDNext; | 1445 | nHDNextSnapshot = nHDNext; |
| 1437 | } | 1446 | } |
| 1438 | } | 1447 | } |
| @@ -1483,11 +1492,13 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st | |||
| 1483 | 1492 | ||
| 1484 | vector<unsigned char> vchCryptedHDMasterNew; | 1493 | vector<unsigned char> vchCryptedHDMasterNew; |
| 1485 | vector<unsigned char> vchCryptedHDChainCodeNew; | 1494 | vector<unsigned char> vchCryptedHDChainCodeNew; |
| 1486 | if (!vchPlainHDMaster.empty() || !vchPlainHDChainCode.empty()) | 1495 | if (fHadHDSeed) |
| 1487 | { | 1496 | { |
| 1488 | if (vchPlainHDMaster.size() != 32 || vchPlainHDChainCode.size() != 32) | 1497 | if (vchPlainHDMaster.size() != 32 || vchPlainHDChainCode.size() != 32) |
| 1489 | { | 1498 | { |
| 1490 | strErrorRet = "wallet has an incomplete HD seed"; | 1499 | strErrorRet = "wallet has an incomplete HD seed"; |
| 1500 | SecureClearBytes(vchPlainHDMaster); | ||
| 1501 | SecureClearBytes(vchPlainHDChainCode); | ||
| 1491 | return false; | 1502 | return false; |
| 1492 | } | 1503 | } |
| 1493 | if (!EncryptSecret(vchMasterKey, vchPlainHDMaster, | 1504 | if (!EncryptSecret(vchMasterKey, vchPlainHDMaster, |
| @@ -1496,8 +1507,12 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st | |||
| 1496 | WalletSecretIV("hdchaincode"), vchCryptedHDChainCodeNew)) | 1507 | WalletSecretIV("hdchaincode"), vchCryptedHDChainCodeNew)) |
| 1497 | { | 1508 | { |
| 1498 | strErrorRet = "could not encrypt the HD seed"; | 1509 | strErrorRet = "could not encrypt the HD seed"; |
| 1510 | SecureClearBytes(vchPlainHDMaster); | ||
| 1511 | SecureClearBytes(vchPlainHDChainCode); | ||
| 1499 | return false; | 1512 | return false; |
| 1500 | } | 1513 | } |
| 1514 | SecureClearBytes(vchPlainHDMaster); | ||
| 1515 | SecureClearBytes(vchPlainHDChainCode); | ||
| 1501 | } | 1516 | } |
| 1502 | 1517 | ||
| 1503 | vector<pair<int64, vector<unsigned char> > > vNewPool; | 1518 | vector<pair<int64, vector<unsigned char> > > vNewPool; |
| @@ -1506,7 +1521,7 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st | |||
| 1506 | for (int i = 0; i < KEYPOOL_SIZE; i++) | 1521 | for (int i = 0; i < KEYPOOL_SIZE; i++) |
| 1507 | { | 1522 | { |
| 1508 | CKey key; | 1523 | CKey key; |
| 1509 | if (!vchPlainHDMaster.empty()) | 1524 | if (fHadHDSeed) |
| 1510 | { | 1525 | { |
| 1511 | string strDeriveError; | 1526 | string strDeriveError; |
| 1512 | if (!DeriveHDKey(nHDNextNew, key, strDeriveError)) | 1527 | if (!DeriveHDKey(nHDNextNew, key, strDeriveError)) |
diff --git a/src/main.cpp b/src/main.cpp index 0342650..e149062 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -165,6 +165,9 @@ int MinerThreadCount() | |||
| 165 | return (int)(nCores - 1); | 165 | return (int)(nCores - 1); |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | static void SecureClearBytes(vector<unsigned char>& vch); | ||
| 169 | static void SecureClearKeyingMaterial(CKeyingMaterial& vch); | ||
| 170 | |||
| 168 | bool AddKey(const CKey& key) | 171 | bool AddKey(const CKey& key) |
| 169 | { | 172 | { |
| 170 | if (IsWalletEncrypted()) | 173 | if (IsWalletEncrypted()) |
| @@ -174,9 +177,13 @@ bool AddKey(const CKey& key) | |||
| 174 | vector<unsigned char> vchCryptedSecret; | 177 | vector<unsigned char> vchCryptedSecret; |
| 175 | if (IsWalletLocked()) | 178 | if (IsWalletLocked()) |
| 176 | return error("AddKey() : wallet is locked\n"); | 179 | return error("AddKey() : wallet is locked\n"); |
| 177 | if (!EncryptSecret(vWalletMasterKey, vector<unsigned char>(vchPrivKey.begin(), vchPrivKey.end()), | 180 | vector<unsigned char> vchPlainSecret(vchPrivKey.begin(), vchPrivKey.end()); |
| 178 | WalletKeyIV(key.GetPubKey()), vchCryptedSecret)) | 181 | if (!EncryptSecret(vWalletMasterKey, vchPlainSecret, WalletKeyIV(key.GetPubKey()), vchCryptedSecret)) |
| 182 | { | ||
| 183 | SecureClearBytes(vchPlainSecret); | ||
| 179 | return error("AddKey() : encrypting key failed\n"); | 184 | return error("AddKey() : encrypting key failed\n"); |
| 185 | } | ||
| 186 | SecureClearBytes(vchPlainSecret); | ||
| 180 | if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) | 187 | if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) |
| 181 | return false; | 188 | return false; |
| 182 | return CWalletDB().WriteCryptedKey(key.GetPubKey(), vchCryptedSecret); | 189 | return CWalletDB().WriteCryptedKey(key.GetPubKey(), vchCryptedSecret); |
| @@ -200,17 +207,31 @@ bool IsWalletLocked() | |||
| 200 | return fWalletEncrypted && fWalletLocked; | 207 | return fWalletEncrypted && fWalletLocked; |
| 201 | } | 208 | } |
| 202 | 209 | ||
| 210 | static void SecureClearBytes(vector<unsigned char>& vch) | ||
| 211 | { | ||
| 212 | if (!vch.empty()) | ||
| 213 | OPENSSL_cleanse(&vch[0], vch.size()); | ||
| 214 | vch.clear(); | ||
| 215 | } | ||
| 216 | |||
| 217 | static void SecureClearKeyingMaterial(CKeyingMaterial& vch) | ||
| 218 | { | ||
| 219 | if (!vch.empty()) | ||
| 220 | OPENSSL_cleanse(&vch[0], vch.size()); | ||
| 221 | vch.clear(); | ||
| 222 | } | ||
| 223 | |||
| 203 | void LockWallet() | 224 | void LockWallet() |
| 204 | { | 225 | { |
| 205 | if (!fWalletEncrypted) | 226 | if (!fWalletEncrypted) |
| 206 | return; | 227 | return; |
| 207 | CRITICAL_BLOCK(cs_mapKeys) | 228 | CRITICAL_BLOCK(cs_mapKeys) |
| 208 | { | 229 | { |
| 209 | vWalletMasterKey.clear(); | 230 | SecureClearKeyingMaterial(vWalletMasterKey); |
| 210 | if (!vchCryptedHDMaster.empty()) | 231 | if (!vchCryptedHDMaster.empty()) |
| 211 | vchHDMaster.clear(); | 232 | SecureClearBytes(vchHDMaster); |
| 212 | if (!vchCryptedHDChainCode.empty()) | 233 | if (!vchCryptedHDChainCode.empty()) |
| 213 | vchHDChainCode.clear(); | 234 | SecureClearBytes(vchHDChainCode); |
| 214 | fWalletLocked = true; | 235 | fWalletLocked = true; |
| 215 | } | 236 | } |
| 216 | } | 237 | } |
| @@ -235,14 +256,14 @@ bool DeriveWalletPassphraseKey(const string& strPassphrase, | |||
| 235 | chKey, chIV); | 256 | chKey, chIV); |
| 236 | if (n != 32) | 257 | if (n != 32) |
| 237 | { | 258 | { |
| 238 | memset(chKey, 0, sizeof(chKey)); | 259 | OPENSSL_cleanse(chKey, sizeof(chKey)); |
| 239 | memset(chIV, 0, sizeof(chIV)); | 260 | OPENSSL_cleanse(chIV, sizeof(chIV)); |
| 240 | return false; | 261 | return false; |
| 241 | } | 262 | } |
| 242 | vchKeyRet.assign(chKey, chKey + sizeof(chKey)); | 263 | vchKeyRet.assign(chKey, chKey + sizeof(chKey)); |
| 243 | vchIVRet.assign(chIV, chIV + sizeof(chIV)); | 264 | vchIVRet.assign(chIV, chIV + sizeof(chIV)); |
| 244 | memset(chKey, 0, sizeof(chKey)); | 265 | OPENSSL_cleanse(chKey, sizeof(chKey)); |
| 245 | memset(chIV, 0, sizeof(chIV)); | 266 | OPENSSL_cleanse(chIV, sizeof(chIV)); |
| 246 | return true; | 267 | return true; |
| 247 | } | 268 | } |
| 248 | 269 | ||
| @@ -303,6 +324,8 @@ bool DecryptSecret(const CKeyingMaterial& vchKey, | |||
| 303 | vchPlaintextRet.swap(vchOut); | 324 | vchPlaintextRet.swap(vchOut); |
| 304 | fOk = true; | 325 | fOk = true; |
| 305 | } | 326 | } |
| 327 | if (!fOk) | ||
| 328 | SecureClearBytes(vchOut); | ||
| 306 | EVP_CIPHER_CTX_free(ctx); | 329 | EVP_CIPHER_CTX_free(ctx); |
| 307 | return fOk; | 330 | return fOk; |
| 308 | } | 331 | } |
| @@ -378,6 +401,7 @@ bool GetWalletPrivKey(const vector<unsigned char>& vchPubKey, | |||
| 378 | } | 401 | } |
| 379 | 402 | ||
| 380 | CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); | 403 | CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); |
| 404 | SecureClearBytes(vchPlaintext); | ||
| 381 | CKey key; | 405 | CKey key; |
| 382 | if (!key.SetPrivKey(vchPrivKey) || key.GetPubKey() != vchPubKey) | 406 | if (!key.SetPrivKey(vchPrivKey) || key.GetPubKey() != vchPubKey) |
| 383 | { | 407 | { |
| @@ -412,11 +436,20 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet) | |||
| 412 | vector<unsigned char> vchMasterPlain; | 436 | vector<unsigned char> vchMasterPlain; |
| 413 | if (!DecryptSecret(vchPassKey, kMasterKey.vchCryptedKey, | 437 | if (!DecryptSecret(vchPassKey, kMasterKey.vchCryptedKey, |
| 414 | vchPassIV, vchMasterPlain)) | 438 | vchPassIV, vchMasterPlain)) |
| 439 | { | ||
| 440 | SecureClearBytes(vchPassIV); | ||
| 415 | continue; | 441 | continue; |
| 442 | } | ||
| 416 | if (vchMasterPlain.size() != 32) | 443 | if (vchMasterPlain.size() != 32) |
| 444 | { | ||
| 445 | SecureClearBytes(vchMasterPlain); | ||
| 446 | SecureClearBytes(vchPassIV); | ||
| 417 | continue; | 447 | continue; |
| 448 | } | ||
| 418 | 449 | ||
| 419 | CKeyingMaterial vchCandidate(vchMasterPlain.begin(), vchMasterPlain.end()); | 450 | CKeyingMaterial vchCandidate(vchMasterPlain.begin(), vchMasterPlain.end()); |
| 451 | SecureClearBytes(vchMasterPlain); | ||
| 452 | SecureClearBytes(vchPassIV); | ||
| 420 | bool fValid = mapCryptedKeys.empty(); | 453 | bool fValid = mapCryptedKeys.empty(); |
| 421 | for (map<vector<unsigned char>, vector<unsigned char> >::iterator mci = mapCryptedKeys.begin(); | 454 | for (map<vector<unsigned char>, vector<unsigned char> >::iterator mci = mapCryptedKeys.begin(); |
| 422 | mci != mapCryptedKeys.end(); ++mci) | 455 | mci != mapCryptedKeys.end(); ++mci) |
| @@ -425,6 +458,7 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet) | |||
| 425 | if (!DecryptSecret(vchCandidate, (*mci).second, WalletKeyIV((*mci).first), vchPlaintext)) | 458 | if (!DecryptSecret(vchCandidate, (*mci).second, WalletKeyIV((*mci).first), vchPlaintext)) |
| 426 | break; | 459 | break; |
| 427 | CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); | 460 | CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); |
| 461 | SecureClearBytes(vchPlaintext); | ||
| 428 | CKey key; | 462 | CKey key; |
| 429 | if (key.SetPrivKey(vchPrivKey) && key.GetPubKey() == (*mci).first) | 463 | if (key.SetPrivKey(vchPrivKey) && key.GetPubKey() == (*mci).first) |
| 430 | fValid = true; | 464 | fValid = true; |
| @@ -440,10 +474,12 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet) | |||
| 440 | WalletSecretIV("hdmaster"), vchPlainHDMaster) || | 474 | WalletSecretIV("hdmaster"), vchPlainHDMaster) || |
| 441 | vchPlainHDMaster.size() != 32) | 475 | vchPlainHDMaster.size() != 32) |
| 442 | { | 476 | { |
| 477 | SecureClearBytes(vchPlainHDMaster); | ||
| 443 | strErrorRet = "could not decrypt HD master key"; | 478 | strErrorRet = "could not decrypt HD master key"; |
| 444 | return false; | 479 | return false; |
| 445 | } | 480 | } |
| 446 | vchHDMaster = vchPlainHDMaster; | 481 | vchHDMaster = vchPlainHDMaster; |
| 482 | SecureClearBytes(vchPlainHDMaster); | ||
| 447 | } | 483 | } |
| 448 | if (!vchCryptedHDChainCode.empty()) | 484 | if (!vchCryptedHDChainCode.empty()) |
| 449 | { | 485 | { |
| @@ -452,10 +488,12 @@ bool UnlockWallet(const string& strPassphrase, string& strErrorRet) | |||
| 452 | WalletSecretIV("hdchaincode"), vchPlainHDChainCode) || | 488 | WalletSecretIV("hdchaincode"), vchPlainHDChainCode) || |
| 453 | vchPlainHDChainCode.size() != 32) | 489 | vchPlainHDChainCode.size() != 32) |
| 454 | { | 490 | { |
| 491 | SecureClearBytes(vchPlainHDChainCode); | ||
| 455 | strErrorRet = "could not decrypt HD chain code"; | 492 | strErrorRet = "could not decrypt HD chain code"; |
| 456 | return false; | 493 | return false; |
| 457 | } | 494 | } |
| 458 | vchHDChainCode = vchPlainHDChainCode; | 495 | vchHDChainCode = vchPlainHDChainCode; |
| 496 | SecureClearBytes(vchPlainHDChainCode); | ||
| 459 | } | 497 | } |
| 460 | fWalletLocked = false; | 498 | fWalletLocked = false; |
| 461 | return true; | 499 | return true; |