Skip to content

Commit 7defefa

Browse files
panvaaduh95
authored andcommitted
crypto: read WebCrypto inputs through primordials
BufferSource conversion hands over the caller's own object uncopied, so byteLength, byteOffset, buffer and length reads on it run user-replaceable prototype accessors. Internal lookup tables are indexed with computed keys, so a polluted %Object.prototype% key answers a miss. The %Set% constructor iterates its argument through the user-mutable %Array.prototype% iterator. The algorithm registry and the hash name tables are detached from %Object.prototype% after construction rather than declared `__proto__: null`, which V8 places in dictionary mode. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65115 Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent f9c21ea commit 7defefa

15 files changed

Lines changed: 618 additions & 69 deletions

File tree

lib/internal/crypto/aes.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const {
2424
const {
2525
getUsagesMask,
2626
jobPromise,
27+
getBufferSourceByteLength,
2728
} = require('internal/crypto/util');
2829

2930
const {
@@ -218,7 +219,7 @@ function aesImportKey(
218219
if (format === 'raw' && name === 'AES-OCB') {
219220
return undefined;
220221
}
221-
length = keyData.byteLength * 8;
222+
length = getBufferSourceByteLength(keyData) * 8;
222223
validateKeyLength(length);
223224
handle = importSecretKey(keyData);
224225
break;

lib/internal/crypto/cfrg.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
const {
4-
SafeSet,
54
StringPrototypeToLowerCase,
65
TypedArrayPrototypeGetBuffer,
76
} = primordials;
@@ -29,6 +28,7 @@ const {
2928
const {
3029
getUsagesMask,
3130
jobPromise,
31+
toUsagesSet,
3232
} = require('internal/crypto/util');
3333

3434
const {
@@ -126,7 +126,7 @@ function cfrgImportKey(
126126
const { name } = algorithm;
127127
let handle;
128128
const allowedUsages = kUsages[name];
129-
const usagesSet = new SafeSet(usages);
129+
const usagesSet = toUsagesSet(usages);
130130
switch (format) {
131131
case 'KeyObjectHandle':
132132
verifyAcceptableKeyUse(

lib/internal/crypto/diffiehellman.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const {
4+
ArrayBufferPrototypeGetByteLength,
45
ArrayBufferPrototypeSlice,
56
FunctionPrototypeCall,
67
ObjectDefineProperty,
@@ -410,7 +411,7 @@ function ecdhDeriveBits(algorithm, baseKey, length) {
410411
return jobPromiseThen(bits, (bits) => {
411412
const sliceLength = numBitsToBytes(length);
412413

413-
const { byteLength } = bits;
414+
const byteLength = ArrayBufferPrototypeGetByteLength(bits);
414415
// If the length is larger than the derived secret, throw.
415416
if (byteLength < sliceLength)
416417
throw lazyDOMException('derived bit length is too small', 'OperationError');

lib/internal/crypto/ec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
const {
4-
SafeSet,
54
TypedArrayPrototypeGetBuffer,
65
TypedArrayPrototypeGetByteLength,
76
} = primordials;
@@ -33,6 +32,7 @@ const {
3332
jobPromise,
3433
normalizeHashName,
3534
kNamedCurveAliases,
35+
toUsagesSet,
3636
} = require('internal/crypto/util');
3737

3838
const {
@@ -142,7 +142,7 @@ function ecImportKey(
142142

143143
let handle;
144144
const allowedUsages = kUsages[name];
145-
const usagesSet = new SafeSet(usages);
145+
const usagesSet = toUsagesSet(usages);
146146
switch (format) {
147147
case 'KeyObjectHandle':
148148
verifyAcceptableKeyUse(
@@ -215,7 +215,8 @@ function ecImportKey(
215215
throw lazyDOMException('Invalid keyData', 'DataError');
216216
}
217217

218-
if (kNamedCurveAliases[namedCurve] !== handle.keyDetail({}).namedCurve)
218+
if (kNamedCurveAliases[namedCurve] !==
219+
handle.keyDetail({ __proto__: null }).namedCurve)
219220
throw lazyDOMException('Named curve mismatch', 'DataError');
220221

221222
return new InternalCryptoKey(

lib/internal/crypto/hash.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const {
3131
kHandle,
3232
getCachedHashId,
3333
getHashCache,
34+
getOptionalByteLength,
3435
} = require('internal/crypto/util');
3536

3637
const {
@@ -236,8 +237,8 @@ function asyncDigest(algorithm, data) {
236237
// Fall through
237238
case 'cSHAKE256': {
238239
const outputLength = algorithm.outputLength;
239-
if (algorithm.functionName?.byteLength ||
240-
algorithm.customization?.byteLength) {
240+
if (getOptionalByteLength(algorithm.functionName) ||
241+
getOptionalByteLength(algorithm.customization)) {
241242
if (CShakeJob === undefined) {
242243
throw lazyDOMException(
243244
'Non-empty CShakeParams functionName or customization is not supported',

lib/internal/crypto/hashnames.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const {
44
ObjectKeys,
5+
ObjectSetPrototypeOf,
56
} = primordials;
67

78
const kHashContextNode = 1;
@@ -71,15 +72,22 @@ const kHashNames = {
7172
},
7273
};
7374

75+
// Both tables are indexed with computed keys, so a polluted %Object.prototype%
76+
// key must not answer a miss. Detached here rather than declared
77+
// `__proto__: null`: V8 puts that literal form in dictionary mode.
78+
ObjectSetPrototypeOf(kHashNames, null);
79+
7480
{
7581
// Index the aliases
7682
const keys = ObjectKeys(kHashNames);
7783
for (let n = 0; n < keys.length; n++) {
78-
const contexts = ObjectKeys(kHashNames[keys[n]]);
84+
const entry = kHashNames[keys[n]];
85+
ObjectSetPrototypeOf(entry, null);
86+
const contexts = ObjectKeys(entry);
7987
for (let i = 0; i < contexts.length; i++) {
80-
const alias = kHashNames[keys[n]][contexts[i]];
88+
const alias = entry[contexts[i]];
8189
if (kHashNames[alias] === undefined)
82-
kHashNames[alias] = kHashNames[keys[n]];
90+
kHashNames[alias] = entry;
8391
}
8492
}
8593
}

lib/internal/crypto/keys.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const {
55
ObjectDefineProperties,
66
ObjectPrototypeHasOwnProperty,
77
ObjectSetPrototypeOf,
8-
SafeSet,
98
StringPrototypeIncludes,
109
StringPrototypeStartsWith,
1110
SymbolToStringTag,
@@ -68,6 +67,7 @@ const {
6867
getUsagesMask,
6968
getUsagesFromMask,
7069
hasUsage,
70+
toUsagesSet,
7171
} = require('internal/crypto/util');
7272

7373
const {
@@ -1332,7 +1332,7 @@ function importGenericSecretKey(
13321332
extractable,
13331333
keyUsages,
13341334
) {
1335-
const usagesSet = new SafeSet(keyUsages);
1335+
const usagesSet = toUsagesSet(keyUsages);
13361336
const { name } = algorithm;
13371337
if (extractable)
13381338
throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError');

lib/internal/crypto/ml_dsa.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
const {
4-
SafeSet,
54
StringPrototypeToLowerCase,
65
TypedArrayPrototypeGetBuffer,
76
TypedArrayPrototypeSet,
@@ -31,6 +30,8 @@ const {
3130
const {
3231
getUsagesMask,
3332
jobPromise,
33+
toUsagesSet,
34+
getBufferSourceByteLength,
3435
} = require('internal/crypto/util');
3536

3637
const {
@@ -130,7 +131,7 @@ function mlDsaImportKey(
130131

131132
const { name } = algorithm;
132133
let handle;
133-
const usagesSet = new SafeSet(usages);
134+
const usagesSet = toUsagesSet(usages);
134135
switch (format) {
135136
case 'KeyObjectHandle':
136137
verifyAcceptableKeyUse(
@@ -155,7 +156,7 @@ function mlDsaImportKey(
155156
'ML-DSA-65': 4060,
156157
'ML-DSA-87': 4924,
157158
};
158-
if (keyData.byteLength === privOnlyLengths[name]) {
159+
if (getBufferSourceByteLength(keyData) === privOnlyLengths[name]) {
159160
throw lazyDOMException(
160161
'Importing an ML-DSA PKCS#8 key without a seed is not supported',
161162
'NotSupportedError');

lib/internal/crypto/ml_kem.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
const {
4-
SafeSet,
54
StringPrototypeToLowerCase,
65
TypedArrayPrototypeGetBuffer,
76
TypedArrayPrototypeSet,
@@ -28,6 +27,8 @@ const {
2827
const {
2928
getUsagesMask,
3029
jobPromise,
30+
toUsagesSet,
31+
getBufferSourceByteLength,
3132
} = require('internal/crypto/util');
3233

3334
const {
@@ -129,7 +130,7 @@ function mlKemImportKey(
129130

130131
const { name } = algorithm;
131132
let handle;
132-
const usagesSet = new SafeSet(usages);
133+
const usagesSet = toUsagesSet(usages);
133134
switch (format) {
134135
case 'KeyObjectHandle':
135136
verifyAcceptableKeyUse(
@@ -154,7 +155,7 @@ function mlKemImportKey(
154155
'ML-KEM-768': 2428,
155156
'ML-KEM-1024': 3196,
156157
};
157-
if (keyData.byteLength === privOnlyLengths[name]) {
158+
if (getBufferSourceByteLength(keyData) === privOnlyLengths[name]) {
158159
throw lazyDOMException(
159160
'Importing an ML-KEM PKCS#8 key without a seed is not supported',
160161
'NotSupportedError');

lib/internal/crypto/rsa.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const {
44
MathCeil,
5-
SafeSet,
65
TypedArrayPrototypeGetBuffer,
76
Uint8Array,
87
} = primordials;
@@ -35,6 +34,7 @@ const {
3534
jobPromise,
3635
normalizeHashName,
3736
validateMaxBufferLength,
37+
toUsagesSet,
3838
} = require('internal/crypto/util');
3939

4040
const {
@@ -174,7 +174,7 @@ function rsaImportKey(
174174
extractable,
175175
usages) {
176176
const allowedUsages = kUsages[algorithm.name];
177-
const usagesSet = new SafeSet(usages);
177+
const usagesSet = toUsagesSet(usages);
178178
let handle;
179179
switch (format) {
180180
case 'KeyObjectHandle':
@@ -234,7 +234,7 @@ function rsaImportKey(
234234
const {
235235
modulusLength,
236236
publicExponent,
237-
} = handle.keyDetail({});
237+
} = handle.keyDetail({ __proto__: null });
238238

239239
return new InternalCryptoKey(handle, {
240240
name: algorithm.name,

0 commit comments

Comments
 (0)