The best summary I've read recently on peppering was posted to the PHC list by Thomas Pornin last week. It's worth quoting in its entirety;
Adding an additional secret key can be added generically, in (at least)
four ways, to any password hashing function:
1. Store: salt + HMAC_K(PHS(pass, salt))
2. Store: salt + PHS(HMAC_K(pass), salt)
3. Store: salt + AES_K(PHS(pass, salt))
4. Store: salt + PHS(AES_K(pass), salt)
I have used here "HMAC" to mean "some appropriate MAC function" and
"AES" to mean "some symmetric encryption scheme".
These methods are not completely equivalent:
-- With method 1, you forfeit any offline work factor extension that
the PHS may offer (i.e. you can no longer raise the work factor of a
hash without knowing the password). With methods 2 and 4 such work
factor extension can be done easily (if the PHS supports it, of
course). With method 3, you can do it but you need the key.
-- With methods 2 and 4, you must either encode the output of HMAC
or AES with Base64 or equivalent; or the PHS must support arbitrary
binary input (all candidates should support arbitrary binary input
anyway, it was part of the CfP).
-- Method 4 requires some form of symmetric encryption that is either
deterministic, or can be made deterministic (e.g. an extra IV is
stored). ECB mode, for all its shortcomings, would work.
-- Method 3 can be rather simple if you configure PHS to output exactly
128 bits, in which case you can do "raw" single-block encryption.
-- Methods 1 and 3 require obtaining the "raw" PHS output, not a
composite string that encodes the output and the salt. In that sense,
they can be a bit cumbersome to retrofit on, say, an existing bcrypt
library.
The important points (in my opinion) to take into account are:
1. This key strengthening (some people have coined the expression
"peppering" as a bad pun on "salting") can be done generically; the
underlying PHS needs not be modified or even made aware of it.
2. Keys imply key management, always a tricky thing. Key should be
generated appropriately (that's not hard but it can be botched in
horrible ways), and stored with care. Sometimes the OS or programming
framework can help (e.g. DPAPI on Windows). Sometimes it makes things
more difficult. You need backups (a lost key implies losing all the
stored passwords), but stolen backups are a classical source of
password hashes leakage, so if you do not take enough care of the
security of your backups then the advantage offered by the key can go
to naught.
3. For some historical reasons, many people feel the need to change
keys regularly. This is rather misguided: key rotation makes sense in
an army or spy network where there are many keys, and partial
compromissions are the normal and expected situation, so a spy network
must, by necessity, be in permanent self-cleansing recovery mode; when
there is a single key and the normal situation is that the key is NOT
compromised, changing it brings no tangible advantage. Nevertheless,
people insist on it, and this is difficult. The "method 3" above
(encryption of the PHS result) is the one that makes key rotation
easiest since you can process all stored hashes in one go, as a
night-time administrative procedure.
4. Key strengthening makes sense only insofar as you can keep the key
secret even when the attacker can see the hashes. In a classical
Web-server-verifies-user-passwords context, the hashes are in the
database; one can argue that database contents can be dumped through a
SQL injection attack, but a key stored outside the database might evade
this partial breach. But if the key is in the database, or the breach
is a stolen whole-server backup, then the key does not bring any
advantage.
5. If you _can_ store a key that attackers won't steal, even if they
get all the hashes, then you can forget all this PHS nonsense and just
use HMAC_K(pass) (or HMAC_K(user+pass)). The key must thus be
envisioned as an additional protection, a third layer (first layer is:
don't let outsiders read your hashes; second layer is: make it so that
your hashes are expensive to compute, in case the first layer was
broken through).
P.S. As the inventor of blind hashing (which would have prevented this breach entirely) I have a serious horse in this race. We launch publicly at RSA 2015 in San Francisco. Hope to see you there!