Commit 712a19bebfd for nodejs
commit 712a19bebfd5367bcaafbfef4e077fbda37bdd4a
Author: Filip Skokan <panva.ip@gmail.com>
Date: Tue Sep 22 22:16:42 2026 +0200
crypto: resolve supports overloads by type
Convert the third argument before checking the operation, and apply
additional-algorithm checks only for that overload.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js
index 4ab482a4307..847017529dc 100644
--- a/lib/internal/crypto/webcrypto.js
+++ b/lib/internal/crypto/webcrypto.js
@@ -1772,6 +1772,22 @@ class SubtleCrypto {
context: '2nd argument',
});
+ let length = null;
+ let additionalAlgorithm;
+ if (lengthOrAdditionalAlgorithm === null ||
+ typeof lengthOrAdditionalAlgorithm === 'number') {
+ if (lengthOrAdditionalAlgorithm !== null) {
+ length = webidl.converters['unsigned long'](lengthOrAdditionalAlgorithm, {
+ prefix,
+ context: '3rd argument',
+ enforceRange: true,
+ });
+ }
+ } else {
+ additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
+ lengthOrAdditionalAlgorithm, { prefix, context: '3rd argument' });
+ }
+
switch (operation) {
case 'decapsulateBits':
case 'decapsulateKey':
@@ -1795,17 +1811,7 @@ class SubtleCrypto {
return false;
}
- let length;
- let additionalAlgorithm;
- if (operation === 'deriveKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
- lengthOrAdditionalAlgorithm,
- {
- prefix,
- context: '3rd argument',
- },
- );
-
+ if (additionalAlgorithm !== undefined && operation === 'deriveKey') {
if (!check('importKey', additionalAlgorithm)) {
return false;
}
@@ -1817,39 +1823,14 @@ class SubtleCrypto {
}
operation = 'deriveBits';
- } else if (operation === 'wrapKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
- lengthOrAdditionalAlgorithm,
- {
- prefix,
- context: '3rd argument',
- },
- );
-
+ } else if (additionalAlgorithm !== undefined && operation === 'wrapKey') {
if (!check('exportKey', additionalAlgorithm)) {
return false;
}
- } else if (operation === 'unwrapKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
- lengthOrAdditionalAlgorithm,
- {
- prefix,
- context: '3rd argument',
- },
- );
-
+ } else if (additionalAlgorithm !== undefined && operation === 'unwrapKey') {
if (!check('importKey', additionalAlgorithm)) {
return false;
}
- } else if (operation === 'deriveBits') {
- length = lengthOrAdditionalAlgorithm;
- if (length !== null) {
- length = webidl.converters['unsigned long'](length, {
- prefix,
- context: '3rd argument',
- enforceRange: true,
- });
- }
} else if (operation === 'getPublicKey') {
let normalizedAlgorithm;
try {
@@ -1870,15 +1851,8 @@ class SubtleCrypto {
default:
return false;
}
- } else if (operation === 'encapsulateKey' || operation === 'decapsulateKey') {
- additionalAlgorithm = webidl.converters.AlgorithmIdentifier(
- lengthOrAdditionalAlgorithm,
- {
- prefix,
- context: '3rd argument',
- },
- );
-
+ } else if (additionalAlgorithm !== undefined &&
+ (operation === 'encapsulateKey' || operation === 'decapsulateKey')) {
let sharedKeyLength;
let normalizedAdditionalAlgorithm;
try {
diff --git a/test/fixtures/webcrypto/supports-level-2.mjs b/test/fixtures/webcrypto/supports-level-2.mjs
index dacaa907002..2e2014bb5f1 100644
--- a/test/fixtures/webcrypto/supports-level-2.mjs
+++ b/test/fixtures/webcrypto/supports-level-2.mjs
@@ -268,12 +268,12 @@ export const vectors = {
[hasX25519, 'X25519'],
],
'wrapKey': [
- [false, 'AES-KW'],
+ [true, 'AES-KW'],
[true, 'AES-KW', 'AES-CTR'],
[true, 'AES-KW', 'HMAC'],
],
'unwrapKey': [
- [false, 'AES-KW'],
+ [true, 'AES-KW'],
[true, 'AES-KW', 'AES-CTR'],
],
'unsupported operation': [
diff --git a/test/parallel/test-webcrypto-supports-overloads.js b/test/parallel/test-webcrypto-supports-overloads.js
new file mode 100644
index 00000000000..d59bf24fa60
--- /dev/null
+++ b/test/parallel/test-webcrypto-supports-overloads.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const { supports } = SubtleCrypto;
+
+for (const operation of ['wrapKey', 'unwrapKey']) {
+ for (const length of [undefined, null, 0, 128]) {
+ assert.strictEqual(supports(operation, 'AES-KW', length), true);
+ }
+ assert.strictEqual(supports(operation, 'AES-KW'), true);
+}
+
+for (const operation of ['digest', 'unknown', 'wrapKey', 'deriveBits']) {
+ for (const value of [-1, NaN, Infinity, 2 ** 32, Symbol()]) {
+ assert.throws(() => supports(operation, 'SHA-256', value), TypeError);
+ }
+}
+
+const hkdf = {
+ name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(), info: new Uint8Array(),
+};
+assert.strictEqual(supports('deriveBits', hkdf, 128), true);
+for (const value of ['128', 'AES-GCM', {}, true, 128n]) {
+ assert.strictEqual(supports('deriveBits', hkdf, value), false);
+}
+assert.strictEqual(supports('deriveKey', hkdf), false);
+assert.strictEqual(supports('deriveKey', hkdf, { name: 'AES-GCM', length: 128 }), true);
+
+if (supports('encapsulateBits', 'ML-KEM-768')) {
+ assert.strictEqual(supports('encapsulateKey', 'ML-KEM-768'), true);
+ assert.strictEqual(supports('decapsulateKey', 'ML-KEM-768', 128), true);
+}