/Users/eugenesiegel/btc/bitcoin/src/wallet/crypter.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2009-2021 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 | 0 | { | 
| 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 | 0 |     if(!count || !key || !iv) | 
| 23 | 0 |         return 0; | 
| 24 |  |  | 
| 25 | 0 |     unsigned char buf[CSHA512::OUTPUT_SIZE]; | 
| 26 | 0 |     CSHA512 di; | 
| 27 |  | 
 | 
| 28 | 0 |     di.Write(UCharCast(key_data.data()), key_data.size()); | 
| 29 | 0 |     di.Write(salt.data(), salt.size()); | 
| 30 | 0 |     di.Finalize(buf); | 
| 31 |  | 
 | 
| 32 | 0 |     for(int i = 0; i != count - 1; i++) | 
| 33 | 0 |         di.Reset().Write(buf, sizeof(buf)).Finalize(buf); | 
| 34 |  | 
 | 
| 35 | 0 |     memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE); | 
| 36 | 0 |     memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE); | 
| 37 | 0 |     memory_cleanse(buf, sizeof(buf)); | 
| 38 | 0 |     return WALLET_CRYPTO_KEY_SIZE; | 
| 39 | 0 | } | 
| 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 | 0 | { | 
| 43 | 0 |     if (rounds < 1 || salt.size() != WALLET_CRYPTO_SALT_SIZE) { | 
| 44 | 0 |         return false; | 
| 45 | 0 |     } | 
| 46 |  |  | 
| 47 | 0 |     int i = 0; | 
| 48 | 0 |     if (derivation_method == 0) { | 
| 49 | 0 |         i = BytesToKeySHA512AES(salt, key_data, rounds, vchKey.data(), vchIV.data()); | 
| 50 | 0 |     } | 
| 51 |  | 
 | 
| 52 | 0 |     if (i != (int)WALLET_CRYPTO_KEY_SIZE) | 
| 53 | 0 |     { | 
| 54 | 0 |         memory_cleanse(vchKey.data(), vchKey.size()); | 
| 55 | 0 |         memory_cleanse(vchIV.data(), vchIV.size()); | 
| 56 | 0 |         return false; | 
| 57 | 0 |     } | 
| 58 |  |  | 
| 59 | 0 |     fKeySet = true; | 
| 60 | 0 |     return true; | 
| 61 | 0 | } | 
| 62 |  |  | 
| 63 |  | bool CCrypter::SetKey(const CKeyingMaterial& new_key, const std::span<const unsigned char> new_iv) | 
| 64 | 0 | { | 
| 65 | 0 |     if (new_key.size() != WALLET_CRYPTO_KEY_SIZE || new_iv.size() != WALLET_CRYPTO_IV_SIZE) { | 
| 66 | 0 |         return false; | 
| 67 | 0 |     } | 
| 68 |  |  | 
| 69 | 0 |     memcpy(vchKey.data(), new_key.data(), new_key.size()); | 
| 70 | 0 |     memcpy(vchIV.data(), new_iv.data(), new_iv.size()); | 
| 71 |  | 
 | 
| 72 | 0 |     fKeySet = true; | 
| 73 | 0 |     return true; | 
| 74 | 0 | } | 
| 75 |  |  | 
| 76 |  | bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const | 
| 77 | 0 | { | 
| 78 | 0 |     if (!fKeySet) | 
| 79 | 0 |         return false; | 
| 80 |  |  | 
| 81 |  |     // max ciphertext len for a n bytes of plaintext is | 
| 82 |  |     // n + AES_BLOCKSIZE bytes | 
| 83 | 0 |     vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE); | 
| 84 |  | 
 | 
| 85 | 0 |     AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true); | 
| 86 | 0 |     size_t nLen = enc.Encrypt(vchPlaintext.data(), vchPlaintext.size(), vchCiphertext.data()); | 
| 87 | 0 |     if(nLen < vchPlaintext.size()) | 
| 88 | 0 |         return false; | 
| 89 | 0 |     vchCiphertext.resize(nLen); | 
| 90 |  | 
 | 
| 91 | 0 |     return true; | 
| 92 | 0 | } | 
| 93 |  |  | 
| 94 |  | bool CCrypter::Decrypt(const std::span<const unsigned char> ciphertext, CKeyingMaterial& plaintext) const | 
| 95 | 0 | { | 
| 96 | 0 |     if (!fKeySet) | 
| 97 | 0 |         return false; | 
| 98 |  |  | 
| 99 |  |     // plaintext will always be equal to or lesser than length of ciphertext | 
| 100 | 0 |     plaintext.resize(ciphertext.size()); | 
| 101 |  | 
 | 
| 102 | 0 |     AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); | 
| 103 | 0 |     int len = dec.Decrypt(ciphertext.data(), ciphertext.size(), plaintext.data()); | 
| 104 | 0 |     if (len == 0) { | 
| 105 | 0 |         return false; | 
| 106 | 0 |     } | 
| 107 | 0 |     plaintext.resize(len); | 
| 108 | 0 |     return true; | 
| 109 | 0 | } | 
| 110 |  |  | 
| 111 |  | bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) | 
| 112 | 0 | { | 
| 113 | 0 |     CCrypter cKeyCrypter; | 
| 114 | 0 |     std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); | 
| 115 | 0 |     memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); | 
| 116 | 0 |     if(!cKeyCrypter.SetKey(vMasterKey, chIV)) | 
| 117 | 0 |         return false; | 
| 118 | 0 |     return cKeyCrypter.Encrypt(vchPlaintext, vchCiphertext); | 
| 119 | 0 | } | 
| 120 |  |  | 
| 121 |  | bool DecryptSecret(const CKeyingMaterial& master_key, const std::span<const unsigned char> ciphertext, const uint256& iv, CKeyingMaterial& plaintext) | 
| 122 | 0 | { | 
| 123 | 0 |     CCrypter key_crypter; | 
| 124 | 0 |     static_assert(WALLET_CRYPTO_IV_SIZE <= std::remove_reference_t<decltype(iv)>::size()); | 
| 125 | 0 |     const std::span iv_prefix{iv.data(), WALLET_CRYPTO_IV_SIZE}; | 
| 126 | 0 |     if (!key_crypter.SetKey(master_key, iv_prefix)) { | 
| 127 | 0 |         return false; | 
| 128 | 0 |     } | 
| 129 | 0 |     return key_crypter.Decrypt(ciphertext, plaintext); | 
| 130 | 0 | } | 
| 131 |  |  | 
| 132 |  | bool DecryptKey(const CKeyingMaterial& master_key, const std::span<const unsigned char> crypted_secret, const CPubKey& pub_key, CKey& key) | 
| 133 | 0 | { | 
| 134 | 0 |     CKeyingMaterial secret; | 
| 135 | 0 |     if (!DecryptSecret(master_key, crypted_secret, pub_key.GetHash(), secret)) { | 
| 136 | 0 |         return false; | 
| 137 | 0 |     } | 
| 138 |  |  | 
| 139 | 0 |     if (secret.size() != 32) { | 
| 140 | 0 |         return false; | 
| 141 | 0 |     } | 
| 142 |  |  | 
| 143 | 0 |     key.Set(secret.begin(), secret.end(), pub_key.IsCompressed()); | 
| 144 | 0 |     return key.VerifyPubKey(pub_key); | 
| 145 | 0 | } | 
| 146 |  | } // namespace wallet |