Score:0

Nonces in chacha20poly1305 vs chacha20

cn flag

I'm currently working on replacing the chacha20 encryption in my app with chacha20poly1305, but I'm running into a few questions that I can't seem to find clear answers to, mainly stemming from the Rust chacha20poly1305 crate:

  • Why does the chacha20poly1305 crate require a nonce for every message, but chacha20 only requires a single nonce when initializing the cipher? Why does this not seem to be the case for other libraries, such as Python's PyCryptodome?
  • Does the ChaCha20Poly1305 algorithm require a new nonce for every message?
  • I was under the understanding that the ChaCha20 algorithm has an internal counter which it combines with the key and nonce to generate blocks of the keystream (one block per increment of the counter). Is this not the case when it is used in ChaCha20Poly1305?

Finally, if ChaCha20Poly1305 (or in my case XChaCha20Poly1305) does need a new nonce for every message, would a 4-byte per-message counter combined with a 20-byte random per-session nonce be suitable to use as the per-message nonce? (the key is password-derived and so likely to repeat between sessions, and somehow storing a universal counter across all sessions is not feasible.)

Thanks!

kelalaka avatar
in flag
Every stream cipher must use the (nonce,key) pair only once. One cannot responsible for the bad choices or your misreadings about this.
kelalaka avatar
in flag
I fear that this question is more into security analysis of the Rust implementations. `initializing the cipher?` is a library choice. A good library must handle all seamlessly and give power to user via options. For example, one can generate random IV's if not supplied or take one from the user, etc..
Keegan Conlee avatar
cn flag
@kelalaka How about this then: would it be accurate to say that in a ChaCha20Poly1305 cipher, the internal ChaCha20 cipher's counter is reset for every message? This would explain needing a unique nonce per message, as opposed to plain ChaCha20 which begins the keystream for each new message wherever the last message left off.
kelalaka avatar
in flag
Usually when you init the cipher it does, maybe you forgot to init in ChaCha20?
Keegan Conlee avatar
cn flag
@kelalaka it does what? My understanding after finally finding some more documentation is that, at least for the libraries I'm using, when you init a ChaCha20 cipher, you give the key and the nonce. The cipher object will hold state (the internal counter), incrementing the counter as needed. Whereas when you init a ChaCha20Poly1305 cipher, you only give it the key as it holds no state other than that, restarting the internal counter (and the keystream) for every new message you want to encrypt, and thus requiring a new initialization vector (which is a new nonce with the same key).
SAI Peregrinus avatar
si flag
Different library APIs. Both ChaCha20 and ChaCha20-Poly1305 need a new nonce for every message to provide any security. Some libraries will internally generate new nonces for you from a single provided input nonce, others won't. Generally crates implementing the `cipher` traits are lower-level than those implementing the `aead` traits.
SAI Peregrinus avatar
si flag
Also the chacha20poly1305 crate allows you to use xChaCha20-Poly1305. That allows for a random nonce, if you use the full 192 bits of nonce space. That way you don't have to store anything between messages. The message counter is handled internally.
Keegan Conlee avatar
cn flag
@SAIPeregrinus it doesn't seem to hold a message counter, though, hence this post. It requires that you pass a nonce with every `encrypt()` call, and doesn't accept a nonce when initializing a new `XChaCha20Poly1305` object. Unless I'm just entirely missing a large chunk of its API.
Keegan Conlee avatar
cn flag
I had a look at the source code for the `chacha20poly1305` crate and it literally does just initialize a completely new cipher for every encrypt() call: https://docs.rs/chacha20poly1305/latest/src/chacha20poly1305/lib.rs.html#271 I can only assume this means I need to keep track of my own message counter.
SAI Peregrinus avatar
si flag
encrypt() is called once per message, with a new nonce every time. The counter in ChaCha is not a message counter, it's an internal counter that's incremented for every 512 bits of the message.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.