Commit ff6027d4872 for nodejs

commit ff6027d487279e16bc0e2fa7956e3e41fcd8013d
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Tue Sep 22 22:19:32 2026 +0200

    crypto: reject duplicate unknown key usages

    Check all JWK key_ops entries for duplicates before ignoring unknown
    usage names.

    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/util.js b/lib/internal/crypto/util.js
index 34e09b8b6ed..537870f25e8 100644
--- a/lib/internal/crypto/util.js
+++ b/lib/internal/crypto/util.js
@@ -1220,15 +1220,16 @@ function validateKeyOps(keyOps, usagesSet) {
   if (keyOps === undefined) return;
   validateArray(keyOps, 'keyData.key_ops');
   let keyOpsMask = 0;
+  const seen = new SafeSet();
   for (let n = 0; n < keyOps.length; n++) {
     const op = keyOps[n];
+    if (seen.has(op))
+      throw lazyDOMException('Duplicate key operation', 'DataError');
+    seen.add(op);
     const opMask = kUsageMasks[op];
     // Skipping unknown key ops
     if (opMask === undefined)
       continue;
-    // Have we seen it already? if so, error
-    if (keyOpsMask & opMask)
-      throw lazyDOMException('Duplicate key operation', 'DataError');
     keyOpsMask |= opMask;

     // TODO(@jasnell): RFC7517 section 4.3 strong recommends validating
diff --git a/test/parallel/test-webcrypto-key-ops-duplicates.js b/test/parallel/test-webcrypto-key-ops-duplicates.js
new file mode 100644
index 00000000000..4a666a1caa4
--- /dev/null
+++ b/test/parallel/test-webcrypto-key-ops-duplicates.js
@@ -0,0 +1,20 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+
+const assert = require('assert');
+const { subtle } = globalThis.crypto;
+
+(async () => {
+  const algorithm = { name: 'HMAC', hash: 'SHA-256' };
+  const jwk = { kty: 'oct', k: 'AAAAAAAAAAAAAAAAAAAAAA' };
+  await subtle.importKey('jwk', { ...jwk, key_ops: ['sign', 'future'] },
+                         algorithm, false, ['sign']);
+  for (const key_ops of [['sign', 'sign'], ['sign', 'future', 'future']]) {
+    await assert.rejects(subtle.importKey('jwk', { ...jwk, key_ops },
+                                          algorithm, false, ['sign']),
+                         { name: 'DataError' });
+  }
+})().then(common.mustCall());