react-native-quick-crypto 1.0.7 → 1.0.8

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 (89) hide show
  1. package/QuickCrypto.podspec +3 -0
  2. package/android/CMakeLists.txt +4 -0
  3. package/cpp/dh/HybridDiffieHellman.cpp +438 -0
  4. package/cpp/dh/HybridDiffieHellman.hpp +41 -0
  5. package/cpp/ecdh/HybridECDH.cpp +306 -0
  6. package/cpp/ecdh/HybridECDH.hpp +42 -0
  7. package/cpp/utils/QuickCryptoUtils.hpp +14 -0
  8. package/lib/commonjs/dh-groups.js +29 -0
  9. package/lib/commonjs/dh-groups.js.map +1 -0
  10. package/lib/commonjs/diffie-hellman.js +147 -0
  11. package/lib/commonjs/diffie-hellman.js.map +1 -0
  12. package/lib/commonjs/ec.js +68 -180
  13. package/lib/commonjs/ec.js.map +1 -1
  14. package/lib/commonjs/ecdh.js +71 -0
  15. package/lib/commonjs/ecdh.js.map +1 -0
  16. package/lib/commonjs/index.js +26 -0
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/keys/generateKeyPair.js.map +1 -1
  19. package/lib/commonjs/keys/index.js +12 -0
  20. package/lib/commonjs/keys/index.js.map +1 -1
  21. package/lib/commonjs/keys/signVerify.js +42 -0
  22. package/lib/commonjs/keys/signVerify.js.map +1 -1
  23. package/lib/commonjs/specs/diffie-hellman.nitro.js +6 -0
  24. package/lib/commonjs/specs/diffie-hellman.nitro.js.map +1 -0
  25. package/lib/commonjs/specs/ecdh.nitro.js +6 -0
  26. package/lib/commonjs/specs/ecdh.nitro.js.map +1 -0
  27. package/lib/commonjs/subtle.js +2 -0
  28. package/lib/commonjs/subtle.js.map +1 -1
  29. package/lib/module/dh-groups.js +25 -0
  30. package/lib/module/dh-groups.js.map +1 -0
  31. package/lib/module/diffie-hellman.js +140 -0
  32. package/lib/module/diffie-hellman.js.map +1 -0
  33. package/lib/module/ec.js +65 -178
  34. package/lib/module/ec.js.map +1 -1
  35. package/lib/module/ecdh.js +65 -0
  36. package/lib/module/ecdh.js.map +1 -0
  37. package/lib/module/index.js +6 -0
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/keys/generateKeyPair.js.map +1 -1
  40. package/lib/module/keys/index.js +2 -2
  41. package/lib/module/keys/index.js.map +1 -1
  42. package/lib/module/keys/signVerify.js +40 -0
  43. package/lib/module/keys/signVerify.js.map +1 -1
  44. package/lib/module/specs/diffie-hellman.nitro.js +4 -0
  45. package/lib/module/specs/diffie-hellman.nitro.js.map +1 -0
  46. package/lib/module/specs/ecdh.nitro.js +4 -0
  47. package/lib/module/specs/ecdh.nitro.js.map +1 -0
  48. package/lib/module/subtle.js +3 -1
  49. package/lib/module/subtle.js.map +1 -1
  50. package/lib/tsconfig.tsbuildinfo +1 -1
  51. package/lib/typescript/dh-groups.d.ts +5 -0
  52. package/lib/typescript/dh-groups.d.ts.map +1 -0
  53. package/lib/typescript/diffie-hellman.d.ts +16 -0
  54. package/lib/typescript/diffie-hellman.d.ts.map +1 -0
  55. package/lib/typescript/ec.d.ts +2 -1
  56. package/lib/typescript/ec.d.ts.map +1 -1
  57. package/lib/typescript/ecdh.d.ts +16 -0
  58. package/lib/typescript/ecdh.d.ts.map +1 -0
  59. package/lib/typescript/index.d.ts +11 -0
  60. package/lib/typescript/index.d.ts.map +1 -1
  61. package/lib/typescript/keys/generateKeyPair.d.ts.map +1 -1
  62. package/lib/typescript/keys/index.d.ts +2 -2
  63. package/lib/typescript/keys/index.d.ts.map +1 -1
  64. package/lib/typescript/keys/signVerify.d.ts +6 -0
  65. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  66. package/lib/typescript/specs/diffie-hellman.nitro.d.ts +17 -0
  67. package/lib/typescript/specs/diffie-hellman.nitro.d.ts.map +1 -0
  68. package/lib/typescript/specs/ecdh.nitro.d.ts +14 -0
  69. package/lib/typescript/specs/ecdh.nitro.d.ts.map +1 -0
  70. package/lib/typescript/subtle.d.ts.map +1 -1
  71. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +2 -0
  72. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +20 -0
  73. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +20 -0
  74. package/nitrogen/generated/shared/c++/HybridDiffieHellmanSpec.cpp +30 -0
  75. package/nitrogen/generated/shared/c++/HybridDiffieHellmanSpec.hpp +72 -0
  76. package/nitrogen/generated/shared/c++/HybridECDHSpec.cpp +27 -0
  77. package/nitrogen/generated/shared/c++/HybridECDHSpec.hpp +70 -0
  78. package/package.json +1 -1
  79. package/src/dh-groups.ts +27 -0
  80. package/src/diffie-hellman.ts +191 -0
  81. package/src/ec.ts +73 -177
  82. package/src/ecdh.ts +76 -0
  83. package/src/index.ts +6 -0
  84. package/src/keys/generateKeyPair.ts +11 -2
  85. package/src/keys/index.ts +10 -1
  86. package/src/keys/signVerify.ts +84 -0
  87. package/src/specs/diffie-hellman.nitro.ts +15 -0
  88. package/src/specs/ecdh.nitro.ts +11 -0
  89. package/src/subtle.ts +8 -1
