2
$\begingroup$

I have an (offline) shared secret key (symmetric) which is used to encrypt/decrypt data.

Now this data belongs to a specific user which is known on the backend and frontend. My idea was to use the username/id as salt. But for AES we only have the iv.

Doing this would make sure that the data on the frontend can only be encrypted if the correct user is logged in, because without the correct salt/iv. The data would be nonsense.

But this iv would not be random anymore.

So do you have a suggestion for me on how to deal with the problem? Also, the secret key and the username/id is the only thing both endpoints know.

$\endgroup$
1
$\begingroup$

You did not mention the mode of operation for any block cipher that is necessary.

  • CBC mode requires the IV random and unpredictable so this is not your way.

  • CTR mode requires an $(IV,Key)$ pair never occurs more than once. One must never use the same $(IV,key)$ pair again, otherwise, confidentiality is lost.

    CTR mode generally uses 96-bit nonce and 32-bit counter to form the Initial value. If the nonce reoccurs and counters repeat then these blocks can be revealed with crib-dragging. The nonce can be chosen randomly that will have a 50% probability of collision after $2^{48}$ random nonce generation. Counter/LFSR-based nonce generation is also recommended by NIST.

CTR mode can be a solution for you.

The above modes don't provide you more than confidentiality. We can, and we should go more;

  • GCM provides you confidentiality, integrity, and authentication. Since GCM uses the CTR mode for encryption, you will have the same (IV, key) reuse problem.

    If the IV has 92-bit length then $J_0 =IV || 0^{31} ||1$ is passed to the CTR mode as the IV where the 32 bits is used as a counter which starts form $1$. SO if you reuse the same IV under the same key, you will have the same problem in the CTR mode.

  • XChaCha20-poly1305 is ChaCha20 with an extended nonce that we can use random nonce more securely. It has 192-bit nonce sizes and one has a collision with 50% probability once generates $2^{96}$ random nonces under the same key. Though this has the same $(IV,key)$ problem the probability of getting it is not-going-to-happen.

  • There is also AES-GCM-SIV mode. This is nonce-misuse resistant and even the IVs are the same the mode will only leak that the messages are the same. This requires additional passes over the data.

Therefore, one can use AES-GCM-SIV to mitigate all possible problems or use xChaCha20-poly1305.

For the user, you should note that the IV/nonce of the encryption is never meant to be secret. They can be stored, usually prepended, with the ciphertext. During the client-side encryption, they can be created. Therefore the user doesn't need to know anything about the IV/nonce after the encryption, the server can store them with the ciphertext. The user only needs the secret key and must keep it secret all the time.

$\endgroup$
17
  • $\begingroup$ Okay, so username obviously can be the same, but so does data. So it might happen that data and username combination is the same but using AES-GCM-SIV this would not be a problem? Also if I use a random IV and prepend it to the cipher text in CTR mode it would be perfectly safe? I did not know that I can just send the IV in plain text. So not using the username it all would be the best solution? $\endgroup$
    – Shurpion
    May 3 at 10:44
  • $\begingroup$ AES-GCM-SIV only leaks that the encrypted data are the same even you use the same $(IV,key)$ pair. The key is different per user, therefore it is the problem of the same user. Nothing is perfectly safe, CTR. In the CTR mode just make sure that the IV never accurs with the same key $\endgroup$
    – kelalaka
    May 3 at 10:55
  • $\begingroup$ Okay, will read on this one. So I think I just go with random IV and prepend it to the data and then use CBC mode. Looks like the simplest solution. Follow up question: Does it make sense to include username in the secret, like exchanging first x bytes of secret with username? Does this provide any additional benefits or worse leaks security? $\endgroup$
    – Shurpion
    May 3 at 10:59
  • $\begingroup$ Use XChaCha20-Poly1305 ( or only XChaCha20 if you only need encryption). CBC mode IV has a higher chance of collision than XChaCha20. Include the user name in the secret? Do you mean encrypt? AES-CTR, CBC, GCM, ChaCha ara all CPA secure, no problem there. $\endgroup$
    – kelalaka
    May 3 at 11:03
  • $\begingroup$ Okay. Thank you that helped. No I meant really include it in the secret with. Like if the secret would ABCDEF1234567890... Does it make sense to encrypt data for user1 with the secret user11234567890...and data for user2 with user21234567890. So different secrets per user but where part of the secret is not random anymore? $\endgroup$
    – Shurpion
    May 3 at 11:08
