Why Confidentiality Is Not Enough
Encryption hides what a message says. It does not tell the recipient whether the message arrived unchanged. Those are two different problems, and for decades they were solved with two separate tools.
An attacker who controls the network can flip bits in a ciphertext without knowing the key. In stream ciphers and CTR mode this is devastating: flipping bit 7 of ciphertext byte 12 flips bit 7 of plaintext byte 12. Exactly. Predictably.
If the recipient has no way to detect the tampering, they decrypt the modified ciphertext, get a modified plaintext, and act on it. The cipher did its job: the attacker never learned the original message. But the recipient was tricked all the same.
The Bit-Flipping Attack
Imagine a banking app encrypts the message TRANSFER $00000100 TO ACCT 4242 with AES in CTR mode. The attacker intercepts the ciphertext on the wire. The attacker does not know the key, cannot read the plaintext, and yet can still alter the amount.
The attack works because the relationship between ciphertext and plaintext in CTR mode is P = C XOR keystream. Anything the attacker does to C is reflected one-for-one in the recovered P. The same flaw applies to any stream cipher, including ChaCha20 used alone.
CBC mode is slightly different: a single bit flip in one ciphertext block scrambles that whole block on decryption but flips exactly that bit in the next block. This is just as exploitable, and it is what powers padding oracle attacks like POODLE.
The fix is the same in every case: bind a tag to the ciphertext that an attacker cannot forge, and refuse to decrypt unless the tag verifies.
Combining Encryption With A MAC
The classical fix was to encrypt with a block cipher mode and then run a Message Authentication Code (MAC), usually HMAC, over the result. Three orderings were possible. Two of them were wrong.
| Order | What it means | Verdict |
|---|---|---|
| Encrypt-then-MAC (EtM) | Encrypt the plaintext, then MAC the ciphertext. Verify MAC before decrypting. | Correct. Used by IPsec. |
| MAC-then-Encrypt (MtE) | MAC the plaintext, append the tag, then encrypt the whole thing. Decrypt first, then verify. | Vulnerable. Used by SSL/TLS up through 1.2; produced padding oracles like Lucky 13. |
| Encrypt-and-MAC (E&M) | Encrypt the plaintext, separately MAC the plaintext, send both. | Vulnerable. The MAC reveals information about the plaintext. Used by SSH. |
Hugo Krawczyk's 2001 paper "The Order of Encryption and Authentication for Protecting Communications" formally proved that Encrypt-then-MAC is the only ordering that is generically secure. By then, SSL had already standardized on the wrong order, and SSH was already running.
Even with the right order, the construction has subtle pitfalls. The MAC and encryption keys must be different. The tag must be checked in constant time to avoid timing oracles. Implementation mistakes were common enough that the cryptography community decided composition itself was the problem, and built single primitives that did both jobs at once.
AEAD: The Modern Primitive
AEAD stands for Authenticated Encryption with Associated Data. It is a single function that does encryption and authentication together, with one key, in a way that cannot be misused into the wrong composition order.
An AEAD encryption call takes four inputs and produces two outputs:
| Input | Purpose |
|---|---|
| Key | Secret. The same key both encrypts and authenticates. |
| Nonce | A unique value per encryption under that key. Not secret. |
| Plaintext | The data to encrypt and authenticate. |
| Associated data (AD) | Data to authenticate but not encrypt. Often packet headers or routing metadata. |
The outputs are the ciphertext (same length as the plaintext) and a fixed-size authentication tag (typically 128 bits). On decryption, the receiver provides key, nonce, ciphertext, tag, and the same associated data. If the tag does not verify, decryption returns an error and no plaintext is released. Period.
This last property is the whole point. An AEAD decrypt failure is a single binary signal: the message is forged or corrupted. The attacker learns nothing about plaintext from a failed decryption.
AES-GCM: How It Works
By far the most widely deployed AEAD is AES-GCM, where GCM stands for Galois/Counter Mode. It combines AES in CTR mode for the encryption half with GHASH, a fast polynomial-evaluation MAC over the field GF(2128), for the authentication half. Modern Intel and ARM CPUs have dedicated instructions (AES-NI and PCLMULQDQ) that make AES-GCM extremely fast, often gigabytes per second per core.
The receiver runs the construction in reverse. They recompute the tag from the received ciphertext, associated data, and key. If their computed tag matches the received tag, they release the plaintext. If it does not match, they return an error and the plaintext is never exposed.
What the receiver does not do is parse the plaintext, check the padding, or inspect any structure before tag verification. That habit is what created Lucky 13 and POODLE in TLS 1.2. AEAD removes the temptation.
ChaCha20-Poly1305
AES-GCM is fast when the CPU has AES-NI instructions. Not every CPU does. Older mobile chips, embedded devices, and some server platforms do not, and on those AES becomes ten to twenty times slower while remaining vulnerable to cache-timing side channels.
ChaCha20-Poly1305 is the standard AEAD alternative for those platforms. ChaCha20 is a stream cipher designed by Daniel J. Bernstein in 2008 that runs fast in pure software with no special instructions and no table lookups, so it is naturally timing-safe. Poly1305 is the companion MAC, also by Bernstein, that operates over the prime field 2130 minus 5.
ChaCha20-Poly1305 was standardized in RFC 8439 in 2018 and is one of the three mandatory AEAD ciphersuites in TLS 1.3. Google rolled it out across Chrome and Android specifically to give phones without hardware AES a fast, safe option. WireGuard, Signal, and OpenSSH all default to ChaCha20-Poly1305.
| AEAD | When it wins | Where you find it |
|---|---|---|
| AES-128-GCM | x86 and ARM CPUs with hardware AES instructions. Highest throughput on servers. | TLS 1.3 default, AWS S3 SSE, IPsec ESP. |
| AES-256-GCM | Same as above, with a larger key for post-quantum hedging. | TLS 1.3, regulated environments. |
| ChaCha20-Poly1305 | Mobile devices, software-only implementations, anything sensitive to timing side channels. | TLS 1.3 on phones, WireGuard, Signal, modern SSH. |
| AES-GCM-SIV | When you cannot guarantee unique nonces. Tolerates nonce reuse at a cost. | Niche but growing for ticket and token encryption. |
Associated Data: The "AD" In AEAD
The associated data is everything that needs to be authenticated but does not need to be hidden. Network protocols are full of it.
Consider a TLS record. The encrypted payload is the application data, but the record also has a header that says how long it is, what protocol version this is, and what record type. The header has to be visible so routers and intermediate parsers can do their job, but it must not be tamperable. If an attacker can rewrite the header type from application_data to handshake, the receiver may try to parse application bytes as a handshake message, and bad things happen.
The solution is to pass the header bytes as associated data. AEAD authenticates them along with the ciphertext but does not encrypt them. If anything in the header changes in transit, the tag fails to verify.
TLS 1.3 record headers, IPsec ESP packet headers, WireGuard packet types and counters, JWT-style token headers, S3 object metadata in envelope encryption, and any RPC framing where the routing fields need integrity but not confidentiality.
The Nonce Reuse Catastrophe
Every AEAD has the same iron rule: never use the same nonce twice with the same key. Violations of this rule are not graceful degradations. They are catastrophic.
Reusing a nonce in AES-GCM lets an attacker recover the GHASH authentication key H from the two ciphertexts. Once they have H, they can forge tags for arbitrary messages under that key. Confidentiality of the affected pair is also lost: XORing the two ciphertexts reveals the XOR of the two plaintexts, which is often enough to recover both.
The nonce in AES-GCM is 96 bits. Two strategies are common:
- Counter. Start at zero and increment for every encryption. Safe as long as the counter is persistent and never resets. Used in TLS 1.3, where the nonce is derived from the record sequence number.
- Random. Generate a fresh 96-bit value from a CSPRNG for every encryption. Safe if the rate is below about 232 messages per key. Above that, the birthday bound makes collisions likely.
Mixing the two is dangerous. So is letting a virtual machine snapshot and then resume, because the resumed VM may emit a nonce the snapshotted one already used. The 2017 paper "Nonce-Disrespecting Adversaries" by Hanno Böck and others found exactly this failure mode in production TLS servers.
If a system genuinely cannot guarantee nonce uniqueness, the correct primitive is AES-GCM-SIV (RFC 8452), which is designed to remain secure even under nonce reuse, at a small performance cost. For most applications, however, a careful counter or a fresh random nonce per message is the right answer.
Where AEAD Shows Up Today
AEAD is no longer an alternative to plain encryption. It is the default. Plain CBC and plain CTR without authentication have effectively disappeared from new protocol design.
| Protocol | AEAD in use |
|---|---|
| TLS 1.3 | AES-128-GCM, AES-256-GCM, or ChaCha20-Poly1305. Only AEAD ciphersuites are permitted; CBC modes were removed. |
| QUIC and HTTP/3 | AES-128-GCM by default, ChaCha20-Poly1305 negotiable. Packet protection is AEAD with the packet header as associated data. |
| IPsec ESP | AES-GCM is the modern recommendation. Older AES-CBC plus HMAC-SHA1 is deprecated. |
| WireGuard | ChaCha20-Poly1305 exclusively. No negotiation, no alternatives. |
| Signal Protocol | AES-CBC with HMAC for legacy reasons in some message types; AES-GCM for newer constructions. The double ratchet provides the keys. |
| OpenSSH | ChaCha20-Poly1305 by default since OpenSSH 6.5 in 2014. AES-GCM also supported. |
| AWS S3 SSE-KMS | AES-256-GCM with envelope-encrypted DEKs. |
| Encrypted JWTs (JWE) | AES-GCM is the recommended content encryption algorithm. |
When you build a system today, the right defaults are: AES-256-GCM if your platform has hardware AES, ChaCha20-Poly1305 if it does not, a fresh 96-bit nonce per message, a 128-bit tag, and any protocol or framing metadata passed in as associated data. You do not need to compose anything. You do not need to invent anything. The library calls are one line each.
You have walked the full symmetric crypto stack: a definition of the model, AES as the workhorse cipher, padding and IVs as the plumbing that makes block ciphers usable, key management as the operational discipline that keeps secrets secret, and AEAD as the modern primitive that ties confidentiality and integrity together. The next track covers asymmetric cryptography, where the rules change in interesting ways.