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
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <a href="https://margelo.io">
2
- <img src="./img/banner.svg" width="100%" />
2
+ <img src="./docs/img/banner.svg" width="100%" />
3
3
  </a>
4
4
 
5
5
  # ⚡️ react-native-quick-crypto
@@ -42,7 +42,7 @@ Creating a Wallet took 289 ms
42
42
  ## Installation
43
43
 
44
44
  <h3>
45
- React Native  <a href="#"><img src="./img/react-native.png" height="15" /></a>
45
+ React Native  <a href="#"><img src="./docs/img/react-native.png" height="15" /></a>
46
46
  </h3>
47
47
 
48
48
  ```sh
@@ -51,7 +51,7 @@ cd ios && pod install
51
51
  ```
52
52
 
53
53
  <h3>
54
- Expo  <a href="#"><img src="./img/expo.png" height="12" /></a>
54
+ Expo  <a href="#"><img src="./docs/img/expo.png" height="12" /></a>
55
55
  </h3>
56
56
 
57
57
  ```sh
@@ -75,7 +75,7 @@ If you are using a library that depends on `crypto`, instead of polyfilling it w
75
75
 
76
76
  Use the [`resolveRequest`](https://facebook.github.io/metro/docs/resolution#resolverequest-customresolver) configuration option in your `metro.config.js`
77
77
 
78
- ```
78
+ ```js
79
79
  config.resolver.resolveRequest = (context, moduleName, platform) => {
80
80
  if (moduleName === 'crypto') {
81
81
  // when importing crypto, resolve to react-native-quick-crypto
@@ -133,67 +133,13 @@ const hashed = QuickCrypto.createHash('sha256')
133
133
  .digest('hex');
134
134
  ```
135
135
 
136
- ## Android build errors
137
-
138
- If you get an error similar to this:
139
-
140
- ```
141
- Execution failed for task ':app:mergeDebugNativeLibs'.
142
- > A failure occurred while executing com.android.build.gradle.internal.tasks.MergeNativeLibsTask$MergeNativeLibsTaskWorkAction
143
- > 2 files found with path 'lib/arm64-v8a/libcrypto.so' from inputs:
144
- - /Users/osp/Developer/mac_test/node_modules/react-native-quick-crypto/android/build/intermediates/library_jni/debug/jni/arm64-v8a/libcrypto.so
145
- - /Users/osp/.gradle/caches/transforms-3/e13f88164840fe641a466d05cd8edac7/transformed/jetified-flipper-0.182.0/jni/arm64-v8a/libcrypto.so
146
- ```
147
-
148
- It means you have a transitive dependency where two libraries depend on OpenSSL and are generating a `libcrypto.so` file. You can get around this issue by adding the following in your `app/build.gradle`:
149
-
150
- <h4>
151
- React Native  <a href="#"><img src="./img/react-native.png" height="15" /></a>
152
- </h4>
153
-
154
- `android/app/build.gradle` file
155
-
156
- ```groovy
157
- packagingOptions {
158
- // Should prevent clashes with other libraries that use OpenSSL
159
- pickFirst '**/libcrypto.so'
160
- }
161
- ```
162
-
163
- <h4>
164
- Expo  <a href="#"><img src="./img/expo.png" height="12" /></a>
165
- </h4>
166
-
167
- `app.json` file
168
-
169
- ```diff
170
- ...
171
- plugins: [
172
- ...
173
- + [
174
- + 'expo-build-properties',
175
- + {
176
- + android: {
177
- + packagingOptions: {
178
- + pickFirst: ['**/libcrypto.so'],
179
- + },
180
- + },
181
- + },
182
- + ],
183
- ],
184
- ```
185
-
186
- > This caused by flipper which also depends on OpenSSL
187
-
188
- This just tells Gradle to grab whatever OpenSSL version it finds first and link against that, but as you can imagine this is not correct if the packages depend on different OpenSSL versions (quick-crypto depends on `com.android.ndk.thirdparty:openssl:1.1.1q-beta-1`). You should make sure all the OpenSSL versions match and you have no conflicts or errors.
189
-
190
136
  ---
