1

I'm attempting to recreate a password hashing implementation in node.js (LTS latest--14.x) that was originally written in PHP (7.2). I believe the node.js implementation that I've written should do the exact same thing; however, the node.js implementation differs after the first pass of the hash in the loop. What am I missing here?

PHP implementation (I cannot change this since it's part of a web framework and existing authentication relies on the hashing mechanism staying the same):

$algo = "sha512";
$salt = "someSalt";
$password = 'somePassword';
$count = 32768;

$hash = hash($algo, $salt . $password, TRUE);
// $hash is the same as in the corresponding line in the node.js implementation
do {
  $hash = hash($algo, $hash . $password, TRUE);
  // $hash differs from the node.js implementation after the first pass here... why?
} while (--$count);

Node.js implementation:

const crypto = require('crypto');

const algorithm = 'sha512';
const salt = 'someSalt';
const password = 'somePassword';
let count = 32768;

let hash = crypto
        .createHash(algorithm)
        .update(salt + password)
        .digest('binary');
// hash is the same as in the PHP implementation here
do {
  hash = crypto.createHash(algorithm).update(hash + password).digest('binary');
  // hash differs between the two implementations after the first pass here... why?
} while (--count);

EDIT: Updated to show the original Node.js implementation where I did not stringify data being passed to update().

8
  • Try to check it to .digest('hex') Commented Apr 7, 2021 at 18:12
  • The original implementation in PHP outputs to binary, not hex (see the last parameter to the hash() function is TRUE). EDIT: I tested it just to be sure, and it definitely results in a different hash still. Commented Apr 7, 2021 at 18:17
  • Look at your NODE update it stringify's the binary output. In the PHP version you do not. I would use the .digest('hex') where possible in NODE, and do bin2hex() in PHP's side. That way you can keep the binary's out and work with the HEX values Commented Apr 7, 2021 at 18:20
  • I've provided an answer along with the output from my tests. PHP 7.4.16, and Node 14.16.0 Commented Apr 7, 2021 at 18:26
  • 1
    In the NodeJS code, binary' must be specified as the second parameter in the update() calls (default is UTF8, which corrupts the data, at least for the 2nd and subsequent update() calls). Then both codes return the same result on my machine. Commented Apr 7, 2021 at 18:31

2 Answers 2

2

Expanding on what my comment was I think the following might work.

NODE:

const crypto = require('crypto');

const algorithm = 'sha512';
const salt = 'someSalt';
const password = 'somePassword';
let count = 32768;

let hash = crypto
        .createHash(algorithm)
        .update(String(salt) + String(password))
        .digest('hex');
// hash is the same as in the PHP implementation here
do {
  hash = crypto.createHash(algorithm).update(String(hash) + String(password)).digest('hex');
  // hash differs between the two implementations after the first pass here... why?
} while (--count);


console.log(hash);

PHP code:

$algo = "sha512";
$salt = "someSalt";
$password = 'somePassword';
$count = 32768;

$hash = bin2hex(hash($algo, $salt . $password, TRUE));
// $hash is the same as in the corresponding line in the node.js implementation
do {
  $hash = bin2hex(hash($algo, $hash . $password, TRUE));
  // $hash differs from the node.js implementation after the first pass here... why?
} while (--$count);

var_dump($hash);

Confirmed my findings with the output from NODE:

node nodeTest.js
df8202221e5cbff38c16a33945efa8dcb44d0e7267cdf1514cefffb3df321f69ad1d9b01cfb6360391f1de4791e26a179fd165248b4b75699cb2d3395c971351

PHP output:

php test.php
string(128) "df8202221e5cbff38c16a33945efa8dcb44d0e7267cdf1514cefffb3df321f69ad1d9b01cfb6360391f1de4791e26a179fd165248b4b75699cb2d3395c971351"
Sign up to request clarification or add additional context in comments.

1 Comment

I just posted this in response to another comment, but I'm unfortunately unable to modify the original PHP implementation since it's part of a web framework and that would break the existing authentication. I'm just attempting to replicate the authentication mechanism in Node.js so I can transition away from the PHP web framework.
0

I had to pass 'binary' to update() while hashing in the loop, since the data being provided is in binary.

// The data being passed to update() here is string (update defaults to utf-8, I believe).
let hash = crypto
        .createHash(algorithm)
        .update(salt + password)
        .digest('binary');
do {
  // The data being passed to update() is binary.
  hash = crypto.createHash(algorithm).update(hash + password, 'binary').digest('binary');
} while (--count);

Credit: @Topaco for the finding the solution to my problem!

Comments

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.