7
$\begingroup$

I am using the following chacha20poly1305 Rust library to encrypt some data in a desktop application. The user provides the key, which never leaves their device, to locally encrypt some data, and then the encrypted data is sent to a server for backup purposes.

This library requires a nonce in order to encrypt the data, and I am not sure what this value should be. Ideally, the user could recover this encrypted data with their key alone. However, in order to decrypt any data, the nonce is also required. Hard-coding some nonce in the application feels odd, but if I randomly generate a nonce, the user will be required to remember this nonce along with their provided key in order to recover the encrypted data.

$\endgroup$
14
$\begingroup$

The library uses XChaCha20Poly1305 and that requires a nonce of 192-bit (24-byte). It is an extension of ChaCha20Poly1305 to increase the nonce size, ChaCha20 had 96-bit nonces. There is no standard for it, only a draft in ietf.org

The nonce is an acronym for 'number used once'. The crucial point is that one must never use the (Key, nonce) pair again. We call it nonce-misuse. If it occurs, the confidentiality is lost as the attacker can use the crib-dragging technique to reveal the two plaintexts. There is even an automated approach to solve it:

Or for detailed examples see the answer of this question;

For 192-bit nonces, random nonce generation is safe, since as per birthday paradox you would need to encrypt $2^{96}$ messages under the same key to hit the same nonce again with 50% probability, so you should encrypt way less messages to ensure that this probability is negligible. Keep in mind that you need to use a good random number generator like /dev/urandom. You can still use counter-based nonces if you want.

I am not sure what this value should be. Ideally, the user could recover this encrypted data with their key alone.

The nonce does not need to be secret and can be saved together with the file without being encrypted. This is not insecurity, only the key is secret. The nonce helps to use a key securely for more than once. It is secure as long as a (key, nonce) pair never occurs twice.

However, in order to decrypt any data, the nonce is also required.

That is completely normal since the decryption is the reverse of the encryption with the same key and nonce. XChaCha20Poly1305 generates a stream and we use it to x-or the plaintext. In order to regenerate the stream we need to use the same inputs.

Hard-coding some nonce in the application feels odd, but if I randomly generate a nonce, the user will be required to remember this nonce along with their provided key in order to recover the encrypted data.

Hard-coding a nonce is not acceptable as then it would be used more than once. There is no problem with rememebering the nonce, it is secure. Save it in a file like $$nonce\mathbin\|encryptedFile\|tag.$$

Note 1: The XChaCha20Poly1305 is an Authenticated Encryption, that will provide you confidentiality, integrity, and authentication if correctly used. Incorrect tags must not be ignored during the decryption. If the tag doesn't match, throw an error.

  • Encryption

    $$(c,tag)= XChaCha20Poly1305Enc(key,nonce,message)$$

  • Decryption

    $$(m|\perp) = XChaCha20Poly1305Dec(key,nonce,c)$$

    where $\perp$ (\perp) is the halt if the tag mismatch.

Note 2: There were a recent question about Is it dangerous to encrypt lots of small files with the same key?. One can use the similar idea to generate different keys for each file, too.

A final note: if you encrypt and update data multiple times with the same key and nonce this also can cause nonce-misuse and some part of the new and old version of the file may lose confidentiality. Therefore, for every update use a new nonce and encrypt the file again.

| improve this answer | |
$\endgroup$
  • 2
    $\begingroup$ We call it nonce-misuse. If occurs the confidentiality occurs. - What exactly do you mean by that? $\endgroup$ – MechMK1 Oct 12 at 9:00
  • $\begingroup$ @MechMK1 ups. It is ... confidentiality is lost. Thanks. Corrected. $\endgroup$ – kelalaka Oct 12 at 9:39
  • $\begingroup$ @Jason It is not required, but perfectly fine to encrypt a nonce. Nonces can sometimes carry sensitive data. In particular, a counter used for nonces discloses how many messages have been previously exchanged. $\endgroup$ – Frank Denis Oct 12 at 15:35
  • $\begingroup$ @FrankDenis, true. Comment removed and replaced, thanks. $\endgroup$ – Jason Oct 12 at 18:46
  • 1
    $\begingroup$ For CBC mode the nonce needs to be unpredictable, thats it. For any schemes those uses CTR mode the (key,IV) pair never be used more than once. If there is a need for encryption of the nonce there there is a problem with the encryption scheme. $\endgroup$ – kelalaka Oct 12 at 19:24
3
$\begingroup$

You should generate a random* nonce and store it alongside the ciphertext, e.g. prepended to it.

Both ChaCha20 and Poly1305 require a unique nonce to be used for each encryption, otherwise they will not be secure. However, the nonce does not need to be kept secret, so you can just include it with the ciphertext.


*) Technically, the nonce for ChaCha20–Poly1305 only needs to be unique, not random, but making the nonce random and sufficiently long is generally the easiest way to ensure uniqueness in a distributed system. Using random 96-bit nonces, generated with a cryptographically secure RNG, will allow you to fairly safely encrypt up to 232 messages per key while having at most a one-in-232 risk of a nonce collision.

If you don't 100% trust your RNG (which could be a sensible precaution, given the various notable operating system RNG bugs in the past), a possible "belt and suspenders" approach could be to concatenate the RNG output (of at least 96 bits) with a high-resolution timestamp, a user ID (if you have one) and any other distinguishing input you can think of, feed them all into a cryptographic hash function like SHA-256, truncate the result down to 96 bits and use that as your nonce. That way, as long as the hash inputs for two encryptions don't all match, the outputs will almost certainly be different.

(In principle, you could even include the encryption key and the plaintext message as additional hash inputs for nonce derivation. At that point you're well on your way to reinventing SIV, though.)

| improve this answer | |
$\endgroup$
0
$\begingroup$

Another option would be to store the nonce is the server side with the encrypted data or in external service. This way you don't need the user to keep it or even to be aware of it and your application should know how to fetch it for decryption when needed.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Then it wouldn't be client-side anymore... $\endgroup$ – forest Oct 13 at 3:07

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.