Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I've been reading up on password storage and such, and have come to the conclusion that I need to be using bCrypt. I've got an implementation working correctly, but I'm wondering the best way to move forward with my application.

The previous password storage system was simply hashing and salting. It's actually easier for me to keep this system and layer bCrypt over the top than it is to rewrite it with bCrypt alone.

However, I keep reading that combining different hashing algorithms is a bad idea - so I wanted to get some clarification, as I can't think of any reason not to implement in this way:

  • Hash(password + salt + pepper) * 1000 iterations - SHA-256
  • Resulting hash is run through bCrypt with its own salting and a defined work factor.
  • Final hash stored in DB along with original salt
  • To check password, it's rehashed as above before being put through the bScrypt hash check.

Is there any reason I shouldn't be doing things this way and should abandon the original password + salt + pepper in favour of just giving the plain password to bCrypt?

Thanks

share|improve this question
    
I presume I also need to convert the hash to base64... I read this somewhere also. Doesn't the character limit of bCrypt cause a problem? –  Gary May 21 at 16:05

1 Answer 1

There aren't any obvious security weaknesses with your schemes. The original one (other than possibly not storing a version number with your hashes so you can upgrade your scheme to more rounds or a new pepper if necessary), or the new one layered with bcrypt. Bcrypt can take 72 bytes of input so there's no problem using sha256 as your hash.

I would be careful if you encode the output of your keystrengthened sha256hash before feeding it to bcrypt. In principle, its probably best to keep it in the binary form -- if you were using sha512, that would let all the bytes be used (as 72 bytes = 576 bits). Granted with sha-256, it doesn't matter as long as you do it correctly. A sha256 hash is 32 bytes long (in binary with non-printable letters), 44 bytes long after base64 encoding, 64 bytes long (in hexadecimal), but 88 bytes if you base64-encoded the hex-encoded hash (which would be partially truncated and slightly weaken security -- e.g., instead of 256-bits of inherent security you have 72/88*256 ~ 209 bits).

Note, if a user has an extremely high entropy password (e.g., 400-bits of entropy) that in principle would require ~2^400 time to crack, by using sha256 as an intermediate step it will only take time ~2^256 before its likely to find some password that works. Granted 2^256 is completely secure these days (as is anything significantly over ~2^80, so this difference has no practical relevance).

It does get more complicated and potentially more annoying to maintain (granted you could argue this is a good thing -- if your database was ever leaked it would be harder for an attacker to get a GPU to brute force it). If you ever upgrade your system and things don't work perfectly there are significantly more points where things could be going wrong. If someone new developer comes along and see hashes are in the form like bcrypt, they may assume they are just bcrypt and change something that breaks the system.

An alternative would be to keep both systems in place and gracefully upgrade users to bcrypt on their next successful login.

def login(username, password):
    user = get_user(username)
    if user.uses_old_hash():
        calculated_old_hash = sha256_1000_round(password, salt=user.salt)
        if constant_time_string_compare(user.hash, calculated_old_hash):
            calculated_new_hash = bcrypt.hashpw(password, salt=bcrypt.gensalt(12))
            user.hash = calculated_new_hash
            print "Login Successful"
            return True
        else:
            print "Login Failed"
            return False
    else:
        hash = user.hash
        calculated_new_hash = bcrypt.hashpw(password, salt=hash)
        if constant_time_string_compare(hash, calculated_new_hash):
            print "Login Successful"
            return True
        else:
            print "Login Failed"
            return False
share|improve this answer
    
Just remember (B)crypt expects a null terminated string when appending the binary form, which can contain null bytes, to the salt. –  Darsstar May 21 at 20:48
    
Thanks for the detailed response. I'm a little confused on a few points here: First, you seem to be suggesting I can use SHA-512 - which is what I'd prefer. I was under the impression that the result wouldn't fit into bCrypt's character limit? Second I read that I needed to base encode the hash to base64 before passing it to bCrypt because of some problem with certain characters that may come out of the SHA hash, but again you seem to suggest otherwise? Third, in your code example, you're passing the password directly to bCrypt rather than the hash result - this is my original question! ;) –  Gary May 22 at 9:16
    
Ok, I think I understand your points regarding the encoding... I don't seem able to keep the hash in binary format and still pass it to the bCrypt hashpw function - this could be a limitation of the library or language I'm using, I'm not sure. As best I can tell the hash function I'm using outputs a printable hexidecimal string, which I was then base64 encoding. If I understand you correctly, I should refrain from base64 encoding this string. If I change to SHA-512, this string becomes 128 hexidecimal characters - I assume I cannot put this through bCrypt. –  Gary May 22 at 10:02
    
With regard to password entropy - one of my reasons for wanting to hash the password before putting through bCrypt, was the character limit. If someone wants to create a passphrase of twenty words, wouldn't bCrypt reduce the entropy of this passphrase due to its length limitations? Whereas the hash of the password is always the same length. Please correct me if I'm wrong, I'm new to the cryptography discussion - I'd thought salting and hashing was secure on its own! ;) –  Gary May 22 at 10:04
    
@Darsstar I'm afraid that's gone over my head a little - My hashing function seems to output a hexidecimal string and while I can convert that to binary, the bCrypt hashpw function crashes out if I pass the result into it. –  Gary May 22 at 11:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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