wallet: unify HD chain generation in LegacyScriptPubKeyMan

most of the steps are overlapping except for the encryption sections,
which we can make contingent on supplying of the keying material. `res`
isn't used anywhere since we plan on hard crashing if they fail anyway,
so if we got that far, we've already succeeded.

we will validate vMasterKey's size before starting the encryption
routine to prevent a value outside of our control from hard-crashing
the client.
This commit is contained in:
Kittywhiskers Van Gogh 2024-07-17 16:31:30 +00:00
parent 1394c41c8d
commit 163d31861c
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
3 changed files with 27 additions and 45 deletions

View File

@ -377,56 +377,42 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
} }
} }
void LegacyScriptPubKeyMan::GenerateNewCryptedHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, CKeyingMaterial vMasterKey) void LegacyScriptPubKeyMan::GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, std::optional<CKeyingMaterial> vMasterKeyOpt)
{
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
CHDChain hdChainTmp;
// NOTE: an empty mnemonic means "generate a new one for me"
// NOTE: default mnemonic passphrase is an empty string
if (!hdChainTmp.SetMnemonic(secureMnemonic, secureMnemonicPassphrase, true)) {
throw std::runtime_error(std::string(__func__) + ": SetMnemonic failed");
}
// add default account
hdChainTmp.AddAccount();
// We need to safe chain for validation further
CHDChain hdChainPrev = hdChainTmp;
bool res = EncryptHDChain(vMasterKey, hdChainTmp);
assert(res);
res = LoadHDChain(hdChainTmp);
assert(res);
CHDChain hdChainCrypted;
res = GetHDChain(hdChainCrypted);
assert(res);
// ids should match, seed hashes should not
assert(hdChainPrev.GetID() == hdChainCrypted.GetID());
assert(hdChainPrev.GetSeedHash() != hdChainCrypted.GetSeedHash());
if (!AddHDChainSingle(hdChainCrypted)) {
throw std::runtime_error(std::string(__func__) + ": AddHDChainSingle failed");
}
}
void LegacyScriptPubKeyMan::GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase)
{ {
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
CHDChain newHdChain; CHDChain newHdChain;
// NOTE: an empty mnemonic means "generate a new one for me" // NOTE: an empty mnemonic means "generate a new one for me"
// NOTE: default mnemonic passphrase is an empty string // NOTE: default mnemonic passphrase is an empty string
if (!newHdChain.SetMnemonic(secureMnemonic, secureMnemonicPassphrase, true)) { if (!newHdChain.SetMnemonic(secureMnemonic, secureMnemonicPassphrase, /* fUpdateID = */ true)) {
throw std::runtime_error(std::string(__func__) + ": SetMnemonic failed"); throw std::runtime_error(std::string(__func__) + ": SetMnemonic failed");
} }
// add default account // Add default account
newHdChain.AddAccount(); newHdChain.AddAccount();
// Encryption routine if vMasterKey has been supplied
if (vMasterKeyOpt.has_value()) {
auto vMasterKey = vMasterKeyOpt.value();
if (vMasterKey.size() != WALLET_CRYPTO_KEY_SIZE) {
throw std::runtime_error(strprintf("%s : invalid vMasterKey size, got %zd (expected %lld)", __func__, vMasterKey.size(), WALLET_CRYPTO_KEY_SIZE));
}
// Maintain an unencrypted copy of the chain for sanity checking
CHDChain prevHdChain{newHdChain};
bool res = EncryptHDChain(vMasterKey, newHdChain);
assert(res);
res = LoadHDChain(newHdChain);
assert(res);
res = GetHDChain(newHdChain);
assert(res);
// IDs should match, seed hashes should not
assert(prevHdChain.GetID() == newHdChain.GetID());
assert(prevHdChain.GetSeedHash() != newHdChain.GetSeedHash());
}
if (!AddHDChainSingle(newHdChain)) { if (!AddHDChainSingle(newHdChain)) {
throw std::runtime_error(std::string(__func__) + ": AddHDChainSingle failed"); throw std::runtime_error(std::string(__func__) + ": AddHDChainSingle failed");
} }

View File

@ -463,8 +463,7 @@ public:
bool GetDecryptedHDChain(CHDChain& hdChainRet); bool GetDecryptedHDChain(CHDChain& hdChainRet);
/* Generates a new HD chain */ /* Generates a new HD chain */
void GenerateNewCryptedHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, CKeyingMaterial vMasterKey); void GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, std::optional<CKeyingMaterial> vMasterKey = std::nullopt);
void GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase);
/** /**
* Explicitly make the wallet learn the related scripts for outputs to the * Explicitly make the wallet learn the related scripts for outputs to the

View File

@ -5677,16 +5677,13 @@ bool CWallet::GenerateNewHDChainEncrypted(const SecureString& secureMnemonic, co
} }
spk_man->GenerateNewCryptedHDChain(secureMnemonic, secureMnemonicPassphrase, vMasterKey); spk_man->GenerateNewHDChain(secureMnemonic, secureMnemonicPassphrase, vMasterKey);
Lock(); Lock();
if (!Unlock(secureWalletPassphrase)) { if (!Unlock(secureWalletPassphrase)) {
// this should never happen // this should never happen
throw std::runtime_error(std::string(__func__) + ": Unlock failed"); throw std::runtime_error(std::string(__func__) + ": Unlock failed");
} }
if (!spk_man->NewKeyPool()) {
throw std::runtime_error(std::string(__func__) + ": NewKeyPool failed");
}
return true; return true;
} }