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
package/src/ed.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  PrivateKeyObject as PrivateKeyObjectClass,
9
9
  } from './keys/classes';
10
10
  import type { EdKeyPair } from './specs/edKeyPair.nitro';
11
+ import type { KeyObjectHandle as KeyObjectHandleSpec } from './specs/keyObjectHandle.nitro';
11
12
  import type {
12
13
  BinaryLike,
13
14
  CFRGKeyPairType,
@@ -31,6 +32,10 @@ import {
31
32
  } from './utils';
32
33
  import { ECDH } from './ecdh';
33
34
 
35
+ function coerceToNumeric(value: unknown): number {
36
+ return typeof value === 'number' ? value : -1;
37
+ }
38
+
34
39
  export class Ed {
35
40
  type: CFRGKeyPairType;
36
41
  config: KeyPairGenConfig;
@@ -86,10 +91,10 @@ export class Ed {
86
91
 
87
92
  async generateKeyPair(): Promise<void> {
88
93
  await this.native.generateKeyPair(
89
- this.config.publicFormat ?? -1,
90
- this.config.publicType ?? -1,
91
- this.config.privateFormat ?? -1,
92
- this.config.privateType ?? -1,
94
+ coerceToNumeric(this.config.publicFormat),
95
+ coerceToNumeric(this.config.publicType),
96
+ coerceToNumeric(this.config.privateFormat),
97
+ coerceToNumeric(this.config.privateType),
93
98
  this.config.cipher,
94
99
  this.config.passphrase as ArrayBuffer,
95
100
  );
@@ -97,10 +102,10 @@ export class Ed {
97
102
 
98
103
  generateKeyPairSync(): void {
99
104
  this.native.generateKeyPairSync(
100
- this.config.publicFormat ?? -1,
101
- this.config.publicType ?? -1,
102
- this.config.privateFormat ?? -1,
103
- this.config.privateType ?? -1,
105
+ coerceToNumeric(this.config.publicFormat),
106
+ coerceToNumeric(this.config.publicType),
107
+ coerceToNumeric(this.config.privateFormat),
108
+ coerceToNumeric(this.config.privateType),
104
109
  this.config.cipher,
105
110
  this.config.passphrase as ArrayBuffer,
106
111
  );
@@ -166,18 +171,86 @@ export function diffieHellman(
166
171
  options: DiffieHellmanOptions,
167
172
  callback?: DiffieHellmanCallback,
168
173
  ): Buffer | void {
169
- checkDiffieHellmanOptions(options);
174
+ if (!options || typeof options !== 'object') {
175
+ throw new TypeError('options must be an object');
176
+ }
177
+ if (callback !== undefined && typeof callback !== 'function') {
178
+ throw new TypeError('callback must be a function');
179
+ }
170
180
 
171
- const privateKey = options.privateKey as PrivateKeyObject;
181
+ const resolvedOptions: DiffieHellmanOptions = {
182
+ publicKey: resolveDhKeyInput(options.publicKey, 'publicKey'),
183
+ privateKey: resolveDhKeyInput(options.privateKey, 'privateKey'),
184
+ };
185
+ checkDiffieHellmanOptions(resolvedOptions);
186
+
187
+ const privateKey = resolvedOptions.privateKey as PrivateKeyObject;
172
188
  const keyType = privateKey.asymmetricKeyType;
173
189
 
174
190
  if (keyType === 'ec') {
175
- return ecDiffieHellman(options, callback);
191
+ return ecDiffieHellman(resolvedOptions, callback);
192
+ }
193
+
194
+ if (keyType === 'dh') {
195
+ throw new Error(
196
+ 'crypto.diffieHellman with DH KeyObjects is not supported yet — use the DiffieHellman class for now',
197
+ );
176
198
  }
177
199
 
178
200
  const type = keyType as CFRGKeyPairType;
179
201
  const ed = new Ed(type, {});
180
- return ed.diffieHellman(options, callback);
202
+ return ed.diffieHellman(resolvedOptions, callback);
203
+ }
204
+
205
+ function isRawKeyInput(value: unknown): value is {
206
+ key: ArrayBuffer | ArrayBufferView | string;
207
+ format: 'raw-public' | 'raw-private' | 'raw-seed';
208
+ asymmetricKeyType: string;
209
+ namedCurve?: string;
210
+ } {
211
+ if (!value || typeof value !== 'object') return false;
212
+ const obj = value as { format?: string };
213
+ return (
214
+ obj.format === 'raw-public' ||
215
+ obj.format === 'raw-private' ||
216
+ obj.format === 'raw-seed'
217
+ );
218
+ }
219
+
220
+ function resolveDhKeyInput(
221
+ input: DiffieHellmanOptions['publicKey'],
222
+ name: 'publicKey' | 'privateKey',
223
+ ): AsymmetricKeyObject {
224
+ if (isRawKeyInput(input)) {
225
+ const expectedKeyType = name === 'publicKey' ? 'public' : 'private';
226
+ if (input.format === 'raw-public' && expectedKeyType !== 'public') {
227
+ throw new Error(`Invalid format 'raw-public' for ${name}`);
228
+ }
229
+ if (
230
+ (input.format === 'raw-private' || input.format === 'raw-seed') &&
231
+ expectedKeyType !== 'private'
232
+ ) {
233
+ throw new Error(`Invalid format '${input.format}' for ${name}`);
234
+ }
235
+ if (input.asymmetricKeyType === 'ec' && !input.namedCurve) {
236
+ throw new Error(`namedCurve is required for EC raw key in ${name}`);
237
+ }
238
+
239
+ const handle =
240
+ NitroModules.createHybridObject<KeyObjectHandleSpec>('KeyObjectHandle');
241
+ const keyData = toAB(input.key as BinaryLike);
242
+ if (input.format === 'raw-public') {
243
+ handle.initRawPublic(input.asymmetricKeyType, keyData, input.namedCurve);
244
+ return new PublicKeyObject(handle);
245
+ }
246
+ if (input.format === 'raw-seed') {
247
+ handle.initRawSeed(input.asymmetricKeyType, keyData);
248
+ return new PrivateKeyObjectClass(handle);
249
+ }
250
+ handle.initRawPrivate(input.asymmetricKeyType, keyData, input.namedCurve);
251
+ return new PrivateKeyObjectClass(handle);
252
+ }
253
+ return input as AsymmetricKeyObject;
181
254
  }
182
255
 
183
256
  function ed_createKeyObjects(ed: Ed): {
@@ -206,17 +279,19 @@ function ed_formatKeyPairOutput(
206
279
  ed: Ed,
207
280
  encoding: KeyPairGenConfig,
208
281
  ): {
209
- publicKey: PublicKeyObject | string | ArrayBuffer;
210
- privateKey: PrivateKeyObjectClass | string | ArrayBuffer;
282
+ publicKey: PublicKeyObject | string | ArrayBuffer | Buffer;
283
+ privateKey: PrivateKeyObjectClass | string | ArrayBuffer | Buffer;
211
284
  } {
212
285
  const { publicFormat, privateFormat, cipher, passphrase } = encoding;
213
286
  const { pub, priv } = ed_createKeyObjects(ed);
214
287
 
215
- let publicKey: PublicKeyObject | string | ArrayBuffer;
216
- let privateKey: PrivateKeyObjectClass | string | ArrayBuffer;
288
+ let publicKey: PublicKeyObject | string | ArrayBuffer | Buffer;
289
+ let privateKey: PrivateKeyObjectClass | string | ArrayBuffer | Buffer;
217
290
 
218
291
  if (publicFormat == null || publicFormat === -1) {
219
292
  publicKey = pub;
293
+ } else if (publicFormat === 'raw-public') {
294
+ publicKey = Buffer.from(pub.handle.exportRawPublic());
220
295
  } else {
221
296
  const format =
222
297
  publicFormat === KFormatType.PEM ? KFormatType.PEM : KFormatType.DER;
@@ -230,6 +305,10 @@ function ed_formatKeyPairOutput(
230
305
 
231
306
  if (privateFormat == null || privateFormat === -1) {
232
307
  privateKey = priv;
308
+ } else if (privateFormat === 'raw-private') {
309
+ privateKey = Buffer.from(priv.handle.exportRawPrivate());
310
+ } else if (privateFormat === 'raw-seed') {
311
+ privateKey = Buffer.from(priv.handle.exportRawSeed());
233
312
  } else {
234
313
  const format =
235
314
  privateFormat === KFormatType.PEM ? KFormatType.PEM : KFormatType.DER;
package/src/hash.ts CHANGED
@@ -3,6 +3,7 @@ import { NitroModules } from 'react-native-nitro-modules';
3
3
  import type { TransformOptions } from 'readable-stream';
4
4
  import { Buffer } from '@craftzdog/react-native-buffer';
5
5
  import type { Hash as NativeHash } from './specs/hash.nitro';
6
+ import type { TurboShake as NativeTurboShake } from './specs/turboshake.nitro';
6
7
  import type {
7
8
  BinaryLike,
8
9
  Encoding,
@@ -267,19 +268,33 @@ export const asyncDigest = async (
267
268
  }
268
269
 
269
270
  if (name === 'cSHAKE128' || name === 'cSHAKE256') {
270
- if (typeof algorithm.length !== 'number' || algorithm.length <= 0) {
271
+ // CShakeParams.outputLength is required (in bits) per the WICG modern-algos
272
+ // spec, renamed from `length` (commit ab8dc2b84c2). Mirror Node's
273
+ // hash.js:223-228 / webidl.js:570-595.
274
+ if (
275
+ typeof algorithm.outputLength !== 'number' ||
276
+ algorithm.outputLength <= 0
277
+ ) {
271
278
  throw lazyDOMException(
272
- 'cSHAKE requires a length parameter',
279
+ 'CShakeParams.outputLength is required',
273
280
  'OperationError',
274
281
  );
275
282
  }
276
- if (algorithm.length % 8) {
283
+ if (algorithm.outputLength % 8) {
277
284
  throw lazyDOMException(
278
- 'Unsupported CShakeParams length',
285
+ 'Unsupported CShakeParams outputLength',
279
286
  'NotSupportedError',
280
287
  );
281
288
  }
282
- return internalDigest(algorithm, data, algorithm.length);
289
+ return internalDigest(algorithm, data, algorithm.outputLength / 8);
290
+ }
291
+
292
+ if (name === 'TurboSHAKE128' || name === 'TurboSHAKE256') {
293
+ return turboShakeDigest(name, algorithm, data);
294
+ }
295
+
296
+ if (name === 'KT128' || name === 'KT256') {
297
+ return kangarooTwelveDigest(name, algorithm, data);
283
298
  }
284
299
 
285
300
  throw lazyDOMException(
@@ -288,6 +303,94 @@ export const asyncDigest = async (
288
303
  );
289
304
  };
290
305
 
306
+ // TurboSHAKE / KangarooTwelve are not exposed by OpenSSL EVP, so we route
307
+ // them to a dedicated Nitro module (cpp/turboshake) that ports the Node.js
308
+ // reference implementation. Lazy-load to keep the module out of the hot path
309
+ // for callers that only use SHA-2/SHA-3.
310
+ let nativeTurboShake: NativeTurboShake | undefined;
311
+ const getTurboShake = (): NativeTurboShake => {
312
+ if (!nativeTurboShake) {
313
+ nativeTurboShake =
314
+ NitroModules.createHybridObject<NativeTurboShake>('TurboShake');
315
+ }
316
+ return nativeTurboShake;
317
+ };
318
+
319
+ // RFC 9861 §2.2 default per the WICG Modern Algos draft for TurboSHAKE.
320
+ const kDefaultTurboShakeDomainSeparation = 0x1f;
321
+
322
+ const turboShakeDigest = async (
323
+ name: 'TurboSHAKE128' | 'TurboSHAKE256',
324
+ algorithm: SubtleAlgorithm,
325
+ data: BufferLike,
326
+ ): Promise<ArrayBuffer> => {
327
+ if (
328
+ typeof algorithm.outputLength !== 'number' ||
329
+ algorithm.outputLength <= 0
330
+ ) {
331
+ throw lazyDOMException(
332
+ 'TurboShakeParams.outputLength is required',
333
+ 'OperationError',
334
+ );
335
+ }
336
+ if (algorithm.outputLength % 8) {
337
+ throw lazyDOMException(
338
+ 'Invalid TurboShakeParams outputLength',
339
+ 'OperationError',
340
+ );
341
+ }
342
+ const ds = algorithm.domainSeparation ?? kDefaultTurboShakeDomainSeparation;
343
+ if (
344
+ typeof ds !== 'number' ||
345
+ !Number.isInteger(ds) ||
346
+ ds < 0x01 ||
347
+ ds > 0x7f
348
+ ) {
349
+ throw lazyDOMException(
350
+ 'TurboShakeParams.domainSeparation must be in range 0x01-0x7f',
351
+ 'OperationError',
352
+ );
353
+ }
354
+ return getTurboShake().turboShake(
355
+ name,
356
+ ds,
357
+ algorithm.outputLength / 8,
358
+ bufferLikeToArrayBuffer(data),
359
+ );
360
+ };
361
+
362
+ const kangarooTwelveDigest = async (
363
+ name: 'KT128' | 'KT256',
364
+ algorithm: SubtleAlgorithm,
365
+ data: BufferLike,
366
+ ): Promise<ArrayBuffer> => {
367
+ if (
368
+ typeof algorithm.outputLength !== 'number' ||
369
+ algorithm.outputLength <= 0
370
+ ) {
371
+ throw lazyDOMException(
372
+ 'KangarooTwelveParams.outputLength is required',
373
+ 'OperationError',
374
+ );
375
+ }
376
+ if (algorithm.outputLength % 8) {
377
+ throw lazyDOMException(
378
+ 'Invalid KangarooTwelveParams outputLength',
379
+ 'OperationError',
380
+ );
381
+ }
382
+ const customization =
383
+ algorithm.customization !== undefined
384
+ ? bufferLikeToArrayBuffer(algorithm.customization)
385
+ : undefined;
386
+ return getTurboShake().kangarooTwelve(
387
+ name,
388
+ algorithm.outputLength / 8,
389
+ bufferLikeToArrayBuffer(data),
390
+ customization,
391
+ );
392
+ };
393
+
291
394
  const internalDigest = (
292
395
  algorithm: SubtleAlgorithm,
293
396
  data: BufferLike,
@@ -1,4 +1,4 @@
1
- import { Buffer } from 'buffer';
1
+ import { Buffer } from '@craftzdog/react-native-buffer';
2
2
  import { NitroModules } from 'react-native-nitro-modules';
3
3
  import type {
4
4
  AsymmetricKeyType,
@@ -9,7 +9,7 @@ import type {
9
9
  KeyUsage,
10
10
  SubtleAlgorithm,
11
11
  } from '../utils';
12
- import { KeyType, KFormatType, KeyEncoding } from '../utils';
12
+ import { KeyType, KFormatType, KeyEncoding, getSortedUsages } from '../utils';
13
13
  import { parsePrivateKeyEncoding, parsePublicKeyEncoding } from './utils';
14
14
 
15
15
  export class CryptoKey {
@@ -30,7 +30,8 @@ export class CryptoKey {
30
30
  ) {
31
31
  this.keyObject = keyObject;
32
32
  this.keyAlgorithm = keyAlgorithm;
33
- this.keyUsages = keyUsages;
33
+ // Frozen so external code can't mutate `key.usages` (per WebCrypto spec).
34
+ this.keyUsages = Object.freeze(getSortedUsages(keyUsages)) as KeyUsage[];
34
35
  this.keyExtractable = keyExtractable;
35
36
  }
36
37
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -286,15 +287,36 @@ export class PublicKeyObject extends AsymmetricKeyObject {
286
287
  export(options: { format: 'pem' } & EncodingOptions): string;
287
288
  export(options: { format: 'der' } & EncodingOptions): Buffer;
288
289
  export(options: { format: 'jwk' } & EncodingOptions): JWK;
290
+ export(options: { format: 'raw-public' } & EncodingOptions): Buffer;
289
291
  export(options: EncodingOptions): string | Buffer | JWK {
290
292
  if (options?.format === 'jwk') {
291
293
  return this.handle.exportJwk({}, false);
292
294
  }
295
+ if (options?.format === 'raw-public') {
296
+ if (this.asymmetricKeyType === 'ec') {
297
+ const pointType = options.type ?? 'uncompressed';
298
+ if (pointType !== 'compressed' && pointType !== 'uncompressed') {
299
+ throw new Error(
300
+ `Invalid options.type for raw-public EC export: ${pointType}`,
301
+ );
302
+ }
303
+ return Buffer.from(
304
+ this.handle.exportECPublicRaw(pointType === 'compressed'),
305
+ );
306
+ }
307
+ return Buffer.from(this.handle.exportRawPublic());
308
+ }
293
309
  const { format, type } = parsePublicKeyEncoding(
294
310
  options,
295
311
  this.asymmetricKeyType,
296
312
  );
297
- const key = this.handle.exportKey(format, type);
313
+ if (typeof format !== 'number') {
314
+ throw new Error(`Unexpected format ${format} after raw-format dispatch`);
315
+ }
316
+ const key = this.handle.exportKey(
317
+ format,
318
+ typeof type === 'string' ? undefined : type,
319
+ );
298
320
  const buffer = Buffer.from(key);
299
321
  if (options?.format === 'pem') {
300
322
  return buffer.toString('utf-8');
@@ -311,6 +333,8 @@ export class PrivateKeyObject extends AsymmetricKeyObject {
311
333
  export(options: { format: 'pem' } & EncodingOptions): string;
312
334
  export(options: { format: 'der' } & EncodingOptions): Buffer;
313
335
  export(options: { format: 'jwk' } & EncodingOptions): JWK;
336
+ export(options: { format: 'raw-private' } & EncodingOptions): Buffer;
337
+ export(options: { format: 'raw-seed' } & EncodingOptions): Buffer;
314
338
  export(options: EncodingOptions): string | Buffer | JWK {
315
339
  if (options?.format === 'jwk') {
316
340
  if (options.passphrase !== undefined) {
@@ -318,11 +342,28 @@ export class PrivateKeyObject extends AsymmetricKeyObject {
318
342
  }
319
343
  return this.handle.exportJwk({}, false);
320
344
  }
345
+ if (options?.format === 'raw-private') {
346
+ if (this.asymmetricKeyType === 'ec') {
347
+ return Buffer.from(this.handle.exportECPrivateRaw());
348
+ }
349
+ return Buffer.from(this.handle.exportRawPrivate());
350
+ }
351
+ if (options?.format === 'raw-seed') {
352
+ return Buffer.from(this.handle.exportRawSeed());
353
+ }
321
354
  const { format, type, cipher, passphrase } = parsePrivateKeyEncoding(
322
355
  options,
323
356
  this.asymmetricKeyType,
324
357
  );
325
- const key = this.handle.exportKey(format, type, cipher, passphrase);
358
+ if (typeof format !== 'number') {
359
+ throw new Error(`Unexpected format ${format} after raw-format dispatch`);
360
+ }
361
+ const key = this.handle.exportKey(
362
+ format,
363
+ typeof type === 'string' ? undefined : type,
364
+ cipher,
365
+ passphrase,
366
+ );
326
367
  const buffer = Buffer.from(key);
327
368
  if (options?.format === 'pem') {
328
369
  return buffer.toString('utf-8');
@@ -1,4 +1,5 @@
1
1
  import { ed_generateKeyPair } from '../ed';
2
+ import { Buffer } from '@craftzdog/react-native-buffer';
2
3
  import { rsa_generateKeyPairNode, rsa_generateKeyPairNodeSync } from '../rsa';
3
4
  import { ec_generateKeyPairNode, ec_generateKeyPairNodeSync } from '../ec';
4
5
  import { dsa_generateKeyPairNode, dsa_generateKeyPairNodeSync } from '../dsa';
@@ -6,6 +7,7 @@ import {
6
7
  dh_generateKeyPairNode,
7
8
  dh_generateKeyPairNodeSync,
8
9
  } from '../dhKeyPair';
10
+ import { SlhDsa, type SlhDsaVariant } from '../slhdsa';
9
11
  import {
10
12
  kEmptyObject,
11
13
  validateFunction,
@@ -17,9 +19,129 @@ import {
17
19
  type KeyPairGenConfig,
18
20
  type KeyPairKey,
19
21
  type KeyPairType,
22
+ type SlhDsaKeyPairType,
23
+ KFormatType,
24
+ KeyEncoding,
20
25
  } from '../utils';
26
+ import {
27
+ KeyObject,
28
+ type PublicKeyObject,
29
+ type PrivateKeyObject,
30
+ } from './classes';
21
31
  import { parsePrivateKeyEncoding, parsePublicKeyEncoding } from './utils';
22
32
 
33
+ const SLH_DSA_TYPE_TO_VARIANT: Readonly<
34
+ Record<SlhDsaKeyPairType, SlhDsaVariant>
35
+ > = {
36
+ 'slh-dsa-sha2-128s': 'SLH-DSA-SHA2-128s',
37
+ 'slh-dsa-sha2-128f': 'SLH-DSA-SHA2-128f',
38
+ 'slh-dsa-sha2-192s': 'SLH-DSA-SHA2-192s',
39
+ 'slh-dsa-sha2-192f': 'SLH-DSA-SHA2-192f',
40
+ 'slh-dsa-sha2-256s': 'SLH-DSA-SHA2-256s',
41
+ 'slh-dsa-sha2-256f': 'SLH-DSA-SHA2-256f',
42
+ 'slh-dsa-shake-128s': 'SLH-DSA-SHAKE-128s',
43
+ 'slh-dsa-shake-128f': 'SLH-DSA-SHAKE-128f',
44
+ 'slh-dsa-shake-192s': 'SLH-DSA-SHAKE-192s',
45
+ 'slh-dsa-shake-192f': 'SLH-DSA-SHAKE-192f',
46
+ 'slh-dsa-shake-256s': 'SLH-DSA-SHAKE-256s',
47
+ 'slh-dsa-shake-256f': 'SLH-DSA-SHAKE-256f',
48
+ };
49
+
50
+ function isSlhDsaType(type: string): type is SlhDsaKeyPairType {
51
+ return type in SLH_DSA_TYPE_TO_VARIANT;
52
+ }
53
+
54
+ function slhDsaFormatKeyPairOutput(
55
+ slhdsa: SlhDsa,
56
+ encoding: KeyPairGenConfig,
57
+ ): {
58
+ publicKey: PublicKeyObject | string | ArrayBuffer | Buffer;
59
+ privateKey: PrivateKeyObject | string | ArrayBuffer | Buffer;
60
+ } {
61
+ const { publicFormat, privateFormat, cipher, passphrase } = encoding;
62
+
63
+ const publicKey = KeyObject.createKeyObject(
64
+ 'public',
65
+ slhdsa.getPublicKey(),
66
+ KFormatType.DER,
67
+ KeyEncoding.SPKI,
68
+ ) as PublicKeyObject;
69
+ const privateKey = KeyObject.createKeyObject(
70
+ 'private',
71
+ slhdsa.getPrivateKey(),
72
+ KFormatType.DER,
73
+ KeyEncoding.PKCS8,
74
+ ) as PrivateKeyObject;
75
+
76
+ let publicKeyOutput: PublicKeyObject | string | ArrayBuffer | Buffer;
77
+ let privateKeyOutput: PrivateKeyObject | string | ArrayBuffer | Buffer;
78
+
79
+ if (publicFormat === -1) {
80
+ publicKeyOutput = publicKey;
81
+ } else if (publicFormat === 'raw-public') {
82
+ publicKeyOutput = Buffer.from(publicKey.handle.exportRawPublic());
83
+ } else {
84
+ const format =
85
+ publicFormat === KFormatType.PEM ? KFormatType.PEM : KFormatType.DER;
86
+ const exported = publicKey.handle.exportKey(format, KeyEncoding.SPKI);
87
+ if (format === KFormatType.PEM) {
88
+ publicKeyOutput = Buffer.from(new Uint8Array(exported)).toString('utf-8');
89
+ } else {
90
+ publicKeyOutput = exported;
91
+ }
92
+ }
93
+
94
+ if (privateFormat === -1) {
95
+ privateKeyOutput = privateKey;
96
+ } else if (privateFormat === 'raw-private') {
97
+ privateKeyOutput = Buffer.from(privateKey.handle.exportRawPrivate());
98
+ } else if (privateFormat === 'raw-seed') {
99
+ privateKeyOutput = Buffer.from(privateKey.handle.exportRawSeed());
100
+ } else {
101
+ const format =
102
+ privateFormat === KFormatType.PEM ? KFormatType.PEM : KFormatType.DER;
103
+ const exported = privateKey.handle.exportKey(
104
+ format,
105
+ KeyEncoding.PKCS8,
106
+ cipher,
107
+ passphrase,
108
+ );
109
+ if (format === KFormatType.PEM) {
110
+ privateKeyOutput = Buffer.from(new Uint8Array(exported)).toString(
111
+ 'utf-8',
112
+ );
113
+ } else {
114
+ privateKeyOutput = exported;
115
+ }
116
+ }
117
+
118
+ return { publicKey: publicKeyOutput, privateKey: privateKeyOutput };
119
+ }
120
+
121
+ function slhDsaGenerateKeyPairNodeSync(
122
+ type: SlhDsaKeyPairType,
123
+ encoding: KeyPairGenConfig,
124
+ ): {
125
+ publicKey: PublicKeyObject | string | ArrayBuffer | Buffer;
126
+ privateKey: PrivateKeyObject | string | ArrayBuffer | Buffer;
127
+ } {
128
+ const slhdsa = new SlhDsa(SLH_DSA_TYPE_TO_VARIANT[type]);
129
+ slhdsa.generateKeyPairSync();
130
+ return slhDsaFormatKeyPairOutput(slhdsa, encoding);
131
+ }
132
+
133
+ async function slhDsaGenerateKeyPairNode(
134
+ type: SlhDsaKeyPairType,
135
+ encoding: KeyPairGenConfig,
136
+ ): Promise<{
137
+ publicKey: PublicKeyObject | string | ArrayBuffer | Buffer;
138
+ privateKey: PrivateKeyObject | string | ArrayBuffer | Buffer;
139
+ }> {
140
+ const slhdsa = new SlhDsa(SLH_DSA_TYPE_TO_VARIANT[type]);
141
+ await slhdsa.generateKeyPair();
142
+ return slhDsaFormatKeyPairOutput(slhdsa, encoding);
143
+ }
144
+
23
145
  export const generateKeyPair = (
24
146
  type: KeyPairType,
25
147
  options: GenerateKeyPairOptions,
@@ -47,7 +169,24 @@ export const generateKeyPairPromise = (
47
169
  };
48
170
 
49
171
  // generateKeyPairSync
50
- export function generateKeyPairSync(type: KeyPairType): CryptoKeyPair;
172
+ export type KeyObjectKeyPair = {
173
+ publicKey: PublicKeyObject;
174
+ privateKey: PrivateKeyObject;
175
+ };
176
+
177
+ type KeyObjectGenerateKeyPairOptions = Omit<
178
+ GenerateKeyPairOptions,
179
+ 'publicKeyEncoding' | 'privateKeyEncoding'
180
+ > & {
181
+ publicKeyEncoding?: undefined;
182
+ privateKeyEncoding?: undefined;
183
+ };
184
+
185
+ export function generateKeyPairSync(type: KeyPairType): KeyObjectKeyPair;
186
+ export function generateKeyPairSync(
187
+ type: KeyPairType,
188
+ options: KeyObjectGenerateKeyPairOptions,
189
+ ): KeyObjectKeyPair;
51
190
  export function generateKeyPairSync(
52
191
  type: KeyPairType,
53
192
  options: GenerateKeyPairOptions,
@@ -117,10 +256,10 @@ function parseKeyPairEncoding(
117
256
  }
118
257
 
119
258
  return {
120
- publicFormat,
121
- publicType,
122
- privateFormat,
123
- privateType,
259
+ publicFormat: publicFormat as KeyPairGenConfig['publicFormat'],
260
+ publicType: publicType as KeyPairGenConfig['publicType'],
261
+ privateFormat: privateFormat as KeyPairGenConfig['privateFormat'],
262
+ privateType: privateType as KeyPairGenConfig['privateType'],
124
263
  cipher,
125
264
  passphrase,
126
265
  };
@@ -147,6 +286,9 @@ function internalGenerateKeyPair(
147
286
  case 'dh':
148
287
  break;
149
288
  default: {
289
+ if (isSlhDsaType(type)) {
290
+ break;
291
+ }
150
292
  const err = new Error(`
151
293
  Invalid Argument options: '${type}' scheme not supported for
152
294
  generateKeyPair(). Currently not all encryption methods are supported in
@@ -168,6 +310,8 @@ function internalGenerateKeyPair(
168
310
  result = await dsa_generateKeyPairNode(options, encoding);
169
311
  } else if (type === 'dh') {
170
312
  result = await dh_generateKeyPairNode(options, encoding);
313
+ } else if (isSlhDsaType(type)) {
314
+ result = await slhDsaGenerateKeyPairNode(type, encoding);
171
315
  } else {
172
316
  throw new Error(`Unsupported key type: ${type}`);
173
317
  }
@@ -198,6 +342,8 @@ function internalGenerateKeyPair(
198
342
  result = dsa_generateKeyPairNodeSync(options, encoding);
199
343
  } else if (type === 'dh') {
200
344
  result = dh_generateKeyPairNodeSync(options, encoding);
345
+ } else if (isSlhDsaType(type)) {
346
+ result = slhDsaGenerateKeyPairNodeSync(type, encoding);
201
347
  } else {
202
348
  throw new Error(`Unsupported key type: ${type}`);
203
349
  }