Prepare codebase for Encrypted Keys.
authorPieter Wuille <pieter.wuille@gmail.com>
Sat, 25 Jun 2011 12:57:32 +0000 (14:57 +0200)
committerMatt Corallo <matt@bluematt.me>
Fri, 8 Jul 2011 13:46:47 +0000 (15:46 +0200)
src/db.cpp
src/init.cpp
src/key.h
src/keystore.cpp
src/keystore.h
src/script.cpp
src/ui.cpp
src/wallet.cpp
src/wallet.h

index f044355..c479a45 100644 (file)
@@ -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<unsigned char> 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")
             {
index 635799c..21b40d5 100644 (file)
@@ -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());
index c973d6e..c0fce18 100644 (file)
--- a/src/key.h
+++ b/src/key.h
 // 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<unsigned char, secure_allocator<unsigned char> > CPrivKey;
-
-
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > 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);
index 7dd045f..765144a 100644 (file)
 #include "headers.h"
 #include "db.h"
 
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapKeys
-//
-
 std::vector<unsigned char> 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<unsigned char>, std::vector<unsigned char> >::const_iterator mi = mapCryptedKeys.begin();
+    for (; mi != mapCryptedKeys.end(); ++mi)
+    {
+        const std::vector<unsigned char> &vchPubKey = (*mi).first;
+        const std::vector<unsigned char> &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<unsigned char> vchCryptedSecret;
+        // encrypt vchSecret using vMasterKey, into vchCryptedSecret
+
+        AddCryptedKey(key.GetPubKey(), vchCryptedSecret);
+    }
+    return true;
+}
+
+
+bool CCryptoKeyStore::AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
+{
+    CRITICAL_BLOCK(cs_KeyStore)
+    {
+        if (!SetCrypted())
+            return false;
+
+        mapCryptedKeys[vchPubKey] = vchCryptedSecret;
+        mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
+    }
+    return true;
+}
+
+bool CCryptoKeyStore::GetPrivKey(const std::vector<unsigned char> &vchPubKey, CPrivKey& keyOut) const
+{
+    if (!IsCrypted())
+        return CBasicKeyStore::GetPrivKey(vchPubKey, keyOut);
+
+    std::map<std::vector<unsigned char>, std::vector<unsigned char> >::const_iterator mi = mapCryptedKeys.find(vchPubKey);
+    if (mi != mapCryptedKeys.end())
+    {
+        const std::vector<unsigned char> &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;
+}
index 6080d7d..4095535 100644 (file)
@@ -4,12 +4,26 @@
 #ifndef BITCOIN_KEYSTORE_H
 #define BITCOIN_KEYSTORE_H
 
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CMasterKey;
+
 class CKeyStore
 {
 public:
+    mutable CCriticalSection cs_KeyStore;
+
+    virtual bool AddKey(const CKey& key) =0;
+    virtual bool HaveKey(const std::vector<unsigned char> &vchPubKey) const =0;
+    virtual bool GetPrivKey(const std::vector<unsigned char> &vchPubKey, CPrivKey& keyOut) const =0;
+    virtual std::vector<unsigned char> GenerateNewKey();
+};
+
+class CBasicKeyStore : public CKeyStore
+{
+protected:
     std::map<std::vector<unsigned char>, CPrivKey> mapKeys;
-    mutable CCriticalSection cs_mapKeys;
-    virtual bool AddKey(const CKey& key);
+
+public:
+    bool AddKey(const CKey& key);
     bool HaveKey(const std::vector<unsigned char> &vchPubKey) const
     {
         return (mapKeys.count(vchPubKey) > 0);
@@ -24,7 +38,76 @@ public:
         }
         return false;
     }
-    std::vector<unsigned char> GenerateNewKey();
+};
+
+class CCryptoKeyStore : public CBasicKeyStore
+{
+private:
+    std::map<std::vector<unsigned char>, std::vector<unsigned char> > 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<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+    bool AddKey(const CKey& key);
+    bool HaveKey(const std::vector<unsigned char> &vchPubKey) const
+    {
+        if (!IsCrypted())
+            return CBasicKeyStore::HaveKey(vchPubKey);
+        return mapCryptedKeys.count(vchPubKey) > 0;
+    }
+    bool GetPrivKey(const std::vector<unsigned char> &vchPubKey, CPrivKey& keyOut) const;
 };
 
 #endif
index bd1b5b3..c175250 100644 (file)
@@ -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)
         {
index 9b84fb9..db02cb5 100644 (file)
@@ -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();
index 6ef75ef..a179876 100644 (file)
@@ -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<pair<CScript, int64> >& vecSend, CW
 
                     // Reserve a new key pair from key pool
                     vector<unsigned char> 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);
index 7d9db97..8fb29a4 100644 (file)
@@ -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<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
@@ -48,7 +48,10 @@ public:
 
     std::vector<unsigned char> 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);