// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "keystore.h" #include "script.h" #include "base58.h" extern bool fWalletUnlockMintOnly; bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const { CKey key; if (!GetKey(address, key)) return false; vchPubKeyOut = key.GetPubKey(); return true; } bool CKeyStore::GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const { CKey key; if (!GetKey(address, key)) return false; vchSecret = key.GetSecret(fCompressed); return true; } bool CBasicKeyStore::AddKey(const CKey& key) { bool fCompressed = false; auto secret = key.GetSecret(fCompressed); { LOCK(cs_KeyStore); mapKeys[key.GetPubKey().GetID()] = { secret, fCompressed }; } return true; } bool CBasicKeyStore::AddMalleableKey(const CMalleableKeyView& keyView, const CSecret& vchSecretH) { { LOCK(cs_KeyStore); mapMalleableKeys[CMalleableKeyView(keyView)] = vchSecretH; } return true; } bool CBasicKeyStore::GetMalleableKey(const CMalleableKeyView &keyView, CMalleableKey &mKey) const { { LOCK(cs_KeyStore); MalleableKeyMap::const_iterator mi = mapMalleableKeys.find(keyView); if (mi != mapMalleableKeys.end()) { mKey = mi->first.GetMalleableKey(mi->second); return true; } } return false; } bool CBasicKeyStore::HaveKey(const CKeyID &address) const { bool result; { LOCK(cs_KeyStore); result = (mapKeys.count(address) > 0); } return result; } void CBasicKeyStore::GetKeys(std::set &setAddress) const { setAddress.clear(); { LOCK(cs_KeyStore); KeyMap::const_iterator mi; for (mi = mapKeys.begin(); mi != mapKeys.end(); ++mi) setAddress.insert((*mi).first); } } bool CBasicKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const { { LOCK(cs_KeyStore); KeyMap::const_iterator mi = mapKeys.find(address); if (mi != mapKeys.end()) { keyOut.SetSecret((*mi).second.first, (*mi).second.second); return true; } } return false; } bool CBasicKeyStore::AddCScript(const CScript& redeemScript) { if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) return error("CBasicKeyStore::AddCScript() : redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE); { LOCK(cs_KeyStore); mapScripts[CScriptID(redeemScript)] = redeemScript; } return true; } bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const { bool result; { LOCK(cs_KeyStore); result = (mapScripts.count(hash) > 0); } return result; } bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const { { LOCK(cs_KeyStore); auto mi = mapScripts.find(hash); if (mi != mapScripts.end()) { redeemScriptOut = (*mi).second; return true; } } return false; } bool CBasicKeyStore::AddWatchOnly(const CScript &dest) { LOCK(cs_KeyStore); CTxDestination address; if (ExtractDestination(dest, address)) { CKeyID keyID; CBitcoinAddress(address).GetKeyID(keyID); if (HaveKey(keyID)) return false; } setWatchOnly.insert(dest); return true; } bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest) { LOCK(cs_KeyStore); setWatchOnly.erase(dest); return true; } bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); return setWatchOnly.count(dest) > 0; } bool CBasicKeyStore::HaveWatchOnly() const { LOCK(cs_KeyStore); return (!setWatchOnly.empty()); } bool CBasicKeyStore::CheckOwnership(const CPubKey &pubKeyVariant, const CPubKey &R) const { { LOCK(cs_KeyStore); for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) { if (mi->first.CheckKeyVariant(R, pubKeyVariant)) return true; } } return false; } bool CBasicKeyStore::CheckOwnership(const CPubKey &pubKeyVariant, const CPubKey &R, CMalleableKeyView &view) const { { LOCK(cs_KeyStore); for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) { if (mi->first.CheckKeyVariant(R, pubKeyVariant)) { view = mi->first; return true; } } } return false; } bool CBasicKeyStore::CreatePrivKey(const CPubKey &pubKeyVariant, const CPubKey &R, CKey &privKey) const { { LOCK(cs_KeyStore); for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) { if (mi->first.CheckKeyVariant(R, pubKeyVariant)) { CMalleableKey mKey = mi->first.GetMalleableKey(mi->second); return mKey.CheckKeyVariant(R, pubKeyVariant, privKey); } } } return false; } void CBasicKeyStore::ListMalleableViews(std::list &malleableViewList) const { malleableViewList.clear(); { LOCK(cs_KeyStore); for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) malleableViewList.push_back(CMalleableKeyView(mi->first)); } } bool CBasicKeyStore::GetMalleableView(const CMalleablePubKey &mpk, CMalleableKeyView &view) { const CKeyID &mpkID = mpk.GetID(); { LOCK(cs_KeyStore); for (MalleableKeyMap::const_iterator mi = mapMalleableKeys.begin(); mi != mapMalleableKeys.end(); mi++) if (mi->first.GetID() == mpkID) { view = CMalleableKeyView(mi->first); return true; } } return false; } bool CCryptoKeyStore::SetCrypted() { { LOCK(cs_KeyStore); if (fUseCrypto) return true; if (!mapKeys.empty()) return false; fUseCrypto = true; } return true; } CCryptoKeyStore::CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false) { } bool CCryptoKeyStore::IsCrypted() const { return fUseCrypto; } bool CCryptoKeyStore::IsLocked() const { if (!IsCrypted()) return false; bool result; { LOCK(cs_KeyStore); result = vMasterKey.empty(); } return result; } bool CCryptoKeyStore::Lock() { if (!SetCrypted()) return false; { LOCK(cs_KeyStore); vMasterKey.clear(); fWalletUnlockMintOnly = false; } NotifyStatusChanged(this); return true; } bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) { { LOCK(cs_KeyStore); if (!SetCrypted()) return false; bool keyPass = false; bool keyFail = false; // Check regular key pairs { auto mi = mapCryptedKeys.begin(); for (; mi != mapCryptedKeys.end(); ++mi) { const auto &vchPubKey = (*mi).second.first; const auto &vchCryptedSecret = (*mi).second.second; CSecret vchSecret; if (!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) { keyFail = true; break; } if (vchSecret.size() != 32) { keyFail = true; break; } keyPass = true; if (fDecryptionThoroughlyChecked) break; } } // Check malleable key pairs { if (keyPass && !keyFail) { auto mi = mapCryptedMalleableKeys.begin(); for(; mi != mapCryptedMalleableKeys.end(); ++mi) { const auto &H = mi->first.GetMalleablePubKey().GetH(); CSecret vchSecretH; if (!DecryptSecret(vMasterKeyIn, mi->second, H.GetHash(), vchSecretH)) { keyFail = true; break; } if (vchSecretH.size() != 32) { keyFail = true; break; } keyPass = true; if (fDecryptionThoroughlyChecked) break; } } } if (keyPass && keyFail) { printf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); assert(false); } if (keyFail || !keyPass) return false; vMasterKey = vMasterKeyIn; fDecryptionThoroughlyChecked = true; } NotifyStatusChanged(this); return true; } bool CCryptoKeyStore::AddKey(const CKey& key) { { LOCK(cs_KeyStore); CScript script; script.SetDestination(key.GetPubKey().GetID()); if (HaveWatchOnly(script)) return false; if (!IsCrypted()) return CBasicKeyStore::AddKey(key); if (IsLocked()) return false; std::vector vchCryptedSecret; auto vchPubKey = key.GetPubKey(); bool fCompressed; if (!EncryptSecret(vMasterKey, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) return false; if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) return false; } return true; } bool CCryptoKeyStore::AddMalleableKey(const CMalleableKeyView& keyView, const CSecret &vchSecretH) { { LOCK(cs_KeyStore); if (!SetCrypted()) return CBasicKeyStore::AddMalleableKey(keyView, vchSecretH); if (IsLocked()) return false; CKey keyH; keyH.SetSecret(vchSecretH, true); std::vector vchCryptedSecretH; if (!EncryptSecret(vMasterKey, vchSecretH, keyH.GetPubKey().GetHash(), vchCryptedSecretH)) return false; if (!AddCryptedMalleableKey(keyView, vchCryptedSecretH)) return false; } return true; } bool CCryptoKeyStore::HaveKey(const CKeyID &address) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::HaveKey(address); return mapCryptedKeys.count(address) > 0; } } bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { { LOCK(cs_KeyStore); if (!SetCrypted()) return false; mapCryptedKeys[vchPubKey.GetID()] = { vchPubKey, vchCryptedSecret }; } return true; } bool CCryptoKeyStore::AddCryptedMalleableKey(const CMalleableKeyView& keyView, const std::vector &vchCryptedSecretH) { { LOCK(cs_KeyStore); if (!SetCrypted()) return false; mapCryptedMalleableKeys[CMalleableKeyView(keyView)] = vchCryptedSecretH; } return true; } bool CCryptoKeyStore::CreatePrivKey(const CPubKey &pubKeyVariant, const CPubKey &R, CKey &privKey) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::CreatePrivKey(pubKeyVariant, R, privKey); for (auto mi = mapCryptedMalleableKeys.begin(); mi != mapCryptedMalleableKeys.end(); mi++) { if (mi->first.CheckKeyVariant(R, pubKeyVariant)) { const CPubKey H = mi->first.GetMalleablePubKey().GetH(); CSecret vchSecretH; if (!DecryptSecret(vMasterKey, mi->second, H.GetHash(), vchSecretH)) return false; if (vchSecretH.size() != 32) return false; CMalleableKey mKey = mi->first.GetMalleableKey(vchSecretH); return mKey.CheckKeyVariant(R, pubKeyVariant, privKey);; } } } return true; } bool CCryptoKeyStore::GetMalleableKey(const CMalleableKeyView &keyView, CMalleableKey &mKey) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::GetMalleableKey(keyView, mKey); auto mi = mapCryptedMalleableKeys.find(keyView); if (mi != mapCryptedMalleableKeys.end()) { const CPubKey H = keyView.GetMalleablePubKey().GetH(); CSecret vchSecretH; if (!DecryptSecret(vMasterKey, mi->second, H.GetHash(), vchSecretH)) return false; if (vchSecretH.size() != 32) return false; mKey = mi->first.GetMalleableKey(vchSecretH); return true; } } return false; } bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::GetKey(address, keyOut); auto mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { const CPubKey &vchPubKey = (*mi).second.first; const std::vector &vchCryptedSecret = (*mi).second.second; CSecret vchSecret; if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) return false; if (vchSecret.size() != 32) return false; keyOut.SetSecret(vchSecret); keyOut.SetCompressedPubKey(vchPubKey.IsCompressed()); return true; } } return false; } bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CKeyStore::GetPubKey(address, vchPubKeyOut); auto mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { vchPubKeyOut = (*mi).second.first; return true; } } return false; } void CCryptoKeyStore::GetKeys(std::set &setAddress) const { if (!IsCrypted()) { CBasicKeyStore::GetKeys(setAddress); return; } setAddress.clear(); CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); while (mi != mapCryptedKeys.end()) { setAddress.insert((*mi).first); mi++; } } bool CCryptoKeyStore::CheckOwnership(const CPubKey &pubKeyVariant, const CPubKey &R) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::CheckOwnership(pubKeyVariant, R); for (CryptedMalleableKeyMap::const_iterator mi = mapCryptedMalleableKeys.begin(); mi != mapCryptedMalleableKeys.end(); mi++) { if (mi->first.CheckKeyVariant(R, pubKeyVariant)) return true; } } return false; } bool CCryptoKeyStore::CheckOwnership(const CPubKey &pubKeyVariant, const CPubKey &R, CMalleableKeyView &view) const { { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::CheckOwnership(pubKeyVariant, R, view); for (CryptedMalleableKeyMap::const_iterator mi = mapCryptedMalleableKeys.begin(); mi != mapCryptedMalleableKeys.end(); mi++) { if (mi->first.CheckKeyVariant(R, pubKeyVariant)) { view = mi->first; return true; } } } return false; } bool CCryptoKeyStore::CheckOwnership(const CMalleablePubKey &mpk) { CMalleableKeyView view; return GetMalleableView(mpk, view); } void CCryptoKeyStore::ListMalleableViews(std::list &malleableViewList) const { malleableViewList.clear(); { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::ListMalleableViews(malleableViewList); for (CryptedMalleableKeyMap::const_iterator mi = mapCryptedMalleableKeys.begin(); mi != mapCryptedMalleableKeys.end(); mi++) malleableViewList.push_back(CMalleableKeyView(mi->first)); } } bool CCryptoKeyStore::GetMalleableView(const CMalleablePubKey &mpk, CMalleableKeyView &view) { const CKeyID &mpkID = mpk.GetID(); { LOCK(cs_KeyStore); if (!IsCrypted()) return CBasicKeyStore::GetMalleableView(mpk, view); for (CryptedMalleableKeyMap::const_iterator mi = mapCryptedMalleableKeys.begin(); mi != mapCryptedMalleableKeys.end(); mi++) if (mi->first.GetID() == mpkID) { view = CMalleableKeyView(mi->first); return true; } } return false; } bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) { { LOCK(cs_KeyStore); if (!mapCryptedKeys.empty() || IsCrypted()) return false; fUseCrypto = true; for(auto& mKey : mapKeys) { CKey key; if (!key.SetSecret(mKey.second.first, mKey.second.second)) return false; const auto vchPubKey = key.GetPubKey(); std::vector vchCryptedSecret; bool fCompressed; if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) return false; if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; } mapKeys.clear(); for(auto& mKey : mapMalleableKeys) { const auto vchPubKeyH = mKey.first.GetMalleablePubKey().GetH(); std::vector vchCryptedSecretH; if (!EncryptSecret(vMasterKeyIn, mKey.second, vchPubKeyH.GetHash(), vchCryptedSecretH)) return false; if (!AddCryptedMalleableKey(mKey.first, vchCryptedSecretH)) return false; } mapMalleableKeys.clear(); } return true; } bool CCryptoKeyStore::DecryptKeys(const CKeyingMaterial& vMasterKeyIn) { { LOCK(cs_KeyStore); if (!IsCrypted()) return false; auto mi = mapCryptedKeys.begin(); for (; mi != mapCryptedKeys.end(); ++mi) { const auto &vchPubKey = (*mi).second.first; const auto &vchCryptedSecret = (*mi).second.second; CSecret vchSecret; if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) return false; if (vchSecret.size() != 32) return false; CKey key; key.SetSecret(vchSecret); key.SetCompressedPubKey(vchPubKey.IsCompressed()); if (!CBasicKeyStore::AddKey(key)) return false; } mapCryptedKeys.clear(); auto mi2 = mapCryptedMalleableKeys.begin(); for(; mi2 != mapCryptedMalleableKeys.end(); ++mi2) { const auto vchPubKeyH = mi2->first.GetMalleablePubKey().GetH(); CSecret vchSecretH; if(!DecryptSecret(vMasterKeyIn, mi2->second, vchPubKeyH.GetHash(), vchSecretH)) return false; if (vchSecretH.size() != 32) return false; if (!CBasicKeyStore::AddMalleableKey(mi2->first, vchSecretH)) return false; } mapCryptedMalleableKeys.clear(); } return true; }