Commit 090c6d7e961 for nodejs

commit 090c6d7e961a1c96c3119652dc90500883dfb76c
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Tue Sep 22 22:36:59 2026 +0200

    crypto: use the public CryptoKey prototype

    Give generated, imported, converted, and transferred keys the public
    interface prototype directly, without an intermediate internal object.

    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/keys.js b/lib/internal/crypto/keys.js
index cdbf163ec08..4b62919a0bb 100644
--- a/lib/internal/crypto/keys.js
+++ b/lib/internal/crypto/keys.js
@@ -1139,10 +1139,9 @@ function getKeyObjectAsymmetricKeyDetails(key) {
 // KEM-only seed data) lives
 // on a C++ class, NativeCryptoKey, created by createCryptoKeyClass.
 // InternalCryptoKey is the only constructor we expose to internal
-// code; it extends NativeCryptoKey to get that storage and then has
-// its prototype spliced so the chain visible to user code is:
-//   instance -> InternalCryptoKey.prototype
-//            -> CryptoKey.prototype
+// code; it extends NativeCryptoKey to get that storage and gives each
+// instance the public interface prototype:
+//   instance -> CryptoKey.prototype
 //            -> Object.prototype
 //
 // Normal construction caches all native internal slots in a private class
@@ -1285,6 +1284,7 @@ const {
         extractable,
         secondaryHandle,
         seedData);
+      ObjectSetPrototypeOf(this, CryptoKey.prototype);
       if (algorithm !== undefined) {
         this.#slots = [
           handle.getKeyType(),
diff --git a/test/parallel/test-webcrypto-cryptokey-brand-check.js b/test/parallel/test-webcrypto-cryptokey-brand-check.js
index 9174aebe8f0..bb32dc84018 100644
--- a/test/parallel/test-webcrypto-cryptokey-brand-check.js
+++ b/test/parallel/test-webcrypto-cryptokey-brand-check.js
@@ -38,11 +38,11 @@ const { subtle } = globalThis.crypto;
   }
   assert.strictEqual(isCryptoKey(key), true);
   assert.strictEqual(Object.hasOwn(CryptoKey, 'getSlots'), false);
-  const internalProto = Object.getPrototypeOf(key);
-  assert.strictEqual(Object.hasOwn(internalProto, 'getSlots'), false);
-  assert.strictEqual('getSlots' in internalProto, false);
-  assert.strictEqual(internalProto.constructor, CryptoKey);
-  assert.strictEqual(Object.getPrototypeOf(internalProto), CryptoKey.prototype);
+  const keyPrototype = Object.getPrototypeOf(key);
+  assert.strictEqual(Object.hasOwn(keyPrototype, 'getSlots'), false);
+  assert.strictEqual('getSlots' in keyPrototype, false);
+  assert.strictEqual(keyPrototype.constructor, CryptoKey);
+  assert.strictEqual(keyPrototype, CryptoKey.prototype);

   const invalidThis = { code: 'ERR_INVALID_THIS', name: 'TypeError' };
   const invalidArgType = { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' };
@@ -93,7 +93,7 @@ const { subtle } = globalThis.crypto;
     await assertInvalidReceiver(receiver);
   }

-  // Prototype spoofing with InternalCryptoKey.prototype must not pass
+  // Prototype spoofing with CryptoKey.prototype must not pass
   // util.types.isCryptoKey().
   const spoofed = {};
   Object.setPrototypeOf(spoofed, Object.getPrototypeOf(key));
diff --git a/test/parallel/test-webcrypto-key-prototype.js b/test/parallel/test-webcrypto-key-prototype.js
new file mode 100644
index 00000000000..61c23ae9383
--- /dev/null
+++ b/test/parallel/test-webcrypto-key-prototype.js
@@ -0,0 +1,25 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+
+const assert = require('assert');
+const { createSecretKey } = require('crypto');
+const { subtle } = globalThis.crypto;
+
+function check(key) {
+  assert.strictEqual(Object.getPrototypeOf(key), CryptoKey.prototype);
+  assert.strictEqual(Object.getPrototypeOf(structuredClone(key)), CryptoKey.prototype);
+  assert(key instanceof CryptoKey);
+}
+
+(async () => {
+  check(await subtle.generateKey({ name: 'AES-GCM', length: 128 }, true, ['encrypt']));
+  check(await subtle.importKey('raw', new Uint8Array(16), 'AES-GCM', true, ['encrypt']));
+  const pair = await subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' },
+                                        true, ['sign', 'verify']);
+  check(pair.publicKey);
+  check(pair.privateKey);
+  check(createSecretKey(new Uint8Array(16)).toCryptoKey('AES-GCM', true, ['encrypt']));
+})().then(common.mustCall());