react-native-quick-crypto 1.0.7 → 1.0.9

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 (91) hide show
  1. package/QuickCrypto.podspec +50 -4
  2. package/README.md +4 -2
  3. package/android/CMakeLists.txt +4 -0
  4. package/android/build.gradle +3 -0
  5. package/cpp/dh/HybridDiffieHellman.cpp +438 -0
  6. package/cpp/dh/HybridDiffieHellman.hpp +41 -0
  7. package/cpp/ecdh/HybridECDH.cpp +306 -0
  8. package/cpp/ecdh/HybridECDH.hpp +42 -0
  9. package/cpp/utils/QuickCryptoUtils.hpp +14 -0
  10. package/lib/commonjs/dh-groups.js +29 -0
  11. package/lib/commonjs/dh-groups.js.map +1 -0
  12. package/lib/commonjs/diffie-hellman.js +147 -0
  13. package/lib/commonjs/diffie-hellman.js.map +1 -0
  14. package/lib/commonjs/ec.js +68 -180
  15. package/lib/commonjs/ec.js.map +1 -1
  16. package/lib/commonjs/ecdh.js +71 -0
  17. package/lib/commonjs/ecdh.js.map +1 -0
  18. package/lib/commonjs/index.js +39 -1
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/commonjs/keys/generateKeyPair.js.map +1 -1
  21. package/lib/commonjs/keys/index.js +12 -0
  22. package/lib/commonjs/keys/index.js.map +1 -1
  23. package/lib/commonjs/keys/signVerify.js +42 -0
  24. package/lib/commonjs/keys/signVerify.js.map +1 -1
  25. package/lib/commonjs/specs/diffie-hellman.nitro.js +6 -0
  26. package/lib/commonjs/specs/diffie-hellman.nitro.js.map +1 -0
  27. package/lib/commonjs/specs/ecdh.nitro.js +6 -0
  28. package/lib/commonjs/specs/ecdh.nitro.js.map +1 -0
  29. package/lib/commonjs/subtle.js +2 -0
  30. package/lib/commonjs/subtle.js.map +1 -1
  31. package/lib/module/dh-groups.js +25 -0
  32. package/lib/module/dh-groups.js.map +1 -0
  33. package/lib/module/diffie-hellman.js +140 -0
  34. package/lib/module/diffie-hellman.js.map +1 -0
  35. package/lib/module/ec.js +65 -178
  36. package/lib/module/ec.js.map +1 -1
  37. package/lib/module/ecdh.js +65 -0
  38. package/lib/module/ecdh.js.map +1 -0
  39. package/lib/module/index.js +13 -1
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/keys/generateKeyPair.js.map +1 -1
  42. package/lib/module/keys/index.js +2 -2
  43. package/lib/module/keys/index.js.map +1 -1
  44. package/lib/module/keys/signVerify.js +40 -0
  45. package/lib/module/keys/signVerify.js.map +1 -1
  46. package/lib/module/specs/diffie-hellman.nitro.js +4 -0
  47. package/lib/module/specs/diffie-hellman.nitro.js.map +1 -0
  48. package/lib/module/specs/ecdh.nitro.js +4 -0
  49. package/lib/module/specs/ecdh.nitro.js.map +1 -0
  50. package/lib/module/subtle.js +3 -1
  51. package/lib/module/subtle.js.map +1 -1
  52. package/lib/tsconfig.tsbuildinfo +1 -1
  53. package/lib/typescript/dh-groups.d.ts +5 -0
  54. package/lib/typescript/dh-groups.d.ts.map +1 -0
  55. package/lib/typescript/diffie-hellman.d.ts +16 -0
  56. package/lib/typescript/diffie-hellman.d.ts.map +1 -0
  57. package/lib/typescript/ec.d.ts +2 -1
  58. package/lib/typescript/ec.d.ts.map +1 -1
  59. package/lib/typescript/ecdh.d.ts +16 -0
  60. package/lib/typescript/ecdh.d.ts.map +1 -0
  61. package/lib/typescript/index.d.ts +13 -0
  62. package/lib/typescript/index.d.ts.map +1 -1
  63. package/lib/typescript/keys/generateKeyPair.d.ts.map +1 -1
  64. package/lib/typescript/keys/index.d.ts +2 -2
  65. package/lib/typescript/keys/index.d.ts.map +1 -1
  66. package/lib/typescript/keys/signVerify.d.ts +6 -0
  67. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  68. package/lib/typescript/specs/diffie-hellman.nitro.d.ts +17 -0
  69. package/lib/typescript/specs/diffie-hellman.nitro.d.ts.map +1 -0
  70. package/lib/typescript/specs/ecdh.nitro.d.ts +14 -0
  71. package/lib/typescript/specs/ecdh.nitro.d.ts.map +1 -0
  72. package/lib/typescript/subtle.d.ts.map +1 -1
  73. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +2 -0
  74. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +20 -0
  75. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +20 -0
  76. package/nitrogen/generated/shared/c++/HybridDiffieHellmanSpec.cpp +30 -0
  77. package/nitrogen/generated/shared/c++/HybridDiffieHellmanSpec.hpp +72 -0
  78. package/nitrogen/generated/shared/c++/HybridECDHSpec.cpp +27 -0
  79. package/nitrogen/generated/shared/c++/HybridECDHSpec.hpp +70 -0
  80. package/package.json +10 -7
  81. package/src/dh-groups.ts +27 -0
  82. package/src/diffie-hellman.ts +191 -0
  83. package/src/ec.ts +73 -177
  84. package/src/ecdh.ts +76 -0
  85. package/src/index.ts +12 -0
  86. package/src/keys/generateKeyPair.ts +11 -2
  87. package/src/keys/index.ts +10 -1
  88. package/src/keys/signVerify.ts +84 -0
  89. package/src/specs/diffie-hellman.nitro.ts +15 -0
  90. package/src/specs/ecdh.nitro.ts +11 -0
  91. package/src/subtle.ts +8 -1
