// Copyright (c) 2009-2018 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> #include <atomic> 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_tests { class TestCrypter; } /** Encryption/decryption context with key information */ class CCrypter { friend class wallet_crypto_tests::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(); } }; /** 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: CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore); //! if fUseCrypto is true, mapKeys must be empty //! if fUseCrypto is false, vMasterKey must be empty std::atomic<bool> fUseCrypto; //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; protected: using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>; bool SetCrypted(); //! will encrypt previously unencrypted keys bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false); CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); public: CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false) { } bool IsCrypted() const { return fUseCrypto; } bool IsLocked() const; bool Lock(); virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; bool HaveKey(const CKeyID &address) const override; bool GetKey(const CKeyID &address, CKey& keyOut) const override; bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; std::set<CKeyID> GetKeys() const override; /** * Wallet status (encrypted, locked) changed. * Note: Called without locks held. */ boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; }; #endif // BITCOIN_WALLET_CRYPTER_H