1 | #include "mtxclient/crypto/utils.hpp" |
2 | |
3 | #include <nlohmann/json.hpp> |
4 | |
5 | #include <openssl/aes.h> |
6 | #include <openssl/evp.h> |
7 | #include <openssl/hmac.h> |
8 | #include <openssl/kdf.h> |
9 | #include <openssl/rand.h> |
10 | #include <openssl/sha.h> |
11 | |
12 | #include <olm/pk.h> |
13 | |
14 | #include <algorithm> |
15 | #include <cstdint> |
16 | |
17 | #include "mtx/log.hpp" |
18 | #include "mtxclient/crypto/client.hpp" |
19 | |
20 | namespace mtx { |
21 | namespace crypto { |
22 | BinaryBuf |
23 | create_buffer(std::size_t nbytes) |
24 | { |
25 | auto buf = BinaryBuf(nbytes); |
26 | RAND_bytes(buf: buf.data(), num: (int)buf.size()); |
27 | |
28 | return buf; |
29 | } |
30 | |
31 | BinaryBuf |
32 | PBKDF2_HMAC_SHA_512(const std::string &pass, |
33 | const BinaryBuf &salt, |
34 | uint32_t iterations, |
35 | uint32_t keylen) |
36 | { |
37 | BinaryBuf out(keylen); |
38 | PKCS5_PBKDF2_HMAC(pass: &pass[0], |
39 | passlen: (int)pass.size(), |
40 | salt: salt.data(), |
41 | saltlen: (int)salt.size(), |
42 | iter: (int)iterations, |
43 | digest: EVP_sha512(), |
44 | keylen: (int)keylen, |
45 | out: out.data()); |
46 | |
47 | return out; |
48 | } |
49 | |
50 | std::optional<BinaryBuf> |
51 | key_from_passphrase(const std::string &password, |
52 | const mtx::secret_storage::AesHmacSha2KeyDescription ¶meters) |
53 | { |
54 | if (!parameters.passphrase) |
55 | throw std::invalid_argument("no passphrase to derive key from" ); |
56 | if (parameters.passphrase->algorithm != "m.pbkdf2" ) |
57 | throw std::invalid_argument("invalid pbkdf algorithm" ); |
58 | auto decryptionKey = PBKDF2_HMAC_SHA_512(pass: password, |
59 | salt: to_binary_buf(str: parameters.passphrase->salt), |
60 | iterations: parameters.passphrase->iterations, |
61 | keylen: parameters.passphrase->bits / 8); |
62 | |
63 | // verify key |
64 | using namespace mtx::crypto; |
65 | auto testKeys = HKDF_SHA256(key: decryptionKey, salt: BinaryBuf(32, 0), info: BinaryBuf{}); |
66 | |
67 | auto encrypted = AES_CTR_256_Encrypt( |
68 | plaintext: std::string(32, '\0'), aes256Key: testKeys.aes, iv: to_binary_buf(str: base642bin(b64: parameters.iv))); |
69 | |
70 | auto mac = HMAC_SHA256(hmacKey: testKeys.mac, data: encrypted); |
71 | if (mac != to_binary_buf(str: base642bin(b64: parameters.mac))) { |
72 | mtx::utils::log::log()->debug( |
73 | fmt: "mac mismatch: {} != {}" , args: bin2base64(bin: to_string(buf: mac)), args: parameters.mac); |
74 | return std::nullopt; |
75 | } |
76 | |
77 | return decryptionKey; |
78 | } |
79 | |
80 | std::optional<BinaryBuf> |
81 | key_from_recoverykey(const std::string &recoverykey, |
82 | const mtx::secret_storage::AesHmacSha2KeyDescription ¶meters) |
83 | { |
84 | auto tempKey = to_binary_buf(str: base582bin(bin: recoverykey)); |
85 | |
86 | if (tempKey.size() < 3 || tempKey[0] != 0x8b || tempKey[1] != 0x01) |
87 | return std::nullopt; |
88 | |
89 | uint8_t parity = 0; |
90 | for (auto byte = tempKey.begin(); byte != tempKey.end() - 1; ++byte) |
91 | parity ^= *byte; |
92 | |
93 | if (parity != tempKey.back()) |
94 | return std::nullopt; |
95 | |
96 | auto decryptionKey = BinaryBuf(tempKey.begin() + 2, tempKey.end() - 1); |
97 | |
98 | // verify key |
99 | using namespace mtx::crypto; |
100 | auto testKeys = HKDF_SHA256(key: decryptionKey, salt: BinaryBuf(32, 0), info: BinaryBuf{}); |
101 | |
102 | auto encrypted = AES_CTR_256_Encrypt( |
103 | plaintext: std::string(32, '\0'), aes256Key: testKeys.aes, iv: to_binary_buf(str: base642bin(b64: parameters.iv))); |
104 | |
105 | auto mac = HMAC_SHA256(hmacKey: testKeys.mac, data: encrypted); |
106 | if (mac != to_binary_buf(str: base642bin(b64: parameters.mac))) { |
107 | mtx::utils::log::log()->debug( |
108 | fmt: "mac mismatch: {} != {}" , args: bin2base64(bin: to_string(buf: mac)), args: parameters.mac); |
109 | return std::nullopt; |
110 | } |
111 | |
112 | return decryptionKey; |
113 | } |
114 | |
115 | std::string |
116 | key_to_recoverykey(const BinaryBuf &key) |
117 | { |
118 | auto buf = BinaryBuf(key.size() + 3); |
119 | buf[0] = 0x8b; |
120 | buf[1] = 0x01; |
121 | std::copy(first: begin(cont: key), last: end(cont: key), result: begin(cont&: buf) + 2); |
122 | |
123 | uint8_t parity = buf[0] ^ buf[1]; |
124 | for (uint8_t b : key) |
125 | parity ^= b; |
126 | buf.back() = parity; |
127 | |
128 | return bin2base58(bin: to_string(buf)); |
129 | } |
130 | |
131 | std::string |
132 | decrypt(const mtx::secret_storage::AesHmacSha2EncryptedData &data, |
133 | const BinaryBuf &decryptionKey, |
134 | const std::string &key_name) |
135 | { |
136 | auto keys = HKDF_SHA256(key: decryptionKey, salt: BinaryBuf(32, 0), info: to_binary_buf(str: key_name)); |
137 | auto keyMac = HMAC_SHA256(hmacKey: keys.mac, data: to_binary_buf(str: base642bin(b64: data.ciphertext))); |
138 | |
139 | if (keyMac != to_binary_buf(str: base642bin(b64: data.mac))) { |
140 | mtx::utils::log::log()->debug( |
141 | fmt: "mac mismatch: {} != {}" , args: bin2base64(bin: to_string(buf: keyMac)), args: data.mac); |
142 | return "" ; |
143 | } |
144 | |
145 | auto decryptedSecret = AES_CTR_256_Decrypt( |
146 | ciphertext: base642bin(b64: data.ciphertext), aes256Key: keys.aes, iv: to_binary_buf(str: base642bin(b64: data.iv))); |
147 | |
148 | return to_string(buf: decryptedSecret); |
149 | } |
150 | |
151 | mtx::secret_storage::AesHmacSha2EncryptedData |
152 | encrypt(const std::string &data, const BinaryBuf &decryptionKey, const std::string &key_name) |
153 | { |
154 | mtx::secret_storage::AesHmacSha2EncryptedData encrypted{}; |
155 | auto iv = compatible_iv(incompatible_iv: create_buffer(nbytes: 16)); |
156 | encrypted.iv = bin2base64(bin: to_string(buf: iv)); |
157 | |
158 | auto keys = HKDF_SHA256(key: decryptionKey, salt: BinaryBuf(32, 0), info: to_binary_buf(str: key_name)); |
159 | |
160 | auto ciphertext = AES_CTR_256_Encrypt(plaintext: data, aes256Key: keys.aes, iv); |
161 | encrypted.ciphertext = bin2base64(bin: to_string(buf: ciphertext)); |
162 | encrypted.mac = bin2base64(bin: to_string(buf: HMAC_SHA256(hmacKey: keys.mac, data: ciphertext))); |
163 | |
164 | return encrypted; |
165 | } |
166 | |
167 | HkdfKeys |
168 | HKDF_SHA256(const BinaryBuf &key, const BinaryBuf &salt, const BinaryBuf &info) |
169 | { |
170 | BinaryBuf buf(64); |
171 | EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, e: nullptr); |
172 | |
173 | if (EVP_PKEY_derive_init(ctx: pctx) <= 0) { |
174 | EVP_PKEY_CTX_free(ctx: pctx); |
175 | throw std::runtime_error("HKDF: failed derive init" ); |
176 | } |
177 | if (EVP_PKEY_CTX_set_hkdf_md(ctx: pctx, md: EVP_sha256()) <= 0) { |
178 | EVP_PKEY_CTX_free(ctx: pctx); |
179 | throw std::runtime_error("HKDF: failed to set digest" ); |
180 | } |
181 | if (EVP_PKEY_CTX_set1_hkdf_salt(ctx: pctx, salt: salt.data(), saltlen: (int)salt.size()) <= 0) { |
182 | EVP_PKEY_CTX_free(ctx: pctx); |
183 | throw std::runtime_error("HKDF: failed to set salt" ); |
184 | } |
185 | if (EVP_PKEY_CTX_set1_hkdf_key(ctx: pctx, key: key.data(), keylen: (int)key.size()) <= 0) { |
186 | EVP_PKEY_CTX_free(ctx: pctx); |
187 | throw std::runtime_error("HKDF: failed to set key" ); |
188 | } |
189 | if (EVP_PKEY_CTX_add1_hkdf_info(ctx: pctx, info: info.data(), infolen: (int)info.size()) <= 0) { |
190 | EVP_PKEY_CTX_free(ctx: pctx); |
191 | throw std::runtime_error("HKDF: failed to set info" ); |
192 | } |
193 | |
194 | std::size_t outlen = buf.size(); |
195 | if (EVP_PKEY_derive(ctx: pctx, key: buf.data(), keylen: &outlen) <= 0) { |
196 | EVP_PKEY_CTX_free(ctx: pctx); |
197 | throw std::runtime_error("HKDF: failed derive" ); |
198 | } |
199 | |
200 | EVP_PKEY_CTX_free(ctx: pctx); |
201 | |
202 | if (outlen != 64) |
203 | throw std::runtime_error("Invalid HKDF size!" ); |
204 | |
205 | BinaryBuf macKey(buf.begin() + 32, buf.end()); |
206 | buf.resize(new_size: 32); |
207 | |
208 | return {.aes: std::move(buf), .mac: std::move(macKey)}; |
209 | } |
210 | |
211 | BinaryBuf |
212 | compatible_iv(BinaryBuf incompatible_iv) |
213 | { |
214 | // need to set bit 63 to 0 |
215 | // Element and everyone else seems to be counting bytes from the back, i.e. iv_data[15] is |
216 | // the last byte. So we need to clear byte 15 - 63%8 = 15 - 7 = 8, the highest bit, 1 << 7 |
217 | // see: |
218 | // https://github.com/matrix-org/matrix-js-sdk/blob/529fe93ab14b93c515e9ab0d0277c1942a5d73c5/src/crypto/aes.ts#L144 |
219 | uint8_t *data = incompatible_iv.data(); |
220 | constexpr std::uint8_t mask = static_cast<std::uint8_t>(~(1U << (63 / 8))); |
221 | data[15 - 63 % 8] &= mask; |
222 | return incompatible_iv; |
223 | } |
224 | |
225 | BinaryBuf |
226 | AES_CTR_256_Encrypt(const std::string &plaintext, const BinaryBuf &aes256Key, BinaryBuf iv) |
227 | { |
228 | EVP_CIPHER_CTX *ctx; |
229 | |
230 | int len; |
231 | |
232 | int ciphertext_len; |
233 | |
234 | // The ciphertext expand up to block size, which is 128 for AES256 |
235 | BinaryBuf encrypted = compatible_iv(incompatible_iv: create_buffer(nbytes: plaintext.size() + AES_BLOCK_SIZE)); |
236 | |
237 | /* Create and initialise the context */ |
238 | if (ctx = EVP_CIPHER_CTX_new(); !ctx) { |
239 | // handleErrors(); |
240 | } |
241 | |
242 | if (1 != EVP_EncryptInit_ex(ctx, cipher: EVP_aes_256_ctr(), impl: nullptr, key: aes256Key.data(), iv: iv.data())) { |
243 | // handleErrors(); |
244 | } |
245 | |
246 | /* Provide the message to be encrypted, and obtain the encrypted output. |
247 | * EVP_EncryptUpdate can be called multiple times if necessary |
248 | */ |
249 | if (1 != EVP_EncryptUpdate(ctx, |
250 | out: encrypted.data(), |
251 | outl: &len, |
252 | in: reinterpret_cast<const unsigned char *>(&plaintext.c_str()[0]), |
253 | inl: (int)plaintext.size())) { |
254 | // handleErrors(); |
255 | } |
256 | ciphertext_len = len; |
257 | |
258 | /* Finalise the encryption. Further ciphertext bytes may be written at |
259 | * this stage. |
260 | */ |
261 | if (1 != EVP_EncryptFinal_ex(ctx, out: encrypted.data() + len, outl: &len)) { |
262 | // handleErrors(); |
263 | } |
264 | |
265 | ciphertext_len += len; |
266 | encrypted.resize(new_size: ciphertext_len); |
267 | |
268 | /* Clean up */ |
269 | EVP_CIPHER_CTX_free(c: ctx); |
270 | |
271 | return encrypted; |
272 | } |
273 | |
274 | BinaryBuf |
275 | AES_CTR_256_Decrypt(const std::string &ciphertext, const BinaryBuf &aes256Key, BinaryBuf iv) |
276 | { |
277 | EVP_CIPHER_CTX *ctx; |
278 | |
279 | int len; |
280 | |
281 | int plaintext_len; |
282 | |
283 | BinaryBuf decrypted = create_buffer(nbytes: ciphertext.size()); |
284 | |
285 | /* Create and initialise the context */ |
286 | if (ctx = EVP_CIPHER_CTX_new(); !ctx) { |
287 | // handleErrors(); |
288 | } |
289 | |
290 | /* Initialise the decryption operation. IMPORTANT - ensure you use a key |
291 | * and IV size appropriate for your cipher |
292 | * In this example we are using 256 bit AES (i.e. a 256 bit key). The |
293 | * IV size for *most* modes is the same as the block size. For AES this |
294 | * is 128 bits */ |
295 | if (1 != EVP_DecryptInit_ex(ctx, cipher: EVP_aes_256_ctr(), impl: nullptr, key: aes256Key.data(), iv: iv.data())) { |
296 | // handleErrors(); |
297 | } |
298 | |
299 | /* Provide the message to be decrypted, and obtain the plaintext output. |
300 | * EVP_DecryptUpdate can be called multiple times if necessary |
301 | */ |
302 | if (1 != EVP_DecryptUpdate(ctx, |
303 | out: decrypted.data(), |
304 | outl: &len, |
305 | in: reinterpret_cast<const unsigned char *>(&ciphertext.data()[0]), |
306 | inl: (int)ciphertext.size())) { |
307 | // handleErrors(); |
308 | } |
309 | plaintext_len = len; |
310 | |
311 | /* Finalise the decryption. Further plaintext bytes may be written at |
312 | * this stage. |
313 | */ |
314 | if (1 != EVP_DecryptFinal_ex(ctx, outm: decrypted.data() + len, outl: &len)) { |
315 | // handleErrors(); |
316 | } |
317 | plaintext_len += len; |
318 | decrypted.resize(new_size: plaintext_len); |
319 | |
320 | /* Clean up */ |
321 | EVP_CIPHER_CTX_free(c: ctx); |
322 | |
323 | return decrypted; |
324 | } |
325 | |
326 | std::string |
327 | CURVE25519_public_key_from_private(const BinaryBuf &privateKey) |
328 | { |
329 | auto ctx = create_olm_object<PkDecryptionObject>(); |
330 | |
331 | BinaryBuf pubkey(::olm_pk_key_length()); |
332 | |
333 | ::olm_pk_key_from_private( |
334 | decryption: ctx.get(), pubkey: pubkey.data(), pubkey_length: pubkey.size(), privkey: privateKey.data(), privkey_length: privateKey.size()); |
335 | |
336 | return to_string(buf: pubkey); |
337 | } |
338 | |
339 | CURVE25519_AES_SHA2_Encrypted |
340 | CURVE25519_AES_SHA2_Encrypt(const std::string &plaintext, const std::string &base64_publicKey) |
341 | { |
342 | auto ctx = create_olm_object<PkEncryptionObject>(); |
343 | |
344 | ::olm_pk_encryption_set_recipient_key( |
345 | encryption: ctx.get(), public_key: base64_publicKey.data(), public_key_length: base64_publicKey.size()); |
346 | |
347 | BinaryBuf ephemeral(::olm_pk_key_length()); |
348 | BinaryBuf mac(::olm_pk_mac_length(encryption: ctx.get())); |
349 | BinaryBuf ciphertext(::olm_pk_ciphertext_length(encryption: ctx.get(), plaintext_length: plaintext.size())); |
350 | BinaryBuf randomBuf = create_buffer(nbytes: ::olm_pk_encrypt_random_length(encryption: ctx.get())); |
351 | auto encrypted_size = ::olm_pk_encrypt(encryption: ctx.get(), |
352 | plaintext: plaintext.data(), |
353 | plaintext_length: plaintext.size(), |
354 | ciphertext: ciphertext.data(), |
355 | ciphertext_length: ciphertext.size(), |
356 | mac: mac.data(), |
357 | mac_length: mac.size(), |
358 | ephemeral_key: ephemeral.data(), |
359 | ephemeral_key_size: ephemeral.size(), |
360 | random: randomBuf.data(), |
361 | random_length: randomBuf.size()); |
362 | |
363 | if (encrypted_size != olm_error()) { |
364 | CURVE25519_AES_SHA2_Encrypted val; |
365 | val.ciphertext = to_string(buf: ciphertext); |
366 | val.mac = to_string(buf: mac); |
367 | val.ephemeral = to_string(buf: ephemeral); |
368 | return val; |
369 | } else |
370 | throw olm_exception(__func__, ctx.get()); |
371 | } |
372 | |
373 | std::string |
374 | CURVE25519_AES_SHA2_Decrypt(std::string base64_ciphertext, |
375 | const BinaryBuf &privateKey, |
376 | const std::string &ephemeral, |
377 | const std::string &mac) |
378 | { |
379 | auto ctx = create_olm_object<PkDecryptionObject>(); |
380 | |
381 | BinaryBuf pubkey(::olm_pk_key_length()); |
382 | |
383 | ::olm_pk_key_from_private( |
384 | decryption: ctx.get(), pubkey: pubkey.data(), pubkey_length: pubkey.size(), privkey: privateKey.data(), privkey_length: privateKey.size()); |
385 | |
386 | std::string plaintext(olm_pk_max_plaintext_length(decryption: ctx.get(), ciphertext_length: base64_ciphertext.size()), '\0'); |
387 | std::size_t decrypted_size = ::olm_pk_decrypt(decryption: ctx.get(), |
388 | ephemeral_key: ephemeral.data(), |
389 | ephemeral_key_length: ephemeral.size(), |
390 | mac: mac.data(), |
391 | mac_length: mac.size(), |
392 | ciphertext: base64_ciphertext.data(), |
393 | ciphertext_length: base64_ciphertext.size(), |
394 | plaintext: plaintext.data(), |
395 | max_plaintext_length: plaintext.size()); |
396 | |
397 | if (decrypted_size != olm_error()) { |
398 | plaintext.resize(n: decrypted_size); |
399 | return plaintext; |
400 | } else |
401 | throw olm_exception(__func__, ctx.get()); |
402 | } |
403 | |
404 | mtx::responses::backup::EncryptedSessionData |
405 | encrypt_session(const mtx::responses::backup::SessionData &data, const std::string &publicKey) |
406 | { |
407 | mtx::responses::backup::EncryptedSessionData d; |
408 | |
409 | auto temp = CURVE25519_AES_SHA2_Encrypt(plaintext: nlohmann::json(data).dump(), base64_publicKey: publicKey); |
410 | d.ciphertext = std::move(temp.ciphertext); |
411 | d.mac = std::move(temp.mac); |
412 | d.ephemeral = std::move(temp.ephemeral); |
413 | |
414 | return d; |
415 | } |
416 | |
417 | mtx::responses::backup::SessionData |
418 | decrypt_session(const mtx::responses::backup::EncryptedSessionData &data, |
419 | const BinaryBuf &privateKey) |
420 | { |
421 | return nlohmann::json::parse( |
422 | i: CURVE25519_AES_SHA2_Decrypt(base64_ciphertext: data.ciphertext, privateKey, ephemeral: data.ephemeral, mac: data.mac)) |
423 | .get<mtx::responses::backup::SessionData>(); |
424 | } |
425 | |
426 | std::string |
427 | sha256(const std::string &data) |
428 | { |
429 | bool success = false; |
430 | std::string hashed; |
431 | |
432 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
433 | EVP_MD_CTX *context = EVP_MD_CTX_create(); |
434 | #else |
435 | EVP_MD_CTX *context = EVP_MD_CTX_new(); |
436 | #endif |
437 | |
438 | if (context != nullptr) { |
439 | if (EVP_DigestInit_ex(ctx: context, type: EVP_sha256(), impl: nullptr)) { |
440 | if (EVP_DigestUpdate(ctx: context, d: data.c_str(), cnt: data.length())) { |
441 | unsigned char hash[EVP_MAX_MD_SIZE]; |
442 | unsigned int lengthOfHash = 0; |
443 | |
444 | if (EVP_DigestFinal_ex(ctx: context, md: hash, s: &lengthOfHash)) { |
445 | hashed = std::string(hash, hash + lengthOfHash); |
446 | success = true; |
447 | } |
448 | } |
449 | } |
450 | |
451 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
452 | EVP_MD_CTX_destroy(context); |
453 | #else |
454 | EVP_MD_CTX_free(ctx: context); |
455 | #endif |
456 | } |
457 | |
458 | if (success) |
459 | return hashed; |
460 | throw std::runtime_error("sha256 failed!" ); |
461 | } |
462 | |
463 | BinaryBuf |
464 | decrypt_file(const std::string &ciphertext, const mtx::crypto::EncryptedFile &encryption_info) |
465 | { |
466 | if (encryption_info.v != "v2" ) |
467 | throw std::invalid_argument("Unsupported encrypted file version" ); |
468 | |
469 | if (encryption_info.key.kty != "oct" ) |
470 | throw std::invalid_argument("Unsupported key type" ); |
471 | |
472 | if (encryption_info.key.alg != "A256CTR" ) |
473 | throw std::invalid_argument("Unsupported algorithm" ); |
474 | |
475 | // Be careful, the key should be urlsafe and unpadded, the iv and sha only need to |
476 | // be unpadded |
477 | if (bin2base64_unpadded(bin: sha256(data: ciphertext)) != encryption_info.hashes.at(k: "sha256" )) |
478 | throw std::invalid_argument( |
479 | "sha256 of encrypted file does not match the ciphertext, expected '" + |
480 | bin2base64_unpadded(bin: sha256(data: ciphertext)) + "', got '" + |
481 | encryption_info.hashes.at(k: "sha256" ) + "'" ); |
482 | |
483 | return AES_CTR_256_Decrypt(ciphertext, |
484 | aes256Key: to_binary_buf(str: base642bin_urlsafe_unpadded(b64: encryption_info.key.k)), |
485 | iv: to_binary_buf(str: base642bin_unpadded(b64: encryption_info.iv))); |
486 | } |
487 | |
488 | std::pair<BinaryBuf, mtx::crypto::EncryptedFile> |
489 | encrypt_file(const std::string &plaintext) |
490 | { |
491 | mtx::crypto::EncryptedFile encryption_info; |
492 | |
493 | // iv has to be 16 bytes, key 32! |
494 | BinaryBuf key = create_buffer(nbytes: 32); |
495 | BinaryBuf iv = create_buffer(nbytes: 16); |
496 | constexpr std::uint8_t mask = static_cast<std::uint8_t>(~(1U << (63 / 8))); |
497 | iv[15 - 63 % 8] &= mask; |
498 | |
499 | // Counter should be 0 in v1.1 of the spec... |
500 | for (std::size_t i = 8; i < 16; i++) |
501 | iv[i] = 0; |
502 | |
503 | BinaryBuf cyphertext = AES_CTR_256_Encrypt(plaintext, aes256Key: key, iv); |
504 | |
505 | // Be careful, the key should be urlsafe and unpadded, the iv and sha only need to |
506 | // be unpadded |
507 | JWK web_key; |
508 | web_key.ext = true; |
509 | web_key.kty = "oct" ; |
510 | web_key.key_ops = {"encrypt" , "decrypt" }; |
511 | web_key.alg = "A256CTR" ; |
512 | web_key.k = bin2base64_urlsafe_unpadded(bin: to_string(buf: key)); |
513 | web_key.ext = true; |
514 | |
515 | encryption_info.key = web_key; |
516 | encryption_info.iv = bin2base64_unpadded(bin: to_string(buf: iv)); |
517 | encryption_info.hashes["sha256" ] = bin2base64_unpadded(bin: sha256(data: to_string(buf: cyphertext))); |
518 | encryption_info.v = "v2" ; |
519 | |
520 | return std::make_pair(x&: cyphertext, y&: encryption_info); |
521 | } |
522 | |
523 | template<typename T> |
524 | void |
525 | remove_substrs(std::basic_string<T> &s, const std::basic_string<T> &p) |
526 | { |
527 | auto n = p.length(); |
528 | |
529 | for (auto i = s.find(p); i != std::basic_string<T>::npos; i = s.find(p)) |
530 | s.erase(i, n); |
531 | } |
532 | |
533 | std::string |
534 | unpack_key_file(const std::string &data) |
535 | { |
536 | std::string unpacked(data); |
537 | remove_substrs(s&: unpacked, p: HEADER_LINE); |
538 | |
539 | remove_substrs(s&: unpacked, p: TRAILER_LINE); |
540 | |
541 | remove_substrs(s&: unpacked, p: std::string("\n" )); |
542 | |
543 | return unpacked; |
544 | } |
545 | |
546 | BinaryBuf |
547 | HMAC_SHA256(const BinaryBuf &hmacKey, const BinaryBuf &data) |
548 | { |
549 | unsigned int len = SHA256_DIGEST_LENGTH; |
550 | unsigned char digest[SHA256_DIGEST_LENGTH]; |
551 | HMAC(evp_md: EVP_sha256(), key: hmacKey.data(), key_len: (int)hmacKey.size(), data: data.data(), data_len: data.size(), md: digest, md_len: &len); |
552 | BinaryBuf output(digest, digest + SHA256_DIGEST_LENGTH); |
553 | return output; |
554 | } |
555 | |
556 | void |
557 | uint8_to_uint32(uint8_t b[4], uint32_t &u32) |
558 | { |
559 | u32 = std::uint32_t{b[0]} << 24 | std::uint32_t{b[1]} << 16 | std::uint32_t{b[2]} << 8 | |
560 | std::uint32_t{b[3]}; |
561 | } |
562 | |
563 | void |
564 | uint32_to_uint8(uint8_t b[4], uint32_t u32) |
565 | { |
566 | b[3] = (uint8_t)u32; |
567 | b[2] = (uint8_t)(u32 >>= 8); |
568 | b[1] = (uint8_t)(u32 >>= 8); |
569 | b[0] = (uint8_t)(u32 >> 8); |
570 | } |
571 | } // namespace crypto |
572 | } // namespace mtx |
573 | |