31

When creating a hash with PBKDF2, it allows the developer to choose the size of the hash. Is longer always better? Also, what about the size of the random salt? Should that be the same size as the hash?

EDIT: Particularly in hashing passwords.

5
  • In which context?
    – curiousguy
    Aug 1, 2012 at 19:40
  • Particularly in hashing passwords.
    – blesh
    Aug 1, 2012 at 19:49
  • Will you use the password to generate encryption and message integrity keys, as in WPA-PSK?
    – curiousguy
    Aug 1, 2012 at 20:04
  • Possibly, but most likely it's going to be the standard hash-check login scenario.
    – blesh
    Aug 1, 2012 at 20:15
  • Then you probably do not need a very long hash.
    – curiousguy
    Aug 1, 2012 at 20:49

3 Answers 3

27

For the hash function, you want to use a function for which the most efficient platform type (the one which will produce the more hash computations per second and per dollar) is the machine that you intend to use (i.e. a PC). That's because you are in a weapon race with the attacker, and the attacker can buy other kinds of hardware to get an edge over you (such as a GPU). GPU are very good at 32-bit arithmetics, but not at 64-bit arithmetics, whereas a PC (in 64-bit mode) will be quite fast at the latter.

Thus, use a hash function which runs on 64-bit arithmetic operations. This points at SHA-512.

PBKDF2 is a key derivation function: it produces an output of configurable size. For password hashing, you want the size to be large enough to deter generic preimage attacks (i.e. trying random passwords until a match is found), so this would need, say, at least 80 bits of output. If only for making the security more convincing for the unwary, and also for aesthetics, go for the next power of 2: a 128-bit output. It is not useful to go beyond that.

The salt must be unique -- as unique as possible. An easy way to achieve unique salt values is to generate salts with a cryptographically strong PRNG: probability of reusing a salt value will be sufficiently low to be neglected if these random salts are large enough, and "large enough" means "128 bits". So use random 128-bit salts.

Personally, I prefer bcrypt over PBKDF2 for password hashing.

6
  • Wha you mean that more than 128 bit output "is not useful"? As the entropy of password and salt will no exceed 128bit? Runtimecost? Will the use of HMAC-SHA1 instead of SHA512 change you given numbers of output and salt size?
    – aggsol
    Jun 5, 2013 at 12:19
  • Given the low security cost of reusing an occasional PBKDF2 salt, the cost to the entropy pool of generating an extra 8 random bytes (and the memory and disk cost to store them), is 128 bits really worth it over 64 bits? Would adding a constant "purpose" string to the salt achieve the goal even better? (I know this is a bit nit-picky; it's just something I've been thinking through recently and was looking for other opinions.)
    – Rob Napier
    Jan 15, 2014 at 21:05
  • 3
    All these questions about "the entropy pool" are misguided. Entropy does not exhaust like gasoline. If you are doing things properly, then whatever "real entropy" you got for hardware is used as a seed for a cryptographically strong PRNG which will give you gigabytes of pseudorandomness which is indistinguishable (in a strong sense) from random bytes -- and that's good enough for crypto. In particular salts, which don't even need to be indistinguishable from randomness; they just need not to repeat too often. Jan 15, 2014 at 21:11
  • 1
    Note that for PBKDF2 in password hashing, the output length must be strictly less than or equal to the native hash output size. I.e. for PBKDF2-HMAC-SHA-512, no more than 512 bits of hash output. The reason for this is that if you ask for more, then PBKDF2 specifies concatenating the result of the first <native hash size> bits with the results of <each following output>, so an attacker will only need to match the first <native hash size> bits to see they've found the password. As the defender, you should simply increase number of iterations, which affects both you and the attacker linearly. Feb 22, 2014 at 21:10
  • @Anti-weakpasswords But if we hash the result - we should be OK with lengths over the native length. Correct? (I asked about it here)
    – ispiro
    Jan 22, 2018 at 15:49
13

According to the PBKDF2 standard, the minimum recommended size for the salt is 64 bits, though I'd personally recommend 128 bits or higher for a decent safety margin. The size of the salt is independant to your choice of hash.

As far as security is concerned, I recommend choosing a derived key length of at least the same size of your salt's output, with a minimum of 256 bits. Any hash size less than 256-bit is below the security boundary of most modern hash functions anyway.

Choosing a derived key length that is less than the output length of the hash function makes little sense, unless you're using the key for a block cipher that can't handle a key of that size.

In terms of optimal security, I'd suggest SHA-512 as the PRF, with a 512-bit derived key, a 128-bit salt, and as many iterations as your situation can warrant.

6

In case your platform of choice is .NET, there is an OWASP article specifically dedicated to its Rfc2898DeriveBytes class. Specifically note (emphasis mine):

The built-in .NET implementation of Rfc2898DeriveBytes limits the user to one psudorandom function - HMAC with SHA-1. This is acceptable in most scenarios today, but in the future, a more complex hashing function may be required.

Using PBKDF2 for password storage, one should never output more bits than the base hash function's size. With PBKDF2-SHA1 this is 160 bits or 20 bytes. Output more bits doesn't make the hash more secure, but it costs the defender a lot more time while not costing the attacker. An attacker will just compare the first hash function sized output saving them the time to generate the reset of the PBKDF2 output.

They also have a code sample, which I adjusted per the other answers' guidance:

public static string Hash(string str)
{
    // Generate the hash, with an automatic 16 byte salt
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(str, 16))
    {
        rfc2898DeriveBytes .IterationCount = 10000 // or whatever you can afford
        byte[] hash = rfc2898DeriveBytes.GetBytes(20);
        byte[] salt = rfc2898DeriveBytes.Salt;
        return Convert.ToBase64String(salt) + "|" + Convert.ToBase64String(hash);
    }
}
1
  • .NET Core supports HMAC-SHA216 and HMAC-SHA512 just fine. That article, and this comment, are woefully outdated.
    – Ian Kemp
    Sep 13, 2021 at 10:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .