Commit d881931af47 for nodejs

commit d881931af47dedb48f283f6ceb8551e9e19df19c
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Tue Sep 22 22:44:50 2026 +0200

    crypto: map JWK export failures to OperationError

    Map inaccessible key material to the Web Crypto error type, including
    seedless ML-DSA and ML-KEM keys converted from KeyObjects.

    Assisted-by: Codex
    Signed-off-by: Filip Skokan <panva.ip@gmail.com>
    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 2f44d726026..6585baa6fc5 100644
--- a/lib/internal/crypto/webcrypto.js
+++ b/lib/internal/crypto/webcrypto.js
@@ -806,7 +806,13 @@ function exportKeyJWK(key) {
   };
   if (alg === undefined) delete parameters.alg;

-  return getCryptoKeyHandle(key).exportJwk(parameters, true);
+  try {
+    return getCryptoKeyHandle(key).exportJwk(parameters, true);
+  } catch (err) {
+    throw lazyDOMException(
+      'The operation failed for an operation-specific reason',
+      { name: 'OperationError', cause: err });
+  }
 }

 function exportKeySync(format, key) {
diff --git a/test/parallel/test-webcrypto-seedless-jwk-export.js b/test/parallel/test-webcrypto-seedless-jwk-export.js
new file mode 100644
index 00000000000..1dcad8c64da
--- /dev/null
+++ b/test/parallel/test-webcrypto-seedless-jwk-export.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+const { hasOpenSSL, isBoringSSL } = require('../common/crypto');
+if (isBoringSSL || !hasOpenSSL(3, 5))
+  common.skip('requires OpenSSL 3.5 or later');
+
+const assert = require('assert');
+const { createPrivateKey } = require('crypto');
+const fixtures = require('../common/fixtures');
+const { subtle } = globalThis.crypto;
+
+(async () => {
+  const algorithm = { name: 'AES-GCM', iv: new Uint8Array(12) };
+  const wrappingKey = await subtle.generateKey({ name: 'AES-GCM', length: 128 },
+                                               false, ['wrapKey']);
+  for (const [name, usage] of [
+    ['ML-KEM-512', 'decapsulateBits'],
+    ['ML-KEM-768', 'decapsulateBits'],
+    ['ML-KEM-1024', 'decapsulateBits'],
+    ['ML-DSA-44', 'sign'],
+    ['ML-DSA-65', 'sign'],
+    ['ML-DSA-87', 'sign'],
+  ]) {
+    const file = `${name.toLowerCase().replaceAll('-', '_')}_private_priv_only.pem`;
+    const key = createPrivateKey(fixtures.readKey(file)).toCryptoKey(name, true, [usage]);
+    await assert.rejects(subtle.exportKey('jwk', key), {
+      name: 'OperationError', constructor: DOMException,
+    });
+    await assert.rejects(subtle.wrapKey('jwk', key, wrappingKey, algorithm), {
+      name: 'OperationError', constructor: DOMException,
+    });
+  }
+})().then(common.mustCall());