Glossary

ChaCha20

ChaCha20 is a stream cipher by Daniel Bernstein that turns a 256-bit key, a 96-bit nonce and a 32-bit counter into a keystream XORed with your data. RFC 8439 specifies it together with Poly1305 as the ChaCha20-Poly1305 authenticated cipher. In TLS 1.3 it appears as TLS_CHACHA20_POLY1305_SHA256, which RFC 8446 says implementations should support. It is a refinement of Salsa20.

How it works

ChaCha20 builds a 4 by 4 grid of 32-bit words, called the state, and scrambles a copy of it to make one 64-byte keystream block. The layout from RFC 8439 is:

  • Words 0 to 3: the constants 0x61707865, 0x3320646e, 0x79622d32 and 0x6b206574, which spell "expand 32-byte k" in ASCII.
  • Words 4 to 11: the 256-bit key, read as eight little-endian words.
  • Word 12: the 32-bit block counter.
  • Words 13 to 15: the 96-bit nonce.

The scrambling uses only addition modulo 2^32, XOR and bit rotations by 16, 12, 8 and 7 positions, applied through the quarter round. Twenty rounds run as ten pairs, alternating column and diagonal quarter rounds. The result is added back to the starting state and serialized little-endian. The cipher then XORs the keystream with the plaintext, so encryption and decryption are the same operation, and the last block may be partial.

Because it uses no lookup tables, ChaCha20 runs in constant time in plain software, which is why it is common on phones and chips without AES instructions.

The example runs the RFC 8439 section 2.4.2 test vector with Python. The library takes a 16-byte value made of the 4-byte little-endian counter followed by the 12-byte nonce.

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
key = bytes(range(32))
nonce = bytes.fromhex("000000000000004a00000000")
pt = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
iv = (1).to_bytes(4, "little") + nonce
ct = Cipher(algorithms.ChaCha20(key, iv), mode=None).encryptor().update(pt)
print(len(pt), len(ct))
print(ct[:16].hex())
114 114
6e2e359a2568f98041ba0728dd0d6981

The first 16 bytes equal the RFC value 6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81. Ciphertext length always equals plaintext length.

What are the ChaCha20 key and nonce sizes?

RFC 8439 ChaCha20 uses a 32-byte key and a 12-byte nonce, and each message can hold at most 2^32 blocks of 64 bytes, which is 256 GiB (about 275 GB). The original design by Bernstein used a 64-bit nonce and a 64-bit counter, which is why a few libraries still show 8-byte nonces. Python's cryptography package rejects a bare 12-byte nonce with "nonce must be 128-bits (16 bytes)" and rejects a 16-byte key to ChaCha20Poly1305 with "ChaCha20Poly1305 key must be 32 bytes."

Does ChaCha20 authenticate data?

No. ChaCha20 alone only hides data, and a flipped ciphertext bit flips the same plaintext bit. Pair it with Poly1305, as RFC 8439 does, so the receiver rejects tampered messages. ChaCha20Poly1305 in Python returns the ciphertext plus a 16-byte tag, so "hello" encrypted to 21 bytes in the test run.

Common pitfalls

  • Reusing a key and nonce pair: the same keystream encrypts two messages, and XORing the ciphertexts gives the XOR of the plaintexts. Two runs with attack at dawn and retreat at six both produced 13110013060a5441155444121e16.
  • Using random 96-bit nonces at high volume: RFC 8439 says the nonce must be unique per key. A message counter is safer than random bytes.
  • Counting the counter wrongly: the IETF variant starts the encryption counter at 1, because block 0 is used to make the Poly1305 key in the AEAD construction.
  • Skipping the tag: raw ChaCha20 gives tampering a free pass. Use the AEAD form.
  • Mixing variants: a 64-bit nonce library and a 96-bit nonce library produce different output for the same bytes.

Related terms

  • Poly1305 — the one-time authenticator paired with ChaCha20.
  • AES — the block cipher that ChaCha20-Poly1305 is the usual alternative to.
  • Salsa20 — the earlier Bernstein cipher ChaCha20 refines.
  • TLS — carries ChaCha20-Poly1305 as a cipher suite.
  • HKDF — derives per-session keys that ChaCha20 then uses.

See also