1

To securely hash passwords, algorithms such as PBKDF2 do many iterations of a common hash such as SHA1. Are there certain ways that these iterations need to be done to be safe?

In particular, from password-hash.js:

function generateHash(algorithm, salt, password, iterations) {
  iterations = iterations || 1;
  try {
    var hash = password;
    for(var i=0; i<iterations; ++i) {
      hash = crypto.createHmac(algorithm, salt).update(hash).digest('hex');
    }

    return algorithm + '$' + salt + '$' + iterations + '$' + hash;
  } catch (e) {
    throw new Error('Invalid message digest algorithm');
  }
}

Is this a safe way to hash the password? I can imagine that if you have an algorithm such as this that does:

HMAC(HMAC(HMAC(password)))

then there exists some faster function

HMAC3(password)

that gives the same output. Is this the case?

2

Yes, there are several requirements for an iterative hash function:

  • It should not be possible to do any precomputation, such as using rainbow tables.
  • The implementation should avoid running into cycles or fixed points in the hash function.
  • The implementation should be as fast as possible, because that is what the attacker would use.

Especially on the final point the example code is lacking. The crypto.createHmac(algorithm, salt) should really be outside of the loop. Because the attacker hashes many passwords with the same salt, he can even place the createHmac call outside of the password loop, e.g:

hmac = crypto.createHmac(algorithm, salt)
for (password in allpasswords) {
    var hash = password;
    for(var i=0; i<iterations; ++i) {
        hash = hmac.update(hash).digest('hex');
    }
    ...
}

This gives the attacker an advantage when cracking passwords, which would be avoided if the HMAC used the password instead of the salt as the key, as PBKDF2 does.

By implementing the given code in C and doing some optimizations, I managed to get it to be 10 times faster than the original NodeJS implementation. This means that an attacker can crack passwords 10 times faster than you intended, without even using a GPU or FPGA.

So there does exist a function that does a fast version of HMAC(HMAC(HMAC(password))), simply because HMAC does much the same work in every iteration.

I wrote a blog post about this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.