react-native-quick-crypto 1.0.11 → 1.0.13

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 (197) hide show
  1. package/android/CMakeLists.txt +7 -0
  2. package/cpp/cipher/CCMCipher.cpp +4 -1
  3. package/cpp/cipher/ChaCha20Cipher.cpp +3 -1
  4. package/cpp/cipher/ChaCha20Poly1305Cipher.cpp +5 -5
  5. package/cpp/cipher/ChaCha20Poly1305Cipher.hpp +1 -2
  6. package/cpp/cipher/HybridCipher.cpp +10 -1
  7. package/cpp/cipher/HybridCipher.hpp +2 -0
  8. package/cpp/cipher/HybridRsaCipher.cpp +0 -13
  9. package/cpp/cipher/XChaCha20Poly1305Cipher.cpp +7 -5
  10. package/cpp/cipher/XChaCha20Poly1305Cipher.hpp +1 -2
  11. package/cpp/cipher/XSalsa20Cipher.cpp +4 -0
  12. package/cpp/cipher/XSalsa20Poly1305Cipher.cpp +7 -5
  13. package/cpp/cipher/XSalsa20Poly1305Cipher.hpp +1 -2
  14. package/cpp/ecdh/HybridECDH.cpp +20 -133
  15. package/cpp/keys/HybridKeyObjectHandle.cpp +144 -141
  16. package/cpp/keys/HybridKeyObjectHandle.hpp +6 -3
  17. package/cpp/keys/KeyObjectData.hpp +2 -0
  18. package/cpp/kmac/HybridKmac.cpp +83 -0
  19. package/cpp/kmac/HybridKmac.hpp +31 -0
  20. package/cpp/mldsa/HybridMlDsaKeyPair.cpp +11 -20
  21. package/cpp/mldsa/HybridMlDsaKeyPair.hpp +4 -2
  22. package/cpp/mlkem/HybridMlKemKeyPair.cpp +319 -0
  23. package/cpp/mlkem/HybridMlKemKeyPair.hpp +48 -0
  24. package/cpp/sign/SignUtils.hpp +9 -26
  25. package/cpp/utils/QuickCryptoUtils.cpp +44 -0
  26. package/cpp/utils/QuickCryptoUtils.hpp +39 -0
  27. package/cpp/x509/HybridX509Certificate.cpp +174 -0
  28. package/cpp/x509/HybridX509Certificate.hpp +51 -0
  29. package/lib/commonjs/cipher.js +15 -2
  30. package/lib/commonjs/cipher.js.map +1 -1
  31. package/lib/commonjs/dhKeyPair.js +3 -3
  32. package/lib/commonjs/dhKeyPair.js.map +1 -1
  33. package/lib/commonjs/dsa.js +3 -3
  34. package/lib/commonjs/dsa.js.map +1 -1
  35. package/lib/commonjs/ec.js +18 -18
  36. package/lib/commonjs/ec.js.map +1 -1
  37. package/lib/commonjs/ed.js +9 -9
  38. package/lib/commonjs/ed.js.map +1 -1
  39. package/lib/commonjs/hash.js +17 -12
  40. package/lib/commonjs/hash.js.map +1 -1
  41. package/lib/commonjs/hkdf.js.map +1 -1
  42. package/lib/commonjs/index.js +22 -0
  43. package/lib/commonjs/index.js.map +1 -1
  44. package/lib/commonjs/keys/classes.js +2 -2
  45. package/lib/commonjs/keys/classes.js.map +1 -1
  46. package/lib/commonjs/keys/index.js +24 -0
  47. package/lib/commonjs/keys/index.js.map +1 -1
  48. package/lib/commonjs/keys/publicCipher.js +2 -2
  49. package/lib/commonjs/keys/publicCipher.js.map +1 -1
  50. package/lib/commonjs/keys/signVerify.js +0 -2
  51. package/lib/commonjs/keys/signVerify.js.map +1 -1
  52. package/lib/commonjs/mlkem.js +219 -0
  53. package/lib/commonjs/mlkem.js.map +1 -0
  54. package/lib/commonjs/pbkdf2.js +18 -1
  55. package/lib/commonjs/pbkdf2.js.map +1 -1
  56. package/lib/commonjs/rsa.js +7 -7
  57. package/lib/commonjs/rsa.js.map +1 -1
  58. package/lib/commonjs/specs/kmac.nitro.js +6 -0
  59. package/lib/commonjs/specs/kmac.nitro.js.map +1 -0
  60. package/lib/commonjs/specs/mlKemKeyPair.nitro.js +6 -0
  61. package/lib/commonjs/specs/mlKemKeyPair.nitro.js.map +1 -0
  62. package/lib/commonjs/specs/x509certificate.nitro.js +6 -0
  63. package/lib/commonjs/specs/x509certificate.nitro.js.map +1 -0
  64. package/lib/commonjs/subtle.js +292 -112
  65. package/lib/commonjs/subtle.js.map +1 -1
  66. package/lib/commonjs/utils/conversion.js +3 -3
  67. package/lib/commonjs/utils/conversion.js.map +1 -1
  68. package/lib/commonjs/utils/hashnames.js +31 -0
  69. package/lib/commonjs/utils/hashnames.js.map +1 -1
  70. package/lib/commonjs/utils/types.js.map +1 -1
  71. package/lib/commonjs/x509certificate.js +189 -0
  72. package/lib/commonjs/x509certificate.js.map +1 -0
  73. package/lib/module/cipher.js +16 -3
  74. package/lib/module/cipher.js.map +1 -1
  75. package/lib/module/dhKeyPair.js +1 -1
  76. package/lib/module/dhKeyPair.js.map +1 -1
  77. package/lib/module/dsa.js +1 -1
  78. package/lib/module/dsa.js.map +1 -1
  79. package/lib/module/ec.js +6 -6
  80. package/lib/module/ec.js.map +1 -1
  81. package/lib/module/ed.js +1 -1
  82. package/lib/module/ed.js.map +1 -1
  83. package/lib/module/hash.js +17 -12
  84. package/lib/module/hash.js.map +1 -1
  85. package/lib/module/hkdf.js.map +1 -1
  86. package/lib/module/index.js +6 -0
  87. package/lib/module/index.js.map +1 -1
  88. package/lib/module/keys/classes.js +2 -2
  89. package/lib/module/keys/classes.js.map +1 -1
  90. package/lib/module/keys/index.js +25 -1
  91. package/lib/module/keys/index.js.map +1 -1
  92. package/lib/module/keys/publicCipher.js +2 -2
  93. package/lib/module/keys/publicCipher.js.map +1 -1
  94. package/lib/module/keys/signVerify.js +0 -2
  95. package/lib/module/keys/signVerify.js.map +1 -1
  96. package/lib/module/mlkem.js +211 -0
  97. package/lib/module/mlkem.js.map +1 -0
  98. package/lib/module/pbkdf2.js +18 -1
  99. package/lib/module/pbkdf2.js.map +1 -1
  100. package/lib/module/rsa.js +1 -1
  101. package/lib/module/rsa.js.map +1 -1
  102. package/lib/module/specs/kmac.nitro.js +4 -0
  103. package/lib/module/specs/kmac.nitro.js.map +1 -0
  104. package/lib/module/specs/mlKemKeyPair.nitro.js +4 -0
  105. package/lib/module/specs/mlKemKeyPair.nitro.js.map +1 -0
  106. package/lib/module/specs/x509certificate.nitro.js +4 -0
  107. package/lib/module/specs/x509certificate.nitro.js.map +1 -0
  108. package/lib/module/subtle.js +292 -112
  109. package/lib/module/subtle.js.map +1 -1
  110. package/lib/module/utils/conversion.js +3 -4
  111. package/lib/module/utils/conversion.js.map +1 -1
  112. package/lib/module/utils/hashnames.js +31 -0
  113. package/lib/module/utils/hashnames.js.map +1 -1
  114. package/lib/module/utils/types.js.map +1 -1
  115. package/lib/module/x509certificate.js +184 -0
  116. package/lib/module/x509certificate.js.map +1 -0
  117. package/lib/tsconfig.tsbuildinfo +1 -1
  118. package/lib/typescript/cipher.d.ts +3 -0
  119. package/lib/typescript/cipher.d.ts.map +1 -1
  120. package/lib/typescript/dhKeyPair.d.ts +1 -1
  121. package/lib/typescript/dhKeyPair.d.ts.map +1 -1
  122. package/lib/typescript/dsa.d.ts +1 -1
  123. package/lib/typescript/dsa.d.ts.map +1 -1
  124. package/lib/typescript/ec.d.ts +1 -1
  125. package/lib/typescript/ec.d.ts.map +1 -1
  126. package/lib/typescript/ed.d.ts +1 -1
  127. package/lib/typescript/ed.d.ts.map +1 -1
  128. package/lib/typescript/hash.d.ts.map +1 -1
  129. package/lib/typescript/hkdf.d.ts +2 -6
  130. package/lib/typescript/hkdf.d.ts.map +1 -1
  131. package/lib/typescript/index.d.ts +15 -4
  132. package/lib/typescript/index.d.ts.map +1 -1
  133. package/lib/typescript/keys/classes.d.ts +5 -5
  134. package/lib/typescript/keys/classes.d.ts.map +1 -1
  135. package/lib/typescript/keys/index.d.ts +2 -2
  136. package/lib/typescript/keys/index.d.ts.map +1 -1
  137. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  138. package/lib/typescript/mlkem.d.ts +30 -0
  139. package/lib/typescript/mlkem.d.ts.map +1 -0
  140. package/lib/typescript/pbkdf2.d.ts +2 -2
  141. package/lib/typescript/pbkdf2.d.ts.map +1 -1
  142. package/lib/typescript/rsa.d.ts +1 -1
  143. package/lib/typescript/rsa.d.ts.map +1 -1
  144. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts +1 -0
  145. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts.map +1 -1
  146. package/lib/typescript/specs/kmac.nitro.d.ts +10 -0
  147. package/lib/typescript/specs/kmac.nitro.d.ts.map +1 -0
  148. package/lib/typescript/specs/mlKemKeyPair.nitro.d.ts +18 -0
  149. package/lib/typescript/specs/mlKemKeyPair.nitro.d.ts.map +1 -0
  150. package/lib/typescript/specs/x509certificate.nitro.d.ts +34 -0
  151. package/lib/typescript/specs/x509certificate.nitro.d.ts.map +1 -0
  152. package/lib/typescript/subtle.d.ts +10 -0
  153. package/lib/typescript/subtle.d.ts.map +1 -1
  154. package/lib/typescript/utils/conversion.d.ts.map +1 -1
  155. package/lib/typescript/utils/hashnames.d.ts +1 -1
  156. package/lib/typescript/utils/hashnames.d.ts.map +1 -1
  157. package/lib/typescript/utils/types.d.ts +13 -7
  158. package/lib/typescript/utils/types.d.ts.map +1 -1
  159. package/lib/typescript/x509certificate.d.ts +64 -0
  160. package/lib/typescript/x509certificate.d.ts.map +1 -0
  161. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +3 -0
  162. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +30 -0
  163. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +30 -0
  164. package/nitrogen/generated/shared/c++/AsymmetricKeyType.hpp +12 -0
  165. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.cpp +1 -0
  166. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.hpp +1 -0
  167. package/nitrogen/generated/shared/c++/HybridKmacSpec.cpp +23 -0
  168. package/nitrogen/generated/shared/c++/HybridKmacSpec.hpp +66 -0
  169. package/nitrogen/generated/shared/c++/HybridMlKemKeyPairSpec.cpp +31 -0
  170. package/nitrogen/generated/shared/c++/HybridMlKemKeyPairSpec.hpp +74 -0
  171. package/nitrogen/generated/shared/c++/HybridX509CertificateHandleSpec.cpp +46 -0
  172. package/nitrogen/generated/shared/c++/HybridX509CertificateHandleSpec.hpp +96 -0
  173. package/package.json +4 -1
  174. package/src/cipher.ts +17 -3
  175. package/src/dhKeyPair.ts +1 -1
  176. package/src/dsa.ts +1 -1
  177. package/src/ec.ts +9 -9
  178. package/src/ed.ts +2 -2
  179. package/src/hash.ts +34 -11
  180. package/src/hkdf.ts +2 -7
  181. package/src/index.ts +7 -0
  182. package/src/keys/classes.ts +10 -9
  183. package/src/keys/index.ts +37 -2
  184. package/src/keys/publicCipher.ts +2 -2
  185. package/src/keys/signVerify.ts +0 -5
  186. package/src/mlkem.ts +350 -0
  187. package/src/pbkdf2.ts +34 -5
  188. package/src/rsa.ts +1 -1
  189. package/src/specs/keyObjectHandle.nitro.ts +5 -0
  190. package/src/specs/kmac.nitro.ts +12 -0
  191. package/src/specs/mlKemKeyPair.nitro.ts +32 -0
  192. package/src/specs/x509certificate.nitro.ts +38 -0
  193. package/src/subtle.ts +551 -125
  194. package/src/utils/conversion.ts +10 -4
  195. package/src/utils/hashnames.ts +33 -2
  196. package/src/utils/types.ts +42 -5
  197. package/src/x509certificate.ts +277 -0
