2
$\begingroup$

I'm working on this project where a client can send a message, now obviously sending it in plainText is not very smart. So I decided to Encrypt it with AES-CBC.

Now, each time I encrypt something, the KEY is derived with PBKDF2, defined in RFC-2829, as:

DERIVED = PBKDF2(salt: 16 random bytes, key: plainText pasword, iteration count = 16834)

and this is the KEY. the IV is different every time BUT it's included in the final cyphertext.

Question: is it safe to use the DERIVED key , where the ORIGINAL key is the same, but the salt is different every time?

$\endgroup$
3
$\begingroup$

Question: is it safe to use the DERIVED key, where the ORIGINAL key is the same, but the salt is different every time?

No, but the reason is a little tricky.

First, forget AES-CBC—you should use an authenticated cipher like AES-GCM (if you must use AES) or NaCl crypto_secretbox_xsalsa20poly1305, and focus on the security contract. AES-CBC is hard to use correctly and doesn't provide good security even if you do. If you really just need to send multiple messages in sequence with a single key you've negotiated, you can just use the number of messages you've sent so far as a nonce for an authenticated cipher—no need to derive keys.

Second, if users are choosing their own passwords, you should use a modern password hash like Argon2 (specifically, Argon2id), not PBKDF2, so that you can take advantage of more of the user's resources to drive up the adversary's costs—specifically, so you can take advantage of the user's memory and parallelism too. (If users aren't choosing their own passwords—if the computer is choosing passwords with (say) 256 bits of entropy—then there's no need for password hashing and you can skip that step.)


But let's say you have to use PBKDF2 for some reason, so that's the only way you can drive up the adversary's costs. Should you repeatedly use PBKDF2 with multiple salts to derive multiple keys?

The purpose of the iteration count is to drive up the adversary's costs, at the expense of also driving up the legitimate users costs. But the costs mean something slightly different to the legitimate user and the adversary:

  • The legitimate user cares about latency of key derivation: How long do I have to wait after I type in my password and hint ENTER before I get logged in?

    If I'm willing to wait (say) one second, that's one second of resources that the application can spend on PBKDF2 to drive up the adversary's costs. From the user's perspective, there's no difference between computing one key in one second vs. two keys consecutively in half a second each. But from the adversary's perspective the story is a little different.

  • The adversary cares about throughput of password guesses: How many passwords can they guess every hour on my 100-CPU cluster?

    The adversary doesn't care about deriving every key needed by the application during the password search process. The adversary is happy if they use one key in the application to confirm a password guess. If the application has to compute PBKDF2 twice for two different keys in one second, the adversary only has to spend half a second to test a password guess.

Even worse, if you ask PBKDF2 for a key that is longer than the hash output size, it is as if you invoked PBKDF2 multiple times in a row with the same iteration count. There are some applications in which this may not hurt, but it was largely a stupid design.


So how should you derive multiple keys from a single passphrase?

Let's suppose you can't just reuse the key with different nonces.

Answer: Use a key derivation function, such as HKDF with SHA-256. (Not a password-based key derivation function, also called a password hash—just a vanilla key derivation function; more on the distinction.) A key derivation function takes a master key, such as what you got from PBKDF2, and a string uniquely identifying a purpose sometimes called the ‘info’, and returns a derived subkey. Every time you feed in the same master key and info, you get back the same subkey, but without knowledge of the master key, the subkeys appear to be independent uniform random bit strings.

For example, you might do this:

  • Master key: $k = \operatorname{PBKDF2-HMAC-SHA256}(\mathit{salt}, 16384, \mathit{pw})$
  • File encryption key: $k_f = \operatorname{HKDF-Expand}_k(\text{‘file encryption’})$.
  • Backup encryption key: $k_b = \operatorname{HKDF-Expand}_k(\text{‘backup encryption key’})$.
  • etc.

(Note: HKDF has two steps—HKDF-Extract and HKDF-Expand. HKDF-Extract is not necessary here: its purpose is to map a high-entropy but nonuniform bit string, like a diceware passphrase, into a short uniform bit string, but PBKDF2 already does that for us. HKDF-Expand takes a short uniform bit string and an info parameter, and derives a subkey. If you only have a combined HKDF-Extract/Expand module available to you, it doesn't hurt to do HKDF-Extract on the output of PBKDF2, but it doesn't do anything useful either.)


Addendum: What about the salt?

The purpose of the salt is to mitigate multi-target attacks: if everyone used the same salt, then breaking the first of $t$ targets would be cheaper than breaking one target by a factor of $t$, because there are ways an adversary can save effort by attacking many targets at once in a batch. Adversaries like batch advantages like this because once you have a foot in the door of a network you can often exploit it to compromise more of the network. But if everyone uses a distinct salt, that factor of $t$ batch advantage evaporates.

$\endgroup$
2
$\begingroup$

For key derivation use PBKDF2, Bcrypt, Scrypt, or better use Argon2id. Argon was the winner of the Password Hashing Competition

question: is it safe to use the DERIVED key , where the ORIGINAL key is the same, but the salt is different every time?

If you use a different randomly generated salt, you will be fine. You can also append a counter to the password for each key generation. Remember that the password must be strong enough against the dictionary attacks. No Key derivation prevents you from a badly chosen password.

Don't use CBC mode. It is an archaic mode of operation and has it's on problems, for example, the IV must be unpredictable and you may have the bit flipping attack. The padding oracle attack may not be related to your case.

In modern Cryptography, we use Authenticated Encryption (AE) modes like AES-GCM or ChaCha20-Poly1305. With AE, you will not only Confidentiality but also Integrity and authentication.

You also did not consider how to send the key or the key agreement. You can use Ephemeral DHKE, RSA-KEM which requires public keys and may hard to implement correctly. You might use existing libraries like Crypto_box which uses public-key cryptography. You might also interested in the Signal Protocole.

$\endgroup$
  • $\begingroup$ Of course, AES-GCM and ChaCha-Poly1305 has their problems, too. $\endgroup$ – kelalaka Oct 15 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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