Skip to content

Commit 3a62202

Browse files
tniessentargos
authored andcommitted
crypto: fix handling of malicious getters (scrypt)
It is possible to bypass parameter validation in crypto.scrypt and crypto.scryptSync by crafting option objects with malicious getters as demonstrated in the regression test. After bypassing validation, any value can be passed to the C++ layer, causing an assertion to crash the process. Fixes: #28836 PR-URL: #28838 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent b7c6ad5 commit 3a62202

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

lib/internal/crypto/scrypt.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,31 +80,31 @@ function check(password, salt, keylen, options) {
8080
if (options && options !== defaults) {
8181
let has_N, has_r, has_p;
8282
if (has_N = (options.N !== undefined)) {
83-
validateUint32(options.N, 'N');
8483
N = options.N;
84+
validateUint32(N, 'N');
8585
}
8686
if (options.cost !== undefined) {
8787
if (has_N) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
88-
validateUint32(options.cost, 'cost');
8988
N = options.cost;
89+
validateUint32(N, 'cost');
9090
}
9191
if (has_r = (options.r !== undefined)) {
92-
validateUint32(options.r, 'r');
9392
r = options.r;
93+
validateUint32(r, 'r');
9494
}
9595
if (options.blockSize !== undefined) {
9696
if (has_r) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
97-
validateUint32(options.blockSize, 'blockSize');
9897
r = options.blockSize;
98+
validateUint32(r, 'blockSize');
9999
}
100100
if (has_p = (options.p !== undefined)) {
101-
validateUint32(options.p, 'p');
102101
p = options.p;
102+
validateUint32(p, 'p');
103103
}
104104
if (options.parallelization !== undefined) {
105105
if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
106-
validateUint32(options.parallelization, 'parallelization');
107106
p = options.parallelization;
107+
validateUint32(p, 'parallelization');
108108
}
109109
if (options.maxmem !== undefined) {
110110
maxmem = options.maxmem;

test/parallel/test-crypto-scrypt.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,38 @@ for (const { args, expected } of badargs) {
235235
code: 'ERR_OUT_OF_RANGE'
236236
});
237237
}
238+
239+
{
240+
// Regression test for https://github.com/nodejs/node/issues/28836.
241+
242+
function testParameter(name, value) {
243+
let accessCount = 0;
244+
245+
// Find out how often the value is accessed.
246+
crypto.scryptSync('', '', 1, {
247+
get [name]() {
248+
accessCount++;
249+
return value;
250+
}
251+
});
252+
253+
// Try to crash the process on the last access.
254+
common.expectsError(() => {
255+
crypto.scryptSync('', '', 1, {
256+
get [name]() {
257+
if (--accessCount === 0)
258+
return '';
259+
return value;
260+
}
261+
});
262+
}, {
263+
code: 'ERR_INVALID_ARG_TYPE'
264+
});
265+
}
266+
267+
[
268+
['N', 16384], ['cost', 16384],
269+
['r', 8], ['blockSize', 8],
270+
['p', 1], ['parallelization', 1]
271+
].forEach((arg) => testParameter(...arg));
272+
}

0 commit comments

Comments
 (0)