@@ -0,0 +1,191 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ import type { DiffieHellman as DiffieHellmanInterface } from './specs/diffie-hellman.nitro';
3
+ import { Buffer } from '@craftzdog/react-native-buffer';
4
+ import { DH_GROUPS } from './dh-groups';
5
+
6
+ export class DiffieHellman {
7
+ private _hybrid: DiffieHellmanInterface;
8
+
9
+ constructor(
10
+ sizeOrPrime: number | Buffer | string,
11
+ generator?: number | Buffer | string,
12
+ encoding?: BufferEncoding,
13
+ ) {
14
+ this._hybrid =
15
+ NitroModules.createHybridObject<DiffieHellmanInterface>('DiffieHellman');
16
+
17
+ if (typeof sizeOrPrime === 'number') {
18
+ const gen = typeof generator === 'number' ? generator : 2;
19
+ this._hybrid.initWithSize(sizeOrPrime, gen);
20
+ } else {
21
+ let primeBuf: Buffer;
22
+ if (Buffer.isBuffer(sizeOrPrime)) {
23
+ primeBuf = sizeOrPrime;
24
+ } else {
25
+ primeBuf = Buffer.from(sizeOrPrime, encoding as BufferEncoding);
26
+ }
27
+
28
+ let genBuf: Buffer;
29
+ if (generator === undefined) {
30
+ genBuf = Buffer.from([2]);
31
+ } else if (typeof generator === 'number') {
32
+ genBuf = Buffer.from([generator]);
33
+ } else if (Buffer.isBuffer(generator)) {
34
+ genBuf = generator;
35
+ } else {
36
+ genBuf = Buffer.from(generator, encoding as BufferEncoding);
37
+ }
38
+
39
+ this._hybrid.init(
40
+ primeBuf.buffer as ArrayBuffer,
41
+ genBuf.buffer as ArrayBuffer,
42
+ );
43
+ }
44
+ }
45
+
46
+ generateKeys(encoding?: BufferEncoding): Buffer | string {
47
+ const keys = Buffer.from(this._hybrid.generateKeys());
48
+ if (encoding) return keys.toString(encoding);
49
+ return keys;
50
+ }
51
+
52
+ computeSecret(
53
+ otherPublicKey: Buffer | string,
54
+ inputEncoding?: BufferEncoding,
55
+ outputEncoding?: BufferEncoding,
56
+ ): Buffer | string {
57
+ let keyBuf: Buffer;
58
+ if (Buffer.isBuffer(otherPublicKey)) {
59
+ keyBuf = otherPublicKey;
60
+ } else {
61
+ keyBuf = Buffer.from(otherPublicKey, inputEncoding);
62
+ }
63
+
64
+ const secret = Buffer.from(
65
+ this._hybrid.computeSecret(keyBuf.buffer as ArrayBuffer),
66
+ );
67
+ if (outputEncoding) return secret.toString(outputEncoding);
68
+ return secret;
69
+ }
70
+
71
+ getPrime(encoding?: BufferEncoding): Buffer | string {
72
+ const p = Buffer.from(this._hybrid.getPrime());
73
+ if (encoding) return p.toString(encoding);
74
+ return p;
75
+ }
76
+
77
+ getGenerator(encoding?: BufferEncoding): Buffer | string {
78
+ const g = Buffer.from(this._hybrid.getGenerator());
79
+ if (encoding) return g.toString(encoding);
80
+ return g;
81
+ }
82
+
83
+ getPublicKey(encoding?: BufferEncoding): Buffer | string {
84
+ const p = Buffer.from(this._hybrid.getPublicKey());
85
+ if (encoding) return p.toString(encoding);
86
+ return p;
87
+ }
88
+
89
+ getPrivateKey(encoding?: BufferEncoding): Buffer | string {
90
+ const p = Buffer.from(this._hybrid.getPrivateKey());
91
+ if (encoding) return p.toString(encoding);
92
+ return p;
93
+ }
94
+
95
+ setPublicKey(publicKey: Buffer | string, encoding?: BufferEncoding): void {
96
+ let keyBuf: Buffer;
97
+ if (Buffer.isBuffer(publicKey)) {
98
+ keyBuf = publicKey;
99
+ } else {
100
+ keyBuf = Buffer.from(publicKey, encoding);
101
+ }
102
+ this._hybrid.setPublicKey(keyBuf.buffer as ArrayBuffer);
103
+ }
104
+
105
+ setPrivateKey(privateKey: Buffer | string, encoding?: BufferEncoding): void {
106
+ let keyBuf: Buffer;
107
+ if (Buffer.isBuffer(privateKey)) {
108
+ keyBuf = privateKey;
109
+ } else {
110
+ keyBuf = Buffer.from(privateKey, encoding);
111
+ }
112
+ this._hybrid.setPrivateKey(keyBuf.buffer as ArrayBuffer);
113
+ }
114
+ }
115
+
116
+ export function createDiffieHellman(
117
+ primeOrSize: number | string | Buffer,
118
+ primeEncodingOrGenerator?: string | number | Buffer,
119
+ generator?: number | string | Buffer,
120
+ _generatorEncoding?: string,
121
+ ): DiffieHellman {
122
+ if (typeof primeOrSize === 'number') {
123
+ const gen =
124
+ typeof primeEncodingOrGenerator === 'number'
125
+ ? primeEncodingOrGenerator
126
+ : 2;
127
+ return new DiffieHellman(primeOrSize, gen);
128
+ }
129
+
130
+ // Standardize arguments for String/Buffer prime
131
+ // createDiffieHellman(prime, [encoding], [generator], [encoding])
132
+
133
+ let prime: Buffer;
134
+ let generatorVal: Buffer | number | undefined;
135
+
136
+ if (Buffer.isBuffer(primeOrSize)) {
137
+ prime = primeOrSize;
138
+ // 2nd arg is generator if not string (encoding)
139
+ if (
140
+ primeEncodingOrGenerator !== undefined &&
141
+ typeof primeEncodingOrGenerator !== 'string'
142
+ ) {
143
+ generatorVal = primeEncodingOrGenerator as Buffer | number;
144
+ } else if (generator !== undefined) {
145
+ generatorVal = generator as Buffer | number;
146
+ } else {
147
+ generatorVal = 2;
148
+ }
149
+ } else {
150
+ // String prime
151
+ const encoding =
152
+ typeof primeEncodingOrGenerator === 'string'
153
+ ? primeEncodingOrGenerator
154
+ : 'utf8'; // Defaulting to utf8 or hex? Node default is 'binary' usually but utf8 safer for TS. Node docs say: "If no encoding is specified, 'binary' is used."
155
+ // We'll trust user passed encoding if it's a string, otherwise handle it.
156
+ prime = Buffer.from(primeOrSize, encoding as BufferEncoding);
157
+
158
+ // Generator handling in this case
159
+ if (generator !== undefined) {
160
+ generatorVal = generator as Buffer | number;
161
+ if (typeof generator === 'string' && _generatorEncoding) {
162
+ generatorVal = Buffer.from(
163
+ generator,
164
+ _generatorEncoding as BufferEncoding,
165
+ );
166
+ } else if (typeof generator === 'string') {
167
+ // string with no encoding, assume same as prime? or utf8?
168
+ generatorVal = Buffer.from(generator, encoding as BufferEncoding);
169
+ }
170
+ } else if (
171
+ typeof primeEncodingOrGenerator !== 'string' &&
172
+ primeEncodingOrGenerator !== undefined
173
+ ) {
174
+ // 2nd arg was generator
175
+ generatorVal = primeEncodingOrGenerator as number;
176
+ } else {
177
+ generatorVal = 2;
178
+ }
179
+ }
180
+
181
+ return new DiffieHellman(prime, generatorVal);
182
+ }
183
+
184
+ export function getDiffieHellman(groupName: string): DiffieHellman {
185
+ const group = DH_GROUPS[groupName];
186
+ if (!group) {
187
+ throw new Error(`Unknown group: ${groupName}`);
188
+ }
189
+ // group.prime and group.generator are hex strings
190
+ return new DiffieHellman(group.prime, group.generator, 'hex');
191
+ }
package/src/ec.ts CHANGED
@@ -31,7 +31,8 @@ import {
31
31
  KeyEncoding,
32
32
  KFormatType,
33
33
  } from './utils';
