'use strict'; const { FunctionPrototypeCall, Promise, } = primordials; const { Buffer } = require('buffer'); const { PBKDF2Job, kCryptoJobAsync, kCryptoJobSync, } = internalBinding('crypto'); const { validateCallback, validateInteger, validateString, validateUint32, } = require('internal/validators'); const { ERR_MISSING_OPTION } = require('internal/errors').codes; const { getArrayBufferOrView, getDefaultEncoding, lazyDOMException, normalizeHashName, kKeyObject, } = 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)); validateCallback(callback); const job = new PBKDF2Job( kCryptoJobAsync, password, salt, iterations, keylen, digest); const encoding = getDefaultEncoding(); job.ondone = (err, result) => { if (err !== undefined) return FunctionPrototypeCall(callback, job, err); const buf = Buffer.from(result); if (encoding === 'buffer') return FunctionPrototypeCall(callback, job, null, buf); FunctionPrototypeCall(callback, job, null, buf.toString(encoding)); }; job.run(); } function pbkdf2Sync(password, salt, iterations, keylen, digest) { ({ password, salt, iterations, keylen, digest } = check(password, salt, iterations, keylen, digest)); const job = new PBKDF2Job( kCryptoJobSync, password, salt, iterations, keylen, digest); 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, iterations, keylen, digest) { validateString(digest, 'digest'); password = getArrayBufferOrView(password, 'password'); salt = getArrayBufferOrView(salt, 'salt'); validateUint32(iterations, 'iterations', true); validateUint32(keylen, 'keylen'); return { password, salt, iterations, keylen, digest }; } async function pbkdf2DeriveBits(algorithm, baseKey, length) { validateUint32(length, 'length'); const { iterations } = algorithm; let { hash } = algorithm; const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt'); if (hash === undefined) throw new ERR_MISSING_OPTION('algorithm.hash'); validateInteger(iterations, 'algorithm.iterations', 1); hash = normalizeHashName(hash.name); 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) => { pbkdf2(raw, salt, iterations, byteLength, hash, (err, result) => { if (err) return reject(err); resolve(result.buffer); }); }); } module.exports = { pbkdf2, pbkdf2Sync, pbkdf2DeriveBits, };