1'use strict'; 2 3// Adapted from the following sources 4// - https://github.com/jsdom/webidl-conversions 5// Copyright Domenic Denicola. Licensed under BSD-2-Clause License. 6// Original license at https://github.com/jsdom/webidl-conversions/blob/master/LICENSE.md. 7// - https://github.com/denoland/deno 8// Copyright Deno authors. Licensed under MIT License. 9// Original license at https://github.com/denoland/deno/blob/main/LICENSE.md. 10// Changes include using primordials and stripping the code down to only what 11// WebCryptoAPI needs. 12 13const { 14 ArrayBufferIsView, 15 ArrayBufferPrototype, 16 ArrayPrototypePush, 17 ArrayPrototypeSort, 18 MathPow, 19 MathTrunc, 20 Number, 21 NumberIsFinite, 22 ObjectAssign, 23 ObjectPrototypeIsPrototypeOf, 24 SafeArrayIterator, 25 SafeSet, 26 String, 27 SymbolIterator, 28 TypedArrayPrototypeGetBuffer, 29 TypedArrayPrototypeGetSymbolToStringTag, 30 TypeError, 31 globalThis: { 32 SharedArrayBuffer, 33 }, 34} = primordials; 35 36const { 37 kEmptyObject, 38 setOwnProperty, 39} = require('internal/util'); 40const { CryptoKey } = require('internal/crypto/webcrypto'); 41const { getDataViewOrTypedArrayBuffer } = require('internal/crypto/util'); 42 43function codedTypeError(message, errorProperties = kEmptyObject) { 44 // eslint-disable-next-line no-restricted-syntax 45 const err = new TypeError(message); 46 ObjectAssign(err, errorProperties); 47 return err; 48} 49 50function makeException(message, opts = kEmptyObject) { 51 const prefix = opts.prefix ? opts.prefix + ': ' : ''; 52 const context = opts.context?.length === 0 ? 53 '' : (opts.context ?? 'Value') + ' '; 54 return codedTypeError( 55 `${prefix}${context}${message}`, 56 { code: opts.code || 'ERR_INVALID_ARG_TYPE' }, 57 ); 58} 59 60// https://tc39.es/ecma262/#sec-tonumber 61function toNumber(value, opts = kEmptyObject) { 62 switch (typeof value) { 63 case 'number': 64 return value; 65 case 'bigint': 66 throw makeException( 67 'is a BigInt and cannot be converted to a number.', 68 opts); 69 case 'symbol': 70 throw makeException( 71 'is a Symbol and cannot be converted to a number.', 72 opts); 73 default: 74 return Number(value); 75 } 76} 77 78function type(V) { 79 if (V === null) 80 return 'Null'; 81 82 switch (typeof V) { 83 case 'undefined': 84 return 'Undefined'; 85 case 'boolean': 86 return 'Boolean'; 87 case 'number': 88 return 'Number'; 89 case 'string': 90 return 'String'; 91 case 'symbol': 92 return 'Symbol'; 93 case 'bigint': 94 return 'BigInt'; 95 case 'object': // Fall through 96 case 'function': // Fall through 97 default: 98 // Per ES spec, typeof returns an implemention-defined value that is not 99 // any of the existing ones for uncallable non-standard exotic objects. 100 // Yet Type() which the Web IDL spec depends on returns Object for such 101 // cases. So treat the default case as an object. 102 return 'Object'; 103 } 104} 105 106const integerPart = MathTrunc; 107 108// This was updated to only consider bitlength up to 32 used by WebCryptoAPI 109function createIntegerConversion(bitLength) { 110 const lowerBound = 0; 111 const upperBound = MathPow(2, bitLength) - 1; 112 113 const twoToTheBitLength = MathPow(2, bitLength); 114 115 return (V, opts = kEmptyObject) => { 116 let x = toNumber(V, opts); 117 118 if (opts.enforceRange) { 119 if (!NumberIsFinite(x)) { 120 throw makeException( 121 'is not a finite number.', 122 opts); 123 } 124 125 x = integerPart(x); 126 127 if (x < lowerBound || x > upperBound) { 128 throw makeException( 129 `is outside the expected range of ${lowerBound} to ${upperBound}.`, 130 { __proto__: null, ...opts, code: 'ERR_OUT_OF_RANGE' }, 131 ); 132 } 133 134 return x; 135 } 136 137 if (!NumberIsFinite(x) || x === 0) { 138 return 0; 139 } 140 141 x = integerPart(x); 142 143 if (x >= lowerBound && x <= upperBound) { 144 return x; 145 } 146 147 x = x % twoToTheBitLength; 148 149 return x; 150 }; 151} 152 153const converters = {}; 154 155converters.boolean = (val) => !!val; 156converters.octet = createIntegerConversion(8); 157converters['unsigned short'] = createIntegerConversion(16); 158converters['unsigned long'] = createIntegerConversion(32); 159 160converters.DOMString = function(V, opts = kEmptyObject) { 161 if (typeof V === 'string') { 162 return V; 163 } else if (typeof V === 'symbol') { 164 throw makeException( 165 'is a Symbol and cannot be converted to a string.', 166 opts); 167 } 168 169 return String(V); 170}; 171 172converters.object = (V, opts) => { 173 if (type(V) !== 'Object') { 174 throw makeException( 175 'is not an object.', 176 opts); 177 } 178 179 return V; 180}; 181 182function isNonSharedArrayBuffer(V) { 183 return ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V); 184} 185 186function isSharedArrayBuffer(V) { 187 return ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V); 188} 189 190converters.Uint8Array = (V, opts = kEmptyObject) => { 191 if (!ArrayBufferIsView(V) || 192 TypedArrayPrototypeGetSymbolToStringTag(V) !== 'Uint8Array') { 193 throw makeException( 194 'is not an Uint8Array object.', 195 opts); 196 } 197 if (isSharedArrayBuffer(TypedArrayPrototypeGetBuffer(V))) { 198 throw makeException( 199 'is a view on a SharedArrayBuffer, which is not allowed.', 200 opts); 201 } 202 203 return V; 204}; 205 206converters.BufferSource = (V, opts = kEmptyObject) => { 207 if (ArrayBufferIsView(V)) { 208 if (isSharedArrayBuffer(getDataViewOrTypedArrayBuffer(V))) { 209 throw makeException( 210 'is a view on a SharedArrayBuffer, which is not allowed.', 211 opts); 212 } 213 214 return V; 215 } 216 217 if (!isNonSharedArrayBuffer(V)) { 218 throw makeException( 219 'is not instance of ArrayBuffer, Buffer, TypedArray, or DataView.', 220 opts); 221 } 222 223 return V; 224}; 225 226converters['sequence<DOMString>'] = createSequenceConverter( 227 converters.DOMString); 228 229function requiredArguments(length, required, opts = kEmptyObject) { 230 if (length < required) { 231 throw makeException( 232 `${required} argument${ 233 required === 1 ? '' : 's' 234 } required, but only ${length} present.`, 235 { __proto__: null, ...opts, context: '', code: 'ERR_MISSING_ARGS' }); 236 } 237} 238 239function createDictionaryConverter(name, dictionaries) { 240 let hasRequiredKey = false; 241 const allMembers = []; 242 for (let i = 0; i < dictionaries.length; i++) { 243 const member = dictionaries[i]; 244 if (member.required) { 245 hasRequiredKey = true; 246 } 247 ArrayPrototypePush(allMembers, member); 248 } 249 ArrayPrototypeSort(allMembers, (a, b) => { 250 if (a.key === b.key) { 251 return 0; 252 } 253 return a.key < b.key ? -1 : 1; 254 }); 255 256 return function(V, opts = kEmptyObject) { 257 const typeV = type(V); 258 switch (typeV) { 259 case 'Undefined': 260 case 'Null': 261 case 'Object': 262 break; 263 default: 264 throw makeException( 265 'can not be converted to a dictionary', 266 opts); 267 } 268 const esDict = V; 269 const idlDict = {}; 270 271 // Fast path null and undefined. 272 if (V == null && !hasRequiredKey) { 273 return idlDict; 274 } 275 276 for (const member of new SafeArrayIterator(allMembers)) { 277 const key = member.key; 278 279 let esMemberValue; 280 if (typeV === 'Undefined' || typeV === 'Null') { 281 esMemberValue = undefined; 282 } else { 283 esMemberValue = esDict[key]; 284 } 285 286 if (esMemberValue !== undefined) { 287 const context = `'${key}' of '${name}'${ 288 opts.context ? ` (${opts.context})` : '' 289 }`; 290 const converter = member.converter; 291 const idlMemberValue = converter(esMemberValue, { 292 __proto__: null, 293 ...opts, 294 context, 295 }); 296 setOwnProperty(idlDict, key, idlMemberValue); 297 } else if (member.required) { 298 throw makeException( 299 `can not be converted to '${name}' because '${key}' is required in '${name}'.`, 300 { __proto__: null, ...opts, code: 'ERR_MISSING_OPTION' }); 301 } 302 } 303 304 return idlDict; 305 }; 306} 307 308function createEnumConverter(name, values) { 309 const E = new SafeSet(values); 310 311 return function(V, opts = kEmptyObject) { 312 const S = String(V); 313 314 if (!E.has(S)) { 315 throw makeException( 316 `value '${S}' is not a valid enum value of type ${name}.`, 317 { __proto__: null, ...opts, code: 'ERR_INVALID_ARG_VALUE' }); 318 } 319 320 return S; 321 }; 322} 323 324function createSequenceConverter(converter) { 325 return function(V, opts = kEmptyObject) { 326 if (type(V) !== 'Object') { 327 throw makeException( 328 'can not be converted to sequence.', 329 opts); 330 } 331 const iter = V?.[SymbolIterator]?.(); 332 if (iter === undefined) { 333 throw makeException( 334 'can not be converted to sequence.', 335 opts); 336 } 337 const array = []; 338 while (true) { 339 const res = iter?.next?.(); 340 if (res === undefined) { 341 throw makeException( 342 'can not be converted to sequence.', 343 opts); 344 } 345 if (res.done === true) break; 346 const val = converter(res.value, { 347 __proto__: null, 348 ...opts, 349 context: `${opts.context}, index ${array.length}`, 350 }); 351 ArrayPrototypePush(array, val); 352 } 353 return array; 354 }; 355} 356 357function createInterfaceConverter(name, prototype) { 358 return (V, opts) => { 359 if (!ObjectPrototypeIsPrototypeOf(prototype, V)) { 360 throw makeException( 361 `is not of type ${name}.`, 362 opts); 363 } 364 return V; 365 }; 366} 367 368converters.AlgorithmIdentifier = (V, opts) => { 369 // Union for (object or DOMString) 370 if (type(V) === 'Object') { 371 return converters.object(V, opts); 372 } 373 return converters.DOMString(V, opts); 374}; 375 376converters.KeyFormat = createEnumConverter('KeyFormat', [ 377 'raw', 378 'pkcs8', 379 'spki', 380 'jwk', 381]); 382 383converters.KeyUsage = createEnumConverter('KeyUsage', [ 384 'encrypt', 385 'decrypt', 386 'sign', 387 'verify', 388 'deriveKey', 389 'deriveBits', 390 'wrapKey', 391 'unwrapKey', 392]); 393 394converters['sequence<KeyUsage>'] = createSequenceConverter(converters.KeyUsage); 395 396converters.HashAlgorithmIdentifier = converters.AlgorithmIdentifier; 397 398const dictAlgorithm = [ 399 { 400 key: 'name', 401 converter: converters.DOMString, 402 required: true, 403 }, 404]; 405 406converters.Algorithm = createDictionaryConverter( 407 'Algorithm', dictAlgorithm); 408 409converters.BigInteger = converters.Uint8Array; 410 411const dictRsaKeyGenParams = [ 412 ...new SafeArrayIterator(dictAlgorithm), 413 { 414 key: 'modulusLength', 415 converter: (V, opts) => 416 converters['unsigned long'](V, { ...opts, enforceRange: true }), 417 required: true, 418 }, 419 { 420 key: 'publicExponent', 421 converter: converters.BigInteger, 422 required: true, 423 }, 424]; 425 426converters.RsaKeyGenParams = createDictionaryConverter( 427 'RsaKeyGenParams', dictRsaKeyGenParams); 428 429converters.RsaHashedKeyGenParams = createDictionaryConverter( 430 'RsaHashedKeyGenParams', [ 431 ...new SafeArrayIterator(dictRsaKeyGenParams), 432 { 433 key: 'hash', 434 converter: converters.HashAlgorithmIdentifier, 435 required: true, 436 }, 437 ]); 438 439converters.RsaHashedImportParams = createDictionaryConverter( 440 'RsaHashedImportParams', [ 441 ...new SafeArrayIterator(dictAlgorithm), 442 { 443 key: 'hash', 444 converter: converters.HashAlgorithmIdentifier, 445 required: true, 446 }, 447 ]); 448 449converters.NamedCurve = converters.DOMString; 450 451converters.EcKeyImportParams = createDictionaryConverter( 452 'EcKeyImportParams', [ 453 ...new SafeArrayIterator(dictAlgorithm), 454 { 455 key: 'namedCurve', 456 converter: converters.NamedCurve, 457 required: true, 458 }, 459 ]); 460 461converters.EcKeyGenParams = createDictionaryConverter( 462 'EcKeyGenParams', [ 463 ...new SafeArrayIterator(dictAlgorithm), 464 { 465 key: 'namedCurve', 466 converter: converters.NamedCurve, 467 required: true, 468 }, 469 ]); 470 471converters.AesKeyGenParams = createDictionaryConverter( 472 'AesKeyGenParams', [ 473 ...new SafeArrayIterator(dictAlgorithm), 474 { 475 key: 'length', 476 converter: (V, opts) => 477 converters['unsigned short'](V, { ...opts, enforceRange: true }), 478 required: true, 479 }, 480 ]); 481 482converters.HmacKeyGenParams = createDictionaryConverter( 483 'HmacKeyGenParams', [ 484 ...new SafeArrayIterator(dictAlgorithm), 485 { 486 key: 'hash', 487 converter: converters.HashAlgorithmIdentifier, 488 required: true, 489 }, 490 { 491 key: 'length', 492 converter: (V, opts) => 493 converters['unsigned long'](V, { ...opts, enforceRange: true }), 494 }, 495 ]); 496 497converters.RsaPssParams = createDictionaryConverter( 498 'RsaPssParams', [ 499 ...new SafeArrayIterator(dictAlgorithm), 500 { 501 key: 'saltLength', 502 converter: (V, opts) => 503 converters['unsigned long'](V, { ...opts, enforceRange: true }), 504 required: true, 505 }, 506 ]); 507 508converters.RsaOaepParams = createDictionaryConverter( 509 'RsaOaepParams', [ 510 ...new SafeArrayIterator(dictAlgorithm), 511 { 512 key: 'label', 513 converter: converters.BufferSource, 514 }, 515 ]); 516 517converters.EcdsaParams = createDictionaryConverter( 518 'EcdsaParams', [ 519 ...new SafeArrayIterator(dictAlgorithm), 520 { 521 key: 'hash', 522 converter: converters.HashAlgorithmIdentifier, 523 required: true, 524 }, 525 ]); 526 527converters.HmacImportParams = createDictionaryConverter( 528 'HmacImportParams', [ 529 ...new SafeArrayIterator(dictAlgorithm), 530 { 531 key: 'hash', 532 converter: converters.HashAlgorithmIdentifier, 533 required: true, 534 }, 535 { 536 key: 'length', 537 converter: (V, opts) => 538 converters['unsigned long'](V, { ...opts, enforceRange: true }), 539 }, 540 ]); 541 542const simpleDomStringKey = (key) => ({ key, converter: converters.DOMString }); 543 544converters.RsaOtherPrimesInfo = createDictionaryConverter( 545 'RsaOtherPrimesInfo', [ 546 simpleDomStringKey('r'), 547 simpleDomStringKey('d'), 548 simpleDomStringKey('t'), 549 ]); 550converters['sequence<RsaOtherPrimesInfo>'] = createSequenceConverter( 551 converters.RsaOtherPrimesInfo); 552 553converters.JsonWebKey = createDictionaryConverter( 554 'JsonWebKey', [ 555 simpleDomStringKey('kty'), 556 simpleDomStringKey('use'), 557 { 558 key: 'key_ops', 559 converter: converters['sequence<DOMString>'], 560 }, 561 simpleDomStringKey('alg'), 562 { 563 key: 'ext', 564 converter: converters.boolean, 565 }, 566 simpleDomStringKey('crv'), 567 simpleDomStringKey('x'), 568 simpleDomStringKey('y'), 569 simpleDomStringKey('d'), 570 simpleDomStringKey('n'), 571 simpleDomStringKey('e'), 572 simpleDomStringKey('p'), 573 simpleDomStringKey('q'), 574 simpleDomStringKey('dp'), 575 simpleDomStringKey('dq'), 576 simpleDomStringKey('qi'), 577 { 578 key: 'oth', 579 converter: converters['sequence<RsaOtherPrimesInfo>'], 580 }, 581 simpleDomStringKey('k'), 582 ]); 583 584converters.HkdfParams = createDictionaryConverter( 585 'HkdfParams', [ 586 ...new SafeArrayIterator(dictAlgorithm), 587 { 588 key: 'hash', 589 converter: converters.HashAlgorithmIdentifier, 590 required: true, 591 }, 592 { 593 key: 'salt', 594 converter: converters.BufferSource, 595 required: true, 596 }, 597 { 598 key: 'info', 599 converter: converters.BufferSource, 600 required: true, 601 }, 602 ]); 603 604converters.Pbkdf2Params = createDictionaryConverter( 605 'Pbkdf2Params', [ 606 ...new SafeArrayIterator(dictAlgorithm), 607 { 608 key: 'hash', 609 converter: converters.HashAlgorithmIdentifier, 610 required: true, 611 }, 612 { 613 key: 'iterations', 614 converter: (V, opts) => 615 converters['unsigned long'](V, { ...opts, enforceRange: true }), 616 required: true, 617 }, 618 { 619 key: 'salt', 620 converter: converters.BufferSource, 621 required: true, 622 }, 623 ]); 624 625converters.AesDerivedKeyParams = createDictionaryConverter( 626 'AesDerivedKeyParams', [ 627 ...new SafeArrayIterator(dictAlgorithm), 628 { 629 key: 'length', 630 converter: (V, opts) => 631 converters['unsigned short'](V, { ...opts, enforceRange: true }), 632 required: true, 633 }, 634 ]); 635 636converters.AesCbcParams = createDictionaryConverter( 637 'AesCbcParams', [ 638 ...new SafeArrayIterator(dictAlgorithm), 639 { 640 key: 'iv', 641 converter: converters.BufferSource, 642 required: true, 643 }, 644 ]); 645 646converters.AesGcmParams = createDictionaryConverter( 647 'AesGcmParams', [ 648 ...new SafeArrayIterator(dictAlgorithm), 649 { 650 key: 'iv', 651 converter: converters.BufferSource, 652 required: true, 653 }, 654 { 655 key: 'tagLength', 656 converter: (V, opts) => 657 converters.octet(V, { ...opts, enforceRange: true }), 658 }, 659 { 660 key: 'additionalData', 661 converter: converters.BufferSource, 662 }, 663 ]); 664 665converters.AesCtrParams = createDictionaryConverter( 666 'AesCtrParams', [ 667 ...new SafeArrayIterator(dictAlgorithm), 668 { 669 key: 'counter', 670 converter: converters.BufferSource, 671 required: true, 672 }, 673 { 674 key: 'length', 675 converter: (V, opts) => 676 converters.octet(V, { ...opts, enforceRange: true }), 677 required: true, 678 }, 679 ]); 680 681converters.CryptoKey = createInterfaceConverter( 682 'CryptoKey', CryptoKey.prototype); 683 684converters.EcdhKeyDeriveParams = createDictionaryConverter( 685 'EcdhKeyDeriveParams', [ 686 ...new SafeArrayIterator(dictAlgorithm), 687 { 688 key: 'public', 689 converter: converters.CryptoKey, 690 required: true, 691 }, 692 ]); 693 694converters.Ed448Params = createDictionaryConverter( 695 'Ed448Params', [ 696 ...new SafeArrayIterator(dictAlgorithm), 697 { 698 key: 'context', 699 converter: converters.BufferSource, 700 required: false, 701 }, 702 ]); 703 704module.exports = { 705 converters, 706 requiredArguments, 707}; 708