• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayPrototypeIncludes,
5  JSONParse,
6  JSONStringify,
7  ObjectDefineProperties,
8  ReflectApply,
9  ReflectConstruct,
10  SafeSet,
11  StringPrototypeRepeat,
12  SymbolToStringTag,
13} = primordials;
14
15const {
16  kWebCryptoKeyFormatRaw,
17  kWebCryptoKeyFormatPKCS8,
18  kWebCryptoKeyFormatSPKI,
19  kWebCryptoCipherEncrypt,
20  kWebCryptoCipherDecrypt,
21} = internalBinding('crypto');
22
23const { TextDecoder, TextEncoder } = require('internal/encoding');
24
25const {
26  codes: {
27    ERR_ILLEGAL_CONSTRUCTOR,
28    ERR_INVALID_THIS,
29  },
30} = require('internal/errors');
31
32const {
33  CryptoKey,
34  InternalCryptoKey,
35  createSecretKey,
36} = require('internal/crypto/keys');
37
38const {
39  asyncDigest,
40} = require('internal/crypto/hash');
41
42const {
43  getBlockSize,
44  hasAnyNotIn,
45  normalizeAlgorithm,
46  normalizeHashName,
47  validateMaxBufferLength,
48  kHandle,
49  kKeyObject,
50} = require('internal/crypto/util');
51
52const {
53  kEnumerableProperty,
54  lazyDOMException,
55} = require('internal/util');
56
57const {
58  getRandomValues: _getRandomValues,
59  randomUUID: _randomUUID,
60} = require('internal/crypto/random');
61
62let webidl;
63
64async function digest(algorithm, data) {
65  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
66
67  webidl ??= require('internal/crypto/webidl');
68  const prefix = "Failed to execute 'digest' on 'SubtleCrypto'";
69  webidl.requiredArguments(arguments.length, 2, { prefix });
70  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
71    prefix,
72    context: '1st argument',
73  });
74  data = webidl.converters.BufferSource(data, {
75    prefix,
76    context: '2nd argument',
77  });
78
79  algorithm = normalizeAlgorithm(algorithm, 'digest');
80
81  return ReflectApply(asyncDigest, this, [algorithm, data]);
82}
83
84function randomUUID() {
85  if (this !== crypto) throw new ERR_INVALID_THIS('Crypto');
86  return _randomUUID();
87}
88
89async function generateKey(
90  algorithm,
91  extractable,
92  keyUsages) {
93  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
94
95  webidl ??= require('internal/crypto/webidl');
96  const prefix = "Failed to execute 'generateKey' on 'SubtleCrypto'";
97  webidl.requiredArguments(arguments.length, 3, { prefix });
98  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
99    prefix,
100    context: '1st argument',
101  });
102  extractable = webidl.converters.boolean(extractable, {
103    prefix,
104    context: '2nd argument',
105  });
106  keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
107    prefix,
108    context: '3rd argument',
109  });
110
111  algorithm = normalizeAlgorithm(algorithm, 'generateKey');
112  let result;
113  let resultType;
114  switch (algorithm.name) {
115    case 'RSASSA-PKCS1-v1_5':
116      // Fall through
117    case 'RSA-PSS':
118      // Fall through
119    case 'RSA-OAEP':
120      resultType = 'CryptoKeyPair';
121      result = await require('internal/crypto/rsa')
122        .rsaKeyGenerate(algorithm, extractable, keyUsages);
123      break;
124    case 'Ed25519':
125      // Fall through
126    case 'Ed448':
127      // Fall through
128    case 'X25519':
129      // Fall through
130    case 'X448':
131      resultType = 'CryptoKeyPair';
132      result = await require('internal/crypto/cfrg')
133        .cfrgGenerateKey(algorithm, extractable, keyUsages);
134      break;
135    case 'ECDSA':
136      // Fall through
137    case 'ECDH':
138      resultType = 'CryptoKeyPair';
139      result = await require('internal/crypto/ec')
140        .ecGenerateKey(algorithm, extractable, keyUsages);
141      break;
142    case 'HMAC':
143      resultType = 'CryptoKey';
144      result = await require('internal/crypto/mac')
145        .hmacGenerateKey(algorithm, extractable, keyUsages);
146      break;
147    case 'AES-CTR':
148      // Fall through
149    case 'AES-CBC':
150      // Fall through
151    case 'AES-GCM':
152      // Fall through
153    case 'AES-KW':
154      resultType = 'CryptoKey';
155      result = await require('internal/crypto/aes')
156        .aesGenerateKey(algorithm, extractable, keyUsages);
157      break;
158    default:
159      throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
160  }
161
162  if (
163    (resultType === 'CryptoKey' &&
164      (result.type === 'secret' || result.type === 'private') &&
165      result.usages.length === 0) ||
166    (resultType === 'CryptoKeyPair' && result.privateKey.usages.length === 0)
167  ) {
168    throw lazyDOMException(
169      'Usages cannot be empty when creating a key.',
170      'SyntaxError');
171  }
172
173  return result;
174}
175
176async function deriveBits(algorithm, baseKey, length) {
177  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
178
179  webidl ??= require('internal/crypto/webidl');
180  const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'";
181  webidl.requiredArguments(arguments.length, 3, { prefix });
182  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
183    prefix,
184    context: '1st argument',
185  });
186  baseKey = webidl.converters.CryptoKey(baseKey, {
187    prefix,
188    context: '2nd argument',
189  });
190  if (length !== null) {
191    length = webidl.converters['unsigned long'](length, {
192      prefix,
193      context: '3rd argument',
194    });
195  }
196
197  algorithm = normalizeAlgorithm(algorithm, 'deriveBits');
198  if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveBits')) {
199    throw lazyDOMException(
200      'baseKey does not have deriveBits usage',
201      'InvalidAccessError');
202  }
203  if (baseKey.algorithm.name !== algorithm.name)
204    throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
205  switch (algorithm.name) {
206    case 'X25519':
207      // Fall through
208    case 'X448':
209      // Fall through
210    case 'ECDH':
211      return require('internal/crypto/diffiehellman')
212        .ecdhDeriveBits(algorithm, baseKey, length);
213    case 'HKDF':
214      return require('internal/crypto/hkdf')
215        .hkdfDeriveBits(algorithm, baseKey, length);
216    case 'PBKDF2':
217      return require('internal/crypto/pbkdf2')
218        .pbkdf2DeriveBits(algorithm, baseKey, length);
219  }
220  throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
221}
222
223function getKeyLength({ name, length, hash }) {
224  switch (name) {
225    case 'AES-CTR':
226    case 'AES-CBC':
227    case 'AES-GCM':
228    case 'AES-KW':
229      if (length !== 128 && length !== 192 && length !== 256)
230        throw lazyDOMException('Invalid key length', 'OperationError');
231
232      return length;
233    case 'HMAC':
234      if (length === undefined) {
235        return getBlockSize(hash?.name);
236      }
237
238      if (typeof length === 'number' && length !== 0) {
239        return length;
240      }
241
242      throw lazyDOMException('Invalid key length', 'OperationError');
243    case 'HKDF':
244    case 'PBKDF2':
245      return null;
246  }
247}
248
249async function deriveKey(
250  algorithm,
251  baseKey,
252  derivedKeyAlgorithm,
253  extractable,
254  keyUsages) {
255  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
256
257  webidl ??= require('internal/crypto/webidl');
258  const prefix = "Failed to execute 'deriveKey' on 'SubtleCrypto'";
259  webidl.requiredArguments(arguments.length, 5, { prefix });
260  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
261    prefix,
262    context: '1st argument',
263  });
264  baseKey = webidl.converters.CryptoKey(baseKey, {
265    prefix,
266    context: '2nd argument',
267  });
268  derivedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(derivedKeyAlgorithm, {
269    prefix,
270    context: '3rd argument',
271  });
272  extractable = webidl.converters.boolean(extractable, {
273    prefix,
274    context: '4th argument',
275  });
276  keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
277    prefix,
278    context: '5th argument',
279  });
280
281  algorithm = normalizeAlgorithm(algorithm, 'deriveBits');
282  derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm, 'importKey');
283  if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveKey')) {
284    throw lazyDOMException(
285      'baseKey does not have deriveKey usage',
286      'InvalidAccessError');
287  }
288  if (baseKey.algorithm.name !== algorithm.name)
289    throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
290
291  const length = getKeyLength(normalizeAlgorithm(arguments[2], 'get key length'));
292  let bits;
293  switch (algorithm.name) {
294    case 'X25519':
295      // Fall through
296    case 'X448':
297      // Fall through
298    case 'ECDH':
299      bits = await require('internal/crypto/diffiehellman')
300        .ecdhDeriveBits(algorithm, baseKey, length);
301      break;
302    case 'HKDF':
303      bits = await require('internal/crypto/hkdf')
304        .hkdfDeriveBits(algorithm, baseKey, length);
305      break;
306    case 'PBKDF2':
307      bits = await require('internal/crypto/pbkdf2')
308        .pbkdf2DeriveBits(algorithm, baseKey, length);
309      break;
310    default:
311      throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
312  }
313
314  return ReflectApply(
315    importKey,
316    this,
317    ['raw', bits, derivedKeyAlgorithm, extractable, keyUsages],
318  );
319}
320
321async function exportKeySpki(key) {
322  switch (key.algorithm.name) {
323    case 'RSASSA-PKCS1-v1_5':
324      // Fall through
325    case 'RSA-PSS':
326      // Fall through
327    case 'RSA-OAEP':
328      if (key.type === 'public') {
329        return require('internal/crypto/rsa')
330          .rsaExportKey(key, kWebCryptoKeyFormatSPKI);
331      }
332      break;
333    case 'ECDSA':
334      // Fall through
335    case 'ECDH':
336      if (key.type === 'public') {
337        return require('internal/crypto/ec')
338          .ecExportKey(key, kWebCryptoKeyFormatSPKI);
339      }
340      break;
341    case 'Ed25519':
342      // Fall through
343    case 'Ed448':
344      // Fall through
345    case 'X25519':
346      // Fall through
347    case 'X448':
348      if (key.type === 'public') {
349        return require('internal/crypto/cfrg')
350          .cfrgExportKey(key, kWebCryptoKeyFormatSPKI);
351      }
352      break;
353  }
354
355  throw lazyDOMException(
356    `Unable to export a raw ${key.algorithm.name} ${key.type} key`,
357    'InvalidAccessError');
358}
359
360async function exportKeyPkcs8(key) {
361  switch (key.algorithm.name) {
362    case 'RSASSA-PKCS1-v1_5':
363      // Fall through
364    case 'RSA-PSS':
365      // Fall through
366    case 'RSA-OAEP':
367      if (key.type === 'private') {
368        return require('internal/crypto/rsa')
369          .rsaExportKey(key, kWebCryptoKeyFormatPKCS8);
370      }
371      break;
372    case 'ECDSA':
373      // Fall through
374    case 'ECDH':
375      if (key.type === 'private') {
376        return require('internal/crypto/ec')
377          .ecExportKey(key, kWebCryptoKeyFormatPKCS8);
378      }
379      break;
380    case 'Ed25519':
381      // Fall through
382    case 'Ed448':
383      // Fall through
384    case 'X25519':
385      // Fall through
386    case 'X448':
387      if (key.type === 'private') {
388        return require('internal/crypto/cfrg')
389          .cfrgExportKey(key, kWebCryptoKeyFormatPKCS8);
390      }
391      break;
392  }
393
394  throw lazyDOMException(
395    `Unable to export a pkcs8 ${key.algorithm.name} ${key.type} key`,
396    'InvalidAccessError');
397}
398
399async function exportKeyRaw(key) {
400  switch (key.algorithm.name) {
401    case 'ECDSA':
402      // Fall through
403    case 'ECDH':
404      if (key.type === 'public') {
405        return require('internal/crypto/ec')
406          .ecExportKey(key, kWebCryptoKeyFormatRaw);
407      }
408      break;
409    case 'Ed25519':
410      // Fall through
411    case 'Ed448':
412      // Fall through
413    case 'X25519':
414      // Fall through
415    case 'X448':
416      if (key.type === 'public') {
417        return require('internal/crypto/cfrg')
418          .cfrgExportKey(key, kWebCryptoKeyFormatRaw);
419      }
420      break;
421    case 'AES-CTR':
422      // Fall through
423    case 'AES-CBC':
424      // Fall through
425    case 'AES-GCM':
426      // Fall through
427    case 'AES-KW':
428      // Fall through
429    case 'HMAC':
430      return key[kKeyObject].export().buffer;
431  }
432
433  throw lazyDOMException(
434    `Unable to export a raw ${key.algorithm.name} ${key.type} key`,
435    'InvalidAccessError');
436}
437
438async function exportKeyJWK(key) {
439  const jwk = key[kKeyObject][kHandle].exportJwk({
440    key_ops: key.usages,
441    ext: key.extractable,
442  }, true);
443  switch (key.algorithm.name) {
444    case 'RSASSA-PKCS1-v1_5':
445      jwk.alg = normalizeHashName(
446        key.algorithm.hash.name,
447        normalizeHashName.kContextJwkRsa);
448      return jwk;
449    case 'RSA-PSS':
450      jwk.alg = normalizeHashName(
451        key.algorithm.hash.name,
452        normalizeHashName.kContextJwkRsaPss);
453      return jwk;
454    case 'RSA-OAEP':
455      jwk.alg = normalizeHashName(
456        key.algorithm.hash.name,
457        normalizeHashName.kContextJwkRsaOaep);
458      return jwk;
459    case 'ECDSA':
460      // Fall through
461    case 'ECDH':
462      jwk.crv ||= key.algorithm.namedCurve;
463      return jwk;
464    case 'X25519':
465      // Fall through
466    case 'X448':
467      jwk.crv ||= key.algorithm.name;
468      return jwk;
469    case 'Ed25519':
470      // Fall through
471    case 'Ed448':
472      jwk.crv ||= key.algorithm.name;
473      jwk.alg = 'EdDSA';
474      return jwk;
475    case 'AES-CTR':
476      // Fall through
477    case 'AES-CBC':
478      // Fall through
479    case 'AES-GCM':
480      // Fall through
481    case 'AES-KW':
482      jwk.alg = require('internal/crypto/aes')
483        .getAlgorithmName(key.algorithm.name, key.algorithm.length);
484      return jwk;
485    case 'HMAC':
486      jwk.alg = normalizeHashName(
487        key.algorithm.hash.name,
488        normalizeHashName.kContextJwkHmac);
489      return jwk;
490    default:
491      // Fall through
492  }
493
494  throw lazyDOMException('Not yet supported', 'NotSupportedError');
495}
496
497async function exportKey(format, key) {
498  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
499
500  webidl ??= require('internal/crypto/webidl');
501  const prefix = "Failed to execute 'exportKey' on 'SubtleCrypto'";
502  webidl.requiredArguments(arguments.length, 2, { prefix });
503  format = webidl.converters.KeyFormat(format, {
504    prefix,
505    context: '1st argument',
506  });
507  key = webidl.converters.CryptoKey(key, {
508    prefix,
509    context: '2nd argument',
510  });
511
512  if (!key.extractable)
513    throw lazyDOMException('key is not extractable', 'InvalidAccessException');
514
515  switch (format) {
516    case 'spki': return exportKeySpki(key);
517    case 'pkcs8': return exportKeyPkcs8(key);
518    case 'jwk': return exportKeyJWK(key);
519    case 'raw': return exportKeyRaw(key);
520  }
521  throw lazyDOMException(
522    'Export format is unsupported', 'NotSupportedError');
523}
524
525async function importGenericSecretKey(
526  { name, length },
527  format,
528  keyData,
529  extractable,
530  keyUsages) {
531  const usagesSet = new SafeSet(keyUsages);
532  if (extractable)
533    throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');
534
535  if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
536    throw lazyDOMException(
537      `Unsupported key usage for a ${name} key`,
538      'SyntaxError');
539  }
540
541  switch (format) {
542    case 'raw': {
543      if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) {
544        throw lazyDOMException(
545          `Unsupported key usage for a ${name} key`,
546          'SyntaxError');
547      }
548
549      const checkLength = keyData.byteLength * 8;
550
551      // The Web Crypto spec allows for key lengths that are not multiples of
552      // 8. We don't. Our check here is stricter than that defined by the spec
553      // in that we require that algorithm.length match keyData.length * 8 if
554      // algorithm.length is specified.
555      if (length !== undefined && length !== checkLength) {
556        throw lazyDOMException('Invalid key length', 'DataError');
557      }
558
559      const keyObject = createSecretKey(keyData);
560      return new InternalCryptoKey(keyObject, { name }, keyUsages, false);
561    }
562  }
563
564  throw lazyDOMException(
565    `Unable to import ${name} key with format ${format}`,
566    'NotSupportedError');
567}
568
569async function importKey(
570  format,
571  keyData,
572  algorithm,
573  extractable,
574  keyUsages) {
575  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
576
577  webidl ??= require('internal/crypto/webidl');
578  const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'";
579  webidl.requiredArguments(arguments.length, 4, { prefix });
580  format = webidl.converters.KeyFormat(format, {
581    prefix,
582    context: '1st argument',
583  });
584  const type = format === 'jwk' ? 'JsonWebKey' : 'BufferSource';
585  keyData = webidl.converters[type](keyData, {
586    prefix,
587    context: '2nd argument',
588  });
589  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
590    prefix,
591    context: '3rd argument',
592  });
593  extractable = webidl.converters.boolean(extractable, {
594    prefix,
595    context: '4th argument',
596  });
597  keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
598    prefix,
599    context: '5th argument',
600  });
601
602  algorithm = normalizeAlgorithm(algorithm, 'importKey');
603  let result;
604  switch (algorithm.name) {
605    case 'RSASSA-PKCS1-v1_5':
606      // Fall through
607    case 'RSA-PSS':
608      // Fall through
609    case 'RSA-OAEP':
610      result = await require('internal/crypto/rsa')
611        .rsaImportKey(format, keyData, algorithm, extractable, keyUsages);
612      break;
613    case 'ECDSA':
614      // Fall through
615    case 'ECDH':
616      result = await require('internal/crypto/ec')
617        .ecImportKey(format, keyData, algorithm, extractable, keyUsages);
618      break;
619    case 'Ed25519':
620      // Fall through
621    case 'Ed448':
622      // Fall through
623    case 'X25519':
624      // Fall through
625    case 'X448':
626      result = await require('internal/crypto/cfrg')
627        .cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
628      break;
629    case 'HMAC':
630      result = await require('internal/crypto/mac')
631        .hmacImportKey(format, keyData, algorithm, extractable, keyUsages);
632      break;
633    case 'AES-CTR':
634      // Fall through
635    case 'AES-CBC':
636      // Fall through
637    case 'AES-GCM':
638      // Fall through
639    case 'AES-KW':
640      result = await require('internal/crypto/aes')
641        .aesImportKey(algorithm, format, keyData, extractable, keyUsages);
642      break;
643    case 'HKDF':
644      // Fall through
645    case 'PBKDF2':
646      result = await importGenericSecretKey(
647        algorithm,
648        format,
649        keyData,
650        extractable,
651        keyUsages);
652      break;
653    default:
654      throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
655  }
656
657  if ((result.type === 'secret' || result.type === 'private') && result.usages.length === 0) {
658    throw lazyDOMException(
659      `Usages cannot be empty when importing a ${result.type} key.`,
660      'SyntaxError');
661  }
662
663  return result;
664}
665
666// subtle.wrapKey() is essentially a subtle.exportKey() followed
667// by a subtle.encrypt().
668async function wrapKey(format, key, wrappingKey, algorithm) {
669  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
670
671  webidl ??= require('internal/crypto/webidl');
672  const prefix = "Failed to execute 'wrapKey' on 'SubtleCrypto'";
673  webidl.requiredArguments(arguments.length, 4, { prefix });
674  format = webidl.converters.KeyFormat(format, {
675    prefix,
676    context: '1st argument',
677  });
678  key = webidl.converters.CryptoKey(key, {
679    prefix,
680    context: '2nd argument',
681  });
682  wrappingKey = webidl.converters.CryptoKey(wrappingKey, {
683    prefix,
684    context: '3rd argument',
685  });
686  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
687    prefix,
688    context: '4th argument',
689  });
690
691  try {
692    algorithm = normalizeAlgorithm(algorithm, 'wrapKey');
693  } catch {
694    algorithm = normalizeAlgorithm(algorithm, 'encrypt');
695  }
696  let keyData = await ReflectApply(exportKey, this, [format, key]);
697
698  if (format === 'jwk') {
699    const ec = new TextEncoder();
700    const raw = JSONStringify(keyData);
701    // As per the NOTE in step 13 https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey
702    // we're padding AES-KW wrapped JWK to make sure it is always a multiple of 8 bytes
703    // in length
704    if (algorithm.name === 'AES-KW' && raw.length % 8 !== 0) {
705      keyData = ec.encode(raw + StringPrototypeRepeat(' ', 8 - (raw.length % 8)));
706    } else {
707      keyData = ec.encode(raw);
708    }
709  }
710
711  return cipherOrWrap(
712    kWebCryptoCipherEncrypt,
713    algorithm,
714    wrappingKey,
715    keyData,
716    'wrapKey');
717}
718
719// subtle.unwrapKey() is essentially a subtle.decrypt() followed
720// by a subtle.importKey().
721async function unwrapKey(
722  format,
723  wrappedKey,
724  unwrappingKey,
725  unwrapAlgo,
726  unwrappedKeyAlgo,
727  extractable,
728  keyUsages) {
729  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
730
731  webidl ??= require('internal/crypto/webidl');
732  const prefix = "Failed to execute 'unwrapKey' on 'SubtleCrypto'";
733  webidl.requiredArguments(arguments.length, 7, { prefix });
734  format = webidl.converters.KeyFormat(format, {
735    prefix,
736    context: '1st argument',
737  });
738  wrappedKey = webidl.converters.BufferSource(wrappedKey, {
739    prefix,
740    context: '2nd argument',
741  });
742  unwrappingKey = webidl.converters.CryptoKey(unwrappingKey, {
743    prefix,
744    context: '3rd argument',
745  });
746  unwrapAlgo = webidl.converters.AlgorithmIdentifier(unwrapAlgo, {
747    prefix,
748    context: '4th argument',
749  });
750  unwrappedKeyAlgo = webidl.converters.AlgorithmIdentifier(
751    unwrappedKeyAlgo,
752    {
753      prefix,
754      context: '5th argument',
755    },
756  );
757  extractable = webidl.converters.boolean(extractable, {
758    prefix,
759    context: '6th argument',
760  });
761  keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
762    prefix,
763    context: '7th argument',
764  });
765
766  try {
767    unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'unwrapKey');
768  } catch {
769    unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'decrypt');
770  }
771
772  let keyData = await cipherOrWrap(
773    kWebCryptoCipherDecrypt,
774    unwrapAlgo,
775    unwrappingKey,
776    wrappedKey,
777    'unwrapKey');
778
779  if (format === 'jwk') {
780    // The fatal: true option is only supported in builds that have ICU.
781    const options = process.versions.icu !== undefined ?
782      { fatal: true } : undefined;
783    const dec = new TextDecoder('utf-8', options);
784    try {
785      keyData = JSONParse(dec.decode(keyData));
786    } catch {
787      throw lazyDOMException('Invalid wrapped JWK key', 'DataError');
788    }
789  }
790
791  return ReflectApply(
792    importKey,
793    this,
794    [format, keyData, unwrappedKeyAlgo, extractable, keyUsages],
795  );
796}
797
798function signVerify(algorithm, key, data, signature) {
799  let usage = 'sign';
800  if (signature !== undefined) {
801    usage = 'verify';
802  }
803  algorithm = normalizeAlgorithm(algorithm, usage);
804
805  if (!ArrayPrototypeIncludes(key.usages, usage) ||
806      algorithm.name !== key.algorithm.name) {
807    throw lazyDOMException(
808      `Unable to use this key to ${usage}`,
809      'InvalidAccessError');
810  }
811
812  switch (algorithm.name) {
813    case 'RSA-PSS':
814      // Fall through
815    case 'RSASSA-PKCS1-v1_5':
816      return require('internal/crypto/rsa')
817        .rsaSignVerify(key, data, algorithm, signature);
818    case 'ECDSA':
819      return require('internal/crypto/ec')
820        .ecdsaSignVerify(key, data, algorithm, signature);
821    case 'Ed25519':
822      // Fall through
823    case 'Ed448':
824      // Fall through
825      return require('internal/crypto/cfrg')
826        .eddsaSignVerify(key, data, algorithm, signature);
827    case 'HMAC':
828      return require('internal/crypto/mac')
829        .hmacSignVerify(key, data, algorithm, signature);
830  }
831  throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
832}
833
834async function sign(algorithm, key, data) {
835  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
836
837  webidl ??= require('internal/crypto/webidl');
838  const prefix = "Failed to execute 'sign' on 'SubtleCrypto'";
839  webidl.requiredArguments(arguments.length, 3, { prefix });
840  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
841    prefix,
842    context: '1st argument',
843  });
844  key = webidl.converters.CryptoKey(key, {
845    prefix,
846    context: '2nd argument',
847  });
848  data = webidl.converters.BufferSource(data, {
849    prefix,
850    context: '3rd argument',
851  });
852
853  return signVerify(algorithm, key, data);
854}
855
856async function verify(algorithm, key, signature, data) {
857  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
858
859  webidl ??= require('internal/crypto/webidl');
860  const prefix = "Failed to execute 'verify' on 'SubtleCrypto'";
861  webidl.requiredArguments(arguments.length, 4, { prefix });
862  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
863    prefix,
864    context: '1st argument',
865  });
866  key = webidl.converters.CryptoKey(key, {
867    prefix,
868    context: '2nd argument',
869  });
870  signature = webidl.converters.BufferSource(signature, {
871    prefix,
872    context: '3rd argument',
873  });
874  data = webidl.converters.BufferSource(data, {
875    prefix,
876    context: '4th argument',
877  });
878
879  return signVerify(algorithm, key, data, signature);
880}
881
882async function cipherOrWrap(mode, algorithm, key, data, op) {
883  // We use a Node.js style error here instead of a DOMException because
884  // the WebCrypto spec is not specific what kind of error is to be thrown
885  // in this case. Both Firefox and Chrome throw simple TypeErrors here.
886  // The key algorithm and cipher algorithm must match, and the
887  // key must have the proper usage.
888  if (key.algorithm.name !== algorithm.name ||
889      !ArrayPrototypeIncludes(key.usages, op)) {
890    throw lazyDOMException(
891      'The requested operation is not valid for the provided key',
892      'InvalidAccessError');
893  }
894
895  // While WebCrypto allows for larger input buffer sizes, we limit
896  // those to sizes that can fit within uint32_t because of limitations
897  // in the OpenSSL API.
898  validateMaxBufferLength(data, 'data');
899
900  switch (algorithm.name) {
901    case 'RSA-OAEP':
902      return require('internal/crypto/rsa')
903        .rsaCipher(mode, key, data, algorithm);
904    case 'AES-CTR':
905      // Fall through
906    case 'AES-CBC':
907      // Fall through
908    case 'AES-GCM':
909      return require('internal/crypto/aes')
910        .aesCipher(mode, key, data, algorithm);
911    case 'AES-KW':
912      if (op === 'wrapKey' || op === 'unwrapKey') {
913        return require('internal/crypto/aes')
914          .aesCipher(mode, key, data, algorithm);
915      }
916  }
917  throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
918}
919
920async function encrypt(algorithm, key, data) {
921  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
922
923  webidl ??= require('internal/crypto/webidl');
924  const prefix = "Failed to execute 'encrypt' on 'SubtleCrypto'";
925  webidl.requiredArguments(arguments.length, 3, { prefix });
926  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
927    prefix,
928    context: '1st argument',
929  });
930  key = webidl.converters.CryptoKey(key, {
931    prefix,
932    context: '2nd argument',
933  });
934  data = webidl.converters.BufferSource(data, {
935    prefix,
936    context: '3rd argument',
937  });
938
939  algorithm = normalizeAlgorithm(algorithm, 'encrypt');
940  return cipherOrWrap(kWebCryptoCipherEncrypt, algorithm, key, data, 'encrypt');
941}
942
943async function decrypt(algorithm, key, data) {
944  if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
945
946  webidl ??= require('internal/crypto/webidl');
947  const prefix = "Failed to execute 'decrypt' on 'SubtleCrypto'";
948  webidl.requiredArguments(arguments.length, 3, { prefix });
949  algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
950    prefix,
951    context: '1st argument',
952  });
953  key = webidl.converters.CryptoKey(key, {
954    prefix,
955    context: '2nd argument',
956  });
957  data = webidl.converters.BufferSource(data, {
958    prefix,
959    context: '3rd argument',
960  });
961
962  algorithm = normalizeAlgorithm(algorithm, 'decrypt');
963  return cipherOrWrap(kWebCryptoCipherDecrypt, algorithm, key, data, 'decrypt');
964}
965
966// The SubtleCrypto and Crypto classes are defined as part of the
967// Web Crypto API standard: https://www.w3.org/TR/WebCryptoAPI/
968
969class SubtleCrypto {
970  constructor() {
971    throw new ERR_ILLEGAL_CONSTRUCTOR();
972  }
973}
974const subtle = ReflectConstruct(function() {}, [], SubtleCrypto);
975
976class Crypto {
977  constructor() {
978    throw new ERR_ILLEGAL_CONSTRUCTOR();
979  }
980
981  get subtle() {
982    if (this !== crypto) throw new ERR_INVALID_THIS('Crypto');
983    return subtle;
984  }
985}
986const crypto = ReflectConstruct(function() {}, [], Crypto);
987
988function getRandomValues(array) {
989  if (this !== crypto) throw new ERR_INVALID_THIS('Crypto');
990
991  webidl ??= require('internal/crypto/webidl');
992  const prefix = "Failed to execute 'getRandomValues' on 'Crypto'";
993  webidl.requiredArguments(arguments.length, 1, { prefix });
994
995  return ReflectApply(_getRandomValues, this, arguments);
996}
997
998ObjectDefineProperties(
999  Crypto.prototype, {
1000    [SymbolToStringTag]: {
1001      __proto__: null,
1002      enumerable: false,
1003      configurable: true,
1004      writable: false,
1005      value: 'Crypto',
1006    },
1007    subtle: kEnumerableProperty,
1008    getRandomValues: {
1009      __proto__: null,
1010      enumerable: true,
1011      configurable: true,
1012      writable: true,
1013      value: getRandomValues,
1014    },
1015    randomUUID: {
1016      __proto__: null,
1017      enumerable: true,
1018      configurable: true,
1019      writable: true,
1020      value: randomUUID,
1021    },
1022    CryptoKey: {
1023      __proto__: null,
1024      enumerable: true,
1025      configurable: true,
1026      writable: true,
1027      value: CryptoKey,
1028    },
1029  });
1030
1031ObjectDefineProperties(
1032  SubtleCrypto.prototype, {
1033    [SymbolToStringTag]: {
1034      __proto__: null,
1035      enumerable: false,
1036      configurable: true,
1037      writable: false,
1038      value: 'SubtleCrypto',
1039    },
1040    encrypt: {
1041      __proto__: null,
1042      enumerable: true,
1043      configurable: true,
1044      writable: true,
1045      value: encrypt,
1046    },
1047    decrypt: {
1048      __proto__: null,
1049      enumerable: true,
1050      configurable: true,
1051      writable: true,
1052      value: decrypt,
1053    },
1054    sign: {
1055      __proto__: null,
1056      enumerable: true,
1057      configurable: true,
1058      writable: true,
1059      value: sign,
1060    },
1061    verify: {
1062      __proto__: null,
1063      enumerable: true,
1064      configurable: true,
1065      writable: true,
1066      value: verify,
1067    },
1068    digest: {
1069      __proto__: null,
1070      enumerable: true,
1071      configurable: true,
1072      writable: true,
1073      value: digest,
1074    },
1075    generateKey: {
1076      __proto__: null,
1077      enumerable: true,
1078      configurable: true,
1079      writable: true,
1080      value: generateKey,
1081    },
1082    deriveKey: {
1083      __proto__: null,
1084      enumerable: true,
1085      configurable: true,
1086      writable: true,
1087      value: deriveKey,
1088    },
1089    deriveBits: {
1090      __proto__: null,
1091      enumerable: true,
1092      configurable: true,
1093      writable: true,
1094      value: deriveBits,
1095    },
1096    importKey: {
1097      __proto__: null,
1098      enumerable: true,
1099      configurable: true,
1100      writable: true,
1101      value: importKey,
1102    },
1103    exportKey: {
1104      __proto__: null,
1105      enumerable: true,
1106      configurable: true,
1107      writable: true,
1108      value: exportKey,
1109    },
1110    wrapKey: {
1111      __proto__: null,
1112      enumerable: true,
1113      configurable: true,
1114      writable: true,
1115      value: wrapKey,
1116    },
1117    unwrapKey: {
1118      __proto__: null,
1119      enumerable: true,
1120      configurable: true,
1121      writable: true,
1122      value: unwrapKey,
1123    },
1124  });
1125
1126module.exports = {
1127  Crypto,
1128  CryptoKey,
1129  SubtleCrypto,
1130  crypto,
1131};
1132