diff --git a/lib/internal/crypto/pbkdf2.js b/lib/internal/crypto/pbkdf2.js index 87f763da9ba8a3..f42ce3bbd133d5 100644 --- a/lib/internal/crypto/pbkdf2.js +++ b/lib/internal/crypto/pbkdf2.js @@ -92,6 +92,8 @@ function check(password, salt, iterations, keylen, digest) { // to the 31-bit range here (which is plenty). validateInt32(iterations, 'iterations', 1); validateInt32(keylen, 'keylen', 0); + // Coerce -0 to +0. + keylen += 0; return { password, salt, iterations, keylen, digest }; } diff --git a/lib/internal/crypto/scrypt.js b/lib/internal/crypto/scrypt.js index ce170dff3b8344..14e5f0ac68ce74 100644 --- a/lib/internal/crypto/scrypt.js +++ b/lib/internal/crypto/scrypt.js @@ -83,6 +83,8 @@ function check(password, salt, keylen, options) { password = getArrayBufferOrView(password, 'password'); salt = getArrayBufferOrView(salt, 'salt'); validateInt32(keylen, 'keylen', 0); + // Coerce -0 to +0. + keylen += 0; let { N, r, p, maxmem } = defaults; if (options && options !== defaults) { diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index efd8d6eaf0d640..78b73ed6c4e0d0 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -110,6 +110,35 @@ for (const iterations of [-1, 0, 2147483648]) { }); }); +// `-0` keylen must not abort the process via the native binding's +// IsInt32() assertion. Behavior of `keylen=0` itself varies by OpenSSL +// build (bundled returns an empty buffer; some shared OpenSSL builds +// throw); the requirement here is only that `-0` produces the same +// outcome as `+0`. +{ + let posError; + let posResult; + try { + posResult = crypto.pbkdf2Sync('password', 'salt', 1, 0, 'sha256'); + } catch (err) { + posError = err; + } + let negError; + let negResult; + try { + negResult = crypto.pbkdf2Sync('password', 'salt', 1, -0, 'sha256'); + } catch (err) { + negError = err; + } + if (posError !== undefined) { + assert.strictEqual(negError?.message, posError.message); + } else { + assert.deepStrictEqual(negResult, posResult); + } + + crypto.pbkdf2('password', 'salt', 1, -0, 'sha256', common.mustCall()); +} + // Should not get FATAL ERROR with empty password and salt // https://github.com/nodejs/node/issues/8571 crypto.pbkdf2('', '', 1, 32, 'sha256', common.mustSucceed()); diff --git a/test/parallel/test-crypto-scrypt.js b/test/parallel/test-crypto-scrypt.js index 5effc083cda11a..421ee4ce8f3490 100644 --- a/test/parallel/test-crypto-scrypt.js +++ b/test/parallel/test-crypto-scrypt.js @@ -274,3 +274,30 @@ for (const { args, expected } of badargs) { ['p', 1], ['parallelization', 1], ].forEach((arg) => testParameter(...arg)); } + +// `-0` keylen must not abort the process via the native binding's +// IsInt32() assertion. Assert that `-0` produces the same outcome as +// `+0` (which differs by OpenSSL build). +{ + let posError; + let posResult; + try { + posResult = crypto.scryptSync('', '', 0); + } catch (err) { + posError = err; + } + let negError; + let negResult; + try { + negResult = crypto.scryptSync('', '', -0); + } catch (err) { + negError = err; + } + if (posError !== undefined) { + assert.strictEqual(negError?.message, posError.message); + } else { + assert.deepStrictEqual(negResult, posResult); + } + + crypto.scrypt('', '', -0, common.mustCall()); +}