# Ciphering the output of a password hash

In an article on password hashing security, Anthony Ferrara suggests using bcrypt to hash passwords and then using a "strong cipher (AES-128-CBC) with a key rotation policy".

This seems like a good suggestion which would help to counter ASIC attacks on bcrypted passwords. However, this could be circumvented if the secret key (or key rotation system) was compromised along with the password hashes.

The Question:

1. is the suggestion to cipher the password hash a good one, or could it actually weaken security?

2. would it be appropriate to use a public key cryptography algorithm like RSA, generate a public and private key pair, and then 'throw away' the private key to prevent the ciphertext from being reversed?

• Where exactly would the key be stored if it is secret? – forest Jun 30 '18 at 7:07
• @forest as long as IVs are generated and used correctly, a single 128-bit key could be stored in a HSM. But I'm not sure if the overall solution would be good, so I'm not going to write an answer for now. – A. Darwin Jun 30 '18 at 8:09
• @forest a secret key could also be stored in a file (which is blocked from being read from the web server) or as a php environment variable, as long as it is separate from the database it adds an extra layer of security. As Darwin mentioned an HSM could be used however I am not so fortunate to have one on my server. The second part of my question gets at just storing the public key and not the private key, so that the ciphertext couldn't be deciphered. So even if the public key was compromised an attacker would still have to perform the ciphering step. – FelisPhasma Jun 30 '18 at 15:56

Q1 : It won't weaken things assuming a few things. Normally storing cipher text is at worst equivalent to storing plaintext. Encryption is invertible. That means no information is lost in the process. It's not different, in that sense, from storing data in a different format. (Raw bytes, base64 encoded text, hexadecimal, etc.)

The assumptions are:

• The cipher implementation is bug-free. (No guarantee that something doesn't go wrong in some way if encryption can go wrong in some known or unknown ways.)
• The key is independent of inputs or personal data. (If a key is derived from the password or personal data, then cracking the key means that data gets leaked. Use a randomly generated key.)
• No side channels. (Implementations of an algorithm can leak plaintext or keys.)

Q2 : That defeats the purpose of encrypting hashes. If you don't have the private key then you cannot rotate keys. This also isn't going to help if people who can crack your passwords have access to the public key.

With the ciphertext, $E_K(H(\text{salt}, \text{password})$, and public key, $K$, the strategy for cracking the encrypted hashed password is about the same as the strategy for cracking hashed passwords.

Normally password cracking is done by generated candidate passwords and testing if $$H(\text{salt}, \text{password}) = H(\text{salt}, \text{candidate)}$$

$$E_K(H(\text{salt}, \text{password})) = E_K(H(\text{salt}, \text{candidate)})$$

I assumed the public key encryption method is deterministic. (Or if it includes a IV that value is known, making it deterministic.) If you cannot decrypt the data then you must have determinism because that leaves only comparing ciphertexts as a possible means of verifying passwords.

The intuition behind using public key encryption here is that it means the password cracker cannot decrypt encrypted data and that the encrypted password hashes are therefore no good to him. That's wrong because the public key (if it becomes "public" or at least known to the cracker) enables him to do, on his own computer, whatever the server does for verifying passwords.

Strong asymmetric encryption with a public key after deleting the private key will act like a one way function, just like hashing. You can compute $H(x)$ but not $H^{-1}(x)$. Similarly you can compute $E_K(x)$ with public key $K$ but not $E_k^{-1}(x)$ without $k$, the private key corresponding to the public key $K$. A password cracker does not need the ability to compute the inverse operation. It suffices to use the forward (one-way) function and compare his result to stored data.

(Also, there are scenarios where I wouldn't worry about symmetric ciphers inadvertently having side channels, but I might worry about public key side channels. If, for example, it didn't have a constant time implementation or if attacks requiring physical proximity were a concern.)