/root/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 |