2

I'm trying to port a legacy password hashing scheme from PHP to Javascript (node.js), but I'm missing something in the process.


Original, working PHP version

Here is the "original", working code:

function create_hash($password) {
    $salt = uniqid();
    $algorithm = '6'; // CRYPT_SHA512
    $rounds = '1234';

    // This is the "salt" string we give to crypt().
    $cryptSalt = '$' . $algorithm . '$rounds=' . $rounds . '$' . $salt;

    $hashedPassword = crypt($password, $cryptSalt);

    return $hashedPassword;

}

function hash_is_valid($password, $hash) {
    return (crypt($password, $hash) == $hash);
}

Working test with above PHP functions:

echo $password = 'secret';
echo '<br /><br />';

echo $hash = create_hash($password);
echo '<br /><br />';

echo 'Should be true: ';
$valid1 = hash_is_valid($password, $hash);
var_dump($valid1); // outputs "bool(true)"
echo '<br /><br />';

echo 'Should be false: ';
$valid2 = hash_is_valid('wrong_pass', $hash);
var_dump($valid2); // outputs "bool(false)"
echo '<br /><br />';

Javascript Version (broken)

Here's what I've got so far, but it isn't quite there

function hash_is_valid(password, hash, cb) {

    // Hash Format:
    // $[algorithm]$[number_of_rounds]$[salt]$[hashed_password]
    let hashParts = hash.split('$')
    console.log(hashParts)
    let salt = hashParts[3]
    let numRounds = parseInt(hashParts[2].substr(7)) // from string like rounds=1234
    let originalHashResult = hashParts[4]

    let salted = password + '{' + salt + '}'
    let processedHash = crypto.createHash('sha512').update(salted, 'utf-8');

    for (let i = 1; i < numRounds ; i++) {
      processedHash = crypto.createHash('sha512').update(processedHash.digest('binary') + salted);
    }

    processedHash = processedHash.digest('base64')

    // Capture the result
    let hashVerified = (processedHash === originalHashResult)

    // Call the callback with the result
    cb(hashVerified)

}

hash_is_valid('secret', '$6$rounds=1234$56ab50921c460$P4bgd3kMX2xyWJTDOYAdow.jsXiS2TARUJW4BXifgm4czraOIDFLqZ5Ii50GLIKwYTjWwN6WrtG82omQes0cK0', (passed) => {
  console.log((passed) ? 'password is good' : 'password is no good')
})

javascript based on answer to SO question How to validate Symfony2 sha512 passwords using nodejs

The javascript version is using some test values:

password: secret

hash: $6$rounds=1234$56ab50921c460$P4bgd3kMX2xyWJTDOYAdow.jsXiS2TARUJW4BXifgm4czraOIDFLqZ5Ii50GLIKwYTjWwN6WrtG82omQes0cK0

2
  • 1
    Can you show what hash does your js code produce? The problem can be in what extra symbols nodejs base64 implementation uses. Also I've noticed that for-loop runs numRounds - 1 times since it starts with 1. Commented Jan 29, 2016 at 12:34
  • @ksimka — example base64 output of hash looks like: nyqekS7RsL62iyBLbCtO9OTKPmNvO1n9mq7yZA15E7x3URU7YN1qsWYjAErujvptidcp1+nYytx4wJGSsAjX0A== Commented Jan 29, 2016 at 18:46

1 Answer 1

1

You can use crypt3 NPM package to accomplish this:

var crypt = require('crypt3'),
    password = 'secret',
    hash = '$6$rounds=1234$56abadac833a5$fQIaUG/da/KqlJc5DzCIym/PL.ZhEdw5VOZsw7mofkm.3aE2rZ/rjDCczymRbj1V3rF6lzJO7DR1WOd2ZnC6O.';

function hash_is_valid(password, hash) {
  return hash === crypt(password, hash);
}

console.log(hash_is_valid(password, hash)); // true

Both PHP's crypt function and NPM's crypt3 package delegate to crypt(3) so given identical data, should produce the same result.

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

1 Comment

Perfect. This is much simpler. Note that it does not work on OS X, but does work on Ubuntu.

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.