@@ -22,10 +22,50 @@ Pod::Spec.new do |s|
22
22
  sodium_enabled = ENV['SODIUM_ENABLED'] == '1'
23
23
  Pod::UI.puts("[QuickCrypto] 🧂 has libsodium #{sodium_enabled ? "enabled" : "disabled"}!")
24
24
 
25
+ # Ensure libsodium source is present during podspec evaluation when enabled.
26
+ # This is necessary because prepare_command is skipped for :path pods.
27
+ if sodium_enabled
28
+ sodium_version = "1.0.20"
29
+ sodium_dir = File.join(__dir__, "ios", "libsodium-stable")
30
+ sodium_header = File.join(sodium_dir, "src", "libsodium", "include", "sodium.h")
31
+ unless File.exist?(sodium_header)
32
+ FileUtils.mkdir_p(File.join(__dir__, "ios"))
33
+ FileUtils.rm_rf(sodium_dir) if File.directory?(sodium_dir)
34
+
35
+ Pod::UI.puts "[QuickCrypto] ⬇️ Downloading libsodium source..."
36
+ Dir.chdir(__dir__) do
37
+ system("curl -sSfL --connect-timeout 30 --max-time 300 -o ios/libsodium.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-#{sodium_version}-stable.tar.gz") || raise("Failed to download libsodium")
38
+ system("tar -xzf ios/libsodium.tar.gz -C ios") || raise("Failed to extract libsodium")
39
+ File.delete("ios/libsodium.tar.gz") if File.exist?("ios/libsodium.tar.gz")
40
+ end
41
+ Pod::UI.puts "[QuickCrypto] ✅ libsodium source downloaded successfully"
42
+ end
43
+ end
44
+
25
45
  # OpenSSL 3.6+ vendored xcframework (not yet on CocoaPods trunk)
26
- openssl_version = "3.6.0000"
46
+ openssl_version = "3.6.0001"
27
47
  openssl_url = "https://github.com/krzyzanowskim/OpenSSL/releases/download/#{openssl_version}/OpenSSL.xcframework.zip"
28
48
 
49
+ # Ensure OpenSSL.xcframework is present during podspec evaluation.
50
+ # This is necessary because prepare_command is skipped for :path pods,
51
+ # which is how React Native native modules are installed.
52
+ # See: https://github.com/margelo/react-native-quick-crypto/issues/882
53
+ openssl_dir = File.join(__dir__, "OpenSSL.xcframework")
54
+ openssl_plist = File.join(openssl_dir, "Info.plist")
55
+ unless File.exist?(openssl_plist)
56
+ # Clean up any partial download
57
+ FileUtils.rm_rf(openssl_dir) if File.directory?(openssl_dir)
58
+ FileUtils.rm_f(File.join(__dir__, "OpenSSL.xcframework.zip"))
59
+
60
+ Pod::UI.puts "[QuickCrypto] ⬇️ Downloading OpenSSL.xcframework..."
61
+ Dir.chdir(__dir__) do
62
+ system("curl -sSfL --connect-timeout 30 --max-time 300 -o OpenSSL.xcframework.zip #{openssl_url}") || raise("Failed to download OpenSSL")
63
+ system("unzip -q -o OpenSSL.xcframework.zip") || raise("Failed to unzip OpenSSL")
64
+ File.delete("OpenSSL.xcframework.zip") if File.exist?("OpenSSL.xcframework.zip")
65
+ end
66
+ Pod::UI.puts "[QuickCrypto] ✅ OpenSSL.xcframework downloaded successfully"
67
+ end
68
+
29
69
  if sodium_enabled