191
137
 
192
138
  ## Sponsors
193
139
 
194
140
  <!-- Onin -->
195
141
  <div align="center">
196
- <img height="50" src="./img/sponsors/onin.svg" align="center"><br/>
142
+ <img height="50" src="./docs/img/sponsors/onin.svg" align="center"><br/>
197
143
  <a href="https://onin.co"><b>Onin</b></a> - This library is supported by Onin. Plan events without leaving the chat: <a href="https://onin.co">onin.co</a>
198
144
  </div>
199
145
  <br/>
@@ -201,7 +147,7 @@ This just tells Gradle to grab whatever OpenSSL version it finds first and link
201
147
 
202
148
  <!-- Steakwallet -->
203
149
  <div align="center">
204
- <img height="37" src="./img/sponsors/omni.png" align="center"><br/>
150
+ <img height="37" src="./docs/img/sponsors/omni.png" align="center"><br/>
205
151
  <a href="https://steakwallet.fi"><b>Omni</b></a> - Web3 for all. Access all of Web3 in one easy to use wallet. Omni supports more blockchains so you get more tokens, more yields, more NFTs, and more fun!
206
152
  </div>
207
153
  <br/>
@@ -209,7 +155,7 @@ This just tells Gradle to grab whatever OpenSSL version it finds first and link
209
155
 
210
156
  <!-- Litentry -->
211
157
  <div align="center">
212
- <img height="70" src="./img/sponsors/litentry.png" align="center"><br/>
158
+ <img height="70" src="./docs/img/sponsors/litentry.png" align="center"><br/>
213
159
  <a href="https://litentry.com"><b>Litentry</b></a> - A decentralized identity aggregator, providing the structure and tools to empower you and your identity.
214
160
  </div>
215
161
  <br/>
@@ -217,7 +163,7 @@ This just tells Gradle to grab whatever OpenSSL version it finds first and link
217
163
 
218
164
  <!-- WalletConnect -->
219
165
  <div align="center">
220
- <img height="35" src="./img/sponsors/walletconnect.png" align="center"><br/>
166
+ <img height="35" src="./docs/img/sponsors/walletconnect.png" align="center"><br/>
221
167
  <a href="https://walletconnect.com"><b>WalletConnect</b></a> - The communications protocol for web3, WalletConnect brings the ecosystem together by enabling wallets and apps to securely connect and interact.
222
168
  </div>
223
169
  <br/>
@@ -227,7 +173,7 @@ This just tells Gradle to grab whatever OpenSSL version it finds first and link
227
173
 
228
174
  <!-- THORSwap -->
229
175
  <div align="center">
230
- <img height="40" src="./img/sponsors/thorswap.png" align="center"><br/>
176
+ <img height="40" src="./docs/img/sponsors/thorswap.png" align="center"><br/>
231
177
  <a href="https://thorswap.finance"><b>THORSwap</b></a> - THORSwap is a cross-chain DEX aggregator that enables users to swap native assets across chains, provide liquidity to earn yield, and more. THORSwap is fully permissionless and non-custodial. No account signup, your wallet, your keys, your coins.
232
178
  </div>
233
179
  <br/>
@@ -237,6 +183,8 @@ This just tells Gradle to grab whatever OpenSSL version it finds first and link
237
183
 
