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/keys/index.ts CHANGED
@@ -40,14 +40,68 @@ import { randomBytes } from '../random';
40
40
 
41
41
  interface KeyInputObject {
42
42
  key: BinaryLike | KeyObject | CryptoKey | JWK;
43
- format?: 'pem' | 'der' | 'jwk';
43
+ format?: 'pem' | 'der' | 'jwk' | 'raw-public' | 'raw-private' | 'raw-seed';
44
44
  type?: 'pkcs1' | 'pkcs8' | 'spki' | 'sec1';
45
45
  passphrase?: BinaryLike;
46
46
  encoding?: BufferEncoding;
47
+ asymmetricKeyType?: string;
48
+ namedCurve?: string;
47
49
  }
48
50
 
49
51
  type KeyInput = BinaryLike | KeyInputObject | KeyObject | CryptoKey;
50
52
 
53
+ function isRawFormat(
54
+ format: string | undefined,
55
+ ): format is 'raw-public' | 'raw-private' | 'raw-seed' {
56
+ return (
57
+ format === 'raw-public' || format === 'raw-private' || format === 'raw-seed'
58
+ );
59
+ }
60
+
61
+ function createPublicKeyFromRaw(input: KeyInputObject): PublicKeyObject {
62
+ if (input.format !== 'raw-public') {
63
+ throw new Error('Invalid format for createPublicKey raw import');
64
+ }
65
+ if (typeof input.asymmetricKeyType !== 'string') {
66
+ throw new Error('options.asymmetricKeyType is required for raw key import');
67
+ }
68
+ if (input.asymmetricKeyType === 'ec' && !input.namedCurve) {
69
+ throw new Error('options.namedCurve is required for EC raw key import');
70
+ }
71
+ const handle =
72
+ NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
73
+ handle.initRawPublic(
74
+ input.asymmetricKeyType,
75
+ toAB(input.key as BinaryLike),
76
+ input.namedCurve,
77
+ );
78
+ return new PublicKeyObject(handle);
79
+ }
80
+
81
+ function createPrivateKeyFromRaw(input: KeyInputObject): PrivateKeyObject {
82
+ if (input.format !== 'raw-private' && input.format !== 'raw-seed') {
83
+ throw new Error('Invalid format for createPrivateKey raw import');
84
+ }
85
+ if (typeof input.asymmetricKeyType !== 'string') {
86
+ throw new Error('options.asymmetricKeyType is required for raw key import');
87
+ }
88
+ if (input.asymmetricKeyType === 'ec' && !input.namedCurve) {
89
+ throw new Error('options.namedCurve is required for EC raw key import');
90
+ }
91
+ const handle =
92
+ NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
93
+ if (input.format === 'raw-seed') {
94
+ handle.initRawSeed(input.asymmetricKeyType, toAB(input.key as BinaryLike));
95
+ } else {
96
+ handle.initRawPrivate(
97
+ input.asymmetricKeyType,
98
+ toAB(input.key as BinaryLike),
99
+ input.namedCurve,
100
+ );
101
+ }
102
+ return new PrivateKeyObject(handle);
103
+ }
104
+
51
105
  function createSecretKey(key: BinaryLike): SecretKeyObject {
52
106
  const keyBuffer = toAB(key);
53
107
  return KeyObject.createKeyObject('secret', keyBuffer) as SecretKeyObject;
@@ -116,8 +170,10 @@ function prepareAsymmetricKey(
116
170
  return { data: toAB(data), format: 'pem', type };
117
171
  }
118
172
 
119
- // Filter out 'jwk' format - only 'pem' and 'der' are supported here
120
- const filteredFormat = format === 'jwk' ? undefined : format;
173
+ // Filter to only 'pem' or 'der' — JWK and raw formats are handled
174
+ // separately via dedicated paths.
175
+ const filteredFormat: 'pem' | 'der' | undefined =
176
+ format === 'pem' || format === 'der' ? format : undefined;
121
177
  return { data: toAB(data), format: filteredFormat, type };
122
178
  }
123
179
 
@@ -125,6 +181,14 @@ function prepareAsymmetricKey(
125
181
  }
126
182
 
