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);
+}