crypter.cpp raw

   1  // Copyright (c) 2009-2021 The Limenka 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/hmac_sha256.h>
  10  #include <crypto/hmac_sha512.h>
  11  #include <crypto/sha256.h>
  12  #include <crypto/sha512.h>
  13  #include <random.h>
  14  #include <support/allocators/secure.h>
  15  
  16  #include <type_traits>
  17  #include <vector>
  18  
  19  namespace wallet {
  20  int CCrypter::BytesToKeySHA512AES(const std::span<const unsigned char> salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv) const
  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  int CCrypter::PBKDF2_SHA512_AES(const std::span<const unsigned char> salt, const SecureString& key_data, int count, unsigned char* key, unsigned char* iv, unsigned char* hmac_key) const
  42  {
  43      if (!count || !key || !iv || !hmac_key) return 0;
  44  
  45      static constexpr unsigned int DK_LEN = WALLET_CRYPTO_KEY_SIZE + WALLET_CRYPTO_IV_SIZE + WALLET_CRYPTO_HMAC_SIZE;
  46      unsigned char dk[DK_LEN];
  47      const unsigned char* pass_bytes = UCharCast(key_data.data());
  48      const size_t pass_len = key_data.size();
  49      const size_t salt_len = salt.size();
  50  
  51      unsigned int blocks = (DK_LEN + CSHA512::OUTPUT_SIZE - 1) / CSHA512::OUTPUT_SIZE;
  52      for (unsigned int block = 1; block <= blocks; block++) {
  53          unsigned char u[CSHA512::OUTPUT_SIZE];
  54          unsigned char t[CSHA512::OUTPUT_SIZE];
  55          memset(t, 0, sizeof(t));
  56  
  57          // U_1 = PRF(Password, Salt || INT_32_BE(i))
  58          CHMAC_SHA512 hmac_u1(pass_bytes, pass_len);
  59          hmac_u1.Write(salt.data(), salt_len);
  60          unsigned char ibuf[4] = {
  61              static_cast<unsigned char>((block >> 24) & 0xff),
  62              static_cast<unsigned char>((block >> 16) & 0xff),
  63              static_cast<unsigned char>((block >> 8) & 0xff),
  64              static_cast<unsigned char>(block & 0xff)
  65          };
  66          hmac_u1.Write(ibuf, 4);
  67          hmac_u1.Finalize(u);
  68          for (unsigned int j = 0; j < CSHA512::OUTPUT_SIZE; j++) t[j] = u[j];
  69  
  70          for (int iter = 1; iter < count; iter++) {
  71              CHMAC_SHA512 hmac_iter(pass_bytes, pass_len);
  72              hmac_iter.Write(u, CSHA512::OUTPUT_SIZE);
  73              hmac_iter.Finalize(u);
  74              for (unsigned int j = 0; j < CSHA512::OUTPUT_SIZE; j++) t[j] ^= u[j];
  75          }
  76  
  77          unsigned int out_offset = (block - 1) * CSHA512::OUTPUT_SIZE;
  78          unsigned int copy_len = std::min<unsigned int>(CSHA512::OUTPUT_SIZE, DK_LEN - out_offset);
  79          memcpy(dk + out_offset, t, copy_len);
  80          memory_cleanse(t, sizeof(t));
  81          memory_cleanse(u, sizeof(u));
  82      }
  83  
  84      memcpy(key, dk, WALLET_CRYPTO_KEY_SIZE);
  85      memcpy(iv, dk + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE);
  86      memcpy(hmac_key, dk + WALLET_CRYPTO_KEY_SIZE + WALLET_CRYPTO_IV_SIZE, WALLET_CRYPTO_HMAC_SIZE);
  87      memory_cleanse(dk, sizeof(dk));
  88      return WALLET_CRYPTO_KEY_SIZE;
  89  }
  90  
  91  bool CCrypter::SetKeyFromPassphrase(const SecureString& key_data, const std::span<const unsigned char> salt, const unsigned int rounds, const unsigned int derivation_method)
  92  {
  93      if (derivation_method == 2) {
  94          if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE_V2) {
  95              return false;
  96          }
  97          int i = PBKDF2_SHA512_AES(salt, key_data, rounds, vchKey.data(), vchIV.data(), vchHmacKey.data());
  98          if (i != (int)WALLET_CRYPTO_KEY_SIZE) {
  99              memory_cleanse(vchKey.data(), vchKey.size());
 100              memory_cleanse(vchIV.data(), vchIV.size());
 101              memory_cleanse(vchHmacKey.data(), vchHmacKey.size());
 102              return false;
 103          }
 104          fKeySet = true;
 105          fUseAEAD = true;
 106          return true;
 107      }
 108  
 109      // Legacy method 0
 110      if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE) {
 111          return false;
 112      }
 113  
 114      int i = 0;
 115      if (derivation_method == 0) {
 116          i = BytesToKeySHA512AES(salt, key_data, rounds, vchKey.data(), vchIV.data());
 117      }
 118  
 119      if (i != (int)WALLET_CRYPTO_KEY_SIZE)
 120      {
 121          memory_cleanse(vchKey.data(), vchKey.size());
 122          memory_cleanse(vchIV.data(), vchIV.size());
 123          return false;
 124      }
 125  
 126      fKeySet = true;
 127      fUseAEAD = false;
 128      return true;
 129  }
 130  
 131  bool CCrypter::SetKey(const CKeyingMaterial& new_key, const std::span<const unsigned char> new_iv)
 132  {
 133      if (new_key.size() != WALLET_CRYPTO_KEY_SIZE || new_iv.size() != WALLET_CRYPTO_IV_SIZE) {
 134          return false;
 135      }
 136  
 137      memcpy(vchKey.data(), new_key.data(), new_key.size());
 138      memcpy(vchIV.data(), new_iv.data(), new_iv.size());
 139      fUseAEAD = false;
 140  
 141      fKeySet = true;
 142      return true;
 143  }
 144  
 145  bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const
 146  {
 147      if (!fKeySet)
 148          return false;
 149  
 150      // max ciphertext len for a n bytes of plaintext is
 151      // n + AES_BLOCKSIZE bytes
 152      std::vector<unsigned char> raw(vchPlaintext.size() + AES_BLOCKSIZE);
 153  
 154      AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true);
 155      size_t nLen = enc.Encrypt(vchPlaintext.data(), vchPlaintext.size(), raw.data());
 156      if(nLen < vchPlaintext.size())
 157          return false;
 158      raw.resize(nLen);
 159  
 160      if (fUseAEAD) {
 161          // Prepend HMAC-SHA256(ciphertext)
 162          CHMAC_SHA256 hmac(vchHmacKey.data(), vchHmacKey.size());
 163          hmac.Write(raw.data(), raw.size());
 164          unsigned char mac[CSHA256::OUTPUT_SIZE];
 165          hmac.Finalize(mac);
 166  
 167          vchCiphertext.resize(WALLET_CRYPTO_HMAC_SIZE + raw.size());
 168          memcpy(vchCiphertext.data(), mac, WALLET_CRYPTO_HMAC_SIZE);
 169          memcpy(vchCiphertext.data() + WALLET_CRYPTO_HMAC_SIZE, raw.data(), raw.size());
 170      } else {
 171          vchCiphertext = std::move(raw);
 172      }
 173  
 174      return true;
 175  }
 176  
 177  bool CCrypter::Decrypt(const std::span<const unsigned char> ciphertext, CKeyingMaterial& plaintext) const
 178  {
 179      if (!fKeySet)
 180          return false;
 181  
 182      const unsigned char* ct_data;
 183      size_t ct_len;
 184  
 185      if (fUseAEAD) {
 186          if (ciphertext.size() < WALLET_CRYPTO_HMAC_SIZE) return false;
 187  
 188          // Verify HMAC
 189          const unsigned char* stored_mac = ciphertext.data();
 190          const unsigned char* ct_part = ciphertext.data() + WALLET_CRYPTO_HMAC_SIZE;
 191          size_t ct_part_len = ciphertext.size() - WALLET_CRYPTO_HMAC_SIZE;
 192  
 193          CHMAC_SHA256 hmac(vchHmacKey.data(), vchHmacKey.size());
 194          hmac.Write(ct_part, ct_part_len);
 195          unsigned char computed_mac[CSHA256::OUTPUT_SIZE];
 196          hmac.Finalize(computed_mac);
 197  
 198          if (memcmp(stored_mac, computed_mac, WALLET_CRYPTO_HMAC_SIZE) != 0) {
 199              memory_cleanse(computed_mac, sizeof(computed_mac));
 200              return false;
 201          }
 202          memory_cleanse(computed_mac, sizeof(computed_mac));
 203  
 204          ct_data = ct_part;
 205          ct_len = ct_part_len;
 206      } else {
 207          ct_data = ciphertext.data();
 208          ct_len = ciphertext.size();
 209      }
 210  
 211      // plaintext will always be equal to or lesser than length of ciphertext
 212      plaintext.resize(ct_len);
 213  
 214      AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true);
 215      int len = dec.Decrypt(ct_data, ct_len, plaintext.data());
 216      if (len == 0) {
 217          return false;
 218      }
 219      plaintext.resize(len);
 220      return true;
 221  }
 222  
 223  bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
 224  {
 225      CCrypter cKeyCrypter;
 226      std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
 227      memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE);
 228      if(!cKeyCrypter.SetKey(vMasterKey, chIV))
 229          return false;
 230      return cKeyCrypter.Encrypt(vchPlaintext, vchCiphertext);
 231  }
 232  
 233  bool DecryptSecret(const CKeyingMaterial& master_key, const std::span<const unsigned char> ciphertext, const uint256& iv, CKeyingMaterial& plaintext)
 234  {
 235      CCrypter key_crypter;
 236      static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t<decltype(iv)>::size());
 237      const std::span iv_prefix{iv.data(), WALLET_CRYPTO_IV_SIZE};
 238      if (!key_crypter.SetKey(master_key, iv_prefix)) {
 239          return false;
 240      }
 241      return key_crypter.Decrypt(ciphertext, plaintext);
 242  }
 243  
 244  bool DecryptKey(const CKeyingMaterial& master_key, const std::span<const unsigned char> crypted_secret, const CPubKey& pub_key, CKey& key)
 245  {
 246      CKeyingMaterial secret;
 247      if (!DecryptSecret(master_key, crypted_secret, pub_key.GetHash(), secret)) {
 248          return false;
 249      }
 250  
 251      if (secret.size() != 32) {
 252          return false;
 253      }
 254  
 255      key.Set(secret.begin(), secret.end(), pub_key.IsCompressed());
 256      return key.VerifyPubKey(pub_key);
 257  }
 258  
 259  bool EncryptSecretV2(const CKeyingMaterial& master_key, const CKeyingMaterial& plaintext, std::vector<unsigned char>& ciphertext)
 260  {
 261      // Use random IV for V2 key encryption
 262      uint256 random_iv = GetRandHash();
 263      std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
 264      memcpy(chIV.data(), random_iv.begin(), WALLET_CRYPTO_IV_SIZE);
 265  
 266      CCrypter key_crypter;
 267      if (!key_crypter.SetKey(master_key, chIV)) return false;
 268      if (!key_crypter.Encrypt(plaintext, ciphertext)) return false;
 269  
 270      // Prepend IV so it can be recovered during decryption
 271      std::vector<unsigned char> result(WALLET_CRYPTO_IV_SIZE + ciphertext.size());
 272      memcpy(result.data(), chIV.data(), WALLET_CRYPTO_IV_SIZE);
 273      memcpy(result.data() + WALLET_CRYPTO_IV_SIZE, ciphertext.data(), ciphertext.size());
 274      ciphertext = std::move(result);
 275      return true;
 276  }
 277  
 278  bool DecryptSecretV2(const CKeyingMaterial& master_key, std::span<const unsigned char> ciphertext, uint256& iv_out, CKeyingMaterial& plaintext)
 279  {
 280      if (ciphertext.size() < WALLET_CRYPTO_IV_SIZE) return false;
 281  
 282      const std::span iv_prefix{ciphertext.data(), WALLET_CRYPTO_IV_SIZE};
 283      const std::span ct_part{ciphertext.data() + WALLET_CRYPTO_IV_SIZE, ciphertext.size() - WALLET_CRYPTO_IV_SIZE};
 284  
 285      memcpy(iv_out.begin(), iv_prefix.data(), WALLET_CRYPTO_IV_SIZE);
 286  
 287      CCrypter key_crypter;
 288      if (!key_crypter.SetKey(master_key, iv_prefix)) return false;
 289      return key_crypter.Decrypt(ct_part, plaintext);
 290  }
 291  } // namespace wallet
 292