text: // Copyright (c) 2016-2018 Duality Blockchain Solutions Developers
  // Copyright (c) 2014-2018 The Dash Core Developers
  // Copyright (c) 2009-2018 The Bitcoin Developers
  // Copyright (c) 2009-2018 Satoshi Nakamoto
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #ifndef DYNAMIC_WALLET_CRYPTER_H
  #define DYNAMIC_WALLET_CRYPTER_H
  
  #include "keystore.h"
  #include "support/allocators/secure.h"
  #include "serialize.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, 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:
      std::vector<unsigned char, secure_allocator<unsigned char>> vchKey;
      std::vector<unsigned char, secure_allocator<unsigned char>> vchIV;
      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) 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 // DYNAMIC_WALLET_CRYPTER_H
  