react-native-quick-crypto 0.7.0 → 0.7.1

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 (131) hide show
  1. package/android/CMakeLists.txt +2 -0
  2. package/cpp/Cipher/MGLRsa.cpp +179 -3
  3. package/cpp/Cipher/MGLRsa.h +40 -0
  4. package/cpp/JSIUtils/MGLJSIUtils.h +8 -0
  5. package/cpp/MGLKeys.cpp +41 -43
  6. package/cpp/MGLKeys.h +9 -2
  7. package/cpp/MGLQuickCryptoHostObject.cpp +6 -6
  8. package/cpp/Utils/MGLUtils.cpp +71 -1
  9. package/cpp/Utils/MGLUtils.h +55 -1
  10. package/cpp/webcrypto/MGLWebCrypto.cpp +89 -37
  11. package/cpp/webcrypto/MGLWebCrypto.h +5 -7
  12. package/cpp/webcrypto/crypto_aes.cpp +516 -0
  13. package/cpp/webcrypto/crypto_aes.h +79 -0
  14. package/cpp/webcrypto/crypto_ec.cpp +4 -20
  15. package/cpp/webcrypto/crypto_ec.h +0 -5
  16. package/cpp/webcrypto/crypto_keygen.cpp +86 -0
  17. package/cpp/webcrypto/crypto_keygen.h +38 -0
  18. package/lib/commonjs/Cipher.js +3 -1
  19. package/lib/commonjs/Cipher.js.map +1 -1
  20. package/lib/commonjs/Hashnames.js +20 -8
  21. package/lib/commonjs/Hashnames.js.map +1 -1
  22. package/lib/commonjs/NativeQuickCrypto/Cipher.js +13 -1
  23. package/lib/commonjs/NativeQuickCrypto/Cipher.js.map +1 -1
  24. package/lib/commonjs/NativeQuickCrypto/NativeQuickCrypto.js.map +1 -1
  25. package/lib/commonjs/NativeQuickCrypto/aes.js +6 -0
  26. package/lib/commonjs/NativeQuickCrypto/aes.js.map +1 -0
  27. package/lib/commonjs/NativeQuickCrypto/keygen.js +6 -0
  28. package/lib/commonjs/NativeQuickCrypto/keygen.js.map +1 -0
  29. package/lib/commonjs/NativeQuickCrypto/rsa.js +6 -0
  30. package/lib/commonjs/NativeQuickCrypto/rsa.js.map +1 -0
  31. package/lib/commonjs/Utils.js +30 -6
  32. package/lib/commonjs/Utils.js.map +1 -1
  33. package/lib/commonjs/aes.js +184 -227
  34. package/lib/commonjs/aes.js.map +1 -1
  35. package/lib/commonjs/index.js +12 -2
  36. package/lib/commonjs/index.js.map +1 -1
  37. package/lib/commonjs/keygen.js +56 -0
  38. package/lib/commonjs/keygen.js.map +1 -0
  39. package/lib/commonjs/keys.js +74 -5
  40. package/lib/commonjs/keys.js.map +1 -1
  41. package/lib/commonjs/rsa.js +115 -196
  42. package/lib/commonjs/rsa.js.map +1 -1
  43. package/lib/commonjs/sig.js.map +1 -1
  44. package/lib/commonjs/subtle.js +140 -78
  45. package/lib/commonjs/subtle.js.map +1 -1
  46. package/lib/commonjs/webcrypto.js +14 -0
  47. package/lib/commonjs/webcrypto.js.map +1 -0
  48. package/lib/module/Cipher.js +3 -1
  49. package/lib/module/Cipher.js.map +1 -1
  50. package/lib/module/Hashnames.js +20 -8
  51. package/lib/module/Hashnames.js.map +1 -1
  52. package/lib/module/NativeQuickCrypto/Cipher.js +12 -0
  53. package/lib/module/NativeQuickCrypto/Cipher.js.map +1 -1
  54. package/lib/module/NativeQuickCrypto/NativeQuickCrypto.js.map +1 -1
  55. package/lib/module/NativeQuickCrypto/aes.js +2 -0
  56. package/lib/module/NativeQuickCrypto/aes.js.map +1 -0
  57. package/lib/module/NativeQuickCrypto/keygen.js +2 -0
  58. package/lib/module/NativeQuickCrypto/keygen.js.map +1 -0
  59. package/lib/module/NativeQuickCrypto/rsa.js +2 -0
  60. package/lib/module/NativeQuickCrypto/rsa.js.map +1 -0
  61. package/lib/module/Utils.js +26 -5
  62. package/lib/module/Utils.js.map +1 -1
  63. package/lib/module/aes.js +183 -228
  64. package/lib/module/aes.js.map +1 -1
  65. package/lib/module/index.js +11 -2
  66. package/lib/module/index.js.map +1 -1
  67. package/lib/module/keygen.js +47 -0
  68. package/lib/module/keygen.js.map +1 -0
  69. package/lib/module/keys.js +68 -4
  70. package/lib/module/keys.js.map +1 -1
  71. package/lib/module/rsa.js +115 -198
  72. package/lib/module/rsa.js.map +1 -1
  73. package/lib/module/sig.js.map +1 -1
  74. package/lib/module/subtle.js +143 -82
  75. package/lib/module/subtle.js.map +1 -1
  76. package/lib/module/webcrypto.js +8 -0
  77. package/lib/module/webcrypto.js.map +1 -0
  78. package/lib/typescript/Cipher.d.ts +0 -1
  79. package/lib/typescript/Cipher.d.ts.map +1 -1
  80. package/lib/typescript/Hash.d.ts.map +1 -1
  81. package/lib/typescript/Hashnames.d.ts +2 -2
  82. package/lib/typescript/Hashnames.d.ts.map +1 -1
  83. package/lib/typescript/NativeQuickCrypto/Cipher.d.ts +5 -0
  84. package/lib/typescript/NativeQuickCrypto/Cipher.d.ts.map +1 -1
  85. package/lib/typescript/NativeQuickCrypto/NativeQuickCrypto.d.ts +4 -1
  86. package/lib/typescript/NativeQuickCrypto/NativeQuickCrypto.d.ts.map +1 -1
  87. package/lib/typescript/NativeQuickCrypto/aes.d.ts +5 -0
  88. package/lib/typescript/NativeQuickCrypto/aes.d.ts.map +1 -0
  89. package/lib/typescript/NativeQuickCrypto/keygen.d.ts +4 -0
  90. package/lib/typescript/NativeQuickCrypto/keygen.d.ts.map +1 -0
  91. package/lib/typescript/NativeQuickCrypto/rsa.d.ts +5 -0
  92. package/lib/typescript/NativeQuickCrypto/rsa.d.ts.map +1 -0
  93. package/lib/typescript/NativeQuickCrypto/webcrypto.d.ts +12 -2
  94. package/lib/typescript/NativeQuickCrypto/webcrypto.d.ts.map +1 -1
  95. package/lib/typescript/Utils.d.ts +4 -4
  96. package/lib/typescript/Utils.d.ts.map +1 -1
  97. package/lib/typescript/aes.d.ts +18 -1
  98. package/lib/typescript/aes.d.ts.map +1 -1
  99. package/lib/typescript/ec.d.ts.map +1 -1
  100. package/lib/typescript/index.d.ts +27 -24
  101. package/lib/typescript/index.d.ts.map +1 -1
  102. package/lib/typescript/keygen.d.ts +6 -0
  103. package/lib/typescript/keygen.d.ts.map +1 -0
  104. package/lib/typescript/keys.d.ts +55 -17
  105. package/lib/typescript/keys.d.ts.map +1 -1
  106. package/lib/typescript/rsa.d.ts +9 -1
  107. package/lib/typescript/rsa.d.ts.map +1 -1
  108. package/lib/typescript/sig.d.ts +3 -17
  109. package/lib/typescript/sig.d.ts.map +1 -1
  110. package/lib/typescript/subtle.d.ts +6 -5
  111. package/lib/typescript/subtle.d.ts.map +1 -1
  112. package/lib/typescript/webcrypto.d.ts +9 -0
  113. package/lib/typescript/webcrypto.d.ts.map +1 -0
  114. package/package.json +2 -2
  115. package/src/Cipher.ts +1 -1
  116. package/src/Hashnames.ts +23 -21
  117. package/src/NativeQuickCrypto/Cipher.ts +32 -0
  118. package/src/NativeQuickCrypto/NativeQuickCrypto.ts +6 -0
  119. package/src/NativeQuickCrypto/aes.ts +14 -0
  120. package/src/NativeQuickCrypto/keygen.ts +7 -0
  121. package/src/NativeQuickCrypto/rsa.ts +12 -0
  122. package/src/NativeQuickCrypto/webcrypto.ts +26 -2
  123. package/src/Utils.ts +37 -8
  124. package/src/aes.ts +259 -222
  125. package/src/index.ts +10 -1
  126. package/src/keygen.ts +80 -0
  127. package/src/keys.ts +139 -30
  128. package/src/rsa.ts +161 -187
  129. package/src/sig.ts +7 -23
  130. package/src/subtle.ts +211 -93
  131. package/src/webcrypto.ts +8 -0
