Skip to content

Commit 28920ad

Browse files
committed
crypto: harden KeyObject internal slots
Move KeyObject type and handle storage behind NativeKeyObject and expose it to JS through a module-private slot reader, mirroring the CryptoKey hardening. Cache the native slot tuple in a private field and lazily derive secret and asymmetric metadata from the cached KeyObjectHandle. Update internal crypto, QUIC, and comparison callers to use private helpers instead of public KeyObject accessors. Keep getKeyObjectSlots restricted to internal/crypto/keys with an ESLint guard. Add regression coverage for brand checks, hidden slots, clone and transfer behavior, own-property reflection, and post-clone crypto operations. Extend the CryptoKey brand test to assert getSlots is not reachable through the public constructor or prototype chain. Signed-off-by: Filip Skokan <panva.ip@gmail.com>
1 parent 1b04f16 commit 28920ad

24 files changed

Lines changed: 858 additions & 128 deletions

lib/eslint.config_partial.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export default [
7575
selector: "VariableDeclarator[init.type='CallExpression'][init.callee.name='internalBinding'][init.arguments.0.value='crypto'] > ObjectPattern > Property[key.name='getCryptoKeySlots']",
7676
message: "Use `const { getCryptoKeySlots } = require('internal/crypto/keys');` instead of destructuring it from `internalBinding('crypto')`.",
7777
},
78+
{
79+
selector: "VariableDeclarator[init.type='CallExpression'][init.callee.name='internalBinding'][init.arguments.0.value='crypto'] > ObjectPattern > Property[key.name='getKeyObjectSlots']",
80+
message: "Use `const { getKeyObjectSlots } = require('internal/crypto/keys');` instead of destructuring it from `internalBinding('crypto')`.",
81+
},
7882
],
7983
'no-restricted-globals': [
8084
'error',

lib/internal/crypto/aes.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const {
3030
getUsagesMask,
3131
hasAnyNotIn,
3232
jobPromise,
33-
kHandle,
3433
} = require('internal/crypto/util');
3534

3635
const {
@@ -41,6 +40,8 @@ const {
4140
InternalCryptoKey,
4241
getCryptoKeyAlgorithm,
4342
getCryptoKeyHandle,
43+
getKeyObjectHandle,
44+
getKeyObjectSymmetricKeySize,
4445
} = require('internal/crypto/keys');
4546

4647
const {
@@ -219,9 +220,9 @@ function aesImportKey(
219220
let length;
220221
switch (format) {
221222
case 'KeyObject': {
222-
length = keyData.symmetricKeySize * 8;
223+
length = getKeyObjectSymmetricKeySize(keyData) * 8;
223224
validateKeyLength(length);
224-
handle = keyData[kHandle];
225+
handle = getKeyObjectHandle(keyData);
225226
break;
226227
}
227228
case 'raw-secret':

lib/internal/crypto/cfrg.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ const {
2828
getUsagesUnion,
2929
hasAnyNotIn,
3030
jobPromise,
31-
kHandle,
3231
} = require('internal/crypto/util');
3332

3433
const {
@@ -38,6 +37,8 @@ const {
3837
const {
3938
getCryptoKeyHandle,
4039
getCryptoKeyType,
40+
getKeyObjectHandle,
41+
getKeyObjectType,
4142
InternalCryptoKey,
4243
} = require('internal/crypto/keys');
4344

@@ -188,8 +189,9 @@ function cfrgImportKey(
188189
const usagesSet = new SafeSet(keyUsages);
189190
switch (format) {
190191
case 'KeyObject': {
191-
verifyAcceptableCfrgKeyUse(name, keyData.type === 'public', usagesSet);
192-
handle = keyData[kHandle];
192+
verifyAcceptableCfrgKeyUse(
193+
name, getKeyObjectType(keyData) === 'public', usagesSet);
194+
handle = getKeyObjectHandle(keyData);
193195
break;
194196
}
195197
case 'spki': {

lib/internal/crypto/chacha20_poly1305.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const {
1414
getUsagesMask,
1515
hasAnyNotIn,
1616
jobPromise,
17-
kHandle,
1817
} = require('internal/crypto/util');
1918

2019
const {
@@ -24,6 +23,7 @@ const {
2423
const {
2524
InternalCryptoKey,
2625
getCryptoKeyHandle,
26+
getKeyObjectHandle,
2727
} = require('internal/crypto/keys');
2828

2929
const {
@@ -87,7 +87,7 @@ function c20pImportKey(
8787
let handle;
8888
switch (format) {
8989
case 'KeyObject': {
90-
handle = keyData[kHandle];
90+
handle = getKeyObjectHandle(keyData);
9191
break;
9292
}
9393
case 'raw-secret': {

lib/internal/crypto/ec.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const {
3434
hasAnyNotIn,
3535
jobPromise,
3636
normalizeHashName,
37-
kHandle,
3837
kNamedCurveAliases,
3938
} = require('internal/crypto/util');
4039

@@ -47,6 +46,8 @@ const {
4746
getCryptoKeyAlgorithm,
4847
getCryptoKeyHandle,
4948
getCryptoKeyType,
49+
getKeyObjectHandle,
50+
getKeyObjectType,
5051
} = require('internal/crypto/keys');
5152

5253
const {
@@ -189,8 +190,9 @@ function ecImportKey(
189190
const usagesSet = new SafeSet(keyUsages);
190191
switch (format) {
191192
case 'KeyObject': {
192-
verifyAcceptableEcKeyUse(name, keyData.type === 'public', usagesSet);
193-
handle = keyData[kHandle];
193+
verifyAcceptableEcKeyUse(
194+
name, getKeyObjectType(keyData) === 'public', usagesSet);
195+
handle = getKeyObjectHandle(keyData);
194196
break;
195197
}
196198
case 'spki': {

lib/internal/crypto/hkdf.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const {
2929
const {
3030
createSecretKey,
3131
getCryptoKeyHandle,
32+
getKeyObjectHandle,
3233
isKeyObject,
3334
} = require('internal/crypto/keys');
3435

@@ -75,10 +76,10 @@ const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
7576

7677
function prepareKey(key) {
7778
if (isKeyObject(key))
78-
return key;
79+
return getKeyObjectHandle(key);
7980

8081
if (isAnyArrayBuffer(key))
81-
return createSecretKey(key);
82+
return getKeyObjectHandle(createSecretKey(key));
8283

8384
key = toBuf(key);
8485

@@ -96,7 +97,7 @@ function prepareKey(key) {
9697
key);
9798
}
9899

99-
return createSecretKey(key);
100+
return getKeyObjectHandle(createSecretKey(key));
100101
}
101102

102103
function hkdf(hash, key, salt, info, length, callback) {

0 commit comments

Comments
 (0)