react-native-quick-crypto 1.1.2 → 1.1.4
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.
- package/QuickCrypto.podspec +2 -2
- package/android/build.gradle +2 -2
- package/cpp/utils/HybridUtils.cpp +60 -10
- package/cpp/utils/HybridUtils.hpp +4 -0
- package/lib/commonjs/expo-plugin/withXCode.js +3 -3
- package/lib/commonjs/index.js +4 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/subtle.js +692 -12
- package/lib/commonjs/subtle.js.map +1 -1
- package/lib/commonjs/utils/conversion.js +8 -6
- package/lib/commonjs/utils/conversion.js.map +1 -1
- package/lib/commonjs/utils/types.js.map +1 -1
- package/lib/module/expo-plugin/withXCode.js +3 -3
- package/lib/module/index.js +4 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/subtle.js +693 -13
- package/lib/module/subtle.js.map +1 -1
- package/lib/module/utils/conversion.js +8 -6
- package/lib/module/utils/conversion.js.map +1 -1
- package/lib/module/utils/types.js.map +1 -1
- package/lib/typescript/index.d.ts +6 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/subtle.d.ts.map +1 -1
- package/lib/typescript/utils/conversion.d.ts +5 -3
- package/lib/typescript/utils/conversion.d.ts.map +1 -1
- package/lib/typescript/utils/types.d.ts +3 -1
- package/lib/typescript/utils/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/expo-plugin/withXCode.ts +3 -3
- package/src/index.ts +16 -3
- package/src/subtle.ts +736 -26
- package/src/utils/conversion.ts +8 -6
- package/src/utils/types.ts +5 -0
package/src/subtle.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
1
|
import { Buffer as SBuffer } from 'safe-buffer';
|
|
3
2
|
import type {
|
|
4
3
|
SubtleAlgorithm,
|
|
@@ -28,7 +27,10 @@ import {
|
|
|
28
27
|
SecretKeyObject,
|
|
29
28
|
} from './keys';
|
|
30
29
|
import type { CryptoKeyPair } from './utils/types';
|
|
31
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
binaryLikeToArrayBuffer,
|
|
32
|
+
bufferLikeToArrayBuffer,
|
|
33
|
+
} from './utils/conversion';
|
|
32
34
|
import { argon2Sync } from './argon2';
|
|
33
35
|
import { lazyDOMException } from './utils/errors';
|
|
34
36
|
import { normalizeHashName, HashContext } from './utils/hashnames';
|
|
@@ -130,19 +132,705 @@ function getCanonicalAlgorithmNames(): Map<string, AnyAlgorithm> {
|
|
|
130
132
|
return _canonicalAlgorithmNames;
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
// Per-algorithm WebIDL converter table. Mirrors Node's kAlgorithmDefinitions
|
|
136
|
+
// (lib/internal/crypto/util.js): each (algorithm, operation) pair maps to a
|
|
137
|
+
// dictionary converter name, or null when only the `name` member is required.
|
|
138
|
+
// Operation keys are missing when an algorithm cannot perform that operation,
|
|
139
|
+
// causing `normalizeAlgorithm` to reject the call.
|
|
140
|
+
const kAlgorithmDefinitions: Record<string, Record<string, string | null>> = {
|
|
141
|
+
'AES-CBC': {
|
|
142
|
+
generateKey: 'AesKeyGenParams',
|
|
143
|
+
exportKey: null,
|
|
144
|
+
importKey: null,
|
|
145
|
+
encrypt: 'AesCbcParams',
|
|
146
|
+
decrypt: 'AesCbcParams',
|
|
147
|
+
'get key length': 'AesDerivedKeyParams',
|
|
148
|
+
},
|
|
149
|
+
'AES-CTR': {
|
|
150
|
+
generateKey: 'AesKeyGenParams',
|
|
151
|
+
exportKey: null,
|
|
152
|
+
importKey: null,
|
|
153
|
+
encrypt: 'AesCtrParams',
|
|
154
|
+
decrypt: 'AesCtrParams',
|
|
155
|
+
'get key length': 'AesDerivedKeyParams',
|
|
156
|
+
},
|
|
157
|
+
'AES-GCM': {
|
|
158
|
+
generateKey: 'AesKeyGenParams',
|
|
159
|
+
exportKey: null,
|
|
160
|
+
importKey: null,
|
|
161
|
+
encrypt: 'AeadParams',
|
|
162
|
+
decrypt: 'AeadParams',
|
|
163
|
+
'get key length': 'AesDerivedKeyParams',
|
|
164
|
+
},
|
|
165
|
+
'AES-KW': {
|
|
166
|
+
generateKey: 'AesKeyGenParams',
|
|
167
|
+
exportKey: null,
|
|
168
|
+
importKey: null,
|
|
169
|
+
'get key length': 'AesDerivedKeyParams',
|
|
170
|
+
wrapKey: null,
|
|
171
|
+
unwrapKey: null,
|
|
172
|
+
},
|
|
173
|
+
'AES-OCB': {
|
|
174
|
+
generateKey: 'AesKeyGenParams',
|
|
175
|
+
exportKey: null,
|
|
176
|
+
importKey: null,
|
|
177
|
+
encrypt: 'AeadParams',
|
|
178
|
+
decrypt: 'AeadParams',
|
|
179
|
+
'get key length': 'AesDerivedKeyParams',
|
|
180
|
+
},
|
|
181
|
+
Argon2d: {
|
|
182
|
+
deriveBits: 'Argon2Params',
|
|
183
|
+
'get key length': null,
|
|
184
|
+
importKey: null,
|
|
185
|
+
},
|
|
186
|
+
Argon2i: {
|
|
187
|
+
deriveBits: 'Argon2Params',
|
|
188
|
+
'get key length': null,
|
|
189
|
+
importKey: null,
|
|
190
|
+
},
|
|
191
|
+
Argon2id: {
|
|
192
|
+
deriveBits: 'Argon2Params',
|
|
193
|
+
'get key length': null,
|
|
194
|
+
importKey: null,
|
|
195
|
+
},
|
|
196
|
+
'ChaCha20-Poly1305': {
|
|
197
|
+
generateKey: null,
|
|
198
|
+
exportKey: null,
|
|
199
|
+
importKey: null,
|
|
200
|
+
encrypt: 'AeadParams',
|
|
201
|
+
decrypt: 'AeadParams',
|
|
202
|
+
'get key length': null,
|
|
203
|
+
},
|
|
204
|
+
ECDH: {
|
|
205
|
+
generateKey: 'EcKeyGenParams',
|
|
206
|
+
exportKey: null,
|
|
207
|
+
importKey: 'EcKeyImportParams',
|
|
208
|
+
deriveBits: 'EcdhKeyDeriveParams',
|
|
209
|
+
},
|
|
210
|
+
ECDSA: {
|
|
211
|
+
generateKey: 'EcKeyGenParams',
|
|
212
|
+
exportKey: null,
|
|
213
|
+
importKey: 'EcKeyImportParams',
|
|
214
|
+
sign: 'EcdsaParams',
|
|
215
|
+
verify: 'EcdsaParams',
|
|
216
|
+
},
|
|
217
|
+
Ed25519: {
|
|
218
|
+
generateKey: null,
|
|
219
|
+
exportKey: null,
|
|
220
|
+
importKey: null,
|
|
221
|
+
sign: null,
|
|
222
|
+
verify: null,
|
|
223
|
+
},
|
|
224
|
+
Ed448: {
|
|
225
|
+
generateKey: null,
|
|
226
|
+
exportKey: null,
|
|
227
|
+
importKey: null,
|
|
228
|
+
sign: 'ContextParams',
|
|
229
|
+
verify: 'ContextParams',
|
|
230
|
+
},
|
|
231
|
+
HKDF: {
|
|
232
|
+
importKey: null,
|
|
233
|
+
deriveBits: 'HkdfParams',
|
|
234
|
+
'get key length': null,
|
|
235
|
+
},
|
|
236
|
+
HMAC: {
|
|
237
|
+
generateKey: 'HmacKeyGenParams',
|
|
238
|
+
exportKey: null,
|
|
239
|
+
importKey: 'HmacImportParams',
|
|
240
|
+
sign: null,
|
|
241
|
+
verify: null,
|
|
242
|
+
'get key length': 'HmacImportParams',
|
|
243
|
+
},
|
|
244
|
+
KMAC128: {
|
|
245
|
+
generateKey: 'KmacKeyGenParams',
|
|
246
|
+
exportKey: null,
|
|
247
|
+
importKey: 'KmacImportParams',
|
|
248
|
+
sign: 'KmacParams',
|
|
249
|
+
verify: 'KmacParams',
|
|
250
|
+
'get key length': 'KmacImportParams',
|
|
251
|
+
},
|
|
252
|
+
KMAC256: {
|
|
253
|
+
generateKey: 'KmacKeyGenParams',
|
|
254
|
+
exportKey: null,
|
|
255
|
+
importKey: 'KmacImportParams',
|
|
256
|
+
sign: 'KmacParams',
|
|
257
|
+
verify: 'KmacParams',
|
|
258
|
+
'get key length': 'KmacImportParams',
|
|
259
|
+
},
|
|
260
|
+
'ML-DSA-44': {
|
|
261
|
+
generateKey: null,
|
|
262
|
+
exportKey: null,
|
|
263
|
+
importKey: null,
|
|
264
|
+
sign: 'ContextParams',
|
|
265
|
+
verify: 'ContextParams',
|
|
266
|
+
},
|
|
267
|
+
'ML-DSA-65': {
|
|
268
|
+
generateKey: null,
|
|
269
|
+
exportKey: null,
|
|
270
|
+
importKey: null,
|
|
271
|
+
sign: 'ContextParams',
|
|
272
|
+
verify: 'ContextParams',
|
|
273
|
+
},
|
|
274
|
+
'ML-DSA-87': {
|
|
275
|
+
generateKey: null,
|
|
276
|
+
exportKey: null,
|
|
277
|
+
importKey: null,
|
|
278
|
+
sign: 'ContextParams',
|
|
279
|
+
verify: 'ContextParams',
|
|
280
|
+
},
|
|
281
|
+
'ML-KEM-512': {
|
|
282
|
+
generateKey: null,
|
|
283
|
+
exportKey: null,
|
|
284
|
+
importKey: null,
|
|
285
|
+
encapsulateBits: null,
|
|
286
|
+
decapsulateBits: null,
|
|
287
|
+
encapsulateKey: null,
|
|
288
|
+
decapsulateKey: null,
|
|
289
|
+
},
|
|
290
|
+
'ML-KEM-768': {
|
|
291
|
+
generateKey: null,
|
|
292
|
+
exportKey: null,
|
|
293
|
+
importKey: null,
|
|
294
|
+
encapsulateBits: null,
|
|
295
|
+
decapsulateBits: null,
|
|
296
|
+
encapsulateKey: null,
|
|
297
|
+
decapsulateKey: null,
|
|
298
|
+
},
|
|
299
|
+
'ML-KEM-1024': {
|
|
300
|
+
generateKey: null,
|
|
301
|
+
exportKey: null,
|
|
302
|
+
importKey: null,
|
|
303
|
+
encapsulateBits: null,
|
|
304
|
+
decapsulateBits: null,
|
|
305
|
+
encapsulateKey: null,
|
|
306
|
+
decapsulateKey: null,
|
|
307
|
+
},
|
|
308
|
+
PBKDF2: {
|
|
309
|
+
importKey: null,
|
|
310
|
+
deriveBits: 'Pbkdf2Params',
|
|
311
|
+
'get key length': null,
|
|
312
|
+
},
|
|
313
|
+
'RSA-OAEP': {
|
|
314
|
+
generateKey: 'RsaHashedKeyGenParams',
|
|
315
|
+
exportKey: null,
|
|
316
|
+
importKey: 'RsaHashedImportParams',
|
|
317
|
+
encrypt: 'RsaOaepParams',
|
|
318
|
+
decrypt: 'RsaOaepParams',
|
|
319
|
+
},
|
|
320
|
+
'RSA-PSS': {
|
|
321
|
+
generateKey: 'RsaHashedKeyGenParams',
|
|
322
|
+
exportKey: null,
|
|
323
|
+
importKey: 'RsaHashedImportParams',
|
|
324
|
+
sign: 'RsaPssParams',
|
|
325
|
+
verify: 'RsaPssParams',
|
|
326
|
+
},
|
|
327
|
+
'RSASSA-PKCS1-v1_5': {
|
|
328
|
+
generateKey: 'RsaHashedKeyGenParams',
|
|
329
|
+
exportKey: null,
|
|
330
|
+
importKey: 'RsaHashedImportParams',
|
|
331
|
+
sign: null,
|
|
332
|
+
verify: null,
|
|
333
|
+
},
|
|
334
|
+
'SHA-1': { digest: null },
|
|
335
|
+
'SHA-256': { digest: null },
|
|
336
|
+
'SHA-384': { digest: null },
|
|
337
|
+
'SHA-512': { digest: null },
|
|
338
|
+
'SHA3-256': { digest: null },
|
|
339
|
+
'SHA3-384': { digest: null },
|
|
340
|
+
'SHA3-512': { digest: null },
|
|
341
|
+
cSHAKE128: { digest: 'CShakeParams' },
|
|
342
|
+
cSHAKE256: { digest: 'CShakeParams' },
|
|
343
|
+
KT128: { digest: 'KangarooTwelveParams' },
|
|
344
|
+
KT256: { digest: 'KangarooTwelveParams' },
|
|
345
|
+
TurboSHAKE128: { digest: 'TurboShakeParams' },
|
|
346
|
+
TurboSHAKE256: { digest: 'TurboShakeParams' },
|
|
347
|
+
X25519: {
|
|
348
|
+
generateKey: null,
|
|
349
|
+
exportKey: null,
|
|
350
|
+
importKey: null,
|
|
351
|
+
deriveBits: 'EcdhKeyDeriveParams',
|
|
352
|
+
},
|
|
353
|
+
X448: {
|
|
354
|
+
generateKey: null,
|
|
355
|
+
exportKey: null,
|
|
356
|
+
importKey: null,
|
|
357
|
+
deriveBits: 'EcdhKeyDeriveParams',
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
for (const v of SLH_DSA_VARIANTS) {
|
|
362
|
+
kAlgorithmDefinitions[v] = {
|
|
363
|
+
generateKey: null,
|
|
364
|
+
exportKey: null,
|
|
365
|
+
importKey: null,
|
|
366
|
+
sign: null,
|
|
367
|
+
verify: null,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// WebIDL dictionary member specs. Mirrors Node's per-converter
|
|
372
|
+
// `createDictionaryConverter` definitions in lib/internal/crypto/webidl.js.
|
|
373
|
+
// `required: true` causes `normalizeAlgorithm` to throw a TypeError when the
|
|
374
|
+
// member is missing — matching the spec'd WebCrypto behavior that
|
|
375
|
+
// `SubtleCrypto.supports` relies on via try/catch.
|
|
376
|
+
interface IdlField {
|
|
377
|
+
key: string;
|
|
378
|
+
required?: boolean;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
type NormalizedAlgorithmRecord = SubtleAlgorithm & Record<string, unknown>;
|
|
382
|
+
|
|
383
|
+
const kRequiredFields: Record<string, IdlField[]> = {
|
|
384
|
+
AesKeyGenParams: [{ key: 'length', required: true }],
|
|
385
|
+
AesDerivedKeyParams: [{ key: 'length', required: true }],
|
|
386
|
+
AesCbcParams: [{ key: 'iv', required: true }],
|
|
387
|
+
AesCtrParams: [
|
|
388
|
+
{ key: 'counter', required: true },
|
|
389
|
+
{ key: 'length', required: true },
|
|
390
|
+
],
|
|
391
|
+
AeadParams: [
|
|
392
|
+
{ key: 'iv', required: true },
|
|
393
|
+
{ key: 'tagLength' },
|
|
394
|
+
{ key: 'additionalData' },
|
|
395
|
+
],
|
|
396
|
+
EcKeyGenParams: [{ key: 'namedCurve', required: true }],
|
|
397
|
+
EcKeyImportParams: [{ key: 'namedCurve', required: true }],
|
|
398
|
+
EcdsaParams: [{ key: 'hash', required: true }],
|
|
399
|
+
EcdhKeyDeriveParams: [{ key: 'public', required: true }],
|
|
400
|
+
HmacKeyGenParams: [{ key: 'hash', required: true }, { key: 'length' }],
|
|
401
|
+
HmacImportParams: [{ key: 'hash', required: true }, { key: 'length' }],
|
|
402
|
+
HkdfParams: [
|
|
403
|
+
{ key: 'hash', required: true },
|
|
404
|
+
{ key: 'salt', required: true },
|
|
405
|
+
{ key: 'info', required: true },
|
|
406
|
+
],
|
|
407
|
+
Pbkdf2Params: [
|
|
408
|
+
{ key: 'hash', required: true },
|
|
409
|
+
{ key: 'iterations', required: true },
|
|
410
|
+
{ key: 'salt', required: true },
|
|
411
|
+
],
|
|
412
|
+
RsaHashedKeyGenParams: [
|
|
413
|
+
{ key: 'modulusLength', required: true },
|
|
414
|
+
{ key: 'publicExponent', required: true },
|
|
415
|
+
{ key: 'hash', required: true },
|
|
416
|
+
],
|
|
417
|
+
RsaHashedImportParams: [{ key: 'hash', required: true }],
|
|
418
|
+
RsaOaepParams: [{ key: 'label' }],
|
|
419
|
+
RsaPssParams: [{ key: 'saltLength', required: true }],
|
|
420
|
+
ContextParams: [{ key: 'context' }],
|
|
421
|
+
Argon2Params: [
|
|
422
|
+
{ key: 'nonce', required: true },
|
|
423
|
+
{ key: 'parallelism', required: true },
|
|
424
|
+
{ key: 'memory', required: true },
|
|
425
|
+
{ key: 'passes', required: true },
|
|
426
|
+
{ key: 'version' },
|
|
427
|
+
{ key: 'secretValue' },
|
|
428
|
+
{ key: 'associatedData' },
|
|
429
|
+
],
|
|
430
|
+
KmacKeyGenParams: [{ key: 'length' }],
|
|
431
|
+
KmacImportParams: [{ key: 'length' }],
|
|
432
|
+
KmacParams: [
|
|
433
|
+
{ key: 'outputLength', required: true },
|
|
434
|
+
{ key: 'customization' },
|
|
435
|
+
],
|
|
436
|
+
CShakeParams: [
|
|
437
|
+
{ key: 'outputLength', required: true },
|
|
438
|
+
{ key: 'functionName' },
|
|
439
|
+
{ key: 'customization' },
|
|
440
|
+
],
|
|
441
|
+
KangarooTwelveParams: [
|
|
442
|
+
{ key: 'outputLength', required: true },
|
|
443
|
+
{ key: 'customization' },
|
|
444
|
+
],
|
|
445
|
+
TurboShakeParams: [
|
|
446
|
+
{ key: 'outputLength', required: true },
|
|
447
|
+
{ key: 'domainSeparation' },
|
|
448
|
+
],
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
function isBufferSource(value: unknown): value is BufferLike {
|
|
452
|
+
return value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function validateBufferSource(
|
|
456
|
+
algorithm: NormalizedAlgorithmRecord,
|
|
457
|
+
key: string,
|
|
458
|
+
): ArrayBuffer | undefined {
|
|
459
|
+
const value = algorithm[key];
|
|
460
|
+
if (value === undefined) return undefined;
|
|
461
|
+
if (!isBufferSource(value)) {
|
|
462
|
+
throw new TypeError(
|
|
463
|
+
`Failed to normalize algorithm: '${key}' must be a BufferSource`,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return bufferLikeToArrayBuffer(value);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function validateBinaryLike(
|
|
470
|
+
algorithm: NormalizedAlgorithmRecord,
|
|
471
|
+
key: string,
|
|
472
|
+
): ArrayBuffer | undefined {
|
|
473
|
+
const value = algorithm[key];
|
|
474
|
+
if (value === undefined) return undefined;
|
|
475
|
+
try {
|
|
476
|
+
return binaryLikeToArrayBuffer(value as BinaryLike);
|
|
477
|
+
} catch {
|
|
478
|
+
throw new TypeError(
|
|
479
|
+
`Failed to normalize algorithm: '${key}' must be a BufferSource`,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function validateByteLength(
|
|
485
|
+
buffer: ArrayBuffer | undefined,
|
|
486
|
+
key: string,
|
|
487
|
+
length: number,
|
|
488
|
+
message?: string,
|
|
489
|
+
): void {
|
|
490
|
+
if (buffer !== undefined && buffer.byteLength !== length) {
|
|
491
|
+
throw lazyDOMException(
|
|
492
|
+
message ?? `${key} must be ${length} bytes`,
|
|
493
|
+
'OperationError',
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function validateUnsignedInteger(
|
|
499
|
+
algorithm: NormalizedAlgorithmRecord,
|
|
500
|
+
key: string,
|
|
501
|
+
): number | undefined {
|
|
502
|
+
const value = algorithm[key];
|
|
503
|
+
if (value === undefined) return undefined;
|
|
504
|
+
const numberValue = Number(value);
|
|
505
|
+
if (
|
|
506
|
+
!Number.isFinite(numberValue) ||
|
|
507
|
+
!Number.isInteger(numberValue) ||
|
|
508
|
+
numberValue < 0
|
|
509
|
+
) {
|
|
510
|
+
throw new TypeError(
|
|
511
|
+
`Failed to normalize algorithm: '${key}' must be an unsigned integer`,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
algorithm[key] = numberValue;
|
|
515
|
+
return numberValue;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function validateHashAlgorithm(
|
|
519
|
+
algorithm: NormalizedAlgorithmRecord,
|
|
520
|
+
converterName: string,
|
|
521
|
+
): void {
|
|
522
|
+
const hash = algorithm.hash as string | { name: string } | undefined;
|
|
523
|
+
if (hash === undefined) return;
|
|
524
|
+
const normalizedHash = normalizeHashName(hash, HashContext.WebCrypto);
|
|
525
|
+
if (
|
|
526
|
+
![
|
|
527
|
+
'SHA-1',
|
|
528
|
+
'SHA-256',
|
|
529
|
+
'SHA-384',
|
|
530
|
+
'SHA-512',
|
|
531
|
+
'SHA3-256',
|
|
532
|
+
'SHA3-384',
|
|
533
|
+
'SHA3-512',
|
|
534
|
+
].includes(normalizedHash)
|
|
535
|
+
) {
|
|
536
|
+
throw lazyDOMException(
|
|
537
|
+
`Unsupported ${converterName}.hash`,
|
|
538
|
+
'NotSupportedError',
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
algorithm.hash = { name: normalizedHash };
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function validateAesLength(length: number | undefined): void {
|
|
545
|
+
if (
|
|
546
|
+
length !== undefined &&
|
|
547
|
+
length !== 128 &&
|
|
548
|
+
length !== 192 &&
|
|
549
|
+
length !== 256
|
|
550
|
+
) {
|
|
551
|
+
throw lazyDOMException('Invalid key length', 'OperationError');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function validateMacLength(
|
|
556
|
+
algorithm: NormalizedAlgorithmRecord,
|
|
557
|
+
key: string,
|
|
558
|
+
zeroError: 'DataError' | 'OperationError',
|
|
559
|
+
): void {
|
|
560
|
+
const length = validateUnsignedInteger(algorithm, key);
|
|
561
|
+
if (length === undefined) return;
|
|
562
|
+
if (length === 0) {
|
|
563
|
+
throw lazyDOMException(`${key} cannot be 0`, zeroError);
|
|
564
|
+
}
|
|
565
|
+
if (length % 8) {
|
|
566
|
+
throw lazyDOMException(`Unsupported ${key}`, 'NotSupportedError');
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function validateAeadParams(algorithm: NormalizedAlgorithmRecord): void {
|
|
571
|
+
const iv = validateBufferSource(algorithm, 'iv');
|
|
572
|
+
const tagLength = validateUnsignedInteger(algorithm, 'tagLength');
|
|
573
|
+
validateBufferSource(algorithm, 'additionalData');
|
|
574
|
+
|
|
575
|
+
switch (algorithm.name) {
|
|
576
|
+
case 'AES-GCM':
|
|
577
|
+
if (
|
|
578
|
+
tagLength !== undefined &&
|
|
579
|
+
![32, 64, 96, 104, 112, 120, 128].includes(tagLength)
|
|
580
|
+
) {
|
|
581
|
+
throw lazyDOMException(
|
|
582
|
+
`${tagLength} is not a valid AES-GCM tag length`,
|
|
583
|
+
'OperationError',
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
break;
|
|
587
|
+
case 'AES-OCB':
|
|
588
|
+
if (iv !== undefined && (iv.byteLength < 1 || iv.byteLength > 15)) {
|
|
589
|
+
throw lazyDOMException(
|
|
590
|
+
'AES-OCB algorithm.iv must be between 1 and 15 bytes',
|
|
591
|
+
'OperationError',
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
if (tagLength !== undefined && ![64, 96, 128].includes(tagLength)) {
|
|
595
|
+
throw lazyDOMException(
|
|
596
|
+
`${tagLength} is not a valid AES-OCB tag length`,
|
|
597
|
+
'OperationError',
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
break;
|
|
601
|
+
case 'ChaCha20-Poly1305':
|
|
602
|
+
validateByteLength(
|
|
603
|
+
iv,
|
|
604
|
+
'algorithm.iv',
|
|
605
|
+
12,
|
|
606
|
+
'ChaCha20-Poly1305 IV must be exactly 12 bytes',
|
|
607
|
+
);
|
|
608
|
+
if (tagLength !== undefined && tagLength !== 128) {
|
|
609
|
+
throw lazyDOMException(
|
|
610
|
+
`${tagLength} is not a valid ChaCha20-Poly1305 tag length`,
|
|
611
|
+
'OperationError',
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function validateNormalizedAlgorithm(
|
|
619
|
+
converterName: string,
|
|
620
|
+
algorithm: NormalizedAlgorithmRecord,
|
|
621
|
+
): void {
|
|
622
|
+
switch (converterName) {
|
|
623
|
+
case 'AesKeyGenParams':
|
|
624
|
+
case 'AesDerivedKeyParams':
|
|
625
|
+
validateAesLength(validateUnsignedInteger(algorithm, 'length'));
|
|
626
|
+
break;
|
|
627
|
+
case 'AesCbcParams':
|
|
628
|
+
validateByteLength(
|
|
629
|
+
validateBufferSource(algorithm, 'iv'),
|
|
630
|
+
'algorithm.iv',
|
|
631
|
+
16,
|
|
632
|
+
'algorithm.iv must contain exactly 16 bytes',
|
|
633
|
+
);
|
|
634
|
+
break;
|
|
635
|
+
case 'AesCtrParams': {
|
|
636
|
+
validateByteLength(
|
|
637
|
+
validateBufferSource(algorithm, 'counter'),
|
|
638
|
+
'algorithm.counter',
|
|
639
|
+
16,
|
|
640
|
+
);
|
|
641
|
+
const length = validateUnsignedInteger(algorithm, 'length');
|
|
642
|
+
if (length !== undefined && (length === 0 || length > 128)) {
|
|
643
|
+
throw lazyDOMException(
|
|
644
|
+
'AES-CTR algorithm.length must be between 1 and 128',
|
|
645
|
+
'OperationError',
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
case 'AeadParams':
|
|
651
|
+
validateAeadParams(algorithm);
|
|
652
|
+
break;
|
|
653
|
+
case 'EcdsaParams':
|
|
654
|
+
case 'HmacKeyGenParams':
|
|
655
|
+
case 'HmacImportParams':
|
|
656
|
+
case 'HkdfParams':
|
|
657
|
+
case 'Pbkdf2Params':
|
|
658
|
+
case 'RsaHashedKeyGenParams':
|
|
659
|
+
case 'RsaHashedImportParams':
|
|
660
|
+
validateHashAlgorithm(algorithm, converterName);
|
|
661
|
+
if (converterName === 'HmacKeyGenParams') {
|
|
662
|
+
validateMacLength(algorithm, 'length', 'OperationError');
|
|
663
|
+
}
|
|
664
|
+
if (converterName === 'HkdfParams') {
|
|
665
|
+
validateBinaryLike(algorithm, 'salt');
|
|
666
|
+
validateBinaryLike(algorithm, 'info');
|
|
667
|
+
} else if (converterName === 'Pbkdf2Params') {
|
|
668
|
+
const iterations = validateUnsignedInteger(algorithm, 'iterations');
|
|
669
|
+
if (iterations === 0) {
|
|
670
|
+
throw lazyDOMException('iterations cannot be zero', 'OperationError');
|
|
671
|
+
}
|
|
672
|
+
validateBinaryLike(algorithm, 'salt');
|
|
673
|
+
} else if (converterName === 'RsaHashedKeyGenParams') {
|
|
674
|
+
validateUnsignedInteger(algorithm, 'modulusLength');
|
|
675
|
+
validateBufferSource(algorithm, 'publicExponent');
|
|
676
|
+
}
|
|
677
|
+
break;
|
|
678
|
+
case 'RsaPssParams':
|
|
679
|
+
validateUnsignedInteger(algorithm, 'saltLength');
|
|
680
|
+
break;
|
|
681
|
+
case 'RsaOaepParams':
|
|
682
|
+
validateBufferSource(algorithm, 'label');
|
|
683
|
+
break;
|
|
684
|
+
case 'ContextParams':
|
|
685
|
+
validateBufferSource(algorithm, 'context');
|
|
686
|
+
break;
|
|
687
|
+
case 'EcdhKeyDeriveParams':
|
|
688
|
+
if (!(algorithm.public instanceof CryptoKey)) {
|
|
689
|
+
throw lazyDOMException(
|
|
690
|
+
'algorithm.public must be a public key',
|
|
691
|
+
'InvalidAccessError',
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
case 'Argon2Params': {
|
|
696
|
+
validateBufferSource(algorithm, 'nonce');
|
|
697
|
+
const parallelism = validateUnsignedInteger(algorithm, 'parallelism');
|
|
698
|
+
const memory = validateUnsignedInteger(algorithm, 'memory');
|
|
699
|
+
validateUnsignedInteger(algorithm, 'passes');
|
|
700
|
+
const version = validateUnsignedInteger(algorithm, 'version');
|
|
701
|
+
validateBufferSource(algorithm, 'secretValue');
|
|
702
|
+
validateBufferSource(algorithm, 'associatedData');
|
|
703
|
+
if (
|
|
704
|
+
parallelism !== undefined &&
|
|
705
|
+
(parallelism === 0 || parallelism > 2 ** 24 - 1)
|
|
706
|
+
) {
|
|
707
|
+
throw lazyDOMException(
|
|
708
|
+
'parallelism must be > 0 and < 16777215',
|
|
709
|
+
'OperationError',
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
if (
|
|
713
|
+
memory !== undefined &&
|
|
714
|
+
parallelism !== undefined &&
|
|
715
|
+
memory < 8 * parallelism
|
|
716
|
+
) {
|
|
717
|
+
throw lazyDOMException(
|
|
718
|
+
'memory must be at least 8 times the degree of parallelism',
|
|
719
|
+
'OperationError',
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
if (version !== undefined && version !== 0x13) {
|
|
723
|
+
throw lazyDOMException(
|
|
724
|
+
`${version} is not a valid Argon2 version`,
|
|
725
|
+
'OperationError',
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
case 'KmacKeyGenParams':
|
|
731
|
+
validateMacLength(algorithm, 'length', 'OperationError');
|
|
732
|
+
break;
|
|
733
|
+
case 'KmacImportParams':
|
|
734
|
+
validateMacLength(algorithm, 'length', 'DataError');
|
|
735
|
+
break;
|
|
736
|
+
case 'KmacParams':
|
|
737
|
+
validateMacLength(algorithm, 'outputLength', 'OperationError');
|
|
738
|
+
validateBufferSource(algorithm, 'customization');
|
|
739
|
+
break;
|
|
740
|
+
case 'CShakeParams':
|
|
741
|
+
case 'KangarooTwelveParams': {
|
|
742
|
+
const outputLength = validateUnsignedInteger(algorithm, 'outputLength');
|
|
743
|
+
if (
|
|
744
|
+
outputLength !== undefined &&
|
|
745
|
+
(outputLength === 0 || outputLength % 8)
|
|
746
|
+
) {
|
|
747
|
+
throw lazyDOMException(
|
|
748
|
+
`Invalid ${converterName} outputLength`,
|
|
749
|
+
'OperationError',
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
validateBufferSource(algorithm, 'functionName');
|
|
753
|
+
validateBufferSource(algorithm, 'customization');
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
case 'TurboShakeParams': {
|
|
757
|
+
const outputLength = validateUnsignedInteger(algorithm, 'outputLength');
|
|
758
|
+
const domainSeparation = validateUnsignedInteger(
|
|
759
|
+
algorithm,
|
|
760
|
+
'domainSeparation',
|
|
761
|
+
);
|
|
762
|
+
if (
|
|
763
|
+
outputLength !== undefined &&
|
|
764
|
+
(outputLength === 0 || outputLength % 8)
|
|
765
|
+
) {
|
|
766
|
+
throw lazyDOMException(
|
|
767
|
+
'Invalid TurboShakeParams outputLength',
|
|
768
|
+
'OperationError',
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
if (
|
|
772
|
+
domainSeparation !== undefined &&
|
|
773
|
+
(domainSeparation < 0x01 || domainSeparation > 0x7f)
|
|
774
|
+
) {
|
|
775
|
+
throw lazyDOMException(
|
|
776
|
+
'TurboShakeParams.domainSeparation must be in range 0x01-0x7f',
|
|
777
|
+
'OperationError',
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// WebCrypto §18.4.4 algorithm normalization. Mirrors Node's normalizeAlgorithm
|
|
786
|
+
// in lib/internal/crypto/util.js: canonicalizes `name`, looks up the
|
|
787
|
+
// (name, op) → converter mapping, and rejects inputs that omit required
|
|
788
|
+
// dictionary members. Callers in Subtle.supports rely on this throwing for
|
|
789
|
+
// invalid params — without it, supports() over-reports capability (#1025).
|
|
133
790
|
function normalizeAlgorithm(
|
|
134
791
|
algorithm: SubtleAlgorithm | AnyAlgorithm,
|
|
135
|
-
|
|
792
|
+
operation: Operation | string,
|
|
136
793
|
): SubtleAlgorithm {
|
|
137
|
-
const map = getCanonicalAlgorithmNames();
|
|
138
794
|
if (typeof algorithm === 'string') {
|
|
139
|
-
return { name:
|
|
795
|
+
return normalizeAlgorithm({ name: algorithm }, operation);
|
|
796
|
+
}
|
|
797
|
+
const name = (algorithm as { name?: unknown }).name;
|
|
798
|
+
if (typeof name !== 'string') {
|
|
799
|
+
throw new TypeError("Algorithm: 'name' is required");
|
|
800
|
+
}
|
|
801
|
+
const map = getCanonicalAlgorithmNames();
|
|
802
|
+
const canonical = map.get(name.toLowerCase());
|
|
803
|
+
if (canonical === undefined) {
|
|
804
|
+
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
|
805
|
+
}
|
|
806
|
+
const opMap = kAlgorithmDefinitions[canonical];
|
|
807
|
+
if (!opMap || !(operation in opMap)) {
|
|
808
|
+
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
|
140
809
|
}
|
|
141
|
-
|
|
142
|
-
|
|
810
|
+
const converterName = opMap[operation];
|
|
811
|
+
if (converterName == null) {
|
|
812
|
+
return { name: canonical };
|
|
813
|
+
}
|
|
814
|
+
const fields = kRequiredFields[converterName];
|
|
815
|
+
if (!fields) {
|
|
143
816
|
return { ...algorithm, name: canonical };
|
|
144
817
|
}
|
|
145
|
-
|
|
818
|
+
const out = { name: canonical } as NormalizedAlgorithmRecord;
|
|
819
|
+
const src = algorithm as Record<string, unknown>;
|
|
820
|
+
for (const field of fields) {
|
|
821
|
+
const value = src[field.key];
|
|
822
|
+
if (value === undefined) {
|
|
823
|
+
if (field.required) {
|
|
824
|
+
throw new TypeError(
|
|
825
|
+
`Failed to normalize algorithm: '${field.key}' is required in '${converterName}'`,
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
(out as Record<string, unknown>)[field.key] = value;
|
|
831
|
+
}
|
|
832
|
+
validateNormalizedAlgorithm(converterName, out);
|
|
833
|
+
return out;
|
|
146
834
|
}
|
|
147
835
|
|
|
148
836
|
function getAlgorithmName(name: string, length: number): string {
|
|
@@ -2884,10 +3572,7 @@ export class Subtle {
|
|
|
2884
3572
|
data: BufferLike,
|
|
2885
3573
|
): Promise<ArrayBuffer> {
|
|
2886
3574
|
requireArgs(arguments.length, 2, 'digest');
|
|
2887
|
-
const normalizedAlgorithm = normalizeAlgorithm(
|
|
2888
|
-
algorithm,
|
|
2889
|
-
'digest' as Operation,
|
|
2890
|
-
);
|
|
3575
|
+
const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'digest');
|
|
2891
3576
|
return asyncDigest(normalizedAlgorithm, data);
|
|
2892
3577
|
}
|
|
2893
3578
|
|
|
@@ -2897,7 +3582,6 @@ export class Subtle {
|
|
|
2897
3582
|
length: number | null = null,
|
|
2898
3583
|
): Promise<ArrayBuffer> {
|
|
2899
3584
|
requireArgs(arguments.length, 2, 'deriveBits');
|
|
2900
|
-
const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'deriveBits');
|
|
2901
3585
|
// WebCrypto §SubtleCrypto.deriveBits step 11: throw InvalidAccessError
|
|
2902
3586
|
// unless `baseKey.[[usages]]` contains "deriveBits" specifically. The
|
|
2903
3587
|
// previous `deriveBits || deriveKey` accept-either branch silently
|
|
@@ -2909,6 +3593,7 @@ export class Subtle {
|
|
|
2909
3593
|
'InvalidAccessError',
|
|
2910
3594
|
);
|
|
2911
3595
|
}
|
|
3596
|
+
const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'deriveBits');
|
|
2912
3597
|
if (baseKey.algorithm.name !== normalizedAlgorithm.name) {
|
|
2913
3598
|
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
|
|
2914
3599
|
}
|
|
@@ -2956,10 +3641,9 @@ export class Subtle {
|
|
|
2956
3641
|
): Promise<CryptoKey> {
|
|
2957
3642
|
requireArgs(arguments.length, 5, 'deriveKey');
|
|
2958
3643
|
const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'deriveBits');
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
);
|
|
3644
|
+
// Validate the derived-key algorithm up front (mirrors Node webcrypto.js:341).
|
|
3645
|
+
// The normalized form is unused — `this.importKey` re-normalizes below.
|
|
3646
|
+
normalizeAlgorithm(derivedKeyAlgorithm, 'importKey');
|
|
2963
3647
|
|
|
2964
3648
|
// Validate baseKey usage
|
|
2965
3649
|
if (!baseKey.usages.includes('deriveKey')) {
|
|
@@ -2974,7 +3658,12 @@ export class Subtle {
|
|
|
2974
3658
|
}
|
|
2975
3659
|
|
|
2976
3660
|
// Calculate required key length (may be null for KDF-derived material).
|
|
2977
|
-
|
|
3661
|
+
// Mirrors Node webcrypto.js:350 — uses the raw derivedKeyAlgorithm with
|
|
3662
|
+
// op='get key length' so AES `length` survives normalization (the
|
|
3663
|
+
// 'importKey' converter for AES is null and strips dictionary members).
|
|
3664
|
+
const length = getKeyLength(
|
|
3665
|
+
normalizeAlgorithm(derivedKeyAlgorithm, 'get key length'),
|
|
3666
|
+
);
|
|
2978
3667
|
|
|
2979
3668
|
// Step 1: Derive bits
|
|
2980
3669
|
let derivedBits: ArrayBuffer;
|
|
@@ -3114,10 +3803,21 @@ export class Subtle {
|
|
|
3114
3803
|
wrapAlgorithm: EncryptDecryptParams,
|
|
3115
3804
|
): Promise<ArrayBuffer> {
|
|
3116
3805
|
requireArgs(arguments.length, 4, 'wrapKey');
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3806
|
+
// Mirrors Node webcrypto.js:923-927: prefer the 'wrapKey' op (only
|
|
3807
|
+
// AES-KW defines it) and fall back to 'encrypt' for cipher-based wrap
|
|
3808
|
+
// algorithms like AES-GCM and RSA-OAEP.
|
|
3809
|
+
let normalizedWrapAlgorithm: EncryptDecryptParams;
|
|
3810
|
+
try {
|
|
3811
|
+
normalizedWrapAlgorithm = normalizeAlgorithm(
|
|
3812
|
+
wrapAlgorithm,
|
|
3813
|
+
'wrapKey',
|
|
3814
|
+
) as EncryptDecryptParams;
|
|
3815
|
+
} catch {
|
|
3816
|
+
normalizedWrapAlgorithm = normalizeAlgorithm(
|
|
3817
|
+
wrapAlgorithm,
|
|
3818
|
+
'encrypt',
|
|
3819
|
+
) as EncryptDecryptParams;
|
|
3820
|
+
}
|
|
3121
3821
|
|
|
3122
3822
|
if (normalizedWrapAlgorithm.name !== wrappingKey.algorithm.name) {
|
|
3123
3823
|
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
|
|
@@ -3174,10 +3874,20 @@ export class Subtle {
|
|
|
3174
3874
|
keyUsages: KeyUsage[],
|
|
3175
3875
|
): Promise<CryptoKey> {
|
|
3176
3876
|
requireArgs(arguments.length, 7, 'unwrapKey');
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3877
|
+
// Mirrors Node webcrypto.js:1006-1010: prefer 'unwrapKey', fall back to
|
|
3878
|
+
// 'decrypt' for cipher-based unwrap algorithms.
|
|
3879
|
+
let normalizedUnwrapAlgorithm: EncryptDecryptParams;
|
|
3880
|
+
try {
|
|
3881
|
+
normalizedUnwrapAlgorithm = normalizeAlgorithm(
|
|
3882
|
+
unwrapAlgorithm,
|
|
3883
|
+
'unwrapKey',
|
|
3884
|
+
) as EncryptDecryptParams;
|
|
3885
|
+
} catch {
|
|
3886
|
+
normalizedUnwrapAlgorithm = normalizeAlgorithm(
|
|
3887
|
+
unwrapAlgorithm,
|
|
3888
|
+
'decrypt',
|
|
3889
|
+
) as EncryptDecryptParams;
|
|
3890
|
+
}
|
|
3181
3891
|
|
|
3182
3892
|
if (normalizedUnwrapAlgorithm.name !== unwrappingKey.algorithm.name) {
|
|
3183
3893
|
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
|