Last active
February 16, 2022 16:58
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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