react-native-quick-crypto 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +11 -63
  2. package/android/CMakeLists.txt +2 -0
  3. package/cpp/Cipher/MGLRsa.cpp +179 -3
  4. package/cpp/Cipher/MGLRsa.h +40 -0
  5. package/cpp/JSIUtils/MGLJSIUtils.h +8 -0
  6. package/cpp/MGLKeys.cpp +41 -43
  7. package/cpp/MGLKeys.h +9 -2
  8. package/cpp/MGLQuickCryptoHostObject.cpp +6 -6
  9. package/cpp/Sig/MGLSignHostObjects.cpp +22 -15
  10. package/cpp/Utils/MGLUtils.cpp +71 -1
  11. package/cpp/Utils/MGLUtils.h +55 -1
  12. package/cpp/webcrypto/MGLWebCrypto.cpp +89 -37
  13. package/cpp/webcrypto/MGLWebCrypto.h +5 -7
  14. package/cpp/webcrypto/crypto_aes.cpp +516 -0
  15. package/cpp/webcrypto/crypto_aes.h +79 -0
  16. package/cpp/webcrypto/crypto_ec.cpp +4 -20
  17. package/cpp/webcrypto/crypto_ec.h +0 -5
  18. package/cpp/webcrypto/crypto_keygen.cpp +86 -0
  19. package/cpp/webcrypto/crypto_keygen.h +38 -0
  20. package/lib/commonjs/Cipher.js +3 -1
  21. package/lib/commonjs/Cipher.js.map +1 -1
  22. package/lib/commonjs/Hashnames.js +20 -8
  23. package/lib/commonjs/Hashnames.js.map +1 -1
  24. package/lib/commonjs/NativeQuickCrypto/Cipher.js +13 -1
  25. package/lib/commonjs/NativeQuickCrypto/Cipher.js.map +1 -1
  26. package/lib/commonjs/NativeQuickCrypto/NativeQuickCrypto.js.map +1 -1
  27. package/lib/commonjs/NativeQuickCrypto/aes.js +6 -0
  28. package/lib/commonjs/NativeQuickCrypto/aes.js.map +1 -0
  29. package/lib/commonjs/NativeQuickCrypto/keygen.js +6 -0
  30. package/lib/commonjs/NativeQuickCrypto/keygen.js.map +1 -0
  31. package/lib/commonjs/NativeQuickCrypto/rsa.js +6 -0
  32. package/lib/commonjs/NativeQuickCrypto/rsa.js.map +1 -0
  33. package/lib/commonjs/Utils.js +30 -6
  34. package/lib/commonjs/Utils.js.map +1 -1
  35. package/lib/commonjs/aes.js +184 -227
  36. package/lib/commonjs/aes.js.map +1 -1
  37. package/lib/commonjs/index.js +12 -2
  38. package/lib/commonjs/index.js.map +1 -1
  39. package/lib/commonjs/keygen.js +56 -0
  40. package/lib/commonjs/keygen.js.map +1 -0
  41. package/lib/commonjs/keys.js +74 -5
  42. package/lib/commonjs/keys.js.map +1 -1
  43. package/lib/commonjs/rsa.js +115 -196
  44. package/lib/commonjs/rsa.js.map +1 -1
  45. package/lib/commonjs/sig.js.map +1 -1
  46. package/lib/commonjs/subtle.js +140 -78
  47. package/lib/commonjs/subtle.js.map +1 -1
  48. package/lib/commonjs/webcrypto.js +14 -0
  49. package/lib/commonjs/webcrypto.js.map +1 -0
  50. package/lib/module/Cipher.js +3 -1
  51. package/lib/module/Cipher.js.map +1 -1
  52. package/lib/module/Hashnames.js +20 -8
  53. package/lib/module/Hashnames.js.map +1 -1
  54. package/lib/module/NativeQuickCrypto/Cipher.js +12 -0
  55. package/lib/module/NativeQuickCrypto/Cipher.js.map +1 -1
  56. package/lib/module/NativeQuickCrypto/NativeQuickCrypto.js.map +1 -1
  57. package/lib/module/NativeQuickCrypto/aes.js +2 -0
  58. package/lib/module/NativeQuickCrypto/aes.js.map +1 -0
  59. package/lib/module/NativeQuickCrypto/keygen.js +2 -0
  60. package/lib/module/NativeQuickCrypto/keygen.js.map +1 -0
  61. package/lib/module/NativeQuickCrypto/rsa.js +2 -0
  62. package/lib/module/NativeQuickCrypto/rsa.js.map +1 -0
  63. package/lib/module/Utils.js +26 -5
  64. package/lib/module/Utils.js.map +1 -1
  65. package/lib/module/aes.js +183 -228
  66. package/lib/module/aes.js.map +1 -1
  67. package/lib/module/index.js +11 -2
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/keygen.js +47 -0
  70. package/lib/module/keygen.js.map +1 -0
  71. package/lib/module/keys.js +68 -4
  72. package/lib/module/keys.js.map +1 -1
  73. package/lib/module/rsa.js +115 -198
  74. package/lib/module/rsa.js.map +1 -1
  75. package/lib/module/sig.js.map +1 -1
  76. package/lib/module/subtle.js +143 -82
  77. package/lib/module/subtle.js.map +1 -1
  78. package/lib/module/webcrypto.js +8 -0
  79. package/lib/module/webcrypto.js.map +1 -0
  80. package/lib/typescript/Cipher.d.ts +0 -1
  81. package/lib/typescript/Cipher.d.ts.map +1 -1
  82. package/lib/typescript/Hash.d.ts.map +1 -1
  83. package/lib/typescript/Hashnames.d.ts +2 -2
  84. package/lib/typescript/Hashnames.d.ts.map +1 -1
  85. package/lib/typescript/NativeQuickCrypto/Cipher.d.ts +5 -0
  86. package/lib/typescript/NativeQuickCrypto/Cipher.d.ts.map +1 -1
  87. package/lib/typescript/NativeQuickCrypto/NativeQuickCrypto.d.ts +4 -1
  88. package/lib/typescript/NativeQuickCrypto/NativeQuickCrypto.d.ts.map +1 -1
  89. package/lib/typescript/NativeQuickCrypto/aes.d.ts +5 -0
  90. package/lib/typescript/NativeQuickCrypto/aes.d.ts.map +1 -0
  91. package/lib/typescript/NativeQuickCrypto/keygen.d.ts +4 -0
  92. package/lib/typescript/NativeQuickCrypto/keygen.d.ts.map +1 -0
  93. package/lib/typescript/NativeQuickCrypto/rsa.d.ts +5 -0
  94. package/lib/typescript/NativeQuickCrypto/rsa.d.ts.map +1 -0
  95. package/lib/typescript/NativeQuickCrypto/webcrypto.d.ts +12 -2
  96. package/lib/typescript/NativeQuickCrypto/webcrypto.d.ts.map +1 -1
  97. package/lib/typescript/Utils.d.ts +4 -4
  98. package/lib/typescript/Utils.d.ts.map +1 -1
  99. package/lib/typescript/aes.d.ts +18 -1
  100. package/lib/typescript/aes.d.ts.map +1 -1
  101. package/lib/typescript/ec.d.ts.map +1 -1
  102. package/lib/typescript/index.d.ts +27 -24
  103. package/lib/typescript/index.d.ts.map +1 -1
  104. package/lib/typescript/keygen.d.ts +6 -0
  105. package/lib/typescript/keygen.d.ts.map +1 -0
  106. package/lib/typescript/keys.d.ts +58 -17
  107. package/lib/typescript/keys.d.ts.map +1 -1
  108. package/lib/typescript/rsa.d.ts +9 -1
  109. package/lib/typescript/rsa.d.ts.map +1 -1
  110. package/lib/typescript/sig.d.ts +3 -17
  111. package/lib/typescript/sig.d.ts.map +1 -1
  112. package/lib/typescript/subtle.d.ts +6 -5
  113. package/lib/typescript/subtle.d.ts.map +1 -1
  114. package/lib/typescript/webcrypto.d.ts +9 -0
  115. package/lib/typescript/webcrypto.d.ts.map +1 -0
  116. package/package.json +12 -12
  117. package/src/Cipher.ts +1 -1
  118. package/src/Hashnames.ts +23 -21
  119. package/src/NativeQuickCrypto/Cipher.ts +32 -0
  120. package/src/NativeQuickCrypto/NativeQuickCrypto.ts +6 -0
  121. package/src/NativeQuickCrypto/aes.ts +14 -0
  122. package/src/NativeQuickCrypto/keygen.ts +7 -0
  123. package/src/NativeQuickCrypto/rsa.ts +12 -0
  124. package/src/NativeQuickCrypto/webcrypto.ts +26 -2
  125. package/src/Utils.ts +37 -8
  126. package/src/aes.ts +259 -222
  127. package/src/index.ts +10 -1
  128. package/src/keygen.ts +80 -0
  129. package/src/keys.ts +143 -30
  130. package/src/rsa.ts +161 -187
  131. package/src/sig.ts +7 -23
  132. package/src/subtle.ts +211 -93
  133. package/src/webcrypto.ts +8 -0
