From acd6501610817eee0bd1c8ea9c591f043affbaec Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 25 Jun 2011 14:57:32 +0200 Subject: [PATCH] Prepare codebase for Encrypted Keys. --- src/db.cpp | 18 +++++--- src/init.cpp | 1 - src/key.h | 70 ++++++++++++++++++++++++++++++- src/keystore.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++----- src/keystore.h | 89 +++++++++++++++++++++++++++++++++++++-- src/script.cpp | 2 +- src/ui.cpp | 2 +- src/wallet.cpp | 9 ++-- src/wallet.h | 5 ++- 9 files changed, 274 insertions(+), 29 deletions(-) diff --git a/src/db.cpp b/src/db.cpp index f044355a3..c479a452c 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -685,7 +685,7 @@ bool CWalletDB::LoadWallet(CWallet* pwallet) //// todo: shouldn't we catch exceptions and try to recover and continue? CRITICAL_BLOCK(pwallet->cs_mapWallet) - CRITICAL_BLOCK(pwallet->cs_mapKeys) + CRITICAL_BLOCK(pwallet->cs_KeyStore) { // Get cursor Dbc* pcursor = GetCursor(); @@ -765,14 +765,20 @@ bool CWalletDB::LoadWallet(CWallet* pwallet) { vector vchPubKey; ssKey >> vchPubKey; - CWalletKey wkey; + CKey key; if (strType == "key") - ssValue >> wkey.vchPrivKey; + { + CPrivKey pkey; + ssValue >> pkey; + key.SetPrivKey(pkey); + } else + { + CWalletKey wkey; ssValue >> wkey; - - pwallet->mapKeys[vchPubKey] = wkey.vchPrivKey; - mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + key.SetPrivKey(wkey.vchPrivKey); + } + pwallet->LoadKey(key); } else if (strType == "defaultkey") { diff --git a/src/init.cpp b/src/init.cpp index 635799ccf..21b40d519 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -416,7 +416,6 @@ bool AppInit2(int argc, char* argv[]) //// debug print printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); printf("nBestHeight = %d\n", nBestHeight); - printf("mapKeys.size() = %d\n", pwalletMain->mapKeys.size()); printf("setKeyPool.size() = %d\n", pwalletMain->setKeyPool.size()); printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); printf("mapWallet.size() = %d\n", pwalletMain->mapWallet.size()); diff --git a/src/key.h b/src/key.h index c973d6eb8..c0fce18bf 100644 --- a/src/key.h +++ b/src/key.h @@ -31,6 +31,41 @@ // see www.keylength.com // script supports up to 75 for single byte push +int static inline EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) +{ + int ok = 0; + BN_CTX *ctx = NULL; + EC_POINT *pub_key = NULL; + + if (!eckey) return 0; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + pub_key = EC_POINT_new(group); + + if (pub_key == NULL) + goto err; + + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + goto err; + + EC_KEY_set_private_key(eckey,priv_key); + EC_KEY_set_public_key(eckey,pub_key); + + ok = 1; + +err: + + if (pub_key) + EC_POINT_free(pub_key); + if (ctx != NULL) + BN_CTX_free(ctx); + + return(ok); +} class key_error : public std::runtime_error @@ -42,8 +77,7 @@ public: // secure_allocator is defined in serialize.h typedef std::vector > CPrivKey; - - +typedef std::vector > CSecret; class CKey { @@ -102,6 +136,38 @@ public: return true; } + bool SetSecret(const CSecret& vchSecret) + { + EC_KEY_free(pkey); + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (pkey == NULL) + throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed"); + if (vchSecret.size() != 32) + throw key_error("CKey::SetSecret() : secret must be 32 bytes"); + BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); + if (bn == NULL) + throw key_error("CKey::SetSecret() : BN_bin2bn failed"); + if (!EC_KEY_regenerate_key(pkey,bn)) + throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); + BN_clear_free(bn); + fSet = true; + return true; + } + + CSecret GetSecret() const + { + CSecret vchRet; + vchRet.resize(32); + const BIGNUM *bn = EC_KEY_get0_private_key(pkey); + int nBytes = BN_num_bytes(bn); + if (bn == NULL) + throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed"); + int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); + if (n != nBytes) + throw key_error("CKey::GetSecret(): BN_bn2bin failed"); + return vchRet; + } + CPrivKey GetPrivKey() const { unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); diff --git a/src/keystore.cpp b/src/keystore.cpp index 7dd045fe5..765144a9b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -5,29 +5,116 @@ #include "headers.h" #include "db.h" - - -////////////////////////////////////////////////////////////////////////////// -// -// mapKeys -// - std::vector CKeyStore::GenerateNewKey() { RandAddSeedPerfmon(); CKey key; key.MakeNewKey(); if (!AddKey(key)) - throw std::runtime_error("GenerateNewKey() : AddKey failed"); + throw std::runtime_error("CKeyStore::GenerateNewKey() : AddKey failed"); return key.GetPubKey(); } -bool CKeyStore::AddKey(const CKey& key) +bool CBasicKeyStore::AddKey(const CKey& key) { - CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(cs_KeyStore) { mapKeys[key.GetPubKey()] = key.GetPrivKey(); mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); } + return true; +} + +bool CCryptoKeyStore::Unlock(const CMasterKey& vMasterKeyIn) +{ + if (!SetCrypted()) + return false; + + std::map, std::vector >::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const std::vector &vchPubKey = (*mi).first; + const std::vector &vchCryptedSecret = (*mi).second; + CSecret vchSecret; + // decrypt vchCryptedSecret using vMasterKeyIn, into vchSecret + CKey key; + key.SetSecret(vchSecret); + if (key.GetPubKey() == vchPubKey) + break; + return false; + } + vMasterKey = vMasterKeyIn; + return true; +} + +bool CCryptoKeyStore::AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_KeyStore) + { + if (!IsCrypted()) + return CBasicKeyStore::AddKey(key); + + if (IsLocked()) + return false; + + CSecret vchSecret = key.GetSecret(); + + std::vector vchCryptedSecret; + // encrypt vchSecret using vMasterKey, into vchCryptedSecret + + AddCryptedKey(key.GetPubKey(), vchCryptedSecret); + } + return true; +} + + +bool CCryptoKeyStore::AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret) +{ + CRITICAL_BLOCK(cs_KeyStore) + { + if (!SetCrypted()) + return false; + + mapCryptedKeys[vchPubKey] = vchCryptedSecret; + mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + } + return true; +} + +bool CCryptoKeyStore::GetPrivKey(const std::vector &vchPubKey, CPrivKey& keyOut) const +{ + if (!IsCrypted()) + return CBasicKeyStore::GetPrivKey(vchPubKey, keyOut); + + std::map, std::vector >::const_iterator mi = mapCryptedKeys.find(vchPubKey); + if (mi != mapCryptedKeys.end()) + { + const std::vector &vchCryptedSecret = (*mi).second; + CSecret vchSecret; + // decrypt vchCryptedSecret using vMasterKey into vchSecret; + CKey key; + key.SetSecret(vchSecret); + keyOut = key.GetPrivKey(); + return true; + } + return false; } +bool CCryptoKeyStore::GenerateMasterKey() +{ + if (!mapCryptedKeys.empty()) + return false; + + RandAddSeedPerfmon(); + + vMasterKey.resize(32); + RAND_bytes(&vMasterKey[0], 32); + + if (!IsCrypted()) + { + // upgrade wallet + fUseCrypto = true; + } + + return true; +} diff --git a/src/keystore.h b/src/keystore.h index 6080d7d7f..409553549 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -4,12 +4,26 @@ #ifndef BITCOIN_KEYSTORE_H #define BITCOIN_KEYSTORE_H +typedef std::vector > CMasterKey; + class CKeyStore { public: + mutable CCriticalSection cs_KeyStore; + + virtual bool AddKey(const CKey& key) =0; + virtual bool HaveKey(const std::vector &vchPubKey) const =0; + virtual bool GetPrivKey(const std::vector &vchPubKey, CPrivKey& keyOut) const =0; + virtual std::vector GenerateNewKey(); +}; + +class CBasicKeyStore : public CKeyStore +{ +protected: std::map, CPrivKey> mapKeys; - mutable CCriticalSection cs_mapKeys; - virtual bool AddKey(const CKey& key); + +public: + bool AddKey(const CKey& key); bool HaveKey(const std::vector &vchPubKey) const { return (mapKeys.count(vchPubKey) > 0); @@ -24,7 +38,76 @@ public: } return false; } - std::vector GenerateNewKey(); +}; + +class CCryptoKeyStore : public CBasicKeyStore +{ +private: + std::map, std::vector > mapCryptedKeys; + + CMasterKey vMasterKey; + + // if fUseCrypto is true, mapKeys must be empty + // if fUseCrypto is false, vMasterKey must be empty + bool fUseCrypto; + +protected: + bool IsCrypted() const + { + return fUseCrypto; + } + + bool SetCrypted() + { + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + } + + // will encrypt previously unencrypted keys + bool GenerateMasterKey(); + + bool GetMasterKey(CMasterKey &vMasterKeyOut) const + { + if (!IsCrypted()) + return false; + if (IsLocked()) + return false; + vMasterKeyOut = vMasterKey; + return true; + } + bool Unlock(const CMasterKey& vMasterKeyIn); + +public: + CCryptoKeyStore() : fUseCrypto(false) + { + } + + bool IsLocked() const + { + if (!IsCrypted()) + return false; + return vMasterKey.empty(); + } + + bool Lock() + { + if (!SetCrypted()) + return false; + vMasterKey.clear(); + } + + virtual bool AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret); + bool AddKey(const CKey& key); + bool HaveKey(const std::vector &vchPubKey) const + { + if (!IsCrypted()) + return CBasicKeyStore::HaveKey(vchPubKey); + return mapCryptedKeys.count(vchPubKey) > 0; + } + bool GetPrivKey(const std::vector &vchPubKey, CPrivKey& keyOut) const; }; #endif diff --git a/src/script.cpp b/src/script.cpp index bd1b5b3c5..c17525034 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1030,7 +1030,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash return false; // Compile solution - CRITICAL_BLOCK(keystore.cs_mapKeys) + CRITICAL_BLOCK(keystore.cs_KeyStore) { BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) { diff --git a/src/ui.cpp b/src/ui.cpp index 9b84fb9e6..db02cb583 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -2382,7 +2382,7 @@ CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInit m_listCtrlReceiving->SetFocus(); // Fill listctrl with address book data - CRITICAL_BLOCK(pwalletMain->cs_mapKeys) + CRITICAL_BLOCK(pwalletMain->cs_KeyStore) CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue(); diff --git a/src/wallet.cpp b/src/wallet.cpp index 6ef75ef27..a179876dc 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -17,7 +17,8 @@ using namespace std; bool CWallet::AddKey(const CKey& key) { - this->CKeyStore::AddKey(key); + if (!CBasicKeyStore::AddKey(key)) + return false; if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); @@ -783,7 +784,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Reserve a new key pair from key pool vector vchPubKey = reservekey.GetReservedKey(); - assert(mapKeys.count(vchPubKey)); + // assert(mapKeys.count(vchPubKey)); // Fill a vout to ourself, using same address type as the payment CScript scriptChange; @@ -957,7 +958,7 @@ bool CWallet::LoadWallet(bool& fFirstRunRet) if (!mapKeys.count(vchDefaultKey)) { - // Create new default key + // Create new keyUser and set as default key RandAddSeedPerfmon(); SetDefaultKey(GetKeyFromKeyPool()); @@ -1062,7 +1063,7 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) setKeyPool.erase(setKeyPool.begin()); if (!walletdb.ReadPool(nIndex, keypool)) throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!mapKeys.count(keypool.vchPubKey)) + if (!HaveKey(keypool.vchPubKey)) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(!keypool.vchPubKey.empty()); printf("keypool reserve %"PRI64d"\n", nIndex); diff --git a/src/wallet.h b/src/wallet.h index 7d9db9726..8fb29a489 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -12,7 +12,7 @@ class CWalletTx; class CReserveKey; class CWalletDB; -class CWallet : public CKeyStore +class CWallet : public CCryptoKeyStore { private: bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set >& setCoinsRet, int64& nValueRet) const; @@ -48,7 +48,10 @@ public: std::vector vchDefaultKey; + // keystore implementation bool AddKey(const CKey& key); + bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } + bool AddToWallet(const CWalletTx& wtxIn); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false); bool EraseFromWallet(uint256 hash);