Skip to content

Instantly share code, notes, and snippets.

@branneman
Last active March 30, 2022 15:56
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save branneman/558827b7a567aa13944d1814600d84b6 to your computer and use it in GitHub Desktop.
Save branneman/558827b7a567aa13944d1814600d84b6 to your computer and use it in GitHub Desktop.
Node.js: Create salted hashed password
const crypto = require('crypto')
const { promisify } = require('util')
const pbkdf2 = promisify(crypto.pbkdf2)
module.exports = { createHashPasswordFn, isPasswordCorrect }
/**
* @typedef {Object} HashPassword
* @property {String} hash
* @property {String} salt
* @property {Number} iterations - HMAC digest: Iterations
* @property {Number} keylength - HMAC digest: Key byte length
* @property {String} digest - HMAC digest: algorithm, see crypto.getHashes()
*/
/**
* Create a hashPassword function
* @param {Number} saltlength - Number of random salt bytes
* @param {Number} iterations
* @param {Number} keylength
* @param {String} digest
* @return {Function}
*/
function createHashPasswordFn(saltlength, iterations, keylength, digest) {
/**
* Hash a password with a random salt
* @param {String} password
* @return {Promise<HashPassword>}
*/
return async function hashPassword(password) {
const salt = crypto.randomBytes(saltlength).toString('base64')
const hashBuffer = await pbkdf2(password, salt, iterations, keylength, digest)
const hash = hashBuffer.toString('hex')
return { hash, salt, iterations, keylength, digest }
}
}
/**
* Compare password attempt to a saved salt-hash-password
* @param {HashPassword} hashPassword - Saved salt-hash-password
* @param {String} passwordAttempt - Plain text password attempt
* @return {Promise<Boolean>}
*/
async function isPasswordCorrect(hashPassword, passwordAttempt) {
const hashBuffer = await pbkdf2(
passwordAttempt,
hashPassword.salt,
hashPassword.iterations,
hashPassword.keylength,
hashPassword.digest
)
const hashAttempt = hashBuffer.toString('hex')
return hashPassword.hash == hashAttempt
}
const { createHashPasswordFn, isPasswordCorrect } = require('./salt-hash-password')
;(async function() {
// Sane defaults for May 2018
const hashPassword = createHashPasswordFn(128, 1e5, 64, 'sha512')
// Result hash is different each time
const pw1 = await hashPassword('s3cret_1')
const pw2 = await hashPassword('s3cret_1')
pw1.hash === pw2.hash
//=> false
// Succesful comparison when given the right password and salt-hash settings
const pw3 = await hashPassword('s3cret_2')
const result = await isPasswordCorrect(pw3, 's3cret_2')
//=> true
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment