There's nothing wrong with nesting algorithms (see the Facebook hash onion), so you can use the following scheme:
bcrypt(bcrypt(password, salt), pepper)
And do a pass on all your database entries like bcrypt(old_hash, pepper) bcrypt(password, salt+pepper)
Observe the difference between that and the rehash you just posted bcrypt(bcrypt(password, salt), pepper)I hope it's obvious that no one should never do this, since the output would contain the "salt+pepper" bits in cleartext alongside the hash, defeating the entire point of the "pepper":
https://www.usenix.org/legacy/event/usenix99/provos/provos_h...
In fact, this is a perfect illustration of why it's bad to put secret bits into a crypto function in a place that's not designed to take secret bits. Bcrypt does not treat the salt parameter as a cryptographic secret, and other algorithms might not either. And they might leak it in more subtle ways.
This is really vague. What kind of algorithm? With itself, or just anything inside anything else? Passing the raw output of any common cryptographic hash (SHA-x) to bcrypt, for example, completely destroys its security, as bcrypt input is null-terminated.
(What happens when you nest DES in A*, anyway?)
I would avoid using any particular symmetric algorithm twice. Otherwise if you have an example of algorithm chaining that can weaken security beyond the weakest link, I would love to see it. (Not that I think nesting is a great idea.)
It (poor implementation) is definitely related to implementing pepper on top of a secure password hash, though, which everybody is already doing differently.
> I would avoid using any particular symmetric algorithm twice. Otherwise if you have an example of algorithm chaining that can weaken security beyond the weakest link, I would love to see it. (Not that I think nesting is a great idea.)
“algorithm” is, again, really vague. (So is “nesting”.) But for something contrived and not snarky, here:
h = sha512_hex(password)
sha512_hex(bcrypt(h, gen_salt()) +
bcrypt(h, gen_salt()) +
bcrypt(h, gen_salt()) +
bcrypt(h, gen_salt()))
The weakest link here is 374 bits (4 bcrypts), but the output is 288.>“algorithm” is, again, really vague
Something that you can use to hash passwords. What you gave works if you assume gen_salt is seeded per user.
>The weakest link here is 374 bits (4 bcrypts), but the output is 288.
I'm afraid I don't follow. Your bit numbers confuse me, and I don't see how this results in an algorithm that is weaker than either sha512 or bcrypt.
They’re bits of entropy (not counting the password itself) (I think). SHA-512(M): 512 bits; SHA-512(SHA-256(M)): 256 bits, for example.
> I don't see how this results in an algorithm that is weaker than either sha512 or bcrypt.
That’s my point. It’s not easy to get this kind of thing right, so just don’t bother with pepper.
>That’s my point. It’s not easy to get this kind of thing right, so just don’t bother with pepper.
What? Your point is that you haven't demonstrated that it's weaker than the weakest link, therefore you win?
Edit: Okay I figured out where you got 288. Still confused by the 374. Anyway you need to make truncations explicit. You didn't pass all of the sha output to bcrypt. You're taking advantage of an implementation API bug.
I'm not asking for evidence that shoving together functions from google without understanding them can go wrong. That's trivially true.
I want an example where combining hash algorithms is inherently wrong. Like using a block cypher twice can pop out your plaintext, but probably not as extreme.
Edit 2: Oh, 384!
passwordHash = bcrypt(salt + password)
encryptedHash = encrypt(passwordHash, pepper)
This way you can rotate your pepper by doing: decryptedHash = decrypt(encryptedHash , oldpepper)
encryptedHash = encrypt(decryptedHash , newpepper)The point of salting password hashes is to prevent identical cleartext passwords from being stored as identical hashes in the database. Salts are often stored in the database, as well.
The point of peppering keeps a database dump from being at all useful for recovering passwords. It make sure that a component of the process of cleartext -> DB entry is not even in the database, requiring something from the app as well.
Why does encryption work here? Because you've already done a one way function on the cleartext -> salted hash. At that point, there is still no way to reverse the process all the way to get the cleartext. By using a two-way encryption function for the pepper portion, you keep the ability to rotate 'peppers' periodically, in case it is leaked, for example.
But, the original example is hashing the password. No encryption involved. So what makes anyone think that they can reverse a hash?
This would work if you were building a new system today, but if you had a DB full of one way hashes you're not going to be able to retroactively modify the pepper.
Straight from the original artical:
hashed_password = scrypt(password, salt + pepper)
or hashed_password = scrypt(scrypt(password, salt),pepper)
Absolutely zero encryption going on here. Therefore, no ability to "decrypt" the pepper result. The pepper IS NOT a key for encryption. Period.After hashing the password he is storing the hash along with the users random salt, not retroactively applying the salt a second time.
I think the article's context is if you don't currently use a pepper, you can easily add one and update all of your password hashes in the database.
Later edit: I'm referring to your example in your link:
How do you retroactively apply this?