Skip to content

Commit

Permalink
crypto: fix handling of malicious getters (scrypt)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
tniessen authored and targos committed Aug 2, 2019
1 parent b7c6ad5 commit 3a62202
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 6 deletions.
12 changes: 6 additions & 6 deletions lib/internal/crypto/scrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,31 +80,31 @@ function check(password, salt, keylen, options) {
if (options && options !== defaults) {
let has_N, has_r, has_p;
if (has_N = (options.N !== undefined)) {
validateUint32(options.N, 'N');
N = options.N;
validateUint32(N, 'N');
}
if (options.cost !== undefined) {
if (has_N) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
validateUint32(options.cost, 'cost');
N = options.cost;
validateUint32(N, 'cost');
}
if (has_r = (options.r !== undefined)) {
validateUint32(options.r, 'r');
r = options.r;
validateUint32(r, 'r');
}
if (options.blockSize !== undefined) {
if (has_r) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
validateUint32(options.blockSize, 'blockSize');
r = options.blockSize;
validateUint32(r, 'blockSize');
}
if (has_p = (options.p !== undefined)) {
validateUint32(options.p, 'p');
p = options.p;
validateUint32(p, 'p');
}
if (options.parallelization !== undefined) {
if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
validateUint32(options.parallelization, 'parallelization');
p = options.parallelization;
validateUint32(p, 'parallelization');
}
if (options.maxmem !== undefined) {
maxmem = options.maxmem;
Expand Down
35 changes: 35 additions & 0 deletions test/parallel/test-crypto-scrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,38 @@ for (const { args, expected } of badargs) {
code: 'ERR_OUT_OF_RANGE'
});
}

{
// Regression test for https://github.com/nodejs/node/issues/28836.

function testParameter(name, value) {
let accessCount = 0;

// Find out how often the value is accessed.
crypto.scryptSync('', '', 1, {
get [name]() {
accessCount++;
return value;
}
});

// Try to crash the process on the last access.
common.expectsError(() => {
crypto.scryptSync('', '', 1, {
get [name]() {
if (--accessCount === 0)
return '';
return value;
}
});
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
}

[
['N', 16384], ['cost', 16384],
['r', 8], ['blockSize', 8],
['p', 1], ['parallelization', 1]
].forEach((arg) => testParameter(...arg));
}

0 comments on commit 3a62202

Please sign in to comment.