34
- import { Buffer } from 'buffer';
34
+ import { Buffer } from '@craftzdog/react-native-buffer';
35
+ import { ECDH } from './ecdh';
35
36
 
36
37
  export class Ec {
37
38
  native: EcKeyPair;
@@ -58,57 +59,6 @@ export class Ec {
58
59
  }
59
60
  }
60
61
 
61
- // function verifyAcceptableEcKeyUse(
62
- // name: AnyAlgorithm,
63
- // isPublic: boolean,
64
- // usages: KeyUsage[],
65
- // ): void {
66
- // let checkSet;
67
- // switch (name) {
68
- // case 'ECDH':
69
- // checkSet = isPublic ? [] : ['deriveKey', 'deriveBits'];
70
- // break;
71
- // case 'ECDSA':
72
- // checkSet = isPublic ? ['verify'] : ['sign'];
73
- // break;
74
- // default:
75
- // throw lazyDOMException(
76
- // 'The algorithm is not supported',
77
- // 'NotSupportedError',
78
- // );
79
- // }
80
- // if (hasAnyNotIn(usages, checkSet)) {
81
- // throw lazyDOMException(
82
- // `Unsupported key usage for a ${name} key`,
83
- // 'SyntaxError',
84
- // );
85
- // }
86
- // }
87
-
88
- // function createECPublicKeyRaw(
89
- // namedCurve: NamedCurve | undefined,
90
- // keyDataBuffer: ArrayBuffer,
91
- // ): PublicKeyObject {
92
- // if (!namedCurve) {
93
- // throw new Error('Invalid namedCurve');
94
- // }
95
- // const handle = NitroModules.createHybridObject(
96
- // 'KeyObjectHandle',
97
- // ) as KeyObjectHandle;
98
-
99
- // if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyDataBuffer)) {
100
- // console.log('keyData', ab2str(keyDataBuffer));
101
- // throw new Error('Invalid keyData 1');
102
- // }
103
-
104
- // return new PublicKeyObject(handle);
105
- // }
106
-
107
- // // Node API
108
- // export function ec_exportKey(key: CryptoKey, format: KeyFormat): ArrayBuffer {
109
- // return ec.native.exportKey(format, key.keyObject.handle);
110
- // }
111
-
112
62
  // Node API
