react-native-quick-crypto 1.0.10 → 1.0.12

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 (293) hide show
  1. package/android/CMakeLists.txt +16 -0
  2. package/cpp/argon2/HybridArgon2.cpp +103 -0
  3. package/cpp/argon2/HybridArgon2.hpp +32 -0
  4. package/cpp/certificate/HybridCertificate.cpp +42 -0
  5. package/cpp/certificate/HybridCertificate.hpp +16 -0
  6. package/cpp/cipher/CCMCipher.cpp +4 -1
  7. package/cpp/cipher/ChaCha20Cipher.cpp +3 -1
  8. package/cpp/cipher/ChaCha20Poly1305Cipher.cpp +5 -5
  9. package/cpp/cipher/ChaCha20Poly1305Cipher.hpp +1 -2
  10. package/cpp/cipher/HybridCipher.cpp +68 -1
  11. package/cpp/cipher/HybridCipher.hpp +6 -0
  12. package/cpp/cipher/HybridRsaCipher.cpp +0 -13
  13. package/cpp/cipher/XChaCha20Poly1305Cipher.cpp +7 -5
  14. package/cpp/cipher/XChaCha20Poly1305Cipher.hpp +1 -2
  15. package/cpp/cipher/XSalsa20Cipher.cpp +4 -0
  16. package/cpp/cipher/XSalsa20Poly1305Cipher.cpp +7 -5
  17. package/cpp/cipher/XSalsa20Poly1305Cipher.hpp +1 -2
  18. package/cpp/dh/HybridDhKeyPair.cpp +179 -0
  19. package/cpp/dh/HybridDhKeyPair.hpp +37 -0
  20. package/cpp/dsa/HybridDsaKeyPair.cpp +128 -0
  21. package/cpp/dsa/HybridDsaKeyPair.hpp +32 -0
  22. package/cpp/ecdh/HybridECDH.cpp +42 -120
  23. package/cpp/ecdh/HybridECDH.hpp +1 -0
  24. package/cpp/keys/HybridKeyObjectHandle.cpp +150 -128
  25. package/cpp/keys/HybridKeyObjectHandle.hpp +6 -3
  26. package/cpp/keys/KeyObjectData.hpp +2 -0
  27. package/cpp/kmac/HybridKmac.cpp +83 -0
  28. package/cpp/kmac/HybridKmac.hpp +31 -0
  29. package/cpp/mldsa/HybridMlDsaKeyPair.cpp +11 -20
  30. package/cpp/mldsa/HybridMlDsaKeyPair.hpp +4 -2
  31. package/cpp/mlkem/HybridMlKemKeyPair.cpp +319 -0
  32. package/cpp/mlkem/HybridMlKemKeyPair.hpp +48 -0
  33. package/cpp/prime/HybridPrime.cpp +81 -0
  34. package/cpp/prime/HybridPrime.hpp +20 -0
  35. package/cpp/sign/SignUtils.hpp +9 -26
  36. package/cpp/utils/QuickCryptoUtils.cpp +44 -0
  37. package/cpp/utils/QuickCryptoUtils.hpp +39 -0
  38. package/cpp/x509/HybridX509Certificate.cpp +174 -0
  39. package/cpp/x509/HybridX509Certificate.hpp +51 -0
  40. package/lib/commonjs/argon2.js +39 -0
  41. package/lib/commonjs/argon2.js.map +1 -0
  42. package/lib/commonjs/certificate.js +35 -0
  43. package/lib/commonjs/certificate.js.map +1 -0
  44. package/lib/commonjs/cipher.js +23 -2
  45. package/lib/commonjs/cipher.js.map +1 -1
  46. package/lib/commonjs/dhKeyPair.js +109 -0
  47. package/lib/commonjs/dhKeyPair.js.map +1 -0
  48. package/lib/commonjs/dsa.js +92 -0
  49. package/lib/commonjs/dsa.js.map +1 -0
  50. package/lib/commonjs/ec.js +18 -18
  51. package/lib/commonjs/ec.js.map +1 -1
  52. package/lib/commonjs/ecdh.js +37 -0
  53. package/lib/commonjs/ecdh.js.map +1 -1
  54. package/lib/commonjs/ed.js +9 -9
  55. package/lib/commonjs/ed.js.map +1 -1
  56. package/lib/commonjs/hash.js +17 -12
  57. package/lib/commonjs/hash.js.map +1 -1
  58. package/lib/commonjs/hkdf.js.map +1 -1
  59. package/lib/commonjs/index.js +57 -0
  60. package/lib/commonjs/index.js.map +1 -1
  61. package/lib/commonjs/keys/classes.js +11 -9
  62. package/lib/commonjs/keys/classes.js.map +1 -1
  63. package/lib/commonjs/keys/generateKeyPair.js +11 -0
  64. package/lib/commonjs/keys/generateKeyPair.js.map +1 -1
  65. package/lib/commonjs/keys/index.js +24 -0
  66. package/lib/commonjs/keys/index.js.map +1 -1
  67. package/lib/commonjs/keys/signVerify.js +0 -2
  68. package/lib/commonjs/keys/signVerify.js.map +1 -1
  69. package/lib/commonjs/mlkem.js +219 -0
  70. package/lib/commonjs/mlkem.js.map +1 -0
  71. package/lib/commonjs/pbkdf2.js +18 -1
  72. package/lib/commonjs/pbkdf2.js.map +1 -1
  73. package/lib/commonjs/prime.js +84 -0
  74. package/lib/commonjs/prime.js.map +1 -0
  75. package/lib/commonjs/rsa.js +7 -7
  76. package/lib/commonjs/rsa.js.map +1 -1
  77. package/lib/commonjs/specs/argon2.nitro.js +6 -0
  78. package/lib/commonjs/specs/argon2.nitro.js.map +1 -0
  79. package/lib/commonjs/specs/certificate.nitro.js +6 -0
  80. package/lib/commonjs/specs/certificate.nitro.js.map +1 -0
  81. package/lib/commonjs/specs/dhKeyPair.nitro.js +6 -0
  82. package/lib/commonjs/specs/dhKeyPair.nitro.js.map +1 -0
  83. package/lib/commonjs/specs/dsaKeyPair.nitro.js +6 -0
  84. package/lib/commonjs/specs/dsaKeyPair.nitro.js.map +1 -0
  85. package/lib/commonjs/specs/kmac.nitro.js +6 -0
  86. package/lib/commonjs/specs/kmac.nitro.js.map +1 -0
  87. package/lib/commonjs/specs/mlKemKeyPair.nitro.js +6 -0
  88. package/lib/commonjs/specs/mlKemKeyPair.nitro.js.map +1 -0
  89. package/lib/commonjs/specs/prime.nitro.js +6 -0
  90. package/lib/commonjs/specs/prime.nitro.js.map +1 -0
  91. package/lib/commonjs/specs/x509certificate.nitro.js +6 -0
  92. package/lib/commonjs/specs/x509certificate.nitro.js.map +1 -0
  93. package/lib/commonjs/subtle.js +385 -114
  94. package/lib/commonjs/subtle.js.map +1 -1
  95. package/lib/commonjs/utils/conversion.js +3 -3
  96. package/lib/commonjs/utils/conversion.js.map +1 -1
  97. package/lib/commonjs/utils/hashnames.js +31 -0
  98. package/lib/commonjs/utils/hashnames.js.map +1 -1
  99. package/lib/commonjs/utils/types.js.map +1 -1
  100. package/lib/commonjs/x509certificate.js +189 -0
  101. package/lib/commonjs/x509certificate.js.map +1 -0
  102. package/lib/module/argon2.js +34 -0
  103. package/lib/module/argon2.js.map +1 -0
  104. package/lib/module/certificate.js +30 -0
  105. package/lib/module/certificate.js.map +1 -0
  106. package/lib/module/cipher.js +23 -3
  107. package/lib/module/cipher.js.map +1 -1
  108. package/lib/module/dhKeyPair.js +102 -0
  109. package/lib/module/dhKeyPair.js.map +1 -0
  110. package/lib/module/dsa.js +85 -0
  111. package/lib/module/dsa.js.map +1 -0
  112. package/lib/module/ec.js +6 -6
  113. package/lib/module/ec.js.map +1 -1
  114. package/lib/module/ecdh.js +37 -0
  115. package/lib/module/ecdh.js.map +1 -1
  116. package/lib/module/ed.js +1 -1
  117. package/lib/module/ed.js.map +1 -1
  118. package/lib/module/hash.js +17 -12
  119. package/lib/module/hash.js.map +1 -1
  120. package/lib/module/hkdf.js.map +1 -1
  121. package/lib/module/index.js +15 -0
  122. package/lib/module/index.js.map +1 -1
  123. package/lib/module/keys/classes.js +11 -9
  124. package/lib/module/keys/classes.js.map +1 -1
  125. package/lib/module/keys/generateKeyPair.js +11 -0
  126. package/lib/module/keys/generateKeyPair.js.map +1 -1
  127. package/lib/module/keys/index.js +25 -1
  128. package/lib/module/keys/index.js.map +1 -1
  129. package/lib/module/keys/signVerify.js +0 -2
  130. package/lib/module/keys/signVerify.js.map +1 -1
  131. package/lib/module/mlkem.js +211 -0
  132. package/lib/module/mlkem.js.map +1 -0
  133. package/lib/module/pbkdf2.js +18 -1
  134. package/lib/module/pbkdf2.js.map +1 -1
  135. package/lib/module/prime.js +77 -0
  136. package/lib/module/prime.js.map +1 -0
  137. package/lib/module/rsa.js +1 -1
  138. package/lib/module/rsa.js.map +1 -1
  139. package/lib/module/specs/argon2.nitro.js +4 -0
  140. package/lib/module/specs/argon2.nitro.js.map +1 -0
  141. package/lib/module/specs/certificate.nitro.js +4 -0
  142. package/lib/module/specs/certificate.nitro.js.map +1 -0
  143. package/lib/module/specs/dhKeyPair.nitro.js +4 -0
  144. package/lib/module/specs/dhKeyPair.nitro.js.map +1 -0
  145. package/lib/module/specs/dsaKeyPair.nitro.js +4 -0
  146. package/lib/module/specs/dsaKeyPair.nitro.js.map +1 -0
  147. package/lib/module/specs/kmac.nitro.js +4 -0
  148. package/lib/module/specs/kmac.nitro.js.map +1 -0
  149. package/lib/module/specs/mlKemKeyPair.nitro.js +4 -0
  150. package/lib/module/specs/mlKemKeyPair.nitro.js.map +1 -0
  151. package/lib/module/specs/prime.nitro.js +4 -0
  152. package/lib/module/specs/prime.nitro.js.map +1 -0
  153. package/lib/module/specs/x509certificate.nitro.js +4 -0
  154. package/lib/module/specs/x509certificate.nitro.js.map +1 -0
  155. package/lib/module/subtle.js +386 -116
  156. package/lib/module/subtle.js.map +1 -1
  157. package/lib/module/utils/conversion.js +3 -4
  158. package/lib/module/utils/conversion.js.map +1 -1
  159. package/lib/module/utils/hashnames.js +31 -0
  160. package/lib/module/utils/hashnames.js.map +1 -1
  161. package/lib/module/utils/types.js.map +1 -1
  162. package/lib/module/x509certificate.js +184 -0
  163. package/lib/module/x509certificate.js.map +1 -0
  164. package/lib/tsconfig.tsbuildinfo +1 -1
  165. package/lib/typescript/argon2.d.ts +16 -0
  166. package/lib/typescript/argon2.d.ts.map +1 -0
  167. package/lib/typescript/certificate.d.ts +8 -0
  168. package/lib/typescript/certificate.d.ts.map +1 -0
  169. package/lib/typescript/cipher.d.ts +15 -0
  170. package/lib/typescript/cipher.d.ts.map +1 -1
  171. package/lib/typescript/dhKeyPair.d.ts +19 -0
  172. package/lib/typescript/dhKeyPair.d.ts.map +1 -0
  173. package/lib/typescript/dsa.d.ts +19 -0
  174. package/lib/typescript/dsa.d.ts.map +1 -0
  175. package/lib/typescript/ec.d.ts +1 -1
  176. package/lib/typescript/ec.d.ts.map +1 -1
  177. package/lib/typescript/ecdh.d.ts +3 -0
  178. package/lib/typescript/ecdh.d.ts.map +1 -1
  179. package/lib/typescript/ed.d.ts +1 -1
  180. package/lib/typescript/ed.d.ts.map +1 -1
  181. package/lib/typescript/hash.d.ts.map +1 -1
  182. package/lib/typescript/hkdf.d.ts +2 -6
  183. package/lib/typescript/hkdf.d.ts.map +1 -1
  184. package/lib/typescript/index.d.ts +32 -4
  185. package/lib/typescript/index.d.ts.map +1 -1
  186. package/lib/typescript/keys/classes.d.ts +7 -5
  187. package/lib/typescript/keys/classes.d.ts.map +1 -1
  188. package/lib/typescript/keys/generateKeyPair.d.ts.map +1 -1
  189. package/lib/typescript/keys/index.d.ts +2 -2
  190. package/lib/typescript/keys/index.d.ts.map +1 -1
  191. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  192. package/lib/typescript/mlkem.d.ts +30 -0
  193. package/lib/typescript/mlkem.d.ts.map +1 -0
  194. package/lib/typescript/pbkdf2.d.ts +2 -2
  195. package/lib/typescript/pbkdf2.d.ts.map +1 -1
  196. package/lib/typescript/prime.d.ts +19 -0
  197. package/lib/typescript/prime.d.ts.map +1 -0
  198. package/lib/typescript/rsa.d.ts +1 -1
  199. package/lib/typescript/rsa.d.ts.map +1 -1
  200. package/lib/typescript/specs/argon2.nitro.d.ts +9 -0
  201. package/lib/typescript/specs/argon2.nitro.d.ts.map +1 -0
  202. package/lib/typescript/specs/certificate.nitro.d.ts +10 -0
  203. package/lib/typescript/specs/certificate.nitro.d.ts.map +1 -0
  204. package/lib/typescript/specs/cipher.nitro.d.ts +9 -0
  205. package/lib/typescript/specs/cipher.nitro.d.ts.map +1 -1
  206. package/lib/typescript/specs/dhKeyPair.nitro.d.ts +14 -0
  207. package/lib/typescript/specs/dhKeyPair.nitro.d.ts.map +1 -0
  208. package/lib/typescript/specs/dsaKeyPair.nitro.d.ts +13 -0
  209. package/lib/typescript/specs/dsaKeyPair.nitro.d.ts.map +1 -0
  210. package/lib/typescript/specs/ecdh.nitro.d.ts +1 -0
  211. package/lib/typescript/specs/ecdh.nitro.d.ts.map +1 -1
  212. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts +1 -0
  213. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts.map +1 -1
  214. package/lib/typescript/specs/kmac.nitro.d.ts +10 -0
  215. package/lib/typescript/specs/kmac.nitro.d.ts.map +1 -0
  216. package/lib/typescript/specs/mlKemKeyPair.nitro.d.ts +18 -0
  217. package/lib/typescript/specs/mlKemKeyPair.nitro.d.ts.map +1 -0
  218. package/lib/typescript/specs/prime.nitro.d.ts +11 -0
  219. package/lib/typescript/specs/prime.nitro.d.ts.map +1 -0
  220. package/lib/typescript/specs/x509certificate.nitro.d.ts +34 -0
  221. package/lib/typescript/specs/x509certificate.nitro.d.ts.map +1 -0
  222. package/lib/typescript/subtle.d.ts +12 -0
  223. package/lib/typescript/subtle.d.ts.map +1 -1
  224. package/lib/typescript/utils/conversion.d.ts.map +1 -1
  225. package/lib/typescript/utils/hashnames.d.ts +1 -1
  226. package/lib/typescript/utils/hashnames.d.ts.map +1 -1
  227. package/lib/typescript/utils/types.d.ts +25 -9
  228. package/lib/typescript/utils/types.d.ts.map +1 -1
  229. package/lib/typescript/x509certificate.d.ts +64 -0
  230. package/lib/typescript/x509certificate.d.ts.map +1 -0
  231. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +8 -0
  232. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +80 -0
  233. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +80 -0
  234. package/nitrogen/generated/shared/c++/AsymmetricKeyType.hpp +12 -0
  235. package/nitrogen/generated/shared/c++/CipherInfo.hpp +104 -0
  236. package/nitrogen/generated/shared/c++/HybridArgon2Spec.cpp +22 -0
  237. package/nitrogen/generated/shared/c++/HybridArgon2Spec.hpp +66 -0
  238. package/nitrogen/generated/shared/c++/HybridCertificateSpec.cpp +23 -0
  239. package/nitrogen/generated/shared/c++/HybridCertificateSpec.hpp +64 -0
  240. package/nitrogen/generated/shared/c++/HybridCipherSpec.cpp +1 -0
  241. package/nitrogen/generated/shared/c++/HybridCipherSpec.hpp +4 -0
  242. package/nitrogen/generated/shared/c++/HybridDhKeyPairSpec.cpp +27 -0
  243. package/nitrogen/generated/shared/c++/HybridDhKeyPairSpec.hpp +69 -0
  244. package/nitrogen/generated/shared/c++/HybridDsaKeyPairSpec.cpp +26 -0
  245. package/nitrogen/generated/shared/c++/HybridDsaKeyPairSpec.hpp +68 -0
  246. package/nitrogen/generated/shared/c++/HybridECDHSpec.cpp +1 -0
  247. package/nitrogen/generated/shared/c++/HybridECDHSpec.hpp +1 -0
  248. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.cpp +1 -0
  249. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.hpp +1 -0
  250. package/nitrogen/generated/shared/c++/HybridKmacSpec.cpp +23 -0
  251. package/nitrogen/generated/shared/c++/HybridKmacSpec.hpp +66 -0
  252. package/nitrogen/generated/shared/c++/HybridMlKemKeyPairSpec.cpp +31 -0
  253. package/nitrogen/generated/shared/c++/HybridMlKemKeyPairSpec.hpp +74 -0
  254. package/nitrogen/generated/shared/c++/HybridPrimeSpec.cpp +24 -0
  255. package/nitrogen/generated/shared/c++/HybridPrimeSpec.hpp +67 -0
  256. package/nitrogen/generated/shared/c++/HybridX509CertificateHandleSpec.cpp +46 -0
  257. package/nitrogen/generated/shared/c++/HybridX509CertificateHandleSpec.hpp +96 -0
  258. package/package.json +4 -1
  259. package/src/argon2.ts +83 -0
  260. package/src/certificate.ts +41 -0
  261. package/src/cipher.ts +41 -3
  262. package/src/dhKeyPair.ts +156 -0
  263. package/src/dsa.ts +129 -0
  264. package/src/ec.ts +9 -9
  265. package/src/ecdh.ts +59 -0
  266. package/src/ed.ts +2 -2
  267. package/src/hash.ts +34 -11
  268. package/src/hkdf.ts +2 -7
  269. package/src/index.ts +16 -0
  270. package/src/keys/classes.ts +26 -14
  271. package/src/keys/generateKeyPair.ts +14 -0
  272. package/src/keys/index.ts +37 -2
  273. package/src/keys/signVerify.ts +0 -5
  274. package/src/mlkem.ts +350 -0
  275. package/src/pbkdf2.ts +34 -5
  276. package/src/prime.ts +134 -0
  277. package/src/rsa.ts +1 -1
  278. package/src/specs/argon2.nitro.ts +29 -0
  279. package/src/specs/certificate.nitro.ts +8 -0
  280. package/src/specs/cipher.nitro.ts +14 -0
  281. package/src/specs/dhKeyPair.nitro.ts +14 -0
  282. package/src/specs/dsaKeyPair.nitro.ts +13 -0
  283. package/src/specs/ecdh.nitro.ts +1 -0
  284. package/src/specs/keyObjectHandle.nitro.ts +5 -0
  285. package/src/specs/kmac.nitro.ts +12 -0
  286. package/src/specs/mlKemKeyPair.nitro.ts +32 -0
  287. package/src/specs/prime.nitro.ts +18 -0
  288. package/src/specs/x509certificate.nitro.ts +38 -0
  289. package/src/subtle.ts +821 -136
  290. package/src/utils/conversion.ts +10 -4
  291. package/src/utils/hashnames.ts +33 -2
  292. package/src/utils/types.ts +64 -8
  293. package/src/x509certificate.ts +277 -0