@@ -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
@@ -184,8 +184,29 @@ ByteSource ByteSource::Foreign(const void* data, size_t size) {
184
184
  return ByteSource(data, nullptr, size);
185
185
  }
186
186
 
187
+ ByteSource ByteSource::FromBN(const BIGNUM* bn, size_t size) {
188
+ std::vector<uint8_t> buf(size);
189
+ CHECK_EQ(BN_bn2binpad(bn, buf.data(), size), size);
190
+ ByteSource::Builder out(size);
191
+ memcpy(out.data<void>(), buf.data(), size);
192
+ return std::move(out).release();
193
+ }
194
+
195
+ ByteSource GetByteSourceFromJS(jsi::Runtime &rt,
196
+ const jsi::Value &value,
197
+ std::string name) {
198
+ if (!value.isObject() || !value.asObject(rt).isArrayBuffer(rt)) {
199
+ throw jsi::JSError(rt, "arg is not an array buffer: " + name);
200
+ }
201
+ ByteSource data = ByteSource::FromStringOrBuffer(rt, value);
202
+ if (data.size() > INT_MAX) {
203
+ throw jsi::JSError(rt, "arg is too big (> int32): " + name);
204
+ }
205
+ return data;
206
+ }
207
+
187
208
  std::string EncodeBignum(const BIGNUM* bn,
188
- int size,
209
+ size_t size,
189
210
  bool url) {
190
211
  if (size == 0)
191
212
  size = BN_num_bytes(bn);
@@ -222,4 +243,53 @@ std::string DecodeBase64(const std::string &in, bool remove_linebreaks) {
222
243
  return base64_decode(in, remove_linebreaks);
223
244
  }
224
245
 
246
+ MUST_USE_RESULT CSPRNGResult CSPRNG(void* buffer, size_t length) {
247
+ unsigned char* buf = static_cast<unsigned char*>(buffer);
248
+ do {
249
+ if (1 == RAND_status()) {
250
+ #if OPENSSL_VERSION_MAJOR >= 3
251
+ if (1 == RAND_bytes_ex(nullptr, buf, length, 0)) return {true};
252
+ #else
253
+ while (length > INT_MAX && 1 == RAND_bytes(buf, INT_MAX)) {
254
+ buf += INT_MAX;
255
+ length -= INT_MAX;
256
+ }
257
+ if (length <= INT_MAX && 1 == RAND_bytes(buf, static_cast<int>(length)))
258
+ return {true};
259
+ #endif
260
+ }
261
+ #if OPENSSL_VERSION_MAJOR >= 3
262
+ const auto code = ERR_peek_last_error();
263
+ // A misconfigured OpenSSL 3 installation may report 1 from RAND_poll()
264
+ // and RAND_status() but fail in RAND_bytes() if it cannot look up
265
+ // a matching algorithm for the CSPRNG.
266
+ if (ERR_GET_LIB(code) == ERR_LIB_RAND) {
267
+ const auto reason = ERR_GET_REASON(code);
268
+ if (reason == RAND_R_ERROR_INSTANTIATING_DRBG ||
269
+ reason == RAND_R_UNABLE_TO_FETCH_DRBG ||
270
+ reason == RAND_R_UNABLE_TO_CREATE_DRBG) {
271
+ return {false};
272
+ }
273
+ }
274
+ #endif
275
+ } while (1 == RAND_poll());
276
+
277
+ return {false};
278
+ }
279
+
280
+ bool SetRsaOaepLabel(const EVPKeyCtxPointer& ctx, const ByteSource& label) {
281
+ if (label.size() != 0) {
282
+ // OpenSSL takes ownership of the label, so we need to create a copy.
283
+ void* label_copy = OPENSSL_memdup(label.data(), label.size());
284
+ CHECK_NOT_NULL(label_copy);
285
+ int ret = EVP_PKEY_CTX_set0_rsa_oaep_label(
286
+ ctx.get(), static_cast<unsigned char*>(label_copy), label.size());
287
+ if (ret <= 0) {
288
+ OPENSSL_free(label_copy);
289
+ return false;
290
+ }
291
+ }
292
+ return true;
293
+ }
294
+
225
295
  } // namespace margelo
