Search code examples
javascriptnode.jscryptographybrowserifynode-crypto

node crypto key derivation from a password produces different results with browserify


I'm trying to re-create Symfony's MessageDigestPasswordEncoder in the browser.

I have some issue with browserify and crypto module. I try to generate hash with JavaScript but without Node.

Here is my code with node:

var crypto = require('crypto');
var encodePassword = function (raw, salt) {
    var salted = raw + '{'+salt+'}',
        hash = crypto.createHash('sha512').update(salted, 'utf-8');

    for (var i = 1; i < 5000 ; i++) {
        hash = crypto.createHash('sha512').update(hash.digest('binary')+salted);
    }

    return hash.digest('base64');
};

console.log(encodePassword("admin", "81b6zjhf64w8kogsgkgw804ss8gc0w0"));

It returns:

qmNs3bqtTeoS4uRq2Chh1fUNPu+lzn3KR7mWFHAq5NEPrK0QZ9XkLDUniZ39uosnozNrPL7mByzUZ/A19Io4sQ==

Now, considering I need to implement this without node, I used browserify

browserify index.js > crypto.js

I created a test page and include:

<script src="crypto.js"></script>

The console log output is:

JtDIZwGDybG6tG7PE2SeXS0BEa4vOoxpu3y7Il6P6OQL9djmrk5S0vjTGoQowGO22OvQ58tC05eZBt/yvyJv+A==

Any idea about why I have two different results?

Otherwise, is there a way to obtains the same result in pure JS (without node)?


Solution

  • I don't know what the trouble with the browserified code is, but the following is a CryptoJS implementation of Symfonys MessageDigestPasswordEncoder.

    /**
     * Encodes a password according to Symfonys MessageDigestPasswordEncoder
     * @param password String
     * @param salt String
     * @param base64 Boolean (default: false) If false, then the result is Hex-encoded
     * @param hasher Optional Hasher (default: CryptoJS.algo.SHA512)
     * @param iterations Optional Integer (default: 5000)
     * @returns {String} Iterated and salted hash of a password
     */
    function encodePassword(password, salt, base64, hasher, iterations) {
      hasher = hasher || CryptoJS.algo.SHA512;
      iterations = iterations || 5000;
      hasher = hasher.create();
      salt = password + "{" + salt + "}";
      var digest = hasher.finalize(salt);
      for (var i = 1; i < iterations; i++) {
        hasher.reset();
        hasher.update(digest);
        digest = hasher.finalize(salt);
      }
      if (base64) {
        return digest.toString(CryptoJS.enc.Base64); // Base64-encoded string
      }
      return digest.toString(); // Hex-encoded string
    }
    
    output.innerHTML = encodePassword("admin", "81b6zjhf64w8kogsgkgw804ss8gc0w0", true);
    <script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/sha512.js"></script>
    <script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/enc-base64-min.js"></script>
    <div id="output"></div>