diff --git a/doc/api/errors.md b/doc/api/errors.md
index 5dbb2106163810..350a9260cdbdc0 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -3104,7 +3104,7 @@ The context must be a `SecureContext`.
### `ERR_TLS_INVALID_PROTOCOL_METHOD`
-The specified `secureProtocol` method is invalid. It is either unknown, or
+The specified `secureProtocol` method is invalid. It is either unknown, or
disabled because it is insecure.
diff --git a/doc/api/webcrypto.md b/doc/api/webcrypto.md
index f357ebb6ae0282..164b8569fb5299 100644
--- a/doc/api/webcrypto.md
+++ b/doc/api/webcrypto.md
@@ -850,7 +850,7 @@ The algorithms currently supported include:
* `'ML-KEM-768'`[^modern-algos]
* `'ML-KEM-1024'`[^modern-algos]
-### `subtle.decapsulateKey(decapsulationAlgorithm, decapsulationKey, ciphertext, sharedKeyAlgorithm, extractable, usages)`
+### `subtle.decapsulateKey(decapsulationAlgorithm, decapsulationKey, ciphertext, sharedKeyAlgorithm, extractable, keyUsages)`
-* `unwrapAlgo` {string|Algorithm|RsaOaepParams|AesCtrParams|AesCbcParams|AeadParams}
-* `unwrappedKeyAlgo` {string|Algorithm|RsaHashedImportParams|EcKeyImportParams|HmacImportParams|KmacImportParams}
+* `unwrapAlgorithm` {string|Algorithm|RsaOaepParams|AesCtrParams|AesCbcParams|AeadParams}
+* `unwrappedKeyAlgorithm` {string|Algorithm|RsaHashedImportParams|EcKeyImportParams|HmacImportParams|KmacImportParams}
@@ -1452,8 +1452,8 @@ In cryptography, "wrapping a key" refers to exporting and then encrypting the
keying material. This method attempts to decrypt a wrapped
key and create a {CryptoKey} instance. It is equivalent to calling
[`subtle.decrypt()`][] first on the encrypted key data (using the `wrappedKey`,
-`unwrapAlgo`, and `unwrappingKey` arguments as input) then passing the results
-to the [`subtle.importKey()`][] method using the `unwrappedKeyAlgo`,
+`unwrapAlgorithm`, and `unwrappingKey` arguments as input) then passing the results
+to the [`subtle.importKey()`][] method using the `unwrappedKeyAlgorithm`,
`extractable`, and `keyUsages` arguments as inputs. If successful, the returned
promise is resolved with a {CryptoKey} object.
@@ -1541,7 +1541,7 @@ The algorithms currently supported include:
* `'RSA-PSS'`
* `'RSASSA-PKCS1-v1_5'`
-### `subtle.wrapKey(format, key, wrappingKey, wrapAlgo)`
+### `subtle.wrapKey(format, key, wrappingKey, wrapAlgorithm)`
@@ -1568,10 +1568,10 @@ changes:
In cryptography, "wrapping a key" refers to exporting and then encrypting the
keying material. This method exports the keying material into
the format identified by `format`, then encrypts it using the method and
-parameters specified by `wrapAlgo` and the keying material provided by
+parameters specified by `wrapAlgorithm` and the keying material provided by
`wrappingKey`. It is the equivalent to calling [`subtle.exportKey()`][] using
`format` and `key` as the arguments, then passing the result to the
-[`subtle.encrypt()`][] method using `wrappingKey` and `wrapAlgo` as inputs. If
+[`subtle.encrypt()`][] method using `wrappingKey` and `wrapAlgorithm` as inputs. If
successful, the returned promise will be resolved with an {ArrayBuffer}
containing the encrypted key data.
@@ -2815,19 +2815,19 @@ added:
[Web Crypto API]: https://www.w3.org/TR/WebCryptoAPI/
[`SubtleCrypto.supports()`]: #static-method-subtlecryptosupportsoperation-algorithm-lengthoradditionalalgorithm
[`subtle.decapsulateBits()`]: #subtledecapsulatebitsdecapsulationalgorithm-decapsulationkey-ciphertext
-[`subtle.decapsulateKey()`]: #subtledecapsulatekeydecapsulationalgorithm-decapsulationkey-ciphertext-sharedkeyalgorithm-extractable-usages
+[`subtle.decapsulateKey()`]: #subtledecapsulatekeydecapsulationalgorithm-decapsulationkey-ciphertext-sharedkeyalgorithm-extractable-keyusages
[`subtle.decrypt()`]: #subtledecryptalgorithm-key-data
[`subtle.deriveBits()`]: #subtlederivebitsalgorithm-basekey-length
-[`subtle.deriveKey()`]: #subtlederivekeyalgorithm-basekey-derivedkeyalgorithm-extractable-keyusages
+[`subtle.deriveKey()`]: #subtlederivekeyalgorithm-basekey-derivedkeytype-extractable-keyusages
[`subtle.digest()`]: #subtledigestalgorithm-data
[`subtle.encapsulateBits()`]: #subtleencapsulatebitsencapsulationalgorithm-encapsulationkey
-[`subtle.encapsulateKey()`]: #subtleencapsulatekeyencapsulationalgorithm-encapsulationkey-sharedkeyalgorithm-extractable-usages
+[`subtle.encapsulateKey()`]: #subtleencapsulatekeyencapsulationalgorithm-encapsulationkey-sharedkeyalgorithm-extractable-keyusages
[`subtle.encrypt()`]: #subtleencryptalgorithm-key-data
[`subtle.exportKey()`]: #subtleexportkeyformat-key
[`subtle.generateKey()`]: #subtlegeneratekeyalgorithm-extractable-keyusages
[`subtle.getPublicKey()`]: #subtlegetpublickeykey-keyusages
[`subtle.importKey()`]: #subtleimportkeyformat-keydata-algorithm-extractable-keyusages
[`subtle.sign()`]: #subtlesignalgorithm-key-data
-[`subtle.unwrapKey()`]: #subtleunwrapkeyformat-wrappedkey-unwrappingkey-unwrapalgo-unwrappedkeyalgo-extractable-keyusages
+[`subtle.unwrapKey()`]: #subtleunwrapkeyformat-wrappedkey-unwrappingkey-unwrapalgorithm-unwrappedkeyalgorithm-extractable-keyusages
[`subtle.verify()`]: #subtleverifyalgorithm-key-signature-data
-[`subtle.wrapKey()`]: #subtlewrapkeyformat-key-wrappingkey-wrapalgo
+[`subtle.wrapKey()`]: #subtlewrapkeyformat-key-wrappingkey-wrapalgorithm
diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js
index 2ed6c69f43e3d4..981502c51700be 100644
--- a/lib/internal/crypto/aes.js
+++ b/lib/internal/crypto/aes.js
@@ -175,14 +175,14 @@ function aesCipher(mode, key, data, algorithm) {
}
}
-function aesGenerateKey(algorithm, extractable, keyUsages) {
+function aesGenerateKey(algorithm, extractable, usages) {
const { name, length } = algorithm;
const checkUsages = ['wrapKey', 'unwrapKey'];
if (name !== 'AES-KW')
ArrayPrototypePush(checkUsages, 'encrypt', 'decrypt');
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
if (hasAnyNotIn(usagesSet, checkUsages)) {
throw lazyDOMException(
'Unsupported key usage for an AES key',
@@ -207,13 +207,13 @@ function aesImportKey(
format,
keyData,
extractable,
- keyUsages) {
+ usages) {
const { name } = algorithm;
const checkUsages = ['wrapKey', 'unwrapKey'];
if (name !== 'AES-KW')
ArrayPrototypePush(checkUsages, 'encrypt', 'decrypt');
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
if (hasAnyNotIn(usagesSet, checkUsages)) {
throw lazyDOMException(
'Unsupported key usage for an AES key',
diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js
index 98b1862b7ad1f8..3e6152b1f55501 100644
--- a/lib/internal/crypto/cfrg.js
+++ b/lib/internal/crypto/cfrg.js
@@ -73,10 +73,10 @@ function verifyAcceptableCfrgKeyUse(name, isPublic, usages) {
}
}
-function cfrgGenerateKey(algorithm, extractable, keyUsages) {
+function cfrgGenerateKey(algorithm, extractable, usages) {
const { name } = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
switch (name) {
case 'Ed25519':
// Fall through
@@ -170,11 +170,11 @@ function cfrgImportKey(
keyData,
algorithm,
extractable,
- keyUsages) {
+ usages) {
const { name } = algorithm;
let handle;
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
switch (format) {
case 'KeyObject': {
verifyAcceptableCfrgKeyUse(
diff --git a/lib/internal/crypto/chacha20_poly1305.js b/lib/internal/crypto/chacha20_poly1305.js
index 9d4606090af8ee..689cab59f3fbf2 100644
--- a/lib/internal/crypto/chacha20_poly1305.js
+++ b/lib/internal/crypto/chacha20_poly1305.js
@@ -47,12 +47,12 @@ function c20pCipher(mode, key, data, algorithm) {
algorithm.additionalData));
}
-function c20pGenerateKey(algorithm, extractable, keyUsages) {
+function c20pGenerateKey(algorithm, extractable, usages) {
const { name } = algorithm;
const checkUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'];
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
if (hasAnyNotIn(usagesSet, checkUsages)) {
throw lazyDOMException(
`Unsupported key usage for a ${algorithm.name} key`,
@@ -77,11 +77,11 @@ function c20pImportKey(
format,
keyData,
extractable,
- keyUsages) {
+ usages) {
const { name } = algorithm;
const checkUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'];
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
if (hasAnyNotIn(usagesSet, checkUsages)) {
throw lazyDOMException(
`Unsupported key usage for a ${algorithm.name} key`,
diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js
index 212ba75e0a9b11..d102b3fe05a29c 100644
--- a/lib/internal/crypto/ec.js
+++ b/lib/internal/crypto/ec.js
@@ -77,10 +77,10 @@ function verifyAcceptableEcKeyUse(name, isPublic, usages) {
}
}
-function ecGenerateKey(algorithm, extractable, keyUsages) {
+function ecGenerateKey(algorithm, extractable, usages) {
const { name, namedCurve } = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
switch (name) {
case 'ECDSA':
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
@@ -178,12 +178,12 @@ function ecImportKey(
keyData,
algorithm,
extractable,
- keyUsages,
+ usages,
) {
const { name, namedCurve } = algorithm;
let handle;
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
switch (format) {
case 'KeyObject': {
verifyAcceptableEcKeyUse(
diff --git a/lib/internal/crypto/mac.js b/lib/internal/crypto/mac.js
index c3418231650b02..724b2104d4b8c8 100644
--- a/lib/internal/crypto/mac.js
+++ b/lib/internal/crypto/mac.js
@@ -40,14 +40,14 @@ const {
validateJwk,
} = require('internal/crypto/webcrypto_util');
-function hmacGenerateKey(algorithm, extractable, keyUsages) {
+function hmacGenerateKey(algorithm, extractable, usages) {
const {
hash,
name,
length = getBlockSize(hash.name),
} = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
throw lazyDOMException(
'Unsupported key usage for an HMAC key',
@@ -67,7 +67,7 @@ function hmacGenerateKey(algorithm, extractable, keyUsages) {
extractable));
}
-function kmacGenerateKey(algorithm, extractable, keyUsages) {
+function kmacGenerateKey(algorithm, extractable, usages) {
const {
name,
length = {
@@ -77,7 +77,7 @@ function kmacGenerateKey(algorithm, extractable, keyUsages) {
}[name],
} = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
throw lazyDOMException(
`Unsupported key usage for ${name} key`,
@@ -102,10 +102,10 @@ function macImportKey(
keyData,
algorithm,
extractable,
- keyUsages,
+ usages,
) {
const isHmac = algorithm.name === 'HMAC';
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) {
throw lazyDOMException(
`Unsupported key usage for ${algorithm.name} key`,
diff --git a/lib/internal/crypto/ml_dsa.js b/lib/internal/crypto/ml_dsa.js
index 5a08291562bcf2..e2497a2b722b97 100644
--- a/lib/internal/crypto/ml_dsa.js
+++ b/lib/internal/crypto/ml_dsa.js
@@ -59,10 +59,10 @@ function verifyAcceptableMlDsaKeyUse(name, isPublic, usages) {
}
}
-function mlDsaGenerateKey(algorithm, extractable, keyUsages) {
+function mlDsaGenerateKey(algorithm, extractable, usages) {
const { name } = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
throw lazyDOMException(
`Unsupported key usage for an ${name} key`,
@@ -136,11 +136,11 @@ function mlDsaImportKey(
keyData,
algorithm,
extractable,
- keyUsages) {
+ usages) {
const { name } = algorithm;
let handle;
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
switch (format) {
case 'KeyObject': {
verifyAcceptableMlDsaKeyUse(
diff --git a/lib/internal/crypto/ml_kem.js b/lib/internal/crypto/ml_kem.js
index 530507be4e340d..2dea4d00af052f 100644
--- a/lib/internal/crypto/ml_kem.js
+++ b/lib/internal/crypto/ml_kem.js
@@ -49,10 +49,10 @@ const {
validateJwk,
} = require('internal/crypto/webcrypto_util');
-function mlKemGenerateKey(algorithm, extractable, keyUsages) {
+function mlKemGenerateKey(algorithm, extractable, usages) {
const { name } = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
if (hasAnyNotIn(usageSet, ['encapsulateKey', 'encapsulateBits', 'decapsulateKey', 'decapsulateBits'])) {
throw lazyDOMException(
`Unsupported key usage for an ${name} key`,
@@ -137,11 +137,11 @@ function mlKemImportKey(
keyData,
algorithm,
extractable,
- keyUsages) {
+ usages) {
const { name } = algorithm;
let handle;
- const usagesSet = new SafeSet(keyUsages);
+ const usagesSet = new SafeSet(usages);
switch (format) {
case 'KeyObject': {
verifyAcceptableMlKemKeyUse(
diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js
index d72d55c2bbff42..a09dd7b9f0fda9 100644
--- a/lib/internal/crypto/rsa.js
+++ b/lib/internal/crypto/rsa.js
@@ -108,7 +108,7 @@ function rsaOaepCipher(mode, key, data, algorithm) {
function rsaKeyGenerate(
algorithm,
extractable,
- keyUsages,
+ usages,
) {
const publicExponentConverted = bigIntArrayToUnsignedInt(algorithm.publicExponent);
if (publicExponentConverted === undefined) {
@@ -123,7 +123,7 @@ function rsaKeyGenerate(
hash,
} = algorithm;
- const usageSet = new SafeSet(keyUsages);
+ const usageSet = new SafeSet(usages);
switch (name) {
case 'RSA-OAEP':
@@ -213,8 +213,8 @@ function rsaImportKey(
keyData,
algorithm,
extractable,
- keyUsages) {
- const usagesSet = new SafeSet(keyUsages);
+ usages) {
+ const usagesSet = new SafeSet(usages);
let handle;
switch (format) {
case 'KeyObject': {
diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js
index 046efc4554ca36..74d86de3f1b9e1 100644
--- a/lib/internal/crypto/util.js
+++ b/lib/internal/crypto/util.js
@@ -960,6 +960,7 @@ module.exports = {
toBuf,
kNamedCurveAliases,
+ kSupportedAlgorithms,
normalizeAlgorithm,
normalizeHashName,
hasAnyNotIn,
diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js
index 996bcb1a729275..05c337d3262229 100644
--- a/lib/internal/crypto/webcrypto.js
+++ b/lib/internal/crypto/webcrypto.js
@@ -128,9 +128,9 @@ function digestImpl(algorithm, data) {
context: '2nd argument',
});
- algorithm = normalizeAlgorithm(algorithm, 'digest');
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'digest');
- return FunctionPrototypeCall(asyncDigest, this, algorithm, data);
+ return FunctionPrototypeCall(asyncDigest, this, normalizedAlgorithm, data);
}
function randomUUID() {
@@ -162,20 +162,20 @@ function generateKeyImpl(
prefix,
context: '2nd argument',
});
- keyUsages = webidl.converters['sequence'](keyUsages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '3rd argument',
});
- algorithm = normalizeAlgorithm(algorithm, 'generateKey');
- switch (algorithm.name) {
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'generateKey');
+ switch (normalizedAlgorithm.name) {
case 'RSASSA-PKCS1-v1_5':
// Fall through
case 'RSA-PSS':
// Fall through
case 'RSA-OAEP':
return require('internal/crypto/rsa')
- .rsaKeyGenerate(algorithm, extractable, keyUsages);
+ .rsaKeyGenerate(normalizedAlgorithm, extractable, usages);
case 'Ed25519':
// Fall through
case 'Ed448':
@@ -184,15 +184,15 @@ function generateKeyImpl(
// Fall through
case 'X448':
return require('internal/crypto/cfrg')
- .cfrgGenerateKey(algorithm, extractable, keyUsages);
+ .cfrgGenerateKey(normalizedAlgorithm, extractable, usages);
case 'ECDSA':
// Fall through
case 'ECDH':
return require('internal/crypto/ec')
- .ecGenerateKey(algorithm, extractable, keyUsages);
+ .ecGenerateKey(normalizedAlgorithm, extractable, usages);
case 'HMAC':
return require('internal/crypto/mac')
- .hmacGenerateKey(algorithm, extractable, keyUsages);
+ .hmacGenerateKey(normalizedAlgorithm, extractable, usages);
case 'AES-CTR':
// Fall through
case 'AES-CBC':
@@ -203,29 +203,29 @@ function generateKeyImpl(
// Fall through
case 'AES-KW':
return require('internal/crypto/aes')
- .aesGenerateKey(algorithm, extractable, keyUsages);
+ .aesGenerateKey(normalizedAlgorithm, extractable, usages);
case 'ChaCha20-Poly1305':
return require('internal/crypto/chacha20_poly1305')
- .c20pGenerateKey(algorithm, extractable, keyUsages);
+ .c20pGenerateKey(normalizedAlgorithm, extractable, usages);
case 'ML-DSA-44':
// Fall through
case 'ML-DSA-65':
// Fall through
case 'ML-DSA-87':
return require('internal/crypto/ml_dsa')
- .mlDsaGenerateKey(algorithm, extractable, keyUsages);
+ .mlDsaGenerateKey(normalizedAlgorithm, extractable, usages);
case 'ML-KEM-512':
// Fall through
case 'ML-KEM-768':
// Fall through
case 'ML-KEM-1024':
return require('internal/crypto/ml_kem')
- .mlKemGenerateKey(algorithm, extractable, keyUsages);
+ .mlKemGenerateKey(normalizedAlgorithm, extractable, usages);
case 'KMAC128':
// Fall through
case 'KMAC256':
return require('internal/crypto/mac')
- .kmacGenerateKey(algorithm, extractable, keyUsages);
+ .kmacGenerateKey(normalizedAlgorithm, extractable, usages);
default:
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
}
@@ -256,35 +256,35 @@ function deriveBitsImpl(algorithm, baseKey, length = null) {
});
}
- algorithm = normalizeAlgorithm(algorithm, 'deriveBits');
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'deriveBits');
if (!hasCryptoKeyUsage(baseKey, 'deriveBits')) {
throw lazyDOMException(
'baseKey does not have deriveBits usage',
'InvalidAccessError');
}
- if (getCryptoKeyAlgorithm(baseKey).name !== algorithm.name)
+ if (getCryptoKeyAlgorithm(baseKey).name !== normalizedAlgorithm.name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
- switch (algorithm.name) {
+ switch (normalizedAlgorithm.name) {
case 'X25519':
// Fall through
case 'X448':
// Fall through
case 'ECDH':
return require('internal/crypto/diffiehellman')
- .ecdhDeriveBits(algorithm, baseKey, length);
+ .ecdhDeriveBits(normalizedAlgorithm, baseKey, length);
case 'HKDF':
return require('internal/crypto/hkdf')
- .hkdfDeriveBits(algorithm, baseKey, length);
+ .hkdfDeriveBits(normalizedAlgorithm, baseKey, length);
case 'PBKDF2':
return require('internal/crypto/pbkdf2')
- .pbkdf2DeriveBits(algorithm, baseKey, length);
+ .pbkdf2DeriveBits(normalizedAlgorithm, baseKey, length);
case 'Argon2d':
// Fall through
case 'Argon2i':
// Fall through
case 'Argon2id':
return require('internal/crypto/argon2')
- .argon2DeriveBits(algorithm, baseKey, length);
+ .argon2DeriveBits(normalizedAlgorithm, baseKey, length);
}
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
}
@@ -331,7 +331,7 @@ function getKeyLength({ name, length, hash }) {
function deriveKey(
algorithm,
baseKey,
- derivedKeyAlgorithm,
+ derivedKeyType,
extractable,
keyUsages) {
return callSubtleCryptoMethod(deriveKeyImpl, this, arguments);
@@ -340,7 +340,7 @@ function deriveKey(
function deriveKeyImpl(
algorithm,
baseKey,
- derivedKeyAlgorithm,
+ derivedKeyType,
extractable,
keyUsages) {
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
@@ -356,7 +356,7 @@ function deriveKeyImpl(
prefix,
context: '2nd argument',
});
- derivedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(derivedKeyAlgorithm, {
+ derivedKeyType = webidl.converters.AlgorithmIdentifier(derivedKeyType, {
prefix,
context: '3rd argument',
});
@@ -364,56 +364,63 @@ function deriveKeyImpl(
prefix,
context: '4th argument',
});
- keyUsages = webidl.converters['sequence'](keyUsages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '5th argument',
});
- algorithm = normalizeAlgorithm(algorithm, 'deriveBits');
- derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm, 'importKey');
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'deriveBits');
+ const normalizedDerivedKeyAlgorithmImport =
+ normalizeAlgorithm(derivedKeyType, 'importKey');
+ const normalizedDerivedKeyAlgorithmLength =
+ normalizeAlgorithm(derivedKeyType, 'get key length');
if (!hasCryptoKeyUsage(baseKey, 'deriveKey')) {
throw lazyDOMException(
'baseKey does not have deriveKey usage',
'InvalidAccessError');
}
- if (getCryptoKeyAlgorithm(baseKey).name !== algorithm.name)
+ if (getCryptoKeyAlgorithm(baseKey).name !== normalizedAlgorithm.name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
- const length = getKeyLength(normalizeAlgorithm(arguments[2], 'get key length'));
- let bits;
- switch (algorithm.name) {
+ const length = getKeyLength(normalizedDerivedKeyAlgorithmLength);
+ let secret;
+ switch (normalizedAlgorithm.name) {
case 'X25519':
// Fall through
case 'X448':
// Fall through
case 'ECDH':
- bits = require('internal/crypto/diffiehellman')
- .ecdhDeriveBits(algorithm, baseKey, length);
+ secret = require('internal/crypto/diffiehellman')
+ .ecdhDeriveBits(normalizedAlgorithm, baseKey, length);
break;
case 'HKDF':
- bits = require('internal/crypto/hkdf')
- .hkdfDeriveBits(algorithm, baseKey, length);
+ secret = require('internal/crypto/hkdf')
+ .hkdfDeriveBits(normalizedAlgorithm, baseKey, length);
break;
case 'PBKDF2':
- bits = require('internal/crypto/pbkdf2')
- .pbkdf2DeriveBits(algorithm, baseKey, length);
+ secret = require('internal/crypto/pbkdf2')
+ .pbkdf2DeriveBits(normalizedAlgorithm, baseKey, length);
break;
case 'Argon2d':
// Fall through
case 'Argon2i':
// Fall through
case 'Argon2id':
- bits = require('internal/crypto/argon2')
- .argon2DeriveBits(algorithm, baseKey, length);
+ secret = require('internal/crypto/argon2')
+ .argon2DeriveBits(normalizedAlgorithm, baseKey, length);
break;
default:
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
}
- return jobPromiseThen(bits, (bits) => FunctionPrototypeCall(
+ return jobPromiseThen(secret, (secret) => FunctionPrototypeCall(
importKeySync,
this,
- 'raw-secret', bits, derivedKeyAlgorithm, extractable, keyUsages,
+ 'raw-secret',
+ secret,
+ normalizedDerivedKeyAlgorithmImport,
+ extractable,
+ usages,
));
}
@@ -805,14 +812,14 @@ function detachFromUserPrototypes(value) {
}
// Parse wrapped JWK bytes according to WebCrypto's "parse a JWK" procedure.
-function parseJwk(keyData) {
+function parseJwk(data) {
let key;
try {
// WebCrypto parses JWKs in a fresh global. Detach parsed JSON values
// from user-mutated prototypes before WebIDL dictionary conversion.
// Wrapped JWKs may be produced outside WebCrypto, so parse using the
// spec-required UTF-8.
- const json = decodeUTF8(keyData, false, true);
+ const json = decodeUTF8(data, false, true);
const result = JSONParse(json);
detachFromUserPrototypes(result);
key = webidl.converters.JsonWebKey(result);
@@ -841,7 +848,7 @@ function aliasKeyFormat(format) {
}
}
-function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
+function importKeySync(format, keyData, algorithm, extractable, usages) {
let result;
switch (algorithm.name) {
case 'RSASSA-PKCS1-v1_5':
@@ -851,14 +858,24 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
case 'RSA-OAEP':
format = aliasKeyFormat(format);
result = require('internal/crypto/rsa')
- .rsaImportKey(format, keyData, algorithm, extractable, keyUsages);
+ .rsaImportKey(
+ format,
+ keyData,
+ algorithm,
+ extractable,
+ usages);
break;
case 'ECDSA':
// Fall through
case 'ECDH':
format = aliasKeyFormat(format);
result = require('internal/crypto/ec')
- .ecImportKey(format, keyData, algorithm, extractable, keyUsages);
+ .ecImportKey(
+ format,
+ keyData,
+ algorithm,
+ extractable,
+ usages);
break;
case 'Ed25519':
// Fall through
@@ -869,7 +886,12 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
case 'X448':
format = aliasKeyFormat(format);
result = require('internal/crypto/cfrg')
- .cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
+ .cfrgImportKey(
+ format,
+ keyData,
+ algorithm,
+ extractable,
+ usages);
break;
case 'HMAC':
// Fall through
@@ -877,7 +899,12 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
// Fall through
case 'KMAC256':
result = require('internal/crypto/mac')
- .macImportKey(format, keyData, algorithm, extractable, keyUsages);
+ .macImportKey(
+ format,
+ keyData,
+ algorithm,
+ extractable,
+ usages);
break;
case 'AES-CTR':
// Fall through
@@ -889,11 +916,21 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
// Fall through
case 'AES-OCB':
result = require('internal/crypto/aes')
- .aesImportKey(algorithm, format, keyData, extractable, keyUsages);
+ .aesImportKey(
+ algorithm,
+ format,
+ keyData,
+ extractable,
+ usages);
break;
case 'ChaCha20-Poly1305':
result = require('internal/crypto/chacha20_poly1305')
- .c20pImportKey(algorithm, format, keyData, extractable, keyUsages);
+ .c20pImportKey(
+ algorithm,
+ format,
+ keyData,
+ extractable,
+ usages);
break;
case 'HKDF':
// Fall through
@@ -904,7 +941,7 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
format,
keyData,
extractable,
- keyUsages);
+ usages);
break;
case 'Argon2d':
// Fall through
@@ -917,7 +954,7 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
format,
keyData,
extractable,
- keyUsages);
+ usages);
}
break;
case 'ML-DSA-44':
@@ -926,7 +963,12 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
// Fall through
case 'ML-DSA-87':
result = require('internal/crypto/ml_dsa')
- .mlDsaImportKey(format, keyData, algorithm, extractable, keyUsages);
+ .mlDsaImportKey(
+ format,
+ keyData,
+ algorithm,
+ extractable,
+ usages);
break;
case 'ML-KEM-512':
// Fall through
@@ -934,7 +976,12 @@ function importKeySync(format, keyData, algorithm, extractable, keyUsages) {
// Fall through
case 'ML-KEM-1024':
result = require('internal/crypto/ml_kem')
- .mlKemImportKey(format, keyData, algorithm, extractable, keyUsages);
+ .mlKemImportKey(
+ format,
+ keyData,
+ algorithm,
+ extractable,
+ usages);
break;
}
@@ -991,27 +1038,31 @@ function importKeyImpl(
prefix,
context: '4th argument',
});
- keyUsages = webidl.converters['sequence'](keyUsages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '5th argument',
});
- algorithm = normalizeAlgorithm(algorithm, 'importKey');
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'importKey');
return FunctionPrototypeCall(
importKeySync,
this,
- format, keyData, algorithm, extractable, keyUsages,
+ format,
+ keyData,
+ normalizedAlgorithm,
+ extractable,
+ usages,
);
}
// subtle.wrapKey() is essentially a subtle.exportKey() followed
// by a subtle.encrypt().
-function wrapKey(format, key, wrappingKey, algorithm) {
+function wrapKey(format, key, wrappingKey, wrapAlgorithm) {
return callSubtleCryptoMethod(wrapKeyImpl, this, arguments);
}
-function wrapKeyImpl(format, key, wrappingKey, algorithm) {
+function wrapKeyImpl(format, key, wrappingKey, wrapAlgorithm) {
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
webidl ??= require('internal/crypto/webidl');
@@ -1029,50 +1080,52 @@ function wrapKeyImpl(format, key, wrappingKey, algorithm) {
prefix,
context: '3rd argument',
});
- algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
+ const algorithm = webidl.converters.AlgorithmIdentifier(wrapAlgorithm, {
prefix,
context: '4th argument',
});
+ let normalizedAlgorithm;
try {
- algorithm = normalizeAlgorithm(algorithm, 'wrapKey');
+ normalizedAlgorithm = normalizeAlgorithm(algorithm, 'wrapKey');
} catch {
- algorithm = normalizeAlgorithm(algorithm, 'encrypt');
+ normalizedAlgorithm = normalizeAlgorithm(algorithm, 'encrypt');
}
- if (algorithm.name !== getCryptoKeyAlgorithm(wrappingKey).name)
+ if (normalizedAlgorithm.name !== getCryptoKeyAlgorithm(wrappingKey).name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
if (!hasCryptoKeyUsage(wrappingKey, 'wrapKey'))
throw lazyDOMException(
'Unable to use this key to wrapKey', 'InvalidAccessError');
- let keyData = exportKeySync(format, key);
+ const exportedKey = exportKeySync(format, key);
+ let bytes = exportedKey;
if (format === 'jwk') {
// The WebCrypto spec stringifies JWKs in a new global object. Rather
// than create a new realm here, detach this internally generated JWK from
// user-mutated prototypes so JSON.stringify cannot read inherited toJSON
// hooks from the current global.
- detachFromUserPrototypes(keyData);
- const json = JSONStringify(keyData);
+ detachFromUserPrototypes(exportedKey);
+ const json = JSONStringify(exportedKey);
// As per the NOTE in step 13 https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey
// we're padding AES-KW wrapped JWK to make sure it is always a multiple of 8 bytes
// in length
// The spec then UTF-8 encodes json.
- if (algorithm.name === 'AES-KW' && json.length % 8 !== 0) {
- keyData = encodeUtf8String(
+ if (normalizedAlgorithm.name === 'AES-KW' && json.length % 8 !== 0) {
+ bytes = encodeUtf8String(
json + StringPrototypeRepeat(' ', 8 - (json.length % 8)));
} else {
- keyData = encodeUtf8String(json);
+ bytes = encodeUtf8String(json);
}
}
return cipherOrWrap(
kWebCryptoCipherEncrypt,
- algorithm,
+ normalizedAlgorithm,
wrappingKey,
- keyData,
+ bytes,
'wrapKey');
}
@@ -1082,8 +1135,8 @@ function unwrapKey(
format,
wrappedKey,
unwrappingKey,
- unwrapAlgo,
- unwrappedKeyAlgo,
+ unwrapAlgorithm,
+ unwrappedKeyAlgorithm,
extractable,
keyUsages) {
return callSubtleCryptoMethod(unwrapKeyImpl, this, arguments);
@@ -1093,8 +1146,8 @@ function unwrapKeyImpl(
format,
wrappedKey,
unwrappingKey,
- unwrapAlgo,
- unwrappedKeyAlgo,
+ unwrapAlgorithm,
+ unwrappedKeyAlgorithm,
extractable,
keyUsages) {
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
@@ -1114,12 +1167,12 @@ function unwrapKeyImpl(
prefix,
context: '3rd argument',
});
- unwrapAlgo = webidl.converters.AlgorithmIdentifier(unwrapAlgo, {
+ const algorithm = webidl.converters.AlgorithmIdentifier(unwrapAlgorithm, {
prefix,
context: '4th argument',
});
- unwrappedKeyAlgo = webidl.converters.AlgorithmIdentifier(
- unwrappedKeyAlgo,
+ unwrappedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(
+ unwrappedKeyAlgorithm,
{
prefix,
context: '5th argument',
@@ -1129,87 +1182,94 @@ function unwrapKeyImpl(
prefix,
context: '6th argument',
});
- keyUsages = webidl.converters['sequence'](keyUsages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '7th argument',
});
+ let normalizedAlgorithm;
try {
- unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'unwrapKey');
+ normalizedAlgorithm = normalizeAlgorithm(algorithm, 'unwrapKey');
} catch {
- unwrapAlgo = normalizeAlgorithm(unwrapAlgo, 'decrypt');
+ normalizedAlgorithm = normalizeAlgorithm(algorithm, 'decrypt');
}
- unwrappedKeyAlgo = normalizeAlgorithm(unwrappedKeyAlgo, 'importKey');
+ const normalizedKeyAlgorithm =
+ normalizeAlgorithm(unwrappedKeyAlgorithm, 'importKey');
- if (unwrapAlgo.name !== getCryptoKeyAlgorithm(unwrappingKey).name)
+ if (normalizedAlgorithm.name !== getCryptoKeyAlgorithm(unwrappingKey).name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
if (!hasCryptoKeyUsage(unwrappingKey, 'unwrapKey'))
throw lazyDOMException(
'Unable to use this key to unwrapKey', 'InvalidAccessError');
- const keyData = cipherOrWrap(
+ const bytes = cipherOrWrap(
kWebCryptoCipherDecrypt,
- unwrapAlgo,
+ normalizedAlgorithm,
unwrappingKey,
wrappedKey,
'unwrapKey');
- return jobPromiseThen(keyData, (keyData) => {
+ return jobPromiseThen(bytes, (bytes) => {
+ let keyData = bytes;
if (format === 'jwk') {
- keyData = parseJwk(keyData);
+ keyData = parseJwk(bytes);
}
return FunctionPrototypeCall(
importKeySync,
this,
- format, keyData, unwrappedKeyAlgo, extractable, keyUsages,
+ format,
+ keyData,
+ normalizedKeyAlgorithm,
+ extractable,
+ usages,
);
});
}
function signVerify(algorithm, key, data, signature) {
- const op = signature !== undefined ? 'verify' : 'sign'; // This is also usage
- algorithm = normalizeAlgorithm(algorithm, op);
+ const operation = signature !== undefined ? 'verify' : 'sign'; // This is also usage
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, operation);
- if (algorithm.name !== getCryptoKeyAlgorithm(key).name)
+ if (normalizedAlgorithm.name !== getCryptoKeyAlgorithm(key).name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
- if (!hasCryptoKeyUsage(key, op))
+ if (!hasCryptoKeyUsage(key, operation))
throw lazyDOMException(
- `Unable to use this key to ${op}`, 'InvalidAccessError');
+ `Unable to use this key to ${operation}`, 'InvalidAccessError');
- switch (algorithm.name) {
+ switch (normalizedAlgorithm.name) {
case 'RSA-PSS':
// Fall through
case 'RSASSA-PKCS1-v1_5':
return require('internal/crypto/rsa')
- .rsaSignVerify(key, data, algorithm, signature);
+ .rsaSignVerify(key, data, normalizedAlgorithm, signature);
case 'ECDSA':
return require('internal/crypto/ec')
- .ecdsaSignVerify(key, data, algorithm, signature);
+ .ecdsaSignVerify(key, data, normalizedAlgorithm, signature);
case 'Ed25519':
// Fall through
case 'Ed448':
// Fall through
return require('internal/crypto/cfrg')
- .eddsaSignVerify(key, data, algorithm, signature);
+ .eddsaSignVerify(key, data, normalizedAlgorithm, signature);
case 'HMAC':
return require('internal/crypto/mac')
- .hmacSignVerify(key, data, algorithm, signature);
+ .hmacSignVerify(key, data, normalizedAlgorithm, signature);
case 'ML-DSA-44':
// Fall through
case 'ML-DSA-65':
// Fall through
case 'ML-DSA-87':
return require('internal/crypto/ml_dsa')
- .mlDsaSignVerify(key, data, algorithm, signature);
+ .mlDsaSignVerify(key, data, normalizedAlgorithm, signature);
case 'KMAC128':
// Fall through
case 'KMAC256':
return require('internal/crypto/mac')
- .kmacSignVerify(key, data, algorithm, signature);
+ .kmacSignVerify(key, data, normalizedAlgorithm, signature);
}
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
}
@@ -1270,16 +1330,16 @@ function verifyImpl(algorithm, key, signature, data) {
return signVerify(algorithm, key, data, signature);
}
-function cipherOrWrap(mode, algorithm, key, data, op) {
+function cipherOrWrap(mode, normalizedAlgorithm, key, data, operation) {
// 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.
validateMaxBufferLength(data, 'data');
- switch (algorithm.name) {
+ switch (normalizedAlgorithm.name) {
case 'RSA-OAEP':
return require('internal/crypto/rsa')
- .rsaCipher(mode, key, data, algorithm);
+ .rsaCipher(mode, key, data, normalizedAlgorithm);
case 'AES-CTR':
// Fall through
case 'AES-CBC':
@@ -1288,14 +1348,14 @@ function cipherOrWrap(mode, algorithm, key, data, op) {
// Fall through
case 'AES-OCB':
return require('internal/crypto/aes')
- .aesCipher(mode, key, data, algorithm);
+ .aesCipher(mode, key, data, normalizedAlgorithm);
case 'ChaCha20-Poly1305':
return require('internal/crypto/chacha20_poly1305')
- .c20pCipher(mode, key, data, algorithm);
+ .c20pCipher(mode, key, data, normalizedAlgorithm);
case 'AES-KW':
- if (op === 'wrapKey' || op === 'unwrapKey') {
+ if (operation === 'wrapKey' || operation === 'unwrapKey') {
return require('internal/crypto/aes')
- .aesCipher(mode, key, data, algorithm);
+ .aesCipher(mode, key, data, normalizedAlgorithm);
}
}
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
@@ -1324,9 +1384,9 @@ function encryptImpl(algorithm, key, data) {
context: '3rd argument',
});
- algorithm = normalizeAlgorithm(algorithm, 'encrypt');
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'encrypt');
- if (algorithm.name !== getCryptoKeyAlgorithm(key).name)
+ if (normalizedAlgorithm.name !== getCryptoKeyAlgorithm(key).name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
if (!hasCryptoKeyUsage(key, 'encrypt'))
@@ -1335,7 +1395,7 @@ function encryptImpl(algorithm, key, data) {
return cipherOrWrap(
kWebCryptoCipherEncrypt,
- algorithm,
+ normalizedAlgorithm,
key,
data,
'encrypt',
@@ -1365,9 +1425,9 @@ function decryptImpl(algorithm, key, data) {
context: '3rd argument',
});
- algorithm = normalizeAlgorithm(algorithm, 'decrypt');
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'decrypt');
- if (algorithm.name !== getCryptoKeyAlgorithm(key).name)
+ if (normalizedAlgorithm.name !== getCryptoKeyAlgorithm(key).name)
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
if (!hasCryptoKeyUsage(key, 'decrypt'))
@@ -1376,7 +1436,7 @@ function decryptImpl(algorithm, key, data) {
return cipherOrWrap(
kWebCryptoCipherDecrypt,
- algorithm,
+ normalizedAlgorithm,
key,
data,
'decrypt',
@@ -1399,7 +1459,7 @@ function getPublicKeyImpl(key, keyUsages) {
prefix,
context: '1st argument',
});
- keyUsages = webidl.converters['sequence'](keyUsages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '2nd argument',
});
@@ -1412,7 +1472,7 @@ function getPublicKeyImpl(key, keyUsages) {
// TODO(panva): this is by no means a hot path, but let's still follow up to get
// rid of this awkwardness
const keyObject = createPublicKey(new PrivateKeyObject(getCryptoKeyHandle(key)));
- return keyObject.toCryptoKey(getCryptoKeyAlgorithm(key), true, keyUsages);
+ return keyObject.toCryptoKey(getCryptoKeyAlgorithm(key), true, usages);
}
function encapsulateBits(encapsulationAlgorithm, encapsulationKey) {
@@ -1426,10 +1486,13 @@ function encapsulateBitsImpl(encapsulationAlgorithm, encapsulationKey) {
webidl ??= require('internal/crypto/webidl');
const prefix = "Failed to execute 'encapsulateBits' on 'SubtleCrypto'";
webidl.requiredArguments(arguments.length, 2, { prefix });
- encapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(encapsulationAlgorithm, {
- prefix,
- context: '1st argument',
- });
+ encapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(
+ encapsulationAlgorithm,
+ {
+ prefix,
+ context: '1st argument',
+ },
+ );
encapsulationKey = webidl.converters.CryptoKey(encapsulationKey, {
prefix,
context: '2nd argument',
@@ -1467,7 +1530,7 @@ function encapsulateKey(
encapsulationKey,
sharedKeyAlgorithm,
extractable,
- usages) {
+ keyUsages) {
return callSubtleCryptoMethod(encapsulateKeyImpl, this, arguments);
}
@@ -1476,30 +1539,36 @@ function encapsulateKeyImpl(
encapsulationKey,
sharedKeyAlgorithm,
extractable,
- usages) {
+ keyUsages) {
emitExperimentalWarning('The encapsulateKey Web Crypto API method');
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
webidl ??= require('internal/crypto/webidl');
const prefix = "Failed to execute 'encapsulateKey' on 'SubtleCrypto'";
webidl.requiredArguments(arguments.length, 5, { prefix });
- encapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(encapsulationAlgorithm, {
- prefix,
- context: '1st argument',
- });
+ encapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(
+ encapsulationAlgorithm,
+ {
+ prefix,
+ context: '1st argument',
+ },
+ );
encapsulationKey = webidl.converters.CryptoKey(encapsulationKey, {
prefix,
context: '2nd argument',
});
- sharedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(sharedKeyAlgorithm, {
- prefix,
- context: '3rd argument',
- });
+ sharedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(
+ sharedKeyAlgorithm,
+ {
+ prefix,
+ context: '3rd argument',
+ },
+ );
extractable = webidl.converters.boolean(extractable, {
prefix,
context: '4th argument',
});
- usages = webidl.converters['sequence'](usages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '5th argument',
});
@@ -1522,28 +1591,31 @@ function encapsulateKeyImpl(
'InvalidAccessError');
}
- let encapsulateBits;
+ let encapsulatedBits;
switch (keyAlgorithm.name) {
case 'ML-KEM-512':
case 'ML-KEM-768':
case 'ML-KEM-1024':
- encapsulateBits = require('internal/crypto/ml_kem')
+ encapsulatedBits = require('internal/crypto/ml_kem')
.mlKemEncapsulate(encapsulationKey);
break;
default:
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
}
- return jobPromiseThen(encapsulateBits, (encapsulateBits) => {
+ return jobPromiseThen(encapsulatedBits, (encapsulatedBits) => {
const sharedKey = FunctionPrototypeCall(
importKeySync,
this,
- 'raw-secret', encapsulateBits.sharedKey, normalizedSharedKeyAlgorithm,
- extractable, usages,
+ 'raw-secret',
+ encapsulatedBits.sharedKey,
+ normalizedSharedKeyAlgorithm,
+ extractable,
+ usages,
);
return {
- ciphertext: encapsulateBits.ciphertext,
+ ciphertext: encapsulatedBits.ciphertext,
sharedKey,
};
});
@@ -1560,10 +1632,13 @@ function decapsulateBitsImpl(decapsulationAlgorithm, decapsulationKey, ciphertex
webidl ??= require('internal/crypto/webidl');
const prefix = "Failed to execute 'decapsulateBits' on 'SubtleCrypto'";
webidl.requiredArguments(arguments.length, 3, { prefix });
- decapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(decapsulationAlgorithm, {
- prefix,
- context: '1st argument',
- });
+ decapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(
+ decapsulationAlgorithm,
+ {
+ prefix,
+ context: '1st argument',
+ },
+ );
decapsulationKey = webidl.converters.CryptoKey(decapsulationKey, {
prefix,
context: '2nd argument',
@@ -1601,8 +1676,12 @@ function decapsulateBitsImpl(decapsulationAlgorithm, decapsulationKey, ciphertex
}
function decapsulateKey(
- decapsulationAlgorithm, decapsulationKey, ciphertext, sharedKeyAlgorithm, extractable, usages,
-) {
+ decapsulationAlgorithm,
+ decapsulationKey,
+ ciphertext,
+ sharedKeyAlgorithm,
+ extractable,
+ keyUsages) {
return callSubtleCryptoMethod(decapsulateKeyImpl, this, arguments);
}
@@ -1612,17 +1691,20 @@ function decapsulateKeyImpl(
ciphertext,
sharedKeyAlgorithm,
extractable,
- usages) {
+ keyUsages) {
emitExperimentalWarning('The decapsulateKey Web Crypto API method');
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
webidl ??= require('internal/crypto/webidl');
const prefix = "Failed to execute 'decapsulateKey' on 'SubtleCrypto'";
webidl.requiredArguments(arguments.length, 6, { prefix });
- decapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(decapsulationAlgorithm, {
- prefix,
- context: '1st argument',
- });
+ decapsulationAlgorithm = webidl.converters.AlgorithmIdentifier(
+ decapsulationAlgorithm,
+ {
+ prefix,
+ context: '1st argument',
+ },
+ );
decapsulationKey = webidl.converters.CryptoKey(decapsulationKey, {
prefix,
context: '2nd argument',
@@ -1631,15 +1713,18 @@ function decapsulateKeyImpl(
prefix,
context: '3rd argument',
});
- sharedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(sharedKeyAlgorithm, {
- prefix,
- context: '4th argument',
- });
+ sharedKeyAlgorithm = webidl.converters.AlgorithmIdentifier(
+ sharedKeyAlgorithm,
+ {
+ prefix,
+ context: '4th argument',
+ },
+ );
extractable = webidl.converters.boolean(extractable, {
prefix,
context: '5th argument',
});
- usages = webidl.converters['sequence'](usages, {
+ const usages = webidl.converters['sequence'](keyUsages, {
prefix,
context: '6th argument',
});
@@ -1677,7 +1762,10 @@ function decapsulateKeyImpl(
return jobPromiseThen(decapsulatedBits, (decapsulatedBits) => FunctionPrototypeCall(
importKeySync,
this,
- 'raw-secret', decapsulatedBits, normalizedSharedKeyAlgorithm, extractable,
+ 'raw-secret',
+ decapsulatedBits,
+ normalizedSharedKeyAlgorithm,
+ extractable,
usages,
));
}
@@ -1733,10 +1821,13 @@ class SubtleCrypto {
let length;
let additionalAlgorithm;
if (operation === 'deriveKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
- prefix,
- context: '3rd argument',
- });
+ additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
+ lengthOrAdditionalAlgorithm,
+ {
+ prefix,
+ context: '3rd argument',
+ },
+ );
if (!check('importKey', additionalAlgorithm)) {
return false;
@@ -1750,19 +1841,25 @@ class SubtleCrypto {
operation = 'deriveBits';
} else if (operation === 'wrapKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
- prefix,
- context: '3rd argument',
- });
+ additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
+ lengthOrAdditionalAlgorithm,
+ {
+ prefix,
+ context: '3rd argument',
+ },
+ );
if (!check('exportKey', additionalAlgorithm)) {
return false;
}
} else if (operation === 'unwrapKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
- prefix,
- context: '3rd argument',
- });
+ additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
+ lengthOrAdditionalAlgorithm,
+ {
+ prefix,
+ context: '3rd argument',
+ },
+ );
if (!check('importKey', additionalAlgorithm)) {
return false;
@@ -1796,10 +1893,13 @@ class SubtleCrypto {
return false;
}
} else if (operation === 'encapsulateKey' || operation === 'decapsulateKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
- prefix,
- context: '3rd argument',
- });
+ additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
+ lengthOrAdditionalAlgorithm,
+ {
+ prefix,
+ context: '3rd argument',
+ },
+ );
let normalizedAdditionalAlgorithm;
try {
@@ -1824,7 +1924,8 @@ class SubtleCrypto {
case 'HMAC':
case 'KMAC128':
case 'KMAC256':
- if (normalizedAdditionalAlgorithm.length === undefined || normalizedAdditionalAlgorithm.length === 256) {
+ if (normalizedAdditionalAlgorithm.length === undefined ||
+ normalizedAdditionalAlgorithm.length === 256) {
break;
}
return false;
diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc
index f23f25ba0d58fe..522d3c24cfba70 100644
--- a/src/node_sqlite.cc
+++ b/src/node_sqlite.cc
@@ -2237,7 +2237,6 @@ static int xConflict(void* pCtx, int eConflict, sqlite3_changeset_iter* pIter) {
static int xFilter(void* pCtx, const char* zTab) {
auto ctx = static_cast(pCtx);
- if (!ctx->filterCallback) return 1;
return ctx->filterCallback(zTab) ? 1 : 0;
}
@@ -2348,7 +2347,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) {
db->connection_,
buf.length(),
const_cast(static_cast(buf.data())),
- xFilter,
+ context.filterCallback ? xFilter : nullptr,
xConflict,
static_cast(&context));
if (r == SQLITE_OK) {
diff --git a/test/fixtures/test426/decoding/scopes/README.md b/test/fixtures/test426/decoding/scopes/README.md
new file mode 100644
index 00000000000000..7b3271adb3ada7
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/README.md
@@ -0,0 +1,18 @@
+## Test cases
+
+- **empty-scopes-field**: Empty original scopes field
+- **nil-scopes**: Multiple null original scopes
+- **close-start-end-position-scopes**: Scopes with very close start and end
+ positions
+- **single-root-original-scope**: A single global root original scope
+- **nested-scopes**: Nested original scopes representing functions and blocks
+- **sibling-scopes**: Multiple sibling top-level functions
+- **scope-variables**: Scopes containing variable declarations
+- **multiple-root-original-scopes-with-nil**: Multiple global root scopes with a
+ null scope in between
+- **sibling-ranges**: Multiple sibling root ranges
+- **nested-ranges**: A root range with a nested function range
+- **range-values**: A root range with a non-function range with bindings
+- **sub-range-values**: A root range with sub-range bindings
+- **range-call-site**: A function inlined into the root scope
+- **hidden-ranges**: A function range corresponding to an original block scope
diff --git a/test/fixtures/test426/decoding/scopes/hidden-ranges.map b/test/fixtures/test426/decoding/scopes/hidden-ranges.map
new file mode 100644
index 00000000000000..779a24269ff2a4
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/hidden-ranges.map
@@ -0,0 +1,13 @@
+{
+ "version": 3,
+ "file": "hidden-ranges.js",
+ "sources": [
+ "original_0.js"
+ ],
+ "mappings": "",
+ "names": [
+ "global",
+ "block"
+ ],
+ "scopes": "BCAAA,BCBAC,CEA,CFA,ECAA,EPBAC,FEA,FFA"
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/hidden-ranges.map.golden b/test/fixtures/test426/decoding/scopes/hidden-ranges.map.golden
new file mode 100644
index 00000000000000..6564cca5ea4e9d
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/hidden-ranges.map.golden
@@ -0,0 +1,75 @@
+{
+ "file": "hidden-ranges.js",
+ "mappings": [],
+ "sources": [
+ {
+ "url": "original_0.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [],
+ "children": [
+ {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 0
+ },
+ "name": null,
+ "kind": "block",
+ "isStackFrame": false,
+ "variables": [],
+ "children": []
+ }
+ ]
+ }
+ }
+ ],
+ "ranges": [
+ {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "definitionIndex": 0,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [],
+ "children": [
+ {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 0
+ },
+ "definitionIndex": 1,
+ "stackFrameType": "hidden",
+ "callSite": null,
+ "bindings": [],
+ "children": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/nested-ranges.map b/test/fixtures/test426/decoding/scopes/nested-ranges.map
new file mode 100644
index 00000000000000..2ab612337ed9a5
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/nested-ranges.map
@@ -0,0 +1,14 @@
+{
+ "version": 3,
+ "file": "nested-ranges.js",
+ "sources": [
+ "original_0.js"
+ ],
+ "mappings": "",
+ "names": [
+ "global",
+ "foo",
+ "function"
+ ],
+ "scopes": "BCAAA,BHBACE,CEA,CFA,ECAA,EGCC,FC,FG"
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/nested-ranges.map.golden b/test/fixtures/test426/decoding/scopes/nested-ranges.map.golden
new file mode 100644
index 00000000000000..4d4138f113d10e
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/nested-ranges.map.golden
@@ -0,0 +1,75 @@
+{
+ "file": "nested-ranges.js",
+ "mappings": [],
+ "sources": [
+ {
+ "url": "original_0.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [],
+ "children": [
+ {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 0
+ },
+ "name": "foo",
+ "kind": "function",
+ "isStackFrame": true,
+ "variables": [],
+ "children": []
+ }
+ ]
+ }
+ }
+ ],
+ "ranges": [
+ {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 0,
+ "column": 10
+ },
+ "definitionIndex": 0,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [],
+ "children": [
+ {
+ "start": {
+ "line": 0,
+ "column": 2
+ },
+ "end": {
+ "line": 0,
+ "column": 4
+ },
+ "definitionIndex": 1,
+ "stackFrameType": "original",
+ "callSite": null,
+ "bindings": [],
+ "children": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/range-call-site.map b/test/fixtures/test426/decoding/scopes/range-call-site.map
new file mode 100644
index 00000000000000..61e5a4c5a5dcee
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/range-call-site.map
@@ -0,0 +1,14 @@
+{
+ "version": 3,
+ "file": "range-call-site.js",
+ "sources": [
+ "original_0.js"
+ ],
+ "mappings": "",
+ "names": [
+ "global",
+ "inlineMe",
+ "function"
+ ],
+ "scopes": "BCAAA,BHBACE,CEA,CFA,ECAA,EDBAC,IAGC,FK,FJA"
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/range-call-site.map.golden b/test/fixtures/test426/decoding/scopes/range-call-site.map.golden
new file mode 100644
index 00000000000000..b3ee2302946c44
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/range-call-site.map.golden
@@ -0,0 +1,79 @@
+{
+ "file": "range-call-site.js",
+ "mappings": [],
+ "sources": [
+ {
+ "url": "original_0.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [],
+ "children": [
+ {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 0
+ },
+ "name": "inlineMe",
+ "kind": "function",
+ "isStackFrame": true,
+ "variables": [],
+ "children": []
+ }
+ ]
+ }
+ }
+ ],
+ "ranges": [
+ {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "definitionIndex": 0,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [],
+ "children": [
+ {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 10
+ },
+ "definitionIndex": 1,
+ "stackFrameType": "none",
+ "callSite": {
+ "sourceIndex": 0,
+ "line": 6,
+ "column": 2
+ },
+ "bindings": [],
+ "children": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/range-values.map b/test/fixtures/test426/decoding/scopes/range-values.map
new file mode 100644
index 00000000000000..c397423a2431c8
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/range-values.map
@@ -0,0 +1,20 @@
+{
+ "version": 3,
+ "file": "range-values.js",
+ "sources": [
+ "original_0.js"
+ ],
+ "mappings": "",
+ "names": [
+ "global",
+ "x",
+ "y",
+ "z",
+ "block",
+ "a",
+ "x_val",
+ "z_val",
+ "a_val"
+ ],
+ "scopes": "BCAAA,DCCC,BCBAI,DE,CEA,CFA,ECAA,GHAI,ECCC,GJ,FC,FG"
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/range-values.map.golden b/test/fixtures/test426/decoding/scopes/range-values.map.golden
new file mode 100644
index 00000000000000..a7cdec5139da88
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/range-values.map.golden
@@ -0,0 +1,119 @@
+{
+ "file": "range-values.js",
+ "mappings": [],
+ "sources": [
+ {
+ "url": "original_0.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [
+ "x",
+ "y",
+ "z"
+ ],
+ "children": [
+ {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 0
+ },
+ "name": null,
+ "kind": "block",
+ "isStackFrame": false,
+ "variables": [
+ "a"
+ ],
+ "children": []
+ }
+ ]
+ }
+ }
+ ],
+ "ranges": [
+ {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 0,
+ "column": 10
+ },
+ "definitionIndex": 0,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [
+ [
+ {
+ "binding": "x_val",
+ "from": {
+ "line": 0,
+ "column": 0
+ }
+ }
+ ],
+ [
+ {
+ "binding": null,
+ "from": {
+ "line": 0,
+ "column": 0
+ }
+ }
+ ],
+ [
+ {
+ "binding": "z_val",
+ "from": {
+ "line": 0,
+ "column": 0
+ }
+ }
+ ]
+ ],
+ "children": [
+ {
+ "start": {
+ "line": 0,
+ "column": 2
+ },
+ "end": {
+ "line": 0,
+ "column": 4
+ },
+ "definitionIndex": 1,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [
+ [
+ {
+ "binding": "a_val",
+ "from": {
+ "line": 0,
+ "column": 2
+ }
+ }
+ ]
+ ],
+ "children": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/sibling-ranges.map b/test/fixtures/test426/decoding/scopes/sibling-ranges.map
new file mode 100644
index 00000000000000..ea90f645612cc4
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/sibling-ranges.map
@@ -0,0 +1,13 @@
+{
+ "version": 3,
+ "file": "sibling-ranges.js",
+ "sources": [
+ "original_0.js",
+ "original_1.js"
+ ],
+ "mappings": "",
+ "names": [
+ "global"
+ ],
+ "scopes": "BCAAA,CKA,BCKAA,CKA,ECAA,FK,EDKAC,FK,EBKA,FK"
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/sibling-ranges.map.golden b/test/fixtures/test426/decoding/scopes/sibling-ranges.map.golden
new file mode 100644
index 00000000000000..dc1975d40d00a5
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/sibling-ranges.map.golden
@@ -0,0 +1,93 @@
+{
+ "file": "sibling-ranges.js",
+ "mappings": [],
+ "sources": [
+ {
+ "url": "original_0.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [],
+ "children": []
+ }
+ },
+ {
+ "url": "original_1.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 10,
+ "column": 0
+ },
+ "end": {
+ "line": 20,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [],
+ "children": []
+ }
+ }
+ ],
+ "ranges": [
+ {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 0,
+ "column": 10
+ },
+ "definitionIndex": 0,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [],
+ "children": []
+ },
+ {
+ "start": {
+ "line": 10,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 10
+ },
+ "definitionIndex": 1,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [],
+ "children": []
+ },
+ {
+ "start": {
+ "line": 20,
+ "column": 0
+ },
+ "end": {
+ "line": 20,
+ "column": 10
+ },
+ "definitionIndex": null,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [],
+ "children": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/sub-range-values.map b/test/fixtures/test426/decoding/scopes/sub-range-values.map
new file mode 100644
index 00000000000000..40155a819383c4
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/sub-range-values.map
@@ -0,0 +1,17 @@
+{
+ "version": 3,
+ "file": "sub-range-values.js",
+ "sources": [
+ "original_0.js"
+ ],
+ "mappings": "",
+ "names": [
+ "global",
+ "x",
+ "y",
+ "x_sub_val1",
+ "y_val",
+ "x_sub_val2"
+ ],
+ "scopes": "BCAAA,DCC,CKA,ECAA,GEF,HAADAADG,FK"
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/decoding/scopes/sub-range-values.map.golden b/test/fixtures/test426/decoding/scopes/sub-range-values.map.golden
new file mode 100644
index 00000000000000..c0662102819561
--- /dev/null
+++ b/test/fixtures/test426/decoding/scopes/sub-range-values.map.golden
@@ -0,0 +1,79 @@
+{
+ "file": "sub-range-values.js",
+ "mappings": [],
+ "sources": [
+ {
+ "url": "original_0.js",
+ "content": null,
+ "ignored": false,
+ "scope": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 10,
+ "column": 0
+ },
+ "name": null,
+ "kind": "global",
+ "isStackFrame": false,
+ "variables": [
+ "x",
+ "y"
+ ],
+ "children": []
+ }
+ }
+ ],
+ "ranges": [
+ {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 0,
+ "column": 10
+ },
+ "definitionIndex": 0,
+ "stackFrameType": "none",
+ "callSite": null,
+ "bindings": [
+ [
+ {
+ "binding": "x_sub_val1",
+ "from": {
+ "line": 0,
+ "column": 0
+ }
+ },
+ {
+ "binding": null,
+ "from": {
+ "line": 0,
+ "column": 3
+ }
+ },
+ {
+ "binding": "x_sub_val2",
+ "from": {
+ "line": 0,
+ "column": 6
+ }
+ }
+ ],
+ [
+ {
+ "binding": "y_val",
+ "from": {
+ "line": 0,
+ "column": 0
+ }
+ }
+ ]
+ ],
+ "children": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/test426/range-mappings-proposal-tests.json b/test/fixtures/test426/range-mappings-proposal-tests.json
index 42d47d57650338..7b108e7e5e15bd 100644
--- a/test/fixtures/test426/range-mappings-proposal-tests.json
+++ b/test/fixtures/test426/range-mappings-proposal-tests.json
@@ -36,6 +36,13 @@
"sourceMapFile": "invalid-base64-char-2.js.map",
"sourceMapIsValid": false
},
+ {
+ "name": "rangeMappingsInvalidVLQZero",
+ "description": "VLQ of zero is invalid because offsets are 1-based",
+ "baseFile": "invalid-vlq-zero.js",
+ "sourceMapFile": "invalid-vlq-zero.js.map",
+ "sourceMapIsValid": false
+ },
{
"name": "rangeMappingsOutOfRange",
"description": "Test an invalid range mapping which is outside the mappings length",
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js b/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js
new file mode 100644
index 00000000000000..c19bce975ac9e5
--- /dev/null
+++ b/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js
@@ -0,0 +1 @@
+//# sourceMappingURL=invalid-vlq-zero.js.map
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map b/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map
new file mode 100644
index 00000000000000..fe13e8f5225acd
--- /dev/null
+++ b/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map
@@ -0,0 +1,9 @@
+{
+ "version": 3,
+ "names": [],
+ "file": "invalid-vlq-zero.js",
+ "sources": ["empty-original.js"],
+ "sourcesContent": [""],
+ "mappings": "A",
+ "rangeMappings": "A"
+}
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map b/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map
index 2fab6bd1977c25..c7ffc83e012283 100644
--- a/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map
+++ b/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map
@@ -4,5 +4,5 @@
"sources": ["multiple-mappings-original.js"],
"sourcesContent": ["\"Hello World\"; function f() { } "],
"mappings": ";CAAA,aAAa,EAAG,iBAAoB;A",
- "rangeMappings": ";AC;"
+ "rangeMappings": ";BC;"
}
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map b/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map
index 9f9e581b9edd17..b6f7b44282c3ff 100644
--- a/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map
+++ b/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map
@@ -5,5 +5,5 @@
"sources": ["newline-semantics-original.js"],
"sourcesContent": ["1234\n5678"],
"mappings": "CAAA;GACG",
- "rangeMappings": "A;"
+ "rangeMappings": "B;"
}
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map b/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map
index a381b123995747..952a3c30b1d084 100644
--- a/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map
+++ b/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map
@@ -5,5 +5,5 @@
"sources": ["simple-original.js"],
"sourcesContent": ["\"Hello World\""],
"mappings": ";CAAA;A",
- "rangeMappings": ";A"
+ "rangeMappings": ";B"
}
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map
index 152fd6653a8546..f573e035dca549 100644
--- a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map
+++ b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map
@@ -5,5 +5,5 @@
"sources": ["foo.js"],
"sourcesContent": ["\"foo\""],
"mappings": "AAA",
- "rangeMappings": "B;A;A"
+ "rangeMappings": "C;B;B"
}
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map
index 5ad9a234d91120..5da575a7a6355d 100644
--- a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map
+++ b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map
@@ -5,5 +5,5 @@
"sources": ["foo.js"],
"sourcesContent": ["\"foo\""],
"mappings": "AAA",
- "rangeMappings": "B"
+ "rangeMappings": "C"
}
diff --git a/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map b/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map
index 0cd782bcee0638..856d31a9c84e14 100644
--- a/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map
+++ b/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map
@@ -5,5 +5,5 @@
"sources": ["simple-original.js"],
"sourcesContent": ["\"Hello World\""],
"mappings": ";CAAA;A",
- "rangeMappings": ";A;"
+ "rangeMappings": ";B;"
}
diff --git a/test/parallel/test-webcrypto-promise-prototype-pollution.mjs b/test/parallel/test-webcrypto-promise-prototype-pollution.mjs
index 17cc5c97716df0..5c13561dc26063 100644
--- a/test/parallel/test-webcrypto-promise-prototype-pollution.mjs
+++ b/test/parallel/test-webcrypto-promise-prototype-pollution.mjs
@@ -1,23 +1,39 @@
+// Flags: --expose-internals
+
import * as common from '../common/index.mjs';
import assert from 'node:assert';
+import { createRequire } from 'node:module';
if (!common.hasCrypto) common.skip('missing crypto');
-// WebCrypto subtle methods must not leak intermediate values
-// through Promise.prototype.then or constructor pollution.
+// WebCrypto subtle methods must not leak intermediate values through
+// Promise.prototype.then, constructor/species, thenable assimilation, or
+// inherited accessors on internally-created JWK/result objects.
// Regression test for https://github.com/nodejs/node/pull/61492
// and https://github.com/nodejs/node/issues/59699.
+//
+// When adding WebCrypto algorithms:
+// - Add a fixture with addFixture() below. Prefer the shared fixture builders
+// unless an algorithm needs operation-specific parameters.
+// - Add new operation names to operationOrder and implement that operation on
+// every affected fixture.
+// - Add new "get key length" algorithms to keyLengthTargets, unless the
+// algorithm is itself a KDF whose getKeyLength() result is null; those belong
+// in nullKeyLengthAlgorithms.
+// The registry assertions at the bottom make missing updates fail loudly.
-import { hasOpenSSL } from '../common/crypto.js';
-
+const require = createRequire(import.meta.url);
+const { kSupportedAlgorithms } = require('internal/crypto/util');
const { subtle } = globalThis.crypto;
Promise.prototype.then = common.mustNotCall('Promise.prototype.then');
+const data = new TextEncoder().encode('prototype pollution');
+
// WebCrypto methods return native promises. Re-wrapping a promise with
// PromiseResolve() or chaining it with Promise.prototype.then can read
// user-mutated constructor/species accessors.
-async function assertNoPromiseConstructorAccess(name, fn) {
+function assertNoPromiseConstructorAccess(name, fn) {
const constructorDescriptor =
Object.getOwnPropertyDescriptor(Promise.prototype, 'constructor');
const speciesDescriptor =
@@ -43,7 +59,7 @@ async function assertNoPromiseConstructorAccess(name, fn) {
constructorDescriptor);
Object.defineProperty(Promise, Symbol.species, speciesDescriptor);
}
- return await promise;
+ return promise;
}
// Exercise each export format through the same promise-constructor guard.
@@ -96,6 +112,11 @@ function assertNoInheritedObjectThenAccess(name, fn) {
fn);
}
+function assertCryptoKeyResult(name, fn) {
+ return assertNoInheritedCryptoKeyThenAccess(name, () =>
+ assertNoPromiseConstructorAccess(name, fn));
+}
+
// wrapKey('jwk') stringifies an internally exported JWK. The spec does this
// in a fresh global, so inherited toJSON hooks from the current global must
// not observe or replace key material.
@@ -131,7 +152,9 @@ async function assertNoInheritedToJSONAccess(name, fn) {
}
// JWK export creates and fills a result object. The exported members must be
-// own data properties, not writes that can observe inherited accessors.
+// own data properties, not writes that can observe inherited accessors. Keep
+// this list complete: if exportKey('jwk') starts returning a new JWK member,
+// this helper must poison that member on Object.prototype too.
async function assertNoInheritedJwkPropertyAccess(name, fn) {
const properties = [
'alg',
@@ -153,6 +176,7 @@ async function assertNoInheritedJwkPropertyAccess(name, fn) {
'x',
'y',
];
+ const handledProperties = new Set(properties);
const descriptors = new Map();
for (const property of properties) {
descriptors.set(
@@ -165,8 +189,9 @@ async function assertNoInheritedJwkPropertyAccess(name, fn) {
set: common.mustNotCall(`${name} Object.prototype.${property} setter`),
});
}
+ let result;
try {
- return await fn();
+ result = await fn();
} finally {
for (const property of properties) {
const descriptor = descriptors.get(property);
@@ -177,6 +202,16 @@ async function assertNoInheritedJwkPropertyAccess(name, fn) {
}
}
}
+
+ if (result !== null && typeof result === 'object') {
+ for (const property of Object.keys(result)) {
+ assert(
+ handledProperties.has(property),
+ `${name} returned unhandled JWK property ${property}`);
+ }
+ }
+
+ return result;
}
// unwrapKey('jwk') parses a JWK and then converts it to the JsonWebKey IDL
@@ -292,389 +327,786 @@ async function assertNoRawSharedKeyObjectThenAccess(name, fn) {
}
}
-await assertNoPromiseConstructorAccess('digest', () =>
- subtle.digest('SHA-256', new Uint8Array([1, 2, 3])));
+function algorithm(name, params = {}) {
+ return { name, ...params };
+}
-const secretKey = await assertNoPromiseConstructorAccess(
- 'generateKey secret',
- () => subtle.generateKey(
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
+function rsaAlgorithm(name) {
+ return algorithm(name, {
+ modulusLength: 1024,
+ publicExponent: new Uint8Array([1, 0, 1]),
+ hash: 'SHA-256',
+ });
+}
-const extractableKeyPair = await assertNoPromiseConstructorAccess('generateKey pair', () =>
- subtle.generateKey(
- { name: 'ECDSA', namedCurve: 'P-256' },
- true,
- ['sign', 'verify']));
-
-const rawKey = globalThis.crypto.getRandomValues(new Uint8Array(32));
-
-const importedKey = await assertNoPromiseConstructorAccess('importKey', () =>
- subtle.importKey(
- 'raw',
- rawKey,
- { name: 'AES-CBC', length: 256 },
- false,
- ['encrypt', 'decrypt']));
-
-await assertNoInheritedCryptoKeyThenAccess('importKey', () =>
- subtle.importKey(
- 'raw',
- rawKey,
- { name: 'AES-CBC', length: 256 },
- false,
- ['encrypt', 'decrypt']));
-
-await assertNoInheritedJwkPropertyAccess('exportKey jwk secret', () =>
- assertExportKeyNoPromiseConstructorAccess(
- 'jwk secret',
- 'jwk',
- secretKey));
-await assertNoInheritedObjectThenAccess('exportKey jwk secret', () =>
- subtle.exportKey('jwk', secretKey));
-await assertNoInheritedJwkPropertyAccess('exportKey jwk public', () =>
- assertExportKeyNoPromiseConstructorAccess(
- 'jwk public',
- 'jwk',
- extractableKeyPair.publicKey));
-await assertNoInheritedObjectThenAccess('exportKey jwk public', () =>
- subtle.exportKey('jwk', extractableKeyPair.publicKey));
-await assertNoInheritedJwkPropertyAccess('exportKey jwk private', () =>
- assertExportKeyNoPromiseConstructorAccess(
- 'jwk private',
- 'jwk',
- extractableKeyPair.privateKey));
-await assertNoInheritedObjectThenAccess('exportKey jwk private', () =>
- subtle.exportKey('jwk', extractableKeyPair.privateKey));
-await assertNoInheritedArrayBufferThenAccess('exportKey raw secret', () =>
- subtle.exportKey('raw', secretKey));
-await assertExportKeyNoPromiseConstructorAccess(
- 'raw secret',
- 'raw',
- secretKey);
-await assertNoInheritedArrayBufferThenAccess('exportKey spki', () =>
- subtle.exportKey('spki', extractableKeyPair.publicKey));
-await assertExportKeyNoPromiseConstructorAccess(
- 'spki',
- 'spki',
- extractableKeyPair.publicKey);
-await assertNoInheritedArrayBufferThenAccess('exportKey pkcs8', () =>
- subtle.exportKey('pkcs8', extractableKeyPair.privateKey));
-await assertExportKeyNoPromiseConstructorAccess(
- 'pkcs8',
- 'pkcs8',
- extractableKeyPair.privateKey);
-await assertNoInheritedArrayBufferThenAccess('exportKey raw-public', () =>
- subtle.exportKey('raw-public', extractableKeyPair.publicKey));
-await assertExportKeyNoPromiseConstructorAccess(
- 'raw-public',
- 'raw-public',
- extractableKeyPair.publicKey);
-
-const iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
-const plaintext = new TextEncoder().encode('Hello, world!');
-
-const ciphertext = await assertNoPromiseConstructorAccess('encrypt', () =>
- subtle.encrypt({ name: 'AES-CBC', iv }, importedKey, plaintext));
-
-await assertNoPromiseConstructorAccess('decrypt', () =>
- subtle.decrypt({ name: 'AES-CBC', iv }, importedKey, ciphertext));
-
-const signingKey = await subtle.generateKey(
- { name: 'HMAC', hash: 'SHA-256' },
- false,
- ['sign', 'verify']);
+function importRsaAlgorithm(name) {
+ return algorithm(name, { hash: 'SHA-256' });
+}
-const data = new TextEncoder().encode('test data');
+function exportArrayBuffer(name, format, key) {
+ return assertNoInheritedArrayBufferThenAccess(`exportKey ${format} ${name}`, () =>
+ assertExportKeyNoPromiseConstructorAccess(`${format} ${name}`, format, key));
+}
-const signature = await assertNoPromiseConstructorAccess('sign', () =>
- subtle.sign('HMAC', signingKey, data));
+function exportJwk(name, key) {
+ return assertNoInheritedJwkPropertyAccess(`exportKey jwk ${name}`, () =>
+ assertNoInheritedObjectThenAccess(`exportKey jwk ${name}`, () =>
+ assertExportKeyNoPromiseConstructorAccess(`jwk ${name}`, 'jwk', key)));
+}
-await assertNoPromiseConstructorAccess('verify', () =>
- subtle.verify('HMAC', signingKey, signature, data));
+function importCryptoKey(name, format, keyData, importAlgorithm, extractable, usages) {
+ return assertCryptoKeyResult(`importKey ${format} ${name}`, () =>
+ subtle.importKey(format, keyData, importAlgorithm, extractable, usages));
+}
-const pbkdf2Key = await subtle.importKey(
- 'raw', rawKey, 'PBKDF2', false, ['deriveBits', 'deriveKey']);
-
-await assertNoPromiseConstructorAccess('deriveBits', () =>
- subtle.deriveBits(
- { name: 'PBKDF2', salt: rawKey, iterations: 1000, hash: 'SHA-256' },
- pbkdf2Key,
- 256));
-
-await assertNoPromiseConstructorAccess('deriveBits PBKDF2 zero-length', () =>
- subtle.deriveBits(
- { name: 'PBKDF2', salt: rawKey, iterations: 1000, hash: 'SHA-256' },
- pbkdf2Key,
- 0));
-
-const hkdfKey = await subtle.importKey(
- 'raw', rawKey, 'HKDF', false, ['deriveBits']);
-
-await assertNoPromiseConstructorAccess('deriveBits HKDF zero-length', () =>
- subtle.deriveBits(
- { name: 'HKDF', hash: 'SHA-256', salt: rawKey, info: rawKey },
- hkdfKey,
- 0));
-
-const ecdhKeyPair = await subtle.generateKey(
- { name: 'ECDH', namedCurve: 'P-256' },
- false,
- ['deriveBits']);
-
-await assertNoPromiseConstructorAccess('deriveBits ECDH', () =>
- subtle.deriveBits(
- { name: 'ECDH', public: ecdhKeyPair.publicKey },
- ecdhKeyPair.privateKey,
- 256));
-
-await assertNoPromiseConstructorAccess('deriveKey', () =>
- subtle.deriveKey(
- { name: 'PBKDF2', salt: rawKey, iterations: 1000, hash: 'SHA-256' },
- pbkdf2Key,
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
-await assertNoInheritedArrayBufferThenAccess('deriveKey', () =>
- subtle.deriveKey(
- { name: 'PBKDF2', salt: rawKey, iterations: 1000, hash: 'SHA-256' },
- pbkdf2Key,
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
-await assertNoInheritedCryptoKeyThenAccess('deriveKey result', () =>
- subtle.deriveKey(
- { name: 'PBKDF2', salt: rawKey, iterations: 1000, hash: 'SHA-256' },
- pbkdf2Key,
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
-
-const wrappingKey = await subtle.generateKey(
- { name: 'AES-KW', length: 256 }, true, ['wrapKey', 'unwrapKey']);
-
-const keyToWrap = await subtle.generateKey(
- { name: 'AES-CBC', length: 256 }, true, ['encrypt', 'decrypt']);
-
-const wrapped = await assertNoPromiseConstructorAccess('wrapKey', () =>
- subtle.wrapKey('raw', keyToWrap, wrappingKey, 'AES-KW'));
-
-const wrappedJwk = await assertNoInheritedJwkPropertyAccess('wrapKey jwk', () =>
- assertNoInheritedToJSONAccess('wrapKey jwk', () =>
- assertNoUserMutableEncodeAccess('wrapKey jwk', () =>
- assertNoPromiseConstructorAccess('wrapKey jwk', () =>
- subtle.wrapKey('jwk', keyToWrap, wrappingKey, 'AES-KW')))));
-
-await assertNoPromiseConstructorAccess('unwrapKey', () =>
- subtle.unwrapKey(
- 'raw',
- wrapped,
- wrappingKey,
- 'AES-KW',
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
-await assertNoInheritedArrayBufferThenAccess('unwrapKey', () =>
- subtle.unwrapKey(
- 'raw',
- wrapped,
- wrappingKey,
- 'AES-KW',
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
-await assertNoInheritedCryptoKeyThenAccess('unwrapKey result', () =>
- subtle.unwrapKey(
- 'raw',
- wrapped,
- wrappingKey,
- 'AES-KW',
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
+async function getKeyToWrap() {
+ if (getKeyToWrap.key === undefined) {
+ getKeyToWrap.key = await subtle.generateKey(
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt']);
+ }
+ return getKeyToWrap.key;
+}
-await assertNoUserMutableDecodeAccess('unwrapKey jwk', () =>
- assertNoPromiseConstructorAccess('unwrapKey jwk', () =>
- subtle.unwrapKey(
+function addCommonKeyExportTests(fixture) {
+ fixture.exportKey = async (ctx) => {
+ if (fixture.rawFormat !== undefined)
+ ctx.raw = await exportArrayBuffer(fixture.name, fixture.rawFormat, ctx.key);
+ ctx.jwk = await exportJwk(fixture.name, ctx.key);
+ };
+ fixture.importKey = async (ctx) => {
+ if (fixture.rawFormat !== undefined) {
+ ctx.importedRaw = await importCryptoKey(
+ fixture.name,
+ fixture.rawFormat,
+ ctx.raw,
+ fixture.importAlgorithm,
+ true,
+ fixture.usages);
+ }
+ ctx.importedJwk = await importCryptoKey(
+ fixture.name,
'jwk',
- wrappedJwk,
- wrappingKey,
- 'AES-KW',
- { name: 'AES-CBC', length: 256 },
+ ctx.jwk,
+ fixture.importAlgorithm,
true,
- ['encrypt', 'decrypt'])));
-await assertNoInheritedArrayBufferThenAccess('unwrapKey jwk', () =>
- subtle.unwrapKey(
- 'jwk',
- wrappedJwk,
- wrappingKey,
- 'AES-KW',
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
-await assertNoInheritedCryptoKeyThenAccess('unwrapKey jwk result', () =>
- subtle.unwrapKey(
- 'jwk',
- wrappedJwk,
- wrappingKey,
- 'AES-KW',
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt', 'decrypt']));
+ fixture.usages);
+ };
+}
-{
- const jwkUnwrappingKey = await subtle.generateKey(
- { name: 'AES-CBC', length: 128 },
- true,
- ['encrypt', 'unwrapKey']);
+function secretKeyFixture(options) {
+ const fixture = {
+ ...options,
+ generateKey: async (ctx) => {
+ ctx.key = await assertNoPromiseConstructorAccess(`generateKey ${options.name}`, () =>
+ subtle.generateKey(options.generateAlgorithm, true, options.usages));
+ },
+ };
- {
- const iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
- const validWrappedJwk = await subtle.encrypt(
- { name: 'AES-CBC', iv },
- jwkUnwrappingKey,
- Buffer.from('{"kty":"oct","k":"AAAAAAAAAAAAAAAAAAAAAA"}'));
-
- await assertNoUserMutableDecodeAccess('unwrapKey jwk AES-CBC', () =>
- assertNoPromiseConstructorAccess('unwrapKey jwk AES-CBC', () =>
- subtle.unwrapKey(
- 'jwk',
- validWrappedJwk,
- jwkUnwrappingKey,
- { name: 'AES-CBC', iv },
- { name: 'AES-CBC', length: 128 },
+ addCommonKeyExportTests(fixture);
+
+ if (options.encryptAlgorithm !== undefined) {
+ fixture.encrypt = async (ctx) => {
+ ctx.ciphertext = await assertNoPromiseConstructorAccess(`encrypt ${options.name}`, () =>
+ subtle.encrypt(options.encryptAlgorithm, ctx.key, data));
+ };
+ fixture.decrypt = async (ctx) => {
+ await assertNoPromiseConstructorAccess(`decrypt ${options.name}`, () =>
+ subtle.decrypt(options.encryptAlgorithm, ctx.key, ctx.ciphertext));
+ };
+ }
+
+ if (options.signAlgorithm !== undefined) {
+ fixture.sign = async (ctx) => {
+ ctx.signature = await assertNoPromiseConstructorAccess(`sign ${options.name}`, () =>
+ subtle.sign(options.signAlgorithm, ctx.key, data));
+ };
+ fixture.verify = async (ctx) => {
+ await assertNoPromiseConstructorAccess(`verify ${options.name}`, () =>
+ subtle.verify(options.signAlgorithm, ctx.key, ctx.signature, data));
+ };
+ }
+
+ if (options.wrapAlgorithm !== undefined) {
+ fixture.wrapKey = async (ctx) => {
+ const keyToWrap = await getKeyToWrap();
+ ctx.wrappedRawSecret = await assertNoPromiseConstructorAccess(
+ `wrapKey raw-secret ${options.name}`,
+ () => subtle.wrapKey('raw-secret', keyToWrap, ctx.key, options.wrapAlgorithm));
+ ctx.wrappedJwk = await assertNoInheritedJwkPropertyAccess(
+ `wrapKey jwk ${options.name}`,
+ () => assertNoInheritedToJSONAccess(
+ `wrapKey jwk ${options.name}`,
+ () => assertNoUserMutableEncodeAccess(
+ `wrapKey jwk ${options.name}`, () =>
+ assertNoPromiseConstructorAccess(`wrapKey jwk ${options.name}`, () =>
+ subtle.wrapKey('jwk', keyToWrap, ctx.key, options.wrapAlgorithm)))));
+ };
+ fixture.unwrapKey = async (ctx) => {
+ await assertNoInheritedArrayBufferThenAccess(`unwrapKey raw-secret ${options.name}`, () =>
+ assertCryptoKeyResult(`unwrapKey raw-secret ${options.name} result`, () =>
+ subtle.unwrapKey(
+ 'raw-secret',
+ ctx.wrappedRawSecret,
+ ctx.key,
+ options.wrapAlgorithm,
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt'])));
+ await assertNoUserMutableDecodeAccess(`unwrapKey jwk ${options.name}`, () =>
+ assertNoInheritedArrayBufferThenAccess(`unwrapKey jwk ${options.name}`, () =>
+ assertCryptoKeyResult(`unwrapKey jwk ${options.name} result`, () =>
+ subtle.unwrapKey(
+ 'jwk',
+ ctx.wrappedJwk,
+ ctx.key,
+ options.wrapAlgorithm,
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt']))));
+ };
+ }
+
+ return fixture;
+}
+
+function pairKeyFixture(options) {
+ const fixture = {
+ ...options,
+ generateKey: async (ctx) => {
+ ctx.keyPair = await assertNoPromiseConstructorAccess(`generateKey ${options.name}`, () =>
+ subtle.generateKey(options.generateAlgorithm, true, options.usages));
+ },
+ exportKey: async (ctx) => {
+ if (options.spki !== false) {
+ ctx.spki = await exportArrayBuffer(`${options.name} public`, 'spki', ctx.keyPair.publicKey);
+ ctx.pkcs8 = await exportArrayBuffer(`${options.name} private`, 'pkcs8', ctx.keyPair.privateKey);
+ }
+ if (options.rawPublic === true) {
+ ctx.rawPublic = await exportArrayBuffer(
+ `${options.name} public`,
+ 'raw-public',
+ ctx.keyPair.publicKey);
+ }
+ if (options.rawSeed === true) {
+ ctx.rawSeed = await exportArrayBuffer(
+ `${options.name} private`,
+ 'raw-seed',
+ ctx.keyPair.privateKey);
+ }
+ ctx.publicJwk = await exportJwk(`${options.name} public`, ctx.keyPair.publicKey);
+ ctx.privateJwk = await exportJwk(`${options.name} private`, ctx.keyPair.privateKey);
+ },
+ importKey: async (ctx) => {
+ if (options.spki !== false) {
+ ctx.importedSpki = await importCryptoKey(
+ `${options.name} public`,
+ 'spki',
+ ctx.spki,
+ options.importAlgorithm,
+ true,
+ options.publicUsages);
+ ctx.importedPkcs8 = await importCryptoKey(
+ `${options.name} private`,
+ 'pkcs8',
+ ctx.pkcs8,
+ options.importAlgorithm,
+ true,
+ options.privateUsages);
+ }
+ if (options.rawPublic === true) {
+ ctx.importedRawPublic = await importCryptoKey(
+ `${options.name} public`,
+ 'raw-public',
+ ctx.rawPublic,
+ options.importAlgorithm,
true,
- ['encrypt'])));
- await assertNoInheritedCryptoKeyThenAccess(
- 'unwrapKey jwk AES-CBC result',
- () => subtle.unwrapKey(
+ options.publicUsages);
+ }
+ if (options.rawSeed === true) {
+ ctx.importedRawSeed = await importCryptoKey(
+ `${options.name} private`,
+ 'raw-seed',
+ ctx.rawSeed,
+ options.importAlgorithm,
+ true,
+ options.privateUsages);
+ }
+ ctx.importedPublicJwk = await importCryptoKey(
+ `${options.name} public`,
+ 'jwk',
+ ctx.publicJwk,
+ options.importAlgorithm,
+ true,
+ options.publicUsages);
+ ctx.importedPrivateJwk = await importCryptoKey(
+ `${options.name} private`,
'jwk',
- validWrappedJwk,
- jwkUnwrappingKey,
- { name: 'AES-CBC', iv },
- { name: 'AES-CBC', length: 128 },
+ ctx.privateJwk,
+ options.importAlgorithm,
true,
- ['encrypt']));
+ options.privateUsages);
+ },
+ };
+
+ if (options.signAlgorithm !== undefined) {
+ fixture.sign = async (ctx) => {
+ ctx.signature = await assertNoPromiseConstructorAccess(`sign ${options.name}`, () =>
+ subtle.sign(options.signAlgorithm, ctx.keyPair.privateKey, data));
+ };
+ fixture.verify = async (ctx) => {
+ await assertNoPromiseConstructorAccess(`verify ${options.name}`, () =>
+ subtle.verify(options.signAlgorithm, ctx.keyPair.publicKey, ctx.signature, data));
+ };
}
- {
- const iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
- const wrappedRawKey = await subtle.encrypt(
- { name: 'AES-CBC', iv },
- jwkUnwrappingKey,
- rawKey);
-
- await assertNoPromiseConstructorAccess('unwrapKey raw AES-CBC', () =>
- subtle.unwrapKey(
- 'raw',
- wrappedRawKey,
- jwkUnwrappingKey,
- { name: 'AES-CBC', iv },
- { name: 'AES-CBC', length: 256 },
- true,
- ['encrypt']));
- await assertNoInheritedArrayBufferThenAccess('unwrapKey raw AES-CBC', () =>
- subtle.unwrapKey(
- 'raw',
- wrappedRawKey,
- jwkUnwrappingKey,
- { name: 'AES-CBC', iv },
- { name: 'AES-CBC', length: 256 },
+ if (options.encryptAlgorithm !== undefined) {
+ fixture.encrypt = async (ctx) => {
+ ctx.ciphertext = await assertNoPromiseConstructorAccess(`encrypt ${options.name}`, () =>
+ subtle.encrypt(options.encryptAlgorithm, ctx.keyPair.publicKey, data));
+ };
+ fixture.decrypt = async (ctx) => {
+ await assertNoPromiseConstructorAccess(`decrypt ${options.name}`, () =>
+ subtle.decrypt(options.encryptAlgorithm, ctx.keyPair.privateKey, ctx.ciphertext));
+ };
+ }
+
+ if (options.deriveAlgorithm !== undefined) {
+ fixture.deriveBits = async (ctx) => {
+ ctx.peerKeyPair ??= await subtle.generateKey(
+ options.generateAlgorithm,
true,
- ['encrypt']));
- await assertNoInheritedCryptoKeyThenAccess(
- 'unwrapKey raw AES-CBC result',
- () => subtle.unwrapKey(
- 'raw',
- wrappedRawKey,
- jwkUnwrappingKey,
- { name: 'AES-CBC', iv },
- { name: 'AES-CBC', length: 256 },
+ options.usages);
+ ctx.derivedBits = await assertNoPromiseConstructorAccess(`deriveBits ${options.name}`, () =>
+ subtle.deriveBits(
+ options.deriveAlgorithm(ctx.peerKeyPair.publicKey),
+ ctx.keyPair.privateKey,
+ options.deriveLength));
+ };
+ fixture.extra = async (ctx) => {
+ ctx.peerKeyPair ??= await subtle.generateKey(
+ options.generateAlgorithm,
true,
- ['encrypt']));
+ options.usages);
+ await assertNoInheritedArrayBufferThenAccess(`deriveKey ${options.name}`, () =>
+ assertCryptoKeyResult(`deriveKey ${options.name} result`, () =>
+ subtle.deriveKey(
+ options.deriveAlgorithm(ctx.peerKeyPair.publicKey),
+ ctx.keyPair.privateKey,
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt'])));
+ };
}
- {
- const iv = globalThis.crypto.getRandomValues(new Uint8Array(16));
- const missingKtyWrappedJwk = await subtle.encrypt(
- { name: 'AES-CBC', iv },
- jwkUnwrappingKey,
- Buffer.from('{"k":"AAAAAAAAAAAAAAAAAAAAAA"}'));
+ fixture.getPublicKey = async (ctx) => {
+ await assertCryptoKeyResult(`getPublicKey ${options.name}`, () =>
+ subtle.getPublicKey(ctx.keyPair.privateKey, options.publicUsages));
+ };
- await assertMissingJwkKtyIgnoresPrototype(() =>
- subtle.unwrapKey(
- 'jwk',
- missingKtyWrappedJwk,
- jwkUnwrappingKey,
- { name: 'AES-CBC', iv },
- { name: 'AES-CBC', length: 128 },
- true,
- ['encrypt']));
- }
+ return fixture;
}
-await assertNoPromiseConstructorAccess('getPublicKey', () =>
- subtle.getPublicKey(extractableKeyPair.privateKey, ['verify']));
-await assertNoInheritedCryptoKeyThenAccess('getPublicKey', () =>
- subtle.getPublicKey(extractableKeyPair.privateKey, ['verify']));
-
-if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
- const kemPair = await subtle.generateKey(
- { name: 'ML-KEM-768' }, true,
- ['encapsulateKey', 'encapsulateBits', 'decapsulateKey', 'decapsulateBits']);
+function kdfFixture(options) {
+ return {
+ ...options,
+ importKey: async (ctx) => {
+ ctx.key = await importCryptoKey(
+ options.name,
+ options.rawFormat,
+ new Uint8Array(32).fill(1),
+ options.importAlgorithm,
+ false,
+ ['deriveBits', 'deriveKey']);
+ },
+ deriveBits: async (ctx) => {
+ await assertNoPromiseConstructorAccess(`deriveBits ${options.name}`, () =>
+ subtle.deriveBits(options.deriveAlgorithm, ctx.key, 256));
+ },
+ extra: async (ctx) => {
+ await assertNoInheritedArrayBufferThenAccess(`deriveKey ${options.name}`, () =>
+ assertCryptoKeyResult(`deriveKey ${options.name} result`, () =>
+ subtle.deriveKey(
+ options.deriveAlgorithm,
+ ctx.key,
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt'])));
+ },
+ };
+}
- await assertNoInheritedArrayBufferThenAccess('exportKey raw-seed', () =>
- subtle.exportKey('raw-seed', kemPair.privateKey));
- await assertExportKeyNoPromiseConstructorAccess(
- 'raw-seed',
- 'raw-seed',
- kemPair.privateKey);
+function kemFixture(options) {
+ const fixture = pairKeyFixture({
+ ...options,
+ usages: [
+ 'encapsulateKey',
+ 'encapsulateBits',
+ 'decapsulateKey',
+ 'decapsulateBits',
+ ],
+ publicUsages: ['encapsulateKey', 'encapsulateBits'],
+ privateUsages: ['decapsulateKey', 'decapsulateBits'],
+ rawPublic: true,
+ rawSeed: true,
+ });
- const { ciphertext: ct1 } =
- await assertNoRawSharedKeyObjectThenAccess('encapsulateKey', () =>
- assertNoPromiseConstructorAccess('encapsulateKey', () =>
+ fixture.encapsulate = async (ctx) => {
+ ctx.encapsulatedBits = await assertNoPromiseConstructorAccess(
+ `encapsulateBits ${options.name}`, () =>
+ subtle.encapsulateBits(algorithm(options.name), ctx.keyPair.publicKey));
+ ctx.encapsulatedKey = await assertNoRawSharedKeyObjectThenAccess(
+ `encapsulateKey ${options.name}`,
+ () => assertNoPromiseConstructorAccess(`encapsulateKey ${options.name}`, () =>
subtle.encapsulateKey(
- { name: 'ML-KEM-768' },
- kemPair.publicKey,
+ algorithm(options.name),
+ ctx.keyPair.publicKey,
'HKDF',
false,
['deriveBits'])));
+ };
+ fixture.decapsulate = async (ctx) => {
+ await assertNoPromiseConstructorAccess(`decapsulateBits ${options.name}`, () =>
+ subtle.decapsulateBits(
+ algorithm(options.name),
+ ctx.keyPair.privateKey,
+ ctx.encapsulatedBits.ciphertext));
+ await assertNoInheritedArrayBufferThenAccess(`decapsulateKey ${options.name}`, () =>
+ assertCryptoKeyResult(`decapsulateKey ${options.name} result`, () =>
+ subtle.decapsulateKey(
+ algorithm(options.name),
+ ctx.keyPair.privateKey,
+ ctx.encapsulatedKey.ciphertext,
+ 'HKDF',
+ false,
+ ['deriveBits'])));
+ };
+
+ return fixture;
+}
+
+// The fixture registry mirrors kSupportedAlgorithms by algorithm name. Each
+// fixture supplies the public SubtleCrypto calls needed to exercise the
+// registered operations for that algorithm.
+const fixtures = new Map();
+
+function addFixture(name, fixture) {
+ fixtures.set(name, fixture);
+}
+
+for (const name of ['AES-CBC', 'AES-CTR', 'AES-GCM', 'AES-OCB']) {
+ const encryptAlgorithms = {
+ 'AES-CBC': algorithm(name, { iv: new Uint8Array(16) }),
+ 'AES-CTR': algorithm(name, { counter: new Uint8Array(16), length: 64 }),
+ 'AES-GCM': algorithm(name, {
+ iv: new Uint8Array(12),
+ additionalData: new Uint8Array(1),
+ tagLength: 128,
+ }),
+ 'AES-OCB': algorithm(name, {
+ iv: new Uint8Array(15),
+ additionalData: new Uint8Array(1),
+ tagLength: 128,
+ }),
+ };
+ addFixture(name, secretKeyFixture({
+ name,
+ generateAlgorithm: algorithm(name, { length: 128 }),
+ importAlgorithm: algorithm(name),
+ usages: ['encrypt', 'decrypt'],
+ rawFormat: 'raw-secret',
+ encryptAlgorithm: encryptAlgorithms[name],
+ }));
+}
+
+addFixture('AES-KW', secretKeyFixture({
+ name: 'AES-KW',
+ generateAlgorithm: algorithm('AES-KW', { length: 128 }),
+ importAlgorithm: algorithm('AES-KW'),
+ usages: ['wrapKey', 'unwrapKey'],
+ rawFormat: 'raw-secret',
+ wrapAlgorithm: 'AES-KW',
+}));
+
+addFixture('ChaCha20-Poly1305', secretKeyFixture({
+ name: 'ChaCha20-Poly1305',
+ generateAlgorithm: algorithm('ChaCha20-Poly1305'),
+ importAlgorithm: algorithm('ChaCha20-Poly1305'),
+ usages: ['encrypt', 'decrypt'],
+ rawFormat: 'raw-secret',
+ encryptAlgorithm: algorithm('ChaCha20-Poly1305', {
+ iv: new Uint8Array(12),
+ additionalData: new Uint8Array(1),
+ tagLength: 128,
+ }),
+}));
+
+addFixture('HMAC', secretKeyFixture({
+ name: 'HMAC',
+ generateAlgorithm: algorithm('HMAC', { hash: 'SHA-256', length: 256 }),
+ importAlgorithm: algorithm('HMAC', { hash: 'SHA-256' }),
+ usages: ['sign', 'verify'],
+ rawFormat: 'raw-secret',
+ signAlgorithm: 'HMAC',
+}));
+
+for (const name of ['KMAC128', 'KMAC256']) {
+ addFixture(name, secretKeyFixture({
+ name,
+ generateAlgorithm: algorithm(name, {
+ length: name === 'KMAC128' ? 128 : 256,
+ }),
+ importAlgorithm: algorithm(name),
+ usages: ['sign', 'verify'],
+ rawFormat: 'raw-secret',
+ signAlgorithm: algorithm(name, { outputLength: 256 }),
+ }));
+}
+
+for (const name of ['RSASSA-PKCS1-v1_5', 'RSA-PSS']) {
+ addFixture(name, pairKeyFixture({
+ name,
+ generateAlgorithm: rsaAlgorithm(name),
+ importAlgorithm: importRsaAlgorithm(name),
+ usages: ['sign', 'verify'],
+ publicUsages: ['verify'],
+ privateUsages: ['sign'],
+ signAlgorithm: name === 'RSA-PSS' ?
+ algorithm(name, { saltLength: 32 }) :
+ algorithm(name),
+ }));
+}
+
+addFixture('RSA-OAEP', pairKeyFixture({
+ name: 'RSA-OAEP',
+ generateAlgorithm: rsaAlgorithm('RSA-OAEP'),
+ importAlgorithm: importRsaAlgorithm('RSA-OAEP'),
+ usages: ['encrypt', 'decrypt'],
+ publicUsages: ['encrypt'],
+ privateUsages: ['decrypt'],
+ encryptAlgorithm: algorithm('RSA-OAEP', { label: new Uint8Array(1) }),
+}));
+
+addFixture('ECDSA', pairKeyFixture({
+ name: 'ECDSA',
+ generateAlgorithm: algorithm('ECDSA', { namedCurve: 'P-256' }),
+ importAlgorithm: algorithm('ECDSA', { namedCurve: 'P-256' }),
+ usages: ['sign', 'verify'],
+ publicUsages: ['verify'],
+ privateUsages: ['sign'],
+ rawPublic: true,
+ signAlgorithm: algorithm('ECDSA', { hash: 'SHA-256' }),
+}));
+
+addFixture('ECDH', pairKeyFixture({
+ name: 'ECDH',
+ generateAlgorithm: algorithm('ECDH', { namedCurve: 'P-256' }),
+ importAlgorithm: algorithm('ECDH', { namedCurve: 'P-256' }),
+ usages: ['deriveBits', 'deriveKey'],
+ publicUsages: [],
+ privateUsages: ['deriveBits', 'deriveKey'],
+ rawPublic: true,
+ deriveAlgorithm: (publicKey) => algorithm('ECDH', { public: publicKey }),
+ deriveLength: 256,
+}));
+
+for (const name of ['Ed25519', 'Ed448']) {
+ addFixture(name, pairKeyFixture({
+ name,
+ generateAlgorithm: algorithm(name),
+ importAlgorithm: algorithm(name),
+ usages: ['sign', 'verify'],
+ publicUsages: ['verify'],
+ privateUsages: ['sign'],
+ rawPublic: true,
+ signAlgorithm: algorithm(name),
+ }));
+}
+
+for (const name of ['X25519', 'X448']) {
+ addFixture(name, pairKeyFixture({
+ name,
+ generateAlgorithm: algorithm(name),
+ importAlgorithm: algorithm(name),
+ usages: ['deriveBits', 'deriveKey'],
+ publicUsages: [],
+ privateUsages: ['deriveBits', 'deriveKey'],
+ rawPublic: true,
+ deriveAlgorithm: (publicKey) => algorithm(name, { public: publicKey }),
+ deriveLength: name === 'X25519' ? 256 : 448,
+ }));
+}
+
+for (const name of ['ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']) {
+ addFixture(name, pairKeyFixture({
+ name,
+ generateAlgorithm: algorithm(name),
+ importAlgorithm: algorithm(name),
+ usages: ['sign', 'verify'],
+ publicUsages: ['verify'],
+ privateUsages: ['sign'],
+ rawPublic: true,
+ rawSeed: true,
+ signAlgorithm: algorithm(name),
+ }));
+}
+
+for (const name of [
+ 'ML-KEM-512',
+ 'ML-KEM-768',
+ 'ML-KEM-1024',
+]) {
+ addFixture(name, kemFixture({
+ name,
+ generateAlgorithm: algorithm(name),
+ importAlgorithm: algorithm(name),
+ }));
+}
+
+for (const name of ['HKDF', 'PBKDF2']) {
+ addFixture(name, kdfFixture({
+ name,
+ importAlgorithm: name,
+ rawFormat: 'raw-secret',
+ deriveAlgorithm: name === 'HKDF' ?
+ algorithm(name, {
+ hash: 'SHA-256',
+ salt: new Uint8Array(8),
+ info: new Uint8Array(8),
+ }) :
+ algorithm(name, {
+ hash: 'SHA-256',
+ salt: new Uint8Array(8),
+ iterations: 1,
+ }),
+ }));
+}
+
+for (const name of ['Argon2d', 'Argon2i', 'Argon2id']) {
+ addFixture(name, kdfFixture({
+ name,
+ importAlgorithm: name,
+ rawFormat: 'raw-secret',
+ deriveAlgorithm: algorithm(name, {
+ memory: 32,
+ passes: 1,
+ parallelism: 1,
+ nonce: new Uint8Array(16),
+ }),
+ }));
+}
+
+function digestAlgorithm(name) {
+ if (name === 'cSHAKE128')
+ return algorithm(name, { outputLength: 256 });
+ if (name === 'cSHAKE256')
+ return algorithm(name, { outputLength: 512 });
+ if (name === 'TurboSHAKE128')
+ return algorithm(name, { outputLength: 256 });
+ if (name === 'TurboSHAKE256')
+ return algorithm(name, { outputLength: 512 });
+ if (name === 'KT128')
+ return algorithm(name, { outputLength: 256 });
+ if (name === 'KT256')
+ return algorithm(name, { outputLength: 512 });
+ return name;
+}
+
+for (const name of [
+ 'SHA-1',
+ 'SHA-256',
+ 'SHA-384',
+ 'SHA-512',
+ 'SHA3-256',
+ 'SHA3-384',
+ 'SHA3-512',
+ 'cSHAKE128',
+ 'cSHAKE256',
+ 'TurboSHAKE128',
+ 'TurboSHAKE256',
+ 'KT128',
+ 'KT256',
+]) {
+ addFixture(name, {
+ name,
+ digest: async () => {
+ await assertNoPromiseConstructorAccess(`digest ${name}`, () =>
+ subtle.digest(digestAlgorithm(name), data));
+ },
+ });
+}
+
+// deriveKey() is the only public API that performs the "get key length"
+// registry operation. Keep this table in sync with algorithms that can be a
+// concrete derived-key target.
+const keyLengthTargets = {
+ 'AES-CBC': {
+ algorithm: algorithm('AES-CBC', { length: 128 }),
+ usages: ['encrypt'],
+ },
+ 'AES-CTR': {
+ algorithm: algorithm('AES-CTR', { length: 128 }),
+ usages: ['encrypt'],
+ },
+ 'AES-GCM': {
+ algorithm: algorithm('AES-GCM', { length: 128 }),
+ usages: ['encrypt'],
+ },
+ 'AES-KW': {
+ algorithm: algorithm('AES-KW', { length: 128 }),
+ usages: ['wrapKey'],
+ },
+ 'AES-OCB': {
+ algorithm: algorithm('AES-OCB', { length: 128 }),
+ usages: ['encrypt'],
+ },
+ 'ChaCha20-Poly1305': {
+ algorithm: algorithm('ChaCha20-Poly1305'),
+ usages: ['encrypt'],
+ },
+ 'HMAC': {
+ algorithm: algorithm('HMAC', { hash: 'SHA-256', length: 256 }),
+ usages: ['sign'],
+ },
+ 'KMAC128': {
+ algorithm: algorithm('KMAC128', { length: 128 }),
+ usages: ['sign'],
+ },
+ 'KMAC256': {
+ algorithm: algorithm('KMAC256', { length: 256 }),
+ usages: ['sign'],
+ },
+};
+
+function getSupportedAlgorithmOperations() {
+ const algorithms = new Map();
+ for (const operation of Object.keys(kSupportedAlgorithms)) {
+ if (operation === 'get key length')
+ continue;
+ for (const name of Object.keys(kSupportedAlgorithms[operation])) {
+ if (!algorithms.has(name))
+ algorithms.set(name, new Set());
+ algorithms.get(name).add(operation);
+ }
+ }
+ return algorithms;
+}
+
+// This is the list of supported public registry operations that this file
+// exercises. A new operation name must be added here before the registry
+// assertion below will pass.
+const operationOrder = [
+ 'digest',
+ 'generateKey',
+ 'exportKey',
+ 'importKey',
+ 'encrypt',
+ 'decrypt',
+ 'sign',
+ 'verify',
+ 'deriveBits',
+ 'encapsulate',
+ 'decapsulate',
+ 'wrapKey',
+ 'unwrapKey',
+];
+
+const coveredOperations = new Set([
+ ...operationOrder,
+ 'get key length',
+]);
+
+for (const operation of Object.keys(kSupportedAlgorithms)) {
+ assert(
+ coveredOperations.has(operation),
+ `missing prototype pollution operation coverage for ${operation}`);
+}
+
+const supportedAlgorithms = getSupportedAlgorithmOperations();
+for (const [name, operations] of supportedAlgorithms) {
+ const fixture = fixtures.get(name);
+ assert(fixture, `missing prototype pollution fixture for ${name}`);
+
+ const ctx = { __proto__: null };
+ for (const operation of operationOrder) {
+ if (!operations.has(operation))
+ continue;
+ assert.strictEqual(
+ typeof fixture[operation],
+ 'function',
+ `missing prototype pollution coverage for ${name} ${operation}`);
+ await fixture[operation](ctx);
+ }
+
+ if (typeof fixture.getPublicKey === 'function' &&
+ ctx.keyPair?.privateKey !== undefined) {
+ await fixture.getPublicKey(ctx);
+ }
+ if (typeof fixture.extra === 'function')
+ await fixture.extra(ctx);
+}
+
+const getKeyLengthAlgorithms =
+ Object.keys(kSupportedAlgorithms['get key length'] ?? {});
+// KDF base algorithms return null from getKeyLength(). They still need to be
+// listed explicitly so a newly registered get-key-length algorithm does not
+// silently skip prototype-pollution coverage.
+const nullKeyLengthAlgorithms = [
+ 'HKDF',
+ 'PBKDF2',
+ 'Argon2d',
+ 'Argon2i',
+ 'Argon2id',
+];
+const pbkdf2Key = await subtle.importKey(
+ 'raw-secret',
+ new Uint8Array(32).fill(1),
+ 'PBKDF2',
+ false,
+ ['deriveKey']);
+for (const name of getKeyLengthAlgorithms) {
+ const target = keyLengthTargets[name];
+ if (target === undefined) {
+ assert(
+ nullKeyLengthAlgorithms.includes(name),
+ `missing get key length coverage for ${name}`);
+ continue;
+ }
+
+ await assertCryptoKeyResult(`get key length ${name}`, () =>
+ subtle.deriveKey(
+ algorithm('PBKDF2', {
+ hash: 'SHA-256',
+ salt: new Uint8Array(8),
+ iterations: 1,
+ }),
+ pbkdf2Key,
+ target.algorithm,
+ true,
+ target.usages));
+}
+
+// Keep one explicit unwrapKey('jwk') negative case: the parsed object must not
+// inherit kty from Object.prototype when the wrapped JSON does not have it.
+{
+ const jwkUnwrappingKey = await subtle.generateKey(
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt', 'unwrapKey']);
+ const iv = new Uint8Array(16);
+ const missingKtyWrappedJwk = await subtle.encrypt(
+ algorithm('AES-CBC', { iv }),
+ jwkUnwrappingKey,
+ new TextEncoder().encode('{"k":"AAAAAAAAAAAAAAAAAAAAAA"}'));
- await assertNoPromiseConstructorAccess('decapsulateKey', () =>
- subtle.decapsulateKey(
- { name: 'ML-KEM-768' },
- kemPair.privateKey,
- ct1,
- 'HKDF',
- false,
- ['deriveBits']));
- await assertNoInheritedArrayBufferThenAccess('decapsulateKey', () =>
- subtle.decapsulateKey(
- { name: 'ML-KEM-768' },
- kemPair.privateKey,
- ct1,
- 'HKDF',
- false,
- ['deriveBits']));
- await assertNoInheritedCryptoKeyThenAccess('decapsulateKey result', () =>
- subtle.decapsulateKey(
- { name: 'ML-KEM-768' },
- kemPair.privateKey,
- ct1,
- 'HKDF',
- false,
- ['deriveBits']));
-
- const { ciphertext: ct2 } =
- await assertNoPromiseConstructorAccess('encapsulateBits', () =>
- subtle.encapsulateBits(
- { name: 'ML-KEM-768' },
- kemPair.publicKey));
-
- await assertNoPromiseConstructorAccess('decapsulateBits', () =>
- subtle.decapsulateBits(
- { name: 'ML-KEM-768' },
- kemPair.privateKey,
- ct2));
+ await assertMissingJwkKtyIgnoresPrototype(() =>
+ subtle.unwrapKey(
+ 'jwk',
+ missingKtyWrappedJwk,
+ jwkUnwrappingKey,
+ algorithm('AES-CBC', { iv }),
+ algorithm('AES-CBC', { length: 128 }),
+ true,
+ ['encrypt']));
}
diff --git a/test/test426/README.md b/test/test426/README.md
index 58b499920f7087..00f249c8d36a29 100644
--- a/test/test426/README.md
+++ b/test/test426/README.md
@@ -7,7 +7,7 @@ suite ensures that the Node.js source map implementation conforms to the
The [`test/fixtures/test426`](../fixtures/test426/) contains a copy of the set of
[Source Map Tests][] suite. The last updated hash is:
-*
+*
See the json files in [the `status` folder](./status) for prerequisites,
expected failures, and support status for specific tests.