crypter.cpp raw

   1  // Copyright (c) 2009-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <wallet/crypter.h>
   6  
   7  #include <common/system.h>
   8  #include <crypto/aes.h>
   9  #include <crypto/sha512.h>
  10  
  11  #include <type_traits>
  12  #include <vector>
  13  
  14  namespace wallet {
  15  int CCrypter::BytesToKeySHA512AES(const std::span<const unsigned char> salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const
  16  {
  17      // This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc
  18      // cipher and sha512 message digest. Because sha512's output size (64b) is
  19      // greater than the aes256 block size (16b) + aes256 key size (32b),
  20      // there's no need to process more than once (D_0).
  21  
  22      if(!count || !key || !iv)
  23          return 0;
  24  
  25      unsigned char buf[CSHA512::OUTPUT_SIZE];
  26      CSHA512 di;
  27  
  28      di.Write(UCharCast(key_data.data()), key_data.size());
  29      di.Write(salt.data(), salt.size());
  30      di.Finalize(buf);
  31  
  32      for(int i = 0; i != count - 1; i++)
  33          di.Reset().Write(buf, sizeof(buf)).Finalize(buf);
  34  
  35      memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE);
  36      memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE);
  37      memory_cleanse(buf, sizeof(buf));
  38      return WALLET_CRYPTO_KEY_SIZE;
  39  }
  40  
  41  bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::span<const unsigned char> salt, const unsigned int rounds, const unsigned int derivation_method)
  42  {
  43      if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE) {
  44          return false;
  45      }
  46  
  47      int i = 0;
  48      if (derivation_method == 0) {
  49          i = BytesToKeySHA512AES(salt, key_data, rounds, vchKey.data(), vchIV.data());
  50      }
  51  
  52      if (i != (int)WALLET_CRYPTO_KEY_SIZE)
  53      {
  54          memory_cleanse(vchKey.data(), vchKey.size());
  55          memory_cleanse(vchIV.data(), vchIV.size());
  56          return false;
  57      }
  58  
  59      fKeySet = true;
  60      return true;
  61  }
  62  
  63  bool CCrypter::SetKey(const CKeyingMaterial& new_key, const std::span<const unsigned char> new_iv)
  64  {
  65      if (new_key.size() != WALLET_CRYPTO_KEY_SIZE || new_iv.size() != WALLET_CRYPTO_IV_SIZE) {
  66          return false;
  67      }
  68  
  69      memcpy(vchKey.data(), new_key.data(), new_key.size());
  70      memcpy(vchIV.data(), new_iv.data(), new_iv.size());
  71  
  72      fKeySet = true;
  73      return true;
  74  }
  75  
  76  bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const
  77  {
  78      if (!fKeySet)
  79          return false;
  80  
  81      // max ciphertext len for a n bytes of plaintext is
  82      // n + AES_BLOCKSIZE bytes
  83      vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);
  84  
  85      AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true);
  86      size_t nLen = enc.Encrypt(vchPlaintext.data(), vchPlaintext.size(), vchCiphertext.data());
  87      if(nLen < vchPlaintext.size())
  88          return false;
  89      vchCiphertext.resize(nLen);
  90  
  91      return true;
  92  }
  93  
  94  bool CCrypter::Decrypt(const std::span<const unsigned char> ciphertext, CKeyingMaterial& plaintext) const
  95  {
  96      if (!fKeySet)
  97          return false;
  98  
  99      // plaintext will always be equal to or lesser than length of ciphertext
 100      plaintext.resize(ciphertext.size());
 101  
 102      AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true);
 103      int len = dec.Decrypt(ciphertext.data(), ciphertext.size(), plaintext.data());
 104      if (len == 0) {
 105          return false;
 106      }
 107      plaintext.resize(len);
 108      return true;
 109  }
 110  
 111  bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
 112  {
 113      CCrypter cKeyCrypter;
 114      std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
 115      memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE);
 116      if(!cKeyCrypter.SetKey(vMasterKey, chIV))
 117          return false;
 118      return cKeyCrypter.Encrypt(vchPlaintext, vchCiphertext);
 119  }
 120  
 121  bool DecryptSecret(const CKeyingMaterial& master_key, const std::span<const unsigned char> ciphertext, const uint256& iv, CKeyingMaterial& plaintext)
 122  {
 123      CCrypter key_crypter;
 124      static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t<decltype(iv)>::size());
 125      const std::span iv_prefix{iv.data(), WALLET_CRYPTO_IV_SIZE};
 126      if (!key_crypter.SetKey(master_key, iv_prefix)) {
 127          return false;
 128      }
 129      return key_crypter.Decrypt(ciphertext, plaintext);
 130  }
 131  
 132  bool DecryptKey(const CKeyingMaterial& master_key, const std::span<const unsigned char> crypted_secret, const CPubKey& pub_key, CKey& key)
 133  {
 134      CKeyingMaterial secret;
 135      if (!DecryptSecret(master_key, crypted_secret, pub_key.GetHash(), secret)) {
 136          return false;
 137      }
 138  
 139      if (secret.size() != 32) {
 140          return false;
 141      }
 142  
 143      key.Set(secret.begin(), secret.end(), pub_key.IsCompressed());
 144      return key.VerifyPubKey(pub_key);
 145  }
 146  } // namespace wallet
 147