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.
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?