Skip to main content
Authenticated Encryption (AEAD) Intermediate

ChaCha20-Poly1305: The Mobile-Friendly AEAD

TLS 1.3's second mandatory cipher suite pairs ChaCha20 encryption with the Poly1305 message authenticator, delivering AES-GCM-equivalent security without needing any special hardware. Here's how the pairing works.

PL
Pashalis Laoutaris
August 4, 2026
5 min read

Interactive ChaCha20-Poly1305 Visualizer

🔐 ChaCha20-Poly1305 AEAD

Defaults reproduce a known Node.js chacha20-poly1305 reference case.
Enter text and click a button to start!
One-Time Poly1305 Key (from ChaCha20 counter=0)
Ciphertext (ChaCha20 counter=1+)
Poly1305 Tag
Verification
Set inputs and click Encrypt & Authenticate.

ChaCha20-Poly1305: The Mobile-Friendly AEAD

Introduction

Just as AES-GCM pairs AES encryption with GHASH authentication, ChaCha20-Poly1305 pairs ChaCha20 encryption with a different, purpose-built authenticator called Poly1305 — also designed by Daniel J. Bernstein. Standardized in RFC 8439, this combination is one of exactly two AEAD cipher suites mandated by TLS 1.3 (the other being AES-GCM), and it’s the backbone of the WireGuard VPN protocol. Its defining advantage: excellent, consistent performance in pure software, with no dependency on the specialized AES-NI or PCLMULQDQ hardware instructions that AES-GCM leans on.

Table of Contents

Why Pair ChaCha20 with Poly1305?

ChaCha20 alone only provides confidentiality. Poly1305 is a fast, information-theoretically-grounded message authentication code (MAC) — not a general-purpose hash function, but a purpose-built one-time authenticator, designed specifically to pair efficiently with a stream cipher like ChaCha20. Together they form a complete AEAD construction: ChaCha20 hides the data, Poly1305 proves nobody tampered with it.

Deriving the One-Time Poly1305 Key

Poly1305 requires a fresh, unique 32-byte key for every single message — reusing a Poly1305 key across two different messages breaks its security guarantees entirely. Rather than requiring protocols to manage a second secret key, the ChaCha20-Poly1305 construction elegantly derives it from the same key and nonce already in use: it runs the ChaCha20 block function once, with the block counter fixed at 0, and uses the first 32 bytes of that keystream block as the one-time Poly1305 key. The actual message encryption then proceeds with ChaCha20 starting at counter 1, guaranteeing the Poly1305 key and the encryption keystream never overlap.

How Poly1305 Computes a Tag

The one-time key splits into two 16-byte halves, r and s. r is “clamped” (specific bits forced to zero) to keep the arithmetic well-behaved, and treated as a number for polynomial evaluation modulo the prime 2¹³⁰ − 5 — the prime that gives Poly1305 its name. The message is split into 16-byte blocks; each block, with a single bit appended, is added into a running accumulator, which is then multiplied by r (mod that prime) — repeated for every block. The final accumulator, plus s, mod 2¹²⁸, is the 16-byte authentication tag.

Interactive Visualizer

The visualizer above runs the complete, genuine construction — real ChaCha20 keystream generation for both key derivation and encryption, real Poly1305 polynomial evaluation for the tag — verified against Node.js’s own built-in ChaCha20-Poly1305 implementation before being published here. Try the tamper button to see authentication correctly reject an altered ciphertext.

Assembling the AEAD Construction

The final Poly1305 MAC is computed over a specifically formatted message: the associated data (padded to a 16-byte boundary), the ciphertext (padded to a 16-byte boundary), then 8-byte little-endian lengths of the associated data and ciphertext. This exact padding and length-encoding scheme prevents subtle ambiguity attacks where an attacker might otherwise construct two different (AAD, ciphertext) pairs that hash to the same tag.

A Verified Example

Using a small demonstration case with an all-but-one-byte-zero key:

  • Key: 32 bytes, all zero except the last byte set to 01
  • Nonce: 12 bytes, all zero except the last byte set to 02
  • Associated data: “hello aad”
  • Plaintext: “The quick brown fox jumps over the lazy dog”

produces a specific ciphertext and 16-byte tag that the visualizer above reproduces exactly, matching Node.js’s own AEAD implementation byte for byte.

ChaCha20-Poly1305 vs. AES-GCM

Both provide equivalent security guarantees as AEAD constructions. The practical difference is performance profile:

  • AES-GCM excels on hardware with AES-NI and PCLMULQDQ instructions — nearly all modern desktop and server CPUs — where it can outperform ChaCha20-Poly1305 substantially.
  • ChaCha20-Poly1305 excels in pure software, particularly on mobile and embedded processors that historically lacked (or had inconsistent) AES hardware acceleration, and is naturally resistant to cache-timing side-channels since it uses no table lookups at all.

TLS 1.3 mandates support for both specifically so each side of a connection can negotiate whichever performs best on its own hardware.

FAQ

Why can’t Poly1305 keys be reused?

Poly1305 is a “one-time” authenticator in the cryptographic sense — its security proof depends on each key being used for exactly one message. Reusing a Poly1305 key across two messages can let an attacker solve for the key algebraically and forge tags for arbitrary future messages.

Is ChaCha20-Poly1305 secure?

Yes — it’s one of exactly two AEAD constructions mandated by TLS 1.3, alongside AES-GCM, and has no known practical weaknesses in its standard configuration.

Why does the construction start ChaCha20 at counter 1 instead of 0?

Counter 0 generates the one-time Poly1305 key itself. Starting the actual message encryption at counter 1 guarantees the keystream used to derive the authentication key never overlaps with the keystream used to encrypt data — reusing that block would leak the Poly1305 key.

What does the “1305” in Poly1305 refer to?

The prime modulus 2¹³⁰ − 5, which the algorithm’s polynomial evaluation is computed modulo — 1305 loosely evokes “130-5.”

Does WhatsApp or Signal use ChaCha20-Poly1305?

Signal Protocol implementations commonly use AES-GCM or ChaCha20-Poly1305 for the actual message encryption layer (after the Double Ratchet establishes per-message keys) — see the Signal Protocol guide for the full picture of how those pieces fit together.

References

  1. RFC 8439. “ChaCha20 and Poly1305 for IETF Protocols.” IETF, 2018. Available at: https://datatracker.ietf.org/doc/html/rfc8439

  2. Bernstein, D. J. “The Poly1305-AES message-authentication code.” FSE 2005 — the original Poly1305 design paper.

  3. Langley, A., Chang, W., Mavrogiannopoulos, N., Strombergson, J., and Josefsson, S. “ChaCha20-Poly1305 Cipher Suites for TLS.” RFC 7905, 2016.

  4. Donenfeld, J. A. “WireGuard: Next Generation Kernel Network Tunnel.” NDSS 2017.