2018-05-18 11:05:20 +02:00
|
|
|
'use strict';
|
|
|
|
|
2018-08-21 08:54:02 +02:00
|
|
|
const { AsyncWrap, Providers } = internalBinding('async_wrap');
|
|
|
|
const { Buffer } = require('buffer');
|
2018-08-29 14:45:29 +02:00
|
|
|
const { scrypt: _scrypt } = internalBinding('crypto');
|
2019-03-12 08:14:19 +01:00
|
|
|
const { validateSafeInteger, validateUint32 } = require('internal/validators');
|
2018-05-18 11:05:20 +02:00
|
|
|
const {
|
|
|
|
ERR_CRYPTO_SCRYPT_INVALID_PARAMETER,
|
|
|
|
ERR_CRYPTO_SCRYPT_NOT_SUPPORTED,
|
2019-07-21 20:14:34 +02:00
|
|
|
ERR_INVALID_CALLBACK
|
2018-05-18 11:05:20 +02:00
|
|
|
} = require('internal/errors').codes;
|
|
|
|
const {
|
|
|
|
getDefaultEncoding,
|
2018-05-30 16:14:37 +02:00
|
|
|
validateArrayBufferView,
|
2018-05-18 11:05:20 +02:00
|
|
|
} = 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')
|
2019-04-02 03:46:17 +02:00
|
|
|
throw new ERR_INVALID_CALLBACK(callback);
|
2018-05-18 11:05:20 +02:00
|
|
|
|
|
|
|
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));
|
|
|
|
};
|
|
|
|
|
2019-06-20 08:09:17 +02:00
|
|
|
handleError(_scrypt(keybuf, password, salt, N, r, p, maxmem, wrap));
|
2018-05-18 11:05:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2019-06-20 08:09:17 +02:00
|
|
|
handleError(_scrypt(keybuf, password, salt, N, r, p, maxmem));
|
2018-05-18 11:05:20 +02:00
|
|
|
const encoding = getDefaultEncoding();
|
|
|
|
if (encoding === 'buffer') return keybuf;
|
|
|
|
return keybuf.toString(encoding);
|
|
|
|
}
|
|
|
|
|
2019-06-20 08:09:17 +02:00
|
|
|
function handleError(ex) {
|
2018-05-18 11:05:20 +02:00
|
|
|
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.
|
|
|
|
}
|
|
|
|
|
2018-09-17 13:44:42 +02:00
|
|
|
function check(password, salt, keylen, options) {
|
2018-05-18 11:05:20 +02:00
|
|
|
if (_scrypt === undefined)
|
|
|
|
throw new ERR_CRYPTO_SCRYPT_NOT_SUPPORTED();
|
|
|
|
|
2018-05-30 16:14:37 +02:00
|
|
|
password = validateArrayBufferView(password, 'password');
|
|
|
|
salt = validateArrayBufferView(salt, 'salt');
|
2019-03-20 12:05:33 +01:00
|
|
|
validateUint32(keylen, 'keylen');
|
2018-05-18 11:05:20 +02:00
|
|
|
|
|
|
|
let { N, r, p, maxmem } = defaults;
|
|
|
|
if (options && options !== defaults) {
|
2018-06-25 17:40:59 +02:00
|
|
|
let has_N, has_r, has_p;
|
2019-03-20 12:05:33 +01:00
|
|
|
if (has_N = (options.N !== undefined)) {
|
|
|
|
N = options.N;
|
2019-07-23 15:12:32 +02:00
|
|
|
validateUint32(N, 'N');
|
2019-03-20 12:05:33 +01:00
|
|
|
}
|
2018-06-25 17:40:59 +02:00
|
|
|
if (options.cost !== undefined) {
|
|
|
|
if (has_N) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
|
2019-03-20 12:05:33 +01:00
|
|
|
N = options.cost;
|
2019-07-23 15:12:32 +02:00
|
|
|
validateUint32(N, 'cost');
|
2019-03-20 12:05:33 +01:00
|
|
|
}
|
|
|
|
if (has_r = (options.r !== undefined)) {
|
|
|
|
r = options.r;
|
2019-07-23 15:12:32 +02:00
|
|
|
validateUint32(r, 'r');
|
2018-06-25 17:40:59 +02:00
|
|
|
}
|
|
|
|
if (options.blockSize !== undefined) {
|
|
|
|
if (has_r) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
|
2019-03-20 12:05:33 +01:00
|
|
|
r = options.blockSize;
|
2019-07-23 15:12:32 +02:00
|
|
|
validateUint32(r, 'blockSize');
|
2019-03-20 12:05:33 +01:00
|
|
|
}
|
|
|
|
if (has_p = (options.p !== undefined)) {
|
|
|
|
p = options.p;
|
2019-07-23 15:12:32 +02:00
|
|
|
validateUint32(p, 'p');
|
2018-06-25 17:40:59 +02:00
|
|
|
}
|
|
|
|
if (options.parallelization !== undefined) {
|
|
|
|
if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
|
2019-03-20 12:05:33 +01:00
|
|
|
p = options.parallelization;
|
2019-07-23 15:12:32 +02:00
|
|
|
validateUint32(p, 'parallelization');
|
2019-03-20 12:05:33 +01:00
|
|
|
}
|
|
|
|
if (options.maxmem !== undefined) {
|
|
|
|
maxmem = options.maxmem;
|
2019-03-12 08:14:19 +01:00
|
|
|
validateSafeInteger(maxmem, 'maxmem', 0);
|
2018-06-25 17:40:59 +02:00
|
|
|
}
|
2018-05-18 11:05:20 +02:00
|
|
|
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 };
|