# bcrypt and pbkdf2 double hashing

If I want to protect myself from implementation of algorithm issues and inherent design flaws of algorithms in password hashing, what would be most secure way to combine two hashes?

1. bcrypt(pbkdf2(pw))
2. pbkdf2(bcrypt(pw))
3. f, l = split(pw); pw = bcrypt(f) + pbkdf2(l) # loses entropy, bad
4. pw = bcrypt(pw) + pbkdf2(pw) ## one broken, both broken, particularly bad

Something else? Should I do variation of 1-2 where I'll add entropy?

Why isn't this common/best-practice when storing user data in databases? Using any single hash algorithm seems risky when you combine implementation + design risks. Using SHA seems odd choice, as it's designed to be fast.
Seems odd that end-user needs to think about this and figure out safe way to store PWs.

I don't want to make perfect-world bcrypt or perfect-world pbkdf2 better by this means, I simply want to assume that there are scenarios where in particular environment one of them may be bad to begin with at least. Or in future one of them is found to be not as nearly secure as it was thought to be, giving me safety margin to migrate to another combo of hashes, without going into panic mode.

-
For this level of paranoia I would suggest using scrypt, although the intended use may allow one to provide a better answer. pbkdf2 can use any hash algorithm by the way. – Richie Frame Sep 15 '13 at 9:13
As neither pbkdf2, scrypt or bcrypt are extremely common, I don't think it's paranoid to think that in some less used programming language implementation may have naive mistakes which make it much less secure than intended. I'm happy about the scrypt suggestion, but the main question remains, how should I combine two hashes to protect myself from scenario where one of them is not as secure as intended. – ytti Sep 15 '13 at 9:23
This question might be of interest, and it's loosely connected to yours – rath Sep 15 '13 at 11:51
@ytti - Perhaps bcrypt and scrypt aren't that common (scrypt less so), but pbkdf2 is very widely used - it's even been standardized. – hunter Sep 15 '13 at 14:53

You have two algorithms, $A$ and $B$, that claim to compute two (essentially) injective, hard-to-invert and costly-to-compute functions $F$ and $G$. Your fear is that either of the algorithms instead compute functions $F'$ and $G'$, that may be neither injective, hard-to-invert, nor costly-to-compute. This may happen because of programming mistakes or (these days) deliberate sabotage.

What you want is some way to combine $A$ and $B$ such that the corresponding combination of $F$ and $G$, $F'$ and $G$, and $F$ and $G'$ is (essentially) injective, hard-to-invert and costly-to-compute. (If both are defective, then it will obviously not be possible to do anything.)

Observe that $F(G(\cdot))$ and $G(F(\cdot))$ are bad constructions (they fail injective).

The construction $(F(\cdot),G(\cdot))$ does not fail injective and costly-to-compute, but may fail hard-to-invert.

If we assume that $F$ and $G$, $F'$ and $G$, and $F$ and $G'$ are in some sense independent (that is, say, $F'$ is not $G$), then $$F(\cdot) \oplus G(\cdot)$$ should be ok.

Why don't we do this? To a certain extent, you can verify the implementations by comparing with test vectors and other known-to-be-good implementations.

-
Why would you even consider the possibility that F(G(·)) is hard to invert? If this is a tangible possibility, it means that there is some message m that is considered weak - namely F(m) is not hard to invert (in your scenario, we declare m = G(·) ). Is there a message that is considered weak for any of the common hash functions? – Ninveh Sep 15 '13 at 15:10
I was thinking about pre-image resistance, rather than inverting the hash function. I'll fix. – K.G. Sep 15 '13 at 16:07

Why isn't this best practice? Because it adds needless complexity. It solves a problem we don't have. The odds of a cryptanalytic breakthrough that yields a shortcut attack on bcrypt or PBKDF2 is... slim to none. It's very unlikely that the mathematics of bcrypt/PBKDF2 are going to be the weakest link in your system.

So, why don't we do this? Because professionals do a rational risk analysis and base their decisions on that. We do threat modelling and then we prioritize the risks. We look at the failure modes that are the most likely and most severe (and that are the cheapest to fix), and then we focus our energy mainly on reducing those risks. We don't mess around with trying to make bcrypt or PBKDF2 stronger because the risk that defends against is already extremely small, much smaller than other ways that the system might be breached.

-
You give opinion on the probability of hash being insecure by design as very low. You don't address the other risk I'm trying to protect from, the probability of some language having catastrophic implementation defect in some crypto library. – ytti Sep 15 '13 at 18:42
@ytti, we generally try to mitigate that risk through test vectors, thorough testing, and code review. That's probably cleaner than adding more code (which provides more opportunities for bugs). – D.W. Sep 15 '13 at 22:17
I like redundancy and I feel probability of having implementation defect in XOR operation is significantly smaller than in external crypto library which I'm not competent enough to review. – ytti Sep 16 '13 at 6:08