Skip to content

Commit b5cf012

Browse files
panvasxa
authored andcommitted
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> PR-URL: #63111 Backport-PR-URL: #63563 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent f6b2466 commit b5cf012

24 files changed

Lines changed: 861 additions & 129 deletions

lib/eslint.config_partial.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,13 @@ export default [
6161
rules: {
6262
'prefer-object-spread': 'error',
6363
'no-buffer-constructor': 'error',
64-
'no-restricted-syntax': noRestrictedSyntax,
64+
'no-restricted-syntax': [
65+
...noRestrictedSyntax,
66+
{
67+
selector: "VariableDeclarator[init.type='CallExpression'][init.callee.name='internalBinding'][init.arguments.0.value='crypto'] > ObjectPattern > Property[key.name='getKeyObjectSlots']",
68+
message: "Use `const { getKeyObjectSlots } = require('internal/crypto/keys');` instead of destructuring it from `internalBinding('crypto')`.",
69+
},
70+
],
6571
'no-restricted-globals': [
6672
'error',
6773
{

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
@@ -30,7 +30,6 @@ const {
3030
getUsagesUnion,
3131
hasAnyNotIn,
3232
jobPromise,
33-
kHandle,
3433
} = require('internal/crypto/util');
3534

3635
const {
@@ -40,6 +39,8 @@ const {
4039
const {
4140
getCryptoKeyHandle,
4241
getCryptoKeyType,
42+
getKeyObjectHandle,
43+
getKeyObjectType,
4344
InternalCryptoKey,
4445
} = require('internal/crypto/keys');
4546

@@ -190,8 +191,9 @@ function cfrgImportKey(
190191
const usagesSet = new SafeSet(keyUsages);
191192
switch (format) {
192193
case 'KeyObject': {
193-
verifyAcceptableCfrgKeyUse(name, keyData.type === 'public', usagesSet);
194-
handle = keyData[kHandle];
194+
verifyAcceptableCfrgKeyUse(
195+
name, getKeyObjectType(keyData) === 'public', usagesSet);
196+
handle = getKeyObjectHandle(keyData);
195197
break;
196198
}
197199
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)