From bc436ebd7ad52c5736832eb57059b631af2bcab4 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 2 Jan 2023 22:28:50 +0100 Subject: [PATCH 01/27] crypto: use WebIDL converters in WebCryptoAPI WebCryptoAPI functions' arguments are now coersed and validated as per their WebIDL definitions like in other Web Crypto API implementations. This further improves interoperability with other implementations of Web Crypto API. --- lib/internal/crypto/aes.js | 6 - lib/internal/crypto/cfrg.js | 4 - lib/internal/crypto/diffiehellman.js | 3 - lib/internal/crypto/ec.js | 2 - lib/internal/crypto/hash.js | 10 +- lib/internal/crypto/hkdf.js | 8 +- lib/internal/crypto/pbkdf2.js | 16 +- lib/internal/crypto/random.js | 3 - lib/internal/crypto/rsa.js | 2 - lib/internal/crypto/util.js | 249 ++++-- lib/internal/crypto/webcrypto.js | 323 ++++++-- lib/internal/crypto/webidl.js | 752 ++++++++++++++++++ test/fixtures/crypto/aes_gcm.js | 2 +- .../test-webcrypto-cryptokey-workers.js | 2 +- .../test-webcrypto-derivebits-cfrg.js | 2 +- .../test-webcrypto-derivebits-ecdh.js | 2 +- .../test-webcrypto-derivebits-hkdf.js | 4 +- .../parallel/test-webcrypto-derivekey-cfrg.js | 2 +- .../parallel/test-webcrypto-derivekey-ecdh.js | 2 +- test/parallel/test-webcrypto-digest.js | 14 +- test/parallel/test-webcrypto-export-import.js | 14 +- test/parallel/test-webcrypto-keygen.js | 50 +- .../test-webcrypto-sign-verify-hmac.js | 17 +- .../test-webcrypto-sign-verify-rsa.js | 19 +- test/parallel/test-webcrypto-util.js | 12 +- .../test-webcrypto-derivebits-pbkdf2.js | 6 +- test/wpt/status/WebCryptoAPI.json | 13 + 27 files changed, 1279 insertions(+), 260 deletions(-) create mode 100644 lib/internal/crypto/webidl.js diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index 5b4e7d8f743469..67b6d306dc44e1 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -32,7 +32,6 @@ const { } = internalBinding('crypto'); const { - getArrayBufferOrView, hasAnyNotIn, jobPromise, validateByteLength, @@ -112,7 +111,6 @@ function getVariant(name, length) { } function asyncAesCtrCipher(mode, key, data, { counter, length }) { - counter = getArrayBufferOrView(counter, 'algorithm.counter'); validateByteLength(counter, 'algorithm.counter', 16); // The length must specify an integer between 1 and 128. While // there is no default, this should typically be 64. @@ -135,7 +133,6 @@ function asyncAesCtrCipher(mode, key, data, { counter, length }) { } function asyncAesCbcCipher(mode, key, data, { iv }) { - iv = getArrayBufferOrView(iv, 'algorithm.iv'); validateByteLength(iv, 'algorithm.iv', 16); return jobPromise(() => new AESCipherJob( kCryptoJobAsync, @@ -166,12 +163,9 @@ function asyncAesGcmCipher( 'OperationError')); } - iv = getArrayBufferOrView(iv, 'algorithm.iv'); validateMaxBufferLength(iv, 'algorithm.iv'); if (additionalData !== undefined) { - additionalData = - getArrayBufferOrView(additionalData, 'algorithm.additionalData'); validateMaxBufferLength(additionalData, 'algorithm.additionalData'); } diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index 6f098df41530b2..47a4f78e415b7a 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -18,7 +18,6 @@ const { } = internalBinding('crypto'); const { - getArrayBufferOrView, getUsagesUnion, hasAnyNotIn, jobPromise, @@ -73,7 +72,6 @@ function verifyAcceptableCfrgKeyUse(name, isPublic, usages) { function createCFRGRawKey(name, keyData, isPublic) { const handle = new KeyObjectHandle(); - keyData = getArrayBufferOrView(keyData, 'keyData'); switch (name) { case 'Ed25519': @@ -337,8 +335,6 @@ function eddsaSignVerify(key, data, { name, context }, signature) { throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError'); if (name === 'Ed448' && context !== undefined) { - context = - getArrayBufferOrView(context, 'algorithm.context'); if (context.byteLength !== 0) { throw lazyDOMException( 'Non zero-length context is not yet supported.', 'NotSupportedError'); diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 391832fde46bc2..2ca2f46330414e 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -48,7 +48,6 @@ const { const { KeyObject, - isCryptoKey, } = require('internal/crypto/keys'); const { @@ -324,8 +323,6 @@ async function ecdhDeriveBits(algorithm, baseKey, length) { // give us everything that is generated. if (length !== null) validateUint32(length, 'length'); - if (!isCryptoKey(key)) - throw new ERR_INVALID_ARG_TYPE('algorithm.public', 'CryptoKey', key); if (key.type !== 'public') { throw lazyDOMException( diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index ef9e4f26b7759f..642b857a1a1655 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -24,7 +24,6 @@ const { } = require('internal/errors'); const { - getArrayBufferOrView, getUsagesUnion, hasAnyNotIn, jobPromise, @@ -76,7 +75,6 @@ function verifyAcceptableEcKeyUse(name, isPublic, usages) { function createECPublicKeyRaw(namedCurve, keyData) { const handle = new KeyObjectHandle(); - keyData = getArrayBufferOrView(keyData, 'keyData'); if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) { throw lazyDOMException('Invalid keyData', 'DataError'); diff --git a/lib/internal/crypto/hash.js b/lib/internal/crypto/hash.js index 72d127b37cbeb6..c7eeed192afacb 100644 --- a/lib/internal/crypto/hash.js +++ b/lib/internal/crypto/hash.js @@ -14,11 +14,9 @@ const { } = internalBinding('crypto'); const { - getArrayBufferOrView, getDefaultEncoding, getStringOption, jobPromise, - normalizeAlgorithm, normalizeHashName, validateMaxBufferLength, kHandle, @@ -168,13 +166,8 @@ Hmac.prototype._transform = Hash.prototype._transform; // Implementation for WebCrypto subtle.digest() async function asyncDigest(algorithm, data) { - algorithm = normalizeAlgorithm(algorithm); - data = getArrayBufferOrView(data, 'data'); validateMaxBufferLength(data, 'data'); - if (algorithm.length !== undefined) - validateUint32(algorithm.length, 'algorithm.length'); - switch (algorithm.name) { case 'SHA-1': // Fall through @@ -186,8 +179,7 @@ async function asyncDigest(algorithm, data) { return jobPromise(() => new HashJob( kCryptoJobAsync, normalizeHashName(algorithm.name), - data, - algorithm.length)); + data)); } throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); diff --git a/lib/internal/crypto/hkdf.js b/lib/internal/crypto/hkdf.js index ec676b3d666d1d..7f0fe5534ee843 100644 --- a/lib/internal/crypto/hkdf.js +++ b/lib/internal/crypto/hkdf.js @@ -19,7 +19,6 @@ const { const { kMaxLength } = require('buffer'); const { - getArrayBufferOrView, normalizeHashName, toBuf, validateByteSource, @@ -45,7 +44,6 @@ const { codes: { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, - ERR_MISSING_OPTION, }, hideStackFrames, } = require('internal/errors'); @@ -140,11 +138,7 @@ function hkdfSync(hash, key, salt, info, length) { const hkdfPromise = promisify(hkdf); async function hkdfDeriveBits(algorithm, baseKey, length) { - const { hash } = algorithm; - const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt'); - const info = getArrayBufferOrView(algorithm.info, 'algorithm.info'); - if (hash === undefined) - throw new ERR_MISSING_OPTION('algorithm.hash'); + const { hash, salt, info } = algorithm; if (length === 0) throw lazyDOMException('length cannot be zero', 'OperationError'); diff --git a/lib/internal/crypto/pbkdf2.js b/lib/internal/crypto/pbkdf2.js index 0ebe865776eca7..a46d5120236fba 100644 --- a/lib/internal/crypto/pbkdf2.js +++ b/lib/internal/crypto/pbkdf2.js @@ -15,12 +15,9 @@ const { const { validateFunction, validateInt32, - validateInteger, validateString, } = require('internal/validators'); -const { ERR_MISSING_OPTION } = require('internal/errors').codes; - const { getArrayBufferOrView, getDefaultEncoding, @@ -101,19 +98,12 @@ function check(password, salt, iterations, keylen, digest) { const pbkdf2Promise = promisify(pbkdf2); async function pbkdf2DeriveBits(algorithm, baseKey, length) { - const { iterations } = algorithm; - let { hash } = algorithm; - const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt'); - if (hash === undefined) - throw new ERR_MISSING_OPTION('algorithm.hash'); - validateInteger(iterations, 'algorithm.iterations'); + const { iterations, hash, salt } = algorithm; if (iterations === 0) throw lazyDOMException( 'iterations cannot be zero', 'OperationError'); - hash = normalizeHashName(hash.name); - const raw = baseKey[kKeyObject].export(); if (length === 0) @@ -128,7 +118,9 @@ async function pbkdf2DeriveBits(algorithm, baseKey, length) { let result; try { - result = await pbkdf2Promise(raw, salt, iterations, length / 8, hash); + result = await pbkdf2Promise( + raw, salt, iterations, length / 8, normalizeHashName(hash.name), + ); } catch (err) { throw lazyDOMException( 'The operation failed for an operation-specific reason', diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index da1ecb80c8941f..fe76cf1a69ef43 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -40,7 +40,6 @@ const { Buffer, kMaxLength } = require('buffer'); const { codes: { ERR_INVALID_ARG_TYPE, - ERR_MISSING_ARGS, ERR_OUT_OF_RANGE, ERR_OPERATION_FAILED, } @@ -316,8 +315,6 @@ function onJobDone(buf, callback, error) { // not allowed to exceed 65536 bytes, and can only // be an integer-type TypedArray. function getRandomValues(data) { - if (arguments.length < 1) - throw new ERR_MISSING_ARGS('typedArray'); if (!isTypedArray(data) || isFloat32Array(data) || isFloat64Array(data)) { diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index 5776d06fd18699..b8668b0c30255e 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -35,7 +35,6 @@ const { const { bigIntArrayToUnsignedInt, - getArrayBufferOrView, getUsagesUnion, hasAnyNotIn, jobPromise, @@ -104,7 +103,6 @@ function rsaOaepCipher(mode, key, data, { label }) { 'InvalidAccessError'); } if (label !== undefined) { - label = getArrayBufferOrView(label, 'algorithm.label'); validateMaxBufferLength(label, 'algorithm.label'); } diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index d7e6356486221e..2ac7c061ba8c15 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -1,15 +1,18 @@ 'use strict'; const { + ArrayBufferIsView, ArrayPrototypeIncludes, ArrayPrototypePush, BigInt, FunctionPrototypeBind, Number, - ObjectKeys, Promise, + TypedArrayPrototypeSlice, + ObjectPrototypeHasOwnProperty, StringPrototypeToLowerCase, Symbol, + Uint8Array, } = primordials; const { @@ -138,40 +141,6 @@ const kNamedCurveAliases = { const kAesKeyLengths = [128, 192, 256]; -// These are the only algorithms we currently support -// via the Web Crypto API -const kAlgorithms = { - 'rsassa-pkcs1-v1_5': 'RSASSA-PKCS1-v1_5', - 'rsa-pss': 'RSA-PSS', - 'rsa-oaep': 'RSA-OAEP', - 'ecdsa': 'ECDSA', - 'ecdh': 'ECDH', - 'aes-ctr': 'AES-CTR', - 'aes-cbc': 'AES-CBC', - 'aes-gcm': 'AES-GCM', - 'aes-kw': 'AES-KW', - 'hmac': 'HMAC', - 'sha-1': 'SHA-1', - 'sha-256': 'SHA-256', - 'sha-384': 'SHA-384', - 'sha-512': 'SHA-512', - 'hkdf': 'HKDF', - 'pbkdf2': 'PBKDF2', - 'ed25519': 'Ed25519', - 'ed448': 'Ed448', - 'x25519': 'X25519', - 'x448': 'X448', -}; -const kAlgorithmsKeys = ObjectKeys(kAlgorithms); - -// These are the only export and import formats we currently -// support via the Web Crypto API -const kExportFormats = [ - 'raw', - 'pkcs8', - 'spki', - 'jwk']; - // These are the only hash algorithms we currently support via // the Web Crypto API. const kHashTypes = [ @@ -181,6 +150,119 @@ const kHashTypes = [ 'SHA-512', ]; +const kSupportedAlgorithms = { + 'digest': { + 'SHA-1': null, + 'SHA-256': null, + 'SHA-384': null, + 'SHA-512': null, + }, + 'generateKey': { + 'RSASSA-PKCS1-v1_5': 'RsaHashedKeyGenParams', + 'RSA-PSS': 'RsaHashedKeyGenParams', + 'RSA-OAEP': 'RsaHashedKeyGenParams', + 'ECDSA': 'EcKeyGenParams', + 'ECDH': 'EcKeyGenParams', + 'AES-CTR': 'AesKeyGenParams', + 'AES-CBC': 'AesKeyGenParams', + 'AES-GCM': 'AesKeyGenParams', + 'AES-KW': 'AesKeyGenParams', + 'HMAC': 'HmacKeyGenParams', + 'X25519': null, + 'Ed25519': null, + 'X448': null, + 'Ed448': null, + }, + 'sign': { + 'RSASSA-PKCS1-v1_5': null, + 'RSA-PSS': 'RsaPssParams', + 'ECDSA': 'EcdsaParams', + 'HMAC': null, + 'Ed25519': null, + 'Ed448': 'Ed448Params', + }, + 'verify': { + 'RSASSA-PKCS1-v1_5': null, + 'RSA-PSS': 'RsaPssParams', + 'ECDSA': 'EcdsaParams', + 'HMAC': null, + 'Ed25519': null, + 'Ed448': 'Ed448Params', + }, + 'importKey': { + 'RSASSA-PKCS1-v1_5': 'RsaHashedImportParams', + 'RSA-PSS': 'RsaHashedImportParams', + 'RSA-OAEP': 'RsaHashedImportParams', + 'ECDSA': 'EcKeyImportParams', + 'ECDH': 'EcKeyImportParams', + 'HMAC': 'HmacImportParams', + 'HKDF': null, + 'PBKDF2': null, + 'AES-CTR': null, + 'AES-CBC': null, + 'AES-GCM': null, + 'AES-KW': null, + 'Ed25519': null, + 'X25519': null, + 'Ed448': null, + 'X448': null, + }, + 'deriveBits': { + 'HKDF': 'HkdfParams', + 'PBKDF2': 'Pbkdf2Params', + 'ECDH': 'EcdhKeyDeriveParams', + 'X25519': 'EcdhKeyDeriveParams', + 'X448': 'EcdhKeyDeriveParams', + }, + 'encrypt': { + 'RSA-OAEP': 'RsaOaepParams', + 'AES-CBC': 'AesCbcParams', + 'AES-GCM': 'AesGcmParams', + 'AES-CTR': 'AesCtrParams', + }, + 'decrypt': { + 'RSA-OAEP': 'RsaOaepParams', + 'AES-CBC': 'AesCbcParams', + 'AES-GCM': 'AesGcmParams', + 'AES-CTR': 'AesCtrParams', + }, + 'get key length': { + 'AES-CBC': 'AesDerivedKeyParams', + 'AES-CTR': 'AesDerivedKeyParams', + 'AES-GCM': 'AesDerivedKeyParams', + 'AES-KW': 'AesDerivedKeyParams', + 'HMAC': 'HmacImportParams', + 'HKDF': null, + 'PBKDF2': null, + }, + 'wrapKey': { + 'AES-KW': null, + }, + 'unwrapKey': { + 'AES-KW': null, + }, +}; + +const simpleAlgorithmDictionaries = { + AesGcmParams: { iv: 'BufferSource', additionalData: 'BufferSource' }, + RsaHashedKeyGenParams: { hash: 'HashAlgorithmIdentifier' }, + EcKeyGenParams: {}, + HmacKeyGenParams: { hash: 'HashAlgorithmIdentifier' }, + RsaPssParams: {}, + EcdsaParams: { hash: 'HashAlgorithmIdentifier' }, + HmacImportParams: { hash: 'HashAlgorithmIdentifier' }, + HkdfParams: { + hash: 'HashAlgorithmIdentifier', + salt: 'BufferSource', + info: 'BufferSource', + }, + Ed448Params: { context: 'BufferSource' }, + Pbkdf2Params: { hash: 'HashAlgorithmIdentifier', salt: 'BufferSource' }, + RsaOaepParams: { label: 'BufferSource' }, + RsaHashedImportParams: { hash: 'HashAlgorithmIdentifier' }, + EcKeyImportParams: {}, +}; + function validateMaxBufferLength(data, name) { if (data.byteLength > kMaxBufferLength) { throw lazyDOMException( @@ -189,36 +271,76 @@ function validateMaxBufferLength(data, name) { } } -function normalizeAlgorithm(algorithm) { - if (algorithm != null) { - if (typeof algorithm === 'string') - algorithm = { name: algorithm }; - - if (typeof algorithm === 'object') { - const { name } = algorithm; - if (typeof name !== 'string' || - !ArrayPrototypeIncludes( - kAlgorithmsKeys, - StringPrototypeToLowerCase(name))) { - throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); - } - let { hash } = algorithm; - if (hash !== undefined) { - hash = normalizeAlgorithm(hash); - if (!ArrayPrototypeIncludes(kHashTypes, hash.name)) - throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); - } - const normalized = { - ...algorithm, - name: kAlgorithms[StringPrototypeToLowerCase(name)], - }; - if (hash) { - normalized.hash = hash; - } - return normalized; +function normalizeAlgorithm(algorithm, op) { + if (typeof algorithm === 'string') + return normalizeAlgorithm({ name: algorithm }, op); + + const webidl = require('internal/crypto/webidl'); + + // 1. + const registeredAlgorithms = kSupportedAlgorithms[op]; + // 2. 3. + const initialAlg = webidl.converters.Algorithm(algorithm, { + prefix: 'Failed to normalize algorithm', + context: 'passed algorithm', + }); + // 4. + let algName = initialAlg.name; + + // 5. + let desiredType; + for (const key in registeredAlgorithms) { + if (!ObjectPrototypeHasOwnProperty(registeredAlgorithms, key)) { + continue; + } + if ( + StringPrototypeToLowerCase(key) === StringPrototypeToLowerCase(algName) + ) { + algName = key; + desiredType = registeredAlgorithms[key]; } } - throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); + if (desiredType === undefined) + throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); + + // Fast path everything below if the registered dictionary is null + if (desiredType === null) + return { name: algName }; + + // 6. + const normalizedAlgorithm = webidl.converters[desiredType](algorithm, { + prefix: 'Failed to normalize algorithm', + context: 'passed algorithm', + }); + // 7. + normalizedAlgorithm.name = algName; + + // 9. + const dict = simpleAlgorithmDictionaries[desiredType]; + // 10. + for (const member in dict) { + if (!ObjectPrototypeHasOwnProperty(dict, member)) + continue; + const idlType = dict[member]; + const idlValue = normalizedAlgorithm[member]; + // 3. + if (idlType === 'BufferSource' && idlValue) { + normalizedAlgorithm[member] = TypedArrayPrototypeSlice( + new Uint8Array( + ArrayBufferIsView(idlValue) ? idlValue.buffer : idlValue, + idlValue.byteOffset ?? 0, + idlValue.byteLength, + ), + ); + } else if (idlType === 'HashAlgorithmIdentifier') { + normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, 'digest'); + } else if (idlType === 'AlgorithmIdentifier') { + // This extension point is not used by any supported algorithm (yet?) + throw lazyDOMException('Not implemented.', 'NotSupportedError'); + } + } + + return normalizedAlgorithm; } function hasAnyNotIn(set, checks) { @@ -413,7 +535,6 @@ module.exports = { kHashTypes, kNamedCurveAliases, kAesKeyLengths, - kExportFormats, normalizeAlgorithm, normalizeHashName, hasAnyNotIn, diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index 1360fc6d757bdf..fed498c8452d13 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -21,14 +21,6 @@ const { kWebCryptoCipherDecrypt, } = internalBinding('crypto'); -const { - validateArray, - validateBoolean, - validateObject, - validateOneOf, - validateString, -} = require('internal/validators'); - const { getOptionValue, } = require('internal/options'); @@ -38,7 +30,6 @@ const { TextDecoder, TextEncoder } = require('internal/encoding'); const { codes: { ERR_ILLEGAL_CONSTRUCTOR, - ERR_INVALID_ARG_TYPE, ERR_INVALID_THIS, } } = require('internal/errors'); @@ -47,7 +38,6 @@ const { CryptoKey, InternalCryptoKey, createSecretKey, - isCryptoKey, } = require('internal/crypto/keys'); const { @@ -55,13 +45,11 @@ const { } = require('internal/crypto/hash'); const { - getArrayBufferOrView, getBlockSize, hasAnyNotIn, normalizeAlgorithm, normalizeHashName, validateMaxBufferLength, - kExportFormats, kHandle, kKeyObject, } = require('internal/crypto/util'); @@ -78,7 +66,22 @@ const { async function digest(algorithm, data) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - return ReflectApply(asyncDigest, this, arguments); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'digest' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 2, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: 'Argument 2', + }); + + algorithm = normalizeAlgorithm(algorithm, 'digest'); + + return ReflectApply(asyncDigest, this, [algorithm, data]); } function randomUUID() { @@ -91,9 +94,24 @@ async function generateKey( extractable, keyUsages) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - algorithm = normalizeAlgorithm(algorithm); - validateBoolean(extractable, 'extractable'); - validateArray(keyUsages, 'keyUsages'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'generateKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + extractable = webidl.converters.boolean(extractable, { + prefix, + context: 'Argument 2', + }); + keyUsages = webidl.converters['sequence'](keyUsages, { + prefix, + context: 'Argument 3', + }); + + algorithm = normalizeAlgorithm(algorithm, 'generateKey'); let result; let resultType; switch (algorithm.name) { @@ -160,9 +178,26 @@ async function generateKey( async function deriveBits(algorithm, baseKey, length) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - algorithm = normalizeAlgorithm(algorithm); - if (!isCryptoKey(baseKey)) - throw new ERR_INVALID_ARG_TYPE('baseKey', 'CryptoKey', baseKey); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + baseKey = webidl.converters.CryptoKey(baseKey, { + prefix, + context: 'Argument 2', + }); + if (length !== null) { + length = webidl.converters['unsigned long'](length, { + prefix, + context: 'Argument 3', + }); + } + + algorithm = normalizeAlgorithm(algorithm, 'deriveBits'); if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveBits')) { throw lazyDOMException( 'baseKey does not have deriveBits usage', @@ -221,10 +256,33 @@ async function deriveKey( extractable, keyUsages) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - algorithm = normalizeAlgorithm(algorithm); - derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm); - if (!isCryptoKey(baseKey)) - throw new ERR_INVALID_ARG_TYPE('baseKey', 'CryptoKey', baseKey); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'deriveKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 5, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + baseKey = webidl.converters.CryptoKey(baseKey, { + prefix, + context: 'Argument 2', + }); + derivedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(derivedKeyAlgorithm, { + prefix, + context: 'Argument 3', + }); + extractable = webidl.converters.boolean(extractable, { + prefix, + context: 'Argument 4', + }); + keyUsages = webidl.converters['sequence'](keyUsages, { + prefix, + context: 'Argument 5', + }); + + algorithm = normalizeAlgorithm(algorithm, 'deriveBits'); + derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm, 'importKey'); if (!ArrayPrototypeIncludes(baseKey.usages, 'deriveKey')) { throw lazyDOMException( 'baseKey does not have deriveKey usage', @@ -232,13 +290,8 @@ async function deriveKey( } if (baseKey.algorithm.name !== algorithm.name) throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError'); - validateObject(derivedKeyAlgorithm, 'derivedKeyAlgorithm', { - allowArray: true, allowFunction: true, - }); - validateBoolean(extractable, 'extractable'); - validateArray(keyUsages, 'keyUsages'); - const length = getKeyLength(derivedKeyAlgorithm); + const length = getKeyLength(normalizeAlgorithm(arguments[2], 'get key length')); let bits; switch (algorithm.name) { case 'X25519': @@ -446,10 +499,18 @@ async function exportKeyJWK(key) { async function exportKey(format, key) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - validateString(format, 'format'); - validateOneOf(format, 'format', kExportFormats); - if (!isCryptoKey(key)) - throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'exportKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 2, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: 'Argument 1', + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: 'Argument 2', + }); if (!key.extractable) throw lazyDOMException('key is not extractable', 'InvalidAccessException'); @@ -515,13 +576,32 @@ async function importKey( extractable, keyUsages) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - validateString(format, 'format'); - validateOneOf(format, 'format', kExportFormats); - if (format !== 'jwk') - keyData = getArrayBufferOrView(keyData, 'keyData'); - algorithm = normalizeAlgorithm(algorithm); - validateBoolean(extractable, 'extractable'); - validateArray(keyUsages, 'keyUsages'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'importKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 4, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: 'Argument 1', + }); + keyData = webidl.converters['BufferSource or JsonWebKey'](keyData, { + prefix, + context: 'Argument 2', + }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 3', + }); + extractable = webidl.converters.boolean(extractable, { + prefix, + context: 'Argument 4', + }); + keyUsages = webidl.converters['sequence'](keyUsages, { + prefix, + context: 'Argument 5', + }); + + algorithm = normalizeAlgorithm(algorithm, 'importKey'); switch (algorithm.name) { case 'RSASSA-PKCS1-v1_5': // Fall through @@ -574,7 +654,32 @@ async function importKey( // by a subtle.encrypt(). async function wrapKey(format, key, wrappingKey, algorithm) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - algorithm = normalizeAlgorithm(algorithm); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'wrapKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 4, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: 'Argument 1', + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: 'Argument 2', + }); + wrappingKey = webidl.converters.CryptoKey(wrappingKey, { + prefix, + context: 'Argument 3', + }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 4', + }); + + try { + algorithm = normalizeAlgorithm(algorithm, 'wrapKey'); + } catch { + algorithm = normalizeAlgorithm(algorithm, 'encrypt'); + } let keyData = await ReflectApply(exportKey, this, [format, key]); if (format === 'jwk') { @@ -604,8 +709,48 @@ async function unwrapKey( extractable, keyUsages) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); - wrappedKey = getArrayBufferOrView(wrappedKey, 'wrappedKey'); - unwrapAlgo = normalizeAlgorithm(unwrapAlgo); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'unwrapKey' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 7, { prefix }); + format = webidl.converters.KeyFormat(format, { + prefix, + context: 'Argument 1', + }); + wrappedKey = webidl.converters.BufferSource(wrappedKey, { + prefix, + context: 'Argument 2', + }); + unwrappingKey = webidl.converters.CryptoKey(unwrappingKey, { + prefix, + context: 'Argument 3', + }); + unwrapAlgo = webidl.converters.AlgorithmIdentifier(unwrapAlgo, { + prefix, + context: 'Argument 4', + }); + unwrappedKeyAlgo = webidl.converters.AlgorithmIdentifier( + unwrappedKeyAlgo, + { + prefix, + context: 'Argument 5', + }, + ); + extractable = webidl.converters.boolean(extractable, { + prefix, + context: 'Argument 6', + }); + keyUsages = webidl.converters['sequence'](keyUsages, { + prefix, + context: 'Argument 7', + }); + + try { + unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'unwrapKey'); + } catch { + unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'decrypt'); + } + let keyData = await cipherOrWrap( kWebCryptoCipherDecrypt, unwrapAlgo, @@ -633,15 +778,11 @@ async function unwrapKey( } function signVerify(algorithm, key, data, signature) { - algorithm = normalizeAlgorithm(algorithm); - if (!isCryptoKey(key)) - throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); - data = getArrayBufferOrView(data, 'data'); let usage = 'sign'; if (signature !== undefined) { - signature = getArrayBufferOrView(signature, 'signature'); usage = 'verify'; } + algorithm = normalizeAlgorithm(algorithm, usage); if (!ArrayPrototypeIncludes(key.usages, usage) || algorithm.name !== key.algorithm.name) { @@ -674,21 +815,56 @@ function signVerify(algorithm, key, data, signature) { async function sign(algorithm, key, data) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'sign' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: 'Argument 2', + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: 'Argument 3', + }); + return signVerify(algorithm, key, data); } async function verify(algorithm, key, signature, data) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'verify' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 4, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: 'Argument 2', + }); + signature = webidl.converters.BufferSource(signature, { + prefix, + context: 'Argument 3', + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: 'Argument 4', + }); + return signVerify(algorithm, key, data, signature); } async function cipherOrWrap(mode, algorithm, key, data, op) { - algorithm = normalizeAlgorithm(algorithm); // We use a Node.js style error here instead of a DOMException because // the WebCrypto spec is not specific what kind of error is to be thrown // in this case. Both Firefox and Chrome throw simple TypeErrors here. - if (!isCryptoKey(key)) - throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); // The key algorithm and cipher algorithm must match, and the // key must have the proper usage. if (key.algorithm.name !== algorithm.name || @@ -698,10 +874,6 @@ async function cipherOrWrap(mode, algorithm, key, data, op) { 'InvalidAccessError'); } - // For the Web Crypto API, the input data can be any ArrayBuffer, - // TypedArray, or DataView. - data = getArrayBufferOrView(data, 'data'); - // While WebCrypto allows for larger input buffer sizes, we limit // those to sizes that can fit within uint32_t because of limitations // in the OpenSSL API. @@ -729,11 +901,47 @@ async function cipherOrWrap(mode, algorithm, key, data, op) { async function encrypt(algorithm, key, data) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'encrypt' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: 'Argument 2', + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: 'Argument 3', + }); + + algorithm = normalizeAlgorithm(algorithm, 'encrypt'); return cipherOrWrap(kWebCryptoCipherEncrypt, algorithm, key, data, 'encrypt'); } async function decrypt(algorithm, key, data) { if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'decrypt' on 'SubtleCrypto'"; + webidl.requiredArguments(arguments.length, 3, { prefix }); + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: 'Argument 1', + }); + key = webidl.converters.CryptoKey(key, { + prefix, + context: 'Argument 2', + }); + data = webidl.converters.BufferSource(data, { + prefix, + context: 'Argument 3', + }); + + algorithm = normalizeAlgorithm(algorithm, 'decrypt'); return cipherOrWrap(kWebCryptoCipherDecrypt, algorithm, key, data, 'decrypt'); } @@ -761,6 +969,11 @@ const crypto = ReflectConstruct(function() {}, [], Crypto); function getRandomValues(array) { if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); + + const webidl = require('internal/crypto/webidl'); + const prefix = "Failed to execute 'getRandomValues' on 'Crypto'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + return ReflectApply(_getRandomValues, this, arguments); } diff --git a/lib/internal/crypto/webidl.js b/lib/internal/crypto/webidl.js new file mode 100644 index 00000000000000..08f75ab2ae3f3f --- /dev/null +++ b/lib/internal/crypto/webidl.js @@ -0,0 +1,752 @@ +'use strict'; + +// Adapted from the following sources +// - https://github.com/jsdom/webidl-conversions +// Copyright Domenic Denicola. Licensed under BSD-2-Clause License. +// Original license at https://github.com/jsdom/webidl-conversions/blob/master/LICENSE.md. +// - https://github.com/denoland/deno +// Copyright Deno authors. Licensed under MIT License. +// Original license at https://github.com/denoland/deno/blob/main/LICENSE.md. +// Changes include using primordials and stripping the code down to only what +// WebCryptoAPI needs. + +const { + ArrayBufferIsView, + ArrayBufferPrototype, + ArrayPrototypePush, + ArrayPrototypeSort, + globalThis, + MathPow, + MathTrunc, + Number, + NumberIsFinite, + ObjectAssign, + ObjectDefineProperty, + ObjectPrototypeIsPrototypeOf, + ReflectHas, + SafeArrayIterator, + SafeSet, + String, + SymbolIterator, + TypedArrayPrototypeGetSymbolToStringTag, + TypeError, +} = primordials; + +const { kEmptyObject } = require('internal/util'); + +const { CryptoKey } = require('internal/crypto/webcrypto'); + +const { SharedArrayBuffer } = globalThis; + +function codedTypeError(message, errorProperties = kEmptyObject) { + // eslint-disable-next-line no-restricted-syntax + const err = new TypeError(message); + ObjectAssign(err, errorProperties); + return err; +} + +function makeException(message, opts = kEmptyObject) { + return codedTypeError( + `${opts.prefix ? opts.prefix + ': ' : ''}${ + opts.context ? opts.context : 'Value' + } ${message}`, + opts.code ? { code: opts.code } : kEmptyObject, + ); +} + +function toNumber(value) { + if (typeof value === 'bigint') { + // eslint-disable-next-line no-restricted-syntax + throw new TypeError('Cannot convert a BigInt value to a number'); + } + return Number(value); +} + +function type(V) { + if (V === null) + return 'Null'; + + switch (typeof V) { + case 'undefined': + return 'Undefined'; + case 'boolean': + return 'Boolean'; + case 'number': + return 'Number'; + case 'string': + return 'String'; + case 'symbol': + return 'Symbol'; + case 'bigint': + return 'BigInt'; + case 'object': // Fall through + case 'function': // Fall through + default: + // Per ES spec, typeof returns an implemention-defined value that is not + // any of the existing ones for uncallable non-standard exotic objects. + // Yet Type() which the Web IDL spec depends on returns Object for such + // cases. So treat the default case as an object. + return 'Object'; + } +} + +const integerPart = MathTrunc; + +function censorNegativeZero(x) { + return x === 0 ? 0 : x; +} + +// This was updated to only consider bitlength up to 32 used by WebCryptoAPI +function createIntegerConversion(bitLength) { + const lowerBound = 0; + const upperBound = MathPow(2, bitLength) - 1; + + const twoToTheBitLength = MathPow(2, bitLength); + + return (V, opts = kEmptyObject) => { + let x = toNumber(V); + x = censorNegativeZero(x); + + if (opts.enforceRange) { + if (!NumberIsFinite(x)) { + throw makeException( + 'is not a finite number', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + + x = integerPart(x); + + if (x < lowerBound || x > upperBound) { + throw makeException( + `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`, + { ...opts, code: 'ERR_OUT_OF_RANGE' }, + ); + } + + return x; + } + + if (!NumberIsFinite(x) || x === 0) { + return 0; + } + + x = integerPart(x); + + if (x >= lowerBound && x <= upperBound) { + return x; + } + + x = x % twoToTheBitLength; + + return x; + }; +} + +const converters = {}; + +converters.boolean = (val) => !!val; +converters.octet = createIntegerConversion(8); +converters['unsigned short'] = createIntegerConversion(16); +converters['unsigned long'] = createIntegerConversion(32); + +converters.DOMString = function(V, opts = kEmptyObject) { + if (typeof V === 'string') { + return V; + } else if (typeof V === 'symbol') { + throw makeException( + 'is a symbol, which cannot be converted to a string', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + + return String(V); +}; + +converters.object = (V, opts) => { + if (type(V) !== 'Object') { + throw makeException( + 'is not an object', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + + return V; +}; + +function isNonSharedArrayBuffer(V) { + return ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V); +} + +function isSharedArrayBuffer(V) { + return ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V); +} + +converters.Uint8Array = (V, opts = kEmptyObject) => { + if (!ArrayBufferIsView(V) || + TypedArrayPrototypeGetSymbolToStringTag(V) !== 'Uint8Array') { + throw makeException( + 'is not an Uint8Array object', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + if (isSharedArrayBuffer(V.buffer)) { + throw makeException( + 'is a view on a SharedArrayBuffer, which is not allowed', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + + return V; +}; + +converters.BufferSource = (V, opts = kEmptyObject) => { + if (ArrayBufferIsView(V)) { + if (isSharedArrayBuffer(V.buffer)) { + throw makeException( + 'is a view on a SharedArrayBuffer, which is not allowed', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + + return V; + } + + if (!isNonSharedArrayBuffer(V)) { + throw makeException( + 'is not an ArrayBuffer or a view on one', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + + return V; +}; + +converters['sequence'] = createSequenceConverter( + converters.DOMString); + +function requiredArguments(length, required, opts = kEmptyObject) { + if (length < required) { + throw makeException( + `${required} argument${ + required === 1 ? '' : 's' + } required, but only ${length} present.`, + { ...opts, code: 'ERR_MISSING_ARGS' }); + } +} + +function createDictionaryConverter(name, ...dictionaries) { + let hasRequiredKey = false; + const allMembers = []; + for (const members of new SafeArrayIterator(dictionaries)) { + for (const member of new SafeArrayIterator(members)) { + if (member.required) { + hasRequiredKey = true; + } + ArrayPrototypePush(allMembers, member); + } + } + ArrayPrototypeSort(allMembers, (a, b) => { + if (a.key === b.key) { + return 0; + } + return a.key < b.key ? -1 : 1; + }); + + const defaultValues = {}; + for (const member of new SafeArrayIterator(allMembers)) { + if (ReflectHas(member, 'defaultValue')) { + const idlMemberValue = member.defaultValue; + const imvType = typeof idlMemberValue; + // Copy by value types can be directly assigned, copy by reference types + // need to be re-created for each allocation. + if ( + imvType === 'number' || + imvType === 'boolean' || + imvType === 'string' || + imvType === 'bigint' || + imvType === 'undefined' + ) { + defaultValues[member.key] = member.converter(idlMemberValue, {}); + } else { + ObjectDefineProperty(defaultValues, member.key, { + __proto__: null, + get() { + return member.converter(idlMemberValue, member.defaultValue); + }, + enumerable: true, + }); + } + } + } + + return function(V, opts = kEmptyObject) { + const typeV = type(V); + switch (typeV) { + case 'Undefined': + case 'Null': + case 'Object': + break; + default: + throw makeException( + 'can not be converted to a dictionary', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + const esDict = V; + + const idlDict = ObjectAssign({}, defaultValues); + + // Fast path null and undefined. + if (V == null && !hasRequiredKey) { + return idlDict; + } + + for (const member of new SafeArrayIterator(allMembers)) { + const key = member.key; + + let esMemberValue; + if (typeV === 'Undefined' || typeV === 'Null') { + esMemberValue = undefined; + } else { + esMemberValue = esDict[key]; + } + + if (esMemberValue !== undefined) { + const context = `'${key}' of '${name}'${ + opts.context ? ` (${opts.context})` : '' + }`; + const converter = member.converter; + const idlMemberValue = converter(esMemberValue, { ...opts, context }); + idlDict[key] = idlMemberValue; + } else if (member.required) { + throw makeException( + `can not be converted to '${name}' because '${key}' is required in '${name}'.`, + { ...opts, code: 'ERR_MISSING_OPTION' }); + } + } + + return idlDict; + }; +} + +function createEnumConverter(name, values) { + const E = new SafeSet(values); + + return function(V, opts = kEmptyObject) { + const S = String(V); + + if (!E.has(S)) { + throw makeException( + `The provided value '${S}' is not a valid enum value of type ${name}.`, + { ...opts, code: 'ERR_INVALID_ARG_VALUE' }); + } + + return S; + }; +} + +function createSequenceConverter(converter) { + return function(V, opts = kEmptyObject) { + if (type(V) !== 'Object') { + throw makeException( + 'can not be converted to sequence.', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + const iter = V?.[SymbolIterator]?.(); + if (iter === undefined) { + throw makeException( + 'can not be converted to sequence.', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + const array = []; + while (true) { + const res = iter?.next?.(); + if (res === undefined) { + throw makeException( + 'can not be converted to sequence.', + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + if (res.done === true) break; + const val = converter(res.value, { + ...opts, + context: `${opts.context}, index ${array.length}`, + }); + ArrayPrototypePush(array, val); + } + return array; + }; +} + +function createInterfaceConverter(name, prototype) { + return (V, opts) => { + if (!ObjectPrototypeIsPrototypeOf(prototype, V)) { + throw makeException( + `is not of type ${name}.`, + { ...opts, code: 'ERR_INVALID_ARG_TYPE' }); + } + return V; + }; +} + +converters.AlgorithmIdentifier = (V, opts) => { + // Union for (object or DOMString) + if (type(V) === 'Object') { + return converters.object(V, opts); + } + return converters.DOMString(V, opts); +}; + +converters['BufferSource or JsonWebKey'] = (V, opts) => { + // Union for (BufferSource or JsonWebKey) + if ( + ArrayBufferIsView(V) || + ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) + ) { + return converters.BufferSource(V, opts); + } + return converters.JsonWebKey(V, opts); +}; + +converters.KeyType = createEnumConverter('KeyType', [ + 'public', + 'private', + 'secret', +]); + +converters.KeyFormat = createEnumConverter('KeyFormat', [ + 'raw', + 'pkcs8', + 'spki', + 'jwk', +]); + +converters.KeyUsage = createEnumConverter('KeyUsage', [ + 'encrypt', + 'decrypt', + 'sign', + 'verify', + 'deriveKey', + 'deriveBits', + 'wrapKey', + 'unwrapKey', +]); + +converters['sequence'] = createSequenceConverter(converters.KeyUsage); + +converters.HashAlgorithmIdentifier = converters.AlgorithmIdentifier; + +const dictAlgorithm = [ + { + key: 'name', + converter: converters.DOMString, + required: true, + }, +]; + +converters.Algorithm = createDictionaryConverter( + 'Algorithm', dictAlgorithm); + +converters.BigInteger = converters.Uint8Array; + +const dictRsaKeyGenParams = [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'modulusLength', + converter: (V, opts) => + converters['unsigned long'](V, { ...opts, enforceRange: true }), + required: true, + }, + { + key: 'publicExponent', + converter: converters.BigInteger, + required: true, + }, +]; + +converters.RsaKeyGenParams = createDictionaryConverter( + 'RsaKeyGenParams', dictRsaKeyGenParams); + +converters.RsaHashedKeyGenParams = createDictionaryConverter( + 'RsaHashedKeyGenParams', [ + ...new SafeArrayIterator(dictRsaKeyGenParams), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + ]); + +converters.RsaHashedImportParams = createDictionaryConverter( + 'RsaHashedImportParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + ]); + +converters.NamedCurve = converters.DOMString; + +converters.EcKeyImportParams = createDictionaryConverter( + 'EcKeyImportParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'namedCurve', + converter: converters.NamedCurve, + required: true, + }, + ]); + +converters.EcKeyGenParams = createDictionaryConverter( + 'EcKeyGenParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'namedCurve', + converter: converters.NamedCurve, + required: true, + }, + ]); + +converters.AesKeyGenParams = createDictionaryConverter( + 'AesKeyGenParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'length', + converter: (V, opts) => + converters['unsigned short'](V, { ...opts, enforceRange: true }), + required: true, + }, + ]); + +converters.HmacKeyGenParams = createDictionaryConverter( + 'HmacKeyGenParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + { + key: 'length', + converter: (V, opts) => + converters['unsigned long'](V, { ...opts, enforceRange: true }), + }, + ]); + +converters.RsaPssParams = createDictionaryConverter( + 'RsaPssParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'saltLength', + converter: (V, opts) => + converters['unsigned long'](V, { ...opts, enforceRange: true }), + required: true, + }, + ]); + +converters.RsaOaepParams = createDictionaryConverter( + 'RsaOaepParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'label', + converter: converters.BufferSource, + }, + ]); + +converters.EcdsaParams = createDictionaryConverter( + 'EcdsaParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + ]); + +converters.HmacImportParams = createDictionaryConverter( + 'HmacImportParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + { + key: 'length', + converter: (V, opts) => + converters['unsigned long'](V, { ...opts, enforceRange: true }), + }, + ]); + +const simpleDomStringKey = (key) => ({ key, converter: converters.DOMString }); + +converters.RsaOtherPrimesInfo = createDictionaryConverter( + 'RsaOtherPrimesInfo', [ + simpleDomStringKey('r'), + simpleDomStringKey('d'), + simpleDomStringKey('t'), + ]); +converters['sequence'] = createSequenceConverter( + converters.RsaOtherPrimesInfo); + +converters.JsonWebKey = createDictionaryConverter( + 'JsonWebKey', [ + simpleDomStringKey('kty'), + simpleDomStringKey('use'), + { + key: 'key_ops', + converter: converters['sequence'], + }, + simpleDomStringKey('alg'), + { + key: 'ext', + converter: converters.boolean, + }, + simpleDomStringKey('crv'), + simpleDomStringKey('x'), + simpleDomStringKey('y'), + simpleDomStringKey('d'), + simpleDomStringKey('n'), + simpleDomStringKey('e'), + simpleDomStringKey('p'), + simpleDomStringKey('q'), + simpleDomStringKey('dp'), + simpleDomStringKey('dq'), + simpleDomStringKey('qi'), + { + key: 'oth', + converter: converters['sequence'], + }, + simpleDomStringKey('k'), + ]); + +converters.HkdfParams = createDictionaryConverter( + 'HkdfParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + { + key: 'salt', + converter: converters.BufferSource, + required: true, + }, + { + key: 'info', + converter: converters.BufferSource, + required: true, + }, + ]); + +converters.Pbkdf2Params = createDictionaryConverter( + 'Pbkdf2Params', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'hash', + converter: converters.HashAlgorithmIdentifier, + required: true, + }, + { + key: 'iterations', + converter: (V, opts) => + converters['unsigned long'](V, { ...opts, enforceRange: true }), + required: true, + }, + { + key: 'salt', + converter: converters.BufferSource, + required: true, + }, + ]); + +converters.AesDerivedKeyParams = createDictionaryConverter( + 'AesDerivedKeyParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'length', + converter: (V, opts) => + converters['unsigned short'](V, { ...opts, enforceRange: true }), + required: true, + }, + ]); + +converters.AesCbcParams = createDictionaryConverter( + 'AesCbcParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'iv', + converter: converters.BufferSource, + required: true, + }, + ]); + +converters.AesGcmParams = createDictionaryConverter( + 'AesGcmParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'iv', + converter: converters.BufferSource, + required: true, + }, + { + key: 'tagLength', + converter: (V, opts) => + converters.octet(V, { ...opts, enforceRange: true }), + }, + { + key: 'additionalData', + converter: converters.BufferSource, + }, + ]); + +converters.AesCtrParams = createDictionaryConverter( + 'AesCtrParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'counter', + converter: converters.BufferSource, + required: true, + }, + { + key: 'length', + converter: (V, opts) => + converters.octet(V, { ...opts, enforceRange: true }), + required: true, + }, + ]); + +converters.CryptoKey = createInterfaceConverter( + 'CryptoKey', CryptoKey.prototype); + +converters.CryptoKeyPair = createDictionaryConverter( + 'CryptoKeyPair', [ + { + key: 'publicKey', + converter: converters.CryptoKey, + }, + { + key: 'privateKey', + converter: converters.CryptoKey, + }, + ]); + +converters.EcdhKeyDeriveParams = createDictionaryConverter( + 'EcdhKeyDeriveParams', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'public', + converter: converters.CryptoKey, + required: true, + }, + ]); + +converters.Ed448Params = createDictionaryConverter( + 'Ed448Params', [ + ...new SafeArrayIterator(dictAlgorithm), + { + key: 'context', + converter: converters.BufferSource, + required: false, + }, + ]); + +module.exports = { + converters, + requiredArguments, +}; diff --git a/test/fixtures/crypto/aes_gcm.js b/test/fixtures/crypto/aes_gcm.js index 07332d44d9e16d..5d6ca46325b6f3 100644 --- a/test/fixtures/crypto/aes_gcm.js +++ b/test/fixtures/crypto/aes_gcm.js @@ -115,7 +115,7 @@ module.exports = function() { const failing = []; kKeyLengths.forEach((keyLength) => { - [24, 48, 72, 95, 129, 256].forEach((badTagLength) => { + [24, 48, 72, 95, 129].forEach((badTagLength) => { failing.push({ keyBuffer: kKeyBytes[keyLength], algorithm: { diff --git a/test/parallel/test-webcrypto-cryptokey-workers.js b/test/parallel/test-webcrypto-cryptokey-workers.js index 4f800fdb9b6871..4de221ec6e822a 100644 --- a/test/parallel/test-webcrypto-cryptokey-workers.js +++ b/test/parallel/test-webcrypto-cryptokey-workers.js @@ -25,7 +25,7 @@ const sig = '13691a79fb55a0417e4d6699a32f91ad29283fa2c1439865cc0632931f4f48dc'; async function doSig(key) { const signature = await subtle.sign({ name: 'HMAC' - }, key, 'some data'); + }, key, Buffer.from('some data')); assert.strictEqual(Buffer.from(signature).toString('hex'), sig); } diff --git a/test/parallel/test-webcrypto-derivebits-cfrg.js b/test/parallel/test-webcrypto-derivebits-cfrg.js index 104ee5f1a9f3b9..d92d2a18610767 100644 --- a/test/parallel/test-webcrypto-derivebits-cfrg.js +++ b/test/parallel/test-webcrypto-derivebits-cfrg.js @@ -144,7 +144,7 @@ async function prepareKeys() { { name: 'X448' }, keys.X448.privateKey, 8 * keys.X448.size), - { code: 'ERR_INVALID_ARG_TYPE' }); + { code: 'ERR_MISSING_OPTION' }); } { diff --git a/test/parallel/test-webcrypto-derivebits-ecdh.js b/test/parallel/test-webcrypto-derivebits-ecdh.js index 9405dbc2d7b8bd..d28899f7f912d7 100644 --- a/test/parallel/test-webcrypto-derivebits-ecdh.js +++ b/test/parallel/test-webcrypto-derivebits-ecdh.js @@ -165,7 +165,7 @@ async function prepareKeys() { { name: 'ECDH' }, keys['P-384'].privateKey, 8 * keys['P-384'].size), - { code: 'ERR_INVALID_ARG_TYPE' }); + { code: 'ERR_MISSING_OPTION' }); } { diff --git a/test/parallel/test-webcrypto-derivebits-hkdf.js b/test/parallel/test-webcrypto-derivebits-hkdf.js index 670a5e0a953b75..73ea97a0d12774 100644 --- a/test/parallel/test-webcrypto-derivebits-hkdf.js +++ b/test/parallel/test-webcrypto-derivebits-hkdf.js @@ -343,7 +343,7 @@ async function testDeriveBitsMissingSalt( return assert.rejects( subtle.deriveBits(algorithm, baseKeys[size], 0), { - code: 'ERR_INVALID_ARG_TYPE' + code: 'ERR_MISSING_OPTION' }); } @@ -361,7 +361,7 @@ async function testDeriveBitsMissingInfo( return assert.rejects( subtle.deriveBits(algorithm, baseKeys[size], 0), { - code: 'ERR_INVALID_ARG_TYPE' + code: 'ERR_MISSING_OPTION' }); } diff --git a/test/parallel/test-webcrypto-derivekey-cfrg.js b/test/parallel/test-webcrypto-derivekey-cfrg.js index 08897aed1baa34..213463355ff758 100644 --- a/test/parallel/test-webcrypto-derivekey-cfrg.js +++ b/test/parallel/test-webcrypto-derivekey-cfrg.js @@ -110,7 +110,7 @@ async function prepareKeys() { { name: 'X448' }, keys.X448.privateKey, ...otherArgs), - { code: 'ERR_INVALID_ARG_TYPE' }); + { code: 'ERR_MISSING_OPTION' }); } { diff --git a/test/parallel/test-webcrypto-derivekey-ecdh.js b/test/parallel/test-webcrypto-derivekey-ecdh.js index 2afec3db26b34b..4f801da75afdf0 100644 --- a/test/parallel/test-webcrypto-derivekey-ecdh.js +++ b/test/parallel/test-webcrypto-derivekey-ecdh.js @@ -127,7 +127,7 @@ async function prepareKeys() { { name: 'ECDH' }, keys['P-384'].privateKey, ...otherArgs), - { code: 'ERR_INVALID_ARG_TYPE' }); + { code: 'ERR_MISSING_OPTION' }); } { diff --git a/test/parallel/test-webcrypto-digest.js b/test/parallel/test-webcrypto-digest.js index ea8c258cf1c2ed..7e8ad8671b5440 100644 --- a/test/parallel/test-webcrypto-digest.js +++ b/test/parallel/test-webcrypto-digest.js @@ -66,11 +66,11 @@ const kData = (new TextEncoder()).encode('hello'); })); })().then(common.mustCall()); -Promise.all([1, [], {}, null, undefined].map((i) => - assert.rejects(subtle.digest(i), { message: /Unrecognized name/ }) +Promise.all([1, null, undefined].map((i) => + assert.rejects(subtle.digest(i, Buffer.alloc(0)), { message: /Unrecognized name/ }) )).then(common.mustCall()); -assert.rejects(subtle.digest(''), { message: /Unrecognized name/ }).then(common.mustCall()); +assert.rejects(subtle.digest('', Buffer.alloc(0)), { message: /Unrecognized name/ }).then(common.mustCall()); Promise.all([1, [], {}, null, undefined].map((i) => assert.rejects(subtle.digest('SHA-256', i), { @@ -78,14 +78,6 @@ Promise.all([1, [], {}, null, undefined].map((i) => }) )).then(common.mustCall()); -// If there is a mismatch between length and the expected digest size for -// the selected algorithm, we fail. The length is a Node.js specific -// addition to the API, and is added as a support for future additional -// hash algorithms that support variable digest output lengths. -assert.rejects(subtle.digest({ name: 'SHA-512', length: 510 }, kData), { - name: 'OperationError', -}).then(common.mustCall()); - const kSourceData = { empty: '', short: '156eea7cc14c56cb94db030a4a9d95ff', diff --git a/test/parallel/test-webcrypto-export-import.js b/test/parallel/test-webcrypto-export-import.js index 23940464b81246..32467e0489b5ca 100644 --- a/test/parallel/test-webcrypto-export-import.js +++ b/test/parallel/test-webcrypto-export-import.js @@ -14,19 +14,17 @@ const { subtle } = globalThis.crypto; await Promise.all([1, null, undefined, {}, []].map((format) => assert.rejects( subtle.importKey(format, keyData, {}, false, ['wrapKey']), { - code: 'ERR_INVALID_ARG_TYPE' + code: 'ERR_INVALID_ARG_VALUE' }) )); await assert.rejects( subtle.importKey('not valid', keyData, {}, false, ['wrapKey']), { code: 'ERR_INVALID_ARG_VALUE' }); - await Promise.all([1, null, undefined, {}, []].map((keyData) => - assert.rejects( - subtle.importKey('raw', keyData, {}, false, ['deriveBits']), { - code: 'ERR_INVALID_ARG_TYPE' - }) - )); + await assert.rejects( + subtle.importKey('raw', 1, {}, false, ['deriveBits']), { + code: 'ERR_INVALID_ARG_TYPE' + }); await assert.rejects( subtle.importKey('raw', keyData, { name: 'HMAC' @@ -65,7 +63,7 @@ const { subtle } = globalThis.crypto; hash: 'SHA-256', }, false, ['sign', 'verify']), { name: 'DataError', - message: 'Invalid JWK keyData' + message: 'Invalid key type' }); } diff --git a/test/parallel/test-webcrypto-keygen.js b/test/parallel/test-webcrypto-keygen.js index 7cb02c9863f4eb..a949c0ae2b8a7d 100644 --- a/test/parallel/test-webcrypto-keygen.js +++ b/test/parallel/test-webcrypto-keygen.js @@ -163,7 +163,7 @@ const vectors = { return assert.rejects( // The extractable and usages values are invalid here also, // but the unrecognized algorithm name should be caught first. - subtle.generateKey(algorithm, 7, ['zebra']), { + subtle.generateKey(algorithm, 7, []), { message: /Unrecognized name/ }); } @@ -299,12 +299,12 @@ const vectors = { // Missing parameters await assert.rejects( subtle.generateKey({ name, publicExponent, hash }, true, usages), { - code: 'ERR_INVALID_ARG_TYPE' + code: 'ERR_MISSING_OPTION' }); await assert.rejects( subtle.generateKey({ name, modulusLength, hash }, true, usages), { - code: 'ERR_INVALID_ARG_TYPE' + code: 'ERR_MISSING_OPTION' }); await assert.rejects( @@ -312,7 +312,7 @@ const vectors = { code: 'ERR_MISSING_OPTION' }); - await Promise.all(['', true, {}].map((modulusLength) => { + await Promise.all([{}].map((modulusLength) => { return assert.rejects(subtle.generateKey({ name, modulusLength, @@ -338,7 +338,7 @@ const vectors = { { code: 'ERR_INVALID_ARG_TYPE' }); })); - await Promise.all([true, {}, 1, []].map((hash) => { + await Promise.all([true, 1].map((hash) => { return assert.rejects(subtle.generateKey({ name, modulusLength, @@ -349,17 +349,6 @@ const vectors = { }); })); - await Promise.all(['', {}, 1, []].map((extractable) => { - return assert.rejects(subtle.generateKey({ - name, - modulusLength, - publicExponent, - hash - }, extractable, usages), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - await Promise.all(['', {}, 1, false].map((usages) => { return assert.rejects(subtle.generateKey({ name, @@ -449,12 +438,17 @@ const vectors = { assert.strictEqual(privateKey.algorithm.namedCurve, namedCurve); // Invalid parameters - [1, true, {}, [], undefined, null].forEach(async (namedCurve) => { + [1, true, {}, [], null].forEach(async (namedCurve) => { await assert.rejects( subtle.generateKey({ name, namedCurve }, true, privateUsages), { name: 'NotSupportedError' }); }); + await assert.rejects( + subtle.generateKey({ name, namedCurve: undefined }, true, privateUsages), { + name: 'TypeError', + code: 'ERR_MISSING_OPTION' + }); } const kTests = [ @@ -509,19 +503,18 @@ const vectors = { assert.strictEqual(key.algorithm.length, length); // Invalid parameters - [1, 100, 257].forEach(async (length) => { + [1, 100, 257, '', false, null].forEach(async (length) => { await assert.rejects( subtle.generateKey({ name, length }, true, usages), { name: 'OperationError' }); }); - ['', {}, [], false, null, undefined].forEach(async (length) => { - await assert.rejects( - subtle.generateKey({ name, length }, true, usages), { - name: 'OperationError', - }); - }); + await assert.rejects( + subtle.generateKey({ name, length: undefined }, true, usages), { + name: 'TypeError', + code: 'ERR_MISSING_OPTION' + }); } const kTests = [ @@ -568,14 +561,7 @@ const vectors = { assert.strictEqual(key.algorithm.length, length); assert.strictEqual(key.algorithm.hash.name, hash); - ['', {}, [], false, null].forEach(async (length) => { - await assert.rejects( - subtle.generateKey({ name: 'HMAC', length, hash }, true, usages), { - code: 'ERR_INVALID_ARG_TYPE' - }); - }); - - [1, {}, [], false, null].forEach(async (hash) => { + [1, false, null].forEach(async (hash) => { await assert.rejects( subtle.generateKey({ name: 'HMAC', length, hash }, true, usages), { message: /Unrecognized name/ diff --git a/test/parallel/test-webcrypto-sign-verify-hmac.js b/test/parallel/test-webcrypto-sign-verify-hmac.js index 9a5ed318e72fd7..98cf61f5c9d179 100644 --- a/test/parallel/test-webcrypto-sign-verify-hmac.js +++ b/test/parallel/test-webcrypto-sign-verify-hmac.js @@ -91,16 +91,14 @@ async function testVerify({ hash, // Test failure when wrong hash is used { const otherhash = hash === 'SHA-1' ? 'SHA-256' : 'SHA-1'; - assert(!(await subtle.verify({ - name, - hash: otherhash - }, key, signature, copy))); + const keyWithOtherHash = await subtle.importKey( + 'raw', + keyBuffer, + { name, hash: otherhash }, + false, + ['verify']); + assert(!(await subtle.verify({ name }, keyWithOtherHash, signature, plaintext))); } - - await assert.rejects( - subtle.verify({ name, hash: 'sha256' }, key, signature, copy), { - message: /Unrecognized name/ - }); } async function testSign({ hash, @@ -156,7 +154,6 @@ async function testSign({ hash, subtle.generateKey({ name }, false, ['sign', 'verify']), { name: 'TypeError', code: 'ERR_MISSING_OPTION', - message: 'algorithm.hash is required' }); // Test failure when no sign usage diff --git a/test/parallel/test-webcrypto-sign-verify-rsa.js b/test/parallel/test-webcrypto-sign-verify-rsa.js index bd727f2f998fa7..6057d2f6ef9325 100644 --- a/test/parallel/test-webcrypto-sign-verify-rsa.js +++ b/test/parallel/test-webcrypto-sign-verify-rsa.js @@ -112,19 +112,14 @@ async function testVerify({ // Test failure when wrong hash is used { const otherhash = hash === 'SHA-1' ? 'SHA-256' : 'SHA-1'; - assert(!(await subtle.verify({ - ...algorithm, - hash: otherhash - }, publicKey, signature, copy))); + const keyWithOtherHash = await subtle.importKey( + 'spki', + publicKeyBuffer, + { name: algorithm.name, hash: otherhash }, + false, + ['verify']); + assert(!(await subtle.verify(algorithm, keyWithOtherHash, signature, plaintext))); } - - await assert.rejects( - subtle.verify( - { ...algorithm, hash: 'sha256' }, - publicKey, - signature, - copy), - { message: /Unrecognized name/ }); } async function testSign({ diff --git a/test/parallel/test-webcrypto-util.js b/test/parallel/test-webcrypto-util.js index 4bb14a7f91494f..b8d5361433fd0e 100644 --- a/test/parallel/test-webcrypto-util.js +++ b/test/parallel/test-webcrypto-util.js @@ -11,15 +11,9 @@ const { normalizeAlgorithm, } = require('internal/crypto/util'); -{ - // Check that normalizeAlgorithm does not add an undefined hash property. - assert.strictEqual('hash' in normalizeAlgorithm({ name: 'ECDH' }), false); - assert.strictEqual('hash' in normalizeAlgorithm('ECDH'), false); -} - { // Check that normalizeAlgorithm does not mutate object inputs. - const algorithm = { name: 'ECDH', hash: 'SHA-256' }; - assert.strictEqual(normalizeAlgorithm(algorithm) !== algorithm, true); - assert.deepStrictEqual(algorithm, { name: 'ECDH', hash: 'SHA-256' }); + const algorithm = { name: 'ECDSA', hash: 'SHA-256' }; + assert.strictEqual(normalizeAlgorithm(algorithm, 'sign') !== algorithm, true); + assert.deepStrictEqual(algorithm, { name: 'ECDSA', hash: 'SHA-256' }); } diff --git a/test/pummel/test-webcrypto-derivebits-pbkdf2.js b/test/pummel/test-webcrypto-derivebits-pbkdf2.js index 243856d372774a..e0f65536fc4ab2 100644 --- a/test/pummel/test-webcrypto-derivebits-pbkdf2.js +++ b/test/pummel/test-webcrypto-derivebits-pbkdf2.js @@ -382,7 +382,7 @@ async function setupBaseKeys() { promises.push( subtle.importKey( 'raw', - kPasswords[size], + Buffer.from(kPasswords[size], 'hex'), { name: 'PBKDF2' }, false, ['deriveBits']) @@ -391,7 +391,7 @@ async function setupBaseKeys() { promises.push( subtle.importKey( 'raw', - kPasswords[size], + Buffer.from(kPasswords[size], 'hex'), { name: 'PBKDF2' }, false, ['deriveKey']) @@ -474,7 +474,7 @@ async function testDeriveBitsBadHash( hash, iterations) { const salt = Buffer.from(kSalts[saltSize], 'hex'); - const algorithm = { name: 'HKDF', salt, iterations }; + const algorithm = { name: 'PBKDF2', salt, iterations }; return Promise.all([ assert.rejects( diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json index 9f9ba93240be25..27d6825a6fa7d5 100644 --- a/test/wpt/status/WebCryptoAPI.json +++ b/test/wpt/status/WebCryptoAPI.json @@ -1,4 +1,17 @@ { + "encrypt_decrypt/aes_gcm.https.any.js": { + "fail": { + "note": "We're throwing correct error for the WebIDL definition, WPT needs update: https://github.com/web-platform-tests/wpt/pull/37734", + "expected": [ + "AES-GCM 128-bit key, illegal tag length 256-bits", + "AES-GCM 192-bit key, illegal tag length 256-bits", + "AES-GCM 256-bit key, illegal tag length 256-bits", + "AES-GCM 128-bit key, illegal tag length 256-bits decryption", + "AES-GCM 192-bit key, illegal tag length 256-bits decryption", + "AES-GCM 256-bit key, illegal tag length 256-bits decryption" + ] + } + }, "algorithm-discards-context.https.window.js": { "skip": "Not relevant in Node.js context" }, From 03c0459554d86cef9b970857338cd34d63355c88 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 3 Jan 2023 11:26:06 +0100 Subject: [PATCH 02/27] add docs change entry --- doc/api/webcrypto.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/api/webcrypto.md b/doc/api/webcrypto.md index 035d8de3e30996..1c943063c72888 100644 --- a/doc/api/webcrypto.md +++ b/doc/api/webcrypto.md @@ -2,6 +2,10 @@