238
184
  As the library uses JSI for synchronous native methods access, remote debugging (e.g. with Chrome) is no longer possible. Instead, you should use [Flipper](https://fbflipper.com).
239
185
 
186
+ Not all cryptographic algorithms are supported yet. See the [implementation coverage](./docs/implementation-coverage.md) document for more details. If you need a specific algorithm, please open a `feature request` issue and we'll see what we can do.
187
+
240
188
  ## Community Discord
241
189
 
242
190
  [Join the Margelo Community Discord](https://discord.gg/6CSHz2qAvA) to chat about react-native-quick-crypto or other Margelo libraries.
@@ -43,7 +43,9 @@ add_library(
43
43
  "../cpp/Sig/MGLVerifyInstaller.cpp"
44
44
  "../cpp/Sig/MGLSignHostObjects.cpp"
45
45
  "../cpp/webcrypto/MGLWebCrypto.cpp"
46
+ "../cpp/webcrypto/crypto_aes.cpp"
46
47
  "../cpp/webcrypto/crypto_ec.cpp"
48
+ "../cpp/webcrypto/crypto_keygen.cpp"
47
49
  )
48
50
 
49
51
  target_include_directories(
@@ -7,9 +7,15 @@
7
7
 
8
8
  #include "MGLRsa.h"
9
9
  #ifdef ANDROID
10
+ #include "Cipher/MGLPublicCipher.h"
10
11
  #include "JSIUtils/MGLJSIMacros.h"
12
+ #include "JSIUtils/MGLJSIUtils.h"
13
+ #include "Utils/MGLUtils.h"
11
14
  #else
15
+ #include "MGLPublicCipher.h"
12
16
  #include "MGLJSIMacros.h"
17
+ #include "MGLJSIUtils.h"
18
+ #include "MGLUtils.h"
13
19
  #endif
14
20
 
15
21
  #include <string>
@@ -186,6 +192,125 @@ std::pair<jsi::Value, jsi::Value> generateRsaKeyPair(
186
192
  return {std::move(publicBuffer), std::move(privateBuffer)};
187
193
  }
188
194
 
195
+ template <MGLPublicCipher::EVP_PKEY_cipher_init_t init,
196
+ MGLPublicCipher::EVP_PKEY_cipher_t cipher>
197
+ WebCryptoCipherStatus RSA_Cipher(const RSACipherConfig& params, ByteSource* out) {
198
+ CHECK_NE(params.key->GetKeyType(), kKeyTypeSecret);
199
+ ManagedEVPPKey m_pkey = params.key->GetAsymmetricKey();
200
+ // Mutex::ScopedLock lock(*m_pkey.mutex());
201
+
202
+ EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(m_pkey.get(), nullptr));
203
+
204
+ if (!ctx || init(ctx.get()) <= 0)
205
+ return WebCryptoCipherStatus::FAILED;
206
+
207
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), params.padding) <= 0) {
208
+ return WebCryptoCipherStatus::FAILED;
209
+ }
210
+
211
+ if (params.digest != nullptr &&
212
+ (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), params.digest) <= 0 ||
213
+ EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), params.digest) <= 0)) {
214
+ return WebCryptoCipherStatus::FAILED;
215
+ }
216
+
217
+ if (!SetRsaOaepLabel(ctx, params.label)) return WebCryptoCipherStatus::FAILED;
218
+
219
+ size_t out_len = 0;
220
+ if (cipher(
221
+ ctx.get(),
222
+ nullptr,
223
+ &out_len,
224
+ params.data.data<unsigned char>(),
225
+ params.data.size()) <= 0) {
226
+ return WebCryptoCipherStatus::FAILED;
227
+ }
228
+
229
+ ByteSource::Builder buf(out_len);
230
+
231
+ if (cipher(ctx.get(),
232
+ buf.data<unsigned char>(),
233
+ &out_len,
234
+ params.data.data<unsigned char>(),
235
+ params.data.size()) <= 0) {
236
+ return WebCryptoCipherStatus::FAILED;
237
+ }
238
+
239
+ *out = std::move(buf).release(out_len);
240
+ return WebCryptoCipherStatus::OK;
241
+ }
242
+
243
+ RSACipherConfig RSACipher::GetParamsFromJS(jsi::Runtime &rt,
244
+ const jsi::Value *args) {
245
+ RSACipherConfig params;
246
+ unsigned int offset = 0;
247
+
248
+ // padding
249
+ params.padding = RSA_PKCS1_OAEP_PADDING;
250
+
251
+ // mode (encrypt/decrypt)
252
+ params.mode = static_cast<WebCryptoCipherMode>((int)args[offset].getNumber());
253
+ offset++;
254
+
255
+ // key (handle)
256
+ if (!args[offset].isObject()) {
257
+ throw std::runtime_error("arg is not a KeyObjectHandle: key");
258
+ }
259
+ std::shared_ptr<KeyObjectHandle> handle =
260
+ std::static_pointer_cast<KeyObjectHandle>(
261
+ args[offset].asObject(rt).getHostObject(rt));
262
+ params.key = handle->Data();
263
+ offset++;
264
+
265
+ // data
266
+ params.data = GetByteSourceFromJS(rt, args[offset], "data");
267
+ offset++;
268
+
269
+ // variant
270
+ if (CheckIsInt32(args[offset])) {
271
+ params.variant = static_cast<RSAKeyVariant>((int)args[offset].getNumber());
272
+ }
273
+ // offset++; // The below variant-dependent params advance offset themselves
274
+
275
+ std::string digest;
276
+ switch (params.variant) {
277
+ case kKeyVariantRSA_OAEP:
278
+ // hash (digest)
279
+ CHECK(args[offset + 1].isString());
280
+ digest = args[offset + 1].asString(rt).utf8(rt);
281
+ params.digest = EVP_get_digestbyname(digest.c_str());
282
+ if (params.digest == nullptr) {
283
+ throw jsi::JSError(rt, "invalid digest: " + digest);
284
+ return params;
285
+ }
286
+
287
+ // label
288
+ if (args[offset + 2].isUndefined()) {
289
+ params.label = ByteSource();
290
+ } else {
291
+ params.label = GetByteSourceFromJS(rt, args[offset + 2], "label");
292
+ }
293
+
294
+ break;
295
+ default:
296
+ throw jsi::JSError(rt, "Invalid RSA key variant");
297
+ }
298
+
299
+ return params;
300
+ }
301
+
302
+ WebCryptoCipherStatus RSACipher::DoCipher(const RSACipherConfig &params,
303
+ ByteSource *out) {
304
+ switch (params.mode) {
305
+ case kEncrypt:
306
+ CHECK_EQ(params.key->GetKeyType(), kKeyTypePublic);
307
+ return RSA_Cipher<EVP_PKEY_encrypt_init, EVP_PKEY_encrypt>(params, out);
308
+ case kDecrypt:
309
+ CHECK_EQ(params.key->GetKeyType(), kKeyTypePrivate);
310
+ return RSA_Cipher<EVP_PKEY_decrypt_init, EVP_PKEY_decrypt>(params, out);
311
+ }
312
+ }
313
+
189
314
  jsi::Value ExportJWKRsaKey(jsi::Runtime &rt,
190
315
  std::shared_ptr<KeyObjectData> key,
191
316
  jsi::Object &target) {
@@ -330,12 +455,11 @@ jsi::Value GetRsaKeyDetail(jsi::Runtime &rt,
330
455
  RSA_get0_key(rsa, &n, &e, nullptr);
331
456
 
332
457
  size_t modulus_length = BN_num_bits(n);
333
- // TODO: should this be modulusLength or n?
334
458
  target.setProperty(rt, "modulusLength", static_cast<double>(modulus_length));
335
459
 
336
460
  size_t exp_size = BN_num_bytes(e);
337
- // TODO: should this be publicExponent or e?
338
- target.setProperty(rt, "publicExponent", EncodeBignum(e, exp_size, true));
461
+ ByteSource public_exponent = ByteSource::FromBN(e, exp_size);
462
+ target.setProperty(rt, "publicExponent", toJSI(rt, std::move(public_exponent)));
339
463
 
340
464
  if (type == EVP_PKEY_RSA_PSS) {
341
465
  // Due to the way ASN.1 encoding works, default values are omitted when
@@ -394,4 +518,56 @@ jsi::Value GetRsaKeyDetail(jsi::Runtime &rt,
394
518
  return target;
395
519
  }
396
520
 
521
+ bool RsaKeyExport::GetParamsFromJS(jsi::Runtime &rt, const jsi::Value *args) {
522
+ RsaKeyExportConfig params;
523
+ unsigned int offset = 0;
524
+
525
+ // format
526
+ params.format = static_cast<WebCryptoKeyFormat>((int)args[offset].getNumber());
527
+ offset++;
528
+
529
+ // key
530
+ std::shared_ptr<KeyObjectHandle> handle =
531
+ std::static_pointer_cast<KeyObjectHandle>(
532
+ args[1].asObject(rt).getHostObject(rt));
533
+ params.key_ = handle->Data();
534
+ offset++;
535
+
536
+ // variant
537
+ params.variant = static_cast<KeyVariant>((int)args[offset].getNumber());
538
+ offset++;
539
+
540
+ this->params_ = std::move(params);
541
+ return true;
542
+ }
543
+
544
+ WebCryptoKeyExportStatus RsaKeyExport::DoExport(ByteSource* out) {
545
+ auto key_data = this->params_.key_;
546
+ CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
547
+
548
+ switch (this->params_.format) {
549
+ case kWebCryptoKeyFormatRaw:
550
+ throw std::runtime_error("Raw format not supported for RSA keys");
551
+ return WebCryptoKeyExportStatus::FAILED;
552
+ case kWebCryptoKeyFormatJWK:
553
+ throw std::runtime_error("JWK format not handled in C++ for RSA keys");
554
+ return WebCryptoKeyExportStatus::FAILED;
555
+ case kWebCryptoKeyFormatPKCS8:
556
+ if (key_data->GetKeyType() != kKeyTypePrivate) {
557
+ throw std::runtime_error("Invalid key type for PKCS8 export");
558
+ return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
559
+ }
560
+ return PKEY_PKCS8_Export(key_data.get(), out);
561
+ case kWebCryptoKeyFormatSPKI:
562
+ if (key_data->GetKeyType() != kKeyTypePublic) {
563
+ throw std::runtime_error("Invalid key type for SPKI export");
564
+ return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
565
+ }
566
+ return PKEY_SPKI_Export(key_data.get(), out);
567
+ default:
568
+ throw std::runtime_error("Unrecognized format for RSA key export");
569
+ return WebCryptoKeyExportStatus::FAILED;
570
+ }
571
+ }
572
+
397
573
  } // namespace margelo
@@ -25,6 +25,13 @@ namespace margelo {
25
25
 
26
26
  namespace jsi = facebook::jsi;
27
27
 
28
+ // TODO: keep in in sync with JS side (src/rsa.ts)
29
+ enum RSAKeyVariant {
30
+ kKeyVariantRSA_SSA_PKCS1_v1_5,
31
+ kKeyVariantRSA_PSS,
32
+ kKeyVariantRSA_OAEP
33
+ };
34
+
28
35
  // On node there is a complete madness of structs/classes that encapsulate and
29
36
  // initialize the data in a generic manner this is to be later be used to
30
37
  // generate the keys in a thread-safe manner (I think) I'm however too dumb and
@@ -64,6 +71,39 @@ std::shared_ptr<KeyObjectData> ImportJWKRsaKey(jsi::Runtime &rt,
64
71
  jsi::Value GetRsaKeyDetail(jsi::Runtime &rt,
65
72
  std::shared_ptr<KeyObjectData> key);
66
73
 
74
+ struct RsaKeyExportConfig final {
75
+ WebCryptoKeyFormat format;
76
+ std::shared_ptr<KeyObjectData> key_;
77
+ KeyVariant variant;
78
+ };
79
+
80
+ class RsaKeyExport {
81
+ public:
82
+ bool GetParamsFromJS(jsi::Runtime &rt, const jsi::Value *args);
83
+ WebCryptoKeyExportStatus DoExport(ByteSource* out);
84
+ private:
85
+ RsaKeyExportConfig params_;
86
+ };
87
+
88
+ struct RSACipherConfig final {
89
+ WebCryptoCipherMode mode;
90
+ std::shared_ptr<KeyObjectData> key;
91
+ ByteSource data;
92
+ RSAKeyVariant variant;
93
+ ByteSource label;
94
+ int padding = 0;
95
+ const EVP_MD* digest = nullptr;
96
+
97
+ RSACipherConfig() = default;
98
+ };
99
+
100
+ class RSACipher {
101
+ public:
102
+ RSACipher() {}
103
+ RSACipherConfig GetParamsFromJS(jsi::Runtime &rt, const jsi::Value *args);
104
+ WebCryptoCipherStatus DoCipher(const RSACipherConfig &params, ByteSource *out);
105
+ };
106
+
67
107
  } // namespace margelo
68
108
 
69
109
  #endif /* MGLRsa_hpp */
@@ -30,4 +30,12 @@ inline bool CheckIsInt32(const jsi::Value &value) {
30
30
  return (d >= std::numeric_limits<int32_t>::lowest() && d <= std::numeric_limits<int32_t>::max());
31
31
  }
32
32
 
33
+ inline bool CheckIsUint32(const jsi::Value &value) {
34
+ if (!value.isNumber()) {
35
+ return false;
36
+ }
37
+ double d = value.asNumber();
38
+ return (d >= std::numeric_limits<uint32_t>::lowest() && d <= std::numeric_limits<uint32_t>::max());
39
+ }
40
+
33
41
  #endif /* MGLJSIUtils_h */
package/cpp/MGLKeys.cpp CHANGED
@@ -562,7 +562,7 @@ jsi::Value ManagedEVPPKey::ToEncodedPublicKey(jsi::Runtime& rt,
562
562
  // Note that this has the downside of containing sensitive data of the
563
563
  // private key.
564
564
  auto data = KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(key));
565
- auto handle = KeyObjectHandle::Create(rt, data);
565
+ auto handle = KeyObjectHandle::Create(data);
566
566
  auto out = jsi::Object::createFromHostObject(rt, handle);
567
567
  return jsi::Value(std::move(out));
568
568
  } else
@@ -583,7 +583,7 @@ jsi::Value ManagedEVPPKey::ToEncodedPrivateKey(jsi::Runtime& rt,
583
583
  if (!key) return {};
584
584
  if (config.output_key_object_) {
585
585
  auto data = KeyObjectData::CreateAsymmetric(kKeyTypePrivate, std::move(key));
586
- auto handle = KeyObjectHandle::Create(rt, data);
586
+ auto handle = KeyObjectHandle::Create(data);
587
587
  auto out = jsi::Object::createFromHostObject(rt, handle);
588
588
  return jsi::Value(std::move(out));
589
589
  } else
@@ -696,6 +696,7 @@ ManagedEVPPKey ManagedEVPPKey::GetPublicOrPrivateKeyFromJs(
696
696
  const jsi::Value* args,
697
697
  unsigned int* offset) {
698
698
  if (args[*offset].asObject(runtime).isArrayBuffer(runtime)) {
699
+ // data (key) as ArrayBuffer
699
700
  auto dataArrayBuffer =
700
701
  args[(*offset)++].asObject(runtime).getArrayBuffer(runtime);
701
702
 
@@ -703,6 +704,7 @@ ManagedEVPPKey ManagedEVPPKey::GetPublicOrPrivateKeyFromJs(
703
704
  throw jsi::JSError(runtime, "data is too big");
704
705
  }
705
706
 
707
+ // rest of args for encoding
706
708
  NonCopyableMaybe<PrivateKeyEncodingConfig> config_ =
707
709
  GetPrivateKeyEncodingFromJs(runtime, args, offset, kKeyContextInput);
708
710
  if (config_.IsEmpty()) return ManagedEVPPKey();
@@ -907,8 +909,7 @@ jsi::Value KeyObjectHandle::get(
907
909
  // registry->Register(Equals);
908
910
  // }
909
911
 
910
- std::shared_ptr<KeyObjectHandle> KeyObjectHandle::Create(jsi::Runtime &rt,
911
- std::shared_ptr<KeyObjectData> data) {
912
+ std::shared_ptr<KeyObjectHandle> KeyObjectHandle::Create(std::shared_ptr<KeyObjectData> data) {
912
913
  auto handle = std::make_shared<KeyObjectHandle>();
913
914
  handle->data_ = data;
914
915
  return handle;
@@ -932,7 +933,7 @@ const std::shared_ptr<KeyObjectData>& KeyObjectHandle::Data() {
932
933
  //
933
934
 
934
935
  jsi::Value KeyObjectHandle::Init(jsi::Runtime &rt) {
935
- return HOSTFN("init", 2) {
936
+ return HOSTFN("init", 5) {
936
937
  CHECK(args[0].isNumber());
937
938
  KeyType type = static_cast<KeyType>((int32_t)args[0].asNumber());
938
939
 
@@ -941,15 +942,13 @@ jsi::Value KeyObjectHandle::Init(jsi::Runtime &rt) {
941
942
 
942
943
  switch (type) {
943
944
  case kKeyTypeSecret: {
944
- // CHECK_EQ(args.Length(), 2);
945
-
945
+ CHECK_EQ(count, 2);
946
946
  ByteSource key = ByteSource::FromStringOrBuffer(rt, args[1]);
947
947
  this->data_ = KeyObjectData::CreateSecret(std::move(key));
948
948
  break;
949
949
  }
950
950
  case kKeyTypePublic: {
951
- // CHECK_EQ(args.Length(), 5);
952
-
951
+ CHECK_EQ(count, 5);
953
952
  offset = 1;
954
953
  pkey = ManagedEVPPKey::GetPublicOrPrivateKeyFromJs(rt, args, &offset);
955
954
  if (!pkey)
@@ -958,8 +957,7 @@ jsi::Value KeyObjectHandle::Init(jsi::Runtime &rt) {
958
957
  break;
959
958
  }
960
959
  case kKeyTypePrivate: {
961
- // CHECK_EQ(args.Length(), 5);
962
-
960
+ CHECK_EQ(count, 5);
963
961
  offset = 1;
964
962
  pkey = ManagedEVPPKey::GetPrivateKeyFromJs(rt, args, &offset, false);
965
963
  if (!pkey)
@@ -1167,7 +1165,7 @@ jsi::Value KeyObjectHandle::GetAsymmetricKeyType(jsi::Runtime &rt) const {
1167
1165
  std::string ret;
1168
1166
  switch (EVP_PKEY_id(key.get())) {
1169
1167
  case EVP_PKEY_RSA:
1170
- ret = "rss";
1168
+ ret = "rsa";
1171
1169
  break;
1172
1170
  case EVP_PKEY_RSA_PSS:
1173
1171
  ret = "rsa_pss";
@@ -1389,37 +1387,37 @@ jsi::Value KeyObjectHandle::ExportJWK(jsi::Runtime &rt) {
1389
1387
  // return std::make_unique<KeyObjectTransferData>(handle_data_);
1390
1388
  //}
1391
1389
  //
1392
- // WebCryptoKeyExportStatus PKEY_SPKI_Export(
1393
- // KeyObjectData* key_data,
1394
- // ByteSource* out) {
1395
- // CHECK_EQ(key_data->GetKeyType(), kKeyTypePublic);
1396
- // ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
1397
- // Mutex::ScopedLock lock(*m_pkey.mutex());
1398
- // BIOPointer bio(BIO_new(BIO_s_mem()));
1399
- // CHECK(bio);
1400
- // if (!i2d_PUBKEY_bio(bio.get(), m_pkey.get()))
1401
- // return WebCryptoKeyExportStatus::FAILED;
1402
- //
1403
- // *out = ByteSource::FromBIO(bio);
1404
- // return WebCryptoKeyExportStatus::OK;
1405
- //}
1406
- //
1407
- // WebCryptoKeyExportStatus PKEY_PKCS8_Export(
1408
- // KeyObjectData* key_data,
1409
- // ByteSource* out) {
1410
- // CHECK_EQ(key_data->GetKeyType(), kKeyTypePrivate);
1411
- // ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
1412
- // Mutex::ScopedLock lock(*m_pkey.mutex());
1413
- //
1414
- // BIOPointer bio(BIO_new(BIO_s_mem()));
1415
- // CHECK(bio);
1416
- // PKCS8Pointer p8inf(EVP_PKEY2PKCS8(m_pkey.get()));
1417
- // if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), p8inf.get()))
1418
- // return WebCryptoKeyExportStatus::FAILED;
1419
- //
1420
- // *out = ByteSource::FromBIO(bio);
1421
- // return WebCryptoKeyExportStatus::OK;
1422
- //}
1390
+ WebCryptoKeyExportStatus PKEY_SPKI_Export(KeyObjectData* key_data,
1391
+ ByteSource* out) {
1392
+ CHECK_EQ(key_data->GetKeyType(), kKeyTypePublic);
1393
+ ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
1394
+ // Mutex::ScopedLock lock(*m_pkey.mutex());
1395
+ BIOPointer bio(BIO_new(BIO_s_mem()));
1396
+ CHECK(bio);
1397
+ if (!i2d_PUBKEY_bio(bio.get(), m_pkey.get())) {
1398
+ throw std::runtime_error("Failed to export key");
1399
+ return WebCryptoKeyExportStatus::FAILED;
1400
+ }
1401
+
1402
+ *out = ByteSource::FromBIO(bio);
1403
+ return WebCryptoKeyExportStatus::OK;
1404
+ }
1405
+
1406
+ WebCryptoKeyExportStatus PKEY_PKCS8_Export(
1407
+ KeyObjectData* key_data,
1408
+ ByteSource* out) {
1409
+ CHECK_EQ(key_data->GetKeyType(), kKeyTypePrivate);
1410
+ ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
1411
+ // Mutex::ScopedLock lock(*m_pkey.mutex());
1412
+ BIOPointer bio(BIO_new(BIO_s_mem()));
1413
+ CHECK(bio);
1414
+ PKCS8Pointer p8inf(EVP_PKEY2PKCS8(m_pkey.get()));
1415
+ if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), p8inf.get()))
1416
+ return WebCryptoKeyExportStatus::FAILED;
1417
+
1418
+ *out = ByteSource::FromBIO(bio);
1419
+ return WebCryptoKeyExportStatus::OK;
1420
+ }
1423
1421
 
1424
1422
  // void RegisterExternalReferences(ExternalReferenceRegistry * registry) {
1425
1423
  // KeyObjectHandle::RegisterExternalReferences(registry);
package/cpp/MGLKeys.h CHANGED
@@ -168,8 +168,7 @@ class JSI_EXPORT KeyObjectHandle: public jsi::HostObject {
168
168
  jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propNameID);
169
169
  const std::shared_ptr<KeyObjectData>& Data();
170
170
 
171
- static std::shared_ptr<KeyObjectHandle> Create(jsi::Runtime &rt,
172
- std::shared_ptr<KeyObjectData> data);
171
+ static std::shared_ptr<KeyObjectHandle> Create(std::shared_ptr<KeyObjectData> data);
173
172
 
174
173
  protected:
175
174
  jsi::Value Export(jsi::Runtime &rt);
@@ -191,6 +190,14 @@ class JSI_EXPORT KeyObjectHandle: public jsi::HostObject {
191
190
  std::shared_ptr<KeyObjectData> data_;
192
191
  };
193
192
 
193
+ WebCryptoKeyExportStatus PKEY_SPKI_Export(
194
+ KeyObjectData* key_data,
195
+ ByteSource* out);
196
+
197
+ WebCryptoKeyExportStatus PKEY_PKCS8_Export(
198
+ KeyObjectData* key_data,
199
+ ByteSource* out);
200
+
194
201
  } // namespace margelo
195
202
 
196
203
  #endif /* MGLCipherKeys_h */
@@ -113,12 +113,12 @@ MGLQuickCryptoHostObject::MGLQuickCryptoHostObject(
113
113
  // createVerify
114
114
  this->fields.push_back(getVerifyFieldDefinition(jsCallInvoker, workerQueue));
115
115
 
116
- // subtle API created from a simple jsi::Object
117
- // because this FieldDefinition is only good for returning
118
- // objects and too convoluted
119
- this->fields.push_back(JSI_VALUE("webcrypto", {
120
- return createWebCryptoObject(runtime);
121
- }));
116
+ // subtle API
117
+ this->fields.push_back(JSI_VALUE("webcrypto", {
118
+ auto hostObject = std::make_shared<MGLWebCryptoHostObject>(
119
+ jsCallInvoker, workerQueue);
120
+ return jsi::Object::createFromHostObject(runtime, hostObject);
121
+ }));
122
122
  }
123
123
 
124
124
  } // namespace margelo