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