react-native-quick-crypto 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/QuickCrypto.podspec +1 -0
  2. package/android/CMakeLists.txt +4 -0
  3. package/cpp/cipher/CCMCipher.cpp +7 -11
  4. package/cpp/cipher/ChaCha20Cipher.cpp +6 -10
  5. package/cpp/cipher/ChaCha20Poly1305Cipher.cpp +10 -16
  6. package/cpp/cipher/GCMCipher.cpp +3 -5
  7. package/cpp/cipher/HybridCipher.cpp +7 -13
  8. package/cpp/cipher/HybridRsaCipher.cpp +19 -27
  9. package/cpp/cipher/OCBCipher.cpp +2 -3
  10. package/cpp/cipher/XChaCha20Poly1305Cipher.cpp +13 -19
  11. package/cpp/cipher/XSalsa20Cipher.cpp +8 -12
  12. package/cpp/cipher/XSalsa20Poly1305Cipher.cpp +11 -16
  13. package/cpp/keys/HybridKeyObjectHandle.cpp +630 -2
  14. package/cpp/keys/HybridKeyObjectHandle.hpp +21 -1
  15. package/cpp/sign/HybridSignHandle.cpp +26 -8
  16. package/cpp/sign/HybridVerifyHandle.cpp +28 -11
  17. package/cpp/slhdsa/HybridSlhDsaKeyPair.cpp +245 -0
  18. package/cpp/slhdsa/HybridSlhDsaKeyPair.hpp +48 -0
  19. package/cpp/turboshake/HybridTurboShake.cpp +379 -0
  20. package/cpp/turboshake/HybridTurboShake.hpp +28 -0
  21. package/cpp/utils/HybridUtils.cpp +26 -14
  22. package/deps/blake3/README.md +6 -7
  23. package/deps/blake3/c/blake3.c +3 -2
  24. package/deps/blake3/c/blake3.h +2 -2
  25. package/deps/blake3/c/blake3_dispatch.c +2 -2
  26. package/deps/blake3/c/blake3_impl.h +1 -1
  27. package/deps/blake3/c/blake3_neon.c +5 -4
  28. package/deps/ncrypto/include/ncrypto/version.h +2 -2
  29. package/deps/ncrypto/include/ncrypto.h +9 -2
  30. package/deps/ncrypto/src/ncrypto.cpp +130 -35
  31. package/lib/commonjs/dhKeyPair.js +3 -0
  32. package/lib/commonjs/dhKeyPair.js.map +1 -1
  33. package/lib/commonjs/dsa.js +3 -0
  34. package/lib/commonjs/dsa.js.map +1 -1
  35. package/lib/commonjs/ec.js +37 -30
  36. package/lib/commonjs/ec.js.map +1 -1
  37. package/lib/commonjs/ed.js +60 -6
  38. package/lib/commonjs/ed.js.map +1 -1
  39. package/lib/commonjs/hash.js +52 -5
  40. package/lib/commonjs/hash.js.map +1 -1
  41. package/lib/commonjs/keys/classes.js +33 -7
  42. package/lib/commonjs/keys/classes.js.map +1 -1
  43. package/lib/commonjs/keys/generateKeyPair.js +85 -4
  44. package/lib/commonjs/keys/generateKeyPair.js.map +1 -1
  45. package/lib/commonjs/keys/index.js +50 -2
  46. package/lib/commonjs/keys/index.js.map +1 -1
  47. package/lib/commonjs/keys/signVerify.js +9 -2
  48. package/lib/commonjs/keys/signVerify.js.map +1 -1
  49. package/lib/commonjs/keys/utils.js +59 -1
  50. package/lib/commonjs/keys/utils.js.map +1 -1
  51. package/lib/commonjs/random.js +63 -9
  52. package/lib/commonjs/random.js.map +1 -1
  53. package/lib/commonjs/rsa.js +3 -0
  54. package/lib/commonjs/rsa.js.map +1 -1
  55. package/lib/commonjs/slhdsa.js +70 -0
  56. package/lib/commonjs/slhdsa.js.map +1 -0
  57. package/lib/commonjs/specs/slhDsaKeyPair.nitro.js +6 -0
  58. package/lib/commonjs/specs/slhDsaKeyPair.nitro.js.map +1 -0
  59. package/lib/commonjs/specs/turboshake.nitro.js +6 -0
  60. package/lib/commonjs/specs/turboshake.nitro.js.map +1 -0
  61. package/lib/commonjs/subtle.js +926 -275
  62. package/lib/commonjs/subtle.js.map +1 -1
  63. package/lib/commonjs/utils/conversion.js +53 -19
  64. package/lib/commonjs/utils/conversion.js.map +1 -1
  65. package/lib/commonjs/utils/errors.js +63 -4
  66. package/lib/commonjs/utils/errors.js.map +1 -1
  67. package/lib/commonjs/utils/types.js.map +1 -1
  68. package/lib/commonjs/utils/validation.js +46 -0
  69. package/lib/commonjs/utils/validation.js.map +1 -1
  70. package/lib/module/dhKeyPair.js +3 -0
  71. package/lib/module/dhKeyPair.js.map +1 -1
  72. package/lib/module/dsa.js +3 -0
  73. package/lib/module/dsa.js.map +1 -1
  74. package/lib/module/ec.js +38 -31
  75. package/lib/module/ec.js.map +1 -1
  76. package/lib/module/ed.js +61 -7
  77. package/lib/module/ed.js.map +1 -1
  78. package/lib/module/hash.js +52 -5
  79. package/lib/module/hash.js.map +1 -1
  80. package/lib/module/keys/classes.js +31 -5
  81. package/lib/module/keys/classes.js.map +1 -1
  82. package/lib/module/keys/generateKeyPair.js +86 -5
  83. package/lib/module/keys/generateKeyPair.js.map +1 -1
  84. package/lib/module/keys/index.js +50 -2
  85. package/lib/module/keys/index.js.map +1 -1
  86. package/lib/module/keys/signVerify.js +9 -2
  87. package/lib/module/keys/signVerify.js.map +1 -1
  88. package/lib/module/keys/utils.js +57 -1
  89. package/lib/module/keys/utils.js.map +1 -1
  90. package/lib/module/random.js +63 -10
  91. package/lib/module/random.js.map +1 -1
  92. package/lib/module/rsa.js +3 -0
  93. package/lib/module/rsa.js.map +1 -1
  94. package/lib/module/slhdsa.js +64 -0
  95. package/lib/module/slhdsa.js.map +1 -0
  96. package/lib/module/specs/slhDsaKeyPair.nitro.js +4 -0
  97. package/lib/module/specs/slhDsaKeyPair.nitro.js.map +1 -0
  98. package/lib/module/specs/turboshake.nitro.js +4 -0
  99. package/lib/module/specs/turboshake.nitro.js.map +1 -0
  100. package/lib/module/subtle.js +927 -276
  101. package/lib/module/subtle.js.map +1 -1
  102. package/lib/module/utils/conversion.js +51 -19
  103. package/lib/module/utils/conversion.js.map +1 -1
  104. package/lib/module/utils/errors.js +61 -4
  105. package/lib/module/utils/errors.js.map +1 -1
  106. package/lib/module/utils/types.js.map +1 -1
  107. package/lib/module/utils/validation.js +44 -0
  108. package/lib/module/utils/validation.js.map +1 -1
  109. package/lib/typescript/dhKeyPair.d.ts.map +1 -1
  110. package/lib/typescript/dsa.d.ts.map +1 -1
  111. package/lib/typescript/ec.d.ts.map +1 -1
  112. package/lib/typescript/ed.d.ts.map +1 -1
  113. package/lib/typescript/hash.d.ts.map +1 -1
  114. package/lib/typescript/index.d.ts +12 -7
  115. package/lib/typescript/index.d.ts.map +1 -1
  116. package/lib/typescript/keys/classes.d.ts +10 -1
  117. package/lib/typescript/keys/classes.d.ts.map +1 -1
  118. package/lib/typescript/keys/generateKeyPair.d.ts +12 -1
  119. package/lib/typescript/keys/generateKeyPair.d.ts.map +1 -1
  120. package/lib/typescript/keys/index.d.ts +3 -1
  121. package/lib/typescript/keys/index.d.ts.map +1 -1
  122. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  123. package/lib/typescript/keys/utils.d.ts +21 -4
  124. package/lib/typescript/keys/utils.d.ts.map +1 -1
  125. package/lib/typescript/random.d.ts +5 -1
  126. package/lib/typescript/random.d.ts.map +1 -1
  127. package/lib/typescript/rsa.d.ts.map +1 -1
  128. package/lib/typescript/slhdsa.d.ts +19 -0
  129. package/lib/typescript/slhdsa.d.ts.map +1 -0
  130. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts +9 -0
  131. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts.map +1 -1
  132. package/lib/typescript/specs/slhDsaKeyPair.nitro.d.ts +16 -0
  133. package/lib/typescript/specs/slhDsaKeyPair.nitro.d.ts.map +1 -0
  134. package/lib/typescript/specs/turboshake.nitro.d.ts +11 -0
  135. package/lib/typescript/specs/turboshake.nitro.d.ts.map +1 -0
  136. package/lib/typescript/subtle.d.ts +3 -2
  137. package/lib/typescript/subtle.d.ts.map +1 -1
  138. package/lib/typescript/utils/conversion.d.ts +4 -3
  139. package/lib/typescript/utils/conversion.d.ts.map +1 -1
  140. package/lib/typescript/utils/errors.d.ts +12 -0
  141. package/lib/typescript/utils/errors.d.ts.map +1 -1
  142. package/lib/typescript/utils/types.d.ts +32 -15
  143. package/lib/typescript/utils/types.d.ts.map +1 -1
  144. package/lib/typescript/utils/validation.d.ts +3 -1
  145. package/lib/typescript/utils/validation.d.ts.map +1 -1
  146. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +2 -0
  147. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +20 -0
  148. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +20 -0
  149. package/nitrogen/generated/shared/c++/AsymmetricKeyType.hpp +48 -0
  150. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.cpp +9 -0
  151. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.hpp +9 -0
  152. package/nitrogen/generated/shared/c++/HybridSlhDsaKeyPairSpec.cpp +29 -0
  153. package/nitrogen/generated/shared/c++/HybridSlhDsaKeyPairSpec.hpp +72 -0
  154. package/nitrogen/generated/shared/c++/HybridTurboShakeSpec.cpp +22 -0
  155. package/nitrogen/generated/shared/c++/HybridTurboShakeSpec.hpp +70 -0
  156. package/nitrogen/generated/shared/c++/JWK.hpp +9 -1
  157. package/nitrogen/generated/shared/c++/JWKkty.hpp +4 -0
  158. package/nitrogen/generated/shared/c++/KangarooTwelveVariant.hpp +76 -0
  159. package/nitrogen/generated/shared/c++/TurboShakeVariant.hpp +76 -0
  160. package/package.json +2 -3
  161. package/src/dhKeyPair.ts +8 -0
  162. package/src/dsa.ts +8 -0
  163. package/src/ec.ts +52 -29
  164. package/src/ed.ts +95 -16
  165. package/src/hash.ts +108 -5
  166. package/src/keys/classes.ts +46 -5
  167. package/src/keys/generateKeyPair.ts +151 -5
  168. package/src/keys/index.ts +73 -3
  169. package/src/keys/signVerify.ts +13 -2
  170. package/src/keys/utils.ts +78 -5
  171. package/src/random.ts +93 -9
  172. package/src/rsa.ts +8 -0
  173. package/src/slhdsa.ts +146 -0
  174. package/src/specs/keyObjectHandle.nitro.ts +17 -0
  175. package/src/specs/slhDsaKeyPair.nitro.ts +29 -0
  176. package/src/specs/turboshake.nitro.ts +21 -0
  177. package/src/subtle.ts +1191 -360
  178. package/src/utils/conversion.ts +72 -21
  179. package/src/utils/errors.ts +72 -4
  180. package/src/utils/types.ts +80 -15
  181. package/src/utils/validation.ts +70 -1
