node/lib/internal/crypto/scrypt.js
Antoine du Hamel 1e761654d3
doc: consolidate use of multiple-byte units
Refs: https://en.wikipedia.org/wiki/Byte#Multiple-byte_units

PR-URL: https://github.com/nodejs/node/pull/42587
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Mestery <mestery@protonmail.com>
2022-04-20 00:46:37 +02:00

186 lines
4.6 KiB
JavaScript

'use strict';
const {
FunctionPrototypeCall,
Promise,
} = primordials;
const { Buffer } = require('buffer');
const {
ScryptJob,
kCryptoJobAsync,
kCryptoJobSync,
} = internalBinding('crypto');
const {
validateFunction,
validateInteger,
validateInt32,
validateUint32,
} = require('internal/validators');
const {
codes: {
ERR_CRYPTO_SCRYPT_INVALID_PARAMETER,
ERR_CRYPTO_SCRYPT_NOT_SUPPORTED,
}
} = require('internal/errors');
const {
getArrayBufferOrView,
getDefaultEncoding,
kKeyObject,
} = require('internal/crypto/util');
const {
lazyDOMException,
} = require('internal/util');
const defaults = {
N: 16384,
r: 8,
p: 1,
maxmem: 32 << 20, // 32 MiB, 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);
validateFunction(callback, 'callback');
const job = new ScryptJob(
kCryptoJobAsync, password, salt, N, r, p, maxmem, keylen);
const encoding = getDefaultEncoding();
job.ondone = (error, result) => {
if (error !== undefined)
return FunctionPrototypeCall(callback, job, error);
const buf = Buffer.from(result);
if (encoding === 'buffer')
return FunctionPrototypeCall(callback, job, null, buf);
FunctionPrototypeCall(callback, job, null, buf.toString(encoding));
};
job.run();
}
function scryptSync(password, salt, keylen, options = defaults) {
options = check(password, salt, keylen, options);
const { N, r, p, maxmem } = options;
({ password, salt, keylen } = options);
const job = new ScryptJob(
kCryptoJobSync, password, salt, N, r, p, maxmem, keylen);
const { 0: err, 1: result } = job.run();
if (err !== undefined)
throw err;
const buf = Buffer.from(result);
const encoding = getDefaultEncoding();
return encoding === 'buffer' ? buf : buf.toString(encoding);
}
function check(password, salt, keylen, options) {
if (ScryptJob === undefined)
throw new ERR_CRYPTO_SCRYPT_NOT_SUPPORTED();
password = getArrayBufferOrView(password, 'password');
salt = getArrayBufferOrView(salt, 'salt');
validateInt32(keylen, 'keylen', 0);
let { N, r, p, maxmem } = defaults;
if (options && options !== defaults) {
const has_N = options.N !== undefined;
if (has_N) {
N = options.N;
validateUint32(N, 'N');
}
if (options.cost !== undefined) {
if (has_N) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
N = options.cost;
validateUint32(N, 'cost');
}
const has_r = (options.r !== undefined);
if (has_r) {
r = options.r;
validateUint32(r, 'r');
}
if (options.blockSize !== undefined) {
if (has_r) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
r = options.blockSize;
validateUint32(r, 'blockSize');
}
const has_p = options.p !== undefined;
if (has_p) {
p = options.p;
validateUint32(p, 'p');
}
if (options.parallelization !== undefined) {
if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
p = options.parallelization;
validateUint32(p, 'parallelization');
}
if (options.maxmem !== undefined) {
maxmem = options.maxmem;
validateInteger(maxmem, 'maxmem', 0);
}
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 };
}
async function scryptDeriveBits(algorithm, baseKey, length) {
validateUint32(length, 'length');
const {
N = 16384,
r = 8,
p = 1,
maxmem = 32 * 1024 * 1024,
encoding,
} = algorithm;
validateUint32(N, 'algorithm.N');
validateUint32(r, 'algorithm.r');
validateUint32(p, 'algorithm.p');
validateUint32(maxmem, 'algorithm.maxmem');
const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt', encoding);
const raw = baseKey[kKeyObject].export();
let byteLength = 64; // the default
if (length !== undefined) {
if (length === 0)
throw lazyDOMException('length cannot be zero', 'OperationError');
if (length % 8) {
throw lazyDOMException(
'length must be a multiple of 8',
'OperationError');
}
byteLength = length / 8;
}
return new Promise((resolve, reject) => {
scrypt(raw, salt, byteLength, { N, r, p, maxmem }, (err, result) => {
if (err) return reject(err);
resolve(result.buffer);
});
});
}
module.exports = {
scrypt,
scryptSync,
scryptDeriveBits,
};