Besides the IV, ChaCha20 takes a random number and a counter as input.

I couldn't find more precise information about this but it seems to be that the counter is just a safety measure in case of a bad the PRNG.

If the user has a good PRNG, would it be safe to use another random number instead of a counter in ChaCha20?

In some applications, such as P2P networks with a flooding protocol, this can be useful so I would not have to keep all these counters in memory.

  • 1
    $\begingroup$ Note that this introduces a single point of failure - if your PRNG ever fails to initialize, or isn't actually sufficiently random, you'll lose the security guarantees. $\endgroup$
    – TLW
    Jun 27, 2018 at 3:00

3 Answers 3


I am using here the description and terminology from RFC 7539.

ChaCha20 is meant to process messages, each message being a sequence of bytes; ChaCha20 produces pseudorandom blocks of 64 bytes each, which are XORed with the data to encrypted or decrypt. The crucial security property is that all invocations of the ChaCha20 block function for a given key use distinct values for what is injected in state words 12 to 15.

In the description of RFC 7539, state word 12 receives a counter value, which is over 32 bits and incremented for each block, while state words 13 to 15 receive a 96-bit "nonce" which is fixed for a given message, but different for each message. The idea is that, for a given key:

  • Each message works over a new nonce value (96 bits), such that no two messages share the same nonce.
  • Within a message, different blocks use the same nonce but a different counter values.
  • No message may contain more than $2^{32}$ blocks (i.e. close to 275 gigabytes, which "ought to be enough").

Nonce values themselves can be generated in any way that offers the unicity guarantee with sufficient probability. If you generate nonce values randomly (with a uniform generator), then the risk of reuse is low as long as you do not produce too many messages; with $n$ messages, probability of reuse is about $n^2/2^{96}$. In other words, if you do not send more than 8 billion messages with random 96-bit nonces and the same key, then the risk of reusing the same nonce is less than one in a billion.

Take care that all of the above is for reusing a nonce value with the same key. If two messages use the same nonce+counter but with different keys, then there is no security issue.

In a P2P network, conceptually, messages are encrypted with keys derived from some sort of key exchange (e.g. ECDH), and each node must remember the key to use for each of its neighbours; it should not be too much a hassle to remember a counter value along with each such key, in particular if these keys are ephemeral, i.e. only in RAM.


Besides the IV, ChaCha20 takes a random number and a counter as input.

No it doesn't (sec. 2.4):

The inputs to ChaCha20 are:

  • A 256-bit key
  • A 32-bit initial counter. This can be set to any number, but will usually be zero or one. It makes sense to use one if we use the zero block for something else, such as generating a one-time authenticator key as part of an AEAD algorithm.
  • A 96-bit nonce. In some protocols, this is known as the Initialization Vector.
  • An arbitrary-length plaintext

There is a nonce/IV input, there is a counter input, but there is no additional "random number" input.

The counter is absolutely essential to the algorithm, too. ChaCha20's design is a variant of CTR mode—it generates a key stream by applying a secret-keyed scrambling function to a nonce + counter pair.

Note that the term "counter" in this context can be confusing, because the nonce/IV input is often also referred to as a "counter," but it's not the same input. You're supposed to:

  1. Choose a 256-bit key at random (or through a mechanism with strong guarantees like a key exchange) and keep it secret.
  2. For a given choice of key, never repeat the same nonce for two different encryptions. Very often this is accomplished by using a counter to supply values for the nonce.

The "counter" argument in the above text refers to the initial value internal block counter within any one encryption operation. This is a value that is often not exposed as an argument at all, and just set to a constant one or zero value. The algorithm internally takes care of incrementing this for each keystream block that it generates to encrypt your message. You have to be doing something somewhat advanced to justify messing with the initial value of the block counter.


Being able to provide a counter value is in no way meant to be a safety measure against spoiled random numbers, but specifying a different initial value is useful for key derivation in AEAD constructions.

In ChaCha20-Poly1305 the cipher (ChaCha20) is first called with an initial counter value of zero to generate a key for the authenticator (Poly1305). When the message is encrypted, the initial counter value is set to one, making the encryption independent from the key derivation. The counter is then only incremented internally by the cipher for each 64 byte block of the message (internal state size).

Using non-deterministic random numbers instead of the counter wouldn't make sense, as you wouldn't be able to decrypt the message later. And using a deterministic random number generator would effectively be the same as using a counter.

Regarding the nonce, RFC 7539 explicitly disallows the usage of random numbers, simply because the probability for a collision is too high.

  • $\begingroup$ A warm welcome to crypto, dhe25519 :) $\endgroup$
    – Maarten Bodewes
    Jun 28, 2018 at 10:33

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.