8
$\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$

3 Answers 3

16
$\begingroup$

The library also 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, 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 to 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 remembering 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 was a recent question about Is it dangerous to encrypt lots of small files with the same key?. One can use a 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 parts of the new and old versions of the file may lose confidentiality. Therefore, for every update use a new nonce and encrypt the file again.

$\endgroup$
8
  • 2
    $\begingroup$ We call it nonce-misuse. If occurs the confidentiality occurs. - What exactly do you mean by that? $\endgroup$
    – MechMK1
    Oct 12, 2020 at 9:00
  • $\begingroup$ @MechMK1 ups. It is ... confidentiality is lost. Thanks. Corrected. $\endgroup$
    – kelalaka
    Oct 12, 2020 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$ Oct 12, 2020 at 15:35
  • $\begingroup$ @FrankDenis, true. Comment removed and replaced, thanks. $\endgroup$
    – Jason
    Oct 12, 2020 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, 2020 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.)

$\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.

$\endgroup$
1
  • $\begingroup$ Then it wouldn't be client-side anymore... $\endgroup$ Oct 13, 2020 at 3:07

Your Answer

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

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