30
70
  # Build libsodium from source for XSalsa20 cipher support
31
71
  # CocoaPods packages are outdated (1.0.12) and SPM causes module conflicts
@@ -132,18 +172,24 @@ Pod::Spec.new do |s|
132
172
  cpp_headers = [
133
173
  "\"$(PODS_TARGET_SRCROOT)/cpp/utils\"",
134
174
  "\"$(PODS_TARGET_SRCROOT)/cpp/hkdf\"",
175
+ "\"$(PODS_TARGET_SRCROOT)/cpp/dh\"",
176
+ "\"$(PODS_TARGET_SRCROOT)/cpp/ecdh\"",
177
+ "\"$(PODS_TARGET_SRCROOT)/nitrogen/generated/shared/c++\"",
135
178
  "\"$(PODS_TARGET_SRCROOT)/deps/ncrypto/include\"",
136
179
  "\"$(PODS_TARGET_SRCROOT)/deps/blake3/c\"",
137
180
  "\"$(PODS_TARGET_SRCROOT)/deps/fastpbkdf2\""
138
181
  ]
139
182
 
140
183
  if sodium_enabled
184
+ # Use absolute path from __dir__ which resolves symlinks correctly.
185
+ # This is necessary in monorepo setups where the podspec is accessed via symlink
186
+ # but the source files are resolved to their real paths during compilation.
187
+ real_sodium_include = File.join(__dir__, "ios", "libsodium-stable", "src", "libsodium", "include")
141
188
  sodium_headers = [
142
189
  "\"$(PODS_TARGET_SRCROOT)/ios/libsodium-stable/src/libsodium/include\"",
143
190
  "\"$(PODS_TARGET_SRCROOT)/ios/libsodium-stable/src/libsodium/include/sodium\"",
144
- "\"$(PODS_TARGET_SRCROOT)/ios/libsodium-stable\"",
145
- "\"$(PODS_ROOT)/../../packages/react-native-quick-crypto/ios/libsodium-stable/src/libsodium/include\"",
146
- "\"$(PODS_ROOT)/../../packages/react-native-quick-crypto/ios/libsodium-stable/src/libsodium/include/sodium\""
191
+ "\"#{real_sodium_include}\"",
192
+ "\"#{real_sodium_include}/sodium\""
147
193
  ]
148
194
  xcconfig["HEADER_SEARCH_PATHS"] = (cpp_headers + sodium_headers).join(' ')
149
195
  xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES BLSALLOC_SODIUM=1"
package/README.md CHANGED
@@ -50,7 +50,7 @@ There is a benchmark suite in the Example app in this repo that has benchmarks o
50
50
  </h3>
51
51
 
52
52
  ```sh
53
- bun add react-native-quick-crypto react-native-nitro-modules
53
+ bun add react-native-quick-crypto react-native-nitro-modules react-native-quick-base64
54
54
  cd ios && pod install
55
55
  ```
56
56
 
@@ -114,7 +114,7 @@ module.exports = {
114
114
  + alias: {
115
115
  + 'crypto': 'react-native-quick-crypto',
116
116
  + 'stream': 'readable-stream',
117
- + 'buffer': '@craftzdog/react-native-buffer',
117
+ + 'buffer': 'react-native-quick-crypto',
118
118
  + },
119
119
  + },
120
120
  + ],
@@ -123,6 +123,8 @@ module.exports = {
123
123
  };
