wallet: move crypter to wallet
This commit is contained in:
292
src/wallet/crypter.cpp
Normal file
292
src/wallet/crypter.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
// Copyright (c) 2009-2013 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "crypter.h"
|
||||
|
||||
#include "script/script.h"
|
||||
#include "script/standard.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
|
||||
{
|
||||
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
|
||||
return false;
|
||||
|
||||
int i = 0;
|
||||
if (nDerivationMethod == 0)
|
||||
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
|
||||
(unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
|
||||
|
||||
if (i != (int)WALLET_CRYPTO_KEY_SIZE)
|
||||
{
|
||||
memory_cleanse(chKey, sizeof(chKey));
|
||||
memory_cleanse(chIV, sizeof(chIV));
|
||||
return false;
|
||||
}
|
||||
|
||||
fKeySet = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
|
||||
{
|
||||
if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE)
|
||||
return false;
|
||||
|
||||
memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
|
||||
memcpy(&chIV[0], &chNewIV[0], sizeof chIV);
|
||||
|
||||
fKeySet = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
|
||||
{
|
||||
if (!fKeySet)
|
||||
return false;
|
||||
|
||||
// max ciphertext len for a n bytes of plaintext is
|
||||
// n + AES_BLOCK_SIZE - 1 bytes
|
||||
int nLen = vchPlaintext.size();
|
||||
int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
|
||||
vchCiphertext = std::vector<unsigned char> (nCLen);
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
|
||||
bool fOk = true;
|
||||
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
|
||||
if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
|
||||
if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
|
||||
if (!fOk) return false;
|
||||
|
||||
vchCiphertext.resize(nCLen + nFLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
|
||||
{
|
||||
if (!fKeySet)
|
||||
return false;
|
||||
|
||||
// plaintext will always be equal to or lesser than length of ciphertext
|
||||
int nLen = vchCiphertext.size();
|
||||
int nPLen = nLen, nFLen = 0;
|
||||
|
||||
vchPlaintext = CKeyingMaterial(nPLen);
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
|
||||
bool fOk = true;
|
||||
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
|
||||
if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
|
||||
if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
|
||||
if (!fOk) return false;
|
||||
|
||||
vchPlaintext.resize(nPLen + nFLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
|
||||
{
|
||||
CCrypter cKeyCrypter;
|
||||
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
|
||||
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
|
||||
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
|
||||
return false;
|
||||
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
|
||||
}
|
||||
|
||||
static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
|
||||
{
|
||||
CCrypter cKeyCrypter;
|
||||
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
|
||||
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
|
||||
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
|
||||
return false;
|
||||
return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
|
||||
}
|
||||
|
||||
static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
|
||||
{
|
||||
CKeyingMaterial vchSecret;
|
||||
if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
|
||||
return false;
|
||||
|
||||
if (vchSecret.size() != 32)
|
||||
return false;
|
||||
|
||||
key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed());
|
||||
return key.VerifyPubKey(vchPubKey);
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::SetCrypted()
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (fUseCrypto)
|
||||
return true;
|
||||
if (!mapKeys.empty())
|
||||
return false;
|
||||
fUseCrypto = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::Lock()
|
||||
{
|
||||
if (!SetCrypted())
|
||||
return false;
|
||||
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
vMasterKey.clear();
|
||||
}
|
||||
|
||||
NotifyStatusChanged(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!SetCrypted())
|
||||
return false;
|
||||
|
||||
bool keyPass = false;
|
||||
bool keyFail = false;
|
||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
|
||||
for (; mi != mapCryptedKeys.end(); ++mi)
|
||||
{
|
||||
const CPubKey &vchPubKey = (*mi).second.first;
|
||||
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
|
||||
CKey key;
|
||||
if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
|
||||
{
|
||||
keyFail = true;
|
||||
break;
|
||||
}
|
||||
keyPass = true;
|
||||
if (fDecryptionThoroughlyChecked)
|
||||
break;
|
||||
}
|
||||
if (keyPass && keyFail)
|
||||
{
|
||||
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.");
|
||||
assert(false);
|
||||
}
|
||||
if (keyFail || !keyPass)
|
||||
return false;
|
||||
vMasterKey = vMasterKeyIn;
|
||||
fDecryptionThoroughlyChecked = true;
|
||||
}
|
||||
NotifyStatusChanged(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!IsCrypted())
|
||||
return CBasicKeyStore::AddKeyPubKey(key, pubkey);
|
||||
|
||||
if (IsLocked())
|
||||
return false;
|
||||
|
||||
std::vector<unsigned char> vchCryptedSecret;
|
||||
CKeyingMaterial vchSecret(key.begin(), key.end());
|
||||
if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret))
|
||||
return false;
|
||||
|
||||
if (!AddCryptedKey(pubkey, vchCryptedSecret))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!SetCrypted())
|
||||
return false;
|
||||
|
||||
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!IsCrypted())
|
||||
return CBasicKeyStore::GetKey(address, keyOut);
|
||||
|
||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
|
||||
if (mi != mapCryptedKeys.end())
|
||||
{
|
||||
const CPubKey &vchPubKey = (*mi).second.first;
|
||||
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
|
||||
return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!IsCrypted())
|
||||
return CKeyStore::GetPubKey(address, vchPubKeyOut);
|
||||
|
||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
|
||||
if (mi != mapCryptedKeys.end())
|
||||
{
|
||||
vchPubKeyOut = (*mi).second.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!mapCryptedKeys.empty() || IsCrypted())
|
||||
return false;
|
||||
|
||||
fUseCrypto = true;
|
||||
BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
|
||||
{
|
||||
const CKey &key = mKey.second;
|
||||
CPubKey vchPubKey = key.GetPubKey();
|
||||
CKeyingMaterial vchSecret(key.begin(), key.end());
|
||||
std::vector<unsigned char> vchCryptedSecret;
|
||||
if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
|
||||
return false;
|
||||
if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
|
||||
return false;
|
||||
}
|
||||
mapKeys.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
196
src/wallet/crypter.h
Normal file
196
src/wallet/crypter.h
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_CRYPTER_H
|
||||
#define BITCOIN_CRYPTER_H
|
||||
|
||||
#include "keystore.h"
|
||||
#include "serialize.h"
|
||||
#include "support/allocators/secure.h"
|
||||
|
||||
class uint256;
|
||||
|
||||
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
|
||||
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Private key encryption is done based on a CMasterKey,
|
||||
* which holds a salt and random encryption key.
|
||||
*
|
||||
* CMasterKeys are encrypted using AES-256-CBC using a key
|
||||
* derived using derivation method nDerivationMethod
|
||||
* (0 == EVP_sha512()) and derivation iterations nDeriveIterations.
|
||||
* vchOtherDerivationParameters is provided for alternative algorithms
|
||||
* which may require more parameters (such as scrypt).
|
||||
*
|
||||
* Wallet Private Keys are then encrypted using AES-256-CBC
|
||||
* with the double-sha256 of the public key as the IV, and the
|
||||
* master key's key as the encryption key (see keystore.[ch]).
|
||||
*/
|
||||
|
||||
/** Master key for wallet encryption */
|
||||
class CMasterKey
|
||||
{
|
||||
public:
|
||||
std::vector<unsigned char> vchCryptedKey;
|
||||
std::vector<unsigned char> vchSalt;
|
||||
//! 0 = EVP_sha512()
|
||||
//! 1 = scrypt()
|
||||
unsigned int nDerivationMethod;
|
||||
unsigned int nDeriveIterations;
|
||||
//! Use this for more parameters to key derivation,
|
||||
//! such as the various parameters to scrypt
|
||||
std::vector<unsigned char> vchOtherDerivationParameters;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(vchCryptedKey);
|
||||
READWRITE(vchSalt);
|
||||
READWRITE(nDerivationMethod);
|
||||
READWRITE(nDeriveIterations);
|
||||
READWRITE(vchOtherDerivationParameters);
|
||||
}
|
||||
|
||||
CMasterKey()
|
||||
{
|
||||
// 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M
|
||||
// ie slightly lower than the lowest hardware we need bother supporting
|
||||
nDeriveIterations = 25000;
|
||||
nDerivationMethod = 0;
|
||||
vchOtherDerivationParameters = std::vector<unsigned char>(0);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
|
||||
|
||||
/** Encryption/decryption context with key information */
|
||||
class CCrypter
|
||||
{
|
||||
private:
|
||||
unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
|
||||
unsigned char chIV[WALLET_CRYPTO_KEY_SIZE];
|
||||
bool fKeySet;
|
||||
|
||||
public:
|
||||
bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod);
|
||||
bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext);
|
||||
bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext);
|
||||
bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);
|
||||
|
||||
void CleanKey()
|
||||
{
|
||||
memory_cleanse(chKey, sizeof(chKey));
|
||||
memory_cleanse(chIV, sizeof(chIV));
|
||||
fKeySet = false;
|
||||
}
|
||||
|
||||
CCrypter()
|
||||
{
|
||||
fKeySet = false;
|
||||
|
||||
// Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap)
|
||||
// Note that this does nothing about suspend-to-disk (which will put all our key data on disk)
|
||||
// Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process.
|
||||
LockedPageManager::Instance().LockRange(&chKey[0], sizeof chKey);
|
||||
LockedPageManager::Instance().LockRange(&chIV[0], sizeof chIV);
|
||||
}
|
||||
|
||||
~CCrypter()
|
||||
{
|
||||
CleanKey();
|
||||
|
||||
LockedPageManager::Instance().UnlockRange(&chKey[0], sizeof chKey);
|
||||
LockedPageManager::Instance().UnlockRange(&chIV[0], sizeof chIV);
|
||||
}
|
||||
};
|
||||
|
||||
/** Keystore which keeps the private keys encrypted.
|
||||
* It derives from the basic key store, which is used if no encryption is active.
|
||||
*/
|
||||
class CCryptoKeyStore : public CBasicKeyStore
|
||||
{
|
||||
private:
|
||||
CryptedKeyMap mapCryptedKeys;
|
||||
|
||||
CKeyingMaterial vMasterKey;
|
||||
|
||||
//! if fUseCrypto is true, mapKeys must be empty
|
||||
//! if fUseCrypto is false, vMasterKey must be empty
|
||||
bool fUseCrypto;
|
||||
|
||||
//! keeps track of whether Unlock has run a thorough check before
|
||||
bool fDecryptionThoroughlyChecked;
|
||||
|
||||
protected:
|
||||
bool SetCrypted();
|
||||
|
||||
//! will encrypt previously unencrypted keys
|
||||
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
|
||||
|
||||
bool Unlock(const CKeyingMaterial& vMasterKeyIn);
|
||||
|
||||
public:
|
||||
CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsCrypted() const
|
||||
{
|
||||
return fUseCrypto;
|
||||
}
|
||||
|
||||
bool IsLocked() const
|
||||
{
|
||||
if (!IsCrypted())
|
||||
return false;
|
||||
bool result;
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
result = vMasterKey.empty();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Lock();
|
||||
|
||||
virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
||||
bool HaveKey(const CKeyID &address) const
|
||||
{
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
if (!IsCrypted())
|
||||
return CBasicKeyStore::HaveKey(address);
|
||||
return mapCryptedKeys.count(address) > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool GetKey(const CKeyID &address, CKey& keyOut) const;
|
||||
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
|
||||
void GetKeys(std::set<CKeyID> &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++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wallet status (encrypted, locked) changed.
|
||||
* Note: Called without locks held.
|
||||
*/
|
||||
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_CRYPTER_H
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "amount.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "crypter.h"
|
||||
#include "key.h"
|
||||
#include "keystore.h"
|
||||
#include "main.h"
|
||||
#include "ui_interface.h"
|
||||
#include "wallet/crypter.h"
|
||||
#include "wallet/wallet_ismine.h"
|
||||
#include "wallet/walletdb.h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user