lib/pbkdf2.js (48 lines of code) (raw):

// Copyright (c) 2017 Chandan B N. All rights reserved. const crypto = require('crypto'); const saltBytes = 16; const hashBytes = 32; const iterations = 100599; const digest = 'sha512'; const version = 1; const encoding = 'base64'; module.exports = { hash: function (password, callback) { crypto.randomBytes(saltBytes, function (err, salt) { if (err) { return callback(err); } crypto.pbkdf2(password, salt, iterations, hashBytes, digest, function (err, hash) { if (err) { return callback(err); } var result = new Buffer(12 + hash.length + salt.length); // save version (4bytes) + salt length (4bytes) + iteration count (4bytes) + salt + hash. result.writeUInt32BE(version, 0, true); result.writeUInt32BE(salt.length, 4, true); result.writeUInt32BE(iterations, 8, true); salt.copy(result, 12); hash.copy(result, salt.length + 12); callback(null, result.toString(encoding)); }); }); }, compare: function (password, shadow, callback) { if (password && shadow) { hash = Buffer.from(shadow, encoding); var version = hash.readUInt32BE(0); var saltBytes = hash.readUInt32BE(4); var hashBytes = hash.length - saltBytes - 12; var iterations = hash.readUInt32BE(8); var salt = hash.slice(12, saltBytes + 12); var hash = hash.toString('binary', saltBytes + 12); // verify the salt and hash against the password crypto.pbkdf2(password, salt, iterations, hashBytes, digest, function (err, verify) { if (err) { return callback(err, false); } callback(null, verify.toString('binary') === hash); }); } else { // empty passwords or empty shadow == no login! callback(null, false); } } }