Prepare codebase for Encrypted Keys.

This commit is contained in:
Pieter Wuille 2011-06-25 14:57:32 +02:00 committed by Matt Corallo
parent e94010b239
commit acd6501610
9 changed files with 274 additions and 29 deletions

View 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")
{

View 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());

View file

@ -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<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);

View file

@ -5,29 +5,116 @@
#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;
}

View 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

View 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)
{

View 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();

View 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);

View 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);