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);
}
}
}