4

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.

ytti
  • 153
  • 5

2 Answers2

6

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.

K.G.
  • 4,947
  • 19
  • 34
3

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.

D.W.
  • 36,982
  • 13
  • 107
  • 196