@@ -15,7 +15,7 @@ namespace margelo::nitro::crypto {
15
15
 
16
16
  class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
17
17
  public:
18
- HybridKeyObjectHandle() : HybridObject(TAG) {}
18
+ HybridKeyObjectHandle();
19
19
 
20
20
  public:
21
21
  std::shared_ptr<ArrayBuffer> exportKey(std::optional<KFormatType> format, std::optional<KeyEncoding> type,
@@ -24,6 +24,16 @@ class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
24
24
 
25
25
  JWK exportJwk(const JWK& key, bool handleRsaPss) override;
26
26
 
27
+ std::shared_ptr<ArrayBuffer> exportRawPublic() override;
28
+
29
+ std::shared_ptr<ArrayBuffer> exportRawPrivate() override;
30
+
31
+ std::shared_ptr<ArrayBuffer> exportRawSeed() override;
32
+
33
+ std::shared_ptr<ArrayBuffer> exportECPublicRaw(bool compressed) override;
34
+
35
+ std::shared_ptr<ArrayBuffer> exportECPrivateRaw() override;
36
+
27
37
  AsymmetricKeyType getAsymmetricKeyType() override;
28
38
 
29
39
  bool init(KeyType keyType, const std::variant<std::shared_ptr<ArrayBuffer>, std::string>& key, std::optional<KFormatType> format,
@@ -33,6 +43,14 @@ class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
33
43
 
34
44
  bool initPqcRaw(const std::string& algorithmName, const std::shared_ptr<ArrayBuffer>& keyData, bool isPublic) override;
35
45
 
46
+ bool initRawPublic(const std::string& asymmetricKeyType, const std::shared_ptr<ArrayBuffer>& keyData,
47
+ const std::optional<std::string>& namedCurve) override;
48
+
49
+ bool initRawPrivate(const std::string& asymmetricKeyType, const std::shared_ptr<ArrayBuffer>& keyData,
50
+ const std::optional<std::string>& namedCurve) override;
51
+
52
+ bool initRawSeed(const std::string& asymmetricKeyType, const std::shared_ptr<ArrayBuffer>& keyData) override;
53
+
36
54
  std::optional<KeyType> initJwk(const JWK& keyData, std::optional<NamedCurve> namedCurve) override;
37
55
 
38
56
  KeyDetail keyDetail() override;
@@ -41,6 +59,8 @@ class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
41
59
 
42
60
  double getSymmetricKeySize() override;
43
61
 
62
+ bool checkEcKeyData() override;
63
+
44
64
  const KeyObjectData& getKeyObjectData() const {
45
65
  return data_;
46
66
  }
@@ -58,15 +58,13 @@ void HybridSignHandle::update(const std::shared_ptr<ArrayBuffer>& data) {
58
58
  throw std::runtime_error("Sign not initialized");
59
59
  }
60
60
 
61
- auto native_data = ToNativeArrayBuffer(data);
62
-
63
61
  // Accumulate raw data for potential one-shot signing (Ed25519/Ed448/ML-DSA)
64
- const uint8_t* ptr = reinterpret_cast<const uint8_t*>(native_data->data());
65
- data_buffer.insert(data_buffer.end(), ptr, ptr + native_data->size());
62
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data->data());
63
+ data_buffer.insert(data_buffer.end(), ptr, ptr + data->size());
66
64
 
67
65
  // Only update digest if we have one (not needed for pure signature schemes)
68
66
  if (md != nullptr) {
69
- if (EVP_DigestUpdate(md_ctx, native_data->data(), native_data->size()) <= 0) {
67
+ if (EVP_DigestUpdate(md_ctx, data->data(), data->size()) <= 0) {
70
68
  unsigned long err = ERR_get_error();
71
69
  char err_buf[256];
72
70
  ERR_error_string_n(err, err_buf, sizeof(err_buf));
@@ -75,12 +73,32 @@ void HybridSignHandle::update(const std::shared_ptr<ArrayBuffer>& data) {
75
73
  }
76
74
  }
77
75
 
78
- // Check if key type requires one-shot signing (Ed25519, Ed448, ML-DSA)
76
+ // Check if key type requires one-shot signing (Ed25519, Ed448, ML-DSA, SLH-DSA)
79
77
  static bool isOneShotVariant(EVP_PKEY* pkey) {
80
78
  int type = EVP_PKEY_id(pkey);
81
79
  #if RNQC_HAS_ML_DSA
82
- return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448 || type == EVP_PKEY_ML_DSA_44 || type == EVP_PKEY_ML_DSA_65 ||
83
- type == EVP_PKEY_ML_DSA_87;
80
+ switch (type) {
81
+ case EVP_PKEY_ED25519:
82
+ case EVP_PKEY_ED448:
83
+ case EVP_PKEY_ML_DSA_44:
84
+ case EVP_PKEY_ML_DSA_65:
85
+ case EVP_PKEY_ML_DSA_87:
86
+ case EVP_PKEY_SLH_DSA_SHA2_128S:
87
+ case EVP_PKEY_SLH_DSA_SHA2_128F:
88
+ case EVP_PKEY_SLH_DSA_SHA2_192S:
89
+ case EVP_PKEY_SLH_DSA_SHA2_192F:
90
+ case EVP_PKEY_SLH_DSA_SHA2_256S:
91
+ case EVP_PKEY_SLH_DSA_SHA2_256F:
92
+ case EVP_PKEY_SLH_DSA_SHAKE_128S:
93
+ case EVP_PKEY_SLH_DSA_SHAKE_128F:
94
+ case EVP_PKEY_SLH_DSA_SHAKE_192S:
95
+ case EVP_PKEY_SLH_DSA_SHAKE_192F:
96
+ case EVP_PKEY_SLH_DSA_SHAKE_256S:
97
+ case EVP_PKEY_SLH_DSA_SHAKE_256F:
98
+ return true;
99
+ default:
100
+ return false;
101
+ }
84
102
  #else
85
103
  return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448;
86
104
  #endif
@@ -58,15 +58,13 @@ void HybridVerifyHandle::update(const std::shared_ptr<ArrayBuffer>& data) {
58
58
  throw std::runtime_error("Verify not initialized");
59
59
  }
60
60
 
61
- auto native_data = ToNativeArrayBuffer(data);
62
-
63
61
  // Accumulate raw data for potential one-shot verification (Ed25519/Ed448/ML-DSA)
64
- const uint8_t* ptr = reinterpret_cast<const uint8_t*>(native_data->data());
65
- data_buffer.insert(data_buffer.end(), ptr, ptr + native_data->size());
62
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data->data());
63
+ data_buffer.insert(data_buffer.end(), ptr, ptr + data->size());
66
64
 
67
65
  // Only update digest if we have one (not needed for pure signature schemes)
68
66
  if (md != nullptr) {
69
- if (EVP_DigestUpdate(md_ctx, native_data->data(), native_data->size()) <= 0) {
67
+ if (EVP_DigestUpdate(md_ctx, data->data(), data->size()) <= 0) {
70
68
  unsigned long err = ERR_get_error();
71
69
  char err_buf[256];
72
70
  ERR_error_string_n(err, err_buf, sizeof(err_buf));
@@ -75,12 +73,32 @@ void HybridVerifyHandle::update(const std::shared_ptr<ArrayBuffer>& data) {
75
73
  }
76
74
  }
77
75
 
78
- // Check if key type requires one-shot verification (Ed25519, Ed448, ML-DSA)
76
+ // Check if key type requires one-shot verification (Ed25519, Ed448, ML-DSA, SLH-DSA)
79
77
  static bool isOneShotVariant(EVP_PKEY* pkey) {
80
78
  int type = EVP_PKEY_id(pkey);
81
79
  #if RNQC_HAS_ML_DSA
82
- return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448 || type == EVP_PKEY_ML_DSA_44 || type == EVP_PKEY_ML_DSA_65 ||
83
- type == EVP_PKEY_ML_DSA_87;
80
+ switch (type) {
81
+ case EVP_PKEY_ED25519:
82
+ case EVP_PKEY_ED448:
83
+ case EVP_PKEY_ML_DSA_44:
84
+ case EVP_PKEY_ML_DSA_65:
85
+ case EVP_PKEY_ML_DSA_87:
86
+ case EVP_PKEY_SLH_DSA_SHA2_128S:
87
+ case EVP_PKEY_SLH_DSA_SHA2_128F:
88
+ case EVP_PKEY_SLH_DSA_SHA2_192S:
89
+ case EVP_PKEY_SLH_DSA_SHA2_192F:
90
+ case EVP_PKEY_SLH_DSA_SHA2_256S:
91
+ case EVP_PKEY_SLH_DSA_SHA2_256F:
92
+ case EVP_PKEY_SLH_DSA_SHAKE_128S:
93
+ case EVP_PKEY_SLH_DSA_SHAKE_128F:
94
+ case EVP_PKEY_SLH_DSA_SHAKE_192S:
95
+ case EVP_PKEY_SLH_DSA_SHAKE_192F:
96
+ case EVP_PKEY_SLH_DSA_SHAKE_256S:
97
+ case EVP_PKEY_SLH_DSA_SHAKE_256F:
98
+ return true;
99
+ default:
100
+ return false;
101
+ }
84
102
  #else
85
103
  return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448;
86
104
  #endif
@@ -107,9 +125,8 @@ bool HybridVerifyHandle::verify(const std::shared_ptr<HybridKeyObjectHandleSpec>
107
125
  throw std::runtime_error("Invalid public key for verification");
108
126
  }
109
127
 
110
- auto native_sig = ToNativeArrayBuffer(signature);
111
- const unsigned char* sig_data = native_sig->data();
112
- size_t sig_len = native_sig->size();
128
+ const unsigned char* sig_data = signature->data();
129
+ size_t sig_len = signature->size();
113
130
 
114
131
  // Ed25519/Ed448/ML-DSA require one-shot verification with EVP_DigestVerify
115
132
  // Also use one-shot path if no digest was specified (md == nullptr)
@@ -0,0 +1,245 @@
1
+ #include "HybridSlhDsaKeyPair.hpp"
2
+
3
+ #include <NitroModules/ArrayBuffer.hpp>
4
+ #include <openssl/bio.h>
5
+ #include <openssl/err.h>
6
+ #include <openssl/pem.h>
7
+
8
+ #include <array>
9
+
10
+ #include "QuickCryptoUtils.hpp"
11
+
12
+ #if OPENSSL_VERSION_NUMBER >= 0x30500000L
13
+ #define RNQC_HAS_SLH_DSA 1
14
+ #else
15
+ #define RNQC_HAS_SLH_DSA 0
16
+ #endif
17
+
18
+ namespace margelo::nitro::crypto {
19
+
20
+ using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>;
21
+ using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
22
+
23
+ #if RNQC_HAS_SLH_DSA
24
+ static constexpr std::array<const char*, 12> kSlhDsaVariants{
25
+ "SLH-DSA-SHA2-128s", "SLH-DSA-SHA2-128f", "SLH-DSA-SHA2-192s", "SLH-DSA-SHA2-192f", "SLH-DSA-SHA2-256s", "SLH-DSA-SHA2-256f",
26
+ "SLH-DSA-SHAKE-128s", "SLH-DSA-SHAKE-128f", "SLH-DSA-SHAKE-192s", "SLH-DSA-SHAKE-192f", "SLH-DSA-SHAKE-256s", "SLH-DSA-SHAKE-256f",
27
+ };
28
+
29
+ static bool isValidSlhDsaVariant(const std::string& variant) {
30
+ for (const char* v : kSlhDsaVariants) {
31
+ if (variant == v) {
32
+ return true;
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ #endif
38
+
39
+ void HybridSlhDsaKeyPair::setVariant(const std::string& variant) {
40
+ #if !RNQC_HAS_SLH_DSA
41
+ throw std::runtime_error("SLH-DSA requires OpenSSL 3.5+");
42
+ #else
43
+ if (!isValidSlhDsaVariant(variant)) {
44
+ throw std::runtime_error("Invalid SLH-DSA variant: " + variant);
45
+ }
46
+ variant_ = variant;
47
+ #endif
48
+ }
49
+
50
+ std::shared_ptr<Promise<void>> HybridSlhDsaKeyPair::generateKeyPair(double publicFormat, double publicType, double privateFormat,
51
+ double privateType) {
52
+ auto self = this->shared_cast<HybridSlhDsaKeyPair>();
53
+ return Promise<void>::async([self, publicFormat, publicType, privateFormat, privateType]() {
54
+ self->generateKeyPairSync(publicFormat, publicType, privateFormat, privateType);
55
+ });
56
+ }
57
+
58
+ void HybridSlhDsaKeyPair::generateKeyPairSync(double publicFormat, double publicType, double privateFormat, double privateType) {
59
+ #if !RNQC_HAS_SLH_DSA
60
+ throw std::runtime_error("SLH-DSA requires OpenSSL 3.5+");
61
+ #else
62
+ clearOpenSSLErrors();
63
+
64
+ if (variant_.empty()) {
65
+ throw std::runtime_error("SLH-DSA variant not set. Call setVariant() first.");
66
+ }
67
+
68
+ publicFormat_ = static_cast<int>(publicFormat);
69
+ publicType_ = static_cast<int>(publicType);
70
+ privateFormat_ = static_cast<int>(privateFormat);
71
+ privateType_ = static_cast<int>(privateType);
72
+
73
+ pkey_.reset();
74
+
75
+ EVP_PKEY_CTX_ptr pctx(EVP_PKEY_CTX_new_from_name(nullptr, variant_.c_str(), nullptr), EVP_PKEY_CTX_free);
76
+ if (pctx == nullptr) {
77
+ throw std::runtime_error("Failed to create key context for " + variant_ + ": " + getOpenSSLError());
78
+ }
79
+
80
+ if (EVP_PKEY_keygen_init(pctx.get()) <= 0) {
81
+ throw std::runtime_error("Failed to initialize keygen: " + getOpenSSLError());
82
+ }
83
+
84
+ EVP_PKEY* raw = nullptr;
85
+ if (EVP_PKEY_keygen(pctx.get(), &raw) <= 0) {
86
+ throw std::runtime_error("Failed to generate SLH-DSA key pair: " + getOpenSSLError());
87
+ }
88
+ pkey_.reset(raw);
89
+ #endif
90
+ }
91
+
92
+ std::shared_ptr<ArrayBuffer> HybridSlhDsaKeyPair::getPublicKey() {
93
+ #if !RNQC_HAS_SLH_DSA
94
+ throw std::runtime_error("SLH-DSA requires OpenSSL 3.5+");
95
+ #else
96
+ checkKeyPair();
97
+
98
+ BIO* bio = BIO_new(BIO_s_mem());
99
+ if (!bio) {
100
+ throw std::runtime_error("Failed to create BIO for public key export");
101
+ }
102
+
103
+ int result;
104
+ if (publicFormat_ == 1) {
105
+ result = PEM_write_bio_PUBKEY(bio, pkey_.get());
106
+ } else {
107
+ result = i2d_PUBKEY_bio(bio, pkey_.get());
108
+ }
109
+
110
+ if (result != 1) {
111
+ BIO_free(bio);
112
+ throw std::runtime_error("Failed to export public key: " + getOpenSSLError());
113
+ }
114
+
115
+ BUF_MEM* bptr;
116
+ BIO_get_mem_ptr(bio, &bptr);
117
+
118
+ size_t len = bptr->length;
119
+ auto buf = std::make_unique<uint8_t[]>(len);
120
+ memcpy(buf.get(), bptr->data, len);
121
+
122
+ BIO_free(bio);
123
+
124
+ uint8_t* raw_ptr = buf.get();
125
+ return std::make_shared<NativeArrayBuffer>(buf.release(), len, [raw_ptr]() { delete[] raw_ptr; });
126
+ #endif
127
+ }
128
+
129
+ std::shared_ptr<ArrayBuffer> HybridSlhDsaKeyPair::getPrivateKey() {
130
+ #if !RNQC_HAS_SLH_DSA
131
+ throw std::runtime_error("SLH-DSA requires OpenSSL 3.5+");
132
+ #else
133
+ checkKeyPair();
134
+
135
+ BIO* bio = BIO_new(BIO_s_mem());
136
+ if (!bio) {
137
+ throw std::runtime_error("Failed to create BIO for private key export");
138
+ }
139
+
140
+ int result;
141
+ if (privateFormat_ == 1) {
142
+ result = PEM_write_bio_PrivateKey(bio, pkey_.get(), nullptr, nullptr, 0, nullptr, nullptr);
143
+ } else {
144
+ result = i2d_PKCS8PrivateKey_bio(bio, pkey_.get(), nullptr, nullptr, 0, nullptr, nullptr);
145
+ }
146
+
147
+ if (result != 1) {
148
+ BIO_free(bio);
149
+ throw std::runtime_error("Failed to export private key: " + getOpenSSLError());
150
+ }
151
+
152
+ BUF_MEM* bptr;
153
+ BIO_get_mem_ptr(bio, &bptr);
154
+
155
+ size_t len = bptr->length;
156
+ auto buf = std::make_unique<uint8_t[]>(len);
157
+ memcpy(buf.get(), bptr->data, len);
158
+
159
+ secureZero(bptr->data, bptr->length);
160
+ BIO_free(bio);
161
+
162
+ uint8_t* raw_ptr = buf.get();
163
+ return std::make_shared<NativeArrayBuffer>(buf.release(), len, [raw_ptr]() { delete[] raw_ptr; });
164
+ #endif
165
+ }
166
+
167
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> HybridSlhDsaKeyPair::sign(const std::shared_ptr<ArrayBuffer>& message) {
168
+ auto nativeMessage = ToNativeArrayBuffer(message);
169
+ auto self = this->shared_cast<HybridSlhDsaKeyPair>();
170
+ return Promise<std::shared_ptr<ArrayBuffer>>::async([self, nativeMessage]() { return self->signSync(nativeMessage); });
171
+ }
172
+
173
+ std::shared_ptr<ArrayBuffer> HybridSlhDsaKeyPair::signSync(const std::shared_ptr<ArrayBuffer>& message) {
174
+ #if !RNQC_HAS_SLH_DSA
175
+ throw std::runtime_error("SLH-DSA requires OpenSSL 3.5+");
176
+ #else
177
+ clearOpenSSLErrors();
178
+ checkKeyPair();
179
+
180
+ EVP_MD_CTX_ptr md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
181
+ if (md_ctx == nullptr) {
182
+ throw std::runtime_error("Failed to create signing context");
183
+ }
184
+
185
+ if (EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey_.get()) <= 0) {
186
+ throw std::runtime_error("Failed to initialize signing: " + getOpenSSLError());
187
+ }
188
+
189
+ size_t sig_len = 0;
190
+ if (EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, message->data(), message->size()) <= 0) {
191
+ throw std::runtime_error("Failed to calculate signature size: " + getOpenSSLError());
192
+ }
193
+
194
+ auto sig = std::make_unique<uint8_t[]>(sig_len);
195
+
196
+ if (EVP_DigestSign(md_ctx.get(), sig.get(), &sig_len, message->data(), message->size()) <= 0) {
197
+ throw std::runtime_error("Failed to sign message: " + getOpenSSLError());
198
+ }
199
+
200
+ uint8_t* raw_ptr = sig.get();
201
+ return std::make_shared<NativeArrayBuffer>(sig.release(), sig_len, [raw_ptr]() { delete[] raw_ptr; });
202
+ #endif
203
+ }
204
+
205
+ std::shared_ptr<Promise<bool>> HybridSlhDsaKeyPair::verify(const std::shared_ptr<ArrayBuffer>& signature,
206
+ const std::shared_ptr<ArrayBuffer>& message) {
207
+ auto nativeSignature = ToNativeArrayBuffer(signature);
208
+ auto nativeMessage = ToNativeArrayBuffer(message);
209
+ auto self = this->shared_cast<HybridSlhDsaKeyPair>();
210
+ return Promise<bool>::async([self, nativeSignature, nativeMessage]() { return self->verifySync(nativeSignature, nativeMessage); });
211
+ }
212
+
213
+ bool HybridSlhDsaKeyPair::verifySync(const std::shared_ptr<ArrayBuffer>& signature, const std::shared_ptr<ArrayBuffer>& message) {
214
+ #if !RNQC_HAS_SLH_DSA
215
+ throw std::runtime_error("SLH-DSA requires OpenSSL 3.5+");
216
+ #else
217
+ clearOpenSSLErrors();
218
+ checkKeyPair();
219
+
220
+ EVP_MD_CTX_ptr md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
221
+ if (md_ctx == nullptr) {
222
+ throw std::runtime_error("Failed to create verify context");
223
+ }
224
+
225
+ if (EVP_DigestVerifyInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey_.get()) <= 0) {
226
+ throw std::runtime_error("Failed to initialize verification: " + getOpenSSLError());
227
+ }
228
+
229
+ int result = EVP_DigestVerify(md_ctx.get(), signature->data(), signature->size(), message->data(), message->size());
230
+
231
+ if (result < 0) {
232
+ throw std::runtime_error("Verification error: " + getOpenSSLError());
233
+ }
234
+
235
+ return result == 1;
236
+ #endif
237
+ }
238
+
239
+ void HybridSlhDsaKeyPair::checkKeyPair() {
240
+ if (!pkey_) {
241
+ throw std::runtime_error("Key pair not initialized");
242
+ }
243
+ }
244
+
245
+ } // namespace margelo::nitro::crypto
@@ -0,0 +1,48 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <openssl/evp.h>
5
+ #include <string>
6
+
7
+ #include "HybridSlhDsaKeyPairSpec.hpp"
8
+
9
+ namespace margelo::nitro::crypto {
10
+
11
+ class HybridSlhDsaKeyPair : public HybridSlhDsaKeyPairSpec {
12
+ public:
13
+ HybridSlhDsaKeyPair() : HybridObject(TAG) {}
14
+ ~HybridSlhDsaKeyPair() override = default;
15
+
16
+ std::shared_ptr<Promise<void>> generateKeyPair(double publicFormat, double publicType, double privateFormat, double privateType) override;
17
+
18
+ void generateKeyPairSync(double publicFormat, double publicType, double privateFormat, double privateType) override;
19
+
20
+ std::shared_ptr<ArrayBuffer> getPublicKey() override;
21
+ std::shared_ptr<ArrayBuffer> getPrivateKey() override;
22
+
23
+ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> sign(const std::shared_ptr<ArrayBuffer>& message) override;
24
+
25
+ std::shared_ptr<ArrayBuffer> signSync(const std::shared_ptr<ArrayBuffer>& message) override;
26
+
27
+ std::shared_ptr<Promise<bool>> verify(const std::shared_ptr<ArrayBuffer>& signature,
28
+ const std::shared_ptr<ArrayBuffer>& message) override;
29
+
30
+ bool verifySync(const std::shared_ptr<ArrayBuffer>& signature, const std::shared_ptr<ArrayBuffer>& message) override;
31
+
32
+ void setVariant(const std::string& variant) override;
33
+
34
+ private:
35
+ using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
36
+
37
+ std::string variant_;
38
+ EVP_PKEY_ptr pkey_{nullptr, EVP_PKEY_free};
39
+
40
+ int publicFormat_ = -1;
41
+ int publicType_ = -1;
42
+ int privateFormat_ = -1;
43
+ int privateType_ = -1;
44
+
45
+ void checkKeyPair();
46
+ };
47
+
48
+ } // namespace margelo::nitro::crypto