@@ -0,0 +1,516 @@
1
+ #include "crypto_aes.h"
2
+
3
+ #ifdef ANDROID
4
+ #include "JSIUtils/MGLJSIUtils.h"
5
+ #include "Utils/MGLUtils.h"
6
+ #else
7
+ #include "MGLJSIUtils.h"
8
+ #include "MGLUtils.h"
9
+ #endif
10
+
11
+ namespace margelo {
12
+
13
+ namespace {
14
+ // Implements general AES encryption and decryption for CBC
15
+ // The key_data must be a secret key.
16
+ // On success, this function sets out to a new ByteSource
17
+ // instance containing the results and returns WebCryptoCipherStatus::OK.
18
+ WebCryptoCipherStatus AES_Cipher(const AESCipherConfig& params, ByteSource* out) {
19
+ CHECK_NOT_NULL(params.key);
20
+ CHECK_EQ(params.key->GetKeyType(), kKeyTypeSecret);
21
+
22
+ const int mode = EVP_CIPHER_mode(params.cipher);
23
+
24
+ CipherCtxPointer ctx(EVP_CIPHER_CTX_new());
25
+ EVP_CIPHER_CTX_init(ctx.get());
26
+ if (mode == EVP_CIPH_WRAP_MODE)
27
+ EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
28
+
29
+ const bool encrypt = params.mode == AESCipherConfig::Mode::kEncrypt;
30
+
31
+ if (!EVP_CipherInit_ex(
32
+ ctx.get(),
33
+ params.cipher,
34
+ nullptr,
35
+ nullptr,
36
+ nullptr,
37
+ encrypt)) {
38
+ // Cipher init failed
39
+ return WebCryptoCipherStatus::FAILED;
40
+ }
41
+
42
+ if (mode == EVP_CIPH_GCM_MODE && !EVP_CIPHER_CTX_ctrl(
43
+ ctx.get(),
44
+ EVP_CTRL_AEAD_SET_IVLEN,
45
+ params.iv.size(),
46
+ nullptr)) {
47
+ return WebCryptoCipherStatus::FAILED;
48
+ }
49
+
50
+ if (!EVP_CIPHER_CTX_set_key_length(
51
+ ctx.get(),
52
+ params.key->GetSymmetricKeySize()) ||
53
+ !EVP_CipherInit_ex(
54
+ ctx.get(),
55
+ nullptr,
56
+ nullptr,
57
+ reinterpret_cast<const unsigned char*>(params.key->GetSymmetricKey().c_str()),
58
+ params.iv.data<unsigned char>(),
59
+ encrypt)) {
60
+ return WebCryptoCipherStatus::FAILED;
61
+ }
62
+
63
+ size_t tag_len = 0;
64
+
65
+ if (mode == EVP_CIPH_GCM_MODE) {
66
+ switch (params.mode) {
67
+ case AESCipherConfig::Mode::kDecrypt:
68
+ // If in decrypt mode, the auth tag must be set in the params.tag.
69
+ CHECK(params.tag);
70
+ if (!EVP_CIPHER_CTX_ctrl(ctx.get(),
71
+ EVP_CTRL_AEAD_SET_TAG,
72
+ params.tag.size(),
73
+ const_cast<char*>(params.tag.data<char>()))) {
74
+ return WebCryptoCipherStatus::FAILED;
75
+ }
76
+ break;
77
+ case AESCipherConfig::Mode::kEncrypt:
78
+ // In decrypt mode, we grab the tag length here. We'll use it to
79
+ // ensure that that allocated buffer has enough room for both the
80
+ // final block and the auth tag. Unlike our other AES-GCM implementation
81
+ // in CipherBase, in WebCrypto, the auth tag is concatenated to the end
82
+ // of the generated ciphertext and returned in the same ArrayBuffer.
83
+ tag_len = params.length;
84
+ break;
85
+ default:
86
+ throw std::runtime_error("Unreachable code in AES_Cipher");
87
+ }
88
+ }
89
+
90
+ size_t total = 0;
91
+ int buf_len = params.data.size() + EVP_CIPHER_CTX_block_size(ctx.get()) + tag_len;
92
+ int out_len;
93
+
94
+ if (mode == EVP_CIPH_GCM_MODE &&
95
+ params.additional_data.size() &&
96
+ !EVP_CipherUpdate(
97
+ ctx.get(),
98
+ nullptr,
99
+ &out_len,
100
+ params.additional_data.data<unsigned char>(),
101
+ params.additional_data.size())) {
102
+ return WebCryptoCipherStatus::FAILED;
103
+ }
104
+
105
+ ByteSource::Builder buf(buf_len);
106
+
107
+ // In some outdated version of OpenSSL (e.g.
108
+ // ubi81_sharedlibs_openssl111fips_x64) may be used in sharedlib mode, the
109
+ // logic will be failed when input size is zero. The newly OpenSSL has fixed
110
+ // it up. But we still have to regard zero as special in Node.js code to
111
+ // prevent old OpenSSL failure.
112
+ //
113
+ // Refs: https://github.com/openssl/openssl/commit/420cb707b880e4fb649094241371701013eeb15f
114
+ // Refs: https://github.com/nodejs/node/pull/38913#issuecomment-866505244
115
+ if (params.data.size() == 0) {
116
+ out_len = 0;
117
+ } else if (!EVP_CipherUpdate(ctx.get(),
118
+ buf.data<unsigned char>(),
119
+ &out_len,
120
+ params.data.data<unsigned char>(),
121
+ params.data.size())) {
122
+ return WebCryptoCipherStatus::FAILED;
123
+ }
124
+
125
+ total += out_len;
126
+ CHECK_LE(out_len, buf_len);
127
+ out_len = EVP_CIPHER_CTX_block_size(ctx.get());
128
+ if (!EVP_CipherFinal_ex(
129
+ ctx.get(), buf.data<unsigned char>() + total, &out_len)) {
130
+ return WebCryptoCipherStatus::FAILED;
131
+ }
132
+ total += out_len;
133
+
134
+ // If using AES_GCM, grab the generated auth tag and append
135
+ // it to the end of the ciphertext.
136
+ if (params.mode == AESCipherConfig::Mode::kEncrypt && mode == EVP_CIPH_GCM_MODE) {
137
+ if (!EVP_CIPHER_CTX_ctrl(ctx.get(),
138
+ EVP_CTRL_AEAD_GET_TAG,
139
+ tag_len,
140
+ buf.data<unsigned char>() + total))
141
+ return WebCryptoCipherStatus::FAILED;
142
+ total += tag_len;
143
+ }
144
+
145
+ // It's possible that we haven't used the full allocated space. Size down.
146
+ *out = std::move(buf).release(total);
147
+
148
+ return WebCryptoCipherStatus::OK;
149
+ }
150
+
151
+ // The AES_CTR implementation here takes it's inspiration from the chromium
152
+ // implementation here:
153
+ // https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/aes_ctr.cc
154
+
155
+ template <typename T>
156
+ T CeilDiv(T a, T b) {
157
+ return a == 0 ? 0 : 1 + (a - 1) / b;
158
+ }
159
+
160
+ BignumPointer GetCounter(const AESCipherConfig& params) {
161
+ unsigned int remainder = (params.length % CHAR_BIT);
162
+ const unsigned char* data = params.iv.data<unsigned char>();
163
+
164
+ if (remainder == 0) {
165
+ unsigned int byte_length = params.length / CHAR_BIT;
166
+ return BignumPointer(BN_bin2bn(
167
+ data + params.iv.size() - byte_length,
168
+ byte_length,
169
+ nullptr));
170
+ }
171
+
172
+ unsigned int byte_length =
173
+ CeilDiv(params.length, static_cast<size_t>(CHAR_BIT));
174
+
175
+ std::vector<unsigned char> counter(
176
+ data + params.iv.size() - byte_length,
177
+ data + params.iv.size());
178
+ counter[0] &= ~(0xFF << remainder);
179
+
180
+ return BignumPointer(BN_bin2bn(counter.data(), counter.size(), nullptr));
181
+ }
182
+
183
+ std::vector<unsigned char> BlockWithZeroedCounter(
184
+ const AESCipherConfig& params) {
185
+ unsigned int length_bytes = params.length / CHAR_BIT;
186
+ unsigned int remainder = params.length % CHAR_BIT;
187
+
188
+ const unsigned char* data = params.iv.data<unsigned char>();
189
+
190
+ std::vector<unsigned char> new_counter_block(data, data + params.iv.size());
191
+
192
+ size_t index = new_counter_block.size() - length_bytes;
193
+ memset(&new_counter_block.front() + index, 0, length_bytes);
194
+
195
+ if (remainder)
196
+ new_counter_block[index - 1] &= 0xFF << remainder;
197
+
198
+ return new_counter_block;
199
+ }
200
+
201
+ WebCryptoCipherStatus AES_CTR_Cipher2(
202
+ const AESCipherConfig& params,
203
+ const ByteSource &in,
204
+ unsigned const char* counter,
205
+ unsigned char* out) {
206
+ CipherCtxPointer ctx(EVP_CIPHER_CTX_new());
207
+ const bool encrypt = params.mode == AESCipherConfig::Mode::kEncrypt;
208
+
209
+ if (!EVP_CipherInit_ex(
210
+ ctx.get(),
211
+ params.cipher,
212
+ nullptr,
213
+ reinterpret_cast<const unsigned char*>(params.key->GetSymmetricKey().c_str()),
214
+ counter,
215
+ encrypt)) {
216
+ // Cipher init failed
217
+ return WebCryptoCipherStatus::FAILED;
218
+ }
219
+
220
+ int out_len = 0;
221
+ int final_len = 0;
222
+ if (!EVP_CipherUpdate(
223
+ ctx.get(),
224
+ out,
225
+ &out_len,
226
+ params.data.data<unsigned char>(),
227
+ params.data.size())) {
228
+ return WebCryptoCipherStatus::FAILED;
229
+ }
230
+
231
+ if (!EVP_CipherFinal_ex(ctx.get(), out + out_len, &final_len))
232
+ return WebCryptoCipherStatus::FAILED;
233
+
234
+ out_len += final_len;
235
+ if (static_cast<unsigned>(out_len) != params.data.size())
236
+ return WebCryptoCipherStatus::FAILED;
237
+
238
+ return WebCryptoCipherStatus::OK;
239
+ }
240
+
241
+ WebCryptoCipherStatus AES_CTR_Cipher(
242
+ const AESCipherConfig& params,
243
+ ByteSource* out) {
244
+ BignumPointer num_counters(BN_new());
245
+ if (!BN_lshift(num_counters.get(), BN_value_one(), params.length))
246
+ return WebCryptoCipherStatus::FAILED;
247
+
248
+ BignumPointer current_counter = GetCounter(params);
249
+
250
+ BignumPointer num_output(BN_new());
251
+
252
+ if (!BN_set_word(num_output.get(), CeilDiv(params.data.size(), kAesBlockSize)))
253
+ return WebCryptoCipherStatus::FAILED;
254
+
255
+ // Just like in chromium's implementation, if the counter will
256
+ // be incremented more than there are counter values, we fail.
257
+ if (BN_cmp(num_output.get(), num_counters.get()) > 0)
258
+ return WebCryptoCipherStatus::FAILED;
259
+
260
+ BignumPointer remaining_until_reset(BN_new());
261
+ if (!BN_sub(remaining_until_reset.get(),
262
+ num_counters.get(),
263
+ current_counter.get())) {
264
+ return WebCryptoCipherStatus::FAILED;
265
+ }
266
+
267
+ // Output size is identical to the input size.
268
+ ByteSource::Builder buf(params.data.size());
269
+
270
+ // Also just like in chromium's implementation, if we can process
271
+ // the input without wrapping the counter, we'll do it as a single
272
+ // call here. If we can't, we'll fallback to the a two-step approach
273
+ if (BN_cmp(remaining_until_reset.get(), num_output.get()) >= 0) {
274
+ auto status = AES_CTR_Cipher2(params,
275
+ params.data,
276
+ params.iv.data<unsigned char>(),
277
+ buf.data<unsigned char>());
278
+ if (status == WebCryptoCipherStatus::OK) *out = std::move(buf).release();
279
+ return status;
280
+ }
281
+
282
+ BN_ULONG blocks_part1 = BN_get_word(remaining_until_reset.get());
283
+ BN_ULONG input_size_part1 = blocks_part1 * kAesBlockSize;
284
+
285
+ // Encrypt the first part...
286
+ auto status =
287
+ AES_CTR_Cipher2(params,
288
+ ByteSource::Foreign(params.data.data<char>(), input_size_part1),
289
+ params.iv.data<unsigned char>(),
290
+ buf.data<unsigned char>());
291
+
292
+ if (status != WebCryptoCipherStatus::OK)
293
+ return status;
294
+
295
+ // Wrap the counter around to zero
296
+ std::vector<unsigned char> new_counter_block = BlockWithZeroedCounter(params);
297
+
298
+ // Encrypt the second part...
299
+ status =
300
+ AES_CTR_Cipher2(params,
301
+ ByteSource::Foreign(params.data.data<char>() + input_size_part1,
302
+ params.data.size() - input_size_part1),
303
+ new_counter_block.data(),
304
+ buf.data<unsigned char>() + input_size_part1);
305
+
306
+ if (status == WebCryptoCipherStatus::OK) *out = std::move(buf).release();
307
+
308
+ return status;
309
+ }
310
+
311
+ bool ValidateIV(
312
+ jsi::Runtime &rt,
313
+ const jsi::Value &value,
314
+ AESCipherConfig *params) {
315
+ params->iv = GetByteSourceFromJS(rt, value, "iv");
316
+ return true;
317
+ }
318
+
319
+ bool ValidateCounter(
320
+ jsi::Runtime &rt,
321
+ const jsi::Value &value,
322
+ AESCipherConfig* params) {
323
+ CHECK(CheckIsUint32(value)); // Length
324
+ params->length = (uint32_t)value.asNumber();
325
+ if (params->iv.size() != 16 ||
326
+ params->length == 0 ||
327
+ params->length > 128) {
328
+ throw std::runtime_error("Invalid counter (AES)");
329
+ return false;
330
+ }
331
+ return true;
332
+ }
333
+
334
+ bool ValidateAuthTag(
335
+ jsi::Runtime &rt,
336
+ AESCipherConfig::Mode cipher_mode,
337
+ const jsi::Value &value_len,
338
+ const jsi::Value &value_auth_tag,
339
+ AESCipherConfig *params) {
340
+ switch (cipher_mode) {
341
+ case AESCipherConfig::Mode::kDecrypt: {
342
+ ByteSource tag = GetByteSourceFromJS(rt, value_auth_tag, "auth_tag");
343
+ params->tag = std::move(tag);
344
+ break;
345
+ }
346
+ case AESCipherConfig::Mode::kEncrypt: {
347
+ CHECK(CheckIsUint32(value_len)); // Length
348
+ params->length = (uint32_t)value_len.asNumber();
349
+ if (params->length > 128) {
350
+ throw std::runtime_error("Invalid tag length (AES)");
351
+ return false;
352
+ }
353
+ break;
354
+ }
355
+ default:
356
+ throw std::runtime_error("Unreachable code in ValidateAuthTag (AES)");
357
+ }
358
+ return true;
359
+ }
360
+
361
+ bool ValidateAdditionalData(
362
+ jsi::Runtime &rt,
363
+ const jsi::Value &value,
364
+ AESCipherConfig *params) {
365
+ // Additional Data
366
+ params->additional_data = GetByteSourceFromJS(rt, value, "additional_data");
367
+ return true;
368
+ }
369
+
370
+ void UseDefaultIV(AESCipherConfig* params) {
371
+ params->iv = ByteSource::Foreign(kDefaultWrapIV, strlen(kDefaultWrapIV));
372
+ }
373
+
374
+ } // namespace
375
+
376
+ AESCipherConfig AESCipher::GetParamsFromJS(jsi::Runtime &rt,
377
+ const jsi::Value *args) {
378
+ AESCipherConfig params;
379
+ unsigned int offset = 0;
380
+
381
+ // mode (encrypt/decrypt)
382
+ AESCipherConfig::Mode mode =
383
+ static_cast<AESCipherConfig::Mode>(args[offset].getNumber());
384
+ params.mode = mode;
385
+ offset++;
386
+
387
+ // key (handle)
388
+ if (!args[offset].isObject()) {
389
+ throw std::runtime_error("arg is not a KeyObjectHandle: key");
390
+ }
391
+ std::shared_ptr<KeyObjectHandle> handle =
392
+ std::static_pointer_cast<KeyObjectHandle>(
393
+ args[offset].asObject(rt).getHostObject(rt));
394
+ params.key = handle->Data();
395
+ offset++;
396
+
397
+ // data
398
+ params.data = GetByteSourceFromJS(rt, args[offset], "data");
399
+ offset++;
400
+
401
+ // AES Key Variant
402
+ if (CheckIsInt32(args[offset])) {
403
+ params.variant = static_cast<AESKeyVariant>(args[offset].asNumber());
404
+ }
405
+ // offset++; // The below variant-dependent params advance offset themselves
406
+
407
+ // cipher
408
+ int cipher_nid;
409
+
410
+ switch (params.variant) {
411
+ case kKeyVariantAES_CTR_128:
412
+ if (!ValidateIV(rt, args[offset + 1], &params) ||
413
+ !ValidateCounter(rt, args[offset + 2], &params)) {
414
+ return params;
415
+ }
416
+ cipher_nid = NID_aes_128_ctr;
417
+ break;
418
+ case kKeyVariantAES_CTR_192:
419
+ if (!ValidateIV(rt, args[offset + 1], &params) ||
420
+ !ValidateCounter(rt, args[offset + 2], &params)) {
421
+ return params;
422
+ }
423
+ cipher_nid = NID_aes_192_ctr;
424
+ break;
425
+ case kKeyVariantAES_CTR_256:
426
+ if (!ValidateIV(rt, args[offset + 1], &params) ||
427
+ !ValidateCounter(rt, args[offset + 2], &params)) {
428
+ return params;
429
+ }
430
+ cipher_nid = NID_aes_256_ctr;
431
+ break;
432
+ case kKeyVariantAES_CBC_128:
433
+ if (!ValidateIV(rt, args[offset + 1], &params))
434
+ return params;
435
+ cipher_nid = NID_aes_128_cbc;
436
+ break;
437
+ case kKeyVariantAES_CBC_192:
438
+ if (!ValidateIV(rt, args[offset + 1], &params))
439
+ return params;
440
+ cipher_nid = NID_aes_192_cbc;
441
+ break;
442
+ case kKeyVariantAES_CBC_256:
443
+ if (!ValidateIV(rt, args[offset + 1], &params))
444
+ return params;
445
+ cipher_nid = NID_aes_256_cbc;
446
+ break;
447
+ case kKeyVariantAES_KW_128:
448
+ UseDefaultIV(&params);
449
+ cipher_nid = NID_id_aes128_wrap;
450
+ break;
451
+ case kKeyVariantAES_KW_192:
452
+ UseDefaultIV(&params);
453
+ cipher_nid = NID_id_aes192_wrap;
454
+ break;
455
+ case kKeyVariantAES_KW_256:
456
+ UseDefaultIV(&params);
457
+ cipher_nid = NID_id_aes256_wrap;
458
+ break;
459
+ case kKeyVariantAES_GCM_128:
460
+ if (!ValidateIV(rt, args[offset + 1], &params) ||
461
+ !ValidateAuthTag(rt, mode, args[offset + 2], args[offset + 3], &params) ||
462
+ !ValidateAdditionalData(rt, args[offset + 4], &params)) {
463
+ return params;
464
+ }
465
+ cipher_nid = NID_aes_128_gcm;
466
+ break;
467
+ case kKeyVariantAES_GCM_192:
468
+ if (!ValidateIV(rt, args[offset + 1], &params) ||
469
+ !ValidateAuthTag(rt, mode, args[offset + 2], args[offset + 3], &params) ||
470
+ !ValidateAdditionalData(rt, args[offset + 4], &params)) {
471
+ return params;
472
+ }
473
+ cipher_nid = NID_aes_192_gcm;
474
+ break;
475
+ case kKeyVariantAES_GCM_256:
476
+ if (!ValidateIV(rt, args[offset + 1], &params) ||
477
+ !ValidateAuthTag(rt, mode, args[offset + 2], args[offset + 3], &params) ||
478
+ !ValidateAdditionalData(rt, args[offset + 4], &params)) {
479
+ return params;
480
+ }
481
+ cipher_nid = NID_aes_256_gcm;
482
+ break;
483
+ default:
484
+ throw std::runtime_error("Unreachable code in GetParamsFromJS (AES)");
485
+ }
486
+
487
+ params.cipher = EVP_get_cipherbynid(cipher_nid);
488
+ if (params.cipher == nullptr) {
489
+ throw std::runtime_error("Unknown cipher (AES)");
490
+ return params;
491
+ }
492
+
493
+ if (params.iv.size() <
494
+ static_cast<size_t>(EVP_CIPHER_iv_length(params.cipher))) {
495
+ throw std::runtime_error("Invalid IV length (AES)");
496
+ return params;
497
+ }
498
+
499
+ return params;
500
+ }
501
+
502
+ WebCryptoCipherStatus AESCipher::DoCipher(const AESCipherConfig &params,
503
+ ByteSource *out) {
504
+ // TODO: threading / async here, as we don't have jsi::Runtime
505
+ #define V(name, fn) \
506
+ case kKeyVariantAES_ ## name: \
507
+ return fn(params, out);
508
+ switch (params.variant) {
509
+ VARIANTS(V)
510
+ default:
511
+ throw std::runtime_error("Unreachable code in DoCipher (AES)");
512
+ }
513
+ #undef V
514
+ }
515
+
516
+ } // namespace margelo
@@ -0,0 +1,79 @@
1
+ #ifndef crypto_aes_h
2
+ #define crypto_aes_h
3
+
4
+ #include <jsi/jsi.h>
5
+
6
+ #include "MGLKeys.h"
7
+ #ifdef ANDROID
8
+ #include "Utils/MGLUtils.h"
9
+ #else
10
+ #include "MGLUtils.h"
11
+ #endif
12
+
13
+ namespace margelo {
14
+
15
+ namespace jsi = facebook::jsi;
16
+
17
+ constexpr size_t kAesBlockSize = 16;
18
+ constexpr unsigned kNoAuthTagLength = static_cast<unsigned>(-1);
19
+ constexpr const char* kDefaultWrapIV = "\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6";
20
+
21
+ #define VARIANTS(V) \
22
+ V(CTR_128, AES_CTR_Cipher) \
23
+ V(CTR_192, AES_CTR_Cipher) \
24
+ V(CTR_256, AES_CTR_Cipher) \
25
+ V(CBC_128, AES_Cipher) \
26
+ V(CBC_192, AES_Cipher) \
27
+ V(CBC_256, AES_Cipher) \
28
+ V(GCM_128, AES_Cipher) \
29
+ V(GCM_192, AES_Cipher) \
30
+ V(GCM_256, AES_Cipher) \
31
+ V(KW_128, AES_Cipher) \
32
+ V(KW_192, AES_Cipher) \
33
+ V(KW_256, AES_Cipher)
34
+
35
+ enum AESKeyVariant {
36
+ #define V(name, _) kKeyVariantAES_ ## name,
37
+ VARIANTS(V)
38
+ #undef V
39
+ };
40
+
41
+ struct AESCipherConfig final {
42
+ enum Mode {
43
+ kEncrypt,
44
+ kDecrypt,
45
+ // kWrapKey,
46
+ // kUnwrapKey,
47
+ };
48
+
49
+ Mode mode;
50
+ AESKeyVariant variant;
51
+ std::shared_ptr<KeyObjectData> key;
52
+ ByteSource data;
53
+ const EVP_CIPHER* cipher;
54
+ ByteSource iv; // Used for both iv or counter
55
+ size_t length;
56
+ ByteSource tag; // Used only for authenticated modes (GCM)
57
+ ByteSource additional_data;
58
+
59
+ AESCipherConfig() = default;
60
+
61
+ // AESCipherConfig(AESCipherConfig&& other) noexcept;
62
+
63
+ // AESCipherConfig& operator=(AESCipherConfig&& other) noexcept;
64
+
65
+ // void MemoryInfo(MemoryTracker* tracker) const override;
66
+ // SET_MEMORY_INFO_NAME(AESCipherConfig)
67
+ // SET_SELF_SIZE(AESCipherConfig)
68
+ };
69
+
70
+ class AESCipher {
71
+ public:
72
+ AESCipher() {}
73
+ AESCipherConfig GetParamsFromJS(jsi::Runtime &rt, const jsi::Value *args);
74
+ WebCryptoCipherStatus DoCipher(const AESCipherConfig &params, ByteSource *out);
75
+ };
76
+
77
+ } // namespace margelo
78
+
79
+ #endif // crypto_aes_h
@@ -61,10 +61,10 @@ WebCryptoKeyExportStatus ECDH::doExport(jsi::Runtime &rt,
61
61
  switch (format) {
62
62
  case kWebCryptoKeyFormatRaw:
63
63
  return EC_Raw_Export(key_data.get(), params, out);
64
- // case kWebCryptoKeyFormatPKCS8:
65
- // if (key_data->GetKeyType() != kKeyTypePrivate)
66
- // return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
67
- // return PKEY_PKCS8_Export(key_data.get(), out);
64
+ case kWebCryptoKeyFormatPKCS8:
65
+ if (key_data->GetKeyType() != kKeyTypePrivate)
66
+ return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
67
+ return PKEY_PKCS8_Export(key_data.get(), out);
68
68
  case kWebCryptoKeyFormatSPKI: {
69
69
  if (key_data->GetKeyType() != kKeyTypePublic)
70
70
  throw std::runtime_error("Invalid type public to be exported");
@@ -122,22 +122,6 @@ WebCryptoKeyExportStatus ECDH::doExport(jsi::Runtime &rt,
122
122
  }
123
123
  }
124
124
 
125
- WebCryptoKeyExportStatus PKEY_SPKI_Export(KeyObjectData* key_data,
126
- ByteSource* out) {
127
- CHECK_EQ(key_data->GetKeyType(), kKeyTypePublic);
128
- ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
129
- // Mutex::ScopedLock lock(*m_pkey.mutex());
130
- BIOPointer bio(BIO_new(BIO_s_mem()));
131
- CHECK(bio);
132
- if (!i2d_PUBKEY_bio(bio.get(), m_pkey.get())) {
133
- throw std::runtime_error("Failed to export key");
134
- return WebCryptoKeyExportStatus::FAILED;
135
- }
136
-
137
- *out = ByteSource::FromBIO(bio);
138
- return WebCryptoKeyExportStatus::OK;
139
- }
140
-
141
125
  WebCryptoKeyExportStatus EC_Raw_Export(KeyObjectData* key_data,
142
126
  const ECKeyExportConfig& params,
143
127
  ByteSource* out) {
@@ -14,11 +14,9 @@
14
14
  #include "MGLKeys.h"
15
15
  #ifdef ANDROID
16
16
  #include "Utils/MGLUtils.h"
17
- #include "webcrypto/MGLWebCrypto.h"
18
17
  #include "JSIUtils/MGLJSIUtils.h"
19
18
  #else
20
19
  #include "MGLUtils.h"
21
- #include "MGLWebCrypto.h"
22
20
  #include "MGLJSIUtils.h"
23
21
  #endif
24
22
 
@@ -44,9 +42,6 @@ class ECDH final {
44
42
  ByteSource* out);
45
43
  };
46
44
 
47
- WebCryptoKeyExportStatus PKEY_SPKI_Export(KeyObjectData* key_data,
48
- ByteSource* out);
49
-
50
45
  WebCryptoKeyExportStatus EC_Raw_Export(KeyObjectData* key_data,
51
46
  const ECKeyExportConfig &params,
52
47
  ByteSource* out);