0
$\begingroup$

The IV is normally stored with the ciphertext. It is not derived from some other secret. To decrypt some encrypted data, all you need to know is the key and the encrypted data.

because without the correct salt/iv. The data would be nonsense.

I'm not sure what you're trying to say. If you're thinking “if someone has the key but not the IV, then they can't decrypt the data”, this is not necessarily true. It depends on the encryption mode and on the partial knowledge that the adversary has about the data. If you want to protect against decryption, the key must be secret.

make sure that the data on the frontend can only be encrypted if the correct user is logged in

To do this, arrange for the encryption key to be “unlocked” when the user logs in. How to do this depends on how users log in. Remember to take into account both what happens on a normal login, what happens on a resumed session (if that's a thing), and what happens for account recovery (e.g. lost password), as well as what happens when the user's authentication method changes (password change).

If users authenticate with a password, here's a common design. Use the password as input to a key stretching function to calculate a user master key $K_m$. A key stretching function is essentially the same as a password hashing function which is used to check that the user provided the expected password: it applies a slow transformation to the password, to reduce the rate at which the output can be guessed by brute force. For password hashing, the expected output is stored in the user database. For key stretching, the output is not stored anywhere, and is used as a key.

To encrypt some data associated with the user, generate a random key $K_d$ for your data encryption method, and encrypt the data with that key. If $K_d$ is used to encrypt more than once, use a random IV/nonce; if you generate a unique $K_d$ each time you encrypt some data, the IV/nonce can be fixed (e.g. always 0). Either way, you'll need to recover $K_d$ to decrypt the data later, so store a copy of $K_d$ encrypted with $K_m$. The next time the user logs in, you'll calculat the same $K_m$ again from their password, which will allow you to decrypt the encrypted copy of $K_m$, which will allow you to decrypt the data.

When the user changes their password, $K_m$ will change. Decrypt the saved encrypted copy of $K_d$ with the old $K_m$ and write a copy of $K_m$ encrypted with the new $K_m$. Don't forget to securely wipe the old encrypted copy, since it would be a vulnerability if the user's old password became known (and that may be a reason why the user is changing their password).

If the user can authenticate by some method other than their password, you may need to keep other copies of $K_m$ “locked” by the other authentication method. For example, if you have a lost password procedure, you'll need to make a choice between telling the user that all their encrypted data is lost, or having a way to recover $K_d$. One possible way to offer a way to recover $K_d$ is to generate a random key $K_r$, save a copy of $K_d$ encrypted with $K_r$, and provide the user with a “recovery code” (a long string that they should print and save securely) which allows you to recover $K_r$ (you would not store $K_r$ itself).

Coming back to the IV for each piece of encrypted data, as I've explained above, it isn't secret. So you just store it with the rest of the ciphertext.

As for choosing an encryption mode, the best choice is Libsodium's crypto_secretbox or a similar API. Let a cryptographer decide what algorithm is best. If you need to know the mode, for example because you need a ciphertext format that is interoperable between multiple pieces of software, ChaCha20-Poly1305 and AES-GCM are two popular choices. SIV variants are better if your library provides them, but they're still relatively new and not available everywhere. Avoid unauthenticated modes like CBC and CTR: they're older and harder to use, and the lack of authentication can open you to subtle attacks.

$\endgroup$
1
  • $\begingroup$ Thank you for all this insight. I will take this into account. Also thanks for providing the info on crypto_secretbox. I cannot upvote, because I only posted as guest. But you have my thanks $\endgroup$
    – Shurpion
    May 6 at 9:25

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.