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