127
183
  function createPublicKey(key: KeyInput): PublicKeyObject {
184
+ if (typeof key === 'object' && 'key' in key && isRawFormat(key.format)) {
185
+ if (key.format !== 'raw-public') {
186
+ throw new Error(
187
+ `Invalid format ${key.format} for createPublicKey — only 'raw-public' is allowed`,
188
+ );
189
+ }
190
+ return createPublicKeyFromRaw(key as KeyInputObject);
191
+ }
128
192
  if (typeof key === 'object' && 'key' in key && key.format === 'jwk') {
129
193
  const handle =
130
194
  NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
@@ -169,6 +233,12 @@ function createPublicKey(key: KeyInput): PublicKeyObject {
169
233
  }
170
234
 
171
235
  function createPrivateKey(key: KeyInput): PrivateKeyObject {
236
+ if (typeof key === 'object' && 'key' in key && isRawFormat(key.format)) {
237
+ if (key.format === 'raw-public') {
238
+ throw new Error("Invalid format 'raw-public' for createPrivateKey");
239
+ }
240
+ return createPrivateKeyFromRaw(key as KeyInputObject);
241
+ }
172
242
  if (typeof key === 'object' && 'key' in key && key.format === 'jwk') {
173
243
  const handle =
174
244
  NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
@@ -13,6 +13,7 @@ import {
13
13
  KFormatType,
14
14
  KeyEncoding,
15
15
  } from '../utils';
16
+ import { constants } from '../constants';
16
17
 
17
18
  type KeyInput = BinaryLike | KeyObject | CryptoKey | KeyInputObject;
18
19
 
@@ -144,6 +145,16 @@ function dsaEncodingToNumber(
144
145
  return undefined;
145
146
  }
146
147
 
148
+ function getSaltLength(options?: SignOptions): number | undefined {
149
+ if (
150
+ options?.padding === constants.RSA_PKCS1_PSS_PADDING &&
151
+ options?.saltLength === undefined
152
+ ) {
153
+ return constants.RSA_PSS_SALTLEN_MAX_SIGN;
154
+ }
155
+ return options?.saltLength;
156
+ }
157
+
147
158
  export class Sign {
148
159
  private handle: SignHandleSpec;
149
160
 
@@ -169,7 +180,7 @@ export class Sign {
169
180
  const signature = this.handle.sign(
170
181
  keyObject.handle,
171
182
  options?.padding,
172
- options?.saltLength,
183
+ getSaltLength(options),
173
184
  dsaEncodingToNumber(options?.dsaEncoding),
174
185
  );
175
186
 
@@ -219,7 +230,7 @@ export class Verify {
219
230
  keyObject.handle,
220
231
  sigBuffer,
221
232
  options?.padding,
222
- options?.saltLength,
233
+ getSaltLength(options),
223
234
  dsaEncodingToNumber(options?.dsaEncoding),
224
235
  );
225
236
  }
package/src/keys/utils.ts CHANGED
@@ -5,13 +5,36 @@ import {
5
5
  KFormatType,
6
6
  } from '../utils';
7
7
  import type { CryptoKeyPair, EncodingOptions } from '../utils';
8
- import type { CryptoKey } from './classes';
8
+ import type { CryptoKey, PublicKeyObject, PrivateKeyObject } from './classes';
9
9
 
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
11
  export const isCryptoKey = (obj: any): boolean => {
12
12
  return obj !== null && obj?.keyObject !== undefined;
13
13
  };
14
14
 
15
+ export function exportPublicKeyRaw(
16
+ pub: PublicKeyObject,
17
+ pointType: 'compressed' | 'uncompressed' | undefined,
18
+ ): ArrayBuffer {
19
+ if (pub.asymmetricKeyType === 'ec') {
20
+ return pub.handle.exportECPublicRaw(pointType === 'compressed');
21
+ }
22
+ return pub.handle.exportRawPublic();
23
+ }
24
+
25
+ export function exportPrivateKeyRaw(
26
+ priv: PrivateKeyObject,
27
+ format: 'raw-private' | 'raw-seed',
28
+ ): ArrayBuffer {
29
+ if (format === 'raw-seed') {
30
+ return priv.handle.exportRawSeed();
31
+ }
32
+ if (priv.asymmetricKeyType === 'ec') {
33
+ return priv.handle.exportECPrivateRaw();
34
+ }
35
+ return priv.handle.exportRawPrivate();
36
+ }
37
+
15
38
  export function getCryptoKeyPair(
16
39
  key: CryptoKey | CryptoKeyPair,
17
40
  ): CryptoKeyPair {
@@ -62,6 +85,19 @@ export function parseKeyEncoding(
62
85
  objName,
63
86
  );
64
87
 
88
+ if (
89
+ format === 'raw-public' ||
90
+ format === 'raw-private' ||
91
+ format === 'raw-seed'
92
+ ) {
93
+ if (enc.cipher != null || enc.passphrase !== undefined) {
94
+ throw new Error(
95
+ `Incompatible key options: ${format} does not support encryption`,
96
+ );
97
+ }
98
+ return { format, type, cipher: undefined, passphrase: undefined };
99
+ }
100
+
65
101
  let cipher, passphrase, encoding;
66
102
  if (isPublic !== true) {
67
103
  ({ cipher, passphrase, encoding } = enc);
@@ -77,7 +113,7 @@ export function parseKeyEncoding(
77
113
  (type === KeyEncoding.PKCS1 || type === KeyEncoding.SEC1)
78
114
  ) {
79
115
  throw new Error(
80
- `Incompatible key options ${encodingNames[type]} does not support encryption`,
116
+ `Incompatible key options ${encodingNames[type as KeyEncoding]} does not support encryption`,
81
117
  );
82
118
  }
83
119
  } else if (passphrase !== undefined) {
@@ -120,12 +156,15 @@ function parseKeyFormat(
120
156
  formatStr?: string,
121
157
  defaultFormat?: KFormatType,
122
158
  optionName?: string,
123
- ) {
159
+ ): KFormatType | 'raw-public' | 'raw-private' | 'raw-seed' {
124
160
  if (formatStr === undefined && defaultFormat !== undefined)
125
161
  return defaultFormat;
126
162
  else if (formatStr === 'pem') return KFormatType.PEM;
127
163
  else if (formatStr === 'der') return KFormatType.DER;
128
164
  else if (formatStr === 'jwk') return KFormatType.JWK;
165
+ else if (formatStr === 'raw-public') return 'raw-public';
166
+ else if (formatStr === 'raw-private') return 'raw-private';
167
+ else if (formatStr === 'raw-seed') return 'raw-seed';
129
168
  throw new Error(`Invalid key format str: ${optionName}`);
130
169
  }
131
170
 
@@ -166,7 +205,10 @@ function parseKeyFormatAndType(
166
205
  keyType?: string,
167
206
  isPublic?: boolean,
168
207
  objName?: string,
169
- ) {
208
+ ): {
209
+ format: KFormatType | 'raw-public' | 'raw-private' | 'raw-seed';
210
+ type: KeyEncoding | 'compressed' | 'uncompressed' | undefined;
211
+ } {
170
212
  const { format: formatStr, type: typeStr } = enc;
171
213
 
172
214
  const isInput = keyType === undefined;
@@ -176,11 +218,42 @@ function parseKeyFormatAndType(
176
218
  option('format', objName),
177
219
  );
178
220
 
221
+ if (format === 'raw-public') {
222
+ if (isPublic === false) {
223
+ throw new Error(
224
+ `Invalid format 'raw-public' for ${option('format', objName)}`,
225
+ );
226
+ }
227
+ if (typeStr === undefined || typeStr === 'uncompressed') {
228
+ return { format, type: 'uncompressed' };
229
+ }
230
+ if (typeStr === 'compressed') {
231
+ return { format, type: 'compressed' };
232
+ }
233
+ throw new Error(
234
+ `Invalid ${option('type', objName)} for raw-public: ${typeStr}`,
235
+ );
236
+ }
237
+
238
+ if (format === 'raw-private' || format === 'raw-seed') {
239
+ if (isPublic === true) {
240
+ throw new Error(
241
+ `Invalid format '${format}' for ${option('format', objName)}`,
242
+ );
243
+ }
244
+ if (typeStr !== undefined) {
245
+ throw new Error(
246
+ `Invalid ${option('type', objName)} for ${format}: ${typeStr}`,
247
+ );
248
+ }
249
+ return { format, type: undefined };
250
+ }
251
+
179
252
  const isRequired =
180
253
  (!isInput || format === KFormatType.DER) && format !== KFormatType.JWK;
181
254
 
182
255
  const type = parseKeyType(
183
- typeStr,
256
+ typeStr as string | undefined,
184
257
  isRequired,
185
258
  keyType,
186
259
  isPublic,
package/src/random.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { Buffer } from '@craftzdog/react-native-buffer';
2
2
  import type { ABV, RandomCallback } from './utils';
3
- import { abvToArrayBuffer } from './utils';
3
+ import {
4
+ abvToArrayBuffer,
5
+ lazyDOMException,
6
+ QuotaExceededError,
7
+ rejectSharedArrayBuffer,
8
+ } from './utils';
4
9
  import { NitroModules } from 'react-native-nitro-modules';
5
10
  import type { Random } from './specs/random.nitro';
6
11
 
@@ -288,6 +293,27 @@ export type RandomTypedArrays =
288
293
  | Uint16Array
289
294
  | Uint32Array;
290
295
 
296
+ // WebCrypto §getRandomValues only accepts integer-typed views. Float and
297
+ // non-TypedArray ABVs (DataView) must be rejected with a TypeMismatchError
298
+ // DOMException — see https://w3c.github.io/webcrypto/#Crypto-method-getRandomValues
299
+ const INTEGER_TYPED_ARRAY_TAGS = new Set([
300
+ 'Int8Array',
301
+ 'Int16Array',
302
+ 'Int32Array',
303
+ 'Uint8Array',
304
+ 'Uint8ClampedArray',
305
+ 'Uint16Array',
306
+ 'Uint32Array',
307
+ 'BigInt64Array',
308
+ 'BigUint64Array',
309
+ ]);
310
+
311
+ function isIntegerTypedArray(value: unknown): boolean {
312
+ if (!ArrayBuffer.isView(value)) return false;
313
+ const tag = (value as { [Symbol.toStringTag]?: string })[Symbol.toStringTag];
314
+ return tag !== undefined && INTEGER_TYPED_ARRAY_TAGS.has(tag);
315
+ }
316
+
291
317
  /**
292
318
  * Fills the provided typed array with cryptographically strong random values.
293
319
  *
@@ -295,8 +321,24 @@ export type RandomTypedArrays =
295
321
  * @returns The filled data
296
322
  */
297
323
  export function getRandomValues(data: RandomTypedArrays) {
324
+ // WebIDL BufferSource conversion (TypeError) must run before the
325
+ // WebCrypto-specific integer-type / size checks (TypeMismatchError /
326
+ // QuotaExceededError). `randomFillSync` below also rejects SAB via
327
+ // `abvToArrayBuffer`, but by then we'd already have thrown the wrong
328
+ // error type for a non-integer SAB-view, so the explicit early call is
329
+ // load-bearing for spec compliance — not redundant.
330
+ rejectSharedArrayBuffer(data);
331
+ if (!isIntegerTypedArray(data)) {
332
+ throw lazyDOMException(
333
+ 'The data argument must be an integer-type TypedArray',
334
+ 'TypeMismatchError',
335
+ );
336
+ }
298
337
  if (data.byteLength > 65536) {
299
- throw new Error('The requested length exceeds 65,536 bytes');
338
+ throw new QuotaExceededError('The requested length exceeds 65,536 bytes', {
339
+ quota: 65536,
340
+ requested: data.byteLength,
341
+ });
300
342
  }
301
343
  randomFillSync(data, 0);
302
344
  return data;
@@ -308,14 +350,28 @@ for (let i = 0; i < 256; ++i) {
308
350
  byteToHex.push((i + 0x100).toString(16).slice(1));
309
351
  }
310
352
 
311
- // Based on https://github.com/uuidjs/uuid/blob/main/src/v4.js
312
- export function randomUUID() {
313
- const size = 16;
314
- const buffer = new Buffer(size);
315
- randomFillSync(buffer, 0, size);
353
+ export interface RandomUUIDOptions {
354
+ // Accepted for Node.js parity. RNQC does not buffer entropy, so this is a
355
+ // no-op: every UUID already pulls fresh bytes from the OS CSPRNG.
356
+ disableEntropyCache?: boolean;
357
+ }
316
358
 
317
- // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
318
- buffer[6] = (buffer[6]! & 0x0f) | 0x40;
359
+ function validateRandomUUIDOptions(options?: RandomUUIDOptions): void {
360
+ if (options === undefined) return;
361
+ if (typeof options !== 'object' || options === null) {
362
+ throw new TypeError('options must be an object');
363
+ }
364
+ if (
365
+ options.disableEntropyCache !== undefined &&
366
+ typeof options.disableEntropyCache !== 'boolean'
367
+ ) {
368
+ throw new TypeError('options.disableEntropyCache must be a boolean');
369
+ }
370
+ }
371
+
372
+ // RFC 9562 variant 10xx is shared by v4 and v7.
373
+ function serializeUUID(buffer: Buffer, version: number): string {
374
+ buffer[6] = (buffer[6]! & 0x0f) | (version << 4);
319
375
  buffer[8] = (buffer[8]! & 0x3f) | 0x80;
320
376
 
321
377
  return (
@@ -341,3 +397,31 @@ export function randomUUID() {
341
397
  byteToHex[buffer[15]!]
342
398
  ).toLowerCase();
343
399
  }
400
+
401
+ // RFC 9562 §5.4 — random UUID (v4).
402
+ export function randomUUID(options?: RandomUUIDOptions): string {
403
+ validateRandomUUIDOptions(options);
404
+ const buffer = new Buffer(16);
405
+ randomFillSync(buffer, 0, 16);
406
+ return serializeUUID(buffer, 4);
407
+ }
408
+
409
+ // RFC 9562 §5.7 — Unix-ms timestamped UUID (v7).
410
+ // Layout: 48-bit big-endian Unix-ms timestamp | 4-bit version (7) |
411
+ // 12 bits random | 2-bit variant (10) | 62 bits random.
412
+ export function randomUUIDv7(options?: RandomUUIDOptions): string {
413
+ validateRandomUUIDOptions(options);
414
+ const buffer = new Buffer(16);
415
+ randomFillSync(buffer, 6, 10);
416
+
417
+ const now = Date.now();
418
+ const msb = Math.floor(now / 0x100000000);
419
+ buffer[0] = (msb >>> 8) & 0xff;
420
+ buffer[1] = msb & 0xff;
421
+ buffer[2] = (now >>> 24) & 0xff;
422
+ buffer[3] = (now >>> 16) & 0xff;
423
+ buffer[4] = (now >>> 8) & 0xff;
424
+ buffer[5] = now & 0xff;
425
+
426
+ return serializeUUID(buffer, 7);
427
+ }
package/src/rsa.ts CHANGED
@@ -260,6 +260,14 @@ function rsa_formatKeyPairOutput(
260
260
  let publicKey: PublicKeyObject | Buffer | string | ArrayBuffer;
261
261
  let privateKey: PrivateKeyObject | Buffer | string | ArrayBuffer;
262
262
 
263
+ if (
264
+ publicFormat === 'raw-public' ||
265
+ privateFormat === 'raw-private' ||
266
+ privateFormat === 'raw-seed'
267
+ ) {
268
+ throw new Error('Raw key formats are not supported for RSA keys');
269
+ }
270
+
263
271
  if (publicFormat === -1) {
264
272
  publicKey = pub;
265
273
  } else {
package/src/slhdsa.ts ADDED
@@ -0,0 +1,146 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ import type { SlhDsaKeyPair } from './specs/slhDsaKeyPair.nitro';
3
+ import {
4
+ CryptoKey,
5
+ KeyObject,
6
+ PublicKeyObject,
7
+ PrivateKeyObject as PrivateKeyObjectClass,
8
+ } from './keys/classes';
9
+ import type {
10
+ CryptoKeyPair,
11
+ KeyUsage,
12
+ SubtleAlgorithm,
13
+ SlhDsaAlgorithm,
14
+ } from './utils';
15
+ import {
16
+ hasAnyNotIn,
17
+ lazyDOMException,
18
+ getUsagesUnion,
19
+ KFormatType,
20
+ KeyEncoding,
21
+ } from './utils';
22
+
23
+ export type SlhDsaVariant = SlhDsaAlgorithm;
24
+
25
+ export const SLH_DSA_VARIANTS: readonly SlhDsaVariant[] = [
26
+ 'SLH-DSA-SHA2-128s',
27
+ 'SLH-DSA-SHA2-128f',
28
+ 'SLH-DSA-SHA2-192s',
29
+ 'SLH-DSA-SHA2-192f',
30
+ 'SLH-DSA-SHA2-256s',
31
+ 'SLH-DSA-SHA2-256f',
32
+ 'SLH-DSA-SHAKE-128s',
33
+ 'SLH-DSA-SHAKE-128f',
34
+ 'SLH-DSA-SHAKE-192s',
35
+ 'SLH-DSA-SHAKE-192f',
36
+ 'SLH-DSA-SHAKE-256s',
37
+ 'SLH-DSA-SHAKE-256f',
38
+ ] as const;
39
+
40
+ export class SlhDsa {
41
+ variant: SlhDsaVariant;
42
+ native: SlhDsaKeyPair;
43
+
44
+ constructor(variant: SlhDsaVariant) {
45
+ this.variant = variant;
46
+ this.native =
47
+ NitroModules.createHybridObject<SlhDsaKeyPair>('SlhDsaKeyPair');
48
+ this.native.setVariant(variant);
49
+ }
50
+
51
+ async generateKeyPair(): Promise<void> {
52
+ await this.native.generateKeyPair(
53
+ KFormatType.DER,
54
+ KeyEncoding.SPKI,
55
+ KFormatType.DER,
56
+ KeyEncoding.PKCS8,
57
+ );
58
+ }
59
+
60
+ generateKeyPairSync(): void {
61
+ this.native.generateKeyPairSync(
62
+ KFormatType.DER,
63
+ KeyEncoding.SPKI,
64
+ KFormatType.DER,
65
+ KeyEncoding.PKCS8,
66
+ );
67
+ }
68
+
69
+ getPublicKey(): ArrayBuffer {
70
+ return this.native.getPublicKey();
71
+ }
72
+
73
+ getPrivateKey(): ArrayBuffer {
74
+ return this.native.getPrivateKey();
75
+ }
76
+
77
+ async sign(message: ArrayBuffer): Promise<ArrayBuffer> {
78
+ return this.native.sign(message);
79
+ }
80
+
81
+ signSync(message: ArrayBuffer): ArrayBuffer {
82
+ return this.native.signSync(message);
83
+ }
84
+
85
+ async verify(signature: ArrayBuffer, message: ArrayBuffer): Promise<boolean> {
86
+ return this.native.verify(signature, message);
87
+ }
88
+
89
+ verifySync(signature: ArrayBuffer, message: ArrayBuffer): boolean {
90
+ return this.native.verifySync(signature, message);
91
+ }
92
+ }
93
+
94
+ export async function slhdsa_generateKeyPairWebCrypto(
95
+ variant: SlhDsaVariant,
96
+ extractable: boolean,
97
+ keyUsages: KeyUsage[],
98
+ ): Promise<CryptoKeyPair> {
99
+ if (hasAnyNotIn(keyUsages, ['sign', 'verify'])) {
100
+ throw lazyDOMException(
101
+ `Unsupported key usage for ${variant}`,
102
+ 'SyntaxError',
103
+ );
104
+ }
105
+
106
+ const publicUsages = getUsagesUnion(keyUsages, 'verify');
107
+ const privateUsages = getUsagesUnion(keyUsages, 'sign');
108
+
109
+ if (privateUsages.length === 0) {
110
+ throw lazyDOMException('Usages cannot be empty', 'SyntaxError');
111
+ }
112
+
113
+ const slhdsa = new SlhDsa(variant);
114
+ await slhdsa.generateKeyPair();
115
+
116
+ const publicKeyData = slhdsa.getPublicKey();
117
+ const privateKeyData = slhdsa.getPrivateKey();
118
+
119
+ const pub = KeyObject.createKeyObject(
120
+ 'public',
121
+ publicKeyData,
122
+ KFormatType.DER,
123
+ KeyEncoding.SPKI,
124
+ ) as PublicKeyObject;
125
+ const publicKey = new CryptoKey(
126
+ pub,
127
+ { name: variant } as SubtleAlgorithm,
128
+ publicUsages,
129
+ true,
130
+ );
131
+
132
+ const priv = KeyObject.createKeyObject(
133
+ 'private',
134
+ privateKeyData,
135
+ KFormatType.DER,
136
+ KeyEncoding.PKCS8,
137
+ ) as PrivateKeyObjectClass;
138
+ const privateKey = new CryptoKey(
139
+ priv,
140
+ { name: variant } as SubtleAlgorithm,
141
+ privateUsages,
142
+ extractable,
143
+ );
144
+
145
+ return { publicKey, privateKey };
146
+ }
@@ -18,6 +18,11 @@ export interface KeyObjectHandle
18
18
  passphrase?: ArrayBuffer,
19
19
  ): ArrayBuffer;
20
20
  exportJwk(key: JWK, handleRsaPss: boolean): JWK;
21
+ exportRawPublic(): ArrayBuffer;
22
+ exportRawPrivate(): ArrayBuffer;
23
+ exportRawSeed(): ArrayBuffer;
24
+ exportECPublicRaw(compressed: boolean): ArrayBuffer;
25
+ exportECPrivateRaw(): ArrayBuffer;
21
26
  getAsymmetricKeyType(): AsymmetricKeyType;
22
27
  init(
23
28
  keyType: KeyType,
@@ -32,8 +37,20 @@ export interface KeyObjectHandle
32
37
  keyData: ArrayBuffer,
33
38
  isPublic: boolean,
34
39
  ): boolean;
40
+ initRawPublic(
41
+ asymmetricKeyType: string,
42
+ keyData: ArrayBuffer,
43
+ namedCurve?: string,
44
+ ): boolean;
45
+ initRawPrivate(
46
+ asymmetricKeyType: string,
47
+ keyData: ArrayBuffer,
48
+ namedCurve?: string,
49
+ ): boolean;
50
+ initRawSeed(asymmetricKeyType: string, keyData: ArrayBuffer): boolean;
35
51
  initJwk(keyData: JWK, namedCurve?: NamedCurve): KeyType | undefined;
36
52
  keyDetail(): KeyDetail;
37
53
  keyEquals(other: KeyObjectHandle): boolean;
38
54
  getSymmetricKeySize(): number;
55
+ checkEcKeyData(): boolean;
39
56
  }
@@ -0,0 +1,29 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+
3
+ export interface SlhDsaKeyPair
4
+ extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
5
+ generateKeyPair(
6
+ publicFormat: number,
7
+ publicType: number,
8
+ privateFormat: number,
9
+ privateType: number,
10
+ ): Promise<void>;
11
+
12
+ generateKeyPairSync(
13
+ publicFormat: number,
14
+ publicType: number,
15
+ privateFormat: number,
16
+ privateType: number,
17
+ ): void;
18
+
19
+ getPublicKey(): ArrayBuffer;
20
+ getPrivateKey(): ArrayBuffer;
21
+
22
+ sign(message: ArrayBuffer): Promise<ArrayBuffer>;
23
+ signSync(message: ArrayBuffer): ArrayBuffer;
24
+
25
+ verify(signature: ArrayBuffer, message: ArrayBuffer): Promise<boolean>;
26
+ verifySync(signature: ArrayBuffer, message: ArrayBuffer): boolean;
27
+
28
+ setVariant(variant: string): void;
29
+ }
@@ -0,0 +1,21 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+
3
+ export type TurboShakeVariant = 'TurboSHAKE128' | 'TurboSHAKE256';
4
+ export type KangarooTwelveVariant = 'KT128' | 'KT256';
5
+
6
+ export interface TurboShake
7
+ extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
8
+ turboShake(
9
+ variant: TurboShakeVariant,
10
+ domainSeparation: number,
11
+ outputLength: number,
12
+ data: ArrayBuffer,
13
+ ): Promise<ArrayBuffer>;
14
+
15
+ kangarooTwelve(
16
+ variant: KangarooTwelveVariant,
17
+ outputLength: number,
18
+ data: ArrayBuffer,
19
+ customization?: ArrayBuffer,
20
+ ): Promise<ArrayBuffer>;
21
+ }