4
$\begingroup$

I am going to store secrets from users. The secrets need to be stored confidentially. For this question, I am concerned only with confidentiality (not integrity).

Here is how my encryption scheme is:

  1. We start with a 20-character randomly generated alphanumeric password.
  2. A new 128-bit salt is generated randomly for each user secret to be stored.
  3. The 20-character password and the 128-bit salt is passed through a HMAC-SHA256-based PBKDF2 function to derive a new 256-bit encryption key for each user secret to be saved.
  4. A new 128-bit initialization vector is generated for each user secret to be saved.
  5. The 256-bit encryption key and the 128-bit initialization vector is used to perform AES-256-CBC encryption of the secret and arrive at the ciphertext.
  6. The salt, initialization vector, and the resulting ciphertext are all saved together.

I want to know if in steps 2 and 3, is it necessary to run the password through PBKDF2 every time for every new secret to be protected?

Or can we just run the password through PBKDF2 only once and use that key for all secrets to be protected?

Is there any provable benefit of generating a different key (with PBKDF2) for each user secret even if we start with the same alphanumeric password every time?

$\endgroup$
21
  • 1
    $\begingroup$ @forest The server side application also needs to be able to read the users' secrets. So if we encrypt the users' secrets using users' passwords, then the server would need to keep the users' passwords encrypted (not hashed). Encrypting users' passwords so that the server can read it would take us back to the same problem we started with: encrypting users' secrets. $\endgroup$ Sep 2, 2019 at 6:31
  • 1
    $\begingroup$ @tylo Unless the server needs to be able to process any user's data regardless of whether or not they are logged in, which is my assumption. $\endgroup$
    – forest
    Sep 2, 2019 at 6:46
  • 1
    $\begingroup$ Just as a side-note, it might be time to phase out PBKDF2 and instead use Argon2id, especially on newer systems, though that topic might warrant another question. $\endgroup$
    – jaaq
    Sep 2, 2019 at 12:32
  • 1
    $\begingroup$ I think, your question would be better if you asked about what you want to achieve instead of proposing your solution - there seems to be a XY problem here. I would not suggest any solution, where the server needs the users password directly, because that is almost just (as bad as) like storing the password in plain. And maybe the question would be a better fit for InfoSec SE. Also, use Argon2 instead of PBKDF2 if you can, or at least scrypt. $\endgroup$
    – tylo
    Sep 2, 2019 at 15:26
  • 1
    $\begingroup$ @tylo It is just secret data being stored, not passwords. Please do not change the scope of the question while you discuss this in comments. Do you know anyway to protect secret data that is being read by an application that is immune to the server being hacked? You can have the master key in an HSM but at some point the application is going to hold the key in its memory to perform decryption and an attacker who has hacked the server can read that key from memory dump of the process. You can reduce the window to retrieve that key from memory dump by zeroing out the memory but it's still there. $\endgroup$ Sep 3, 2019 at 5:07

3 Answers 3

13
$\begingroup$

Assume you have an IND-CCA secure cryptosystem $E$ that runs a password through a slow KDF and implicitly handles salts and random IVs, a human-chosen password $p$, and messages $m_1$ through $m_n$ to encrypt. Is $E_p(m_1+m_2+\cdots+m_n)$ or $E_p(m_1)+E_p(m_2)+\cdots+E_p(m_n)$ better for this? Each invocation of $E$ is slow due to it running a KDF on $p$, but an attacker only needs to guess $p$ once. This means that both techniques are equally secure against an attacker assuming $E$ takes a constant amount of time, but the former is significantly faster for honest parties, so the former is better.

The purpose of a slow KDF is to make brute force of a weak, human-generated password difficult. You need to run it each time a password is input. Assuming only one password is being input, then it only has to be run through a KDF once. The output key can be used to protect as many secrets as you wish.

The reason is simple. If an attacker manages to guess the original password, they'll be able to use it again and again to decrypt all the data, whether or not the data was encrypted all at once or encrypted piece by piece by running the password through a KDF multiple times to generate multiple keys.

$\endgroup$
5
  • 1
    $\begingroup$ It's true that a system using one password-derived key is no easier to brute force than one with multiple keys. But only deriving key bits once each time the user enters their password is actually better than multiple (redundant) derivations. A password is only as difficult to brute force as the most easily brute forced hash or derived key. So it is a better use of your resources derive one key that needs 1 second to hash than it is use the same amount of resources to derive 100 keys, each key needing only 10 ms to derive from a password. $\endgroup$ Sep 2, 2019 at 1:47
  • $\begingroup$ @FutureSecurity Indeed. I was assuming each KDF would be just as slow, so it would be 1 second to decrypt everything, or 1 second for each piece of data that needs to be decrypted. Both are technically equally secure, but the former is far faster for the same level of security. $\endgroup$
    – forest
    Sep 2, 2019 at 1:50
  • $\begingroup$ Oh, no... I missed the comment where the OP said that there was only one password shared between every user. I would not have added my previous comment had I noticed. It sounds like the OP may be confused with regards to some fundamental cryptography concepts and I worry that the extra nuance could only add to the confusion. $\endgroup$ Sep 2, 2019 at 2:41
  • $\begingroup$ @FutureSecurity What do you think I am confused with? Can you be more specific? $\endgroup$ Sep 2, 2019 at 6:33
  • $\begingroup$ @LoneLearner In the chat, the possibility was raised that you were trying to store per-user secrets with a master key when it would have been possible to store them with a per-user key. If that's not possible, then that alleviates the concern. The problem was just that, the way you described it could have easily meant that you were trying to store user passwords encrypted with a master password (instead of hashing them). $\endgroup$
    – forest
    Sep 2, 2019 at 6:34
2
$\begingroup$

An important requirement for reusing the key is that there should be a random IV (CBC) or nonce used for each plaintext to protect. Otherwise the security the cipher is breached as the result is deterministic. It can even be fatal if CTR mode - or one of the many AEAD schemes that use CTR mode - is used for encryption.

Many implementations of PBKDF both derive the key and IV from the PBKDF function, so they both rely on the same salt. Reusing the key is fine as long as you are OK with keeping the key in memory. Reusing the IV for the same key is certainly not. Using a random IV is strongly suggested.

Depending on the cipher used you may want to think of a smart scheme to make sure that the IV is at least always unique; you may want to limit the number of IV's generated and then use a different salt, for instance. This is especially important if a smaller IV size is used (when using GCM mode, for instance).

$\endgroup$
0
$\begingroup$

Briefly: Single PBKDF2 hash per user is sufficient.

PBKDF2 is designed to be slow and thus resistant to brute force atacks. That's why it has no much benefits if you use many different PBKDF2 hashes for the same user. On the other hand, if you use different PBKDF2 hashes for decryption of many encrypted data pieces, you will have to compute PBKDF2 many times and thus your performance will suffer from the same problem as attacker would suffer.

$\endgroup$
2
  • 1
    $\begingroup$ Actually the attacker will suffer less than the defender if the same password is hashed many times. The defender needs to derive many keys, but the attacker only needs to test one key for each potential password to determine whether they have guessed the password correctly. $\endgroup$ Sep 2, 2019 at 2:51
  • $\begingroup$ Correct. But the question was if PBKDF2 should be executed each time or a single call is sufficient. $\endgroup$
    – mentallurg
    Sep 2, 2019 at 22:09

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.