@@ -2,6 +2,7 @@
2
2
  #include "QuickCryptoUtils.hpp"
3
3
  #include <NitroModules/ArrayBuffer.hpp>
4
4
  #include <openssl/bn.h>
5
+ #include <openssl/core_names.h>
5
6
  #include <openssl/ec.h>
6
7
  #include <openssl/err.h>
7
8
  #include <openssl/evp.h>
@@ -12,15 +13,9 @@ namespace margelo::nitro::crypto {
12
13
 
13
14
  // Smart pointer type aliases for RAII
14
15
  using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
15
- using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)>;
16
16
  using EC_POINT_ptr = std::unique_ptr<EC_POINT, decltype(&EC_POINT_free)>;
17
17
  using BN_ptr = std::unique_ptr<BIGNUM, decltype(&BN_free)>;
18
18
 
19
- // Suppress deprecation warnings for EC_KEY_* functions
20
- // These APIs work but are deprecated in OpenSSL 3.x
21
- #pragma clang diagnostic push
22
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
23
-
24
19
  void HybridECDH::init(const std::string& curveName) {
25
20
  int nid = getCurveNid(curveName);
26
21
  if (nid == NID_undef) {
@@ -70,41 +65,8 @@ std::shared_ptr<ArrayBuffer> HybridECDH::computeSecret(const std::shared_ptr<Arr
70
65
  throw std::runtime_error("ECDH: private key not set");
71
66
  }
72
67
 
73
- // Create EC_POINT from the peer's public key bytes
74
- EC_POINT_ptr point(EC_POINT_new(_group.get()), EC_POINT_free);
75
- if (!point) {
76
- throw std::runtime_error("ECDH: failed to create EC point");
77
- }
78
-
79
- if (EC_POINT_oct2point(_group.get(), point.get(), otherPublicKey->data(), otherPublicKey->size(), nullptr) != 1) {
80
- throw std::runtime_error("ECDH: failed to decode peer public key");
81
- }
82
-
83
- // Create EC_KEY for the peer
84
- EC_KEY_ptr ecKey(EC_KEY_new(), EC_KEY_free);
85
- if (!ecKey) {
86
- throw std::runtime_error("ECDH: failed to create EC_KEY");
87
- }
88
-
89
- if (EC_KEY_set_group(ecKey.get(), _group.get()) != 1) {
90
- throw std::runtime_error("ECDH: failed to set EC group");
91
- }
92
-
93
- if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
94
- throw std::runtime_error("ECDH: failed to set peer public key");
95
- }
96
-
97
- // Create EVP_PKEY for the peer
98
- EVP_PKEY_ptr peerPkey(EVP_PKEY_new(), EVP_PKEY_free);
99
- if (!peerPkey) {
100
- throw std::runtime_error("ECDH: failed to create peer EVP_PKEY");
101
- }
102
-
103
- // EVP_PKEY_assign_EC_KEY takes ownership of ecKey on success
104
- if (EVP_PKEY_assign_EC_KEY(peerPkey.get(), ecKey.get()) != 1) {
105
- throw std::runtime_error("ECDH: failed to assign EC_KEY to EVP_PKEY");
106
- }
107
- ecKey.release(); // EVP_PKEY now owns the EC_KEY
68
+ // Build peer EVP_PKEY from raw public key octets
69
+ EVP_PKEY_ptr peerPkey(createEcEvpPkey(_curveName.c_str(), otherPublicKey->data(), otherPublicKey->size()), EVP_PKEY_free);
108
70
 
109
71
  // Derive shared secret using EVP API
110
72
  EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(_pkey.get(), nullptr), EVP_PKEY_CTX_free);