@@ -50,6 +50,27 @@ using EVPMDPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
50
50
  using ECDSASigPointer = DeleteFnPtr<ECDSA_SIG, ECDSA_SIG_free>;
51
51
  using ECKeyPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
52
52
  using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
53
+ using CipherCtxPointer = DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
54
+
55
+
56
+ #ifdef __GNUC__
57
+ #define MUST_USE_RESULT __attribute__((warn_unused_result))
58
+ #else
59
+ #define MUST_USE_RESULT
60
+ #endif
61
+
62
+ struct CSPRNGResult {
63
+ const bool ok;
64
+ MUST_USE_RESULT bool is_ok() const { return ok; }
65
+ MUST_USE_RESULT bool is_err() const { return !ok; }
66
+ };
67
+
68
+ // Either succeeds with exactly |length| bytes of cryptographically
69
+ // strong pseudo-random data, or fails. This function may block.
70
+ // Don't assume anything about the contents of |buffer| on error.
71
+ // As a special case, |length == 0| can be used to check if the CSPRNG
72
+ // is properly seeded without consuming entropy.
73
+ MUST_USE_RESULT CSPRNGResult CSPRNG(void* buffer, size_t length);
53
74
 
54
75
  template <typename T>
55
76
  class NonCopyableMaybe {
@@ -198,6 +219,8 @@ class ByteSource {
198
219
 
199
220
  static ByteSource FromBIO(const BIOPointer& bio);
200
221
 
222
+ static ByteSource FromBN(const BIGNUM* bn, size_t size);
223
+
201
224
  // static ByteSource NullTerminatedCopy(Environment* env,
202
225
  // v8::Local<v8::Value> value);
203
226
  //
@@ -281,13 +304,19 @@ inline jsi::Value toJSI(jsi::Runtime& rt, ByteSource value) {
281
304
  return o;
282
305
  }
283
306
 
307
+ ByteSource GetByteSourceFromJS(jsi::Runtime &rt,
308
+ const jsi::Value &value,
309
+ std::string name);
310
+
284
311
  std::string EncodeBignum(const BIGNUM* bn,
285
- int size,
312
+ size_t size,
286
313
  bool url = false);
287
314
 
288
315
  std::string EncodeBase64(const std::string data, bool url = false);
289
316
  std::string DecodeBase64(const std::string &in, bool remove_linebreaks = false);
290
317
 
318
+ bool SetRsaOaepLabel(const EVPKeyCtxPointer& ctx, const ByteSource& label);
319
+
291
320
  // TODO: until shared, keep in sync with JS side (src/NativeQuickCrypto/Cipher.ts)
292
321
  enum KeyVariant {
293
322
  kvRSA_SSA_PKCS1_v1_5,
@@ -299,6 +328,31 @@ enum KeyVariant {
299
328
  kvDH,
300
329
  };
301
330
 
331
+ enum FnMode {
332
+ kAsync,
333
+ kSync,
334
+ };
335
+
336
+ enum WebCryptoKeyFormat {
337
+ kWebCryptoKeyFormatRaw,
338
+ kWebCryptoKeyFormatPKCS8,
339
+ kWebCryptoKeyFormatSPKI,
340
+ kWebCryptoKeyFormatJWK
341
+ };
342
+
343
+ enum WebCryptoCipherMode {
344
+ kEncrypt,
345
+ kDecrypt,
346
+ // kWrapKey,
347
+ // kUnwrapKey,
348
+ };
349
+
350
+ enum class WebCryptoCipherStatus {
351
+ OK,
352
+ INVALID_KEY_TYPE,
353
+ FAILED
354
+ };
355
+
302
356
  } // namespace margelo
303
357
 
304
358
  #endif /* MGLUtils_h */