@hxa-rn/react-native-quick-crypto 1.1.0-beta.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,501 +1,501 @@
1
- #include "HybridDiffieHellman.hpp"
2
- #include "QuickCryptoUtils.hpp"
3
- #include <NitroModules/ArrayBuffer.hpp>
4
- #include <cmath>
5
- #include <openssl/bn.h>
6
- #include <openssl/dh.h>
7
- #include <openssl/err.h>
8
- #include <openssl/evp.h>
9
- #include <stdexcept>
10
-
11
- namespace margelo::nitro::crypto {
12
-
13
- // Smart pointer type aliases for RAII
14
- using BN_ptr = std::unique_ptr<BIGNUM, decltype(&BN_free)>;
15
- using DH_ptr = std::unique_ptr<DH, decltype(&DH_free)>;
16
- using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
17
-
18
- // Align with Node.js practical floor (tests / legacy interop). Prefer >=2048 in production.
19
- static constexpr int kMinDHPrimeBits = 512;
20
-
21
- // Suppress deprecation warnings for DH_* functions
22
- // Node.js ncrypto uses the same pattern - these APIs work but are deprecated in OpenSSL 3.x
23
- #pragma clang diagnostic push
24
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
25
-
26
- static BIGNUM* getKnownModpPrime(int primeBits) {
27
- switch (primeBits) {
28
- case 2048:
29
- return BN_get_rfc3526_prime_2048(nullptr);
30
- case 3072:
31
- return BN_get_rfc3526_prime_3072(nullptr);
32
- case 4096:
33
- return BN_get_rfc3526_prime_4096(nullptr);
34
- case 6144:
35
- return BN_get_rfc3526_prime_6144(nullptr);
36
- case 8192:
37
- return BN_get_rfc3526_prime_8192(nullptr);
38
- default:
39
- return nullptr;
40
- }
41
- }
42
-
43
- void HybridDiffieHellman::init(const std::shared_ptr<ArrayBuffer>& prime, const std::shared_ptr<ArrayBuffer>& generator) {
44
- // Create DH structure
45
- DH_ptr dh(DH_new(), DH_free);
46
- if (!dh) {
47
- throw std::runtime_error("DiffieHellman: failed to create DH structure");
48
- }
49
-
50
- // Convert prime and generator to BIGNUMs
51
- BIGNUM* p = BN_bin2bn(prime->data(), static_cast<int>(prime->size()), nullptr);
52
- BIGNUM* g = BN_bin2bn(generator->data(), static_cast<int>(generator->size()), nullptr);
53
-
54
- if (!p || !g) {
55
- if (p)
56
- BN_free(p);
57
- if (g)
58
- BN_free(g);
59
- throw std::runtime_error("DiffieHellman: failed to convert parameters to BIGNUM");
60
- }
61
-
62
- // DH_set0_pqg takes ownership of p and g on success
63
- if (DH_set0_pqg(dh.get(), p, nullptr, g) != 1) {
64
- BN_free(p);
65
- BN_free(g);
66
- throw std::runtime_error("DiffieHellman: failed to set DH parameters");
67
- }
68
-
69
- // Create EVP_PKEY and assign DH to it
70
- EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
71
- if (!pkey) {
72
- throw std::runtime_error("DiffieHellman: failed to create EVP_PKEY");
73
- }
74
-
75
- // EVP_PKEY_assign_DH takes ownership of dh on success
76
- if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
77
- throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
78
- }
79
- dh.release(); // EVP_PKEY now owns the DH
80
-
81
- _pkey = std::move(pkey);
82
- }
83
-
84
- void HybridDiffieHellman::initWithSize(double primeLength, double generator) {
85
- int primeBits = static_cast<int>(std::llround(primeLength));
86
- int gen = static_cast<int>(std::llround(generator));
87
- if (gen <= 0) {
88
- gen = 2;
89
- }
90
-
91
- if (primeBits < kMinDHPrimeBits) {
92
- throw std::runtime_error("DiffieHellman: prime length must be at least 512 bits");
93
- }
94
-
95
- // Fast path: well-known MODP groups (avoids multi-minute DH paramgen for 2048+)
96
- if (BIGNUM* knownPrime = getKnownModpPrime(primeBits)) {
97
- DH_ptr dh(DH_new(), DH_free);
98
- if (!dh) {
99
- BN_free(knownPrime);
100
- throw std::runtime_error("DiffieHellman: failed to create DH structure");
101
- }
102
- BIGNUM* g = BN_new();
103
- if (!g || BN_set_word(g, static_cast<unsigned long>(gen)) != 1) {
104
- if (g)
105
- BN_free(g);
106
- BN_free(knownPrime);
107
- throw std::runtime_error("DiffieHellman: failed to set generator");
108
- }
109
- if (DH_set0_pqg(dh.get(), knownPrime, nullptr, g) != 1) {
110
- BN_free(knownPrime);
111
- BN_free(g);
112
- throw std::runtime_error("DiffieHellman: failed to set DH parameters");
113
- }
114
- EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
115
- if (!pkey || EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
116
- throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
117
- }
118
- dh.release();
119
- _pkey = std::move(pkey);
120
- return;
121
- }
122
-
123
- // Create parameter generation context
124
- EVP_PKEY_CTX_ptr pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr), EVP_PKEY_CTX_free);
125
- if (!pctx) {
126
- throw std::runtime_error("DiffieHellman: failed to create parameter context");
127
- }
128
-
129
- if (EVP_PKEY_paramgen_init(pctx.get()) <= 0) {
130
- throw std::runtime_error("DiffieHellman: failed to initialize parameter generation");
131
- }
132
-
133
- if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx.get(), primeBits) <= 0) {
134
- throw std::runtime_error("DiffieHellman: failed to set prime length");
135
- }
136
-
137
- if (EVP_PKEY_CTX_set_dh_paramgen_generator(pctx.get(), gen) <= 0) {
138
- throw std::runtime_error("DiffieHellman: failed to set generator");
139
- }
140
-
141
- EVP_PKEY* params = nullptr;
142
- if (EVP_PKEY_paramgen(pctx.get(), &params) <= 0) {
143
- throw std::runtime_error("DiffieHellman: failed to generate parameters");
144
- }
145
-
146
- _pkey.reset(params);
147
- }
148
-
149
- std::shared_ptr<ArrayBuffer> HybridDiffieHellman::generateKeys() {
150
- ensureInitialized();
151
-
152
- // EVP_PKEY_get1_DH returns a mutable, ref-counted DH copy so we can
153
- // generate keys on it directly, then re-wrap in a fresh EVP_PKEY.
154
- DH* dh = EVP_PKEY_get1_DH(_pkey.get());
155
- if (!dh) {
156
- throw std::runtime_error("DiffieHellman: failed to get DH key");
157
- }
158
-
159
- // DH_generate_key preserves an existing private key and only computes the
160
- // public key. When no private key is set it generates both.
161
- if (!DH_generate_key(dh)) {
162
- DH_free(dh);
163
- throw std::runtime_error("DiffieHellman: failed to generate key pair");
164
- }
165
-
166
- EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
167
- if (!newPkey || EVP_PKEY_assign_DH(newPkey.get(), dh) != 1) {
168
- DH_free(dh);
169
- throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
170
- }
171
- // EVP_PKEY_assign_DH took ownership of dh
172
-
173
- _pkey = std::move(newPkey);
174
- return getPublicKey();
175
- }
176
-
177
- std::shared_ptr<ArrayBuffer> HybridDiffieHellman::computeSecret(const std::shared_ptr<ArrayBuffer>& otherPublicKey) {
178
- ensureInitialized();
179
-
180
- const DH* ourDh = getDH();
181
- const BIGNUM *p, *q, *g;
182
- DH_get0_pqg(ourDh, &p, &q, &g);
183
-
184
- // Create peer DH with same parameters but peer's public key
185
- DH_ptr peerDh(DH_new(), DH_free);
186
- if (!peerDh) {
187
- throw std::runtime_error("DiffieHellman: failed to create peer DH structure");
188
- }
189
-
190
- // Duplicate parameters for peer
191
- BIGNUM* peerP = BN_dup(p);
192
- BIGNUM* peerG = BN_dup(g);
193
- BIGNUM* peerPubKey = BN_bin2bn(otherPublicKey->data(), static_cast<int>(otherPublicKey->size()), nullptr);
194
-
195
- if (!peerP || !peerG || !peerPubKey) {
196
- if (peerP)
197
- BN_free(peerP);
198
- if (peerG)
199
- BN_free(peerG);
200
- if (peerPubKey)
201
- BN_free(peerPubKey);
202
- throw std::runtime_error("DiffieHellman: failed to create peer parameters");
203
- }
204
-
205
- // Set peer DH parameters (takes ownership on success)
206
- if (DH_set0_pqg(peerDh.get(), peerP, nullptr, peerG) != 1) {
207
- BN_free(peerP);
208
- BN_free(peerG);
209
- BN_free(peerPubKey);
210
- throw std::runtime_error("DiffieHellman: failed to set peer DH parameters");
211
- }
212
-
213
- // Set peer public key (takes ownership on success)
214
- if (DH_set0_key(peerDh.get(), peerPubKey, nullptr) != 1) {
215
- BN_free(peerPubKey);
216
- throw std::runtime_error("DiffieHellman: failed to set peer public key");
217
- }
218
-
219
- // Create peer EVP_PKEY
220
- EVP_PKEY_ptr peerPkey(EVP_PKEY_new(), EVP_PKEY_free);
221
- if (!peerPkey) {
222
- throw std::runtime_error("DiffieHellman: failed to create peer EVP_PKEY");
223
- }
224
-
225
- // EVP_PKEY_assign_DH takes ownership of peerDh on success
226
- if (EVP_PKEY_assign_DH(peerPkey.get(), peerDh.get()) != 1) {
227
- throw std::runtime_error("DiffieHellman: failed to assign peer DH to EVP_PKEY");
228
- }
229
- peerDh.release(); // EVP_PKEY now owns the DH
230
-
231
- // Derive shared secret using EVP API
232
- EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(_pkey.get(), nullptr), EVP_PKEY_CTX_free);
233
- if (!ctx) {
234
- throw std::runtime_error("DiffieHellman: failed to create derive context");
235
- }
236
-
237
- if (EVP_PKEY_derive_init(ctx.get()) <= 0) {
238
- throw std::runtime_error("DiffieHellman: failed to initialize key derivation");
239
- }
240
-
241
- if (EVP_PKEY_derive_set_peer(ctx.get(), peerPkey.get()) <= 0) {
242
- throw std::runtime_error("DiffieHellman: failed to set peer key for derivation");
243
- }
244
-
245
- // Get required buffer size
246
- size_t secretLen = 0;
247
- if (EVP_PKEY_derive(ctx.get(), nullptr, &secretLen) <= 0) {
248
- throw std::runtime_error("DiffieHellman: failed to get shared secret length");
249
- }
250
-
251
- // Derive the shared secret
252
- std::vector<uint8_t> secret(secretLen);
253
- if (EVP_PKEY_derive(ctx.get(), secret.data(), &secretLen) <= 0) {
254
- throw std::runtime_error("DiffieHellman: failed to derive shared secret");
255
- }
256
-
257
- // Resize to actual length (may be smaller due to leading zeros)
258
- secret.resize(secretLen);
259
-
260
- return ToNativeArrayBuffer(secret);
261
- }
262
-
263
- std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPrime() {
264
- ensureInitialized();
265
- const DH* dh = getDH();
266
-
267
- const BIGNUM *p, *q, *g;
268
- DH_get0_pqg(dh, &p, &q, &g);
269
- if (!p) {
270
- throw std::runtime_error("DiffieHellman: no prime available");
271
- }
272
-
273
- int len = BN_num_bytes(p);
274
- std::vector<uint8_t> buf(len);
275
- BN_bn2bin(p, buf.data());
276
-
277
- return ToNativeArrayBuffer(buf);
278
- }
279
-
280
- std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getGenerator() {
281
- ensureInitialized();
282
- const DH* dh = getDH();
283
-
284
- const BIGNUM *p, *q, *g;
285
- DH_get0_pqg(dh, &p, &q, &g);
286
- if (!g) {
287
- throw std::runtime_error("DiffieHellman: no generator available");
288
- }
289
-
290
- int len = BN_num_bytes(g);
291
- std::vector<uint8_t> buf(len);
292
- BN_bn2bin(g, buf.data());
293
-
294
- return ToNativeArrayBuffer(buf);
295
- }
296
-
297
- std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPublicKey() {
298
- ensureInitialized();
299
- const DH* dh = getDH();
300
-
301
- const BIGNUM *pub, *priv;
302
- DH_get0_key(dh, &pub, &priv);
303
- if (!pub) {
304
- throw std::runtime_error("DiffieHellman: no public key available");
305
- }
306
-
307
- int len = BN_num_bytes(pub);
308
- std::vector<uint8_t> buf(len);
309
- BN_bn2bin(pub, buf.data());
310
-
311
- return ToNativeArrayBuffer(buf);
312
- }
313
-
314
- std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPrivateKey() {
315
- ensureInitialized();
316
- const DH* dh = getDH();
317
-
318
- const BIGNUM *pub, *priv;
319
- DH_get0_key(dh, &pub, &priv);
320
- if (!priv) {
321
- throw std::runtime_error("DiffieHellman: no private key available");
322
- }
323
-
324
- int len = BN_num_bytes(priv);
325
- std::vector<uint8_t> buf(len);
326
- BN_bn2bin(priv, buf.data());
327
-
328
- return ToNativeArrayBuffer(buf);
329
- }
330
-
331
- void HybridDiffieHellman::setPublicKey(const std::shared_ptr<ArrayBuffer>& publicKey) {
332
- ensureInitialized();
333
- const DH* dh = getDH();
334
-
335
- // Get existing keys
336
- const BIGNUM *oldPub, *oldPriv;
337
- DH_get0_key(dh, &oldPub, &oldPriv);
338
-
339
- // Get parameters
340
- const BIGNUM *p, *q, *g;
341
- DH_get0_pqg(dh, &p, &q, &g);
342
-
343
- // Create new DH with copied parameters
344
- DH_ptr newDh(DH_new(), DH_free);
345
- if (!newDh) {
346
- throw std::runtime_error("DiffieHellman: failed to create new DH structure");
347
- }
348
-
349
- // Duplicate parameters
350
- BIGNUM* newP = BN_dup(p);
351
- BIGNUM* newQ = q ? BN_dup(q) : nullptr;
352
- BIGNUM* newG = BN_dup(g);
353
-
354
- if (!newP || !newG) {
355
- if (newP)
356
- BN_free(newP);
357
- if (newQ)
358
- BN_free(newQ);
359
- if (newG)
360
- BN_free(newG);
361
- throw std::runtime_error("DiffieHellman: failed to duplicate parameters");
362
- }
363
-
364
- if (DH_set0_pqg(newDh.get(), newP, newQ, newG) != 1) {
365
- BN_free(newP);
366
- if (newQ)
367
- BN_free(newQ);
368
- BN_free(newG);
369
- throw std::runtime_error("DiffieHellman: failed to set parameters");
370
- }
371
-
372
- // Convert new public key
373
- BIGNUM* newPub = BN_bin2bn(publicKey->data(), static_cast<int>(publicKey->size()), nullptr);
374
- BIGNUM* newPriv = oldPriv ? BN_dup(oldPriv) : nullptr;
375
-
376
- if (!newPub) {
377
- if (newPriv)
378
- BN_free(newPriv);
379
- throw std::runtime_error("DiffieHellman: failed to convert public key");
380
- }
381
-
382
- if (DH_set0_key(newDh.get(), newPub, newPriv) != 1) {
383
- BN_free(newPub);
384
- if (newPriv)
385
- BN_free(newPriv);
386
- throw std::runtime_error("DiffieHellman: failed to set keys");
387
- }
388
-
389
- // Create new EVP_PKEY
390
- EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
391
- if (!newPkey) {
392
- throw std::runtime_error("DiffieHellman: failed to create new EVP_PKEY");
393
- }
394
-
395
- if (EVP_PKEY_assign_DH(newPkey.get(), newDh.get()) != 1) {
396
- throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
397
- }
398
- newDh.release();
399
-
400
- _pkey = std::move(newPkey);
401
- }
402
-
403
- void HybridDiffieHellman::setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) {
404
- ensureInitialized();
405
- const DH* dh = getDH();
406
-
407
- // Get existing keys
408
- const BIGNUM *oldPub, *oldPriv;
409
- DH_get0_key(dh, &oldPub, &oldPriv);
410
-
411
- // Get parameters
412
- const BIGNUM *p, *q, *g;
413
- DH_get0_pqg(dh, &p, &q, &g);
414
-
415
- // Create new DH with copied parameters
416
- DH_ptr newDh(DH_new(), DH_free);
417
- if (!newDh) {
418
- throw std::runtime_error("DiffieHellman: failed to create new DH structure");
419
- }
420
-
421
- // Duplicate parameters
422
- BIGNUM* newP = BN_dup(p);
423
- BIGNUM* newQ = q ? BN_dup(q) : nullptr;
424
- BIGNUM* newG = BN_dup(g);
425
-
426
- if (!newP || !newG) {
427
- if (newP)
428
- BN_free(newP);
429
- if (newQ)
430
- BN_free(newQ);
431
- if (newG)
432
- BN_free(newG);
433
- throw std::runtime_error("DiffieHellman: failed to duplicate parameters");
434
- }
435
-
436
- if (DH_set0_pqg(newDh.get(), newP, newQ, newG) != 1) {
437
- BN_free(newP);
438
- if (newQ)
439
- BN_free(newQ);
440
- BN_free(newG);
441
- throw std::runtime_error("DiffieHellman: failed to set parameters");
442
- }
443
-
444
- // Convert new private key
445
- BIGNUM* newPub = oldPub ? BN_dup(oldPub) : nullptr;
446
- BIGNUM* newPriv = BN_bin2bn(privateKey->data(), static_cast<int>(privateKey->size()), nullptr);
447
-
448
- if (!newPriv) {
449
- if (newPub)
450
- BN_free(newPub);
451
- throw std::runtime_error("DiffieHellman: failed to convert private key");
452
- }
453
-
454
- if (DH_set0_key(newDh.get(), newPub, newPriv) != 1) {
455
- if (newPub)
456
- BN_free(newPub);
457
- BN_free(newPriv);
458
- throw std::runtime_error("DiffieHellman: failed to set keys");
459
- }
460
-
461
- // Create new EVP_PKEY
462
- EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
463
- if (!newPkey) {
464
- throw std::runtime_error("DiffieHellman: failed to create new EVP_PKEY");
465
- }
466
-
467
- if (EVP_PKEY_assign_DH(newPkey.get(), newDh.get()) != 1) {
468
- throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
469
- }
470
- newDh.release();
471
-
472
- _pkey = std::move(newPkey);
473
- }
474
-
475
- void HybridDiffieHellman::ensureInitialized() const {
476
- if (!_pkey) {
477
- throw std::runtime_error("DiffieHellman: not initialized");
478
- }
479
- }
480
-
481
- const DH* HybridDiffieHellman::getDH() const {
482
- const DH* dh = EVP_PKEY_get0_DH(_pkey.get());
483
- if (!dh) {
484
- throw std::runtime_error("DiffieHellman: key is not a DH key");
485
- }
486
- return dh;
487
- }
488
-
489
- double HybridDiffieHellman::getVerifyError() {
490
- ensureInitialized();
491
- const DH* dh = getDH();
492
- int codes = 0;
493
- if (DH_check(const_cast<DH*>(dh), &codes) != 1) {
494
- return 0;
495
- }
496
- return static_cast<double>(codes);
497
- }
498
-
499
- #pragma clang diagnostic pop
500
-
501
- } // namespace margelo::nitro::crypto
1
+ #include "HybridDiffieHellman.hpp"
2
+ #include "QuickCryptoUtils.hpp"
3
+ #include <NitroModules/ArrayBuffer.hpp>
4
+ #include <cmath>
5
+ #include <openssl/bn.h>
6
+ #include <openssl/dh.h>
7
+ #include <openssl/err.h>
8
+ #include <openssl/evp.h>
9
+ #include <stdexcept>
10
+
11
+ namespace margelo::nitro::crypto {
12
+
13
+ // Smart pointer type aliases for RAII
14
+ using BN_ptr = std::unique_ptr<BIGNUM, decltype(&BN_free)>;
15
+ using DH_ptr = std::unique_ptr<DH, decltype(&DH_free)>;
16
+ using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
17
+
18
+ // Align with Node.js practical floor (tests / legacy interop). Prefer >=2048 in production.
19
+ static constexpr int kMinDHPrimeBits = 512;
20
+
21
+ // Suppress deprecation warnings for DH_* functions
22
+ // Node.js ncrypto uses the same pattern - these APIs work but are deprecated in OpenSSL 3.x
23
+ #pragma clang diagnostic push
24
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
25
+
26
+ static BIGNUM* getKnownModpPrime(int primeBits) {
27
+ switch (primeBits) {
28
+ case 2048:
29
+ return BN_get_rfc3526_prime_2048(nullptr);
30
+ case 3072:
31
+ return BN_get_rfc3526_prime_3072(nullptr);
32
+ case 4096:
33
+ return BN_get_rfc3526_prime_4096(nullptr);
34
+ case 6144:
35
+ return BN_get_rfc3526_prime_6144(nullptr);
36
+ case 8192:
37
+ return BN_get_rfc3526_prime_8192(nullptr);
38
+ default:
39
+ return nullptr;
40
+ }
41
+ }
42
+
43
+ void HybridDiffieHellman::init(const std::shared_ptr<ArrayBuffer>& prime, const std::shared_ptr<ArrayBuffer>& generator) {
44
+ // Create DH structure
45
+ DH_ptr dh(DH_new(), DH_free);
46
+ if (!dh) {
47
+ throw std::runtime_error("DiffieHellman: failed to create DH structure");
48
+ }
49
+
50
+ // Convert prime and generator to BIGNUMs
51
+ BIGNUM* p = BN_bin2bn(prime->data(), static_cast<int>(prime->size()), nullptr);
52
+ BIGNUM* g = BN_bin2bn(generator->data(), static_cast<int>(generator->size()), nullptr);
53
+
54
+ if (!p || !g) {
55
+ if (p)
56
+ BN_free(p);
57
+ if (g)
58
+ BN_free(g);
59
+ throw std::runtime_error("DiffieHellman: failed to convert parameters to BIGNUM");
60
+ }
61
+
62
+ // DH_set0_pqg takes ownership of p and g on success
63
+ if (DH_set0_pqg(dh.get(), p, nullptr, g) != 1) {
64
+ BN_free(p);
65
+ BN_free(g);
66
+ throw std::runtime_error("DiffieHellman: failed to set DH parameters");
67
+ }
68
+
69
+ // Create EVP_PKEY and assign DH to it
70
+ EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
71
+ if (!pkey) {
72
+ throw std::runtime_error("DiffieHellman: failed to create EVP_PKEY");
73
+ }
74
+
75
+ // EVP_PKEY_assign_DH takes ownership of dh on success
76
+ if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
77
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
78
+ }
79
+ dh.release(); // EVP_PKEY now owns the DH
80
+
81
+ _pkey = std::move(pkey);
82
+ }
83
+
84
+ void HybridDiffieHellman::initWithSize(double primeLength, double generator) {
85
+ int primeBits = static_cast<int>(std::llround(primeLength));
86
+ int gen = static_cast<int>(std::llround(generator));
87
+ if (gen <= 0) {
88
+ gen = 2;
89
+ }
90
+
91
+ if (primeBits < kMinDHPrimeBits) {
92
+ throw std::runtime_error("DiffieHellman: prime length must be at least 512 bits");
93
+ }
94
+
95
+ // Fast path: well-known MODP groups (avoids multi-minute DH paramgen for 2048+)
96
+ if (BIGNUM* knownPrime = getKnownModpPrime(primeBits)) {
97
+ DH_ptr dh(DH_new(), DH_free);
98
+ if (!dh) {
99
+ BN_free(knownPrime);
100
+ throw std::runtime_error("DiffieHellman: failed to create DH structure");
101
+ }
102
+ BIGNUM* g = BN_new();
103
+ if (!g || BN_set_word(g, static_cast<unsigned long>(gen)) != 1) {
104
+ if (g)
105
+ BN_free(g);
106
+ BN_free(knownPrime);
107
+ throw std::runtime_error("DiffieHellman: failed to set generator");
108
+ }
109
+ if (DH_set0_pqg(dh.get(), knownPrime, nullptr, g) != 1) {
110
+ BN_free(knownPrime);
111
+ BN_free(g);
112
+ throw std::runtime_error("DiffieHellman: failed to set DH parameters");
113
+ }
114
+ EVP_PKEY_ptr pkey(EVP_PKEY_new(), EVP_PKEY_free);
115
+ if (!pkey || EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
116
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
117
+ }
118
+ dh.release();
119
+ _pkey = std::move(pkey);
120
+ return;
121
+ }
122
+
123
+ // Create parameter generation context
124
+ EVP_PKEY_CTX_ptr pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr), EVP_PKEY_CTX_free);
125
+ if (!pctx) {
126
+ throw std::runtime_error("DiffieHellman: failed to create parameter context");
127
+ }
128
+
129
+ if (EVP_PKEY_paramgen_init(pctx.get()) <= 0) {
130
+ throw std::runtime_error("DiffieHellman: failed to initialize parameter generation");
131
+ }
132
+
133
+ if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx.get(), primeBits) <= 0) {
134
+ throw std::runtime_error("DiffieHellman: failed to set prime length");
135
+ }
136
+
137
+ if (EVP_PKEY_CTX_set_dh_paramgen_generator(pctx.get(), gen) <= 0) {
138
+ throw std::runtime_error("DiffieHellman: failed to set generator");
139
+ }
140
+
141
+ EVP_PKEY* params = nullptr;
142
+ if (EVP_PKEY_paramgen(pctx.get(), &params) <= 0) {
143
+ throw std::runtime_error("DiffieHellman: failed to generate parameters");
144
+ }
145
+
146
+ _pkey.reset(params);
147
+ }
148
+
149
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::generateKeys() {
150
+ ensureInitialized();
151
+
152
+ // EVP_PKEY_get1_DH returns a mutable, ref-counted DH copy so we can
153
+ // generate keys on it directly, then re-wrap in a fresh EVP_PKEY.
154
+ DH* dh = EVP_PKEY_get1_DH(_pkey.get());
155
+ if (!dh) {
156
+ throw std::runtime_error("DiffieHellman: failed to get DH key");
157
+ }
158
+
159
+ // DH_generate_key preserves an existing private key and only computes the
160
+ // public key. When no private key is set it generates both.
161
+ if (!DH_generate_key(dh)) {
162
+ DH_free(dh);
163
+ throw std::runtime_error("DiffieHellman: failed to generate key pair");
164
+ }
165
+
166
+ EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
167
+ if (!newPkey || EVP_PKEY_assign_DH(newPkey.get(), dh) != 1) {
168
+ DH_free(dh);
169
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
170
+ }
171
+ // EVP_PKEY_assign_DH took ownership of dh
172
+
173
+ _pkey = std::move(newPkey);
174
+ return getPublicKey();
175
+ }
176
+
177
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::computeSecret(const std::shared_ptr<ArrayBuffer>& otherPublicKey) {
178
+ ensureInitialized();
179
+
180
+ const DH* ourDh = getDH();
181
+ const BIGNUM *p, *q, *g;
182
+ DH_get0_pqg(ourDh, &p, &q, &g);
183
+
184
+ // Create peer DH with same parameters but peer's public key
185
+ DH_ptr peerDh(DH_new(), DH_free);
186
+ if (!peerDh) {
187
+ throw std::runtime_error("DiffieHellman: failed to create peer DH structure");
188
+ }
189
+
190
+ // Duplicate parameters for peer
191
+ BIGNUM* peerP = BN_dup(p);
192
+ BIGNUM* peerG = BN_dup(g);
193
+ BIGNUM* peerPubKey = BN_bin2bn(otherPublicKey->data(), static_cast<int>(otherPublicKey->size()), nullptr);
194
+
195
+ if (!peerP || !peerG || !peerPubKey) {
196
+ if (peerP)
197
+ BN_free(peerP);
198
+ if (peerG)
199
+ BN_free(peerG);
200
+ if (peerPubKey)
201
+ BN_free(peerPubKey);
202
+ throw std::runtime_error("DiffieHellman: failed to create peer parameters");
203
+ }
204
+
205
+ // Set peer DH parameters (takes ownership on success)
206
+ if (DH_set0_pqg(peerDh.get(), peerP, nullptr, peerG) != 1) {
207
+ BN_free(peerP);
208
+ BN_free(peerG);
209
+ BN_free(peerPubKey);
210
+ throw std::runtime_error("DiffieHellman: failed to set peer DH parameters");
211
+ }
212
+
213
+ // Set peer public key (takes ownership on success)
214
+ if (DH_set0_key(peerDh.get(), peerPubKey, nullptr) != 1) {
215
+ BN_free(peerPubKey);
216
+ throw std::runtime_error("DiffieHellman: failed to set peer public key");
217
+ }
218
+
219
+ // Create peer EVP_PKEY
220
+ EVP_PKEY_ptr peerPkey(EVP_PKEY_new(), EVP_PKEY_free);
221
+ if (!peerPkey) {
222
+ throw std::runtime_error("DiffieHellman: failed to create peer EVP_PKEY");
223
+ }
224
+
225
+ // EVP_PKEY_assign_DH takes ownership of peerDh on success
226
+ if (EVP_PKEY_assign_DH(peerPkey.get(), peerDh.get()) != 1) {
227
+ throw std::runtime_error("DiffieHellman: failed to assign peer DH to EVP_PKEY");
228
+ }
229
+ peerDh.release(); // EVP_PKEY now owns the DH
230
+
231
+ // Derive shared secret using EVP API
232
+ EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(_pkey.get(), nullptr), EVP_PKEY_CTX_free);
233
+ if (!ctx) {
234
+ throw std::runtime_error("DiffieHellman: failed to create derive context");
235
+ }
236
+
237
+ if (EVP_PKEY_derive_init(ctx.get()) <= 0) {
238
+ throw std::runtime_error("DiffieHellman: failed to initialize key derivation");
239
+ }
240
+
241
+ if (EVP_PKEY_derive_set_peer(ctx.get(), peerPkey.get()) <= 0) {
242
+ throw std::runtime_error("DiffieHellman: failed to set peer key for derivation");
243
+ }
244
+
245
+ // Get required buffer size
246
+ size_t secretLen = 0;
247
+ if (EVP_PKEY_derive(ctx.get(), nullptr, &secretLen) <= 0) {
248
+ throw std::runtime_error("DiffieHellman: failed to get shared secret length");
249
+ }
250
+
251
+ // Derive the shared secret
252
+ std::vector<uint8_t> secret(secretLen);
253
+ if (EVP_PKEY_derive(ctx.get(), secret.data(), &secretLen) <= 0) {
254
+ throw std::runtime_error("DiffieHellman: failed to derive shared secret");
255
+ }
256
+
257
+ // Resize to actual length (may be smaller due to leading zeros)
258
+ secret.resize(secretLen);
259
+
260
+ return ToNativeArrayBuffer(secret);
261
+ }
262
+
263
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPrime() {
264
+ ensureInitialized();
265
+ const DH* dh = getDH();
266
+
267
+ const BIGNUM *p, *q, *g;
268
+ DH_get0_pqg(dh, &p, &q, &g);
269
+ if (!p) {
270
+ throw std::runtime_error("DiffieHellman: no prime available");
271
+ }
272
+
273
+ int len = BN_num_bytes(p);
274
+ std::vector<uint8_t> buf(len);
275
+ BN_bn2bin(p, buf.data());
276
+
277
+ return ToNativeArrayBuffer(buf);
278
+ }
279
+
280
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getGenerator() {
281
+ ensureInitialized();
282
+ const DH* dh = getDH();
283
+
284
+ const BIGNUM *p, *q, *g;
285
+ DH_get0_pqg(dh, &p, &q, &g);
286
+ if (!g) {
287
+ throw std::runtime_error("DiffieHellman: no generator available");
288
+ }
289
+
290
+ int len = BN_num_bytes(g);
291
+ std::vector<uint8_t> buf(len);
292
+ BN_bn2bin(g, buf.data());
293
+
294
+ return ToNativeArrayBuffer(buf);
295
+ }
296
+
297
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPublicKey() {
298
+ ensureInitialized();
299
+ const DH* dh = getDH();
300
+
301
+ const BIGNUM *pub, *priv;
302
+ DH_get0_key(dh, &pub, &priv);
303
+ if (!pub) {
304
+ throw std::runtime_error("DiffieHellman: no public key available");
305
+ }
306
+
307
+ int len = BN_num_bytes(pub);
308
+ std::vector<uint8_t> buf(len);
309
+ BN_bn2bin(pub, buf.data());
310
+
311
+ return ToNativeArrayBuffer(buf);
312
+ }
313
+
314
+ std::shared_ptr<ArrayBuffer> HybridDiffieHellman::getPrivateKey() {
315
+ ensureInitialized();
316
+ const DH* dh = getDH();
317
+
318
+ const BIGNUM *pub, *priv;
319
+ DH_get0_key(dh, &pub, &priv);
320
+ if (!priv) {
321
+ throw std::runtime_error("DiffieHellman: no private key available");
322
+ }
323
+
324
+ int len = BN_num_bytes(priv);
325
+ std::vector<uint8_t> buf(len);
326
+ BN_bn2bin(priv, buf.data());
327
+
328
+ return ToNativeArrayBuffer(buf);
329
+ }
330
+
331
+ void HybridDiffieHellman::setPublicKey(const std::shared_ptr<ArrayBuffer>& publicKey) {
332
+ ensureInitialized();
333
+ const DH* dh = getDH();
334
+
335
+ // Get existing keys
336
+ const BIGNUM *oldPub, *oldPriv;
337
+ DH_get0_key(dh, &oldPub, &oldPriv);
338
+
339
+ // Get parameters
340
+ const BIGNUM *p, *q, *g;
341
+ DH_get0_pqg(dh, &p, &q, &g);
342
+
343
+ // Create new DH with copied parameters
344
+ DH_ptr newDh(DH_new(), DH_free);
345
+ if (!newDh) {
346
+ throw std::runtime_error("DiffieHellman: failed to create new DH structure");
347
+ }
348
+
349
+ // Duplicate parameters
350
+ BIGNUM* newP = BN_dup(p);
351
+ BIGNUM* newQ = q ? BN_dup(q) : nullptr;
352
+ BIGNUM* newG = BN_dup(g);
353
+
354
+ if (!newP || !newG) {
355
+ if (newP)
356
+ BN_free(newP);
357
+ if (newQ)
358
+ BN_free(newQ);
359
+ if (newG)
360
+ BN_free(newG);
361
+ throw std::runtime_error("DiffieHellman: failed to duplicate parameters");
362
+ }
363
+
364
+ if (DH_set0_pqg(newDh.get(), newP, newQ, newG) != 1) {
365
+ BN_free(newP);
366
+ if (newQ)
367
+ BN_free(newQ);
368
+ BN_free(newG);
369
+ throw std::runtime_error("DiffieHellman: failed to set parameters");
370
+ }
371
+
372
+ // Convert new public key
373
+ BIGNUM* newPub = BN_bin2bn(publicKey->data(), static_cast<int>(publicKey->size()), nullptr);
374
+ BIGNUM* newPriv = oldPriv ? BN_dup(oldPriv) : nullptr;
375
+
376
+ if (!newPub) {
377
+ if (newPriv)
378
+ BN_free(newPriv);
379
+ throw std::runtime_error("DiffieHellman: failed to convert public key");
380
+ }
381
+
382
+ if (DH_set0_key(newDh.get(), newPub, newPriv) != 1) {
383
+ BN_free(newPub);
384
+ if (newPriv)
385
+ BN_free(newPriv);
386
+ throw std::runtime_error("DiffieHellman: failed to set keys");
387
+ }
388
+
389
+ // Create new EVP_PKEY
390
+ EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
391
+ if (!newPkey) {
392
+ throw std::runtime_error("DiffieHellman: failed to create new EVP_PKEY");
393
+ }
394
+
395
+ if (EVP_PKEY_assign_DH(newPkey.get(), newDh.get()) != 1) {
396
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
397
+ }
398
+ newDh.release();
399
+
400
+ _pkey = std::move(newPkey);
401
+ }
402
+
403
+ void HybridDiffieHellman::setPrivateKey(const std::shared_ptr<ArrayBuffer>& privateKey) {
404
+ ensureInitialized();
405
+ const DH* dh = getDH();
406
+
407
+ // Get existing keys
408
+ const BIGNUM *oldPub, *oldPriv;
409
+ DH_get0_key(dh, &oldPub, &oldPriv);
410
+
411
+ // Get parameters
412
+ const BIGNUM *p, *q, *g;
413
+ DH_get0_pqg(dh, &p, &q, &g);
414
+
415
+ // Create new DH with copied parameters
416
+ DH_ptr newDh(DH_new(), DH_free);
417
+ if (!newDh) {
418
+ throw std::runtime_error("DiffieHellman: failed to create new DH structure");
419
+ }
420
+
421
+ // Duplicate parameters
422
+ BIGNUM* newP = BN_dup(p);
423
+ BIGNUM* newQ = q ? BN_dup(q) : nullptr;
424
+ BIGNUM* newG = BN_dup(g);
425
+
426
+ if (!newP || !newG) {
427
+ if (newP)
428
+ BN_free(newP);
429
+ if (newQ)
430
+ BN_free(newQ);
431
+ if (newG)
432
+ BN_free(newG);
433
+ throw std::runtime_error("DiffieHellman: failed to duplicate parameters");
434
+ }
435
+
436
+ if (DH_set0_pqg(newDh.get(), newP, newQ, newG) != 1) {
437
+ BN_free(newP);
438
+ if (newQ)
439
+ BN_free(newQ);
440
+ BN_free(newG);
441
+ throw std::runtime_error("DiffieHellman: failed to set parameters");
442
+ }
443
+
444
+ // Convert new private key
445
+ BIGNUM* newPub = oldPub ? BN_dup(oldPub) : nullptr;
446
+ BIGNUM* newPriv = BN_bin2bn(privateKey->data(), static_cast<int>(privateKey->size()), nullptr);
447
+
448
+ if (!newPriv) {
449
+ if (newPub)
450
+ BN_free(newPub);
451
+ throw std::runtime_error("DiffieHellman: failed to convert private key");
452
+ }
453
+
454
+ if (DH_set0_key(newDh.get(), newPub, newPriv) != 1) {
455
+ if (newPub)
456
+ BN_free(newPub);
457
+ BN_free(newPriv);
458
+ throw std::runtime_error("DiffieHellman: failed to set keys");
459
+ }
460
+
461
+ // Create new EVP_PKEY
462
+ EVP_PKEY_ptr newPkey(EVP_PKEY_new(), EVP_PKEY_free);
463
+ if (!newPkey) {
464
+ throw std::runtime_error("DiffieHellman: failed to create new EVP_PKEY");
465
+ }
466
+
467
+ if (EVP_PKEY_assign_DH(newPkey.get(), newDh.get()) != 1) {
468
+ throw std::runtime_error("DiffieHellman: failed to assign DH to EVP_PKEY");
469
+ }
470
+ newDh.release();
471
+
472
+ _pkey = std::move(newPkey);
473
+ }
474
+
475
+ void HybridDiffieHellman::ensureInitialized() const {
476
+ if (!_pkey) {
477
+ throw std::runtime_error("DiffieHellman: not initialized");
478
+ }
479
+ }
480
+
481
+ const DH* HybridDiffieHellman::getDH() const {
482
+ const DH* dh = EVP_PKEY_get0_DH(_pkey.get());
483
+ if (!dh) {
484
+ throw std::runtime_error("DiffieHellman: key is not a DH key");
485
+ }
486
+ return dh;
487
+ }
488
+
489
+ double HybridDiffieHellman::getVerifyError() {
490
+ ensureInitialized();
491
+ const DH* dh = getDH();
492
+ int codes = 0;
493
+ if (DH_check(const_cast<DH*>(dh), &codes) != 1) {
494
+ return 0;
495
+ }
496
+ return static_cast<double>(codes);
497
+ }
498
+
499
+ #pragma clang diagnostic pop
500
+
501
+ } // namespace margelo::nitro::crypto