124
124
  ```
125
125
 
126
+ > **Note:** `react-native-quick-crypto` re-exports `Buffer` from `@craftzdog/react-native-buffer`, so you can use either as the buffer alias. Using `react-native-quick-crypto` ensures a single Buffer instance across your app.
127
+
126
128
  Then restart your bundler using `yarn start --reset-cache`.
127
129
 
128
130
  ## Usage
@@ -34,7 +34,9 @@ add_library(
34
34
  ../cpp/cipher/XSalsa20Cipher.cpp
35
35
  ../cpp/cipher/ChaCha20Cipher.cpp
36
36
  ../cpp/cipher/ChaCha20Poly1305Cipher.cpp
37
+ ../cpp/dh/HybridDiffieHellman.cpp
37
38
  ../cpp/ec/HybridEcKeyPair.cpp
39
+ ../cpp/ecdh/HybridECDH.cpp
38
40
  ../cpp/ed25519/HybridEdKeyPair.cpp
39
41
  ../cpp/hash/HybridHash.cpp
40
42
  ../cpp/hmac/HybridHmac.cpp
@@ -62,7 +64,9 @@ include_directories(
62
64
  "src/main/cpp"
63
65
  "../cpp/blake3"
64
66
  "../cpp/cipher"
67
+ "../cpp/dh"
65
68
  "../cpp/ec"
69
+ "../cpp/ecdh"
66
70
  "../cpp/ed25519"
67
71
  "../cpp/hash"
68
72
  "../cpp/hkdf"
@@ -79,6 +79,9 @@ android {
79
79
  buildFeatures {
80
80
  buildConfig true
81
81
  prefab true
82
+ // Explicitly disable RenderScript (deprecated since Android 12, removed in AGP 9.0)
83
+ // to suppress 'isRenderscriptDebuggable is obsolete' warning. See #802.
84
+ renderScript = false
82
85
  }
83
86
 
84
87
  packagingOptions {
@@ -0,0 +1,438 @@
1
+ #include "HybridDiffieHellman.hpp"
2
+ #include "QuickCryptoUtils.hpp"
3
+ #include <NitroModules/ArrayBuffer.hpp>
4
+ #include <openssl/bn.h>
5
+ #include <openssl/dh.h>
6
+ #include <openssl/err.h>
7
+ #include <openssl/evp.h>
8
+ #include <stdexcept>
9
+
10
+ namespace margelo::nitro::crypto {
11
+
12
+ // Smart pointer type aliases for RAII
13
+ using BN_ptr = std::unique_ptr<BIGNUM, decltype(&BN_free)>;
14
+ using DH_ptr = std::unique_ptr<DH, decltype(&DH_free)>;
15
+ using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
16
+
17
+ // Minimum DH prime size for security (2048 bits = 256 bytes)
18
+ static constexpr int kMinDHPrimeBits = 2048;
19
+
20
+ // Suppress deprecation warnings for DH_* functions
21
+ // Node.js ncrypto uses the same pattern - these APIs work but are deprecated in OpenSSL 3.x
22
+ #pragma clang diagnostic push
23
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
24
+
25
+ void HybridDiffieHellman::init(const std::shared_ptr<ArrayBuffer>& prime, const std::shared_ptr<ArrayBuffer>& generator) {
26
+ // Create DH structure
27
+ DH_ptr dh(DH_new(), DH_free);
28
+ if (!dh) {
29
+ throw std::runtime_error("DiffieHellman: failed to create DH structure");
30
+ }
31
+
32
+ // Convert prime and generator to BIGNUMs
33
+ BIGNUM* p = BN_bin2bn(prime->data(), static_cast<int>(prime->size()), nullptr);
34
+ BIGNUM* g = BN_bin2bn(generator->data(), static_cast<int>(generator->size()), nullptr);
35
+
36
+ if (!p || !g) {
37
+ if (p)
38
+ BN_free(p);
39
+ if (g)
40
+ BN_free(g);
41
+ throw std::runtime_error("DiffieHellman: failed to convert parameters to BIGNUM");
42
+ }
43
+
44
+ // DH_set0_pqg takes ownership of p and g on success
45
+ if (DH_set0_pqg(dh.get(), p, nullptr, g) != 1) {
46
+ BN_free(p);
47
+ BN_free(g);
48
+ throw std::runtime_error("DiffieHellman: failed to set DH parameters");
49
+ }
50
+
51
+ // Create EVP_PKEY and assign DH to it
52
+ EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
53
+ if (!pkey) {
54
+ throw std::runtime_error("DiffieHellman: failed to create EVP_PKEY");
55
+ }
56
+
57
+ // EVP_PKEY_assign_DH takes ownership of dh on success
58
+ if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
59
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
60
+ }
61
+ dh.release(); // EVP_PKEY now owns the DH
62
+
63
+ _pkey = std::move(pkey);
64
+ }
65
+
66
+ void HybridDiffieHellman::initWithSize(double primeLength, double generator) {
67
+ int primeBits = static_cast<int>(primeLength);
68
+ int gen = static_cast<int>(generator);
69
+
70
+ // Validate minimum key size for security
71
+ if (primeBits < kMinDHPrimeBits) {
72
+ throw std::runtime_error("DiffieHellman: prime length must be at least 2048 bits");
73
+ }
74
+
75
+ // Create parameter generation context
76
+ EVP_PKEY_CTX_ptr pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr), EVP_PKEY_CTX_free);
77
+ if (!pctx) {
78
+ throw std::runtime_error("DiffieHellman: failed to create parameter context");
79
+ }
80
+
81
+ if (EVP_PKEY_paramgen_init(pctx.get()) <= 0) {
82
+ throw std::runtime_error("DiffieHellman: failed to initialize parameter generation");
83
+ }
84
+
85
+ if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx.get(), primeBits) <= 0) {
86
+ throw std::runtime_error("DiffieHellman: failed to set prime length");
87
+ }
88
+
89
+ if (EVP_PKEY_CTX_set_dh_paramgen_generator(pctx.get(), gen) <= 0) {
90
+ throw std::runtime_error("DiffieHellman: failed to set generator");
91
+ }
92
+
93
+ EVP_PKEY* params = nullptr;
94
+ if (EVP_PKEY_paramgen(pctx.get(), &params) <= 0) {
95
+ throw std::runtime_error("DiffieHellman: failed to generate parameters");
96
+ }
97
+
98
+ _pkey.reset(params);
99
+ }
100
+
101
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::generateKeys() {
102
+ ensureInitialized();
103
+
104
+ EVP_PKEY_CTX_ptr kctx(EVP_PKEY_CTX_new(_pkey.get(), nullptr), EVP_PKEY_CTX_free);
105
+ if (!kctx) {
106
+ throw std::runtime_error("DiffieHellman: failed to create keygen context");
107
+ }
108
+
109
+ if (EVP_PKEY_keygen_init(kctx.get()) <= 0) {
110
+ throw std::runtime_error("DiffieHellman: failed to initialize key generation");
111
+ }
112
+
113
+ EVP_PKEY* newKey = nullptr;
114
+ if (EVP_PKEY_keygen(kctx.get(), &newKey) <= 0) {
115
+ throw std::runtime_error("DiffieHellman: failed to generate key pair");
116
+ }
117
+
118
+ // Replace parameters-only key with full key (which includes parameters)
119
+ _pkey.reset(newKey);
120
+
121
+ return getPublicKey();
122
+ }
123
+
124
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::computeSecret(const std::shared_ptr<ArrayBuffer>& otherPublicKey) {
125
+ ensureInitialized();
126
+
127
+ const DH* ourDh = getDH();
128
+ const BIGNUM *p, *q, *g;
129
+ DH_get0_pqg(ourDh, &p, &q, &g);
130
+
131
+ // Create peer DH with same parameters but peer's public key
132
+ DH_ptr peerDh(DH_new(), DH_free);
133
+ if (!peerDh) {
134
+ throw std::runtime_error("DiffieHellman: failed to create peer DH structure");
135
+ }
136
+
137
+ // Duplicate parameters for peer
138
+ BIGNUM* peerP = BN_dup(p);
139
+ BIGNUM* peerG = BN_dup(g);
140
+ BIGNUM* peerPubKey = BN_bin2bn(otherPublicKey->data(), static_cast<int>(otherPublicKey->size()), nullptr);
141
+
142
+ if (!peerP || !peerG || !peerPubKey) {
143
+ if (peerP)
144
+ BN_free(peerP);
145
+ if (peerG)
146
+ BN_free(peerG);
147
+ if (peerPubKey)
148
+ BN_free(peerPubKey);
149
+ throw std::runtime_error("DiffieHellman: failed to create peer parameters");
150
+ }
151
+
152
+ // Set peer DH parameters (takes ownership on success)
153
+ if (DH_set0_pqg(peerDh.get(), peerP, nullptr, peerG) != 1) {
154
+ BN_free(peerP);
155
+ BN_free(peerG);
156
+ BN_free(peerPubKey);
157
+ throw std::runtime_error("DiffieHellman: failed to set peer DH parameters");
158
+ }
159
+
160
+ // Set peer public key (takes ownership on success)
161
+ if (DH_set0_key(peerDh.get(), peerPubKey, nullptr) != 1) {
162
+ BN_free(peerPubKey);
163
+ throw std::runtime_error("DiffieHellman: failed to set peer public key");
164
+ }
165
+
166
+ // Create peer EVP_PKEY
167
+ EVP_PKEY_ptr peerPkey(EVP_PKEY_new(), EVP_PKEY_free);
168
+ if (!peerPkey) {
169
+ throw std::runtime_error("DiffieHellman: failed to create peer EVP_PKEY");
170
+ }
171
+
172
+ // EVP_PKEY_assign_DH takes ownership of peerDh on success
173
+ if (EVP_PKEY_assign_DH(peerPkey.get(), peerDh.get()) != 1) {
174
+ throw std::runtime_error("DiffieHellman: failed to assign peer DH to EVP_PKEY");
175
+ }
176
+ peerDh.release(); // EVP_PKEY now owns the DH
177
+
178
+ // Derive shared secret using EVP API
179
+ EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(_pkey.get(), nullptr), EVP_PKEY_CTX_free);
180
+ if (!ctx) {
181
+ throw std::runtime_error("DiffieHellman: failed to create derive context");
182
+ }
183
+
184
+ if (EVP_PKEY_derive_init(ctx.get()) <= 0) {
185
+ throw std::runtime_error("DiffieHellman: failed to initialize key derivation");
186
+ }
187
+
188
+ if (EVP_PKEY_derive_set_peer(ctx.get(), peerPkey.get()) <= 0) {
189
+ throw std::runtime_error("DiffieHellman: failed to set peer key for derivation");
190
+ }
191
+
192
+ // Get required buffer size
193
+ size_t secretLen = 0;
194
+ if (EVP_PKEY_derive(ctx.get(), nullptr, &secretLen) <= 0) {
195
+ throw std::runtime_error("DiffieHellman: failed to get shared secret length");
196
+ }
197
+
198
+ // Derive the shared secret
199
+ std::vector<uint8_t> secret(secretLen);
200
+ if (EVP_PKEY_derive(ctx.get(), secret.data(), &secretLen) <= 0) {
201
+ throw std::runtime_error("DiffieHellman: failed to derive shared secret");
202
+ }
203
+
204
+ // Resize to actual length (may be smaller due to leading zeros)
205
+ secret.resize(secretLen);
206
+
207
+ return ToNativeArrayBuffer(secret);
208
+ }
209
+
210
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPrime() {
211
+ ensureInitialized();
212
+ const DH* dh = getDH();
213
+
214
+ const BIGNUM *p, *q, *g;
215
+ DH_get0_pqg(dh, &p, &q, &g);
216
+ if (!p) {
217
+ throw std::runtime_error("DiffieHellman: no prime available");
218
+ }
219
+
220
+ int len = BN_num_bytes(p);
221
+ std::vector<uint8_t> buf(len);
222
+ BN_bn2bin(p, buf.data());
223
+
224
+ return ToNativeArrayBuffer(buf);
225
+ }
226
+
227
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getGenerator() {
228
+ ensureInitialized();
229
+ const DH* dh = getDH();
230
+
231
+ const BIGNUM *p, *q, *g;
232
+ DH_get0_pqg(dh, &p, &q, &g);
233
+ if (!g) {
234
+ throw std::runtime_error("DiffieHellman: no generator available");
235
+ }
236
+
237
+ int len = BN_num_bytes(g);
238
+ std::vector<uint8_t> buf(len);
239
+ BN_bn2bin(g, buf.data());
240
+
241
+ return ToNativeArrayBuffer(buf);
242
+ }
243
+
244
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPublicKey() {
245
+ ensureInitialized();
246
+ const DH* dh = getDH();
247
+
248
+ const BIGNUM *pub, *priv;
249
+ DH_get0_key(dh, &pub, &priv);
250
+ if (!pub) {
251
+ throw std::runtime_error("DiffieHellman: no public key available");
252
+ }
253
+
254
+ int len = BN_num_bytes(pub);
255
+ std::vector<uint8_t> buf(len);
256
+ BN_bn2bin(pub, buf.data());
257
+
258
+ return ToNativeArrayBuffer(buf);
259
+ }
260
+
261
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPrivateKey() {
262
+ ensureInitialized();
263
+ const DH* dh = getDH();
264
+
265
+ const BIGNUM *pub, *priv;
266
+ DH_get0_key(dh, &pub, &priv);
267
+ if (!priv) {
268
+ throw std::runtime_error("DiffieHellman: no private key available");
269
+ }
270
+
271
+ int len = BN_num_bytes(priv);
272
+ std::vector<uint8_t> buf(len);
273
+ BN_bn2bin(priv, buf.data());
274
+
275
+ return ToNativeArrayBuffer(buf);
276
+ }
277
+
278
+ void HybridDiffieHellman::setPublicKey(const std::shared_ptr<ArrayBuffer>& publicKey) {
279
+ ensureInitialized();
280
+ const DH* dh = getDH();
281
+
282
+ // Get existing keys
283
+ const BIGNUM *oldPub, *oldPriv;
284
+ DH_get0_key(dh, &oldPub, &oldPriv);
285
+
286
+ // Get parameters
287
+ const BIGNUM *p, *q, *g;
288
+ DH_get0_pqg(dh, &p, &q, &g);
289
+
290
+ // Create new DH with copied parameters
291
+ DH_ptr newDh(DH_new(), DH_free);
292
+ if (!newDh) {
293
+ throw std::runtime_error("DiffieHellman: failed to create new DH structure");
294
+ }
295
+
296
+ // Duplicate parameters
297
+ BIGNUM* newP = BN_dup(p);
298
+ BIGNUM* newQ = q ? BN_dup(q) : nullptr;
299
+ BIGNUM* newG = BN_dup(g);
300
+
301
+ if (!newP || !newG) {
302
+ if (newP)
303
+ BN_free(newP);
304
+ if (newQ)
305
+ BN_free(newQ);
306
+ if (newG)
307
+ BN_free(newG);
308
+ throw std::runtime_error("DiffieHellman: failed to duplicate parameters");
309
+ }
310
+
311
+ if (DH_set0_pqg(newDh.get(), newP, newQ, newG) != 1) {
312
+ BN_free(newP);
313
+ if (newQ)
314
+ BN_free(newQ);
315
+ BN_free(newG);
316
+ throw std::runtime_error("DiffieHellman: failed to set parameters");
317
+ }
318
+
319
+ // Convert new public key
320
+ BIGNUM* newPub = BN_bin2bn(publicKey->data(), static_cast<int>(publicKey->size()), nullptr);
321
+ BIGNUM* newPriv = oldPriv ? BN_dup(oldPriv) : nullptr;
322
+
323
+ if (!newPub) {
324
+ if (newPriv)
325
+ BN_free(newPriv);
326
+ throw std::runtime_error("DiffieHellman: failed to convert public key");
327
+ }
328
+
329
+ if (DH_set0_key(newDh.get(), newPub, newPriv) != 1) {
330
+ BN_free(newPub);
331
+ if (newPriv)
332
+ BN_free(newPriv);
333
+ throw std::runtime_error("DiffieHellman: failed to set keys");
334
+ }
335
+
336
+ // Create new EVP_PKEY
337
+ EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
338
+ if (!newPkey) {
339
+ throw std::runtime_error("DiffieHellman: failed to create new EVP_PKEY");
340
+ }
341
+
342
+ if (EVP_PKEY_assign_DH(newPkey.get(), newDh.get()) != 1) {
343
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
344
+ }
345
+ newDh.release();
346
+
347
+ _pkey = std::move(newPkey);
348
+ }
349
+
350
+ void HybridDiffieHellman::setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) {
351
+ ensureInitialized();
352
+ const DH* dh = getDH();
353
+
354
+ // Get existing keys
355
+ const BIGNUM *oldPub, *oldPriv;
356
+ DH_get0_key(dh, &oldPub, &oldPriv);
357
+
358
+ // Get parameters
359
+ const BIGNUM *p, *q, *g;
360
+ DH_get0_pqg(dh, &p, &q, &g);
361
+
362
+ // Create new DH with copied parameters
363
+ DH_ptr newDh(DH_new(), DH_free);
364
+ if (!newDh) {
365
+ throw std::runtime_error("DiffieHellman: failed to create new DH structure");
366
+ }
367
+
368
+ // Duplicate parameters
369
+ BIGNUM* newP = BN_dup(p);
370
+ BIGNUM* newQ = q ? BN_dup(q) : nullptr;
371
+ BIGNUM* newG = BN_dup(g);
372
+
373
+ if (!newP || !newG) {
374
+ if (newP)
375
+ BN_free(newP);
376
+ if (newQ)
377
+ BN_free(newQ);
378
+ if (newG)
379
+ BN_free(newG);
380
+ throw std::runtime_error("DiffieHellman: failed to duplicate parameters");
381
+ }
382
+
383
+ if (DH_set0_pqg(newDh.get(), newP, newQ, newG) != 1) {
384
+ BN_free(newP);
385
+ if (newQ)
386
+ BN_free(newQ);
387
+ BN_free(newG);
388
+ throw std::runtime_error("DiffieHellman: failed to set parameters");
389
+ }
390
+
391
+ // Convert new private key
392
+ BIGNUM* newPub = oldPub ? BN_dup(oldPub) : nullptr;
393
+ BIGNUM* newPriv = BN_bin2bn(privateKey->data(), static_cast<int>(privateKey->size()), nullptr);
394
+
395
+ if (!newPriv) {
396
+ if (newPub)
397
+ BN_free(newPub);
398
+ throw std::runtime_error("DiffieHellman: failed to convert private key");
399
+ }
400
+
401
+ if (DH_set0_key(newDh.get(), newPub, newPriv) != 1) {
402
+ if (newPub)
403
+ BN_free(newPub);
404
+ BN_free(newPriv);
405
+ throw std::runtime_error("DiffieHellman: failed to set keys");
406
+ }
407
+
408
+ // Create new EVP_PKEY
409
+ EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
410
+ if (!newPkey) {
411
+ throw std::runtime_error("DiffieHellman: failed to create new EVP_PKEY");
412
+ }
413
+
414
+ if (EVP_PKEY_assign_DH(newPkey.get(), newDh.get()) != 1) {
415
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
416
+ }
417
+ newDh.release();
418
+
419
+ _pkey = std::move(newPkey);
420
+ }
421
+
422
+ void HybridDiffieHellman::ensureInitialized() const {
423
+ if (!_pkey) {
424
+ throw std::runtime_error("DiffieHellman: not initialized");
425
+ }
426
+ }
427
+
428
+ const DH* HybridDiffieHellman::getDH() const {
429
+ const DH* dh = EVP_PKEY_get0_DH(_pkey.get());
430
+ if (!dh) {
431
+ throw std::runtime_error("DiffieHellman: key is not a DH key");
432
+ }
433
+ return dh;
434
+ }
435
+
436
+ #pragma clang diagnostic pop
437
+
438
+ } // namespace margelo::nitro::crypto
@@ -0,0 +1,41 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <openssl/dh.h>
5
+ #include <openssl/evp.h>
6
+ #include <string>
7
+ #include <vector>
8
+
9
+ #include "HybridDiffieHellmanSpec.hpp"
10
+
11
+ namespace margelo::nitro::crypto {
12
+
13
+ using namespace facebook;
14
+ using margelo::nitro::ArrayBuffer;
15
+
16
+ using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
17
+
18
+ class HybridDiffieHellman : public HybridDiffieHellmanSpec {
19
+ public:
20
+ HybridDiffieHellman() : HybridObject("DiffieHellman"), _pkey(nullptr, EVP_PKEY_free) {}
21
+ virtual ~HybridDiffieHellman() = default;
22
+
23
+ void init(const std::shared_ptr<ArrayBuffer>& prime, const std::shared_ptr<ArrayBuffer>& generator) override;
24
+ void initWithSize(double primeLength, double generator) override;
25
+ std::shared_ptr<ArrayBuffer> generateKeys() override;
26
+ std::shared_ptr<ArrayBuffer> computeSecret(const std::shared_ptr<ArrayBuffer>& otherPublicKey) override;
27
+ std::shared_ptr<ArrayBuffer> getPrime() override;
28
+ std::shared_ptr<ArrayBuffer> getGenerator() override;
29
+ std::shared_ptr<ArrayBuffer> getPublicKey() override;
30
+ std::shared_ptr<ArrayBuffer> getPrivateKey() override;
31
+ void setPublicKey(const std::shared_ptr<ArrayBuffer>& publicKey) override;
32
+ void setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) override;
33
+
34
+ private:
35
+ EVP_PKEY_ptr _pkey;
36
+
37
+ void ensureInitialized() const;
38
+ const DH* getDH() const;
39
+ };
40
+
41
+ } // namespace margelo::nitro::crypto