mirror of
https://github.com/nodejs/node.git
synced 2025-05-05 05:53:11 +00:00

Scrypt is a password-based key derivation function that is designed to be expensive both computationally and memory-wise in order to make brute-force attacks unrewarding. OpenSSL has had support for the scrypt algorithm since v1.1.0. Add a Node.js API modeled after `crypto.pbkdf2()` and `crypto.pbkdf2Sync()`. Changes: * Introduce helpers for copying buffers, collecting openssl errors, etc. * Add new infrastructure for offloading crypto to a worker thread. * Add a `AsyncWrap` JS class to simplify pbkdf2(), randomBytes() and scrypt(). Fixes: https://github.com/nodejs/node/issues/8417 PR-URL: https://github.com/nodejs/node/pull/20816 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
98 lines
3.0 KiB
JavaScript
98 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
const { AsyncWrap, Providers } = process.binding('async_wrap');
|
|
const { Buffer } = require('buffer');
|
|
const { scrypt: _scrypt } = process.binding('crypto');
|
|
const {
|
|
ERR_CRYPTO_SCRYPT_INVALID_PARAMETER,
|
|
ERR_CRYPTO_SCRYPT_NOT_SUPPORTED,
|
|
ERR_INVALID_CALLBACK,
|
|
} = require('internal/errors').codes;
|
|
const {
|
|
checkIsArrayBufferView,
|
|
checkIsUint,
|
|
getDefaultEncoding,
|
|
} = require('internal/crypto/util');
|
|
|
|
const defaults = {
|
|
N: 16384,
|
|
r: 8,
|
|
p: 1,
|
|
maxmem: 32 << 20, // 32 MB, matches SCRYPT_MAX_MEM.
|
|
};
|
|
|
|
function scrypt(password, salt, keylen, options, callback = defaults) {
|
|
if (callback === defaults) {
|
|
callback = options;
|
|
options = defaults;
|
|
}
|
|
|
|
options = check(password, salt, keylen, options);
|
|
const { N, r, p, maxmem } = options;
|
|
({ password, salt, keylen } = options);
|
|
|
|
if (typeof callback !== 'function')
|
|
throw new ERR_INVALID_CALLBACK();
|
|
|
|
const encoding = getDefaultEncoding();
|
|
const keybuf = Buffer.alloc(keylen);
|
|
|
|
const wrap = new AsyncWrap(Providers.SCRYPTREQUEST);
|
|
wrap.ondone = (ex) => { // Retains keybuf while request is in flight.
|
|
if (ex) return callback.call(wrap, ex);
|
|
if (encoding === 'buffer') return callback.call(wrap, null, keybuf);
|
|
callback.call(wrap, null, keybuf.toString(encoding));
|
|
};
|
|
|
|
handleError(keybuf, password, salt, N, r, p, maxmem, wrap);
|
|
}
|
|
|
|
function scryptSync(password, salt, keylen, options = defaults) {
|
|
options = check(password, salt, keylen, options);
|
|
const { N, r, p, maxmem } = options;
|
|
({ password, salt, keylen } = options);
|
|
const keybuf = Buffer.alloc(keylen);
|
|
handleError(keybuf, password, salt, N, r, p, maxmem);
|
|
const encoding = getDefaultEncoding();
|
|
if (encoding === 'buffer') return keybuf;
|
|
return keybuf.toString(encoding);
|
|
}
|
|
|
|
function handleError(keybuf, password, salt, N, r, p, maxmem, wrap) {
|
|
const ex = _scrypt(keybuf, password, salt, N, r, p, maxmem, wrap);
|
|
|
|
if (ex === undefined)
|
|
return;
|
|
|
|
if (ex === null)
|
|
throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER(); // Bad N, r, p, or maxmem.
|
|
|
|
throw ex; // Scrypt operation failed, exception object contains details.
|
|
}
|
|
|
|
function check(password, salt, keylen, options, callback) {
|
|
if (_scrypt === undefined)
|
|
throw new ERR_CRYPTO_SCRYPT_NOT_SUPPORTED();
|
|
|
|
password = checkIsArrayBufferView('password', password);
|
|
salt = checkIsArrayBufferView('salt', salt);
|
|
keylen = checkIsUint('keylen', keylen);
|
|
|
|
let { N, r, p, maxmem } = defaults;
|
|
if (options && options !== defaults) {
|
|
if (options.hasOwnProperty('N')) N = checkIsUint('N', options.N);
|
|
if (options.hasOwnProperty('r')) r = checkIsUint('r', options.r);
|
|
if (options.hasOwnProperty('p')) p = checkIsUint('p', options.p);
|
|
if (options.hasOwnProperty('maxmem'))
|
|
maxmem = checkIsUint('maxmem', options.maxmem);
|
|
if (N === 0) N = defaults.N;
|
|
if (r === 0) r = defaults.r;
|
|
if (p === 0) p = defaults.p;
|
|
if (maxmem === 0) maxmem = defaults.maxmem;
|
|
}
|
|
|
|
return { password, salt, keylen, N, r, p, maxmem };
|
|
}
|
|
|
|
module.exports = { scrypt, scryptSync };
|