@@ -142,19 +104,15 @@ std::shared_ptr<ArrayBuffer> HybridECDH::getPrivateKey() {
142
104
  throw std::runtime_error("ECDH: no key set");
143
105
  }
144
106
 
145
- const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(_pkey.get());
146
- if (!ec) {
147
- throw std::runtime_error("ECDH: key is not an EC key");
148
- }
149
-
150
- const BIGNUM* priv = EC_KEY_get0_private_key(ec);
151
- if (!priv) {
107
+ BIGNUM* priv = nullptr;
108
+ if (EVP_PKEY_get_bn_param(_pkey.get(), OSSL_PKEY_PARAM_PRIV_KEY, &priv) != 1 || !priv) {
152
109
  throw std::runtime_error("ECDH: no private key available");
153
110
  }
154
111
 
155
112
  int len = BN_num_bytes(priv);
156
113
  std::vector<uint8_t> buf(len);
157
114
  BN_bn2bin(priv, buf.data());
115
+ BN_free(priv);
158
116
 
159
117
  return ToNativeArrayBuffer(buf);
160
118
  }
@@ -162,23 +120,12 @@ std::shared_ptr<ArrayBuffer> HybridECDH::getPrivateKey() {
162
120
  void HybridECDH::setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) {
163
121
  ensureInitialized();
164
122
 
165
- // Create new EC_KEY
166
- EC_KEY_ptr ecKey(EC_KEY_new(), EC_KEY_free);
167
- if (!ecKey) {
168
- throw std::runtime_error("ECDH: failed to create EC_KEY");
169
- }
170
-
171
- if (EC_KEY_set_group(ecKey.get(), _group.get()) != 1) {
172
- throw std::runtime_error("ECDH: failed to set EC group");
173
- }
174
-
175
123
  // Convert private key bytes to BIGNUM
176
124
  BN_ptr privBn(BN_bin2bn(privateKey->data(), static_cast<int>(privateKey->size()), nullptr), BN_free);
177
125
  if (!privBn) {
178
126
  throw std::runtime_error("ECDH: failed to convert private key");
179
127
  }
180
128
 
181
- // Calculate public key from private key
182
129
  EC_POINT_ptr pubPoint(EC_POINT_new(_group.get()), EC_POINT_free);
183
130
  if (!pubPoint) {
184
131
  throw std::runtime_error("ECDH: failed to create EC point");
@@ -188,28 +135,17 @@ void HybridECDH::setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) {
188
135
  throw std::runtime_error("ECDH: failed to compute public key from private key");
189
136
  }
190
137
 
191
- // Set keys on EC_KEY (these functions copy the values, so we still own privBn and pubPoint)
192
- if (EC_KEY_set_private_key(ecKey.get(), privBn.get()) != 1) {
193
- throw std::runtime_error("ECDH: failed to set private key");
194
- }
195
-
196
- if (EC_KEY_set_public_key(ecKey.get(), pubPoint.get()) != 1) {
197
- throw std::runtime_error("ECDH: failed to set public key");
198
- }
199
-
200
- // Create new EVP_PKEY
201
- EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
202
- if (!pkey) {
203
- throw std::runtime_error("ECDH: failed to create EVP_PKEY");
138
+ size_t pubLen = EC_POINT_point2oct(_group.get(), pubPoint.get(), POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
139
+ if (pubLen == 0) {
140
+ throw std::runtime_error("ECDH: failed to get public key length");
204
141
  }
205
-
206
- // EVP_PKEY_assign_EC_KEY takes ownership of ecKey on success
207
- if (EVP_PKEY_assign_EC_KEY(pkey.get(), ecKey.get()) != 1) {
208
- throw std::runtime_error("ECDH: failed to assign EC_KEY to EVP_PKEY");
142
+ std::vector<uint8_t> pubOct(pubLen);
143
+ if (EC_POINT_point2oct(_group.get(), pubPoint.get(), POINT_CONVERSION_UNCOMPRESSED, pubOct.data(), pubLen, nullptr) == 0) {
144
+ throw std::runtime_error("ECDH: failed to serialize public key");
209
145
  }
210
- ecKey.release(); // EVP_PKEY now owns the EC_KEY
211
146
 
212
- _pkey = std::move(pkey);
147
+ // Build EVP_PKEY via OSSL_PARAM_BLD
148
+ _pkey.reset(createEcEvpPkey(_curveName.c_str(), pubOct.data(), pubOct.size(), privBn.get()));
213
149
  }
214
150
 
215
151
  std::shared_ptr<ArrayBuffer> HybridECDH::getPublicKey() {
@@ -217,26 +153,14 @@ std::shared_ptr<ArrayBuffer> HybridECDH::getPublicKey() {
217
153
  throw std::runtime_error("ECDH: no key set");
218
154
  }
219
155
 
220
- const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(_pkey.get());
221
- if (!ec) {
222
- throw std::runtime_error("ECDH: key is not an EC key");
223
- }
224
-
225
- const EC_POINT* point = EC_KEY_get0_public_key(ec);
226
- const EC_GROUP* group = EC_KEY_get0_group(ec);
227
- if (!point || !group) {
228
- throw std::runtime_error("ECDH: incomplete key");
229
- }
230
-
231
- // Get uncompressed public key size
232
- size_t len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
233
- if (len == 0) {
156
+ size_t len = 0;
157
+ if (EVP_PKEY_get_octet_string_param(_pkey.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &len) != 1 || len == 0) {
234
158
  throw std::runtime_error("ECDH: failed to get public key length");
235
159
  }
236
160
 
237
161
  std::vector<uint8_t> buf(len);
238
- if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, buf.data(), len, nullptr) == 0) {
239
- throw std::runtime_error("ECDH: failed to encode public key");
162
+ if (EVP_PKEY_get_octet_string_param(_pkey.get(), OSSL_PKEY_PARAM_PUB_KEY, buf.data(), buf.size(), &len) != 1) {
163
+ throw std::runtime_error("ECDH: failed to get public key");
240
164
  }
241
165
 
242
166
  return ToNativeArrayBuffer(buf);
@@ -245,43 +169,43 @@ std::shared_ptr<ArrayBuffer> HybridECDH::getPublicKey() {
245
169
  void HybridECDH::setPublicKey(const std::shared_ptr<ArrayBuffer>& publicKey) {
246
170
  ensureInitialized();
247
171
 
248
- // Create EC_POINT from the public key bytes
249
- EC_POINT_ptr point(EC_POINT_new(_group.get()), EC_POINT_free);
250
- if (!point) {
251
- throw std::runtime_error("ECDH: failed to create EC point");
252
- }
172
+ // Build EVP_PKEY directly from public key octets
173
+ _pkey.reset(createEcEvpPkey(_curveName.c_str(), publicKey->data(), publicKey->size()));
174
+ }
253
175
 
254
- if (EC_POINT_oct2point(_group.get(), point.get(), publicKey->data(), publicKey->size(), nullptr) != 1) {
255
- throw std::runtime_error("ECDH: invalid public key");
176
+ std::shared_ptr<ArrayBuffer> HybridECDH::convertKey(const std::shared_ptr<ArrayBuffer>& key, const std::string& curve, double format) {
177
+ int nid = getCurveNid(curve);
178
+ if (nid == NID_undef) {
179
+ throw std::runtime_error("ECDH: unknown curve: " + curve);
256
180
  }
257
181
 
258
- // Create new EC_KEY
259
- EC_KEY_ptr ecKey(EC_KEY_new(), EC_KEY_free);
260
- if (!ecKey) {
261
- throw std::runtime_error("ECDH: failed to create EC_KEY");
182
+ EC_GROUP_ptr group(EC_GROUP_new_by_curve_name(nid), EC_GROUP_free);
183
+ if (!group) {
184
+ throw std::runtime_error("ECDH: failed to create EC group for curve: " + curve);
262
185
  }
263
186
 
264
- if (EC_KEY_set_group(ecKey.get(), _group.get()) != 1) {
265
- throw std::runtime_error("ECDH: failed to set EC group");
187
+ EC_POINT_ptr point(EC_POINT_new(group.get()), EC_POINT_free);
188
+ if (!point) {
189
+ throw std::runtime_error("ECDH: failed to create EC point");
266
190
  }
267
191
 
268
- if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
269
- throw std::runtime_error("ECDH: failed to set public key");
192
+ if (EC_POINT_oct2point(group.get(), point.get(), key->data(), key->size(), nullptr) != 1) {
193
+ throw std::runtime_error("ECDH: failed to decode public key");
270
194
  }
271
195
 
272
- // Create new EVP_PKEY
273
- EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
274
- if (!pkey) {
275
- throw std::runtime_error("ECDH: failed to create EVP_PKEY");
196
+ auto form = static_cast<point_conversion_form_t>(static_cast<int>(format));
197
+
198
+ size_t len = EC_POINT_point2oct(group.get(), point.get(), form, nullptr, 0, nullptr);
199
+ if (len == 0) {
200
+ throw std::runtime_error("ECDH: failed to get converted key length");
276
201
  }
277
202
 
278
- // EVP_PKEY_assign_EC_KEY takes ownership of ecKey on success
279
- if (EVP_PKEY_assign_EC_KEY(pkey.get(), ecKey.get()) != 1) {
280
- throw std::runtime_error("ECDH: failed to assign EC_KEY to EVP_PKEY");
203
+ std::vector<uint8_t> buf(len);
204
+ if (EC_POINT_point2oct(group.get(), point.get(), form, buf.data(), len, nullptr) == 0) {
205
+ throw std::runtime_error("ECDH: failed to convert key");
281
206
  }
282
- ecKey.release(); // EVP_PKEY now owns the EC_KEY
283
207
 
284
- _pkey = std::move(pkey);
208
+ return ToNativeArrayBuffer(buf);
285
209
  }
286
210
 
287
211
  void HybridECDH::ensureInitialized() const {
@@ -301,6 +225,4 @@ int HybridECDH::getCurveNid(const std::string& name) {
301
225
  return nid;
302
226
  }
303
227
 
304
- #pragma clang diagnostic pop
305
-
306
228
  } // namespace margelo::nitro::crypto
@@ -28,6 +28,7 @@ class HybridECDH : public HybridECDHSpec {
28
28
  void setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) override;
29
29
  std::shared_ptr<ArrayBuffer> getPublicKey() override;
30
30
  void setPublicKey(const std::shared_ptr<ArrayBuffer>& publicKey) override;
31
+ std::shared_ptr<ArrayBuffer> convertKey(const std::shared_ptr<ArrayBuffer>& key, const std::string& curve, double format) override;
31
32
 
32
33
  private:
33
34
  EVP_PKEY_ptr _pkey;
@@ -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)
@@ -125,6 +126,42 @@ std::shared_ptr<ArrayBuffer> HybridKeyObjectHandle::exportKey(std::optional<KFor
125
126
  }
126
127
  }
127
128
 
129
+ // For EC keys, handle raw format (uncompressed point)
130
+ if (!format.has_value() && !type.has_value() && keyId == EVP_PKEY_EC && keyType == KeyType::PUBLIC) {
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");
134
+ std::vector<uint8_t> buf(len);
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
+ }
161
+ }
162
+ }
163
+ #endif
164
+
128
165
  // Set default format and type if not provided
129
166
  auto exportFormat = format.value_or(KFormatType::DER);
130
167
  auto exportType = type.value_or(keyType == KeyType::PUBLIC ? KeyEncoding::SPKI : KeyEncoding::PKCS8);
@@ -239,54 +276,50 @@ JWK HybridKeyObjectHandle::exportJwk(const JWK& key, bool handleRsaPss) {
239
276
 
240
277
  // Export EC keys
241
278
  if (keyId == EVP_PKEY_EC) {
242
- const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey.get());
243
- if (!ec)
244
- throw std::runtime_error("Failed to get EC key");
245
-
246
- const EC_GROUP* group = EC_KEY_get0_group(ec);
247
- if (!group)
248
- throw std::runtime_error("Failed to get EC group");
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");
249
283
 
250
- int nid = EC_GROUP_get_curve_name(group);
251
- const char* curve_name = OBJ_nid2sn(nid);
252
- if (!curve_name)
253
- throw std::runtime_error("Unknown curve");
284
+ std::string curve_name(curve_name_buf, name_len);
254
285
 
255
- // Get the field size in bytes for proper padding
256
- 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;
257
290
 
258
291
  result.kty = JWKkty::EC;
259
292
 
260
293
  // Map OpenSSL curve names to JWK curve names
261
- if (strcmp(curve_name, "prime256v1") == 0) {
294
+ if (curve_name == "prime256v1") {
262
295
  result.crv = "P-256";
263
- } else if (strcmp(curve_name, "secp384r1") == 0) {
296
+ } else if (curve_name == "secp384r1") {
264
297
  result.crv = "P-384";
265
- } else if (strcmp(curve_name, "secp521r1") == 0) {
298
+ } else if (curve_name == "secp521r1") {
266
299
  result.crv = "P-521";
267
300
  } else {
268
301
  result.crv = curve_name;
269
302
  }
270
303
 
271
- const EC_POINT* pub_key = EC_KEY_get0_public_key(ec);
272
- if (pub_key) {
273
- BIGNUM* x_bn = BN_new();
274
- BIGNUM* y_bn = BN_new();
275
-
276
- if (EC_POINT_get_affine_coordinates(group, pub_key, x_bn, y_bn, nullptr) == 1) {
277
- result.x = bn_to_base64url(x_bn, field_size);
278
- result.y = bn_to_base64url(y_bn, field_size);
279
- }
280
-
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) {
281
308
  BN_free(x_bn);
282
309
  BN_free(y_bn);
310
+ throw std::runtime_error("Failed to get EC public key coordinates");
283
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);
284
316
 
285
317
  // Export private key if this is a private key
286
318
  if (keyType == KeyType::PRIVATE) {
287
- const BIGNUM* priv_key = EC_KEY_get0_private_key(ec);
288
- if (priv_key) {
289
- 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);
290
323
  }
291
324
  }
292
325
 
@@ -370,8 +403,25 @@ AsymmetricKeyType HybridKeyObjectHandle::getAsymmetricKeyType() {
370
403
  return AsymmetricKeyType::ML_DSA_87;
371
404
  #endif
372
405
  default:
373
- throw std::runtime_error("Unsupported asymmetric key type");
406
+ break;
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;
374
421
  }
422
+ #endif
423
+
424
+ throw std::runtime_error("Unsupported asymmetric key type");
375
425
  }
376
426
 
377
427
  bool HybridKeyObjectHandle::init(KeyType keyType, const std::variant<std::shared_ptr<ArrayBuffer>, std::string>& key,
@@ -555,81 +605,54 @@ std::optional<KeyType> HybridKeyObjectHandle::initJwk(const JWK& keyData, std::o
555
605
 
556
606
  std::string crv = keyData.crv.value();
557
607
 
558
- // Map JWK curve names to OpenSSL NIDs
559
- int nid;
608
+ // Map JWK curve names to OpenSSL group names and field sizes
609
+ const char* group_name;
610
+ size_t field_size;
560
611
  if (crv == "P-256") {
561
- nid = NID_X9_62_prime256v1;
612
+ group_name = "prime256v1";
613
+ field_size = 32;
562
614
  } else if (crv == "P-384") {
563
- nid = NID_secp384r1;
615
+ group_name = "secp384r1";
616
+ field_size = 48;
564
617
  } else if (crv == "P-521") {
565
- nid = NID_secp521r1;
618
+ group_name = "secp521r1";
619
+ field_size = 66;
566
620
  } else {
567
621
  throw std::runtime_error("Unsupported EC curve: " + crv);
568
622
  }
569
623
 
570
- // Create EC_KEY
571
- EC_KEY* ec = EC_KEY_new_by_curve_name(nid);
572
- if (!ec)
573
- throw std::runtime_error("Failed to create EC key");
574
-
575
- const EC_GROUP* group = EC_KEY_get0_group(ec);
576
-
577
624
  // Decode public key coordinates
578
625
  BIGNUM* x_bn = base64url_to_bn(keyData.x.value());
579
626
  BIGNUM* y_bn = base64url_to_bn(keyData.y.value());
580
-
581
627
  if (!x_bn || !y_bn) {
582
- EC_KEY_free(ec);
583
- throw std::runtime_error("Failed to decode EC public key coordinates");
584
- }
585
-
586
- // Set public key
587
- EC_POINT* pub_key = EC_POINT_new(group);
588
- if (!pub_key || EC_POINT_set_affine_coordinates(group, pub_key, x_bn, y_bn, nullptr) != 1) {
589
628
  BN_free(x_bn);
590
629
  BN_free(y_bn);
591
- if (pub_key)
592
- EC_POINT_free(pub_key);
593
- EC_KEY_free(ec);
594
- throw std::runtime_error("Failed to set EC public key");
630
+ throw std::runtime_error("Failed to decode EC public key coordinates");
595
631
  }
596
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));
597
638
  BN_free(x_bn);
598
639
  BN_free(y_bn);
599
640
 
600
- if (EC_KEY_set_public_key(ec, pub_key) != 1) {
601
- EC_POINT_free(pub_key);
602
- EC_KEY_free(ec);
603
- throw std::runtime_error("Failed to set EC public key on EC_KEY");
604
- }
605
-
606
- EC_POINT_free(pub_key);
607
-
608
- // Set private key if present
641
+ BIGNUM* d_bn = nullptr;
609
642
  if (isPrivate) {
610
- BIGNUM* d_bn = base64url_to_bn(keyData.d.value());
611
- if (!d_bn) {
612
- EC_KEY_free(ec);
643
+ d_bn = base64url_to_bn(keyData.d.value());
644
+ if (!d_bn)
613
645
  throw std::runtime_error("Failed to decode EC private key");
614
- }
615
-
616
- if (EC_KEY_set_private_key(ec, d_bn) != 1) {
617
- BN_free(d_bn);
618
- EC_KEY_free(ec);
619
- throw std::runtime_error("Failed to set EC private key");
620
- }
621
-
622
- BN_free(d_bn);
623
646
  }
624
647
 
625
- // Create EVP_PKEY from EC_KEY
626
- EVP_PKEY* pkey = EVP_PKEY_new();
627
- if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
628
- EC_KEY_free(ec);
629
- if (pkey)
630
- EVP_PKEY_free(pkey);
631
- 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;
632
654
  }
655
+ BN_free(d_bn);
633
656
 
634
657
  KeyType type = isPrivate ? KeyType::PRIVATE : KeyType::PUBLIC;
635
658
  data_ = KeyObjectData::CreateAsymmetric(type, ncrypto::EVPKeyPointer(pkey));
@@ -714,20 +737,11 @@ KeyDetail HybridKeyObjectHandle::keyDetail() {
714
737
  }
715
738
 
716
739
  if (keyType == EVP_PKEY_EC) {
717
- // Extract EC curve name
718
- EC_KEY* ec_key = EVP_PKEY_get1_EC_KEY(pkey);
719
- if (ec_key) {
720
- const EC_GROUP* group = EC_KEY_get0_group(ec_key);
721
- if (group) {
722
- int nid = EC_GROUP_get_curve_name(group);
723
- const char* curve_name = OBJ_nid2sn(nid);
724
- if (curve_name) {
725
- std::string namedCurve(curve_name);
726
- EC_KEY_free(ec_key);
727
- return KeyDetail(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, namedCurve);
728
- }
729
- }
730
- 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));
731
745
  }
732
746
  }
733
747
 
@@ -793,48 +807,56 @@ bool HybridKeyObjectHandle::initECRaw(const std::string& namedCurve, const std::
793
807
  throw std::runtime_error("Unknown curve: " + namedCurve);
794
808
  }
795
809
 
796
- // Create EC_GROUP for the curve
797
- ncrypto::ECGroupPointer group = ncrypto::ECGroupPointer::NewByCurveName(nid);
798
- if (!group) {
799
- 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");
800
814
  }
801
815
 
802
- // Create EC_POINT from raw bytes
803
- ncrypto::ECPointPointer point = ncrypto::ECPointPointer::New(group.get());
804
- if (!point) {
805
- throw std::runtime_error("Failed to create EC_POINT");
806
- }
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
+ }
807
820
 
808
- // Convert raw bytes to EC_POINT
809
- 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();
810
824
 
811
- if (!point.setFromBuffer(buffer, group.get())) {
812
- throw std::runtime_error("Failed to read DER asymmetric key");
813
- }
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);
814
840
 
815
- // Create EC_KEY and set the public key
816
- ncrypto::ECKeyPointer ec = ncrypto::ECKeyPointer::New(group.get());
817
- if (!ec) {
818
- throw std::runtime_error("Failed to create EC_KEY");
819
- }
841
+ ncrypto::Buffer<const unsigned char> buffer{.data = reinterpret_cast<const unsigned char*>(keyData->data()), .len = keyData->size()};
820
842
 
821
- if (!ec.setPublicKey(point)) {
822
- 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);
823
848
  }
824
849
 
825
- // Create EVP_PKEY from EC_KEY
826
- ncrypto::EVPKeyPointer pkey = ncrypto::EVPKeyPointer::New();
827
850
  if (!pkey) {
828
- throw std::runtime_error("Failed to create EVP_PKEY");
829
- }
830
-
831
- if (!pkey.set(ec)) {
832
- throw std::runtime_error("Failed to assign EC_KEY to EVP_PKEY");
851
+ return false;
833
852
  }
834
853
 
835
- // Store as public key
836
- 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));
837
856
  return true;
857
+ #else
858
+ throw std::runtime_error("PQC raw key import requires OpenSSL 3.5+");
859
+ #endif
838
860
  }
839
861
 
840
862
  bool HybridKeyObjectHandle::keyEquals(const std::shared_ptr<HybridKeyObjectHandleSpec>& other) {