3
$\begingroup$

I need to store passwords for a web application, and was looking at Bcrypt for a possible solution. After some research, it appears only the first 72 bytes influence the final hash output. While it is possible for the application to limit input to 72 characters, this hurts users with password managers that produce very long passwords.

While it is well known that hash1(hash2(x)) only serves to increase collisions, there doesn't seem to be much information about the implications of using bcrypt(hash1(x)), as indicated by this answer.

First idea

If the password p is p > 72 bytes, SHA512 [64 bytes] the password and then Bcrypt the hash.

Possible cons:

  • hashing a hash
  • the input to Bcrypt is only ever as big as the output of the hash function (64 bytes)

Second idea

If the password p is p > 72 bytes, calculate the hash with (pseudo-code):

bcrypt(p[0:8] + sha512(p))

This Bcrypt's the concatenation of the first 8 bytes of the password (which is 72 - 64, where 64 is the bytes of the SHA512 hash) with the SHA512 [64 bytes] digest of the entire password. By this, the Bcrypt function hashes an entire 72 bytes, instead of just 64 like with the plain old SHA512 [64 bytes] hash.

Probable pros:

  • full 72 bytes are Bcrypt'ed
  • does not Bcrypt just a hash; this may make it so that if a collision is found in the SHA512 [64 bytes] hash, there are still a leftover of 8 bytes of entropy needed to crack the password

Possible cons:

  • still involves partially hashing a hash

I'm under the impression that the second idea is the best and most secure way to go, but I'm not sure if that, at all, decreases security for passwords > 72 bytes in length. I'm asking if there are any obvious (or non-obvious) security implications with choosing this method.

$\endgroup$
2
$\begingroup$

A password manager that produces 16-character passwords is sufficient for most cases. Users who go for 100-byte passwords are usually overly-paranoid, since the actual security benefit is outweighed by the inconvenience. Therefore, limiting a password to 72 characters, while in theory reducing the number of possible passwords, is still very reasonable.

That said, hashing a hash is a bad idea if the hashes are related. sha512(sha512(password)) is more prone to collisions than sha512(password). However, bcrypt (being built with blowfish) is different enough from sha512 that this doesn't apply (not to mention bcrypt is salted). While you're still reducing a user's password from 72+ bytes to 64 bytes, the expected entropy of a user password is orders of magnitude less than 512 bits. It is safe to assume that sha512(password), while technically shorter than password, has much entropy as the original password.

Your second idea seems to be reusing a part of the password, so I don't see much of an increase in security. The entropy from p[0:8] will be spread out with sha512 sufficiently enough already.

$\endgroup$
  • $\begingroup$ I'll probably be going with limiting the length, but thank you for explaining the security of the other ideas. I also learned that GitHub limits their passwords to 72 characters, and Google limits it to 100; therefore, if such successful companies are okay with it, as am I. :) $\endgroup$ – T.J. Mazeika Jan 30 '16 at 23:47
2
$\begingroup$

While it is well known that hash1(hash2(x)) only serves to increase collisions,

Collisions essentially do not matter at all for password hashing. You will only lose entropy to collisions if the input entropy is near the size of the hash output. And in that case you are well and truly out of the realm of what can be cracked for any popular hash function.

Further, bcrypt itself only produces a 184-bit output. So entropy beyond that is completely wasted anyway.

First idea

If the password p is p > 72 bytes, SHA512 [64 bytes] the password and then Bcrypt the hash.

Neither of the cons you mention matters. However, there is a minor issue (that is also shared by HMAC): you introduce a set of equivalent passwords. Any password longer than 72 bytes is equivalent to a shorter password – it's SHA-512 hash. Of course most of those may not even be typable if you use the binary hash (but watch out for zeros if you do), and such long passwords should have enough entropy anyway.

Second idea

If the password p is p > 72 bytes, calculate the hash with (pseudo-code):

bcrypt(p[0:8] + sha512(p))

This does not increase the entropy of the bcrypt input at all for realistic passwords with much less than 512 bits of entropy. And like I wrote above, the output always has at most 184 bits anyway. So this is really no better than the first one.

By the way, you are still hashing a hash, you have only defined another hash: H(p) = p[:8] + sha512(p).


My recommendation would be to just hash the input in all cases, if you want to allow unlimited password length. That avoids having two different cases, the (really non-)issue of equivalent passwords, and it can be useful for other reasons. If you can do the initial unsalted SHA-512 in the browser, you also ensure the password length cannot leak when it is sent to the web server.

$\endgroup$

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.