@@ -5,6 +5,7 @@
5
5
  #include "HybridKeyObjectHandle.hpp"
6
6
  #include "QuickCryptoUtils.hpp"
7
7
  #include <openssl/bn.h>
8
+ #include <openssl/core_names.h>
8
9
  #include <openssl/crypto.h>
9
10
  #include <openssl/ec.h>
10
11
  #include <openssl/evp.h>
@@ -91,9 +92,9 @@ std::shared_ptr<ArrayBuffer> HybridKeyObjectHandle::exportKey(std::optional<KFor
91
92
  const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
92
93
  auto keyType = data_.GetKeyType();
93
94
 
94
- // Handle secret keys
95
+ // Copy to avoid JSI ArrayBuffer GC issues. See #645.
95
96
  if (keyType == KeyType::SECRET) {
96
- return data_.GetSymmetricKey();
97
+ return ToNativeArrayBuffer(data_.GetSymmetricKey());
97
98
  }
98
99
 
99
100
  // Handle asymmetric keys (public/private)
@@ -127,22 +128,39 @@ std::shared_ptr<ArrayBuffer> HybridKeyObjectHandle::exportKey(std::optional<KFor
127
128
 
128
129
  // For EC keys, handle raw format (uncompressed point)
129
130
  if (!format.has_value() && !type.has_value() && keyId == EVP_PKEY_EC && keyType == KeyType::PUBLIC) {
130
- const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
131
- if (!ec_key)
132
- throw std::runtime_error("Failed to get EC key");
133
- const EC_GROUP* group = EC_KEY_get0_group(ec_key);
134
- const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
135
- if (!group || !point)
136
- throw std::runtime_error("Failed to get EC public key");
137
- size_t len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
138
- if (len == 0)
139
- throw std::runtime_error("Failed to get EC point size");
131
+ size_t len = 0;
132
+ if (EVP_PKEY_get_octet_string_param(pkey.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &len) != 1 || len == 0)
133
+ throw std::runtime_error("Failed to get EC public key size");
140
134
  std::vector<uint8_t> buf(len);
141
- if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, buf.data(), len, nullptr) == 0) {
142
- throw std::runtime_error("Failed to encode EC public key");
135
+ if (EVP_PKEY_get_octet_string_param(pkey.get(), OSSL_PKEY_PARAM_PUB_KEY, buf.data(), buf.size(), &len) != 1)
136
+ throw std::runtime_error("Failed to get EC public key");
137
+ return ToNativeArrayBuffer(std::string(reinterpret_cast<const char*>(buf.data()), len));
138
+ }
139
+
140
+ #if OPENSSL_VERSION_NUMBER >= 0x30500000L
141
+ if (!format.has_value() && !type.has_value()) {
142
+ const char* typeName = EVP_PKEY_get0_type_name(pkey.get());
143
+ if (typeName != nullptr) {
144
+ std::string name(typeName);
145
+ bool isPqcKey = (name.starts_with("ML-KEM-") || name.starts_with("ML-DSA-"));
146
+ if (isPqcKey) {
147
+ if (keyType == KeyType::PUBLIC) {
148
+ auto rawData = pkey.rawPublicKey();
149
+ if (!rawData) {
150
+ throw std::runtime_error("Failed to get raw PQC public key");
151
+ }
152
+ return ToNativeArrayBuffer(std::string(reinterpret_cast<const char*>(rawData.get()), rawData.size()));
153
+ } else {
154
+ auto rawData = pkey.rawSeed();
155
+ if (!rawData) {
156
+ throw std::runtime_error("Failed to get raw PQC seed");
157
+ }
158
+ return ToNativeArrayBuffer(std::string(reinterpret_cast<const char*>(rawData.get()), rawData.size()));
159
+ }
160
+ }
143
161
  }
144
- return ToNativeArrayBuffer(std::string(reinterpret_cast<const char*>(buf.data()), buf.size()));
145
162
  }
163
+ #endif
146
164
 
147
165
  // Set default format and type if not provided
148
166
  auto exportFormat = format.value_or(KFormatType::DER);
@@ -258,54 +276,50 @@ JWK HybridKeyObjectHandle::exportJwk(const JWK& key, bool handleRsaPss) {
258
276
 
259
277
  // Export EC keys
260
278
  if (keyId == EVP_PKEY_EC) {
261
- const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey.get());
262
- if (!ec)
263
- throw std::runtime_error("Failed to get EC key");
279
+ char curve_name_buf[64];
280
+ size_t name_len = 0;
281
+ if (EVP_PKEY_get_utf8_string_param(pkey.get(), OSSL_PKEY_PARAM_GROUP_NAME, curve_name_buf, sizeof(curve_name_buf), &name_len) != 1)
282
+ throw std::runtime_error("Failed to get EC group name");
264
283
 
265
- const EC_GROUP* group = EC_KEY_get0_group(ec);
266
- if (!group)
267
- throw std::runtime_error("Failed to get EC group");
284
+ std::string curve_name(curve_name_buf, name_len);
268
285
 
269
- int nid = EC_GROUP_get_curve_name(group);
270
- const char* curve_name = OBJ_nid2sn(nid);
271
- if (!curve_name)
272
- throw std::runtime_error("Unknown curve");
273
-
274
- // Get the field size in bytes for proper padding
275
- size_t field_size = (EC_GROUP_get_degree(group) + 7) / 8;
286
+ int bits = EVP_PKEY_bits(pkey.get());
287
+ if (bits <= 0)
288
+ throw std::runtime_error("Failed to get EC key size");
289
+ size_t field_size = (static_cast<size_t>(bits) + 7) / 8;
276
290
 
277
291
  result.kty = JWKkty::EC;
278
292
 
279
293
  // Map OpenSSL curve names to JWK curve names
280
- if (strcmp(curve_name, "prime256v1") == 0) {
294
+ if (curve_name == "prime256v1") {
281
295
  result.crv = "P-256";
282
- } else if (strcmp(curve_name, "secp384r1") == 0) {
296
+ } else if (curve_name == "secp384r1") {
283
297
  result.crv = "P-384";
284
- } else if (strcmp(curve_name, "secp521r1") == 0) {
298
+ } else if (curve_name == "secp521r1") {
285
299
  result.crv = "P-521";
286
300
  } else {
287
301
  result.crv = curve_name;
288
302
  }
289
303
 
290
- const EC_POINT* pub_key = EC_KEY_get0_public_key(ec);
291
- if (pub_key) {
292
- BIGNUM* x_bn = BN_new();
293
- BIGNUM* y_bn = BN_new();
294
-
295
- if (EC_POINT_get_affine_coordinates(group, pub_key, x_bn, y_bn, nullptr) == 1) {
296
- result.x = bn_to_base64url(x_bn, field_size);
297
- result.y = bn_to_base64url(y_bn, field_size);
298
- }
299
-
304
+ BIGNUM* x_bn = nullptr;
305
+ BIGNUM* y_bn = nullptr;
306
+ if (EVP_PKEY_get_bn_param(pkey.get(), OSSL_PKEY_PARAM_EC_PUB_X, &x_bn) != 1 ||
307
+ EVP_PKEY_get_bn_param(pkey.get(), OSSL_PKEY_PARAM_EC_PUB_Y, &y_bn) != 1) {
300
308
  BN_free(x_bn);
301
309
  BN_free(y_bn);
310
+ throw std::runtime_error("Failed to get EC public key coordinates");
302
311
  }
312
+ result.x = bn_to_base64url(x_bn, field_size);
313
+ result.y = bn_to_base64url(y_bn, field_size);
314
+ BN_free(x_bn);
315
+ BN_free(y_bn);
303
316
 
304
317
  // Export private key if this is a private key
305
318
  if (keyType == KeyType::PRIVATE) {
306
- const BIGNUM* priv_key = EC_KEY_get0_private_key(ec);
307
- if (priv_key) {
308
- result.d = bn_to_base64url(priv_key, field_size);
319
+ BIGNUM* priv_bn = nullptr;
320
+ if (EVP_PKEY_get_bn_param(pkey.get(), OSSL_PKEY_PARAM_PRIV_KEY, &priv_bn) == 1 && priv_bn) {
321
+ result.d = bn_to_base64url(priv_bn, field_size);
322
+ BN_free(priv_bn);
309
323
  }
310
324
  }
311
325
 
@@ -389,8 +403,25 @@ AsymmetricKeyType HybridKeyObjectHandle::getAsymmetricKeyType() {
389
403
  return AsymmetricKeyType::ML_DSA_87;
390
404
  #endif
391
405
  default:
392
- throw std::runtime_error("Unsupported asymmetric key type");
406
+ break;
393
407
  }
408
+
409
+ #if OPENSSL_VERSION_NUMBER >= 0x30500000L
410
+ // EVP_PKEY_id returns -1 for provider-only key types (e.g. ML-KEM)
411
+ // Fall back to string-based type name comparison
412
+ const char* typeName = EVP_PKEY_get0_type_name(pkey.get());
413
+ if (typeName != nullptr) {
414
+ std::string name(typeName);
415
+ if (name == "ML-KEM-512")
416
+ return AsymmetricKeyType::ML_KEM_512;
417
+ if (name == "ML-KEM-768")
418
+ return AsymmetricKeyType::ML_KEM_768;
419
+ if (name == "ML-KEM-1024")
420
+ return AsymmetricKeyType::ML_KEM_1024;
421
+ }
422
+ #endif
423
+
424
+ throw std::runtime_error("Unsupported asymmetric key type");
394
425
  }
395
426
 
396
427
  bool HybridKeyObjectHandle::init(KeyType keyType, const std::variant<std::shared_ptr<ArrayBuffer>, std::string>& key,
@@ -574,81 +605,54 @@ std::optional<KeyType> HybridKeyObjectHandle::initJwk(const JWK& keyData, std::o
574
605
 
575
606
  std::string crv = keyData.crv.value();
576
607
 
577
- // Map JWK curve names to OpenSSL NIDs
578
- int nid;
608
+ // Map JWK curve names to OpenSSL group names and field sizes
609
+ const char* group_name;
610
+ size_t field_size;
579
611
  if (crv == "P-256") {
580
- nid = NID_X9_62_prime256v1;
612
+ group_name = "prime256v1";
613
+ field_size = 32;
581
614
  } else if (crv == "P-384") {
582
- nid = NID_secp384r1;
615
+ group_name = "secp384r1";
616
+ field_size = 48;
583
617
  } else if (crv == "P-521") {
584
- nid = NID_secp521r1;
618
+ group_name = "secp521r1";
619
+ field_size = 66;
585
620
  } else {
586
621
  throw std::runtime_error("Unsupported EC curve: " + crv);
587
622
  }
588
623
 
589
- // Create EC_KEY
590
- EC_KEY* ec = EC_KEY_new_by_curve_name(nid);
591
- if (!ec)
592
- throw std::runtime_error("Failed to create EC key");
593
-
594
- const EC_GROUP* group = EC_KEY_get0_group(ec);
595
-
596
624
  // Decode public key coordinates
597
625
  BIGNUM* x_bn = base64url_to_bn(keyData.x.value());
598
626
  BIGNUM* y_bn = base64url_to_bn(keyData.y.value());
599
-
600
627
  if (!x_bn || !y_bn) {
601
- EC_KEY_free(ec);
602
- throw std::runtime_error("Failed to decode EC public key coordinates");
603
- }
604
-
605
- // Set public key
606
- EC_POINT* pub_key = EC_POINT_new(group);
607
- if (!pub_key || EC_POINT_set_affine_coordinates(group, pub_key, x_bn, y_bn, nullptr) != 1) {
608
628
  BN_free(x_bn);
609
629
  BN_free(y_bn);
610
- if (pub_key)
611
- EC_POINT_free(pub_key);
612
- EC_KEY_free(ec);
613
- throw std::runtime_error("Failed to set EC public key");
630
+ throw std::runtime_error("Failed to decode EC public key coordinates");
614
631
  }
615
632
 
633
+ // Build uncompressed point: 0x04 || x_padded || y_padded
634
+ std::vector<uint8_t> pub_oct(1 + 2 * field_size, 0);
635
+ pub_oct[0] = 0x04;
636
+ BN_bn2binpad(x_bn, pub_oct.data() + 1, static_cast<int>(field_size));
637
+ BN_bn2binpad(y_bn, pub_oct.data() + 1 + field_size, static_cast<int>(field_size));
616
638
  BN_free(x_bn);
617
639
  BN_free(y_bn);
618
640
 
619
- if (EC_KEY_set_public_key(ec, pub_key) != 1) {
620
- EC_POINT_free(pub_key);
621
- EC_KEY_free(ec);
622
- throw std::runtime_error("Failed to set EC public key on EC_KEY");
623
- }
624
-
625
- EC_POINT_free(pub_key);
626
-
627
- // Set private key if present
641
+ BIGNUM* d_bn = nullptr;
628
642
  if (isPrivate) {
629
- BIGNUM* d_bn = base64url_to_bn(keyData.d.value());
630
- if (!d_bn) {
631
- EC_KEY_free(ec);
643
+ d_bn = base64url_to_bn(keyData.d.value());
644
+ if (!d_bn)
632
645
  throw std::runtime_error("Failed to decode EC private key");
633
- }
634
-
635
- if (EC_KEY_set_private_key(ec, d_bn) != 1) {
636
- BN_free(d_bn);
637
- EC_KEY_free(ec);
638
- throw std::runtime_error("Failed to set EC private key");
639
- }
640
-
641
- BN_free(d_bn);
642
646
  }
643
647
 
644
- // Create EVP_PKEY from EC_KEY
645
- EVP_PKEY* pkey = EVP_PKEY_new();
646
- if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
647
- EC_KEY_free(ec);
648
- if (pkey)
649
- EVP_PKEY_free(pkey);
650
- throw std::runtime_error("Failed to create EVP_PKEY from EC_KEY");
648
+ EVP_PKEY* pkey = nullptr;
649
+ try {
650
+ pkey = createEcEvpPkey(group_name, pub_oct.data(), pub_oct.size(), d_bn);
651
+ } catch (...) {
652
+ BN_free(d_bn);
653
+ throw;
651
654
  }
655
+ BN_free(d_bn);
652
656
 
653
657
  KeyType type = isPrivate ? KeyType::PRIVATE : KeyType::PUBLIC;
654
658
  data_ = KeyObjectData::CreateAsymmetric(type, ncrypto::EVPKeyPointer(pkey));
@@ -733,20 +737,11 @@ KeyDetail HybridKeyObjectHandle::keyDetail() {
733
737
  }
734
738
 
735
739
  if (keyType == EVP_PKEY_EC) {
736
- // Extract EC curve name
737
- EC_KEY* ec_key = EVP_PKEY_get1_EC_KEY(pkey);
738
- if (ec_key) {
739
- const EC_GROUP* group = EC_KEY_get0_group(ec_key);
740
- if (group) {
741
- int nid = EC_GROUP_get_curve_name(group);
742
- const char* curve_name = OBJ_nid2sn(nid);
743
- if (curve_name) {
744
- std::string namedCurve(curve_name);
745
- EC_KEY_free(ec_key);
746
- return KeyDetail(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, namedCurve);
747
- }
748
- }
749
- EC_KEY_free(ec_key);
740
+ char curve_name[64];
741
+ size_t name_len = 0;
742
+ if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve_name, sizeof(curve_name), &name_len) == 1) {
743
+ return KeyDetail(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
744
+ std::string(curve_name, name_len));
750
745
  }
751
746
  }
752
747
 
@@ -812,48 +807,56 @@ bool HybridKeyObjectHandle::initECRaw(const std::string& namedCurve, const std::
812
807
  throw std::runtime_error("Unknown curve: " + namedCurve);
813
808
  }
814
809
 
815
- // Create EC_GROUP for the curve
816
- ncrypto::ECGroupPointer group = ncrypto::ECGroupPointer::NewByCurveName(nid);
817
- if (!group) {
818
- throw std::runtime_error("Failed to create EC_GROUP for curve");
810
+ // Get the OpenSSL group name for this curve
811
+ const char* group_name = OBJ_nid2sn(nid);
812
+ if (!group_name) {
813
+ throw std::runtime_error("Failed to get curve name for NID");
819
814
  }
820
815
 
821
- // Create EC_POINT from raw bytes
822
- ncrypto::ECPointPointer point = ncrypto::ECPointPointer::New(group.get());
823
- if (!point) {
824
- throw std::runtime_error("Failed to create EC_POINT");
825
- }
816
+ EVP_PKEY* pkey = createEcEvpPkey(group_name, keyData->data(), keyData->size());
817
+ this->data_ = KeyObjectData::CreateAsymmetric(KeyType::PUBLIC, ncrypto::EVPKeyPointer(pkey));
818
+ return true;
819
+ }
826
820
 
827
- // Convert raw bytes to EC_POINT
828
- ncrypto::Buffer<const unsigned char> buffer{.data = reinterpret_cast<const unsigned char*>(keyData->data()), .len = keyData->size()};
821
+ bool HybridKeyObjectHandle::initPqcRaw(const std::string& algorithmName, const std::shared_ptr<ArrayBuffer>& keyData, bool isPublic) {
822
+ #if OPENSSL_VERSION_NUMBER >= 0x30500000L
823
+ data_ = KeyObjectData();
829
824
 
830
- if (!point.setFromBuffer(buffer, group.get())) {
831
- throw std::runtime_error("Failed to read DER asymmetric key");
832
- }
825
+ int nid = 0;
826
+ if (algorithmName == "ML-KEM-512")
827
+ nid = EVP_PKEY_ML_KEM_512;
828
+ else if (algorithmName == "ML-KEM-768")
829
+ nid = EVP_PKEY_ML_KEM_768;
830
+ else if (algorithmName == "ML-KEM-1024")
831
+ nid = EVP_PKEY_ML_KEM_1024;
832
+ else if (algorithmName == "ML-DSA-44")
833
+ nid = EVP_PKEY_ML_DSA_44;
834
+ else if (algorithmName == "ML-DSA-65")
835
+ nid = EVP_PKEY_ML_DSA_65;
836
+ else if (algorithmName == "ML-DSA-87")
837
+ nid = EVP_PKEY_ML_DSA_87;
838
+ else
839
+ throw std::runtime_error("Unknown PQC algorithm: " + algorithmName);
833
840
 
834
- // Create EC_KEY and set the public key
835
- ncrypto::ECKeyPointer ec = ncrypto::ECKeyPointer::New(group.get());
836
- if (!ec) {
837
- throw std::runtime_error("Failed to create EC_KEY");
838
- }
841
+ ncrypto::Buffer<const unsigned char> buffer{.data = reinterpret_cast<const unsigned char*>(keyData->data()), .len = keyData->size()};
839
842
 
840
- if (!ec.setPublicKey(point)) {
841
- throw std::runtime_error("Failed to set public key on EC_KEY");
843
+ ncrypto::EVPKeyPointer pkey;
844
+ if (isPublic) {
845
+ pkey = ncrypto::EVPKeyPointer::NewRawPublic(nid, buffer);
846
+ } else {
847
+ pkey = ncrypto::EVPKeyPointer::NewRawSeed(nid, buffer);
842
848
  }
843
849
 
844
- // Create EVP_PKEY from EC_KEY
845
- ncrypto::EVPKeyPointer pkey = ncrypto::EVPKeyPointer::New();
846
850
  if (!pkey) {
847
- throw std::runtime_error("Failed to create EVP_PKEY");
848
- }
849
-
850
- if (!pkey.set(ec)) {
851
- throw std::runtime_error("Failed to assign EC_KEY to EVP_PKEY");
851
+ return false;
852
852
  }
853
853
 
854
- // Store as public key
855
- this->data_ = KeyObjectData::CreateAsymmetric(KeyType::PUBLIC, std::move(pkey));
854
+ auto keyType = isPublic ? KeyType::PUBLIC : KeyType::PRIVATE;
855
+ this->data_ = KeyObjectData::CreateAsymmetric(keyType, std::move(pkey));
856
856
  return true;
857
+ #else
858
+ throw std::runtime_error("PQC raw key import requires OpenSSL 3.5+");
859
+ #endif
857
860
  }
858
861
 
859
862
  bool HybridKeyObjectHandle::keyEquals(const std::shared_ptr<HybridKeyObjectHandleSpec>& other) {
@@ -31,6 +31,8 @@ class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
31
31
 
32
32
  bool initECRaw(const std::string& namedCurve, const std::shared_ptr<ArrayBuffer>& keyData) override;
33
33
 
34
+ bool initPqcRaw(const std::string& algorithmName, const std::shared_ptr<ArrayBuffer>& keyData, bool isPublic) override;
35
+
34
36
  std::optional<KeyType> initJwk(const JWK& keyData, std::optional<NamedCurve> namedCurve) override;
35
37
 
36
38
  KeyDetail keyDetail() override;
@@ -39,13 +41,14 @@ class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
39
41
 
40
42
  double getSymmetricKeySize() override;
41
43
 
42
- KeyObjectData& getKeyObjectData() {
43
- return data_;
44
- }
45
44
  const KeyObjectData& getKeyObjectData() const {
46
45
  return data_;
47
46
  }
48
47
 
48
+ void setKeyObjectData(KeyObjectData data) {
49
+ data_ = std::move(data);
50
+ }
51
+
49
52
  private:
50
53
  KeyObjectData data_;
51
54
 
@@ -1,3 +1,5 @@
1
+ #pragma once
2
+
1
3
  #include <memory>
2
4
 
3
5
  #include <NitroModules/ArrayBuffer.hpp>
@@ -0,0 +1,83 @@
1
+ #include <NitroModules/ArrayBuffer.hpp>
2
+ #include <memory>
3
+ #include <openssl/core_names.h>
4
+ #include <openssl/err.h>
5
+ #include <openssl/evp.h>
6
+ #include <string>
7
+ #include <vector>
8
+
9
+ #include "HybridKmac.hpp"
10
+
11
+ namespace margelo::nitro::crypto {
12
+
13
+ void HybridKmac::createKmac(const std::string& algorithm, const std::shared_ptr<ArrayBuffer>& key, double outputLength,
14
+ const std::optional<std::shared_ptr<ArrayBuffer>>& customization) {
15
+ outputLen = static_cast<size_t>(outputLength);
16
+ if (outputLen == 0) {
17
+ throw std::runtime_error("KMAC output length must be greater than 0");
18
+ }
19
+
20
+ std::unique_ptr<EVP_MAC, decltype(&EVP_MAC_free)> mac(EVP_MAC_fetch(nullptr, algorithm.c_str(), nullptr), EVP_MAC_free);
21
+ if (!mac) {
22
+ throw std::runtime_error("Failed to fetch " + algorithm + " implementation: " + std::to_string(ERR_get_error()));
23
+ }
24
+
25
+ ctx.reset(EVP_MAC_CTX_new(mac.get()));
26
+ if (!ctx) {
27
+ throw std::runtime_error("Failed to create KMAC context: " + std::to_string(ERR_get_error()));
28
+ }
29
+
30
+ OSSL_PARAM params[3];
31
+ size_t paramCount = 0;
32
+
33
+ params[paramCount++] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outputLen);
34
+
35
+ std::vector<uint8_t> custData;
36
+ if (customization.has_value() && customization.value()->size() > 0) {
37
+ const auto& custBuf = customization.value();
38
+ custData.assign(reinterpret_cast<const uint8_t*>(custBuf->data()), reinterpret_cast<const uint8_t*>(custBuf->data()) + custBuf->size());
39
+ params[paramCount++] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_CUSTOM, custData.data(), custData.size());
40
+ }
41
+
42
+ params[paramCount] = OSSL_PARAM_construct_end();
43
+
44
+ const uint8_t* keyData = reinterpret_cast<const uint8_t*>(key->data());
45
+ size_t keySize = key->size();
46
+
47
+ if (keySize == 0) {
48
+ throw std::runtime_error("KMAC key must not be empty");
49
+ }
50
+
51
+ if (EVP_MAC_init(ctx.get(), keyData, keySize, params) != 1) {
52
+ throw std::runtime_error("Failed to initialize KMAC: " + std::to_string(ERR_get_error()));
53
+ }
54
+ }
55
+
56
+ void HybridKmac::update(const std::shared_ptr<ArrayBuffer>& data) {
57
+ if (!ctx) {
58
+ throw std::runtime_error("KMAC context not initialized");
59
+ }
60
+
61
+ if (EVP_MAC_update(ctx.get(), reinterpret_cast<const uint8_t*>(data->data()), data->size()) != 1) {
62
+ throw std::runtime_error("Failed to update KMAC: " + std::to_string(ERR_get_error()));
63
+ }
64
+ }
65
+
66
+ std::shared_ptr<ArrayBuffer> HybridKmac::digest() {
67
+ if (!ctx) {
68
+ throw std::runtime_error("KMAC context not initialized");
69
+ }
70
+
71
+ uint8_t* buffer = new uint8_t[outputLen];
72
+
73
+ if (EVP_MAC_final(ctx.get(), buffer, nullptr, outputLen) != 1) {
74
+ delete[] buffer;
75
+ throw std::runtime_error("Failed to finalize KMAC digest: " + std::to_string(ERR_get_error()));
76
+ }
77
+
78
+ ctx.reset();
79
+
80
+ return std::make_shared<NativeArrayBuffer>(buffer, outputLen, [=]() { delete[] buffer; });
81
+ }
82
+
83
+ } // namespace margelo::nitro::crypto
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #include <NitroModules/ArrayBuffer.hpp>
4
+ #include <memory>
5
+ #include <openssl/evp.h>
6
+ #include <string>
7
+
8
+ #include "HybridKmacSpec.hpp"
9
+
10
+ namespace margelo::nitro::crypto {
11
+
12
+ using namespace facebook;
13
+
14
+ using EVP_MAC_CTX_ptr = std::unique_ptr<EVP_MAC_CTX, decltype(&EVP_MAC_CTX_free)>;
15
+
16
+ class HybridKmac : public HybridKmacSpec {
17
+ public:
18
+ HybridKmac() : HybridObject(TAG) {}
19
+
20
+ public:
21
+ void createKmac(const std::string& algorithm, const std::shared_ptr<ArrayBuffer>& key, double outputLength,
22
+ const std::optional<std::shared_ptr<ArrayBuffer>>& customization) override;
23
+ void update(const std::shared_ptr<ArrayBuffer>& data) override;
24
+ std::shared_ptr<ArrayBuffer> digest() override;
25
+
26
+ private:
27
+ EVP_MAC_CTX_ptr ctx{nullptr, EVP_MAC_CTX_free};
28
+ size_t outputLen = 0;
29
+ };
30
+
31
+ } // namespace margelo::nitro::crypto
@@ -15,13 +15,6 @@
15
15
 
16
16
  namespace margelo::nitro::crypto {
17
17
 
18
- HybridMlDsaKeyPair::~HybridMlDsaKeyPair() {
19
- if (pkey_ != nullptr) {
20
- EVP_PKEY_free(pkey_);
21
- pkey_ = nullptr;
22
- }
23
- }
24
-
25
18
  int HybridMlDsaKeyPair::getEvpPkeyType() const {
26
19
  #if RNQC_HAS_ML_DSA
27
20
  if (variant_ == "ML-DSA-44")
@@ -66,10 +59,7 @@ void HybridMlDsaKeyPair::generateKeyPairSync(double publicFormat, double publicT
66
59
  privateFormat_ = static_cast<int>(privateFormat);
67
60
  privateType_ = static_cast<int>(privateType);
68
61
 
69
- if (pkey_ != nullptr) {
70
- EVP_PKEY_free(pkey_);
71
- pkey_ = nullptr;
72
- }
62
+ pkey_.reset();
73
63
 
74
64
  EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(nullptr, variant_.c_str(), nullptr);
75
65
  if (pctx == nullptr) {
@@ -81,10 +71,12 @@ void HybridMlDsaKeyPair::generateKeyPairSync(double publicFormat, double publicT
81
71
  throw std::runtime_error("Failed to initialize keygen: " + getOpenSSLError());
82
72
  }
83
73
 
84
- if (EVP_PKEY_keygen(pctx, &pkey_) <= 0) {
74
+ EVP_PKEY* raw = nullptr;
75
+ if (EVP_PKEY_keygen(pctx, &raw) <= 0) {
85
76
  EVP_PKEY_CTX_free(pctx);
86
77
  throw std::runtime_error("Failed to generate ML-DSA key pair: " + getOpenSSLError());
87
78
  }
79
+ pkey_.reset(raw);
88
80
 
89
81
  EVP_PKEY_CTX_free(pctx);
90
82
  #endif
@@ -103,9 +95,9 @@ std::shared_ptr<ArrayBuffer> HybridMlDsaKeyPair::getPublicKey() {
103
95
 
104
96
  int result;
105
97
  if (publicFormat_ == 1) {
106
- result = PEM_write_bio_PUBKEY(bio, pkey_);
98
+ result = PEM_write_bio_PUBKEY(bio, pkey_.get());
107
99
  } else {
108
- result = i2d_PUBKEY_bio(bio, pkey_);
100
+ result = i2d_PUBKEY_bio(bio, pkey_.get());
109
101
  }
110
102
 
111
103
  if (result != 1) {
@@ -139,10 +131,9 @@ std::shared_ptr<ArrayBuffer> HybridMlDsaKeyPair::getPrivateKey() {
139
131
 
140
132
  int result;
141
133
  if (privateFormat_ == 1) {
142
- result = PEM_write_bio_PrivateKey(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr);
134
+ result = PEM_write_bio_PrivateKey(bio, pkey_.get(), nullptr, nullptr, 0, nullptr, nullptr);
143
135
  } else {
144
- // Use PKCS8 format for DER export (not raw private key format)
145
- result = i2d_PKCS8PrivateKey_bio(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr);
136
+ result = i2d_PKCS8PrivateKey_bio(bio, pkey_.get(), nullptr, nullptr, 0, nullptr, nullptr);
146
137
  }
147
138
 
148
139
  if (result != 1) {
@@ -186,7 +177,7 @@ std::shared_ptr<ArrayBuffer> HybridMlDsaKeyPair::signSync(const std::shared_ptr<
186
177
  throw std::runtime_error("Failed to create signing context for " + variant_);
187
178
  }
188
179
 
189
- if (EVP_DigestSignInit(md_ctx, &pkey_ctx, nullptr, nullptr, pkey_) <= 0) {
180
+ if (EVP_DigestSignInit(md_ctx, &pkey_ctx, nullptr, nullptr, pkey_.get()) <= 0) {
190
181
  EVP_MD_CTX_free(md_ctx);
191
182
  EVP_PKEY_CTX_free(pkey_ctx);
192
183
  throw std::runtime_error("Failed to initialize signing: " + getOpenSSLError());
@@ -237,7 +228,7 @@ bool HybridMlDsaKeyPair::verifySync(const std::shared_ptr<ArrayBuffer>& signatur
237
228
  throw std::runtime_error("Failed to create verify context for " + variant_);
238
229
  }
239
230
 
240
- if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, nullptr, nullptr, pkey_) <= 0) {
231
+ if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, nullptr, nullptr, pkey_.get()) <= 0) {
241
232
  EVP_MD_CTX_free(md_ctx);
242
233
  EVP_PKEY_CTX_free(pkey_ctx);
243
234
  throw std::runtime_error("Failed to initialize verification: " + getOpenSSLError());
@@ -256,7 +247,7 @@ bool HybridMlDsaKeyPair::verifySync(const std::shared_ptr<ArrayBuffer>& signatur
256
247
  }
257
248
 
258
249
  void HybridMlDsaKeyPair::checkKeyPair() {
259
- if (pkey_ == nullptr) {
250
+ if (!pkey_) {
260
251
  throw std::runtime_error("Key pair not initialized");
261
252
  }
262
253
  }
@@ -11,7 +11,7 @@ namespace margelo::nitro::crypto {
11
11
  class HybridMlDsaKeyPair : public HybridMlDsaKeyPairSpec {
12
12
  public:
13
13
  HybridMlDsaKeyPair() : HybridObject(TAG) {}
14
- ~HybridMlDsaKeyPair();
14
+ ~HybridMlDsaKeyPair() override = default;
15
15
 
16
16
  std::shared_ptr<Promise<void>> generateKeyPair(double publicFormat, double publicType, double privateFormat, double privateType) override;
17
17
 
@@ -32,8 +32,10 @@ class HybridMlDsaKeyPair : public HybridMlDsaKeyPairSpec {
32
32
  void setVariant(const std::string& variant) override;
33
33
 
34
34
  private:
35
+ using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
36
+
35
37
  std::string variant_;
36
- EVP_PKEY* pkey_ = nullptr;
38
+ EVP_PKEY_ptr pkey_{nullptr, EVP_PKEY_free};
37
39
 
38
40
  int publicFormat_ = -1;
39
41
  int publicType_ = -1;