Store key origin info in key metadata

Store the master key fingerprint and derivation path in the
key metadata. hdKeypath is kept to indicate the seed and for
backwards compatibility, but all key derivation path output
uses the key origin info instead of hdKeypath.
This commit is contained in:
Andrew Chow
2018-11-06 09:23:37 -05:00
parent 345bff6013
commit eab63bc264
7 changed files with 100 additions and 18 deletions

View File

@@ -256,16 +256,25 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey
if (internal) {
chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/1'/" + std::to_string(hdChain.nInternalChainCounter) + "'";
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
hdChain.nInternalChainCounter++;
}
else {
chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
hdChain.nExternalChainCounter++;
}
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
metadata.hd_seed_id = hdChain.seed_id;
CKeyID master_id = masterKey.key.GetPubKey().GetID();
std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint);
metadata.has_key_origin = true;
// update the chain model in the database
if (!batch.WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
@@ -355,6 +364,41 @@ bool CWallet::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey,
return WalletBatch(*database).WriteKeyMetadata(meta, pubkey, overwrite);
}
void CWallet::UpgradeKeyMetadata()
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
return;
}
for (auto& meta_pair : mapKeyMetadata) {
CKeyMetadata& meta = meta_pair.second;
if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin
CKey key;
GetKey(meta.hd_seed_id, key);
CExtKey masterKey;
masterKey.SetSeed(key.begin(), key.size());
// Add to map
CKeyID master_id = masterKey.key.GetPubKey().GetID();
std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint);
if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) {
throw std::runtime_error("Invalid stored hdKeypath");
}
meta.has_key_origin = true;
if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) {
meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN;
}
// Write meta to wallet
CPubKey pubkey;
if (GetPubKey(meta_pair.first, pubkey)) {
WriteKeyMetadata(meta, pubkey, true);
}
}
}
SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
}
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
@@ -453,8 +497,11 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_key
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
continue; // try another master key
if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys))
if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys)) {
// Now that we've unlocked, upgrade the key metadata
UpgradeKeyMetadata();
return true;
}
}
}
return false;
@@ -1414,6 +1461,7 @@ CPubKey CWallet::DeriveNewSeed(const CKey& key)
// set the hd keypath to "s" -> Seed, refers the seed to itself
metadata.hdKeypath = "s";
metadata.has_key_origin = false;
metadata.hd_seed_id = seed.GetID();
{
@@ -4494,16 +4542,9 @@ bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
meta = it->second;
}
}
if (!meta.hdKeypath.empty()) {
if (!ParseHDKeypath(meta.hdKeypath, info.path)) return false;
// Get the proper master key id
CKey key;
GetKey(meta.hd_seed_id, key);
CExtKey masterKey;
masterKey.SetSeed(key.begin(), key.size());
// Compute identifier
CKeyID masterid = masterKey.key.GetPubKey().GetID();
std::copy(masterid.begin(), masterid.begin() + 4, info.fingerprint);
if (meta.has_key_origin) {
std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, info.fingerprint);
info.path = meta.key_origin.path;
} else { // Single pubkeys get the master fingerprint of themselves
std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint);
}