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());