node/lib/internal/crypto/pbkdf2.js
Tobias Nießen 10f5fa7513
crypto: forbid setting the PBKDF2 iter count to 0
RFC 2898 does not permit an iteration count of zero, and OpenSSL 1.1.1
will treat it as one iteration internally.

Future OpenSSL versions will reject such inputs (already on master
branch), but until that happens, Node.js should manually reject them.

Refs: https://github.com/nodejs/webcrypto/pull/29

PR-URL: https://github.com/nodejs/node/pull/30578
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: David Carlier <devnexen@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
2019-11-28 00:36:36 +01:00

87 lines
2.7 KiB
JavaScript

'use strict';
const { AsyncWrap, Providers } = internalBinding('async_wrap');
const { Buffer } = require('buffer');
const { pbkdf2: _pbkdf2 } = internalBinding('crypto');
const { validateUint32 } = require('internal/validators');
const { deprecate } = require('internal/util');
const {
ERR_CRYPTO_INVALID_DIGEST,
ERR_CRYPTO_PBKDF2_ERROR,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK,
} = require('internal/errors').codes;
const {
getDefaultEncoding,
getArrayBufferView,
} = require('internal/crypto/util');
function pbkdf2(password, salt, iterations, keylen, digest, callback) {
if (typeof digest === 'function') {
callback = digest;
digest = undefined;
}
({ password, salt, iterations, keylen, digest } =
check(password, salt, iterations, keylen, digest));
if (typeof callback !== 'function')
throw new ERR_INVALID_CALLBACK(callback);
const encoding = getDefaultEncoding();
const keybuf = Buffer.alloc(keylen);
const wrap = new AsyncWrap(Providers.PBKDF2REQUEST);
wrap.ondone = (ok) => { // Retains keybuf while request is in flight.
if (!ok) return callback.call(wrap, new ERR_CRYPTO_PBKDF2_ERROR());
if (encoding === 'buffer') return callback.call(wrap, null, keybuf);
callback.call(wrap, null, keybuf.toString(encoding));
};
handleError(_pbkdf2(keybuf, password, salt, iterations, digest, wrap),
digest);
}
function pbkdf2Sync(password, salt, iterations, keylen, digest) {
({ password, salt, iterations, keylen, digest } =
check(password, salt, iterations, keylen, digest));
const keybuf = Buffer.alloc(keylen);
handleError(_pbkdf2(keybuf, password, salt, iterations, digest), digest);
const encoding = getDefaultEncoding();
if (encoding === 'buffer') return keybuf;
return keybuf.toString(encoding);
}
const defaultDigest = deprecate(() => 'sha1',
'Calling pbkdf2 or pbkdf2Sync with "digest" ' +
'set to null is deprecated.',
'DEP0009');
function check(password, salt, iterations, keylen, digest) {
if (typeof digest !== 'string') {
if (digest !== null)
throw new ERR_INVALID_ARG_TYPE('digest', ['string', 'null'], digest);
digest = defaultDigest();
}
password = getArrayBufferView(password, 'password');
salt = getArrayBufferView(salt, 'salt');
validateUint32(iterations, 'iterations', true);
validateUint32(keylen, 'keylen');
return { password, salt, iterations, keylen, digest };
}
function handleError(rc, digest) {
if (rc === -1)
throw new ERR_CRYPTO_INVALID_DIGEST(digest);
if (rc === false)
throw new ERR_CRYPTO_PBKDF2_ERROR();
}
module.exports = {
pbkdf2,
pbkdf2Sync
};