113
63
  export function ecImportKey(
114
64
  format: ImportFormat,
@@ -169,7 +119,7 @@ export function ecImportKey(
169
119
  expectedAlg = 'ECDH-ES';
170
120
  }
171
121
 
172
- if (expectedAlg && jwk.alg !== expectedAlg) {
122
+ if (expectedAlg && jwk.alg !== undefined && jwk.alg !== expectedAlg) {
173
123
  throw lazyDOMException(
174
124
  'JWK "alg" does not match the requested algorithm',
175
125
  'DataError',
@@ -245,6 +195,7 @@ export function ecImportKey(
245
195
  NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
246
196
  const curveAlias =
247
197
  kNamedCurveAliases[namedCurve as keyof typeof kNamedCurveAliases];
198
+ // Only throw if initialization explicitly fails
248
199
  if (!handle.initECRaw(curveAlias, keyBuffer)) {
249
200
  throw lazyDOMException('Failed to import EC raw key', 'DataError');
250
201
  }
@@ -260,131 +211,8 @@ export function ecImportKey(
260
211
  }
261
212
 
262
213
  return new CryptoKey(keyObject, algorithm, keyUsages, extractable);
263
- // // // verifyAcceptableEcKeyUse(name, true, usagesSet);
264
- // // try {
265
- // // keyObject = createPublicKey({
266
- // // key: keyData,
267
- // // format: 'der',
268
- // // type: 'spki',
269
- // // });
270
- // // } catch (err) {
271
- // // throw new Error(`Invalid keyData 2: ${err}`);
272
- // // }
273
- // // break;
274
- // // }
275
- // // case 'pkcs8': {
276
- // // // verifyAcceptableEcKeyUse(name, false, usagesSet);
277
- // // try {
278
- // // keyObject = createPrivateKey({
279
- // // key: keyData,
280
- // // format: 'der',
281
- // // type: 'pkcs8',
282
- // // });
283
- // // } catch (err) {
284
- // // throw new Error(`Invalid keyData 3 ${err}`);
285
- // // }
286
- // // break;
287
- // // }
288
214
  }
289
215
 
290
- // case 'jwk': {
291
- // const data = keyData as JWK;
292
-
293
- // if (!data.kty) throw lazyDOMException('Invalid keyData 4', 'DataError');
294
- // if (data.kty !== 'EC')
295
- // throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
296
- // if (data.crv !== namedCurve)
297
- // throw lazyDOMException(
298
- // 'JWK "crv" does not match the requested algorithm',
299
- // 'DataError',
300
- // );
301
-
302
- // verifyAcceptableEcKeyUse(name, data.d === undefined, keyUsages);
303
-
304
- // if (keyUsages.length > 0 && data.use !== undefined) {
305
- // const checkUse = name === 'ECDH' ? 'enc' : 'sig';
306
- // if (data.use !== checkUse)
307
- // throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
308
- // }
309
-
310
- // validateKeyOps(data.key_ops, keyUsages);
311
-
312
- // if (
313
- // data.ext !== undefined &&
314
- // data.ext === false &&
315
- // extractable === true
316
- // ) {
317
- // throw lazyDOMException(
318
- // 'JWK "ext" Parameter and extractable mismatch',
319
- // 'DataError',
320
- // );
321
- // }
322
-
323
- // if (algorithm.name === 'ECDSA' && data.alg !== undefined) {
324
- // let algNamedCurve;
325
- // switch (data.alg) {
326
- // case 'ES256':
327
- // algNamedCurve = 'P-256';
328
- // break;
329
- // case 'ES384':
330
- // algNamedCurve = 'P-384';
331
- // break;
332
- // case 'ES512':
333
- // algNamedCurve = 'P-521';
334
- // break;
335
- // }
336
- // if (algNamedCurve !== namedCurve)
337
- // throw lazyDOMException(
338
- // 'JWK "alg" does not match the requested algorithm',
339
- // 'DataError',
340
- // );
341
- // }
342
-
343
- // const handle = NativeQuickCrypto.webcrypto.createKeyObjectHandle();
344
- // const type = handle.initJwk(data, namedCurve);
345
- // if (type === undefined)
346
- // throw lazyDOMException('Invalid JWK', 'DataError');
347
- // keyObject =
348
- // type === KeyType.PRIVATE
349
- // ? new PrivateKeyObject(handle)
350
- // : new PublicKeyObject(handle);
351
- // break;
352
- // }
353
- // case 'raw': {
354
- // const data = keyData as BufferLike | BinaryLike;
355
- // verifyAcceptableEcKeyUse(name, true, keyUsages);
356
- // const buffer =
357
- // typeof data === 'string'
358
- // ? binaryLikeToArrayBuffer(data)
359
- // : bufferLikeToArrayBuffer(data);
360
- // keyObject = createECPublicKeyRaw(namedCurve, buffer);
361
- // break;
362
- // }
363
- // default: {
364
- // throw new Error(`Unknown EC import format: ${format}`);
365
- // }
366
- // }
367
-
368
- // switch (algorithm.name) {
369
- // case 'ECDSA':
370
- // // Fall through
371
- // case 'ECDH':
372
- // if (keyObject.asymmetricKeyType !== ('ec' as AsymmetricKeyType))
373
- // throw new Error('Invalid key type');
374
- // break;
375
- // }
376
-
377
- // // if (!keyObject[kHandle].checkEcKeyData()) {
378
- // // throw new Error('Invalid keyData 5');
379
- // // }
380
-
381
- // // const { namedCurve: checkNamedCurve } = keyObject[kHandle].keyDetail({});
382
- // // if (kNamedCurveAliases[namedCurve] !== checkNamedCurve)
383
- // // throw new Error('Named curve mismatch');
384
-
385
- // return new CryptoKey(keyObject, { name, namedCurve }, keyUsages, extractable);
386
- // }
387
-
388
216
  // Node API
389
217
  export const ecdsaSignVerify = (
390
218
  key: CryptoKey,
@@ -462,7 +290,6 @@ export async function ec_generateKeyPair(
462
290
  );
463
291
  }
464
292
 
465
- // const usageSet = new SafeSet(keyUsages);
466
293
  switch (name) {
467
294
  case 'ECDSA':
468
295
  if (hasAnyNotIn(keyUsages, ['sign', 'verify'])) {
@@ -655,3 +482,72 @@ export function ec_generateKeyPairNodeSync(
655
482
  ec.generateKeyPairSync();
656
483
  return ec_formatKeyPairOutput(ec, encoding);
657
484
  }
485
+
486
+ export function ecDeriveBits(
487
+ algorithm: SubtleAlgorithm,
488
+ baseKey: CryptoKey,
489
+ length: number | null,
490
+ ): ArrayBuffer {
491
+ const publicParams = algorithm as SubtleAlgorithm & { public?: CryptoKey };
492
+ const publicKey = publicParams.public;
493
+
494
+ if (!publicKey) {
495
+ throw new Error('Public key is required for ECDH derivation');
496
+ }
497
+
498
+ if (baseKey.algorithm.name !== publicKey.algorithm.name) {
499
+ throw new Error('Keys must be of the same algorithm');
500
+ }
501
+
502
+ if (baseKey.algorithm.namedCurve !== publicKey.algorithm.namedCurve) {
503
+ throw new Error('Keys must use the same curve');
504
+ }
505
+
506
+ const namedCurve = baseKey.algorithm.namedCurve;
507
+ if (!namedCurve) {
508
+ throw new Error('Curve name is missing');
509
+ }
510
+
511
+ // Create new ECDH instance (Node.js style wrapper)
512
+ const ecdh = new ECDH(namedCurve);
513
+
514
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
515
+ const jwkPrivate = baseKey.keyObject.export({ format: 'jwk' }) as any;
516
+ if (!jwkPrivate.d) throw new Error('Invalid private key');
517
+ const privateBytes = Buffer.from(jwkPrivate.d, 'base64');
518
+
519
+ ecdh.setPrivateKey(privateBytes);
520
+
521
+ // Public key
522
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
523
+ const jwkPublic = publicKey.keyObject.export({ format: 'jwk' }) as any;
524
+
525
+ // HybridECDH `computeSecret` takes public key.
526
+ // My implementation `HybridECDH.cpp` `computeSecret` expects what?
527
+ // `derive_secret` -> `EVP_PKEY_derive_set_peer`
528
+ // `computeSecret` calls `EC_POINT_oct2point`. So it expects an uncompressed/compressed point (04... or 02/03...).
529
+ // JWK gives `x` and `y`. We can construct the uncompressed point 04 + x + y.
530
+
531
+ if (!jwkPublic.x || !jwkPublic.y) throw new Error('Invalid public key');
532
+ const x = Buffer.from(jwkPublic.x, 'base64');
533
+ const y = Buffer.from(jwkPublic.y, 'base64');
534
+
535
+ // Uncompressed point: 0x04 || x || y
536
+ const publicBytes = Buffer.concat([Buffer.from([0x04]), x, y]);
537
+
538
+ const secret = ecdh.computeSecret(publicBytes);
539
+ const secretBuf = Buffer.from(secret);
540
+
541
+ // If length is null, return full secret
542
+ if (length === null) {
543
+ return secretBuf.buffer;
544
+ }
545
+
546
+ // If length is specified, truncate
547
+ const byteLength = Math.ceil(length / 8);
548
+ if (secretBuf.byteLength >= byteLength) {
549
+ return secretBuf.subarray(0, byteLength).buffer as ArrayBuffer;
550
+ }
551
+
552
+ throw new Error('Derived key is shorter than requested length');
553
+ }
package/src/ecdh.ts ADDED
@@ -0,0 +1,76 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ import type { ECDH as ECDHInterface } from './specs/ecdh.nitro';
3
+ import { Buffer } from '@craftzdog/react-native-buffer';
4
+
5
+ export class ECDH {
6
+ private _hybrid: ECDHInterface;
7
+
8
+ constructor(curveName: string) {
9
+ this._hybrid = NitroModules.createHybridObject<ECDHInterface>('ECDH');
10
+ this._hybrid.init(curveName);
11
+ }
12
+
13
+ generateKeys(): Buffer {
14
+ const key = this._hybrid.generateKeys();
15
+ return Buffer.from(key);
16
+ }
17
+
18
+ computeSecret(
19
+ otherPublicKey: Buffer | string | { code: number; byteLength: number },
20
+ inputEncoding?: BufferEncoding,
21
+ ): Buffer {
22
+ let keyBuf: Buffer;
23
+ if (Buffer.isBuffer(otherPublicKey)) {
24
+ keyBuf = otherPublicKey;
25
+ } else if (typeof otherPublicKey === 'string') {
26
+ keyBuf = Buffer.from(otherPublicKey, inputEncoding);
27
+ } else {
28
+ // Handle array view or other types if necessary, but Node.js typically expects Buffer or string + encoding
29
+ throw new TypeError('Invalid otherPublicKey type');
30
+ }
31
+
32
+ // ECDH.computeSecret in Node.js returns Buffer
33
+ const secret = this._hybrid.computeSecret(keyBuf.buffer as ArrayBuffer);
34
+ return Buffer.from(secret);
35
+ }
36
+
37
+ getPrivateKey(): Buffer {
38
+ return Buffer.from(this._hybrid.getPrivateKey());
39
+ }
40
+
41
+ setPrivateKey(privateKey: Buffer | string, encoding?: BufferEncoding): void {
42
+ let keyBuf: Buffer;
43
+ if (Buffer.isBuffer(privateKey)) {
44
+ keyBuf = privateKey;
45
+ } else {
46
+ keyBuf = Buffer.from(privateKey, encoding);
47
+ }
48
+ this._hybrid.setPrivateKey(keyBuf.buffer as ArrayBuffer);
49
+ }
50
+
51
+ getPublicKey(encoding?: BufferEncoding): Buffer | string {
52
+ // Node.js getPublicKey([encoding[, format]])
53
+ // If encoding is provided, returns string. If not, Buffer.
54
+ // Our C++ returns ArrayBuffer (Buffer).
55
+ // We ignore format for now as C++ implementation defaults to uncompressed.
56
+ const pub = Buffer.from(this._hybrid.getPublicKey());
57
+ if (encoding) {
58
+ return pub.toString(encoding);
59
+ }
60
+ return pub;
61
+ }
62
+
63
+ setPublicKey(publicKey: Buffer | string, encoding?: BufferEncoding): void {
64
+ let keyBuf: Buffer;
65
+ if (Buffer.isBuffer(publicKey)) {
66
+ keyBuf = publicKey;
67
+ } else {
68
+ keyBuf = Buffer.from(publicKey, encoding);
69
+ }
70
+ this._hybrid.setPublicKey(keyBuf.buffer as ArrayBuffer);
71
+ }
72
+ }
73
+
74
+ export function createECDH(curveName: string): ECDH {
75
+ return new ECDH(curveName);
76
+ }
package/src/index.ts CHANGED
@@ -12,6 +12,8 @@ import * as hkdf from './hkdf';
12
12
  import * as pbkdf2 from './pbkdf2';
13
13
  import * as scrypt from './scrypt';
14
14
  import * as random from './random';
15
+ import * as ecdh from './ecdh';
16
+ import * as dh from './diffie-hellman';
15
17
  import { constants } from './constants';
16
18
 
17
19
  // utils import
@@ -33,6 +35,8 @@ const QuickCrypto = {
33
35
  ...pbkdf2,
34
36
  ...scrypt,
35
37
  ...random,
38
+ ...ecdh,
39
+ ...dh,
36
40
  ...utils,
37
41
  ...subtle,
38
42
  constants,
@@ -71,6 +75,8 @@ export * from './hkdf';
71
75
  export * from './pbkdf2';
72
76
  export * from './scrypt';
73
77
  export * from './random';
78
+ export * from './ecdh';
79
+ export * from './diffie-hellman';
74
80
  export * from './utils';
75
81
  export * from './subtle';
76
82
  export { subtle, isCryptoKeyPair } from './subtle';
@@ -10,6 +10,7 @@ import {
10
10
  type GenerateKeyPairPromiseReturn,
11
11
  type GenerateKeyPairReturn,
12
12
  type KeyPairGenConfig,
13
+ type KeyPairKey,
13
14
  type KeyPairType,
14
15
  } from '../utils';
15
16
  import { parsePrivateKeyEncoding, parsePublicKeyEncoding } from './utils';
@@ -160,7 +161,11 @@ function internalGenerateKeyPair(
160
161
  } else {
161
162
  throw new Error(`Unsupported key type: ${type}`);
162
163
  }
163
- return [undefined, result.publicKey, result.privateKey];
164
+ return [
165
+ undefined,
166
+ result.publicKey as KeyPairKey,
167
+ result.privateKey as KeyPairKey,
168
+ ];
164
169
  } catch (error) {
165
170
  return [error as Error, undefined, undefined];
166
171
  }
@@ -182,7 +187,11 @@ function internalGenerateKeyPair(
182
187
  } else {
183
188
  throw new Error(`Unsupported key type: ${type}`);
184
189
  }
185
- return [undefined, result.publicKey, result.privateKey];
190
+ return [
191
+ undefined,
192
+ result.publicKey as KeyPairKey,
193
+ result.privateKey as KeyPairKey,
194
+ ];
186
195
  } catch (error) {
187
196
  return [error as Error, undefined, undefined];
188
197
  }
package/src/keys/index.ts CHANGED
@@ -7,7 +7,14 @@ import {
7
7
  PrivateKeyObject,
8
8
  } from './classes';
9
9
  import { generateKeyPair, generateKeyPairSync } from './generateKeyPair';
10
- import { createSign, createVerify, Sign, Verify } from './signVerify';
10
+ import {
11
+ createSign,
12
+ createVerify,
13
+ sign,
14
+ verify,
15
+ Sign,
16
+ Verify,
17
+ } from './signVerify';
11
18
  import {
12
19
  publicEncrypt,
13
20
  publicDecrypt,
@@ -236,6 +243,8 @@ export {
236
243
  KeyObject,
237
244
  createSign,
238
245
  createVerify,
246
+ sign,
247
+ verify,
239
248
  Sign,
240
249
  Verify,
241
250
  publicEncrypt,