10
$\begingroup$

There have been several questions regarding password hashing here and on Security.SE.

A "pepper" is sometimes mentioned – an application-specific secret key. The canonical answer on password hashing implies that a adding pepper brings the composition close to a MAC, and that cryptographic MAC function should be used rather than concatenating a pepper together with the salt and password.

My question is: Where applicable, what would be a generally recommendable, sound, well understood way to MAC a user password and a secret key, in the context of password hashing?

I want to MAC the password using a secret key, then feed the output of the MAC to a slow hash function (like bcrypt, scrypt, or PBKDF2). I want to know what kind of MAC to use. To represent the MAC, I'm using this notation

  • HMAC-SHA256secretkey(message) and HKDFsalt(password)
  • password is the low-entropy user password
  • key is the high entropy secret key (the "pepper")
  • salt is a per-password random value, optionally reused by the subsequent computationally expensive hash such as bcrypt, PBKDF2, or scrypt

Would any of these constructs be more recommendable? Or is there an even better way? And if so, briefly why?

  1. HMAC-SHA256 key(password)
  2. HMAC-SHA256password(key)
  3. HKDFpassword(salt)

NB: This question is in relation to the "pepper" section near the end of the accepted answer to "How to securely hash passwords". Thus the output of the MAC will be fed to a slow, computationally expensive KDF like bcrypt, PBKDF2, or scrypt.

$\endgroup$
6
  • $\begingroup$ An alternative would be using the password hash before the pepper. $\:$ (I don't know which would be better.) $\endgroup$
    – user991
    May 8, 2013 at 17:30
  • $\begingroup$ You might want to read the HKDF specification, in particular the section on "extraction". You should change the role of the salt and the password in 3. $\endgroup$ May 8, 2013 at 18:54
  • $\begingroup$ @Henrick Hellström: Changed. Thanks. :) I'm not sure I understand the reasoning. Feel free to edit the question to improve it. :) $\endgroup$
    – user950
    May 8, 2013 at 19:09
  • $\begingroup$ "by a subsequent" $\: \mapsto \:$ "by the subsequent" $\;\;\;$ $\endgroup$
    – user991
    May 8, 2013 at 20:59
  • 1
    $\begingroup$ I would encrypt the hash $\endgroup$ May 9, 2013 at 11:28

1 Answer 1

8
$\begingroup$

Given the clarified answer, I suggest you do the following:

$T = F_k(\text{password})$

$Y = H(T, \text{salt})$

where $F$ is a PRF (pseudorandom function) and $H$ is a slow hash. Here $k$ is the secret key (the "pepper").

You can instantiate $F$ with AES-CMAC or SHA256-HMAC (or any other good PRF). When you use $F$, feed the secret key $k$ (the pepper) into the key input of the PRF, and feed the password into the message input for the PRF. (This corresponds to your option 1. I do not recommend using your option 2, and definitely not option 3.)

You can instantiate $H$ with a slow hash like bcrypt, scrypt, or PBKDF2. When you use $H$, feed $T$ into the password input for the slow hash, and feed the salt into the salt input for the slow hash.

Finally, store $Y$ and the salt in your database of hashed passwords.

This scheme ensures that if the key $k$ (the "pepper") is kept secret and is not known to the adversary, then the adversary will not be able to recover any of the passwords. It also ensures that if the key $k$ does become known to the adversary, the best the adversary can do is a dictionary attack on the passwords -- but this will be slowed down because of the slow hash. Consequently, if $k$ does become known to the adversary, this scheme should be no worse than just using scrypt/bcrypt/PBKDF2 without a pepper.

$\endgroup$
2
  • $\begingroup$ Let me see if I understood correctly. This would be the correct process? $pass = "electric horse stapple corect"; $pepper = "60 bytes of gibberish (upper ASCII symbols, etc)"; $mac = hash_hmac('whirlpool', $pass, $pepper); $salt = "60 bytes from /dev/urandom (bynary output)"; $hashedPass = brcript(13, $mac . $salt); // bcript work factor of 2**13 And store hashedPassas and salt, while pepper is in a config file in a different server. $\endgroup$ May 19, 2013 at 9:06
  • $\begingroup$ @TheDisintegrator, the pepper should be truly random (e.g., generated from /dev/urandom and kept secret thereafter). I don't know about what order your $hash_h mac$ accepts parameters in; I explain where the inputs should go in my answer. As far as how to call bcrypt, that will depend upon your particular language and library's API. If you want to know how to code this up, that's better for StackOverflow. You choose the work factor for bcrypt so that computing bcrypt takes, say, 50ms. $\endgroup$
    – D.W.
    May 19, 2013 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.