This has been already brought up on security SE, but very sadly, this particular interesting issue got watered down in a far broader question.

If and why is it bad to use the following hashing algorithm to store passwords?

scrypt(bcrypt(password + salt))

The standard argument to questions like this, reiterated over and over on Cryptography.SE, Security.SE, and many other places in the Internet (1, 2, 3, 4, 5, 6, 7, 8 and many others) is as follows:

You should use well-known, well-tested, public algorithms prepared by the world's best of the best cryptographers ONLY. You should use well-known, well-tested, public implementations of these algorithms ONLY. Home-brewing ANYTHING, however slight, is a Bad Thing ™, because frankly you are not competent enough to do this.

By this line of argumentation it is wrong to use scrypt(bcrypt(password + salt)) only because there is no such primitive provided out-of-the box by Oh Esteemed Experts. They only provide scrypt itself, or bcrypt itself, so we must stick to either; but joining them is a home-brew addition, so doing this is Evil.

This kind of argumentation besides, what are the particular evils of scrypting a bcrypted password? In this particular case, there are interesting arguments FOR doing this. Let me present a few arguments and counter-arguments

The primary argument for using this hash:

Additionally, if someone figures out a more intelligent way to guess the original plaintext for one hashing algorithm, they probably won't simultaneously have a faster way for the other algorithm. Say people legitimately thought ROT13 was a good hashing mechanism. If I used ROT13(bcrypt(x)), wouldn't I still be better off when people 'cracked' ROT13?

Counter-argument, which I don't find very convincing:

Another potential issue with combining bcrypt and scrypt like this is that there has been very little study into how the two interact. As such, we don't know if there are any weird cases that can cause problems. As a more obvious example, take the one time pad. We compute c=m^k for some message m and some equally long perfectly random key k, and we get perfect security. So let's do it twice, for even more security! That gives us c=m^k^k... oh, wait, that just gives us m. So because we didn't take the time to properly understand how the internals of the system worked, we ended up with a real security vulnerability. Obviously it's more complicated in the case of KDFs, but the same principle applies.

Counter-counter argument that seems to destroy the above one:

You're doing it wrong. The bcrypt(scrypt(x)) stuff is ok while xor(xor(x,k),k) isn't because an extra bcrypt is something an attacker could do, too.

And also:

I still don't see why the scrypt(bcrypt(x)) thing could be a problem. If the two interact weirdly, just using bcrypt doesn't make it more secure- an attacker can simply scrypt() what you've bcrypt()'d.

Counter-argument, which I don't find very convincing, because how many lines of code would this be?

The primary issue is that you have to introduce more code to parse the encoded output of scrypt / bcrypt so that the salt and round count can be stored separately.

The only counter-argument that seems valid to my laymen eye, but still fails to invalidate the primary argument for this cipher:

Using scrypt and bcrypt together has the problem that they both take time to run - you'll probably wind up halving the number of rounds on each to compensate for having to run both, which is more likely to be insecure than a "full" scrypt on top of a "full" bcrypt is.

So for the last time: Is it bad or good to do scrypt(bcrypt(password+salt)) and why exactly?

  • You have my vote. I think that you'll find the answer is cryptographic monoculture. The OTP counter argument is clearly facile and in no way represents valid opposition to your question. And how would they characterise $m \oplus k_1 \oplus k_2 $ except as a bit OTT? – Paul Uszak May 27 at 19:17

The combined strength of two key stretching algorithms is not greater than the sum of its parts. It is at best equal to the sum of its parts.

If you budget $x$ amount of CPU time to password stretching then you have to decide, do you give 100% of $x$ to scrypt? 100% to bcrypt? 50-50?

You can't give both 100% of $x$ to both scrypt and bcrypt because that's the same as doing a 50-50 split with double your initial time budget.

There is one important detail to scrypt that makes it better than bcrypt and PBKDF2. And that is that it is designed to use a lot of RAM. Storage with random access is hard to implement on ASICs in such a way that gives password crackers a significant advantage of the password-defender. The RAM use makes it hard to speed up cracking with GPUs as well.

Scrypt has a cost parameter. If you decrease it then it decreases both the amount of time that it uses and the amount of memory it uses. Bcrypt can be cracked faster than scrypt when scrypt is allowed high memory usage (and an equal amount of password-defender CPU time). So, when you take time (and thus memory) away from scrypt to give to bcrypt, you're letting a password cracker perform more guesses using less of their own resources.

  • Agreed. The only reason to use bcrypt is to decrease the amount of cache misses I suppose. If you're going to spend RAM and memory access to scrypt anyway, you might as well stick to it. – Maarten Bodewes May 27 at 23:18
  • This is, of course, a very good answer, have my upvote. However, it still fails to address the primary argument for stacking scrypt with bcrypt: Suppose that either is cracked, you still have the other one to protect you. However, if you stick to either and that one is cracked, you're screwed. – gaazkam May 28 at 9:21
  • @gaazkam That is only true when you take pre-image-attacks into account, but when you are able to create collisions for bcrypt, you are as well able to create a collision for scrypt(brcypt) (or the other way arround) so you actually decrease collision resistance. – VincBreaker May 28 at 16:43
  • Collision resistance is not relevant to password hashing. – Squeamish Ossifrage May 28 at 20:52

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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