1

I have a backend application written in Python used by the content managers of my site. Users' passwords are hashed using passlib's pbkdf2_sha512 function. I began to develop the frontend application for which I decided to use nodejs with React for UX reasons.

Now my problem is I can't figure out how can I verify the passwords hashed by passlib using nodejs for authenticating my users. Passlib's implementation seems too specific to me and I'm not really into crypto stuff to figure it out.

I have the MCF so I know the algorithm and digest type, the salt, the number of iterations and the key length. How can I verify this output from passlib in node? Should I rather choose another algorithm better supported by both platforms?

3 Answers 3

2

We had exactly the same Problem but switching to sha512crypt was not an option. In our case the passwords were generated with flask-security. The examples below cover regular passlib and flask-security hashes, which first generate a HMAC with a secret salt and use it as the pbkdf2-sha512 password. See code below.

Source is on GitHub: https://github.com/badzong/node-verify-flask-security-passwords

var crypto = require('crypto');
var pbkdf2_sha512 = require('pbkdf2-sha512');

function b64trimmed(buf) {
        return buf.toString('base64').replace(/=*$/, '').replace('+', '.');
}

function b64decode(str) {
        // . in Base64?
        str = str.replace('.', '+');
        if (str.length % 4) {
                str += '='.repeat(4 - str.length % 4);
        }
        return new Buffer(str, 'base64');
}

function get_hmac(secret, password) {
        var hmac = crypto.createHmac('sha512', secret).update(password).digest('base64');

        return hmac;
}

function get_hash(password, salt, rounds) {

        // FIXME: KeyLenBytes is hardcoded
        var h = b64trimmed(pbkdf2_sha512(password, salt, rounds, 64));
        var joined_hash = ['', 'pbkdf2-sha512', rounds, b64trimmed(salt), h].join('$');

        return joined_hash;
}

function verify_hash(password, stored_hash) {
        var scheme = stored_hash.split('$')[1];
        var rounds = stored_hash.split('$')[2];
        var salt = stored_hash.split('$')[3];

        // FIXME: Maybe throw an exception
        if (scheme !== 'pbkdf2-sha512') {
                return false;
        }

        var h = get_hash(password, b64decode(salt), rounds);

        return h === stored_hash;
}

function new_hash(password, rounds) {

        // FIXME: Salt size is hardcoded
        var salt = crypto.randomBytes(16);

        return get_hash(password, salt, rounds);
}

var password = 'Example Password';

// Usage:
var h = new_hash(password, 20000);
console.log('HASH ' + h);
console.log('VERIFY ' + verify_hash(password, h));

// Usage for passwords generated with flask_security:

// SECURITY_PASSWORD_SALT is set in config.py and used by flask-security
var SECURITY_PASSWORD_SALT = 'Many random bytes...';

var password_hmac = get_hmac(SECURITY_PASSWORD_SALT, password);
var h = new_hash(password_hmac, 20000);
console.log('HASH ' + h);
console.log('VERIFY ' + verify_hash(password_hmac, h));
        

Sign up to request clarification or add additional context in comments.

Comments

1

Ok, I turned to sha512_crypt instead and found a nice library for node called sha512crypt-node. The README itself contains an example for both Python and Node, exactly what I needed. Here's a little example for ppl. using these platforms:

Python:

from passlib.hash import sha512_crypt

orig = "password"
h = sha512_crypt.encrypt(orig)
print("hash", h)
# h for eg. is $6$rounds=100000$5YnTXatKh4b1pLjp$3QQjVIfjrbiTakj.wkaw1woAcFiPRAjJP2U/b3BiGW4m8OvI8x0tgw1bb63dNQWMUl1uYNDBcTO3tWgrJ6eHh1

okay = sha512_crypt.verify(orig, h)
print("verified", okay)

Node:

var sha512crypt = require("sha512crypt-node").sha512crypt;

// origHash is the hash generated by passlib    
var origHash = "$6$rounds=100000$5YnTXatKh4b1pLjp$3QQjVIfjrbiTakj.wkaw1woAcFiPRAjJP2U/b3BiGW4m8OvI8x0tgw1bb63dNQWMUl1uYNDBcTO3tWgrJ6eHh1",
    parts = origHash.split('$'),
    rounds = parts[2],
    salt = '$' + parts[1] + '$' + rounds + '$' + parts[3],
    password = "password";

var hash = sha512crypt(password, salt);
console.log("verified", hash === origHash);

Comments

1

Sadly none of the mentioned solutions work for me (passlib 1.7.4). I ended up writing my own little node-module. If someone is still stuck on this feel free to use node-passlib

Example:

import nodePasslib from 'node-passlib';

// python-passlib PBKDF2-SHA512 hash
const pbkdf2Hash = '$pbkdf2-sha512$25000$JKSU8r63VgrBmHMO4RwjRA$bUL/owmBl8slaj.fjONmdRijzOs4Lo6EwbKtoA6EPX1hs1BCdg3JRjfkR3WX5/mZ4cIhtJhFVFxrLlq1lHfpQw';

nodePasslib.verify('yourpassword', pbkdf2Hash);
// true

nodePasslib.verify('wrongpassword', pbkdf2Hash);
// false

The default keylen is set to 64. If you use a SHA256 encoded hash you need to specify a different keylen:

nodePasslib.verify('yourpassword', pbkdf2Hash, 32);

Hope that helps.

1 Comment

Thanks for answering the question. Consider adding here a small example on how to use this module.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.