neobytes/src/wallet/crypter.h
Wladimir J. van der Laan 19a2d668cf Merge #9039: Various serialization simplifcations and optimizations
d59a518 Use fixed preallocation instead of costly GetSerializeSize (Pieter Wuille)
25a211a Add optimized CSizeComputer serializers (Pieter Wuille)
a2929a2 Make CSerAction's ForRead() constexpr (Pieter Wuille)
a603925 Avoid -Wshadow errors (Pieter Wuille)
5284721 Get rid of nType and nVersion (Pieter Wuille)
657e05a Make GetSerializeSize a wrapper on top of CSizeComputer (Pieter Wuille)
fad9b66 Make nType and nVersion private and sometimes const (Pieter Wuille)
c2c5d42 Make streams' read and write return void (Pieter Wuille)
50e8a9c Remove unused ReadVersion and WriteVersion (Pieter Wuille)
2018-01-15 06:14:15 +01:00

231 lines
7.4 KiB
C++

// Copyright (c) 2009-2015 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_WALLET_CRYPTER_H
#define BITCOIN_WALLET_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;
const unsigned int WALLET_CRYPTO_IV_SIZE = 16;
/**
* 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) {
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;
namespace wallet_crypto
{
class TestCrypter;
}
/** Encryption/decryption context with key information */
class CCrypter
{
friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV
private:
std::vector<unsigned char, secure_allocator<unsigned char>> vchKey;
std::vector<unsigned char, secure_allocator<unsigned char>> vchIV;
bool fKeySet;
int BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const;
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) const;
bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const;
bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);
void CleanKey()
{
memory_cleanse(vchKey.data(), vchKey.size());
memory_cleanse(vchIV.data(), vchIV.size());
fKeySet = false;
}
CCrypter()
{
fKeySet = false;
vchKey.resize(WALLET_CRYPTO_KEY_SIZE);
vchIV.resize(WALLET_CRYPTO_IV_SIZE);
}
~CCrypter()
{
CleanKey();
}
};
bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, const std::string& sIV, std::string& sCiphertext);
bool DecryptAES256(const SecureString& sKey, const std::string& sCiphertext, const std::string& sIV, SecureString& sPlaintext);
/** 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;
CHDChain cryptedHDChain;
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;
//! if fOnlyMixingAllowed is true, only mixing should be allowed in unlocked wallet
bool fOnlyMixingAllowed;
protected:
bool SetCrypted();
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool EncryptHDChain(const CKeyingMaterial& vMasterKeyIn);
bool DecryptHDChain(CHDChain& hdChainRet) const;
bool SetHDChain(const CHDChain& chain);
bool SetCryptedHDChain(const CHDChain& chain);
bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly = false);
public:
CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false), fOnlyMixingAllowed(false)
{
}
bool IsCrypted() const
{
return fUseCrypto;
}
// This function should be used in a different combinations to determine
// if CCryptoKeyStore is fully locked so that no operations requiring access
// to private keys are possible:
// IsLocked(true)
// or if CCryptoKeyStore's private keys are available for mixing only:
// !IsLocked(true) && IsLocked()
// or if they are available for everything:
// !IsLocked()
bool IsLocked(bool fForMixing = false) const
{
if (!IsCrypted())
return false;
bool result;
{
LOCK(cs_KeyStore);
result = vMasterKey.empty();
}
// fForMixing fOnlyMixingAllowed return
// ---------------------------------------
// true true result
// true false result
// false true true
// false false result
if(!fForMixing && fOnlyMixingAllowed) return true;
return result;
}
bool Lock(bool fAllowMixing = false);
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++;
}
}
bool GetHDChain(CHDChain& hdChainRet) const;
/**
* Wallet status (encrypted, locked) changed.
* Note: Called without locks held.
*/
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
};
#endif // BITCOIN_WALLET_CRYPTER_H