Skip to content

Instantly share code, notes, and snippets.

@redgeoff
Last active February 16, 2022 16:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save redgeoff/12cdf5b0043c3617ca54cbde3fbb8f7b to your computer and use it in GitHub Desktop.
Save redgeoff/12cdf5b0043c3617ca54cbde3fbb8f7b to your computer and use it in GitHub Desktop.
Encode/decode a password in JS in a way that is compatible with Python's werkzeug password hashing
// Encode/decode a password in JS in a way that is compatible with Python's werkzeug password hashing
const crypto = require('crypto');
const util = require('util')
// Hashed value of clearTextPassword='secret'
const EXAMPLE_PYTHON_PASSWORD = 'pbkdf2:sha256:50000$GPJFgPGqrbYqxiM1DITq919diw3sRb5k$970485eef0aa61bf39dba3468b913ada19937eec00fec74b3077a7aec905f9c9'
const pbkdf2 = util.promisify(crypto.pbkdf2)
// Roughly compatible with Python's generate_password_hash (https://werkzeug.palletsprojects.com/en/1.0.x/utils/#werkzeug.security.generate_password_hash)
const generatePasswordHash = async (password) => {
const saltLength = 16; // Translates to a length of 32 when hex encoded
const salt = crypto.randomBytes(saltLength).toString('hex');
const iterations = 50000;
const keylen = 32; // Translates to a length of 64 when hex encoded
const digest = 'sha256'
const hash = await pbkdf2(password, salt, iterations, keylen, digest);
const encodedHash = hash.toString('hex');
return `pbkdf2:${digest}:${iterations}$${salt}$${encodedHash}`
}
// Roughly compatible with Python's check_password_hash (https://werkzeug.palletsprojects.com/en/1.0.x/utils/#werkzeug.security.check_password_hash)
const checkPasswordHash = async (hashedPassword, clearTextPassword) => {
const pieces = hashedPassword.split(/:|\$/);
const digest = pieces[1];
const iterations = parseInt(pieces[2]);
const salt = pieces[3];
const hash = pieces[4];
const decodedHash = Buffer.from(hash, 'hex');
const keylen = decodedHash.length;
const hashedClearTextPassword = await pbkdf2(clearTextPassword, salt, iterations, keylen, digest);
const encodedClearTextPassword = hashedClearTextPassword.toString('hex');
return encodedClearTextPassword === hash;
}
const main = async () => {
const hash = await generatePasswordHash('mypassword')
console.log({ hash });
const matches1 = await checkPasswordHash(hash, 'mypassword')
console.log({ matches1 })
const matches2 = await checkPasswordHash(EXAMPLE_PYTHON_PASSWORD, 'secret')
console.log({ matches2 })
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment