open atlas
↑ Back to track
Security Foundations SECF · 02 · 02

Symmetric encryption and AEAD

Raw block-cipher modes leak structure and say nothing about tampering. AEAD (AES-GCM, ChaCha20-Poly1305) gives you confidentiality and integrity in one primitive — but a single reused nonce can leak the keystream and forge messages.

SECF Middle ◷ 15 min
Level
FoundationsJuniorMiddleSenior

A 2008 screenshot made the rounds for years: the Linux penguin, encrypted, still recognizably the penguin. The image had been run through AES — a cipher nobody has broken — in ECB mode, which encrypts each 16-byte block independently. Identical plaintext blocks produced identical ciphertext blocks, so every flat region of the same color mapped to the same ciphertext, and the silhouette survived encryption intact. The key was never the problem. The mode leaked the structure of the data while faithfully hiding the bytes. Years later the same class of mistake shows up wearing a suit: a service encrypts session tokens with AES-CBC, ships them to the client, and an attacker who can flip ciphertext bytes — with no key, no plaintext — quietly rewrites the decrypted token, because CBC says nothing about whether the ciphertext was tampered with. Same lesson, higher stakes: a strong cipher in the wrong construction is a vulnerability with good PR.

By the end of this lesson you’ll know why raw cipher modes leak or accept tampering, how AEAD folds confidentiality and integrity into one primitive, and why a reused nonce is the foot-gun that ends careers.

Symmetric ciphers: one key, two ways to use it wrong

Symmetric encryption means both sides share one secret key and the same key both encrypts and decrypts. That is its whole appeal: it is fast — AES with hardware acceleration (AES-NI) runs at gigabytes per second, orders of magnitude faster than any public-key operation — which is exactly why TLS uses asymmetric crypto only to agree on a symmetric key, then does the bulk traffic encryption symmetrically. The catch is distribution: both parties must already share the key, and that bootstrap is the hard part (the next lesson’s territory).

A block cipher like AES is not, by itself, a way to encrypt a message. AES transforms exactly one fixed-size block — 16 bytes — under the key. Real messages are longer than 16 bytes, so you need a mode of operation that says how to apply the block cipher across many blocks. The mode is where the security lives or dies, and the two classic failures are the two halves of the hook:

  • ECB (Electronic Codebook) encrypts each block independently with no chaining. Because the transform is deterministic, the same plaintext block always yields the same ciphertext block — that is the penguin. ECB leaks equality of blocks, which leaks structure, which for many real payloads leaks the data itself. Never use ECB. There is essentially no setting where it is the right answer.
  • CBC (Cipher Block Chaining) fixes the determinism by XOR-ing each plaintext block with the previous ciphertext block (and a random IV for the first), so identical plaintext no longer produces identical ciphertext. But CBC provides confidentiality only. It does not tell you whether the ciphertext was modified in transit. An attacker who flips a bit in the ciphertext produces a predictable, attacker-influenced change in the decrypted plaintext — the basis of bit-flipping and padding-oracle attacks. CBC keeps the secret; it does not keep the truth.

That second gap — encryption without integrity — is the one engineers underestimate, because the message still decrypts to something and the demo still works.

Why confidentiality without integrity is a trap

Encryption hides content. It does not, on its own, prove the content is unchanged or came from who you think. Treating “it’s encrypted” as “it’s safe” is the single most common crypto mistake in application code. The classic demonstration is the padding oracle: with CBC, if the server reveals — even through a timing difference or a generic 500 vs 403 — whether decryption produced valid padding, an attacker can decrypt the entire ciphertext byte by byte without the key, by submitting crafted ciphertexts and watching the oracle. No key is ever recovered; the attacker simply uses the server’s own decrypt-and-check behavior as a side channel.

The senior reflex is encrypt-then-MAC done right: never hand a decrypted message to your application logic until you have verified, with a keyed authentication tag, that the ciphertext is exactly what the legitimate sender produced. Rolling that yourself — picking a MAC, ordering it correctly, comparing tags in constant time — is a minefield (encrypt-then-MAC vs MAC-then-encrypt ordering alone has sunk real protocols). Which is precisely why the field stopped hand-assembling these pieces and standardized the combined primitive.

AEAD: confidentiality and integrity in one primitive

AEAD — Authenticated Encryption with Associated Data — is the modern default, and it is what TLS 1.3 mandates: the spec removed every non-AEAD cipher, so all TLS 1.3 traffic is authenticated encryption. An AEAD primitive does three jobs in one call:

  1. Encrypts the plaintext (confidentiality).
  2. Produces an authentication tag over the ciphertext (integrity + authenticity): on decrypt, if the ciphertext or tag was altered by even one bit, decryption fails loudly and returns no plaintext at all. There is nothing to feed a padding oracle.
  3. Optionally authenticates Associated Data (the “AD”) — fields you want bound to the ciphertext but sent in the clear, like a message header, sequence number, or recipient id. The AD is not encrypted, but it is covered by the tag, so an attacker can’t move a valid ciphertext to a different header or replay it under a different sequence number.

Two AEAD constructions dominate. AES-GCM pairs AES in counter mode with the GHASH authenticator; it is the fast choice on any CPU with AES-NI, which is most servers. ChaCha20-Poly1305 is a stream cipher plus the Poly1305 MAC; it has no hardware dependency, so it is constant-time and faster in software — which is why it is the default on mobile and on hardware without AES acceleration. Both give the same guarantee. Both share the same lethal precondition.

The nonce-reuse foot-gun

Every AEAD call takes a nonce (number used once) alongside the key. The contract is in the name: for a given key, a nonce must never repeat. Honor it and AEAD is excellent. Violate it and the guarantees don’t degrade gracefully — they collapse.

For AES-GCM the collapse is total and well-documented. GCM is counter mode underneath: the nonce seeds the keystream. Reuse a (key, nonce) pair on two different messages and you’ve encrypted two plaintexts with the same keystream; XOR the two ciphertexts and the keystream cancels, leaking the XOR of the two plaintexts — and from there, often the plaintexts. Worse, nonce reuse in GCM leaks the authentication subkey (H), which lets an attacker forge valid tags for arbitrary messages under that key. Confidentiality and integrity fall in one mistake. This is not theoretical: it is how real systems have been broken, and it is why GCM’s 96-bit (12-byte) nonce is dangerous with random generation at scale — the birthday bound means collisions become likely well before you’ve sent 2⁹⁶ messages (the danger zone starts around 2³² messages with random 96-bit nonces under one key).

Why this works

Why is a 96-bit random nonce “risky at scale” when 2⁹⁶ is an astronomically large number? Because the math that matters is the birthday bound, not the full space. With random nonces, the chance of a collision rises with the square of the message count: you cross meaningful collision probability around 2⁴⁸ messages, and NIST caps a single key at 2³² invocations with random 96-bit nonces for exactly this reason. The two robust escapes: use a deterministic counter nonce (a per-key message counter that provably never repeats until the counter wraps), or switch to an XChaCha20-Poly1305-style construction whose 192-bit nonce is large enough that random generation is safe. The failure isn’t a weak cipher — it’s a strong cipher fed a nonce its construction never promised to tolerate.

ChaCha20-Poly1305 is no more forgiving — nonce reuse equally leaks the keystream and the Poly1305 authentication key — which is why the practical guidance is mode-independent: manage nonces as carefully as keys. Prefer a deterministic counter you control over random() you hope is unique, never reuse a (key, nonce) pair across two messages, and rotate the key before the nonce space gets uncomfortable. When you genuinely can’t guarantee uniqueness (stateless workers, no shared counter), reach for a misuse-resistant AEAD (AES-GCM-SIV) or a large-nonce construction (XChaCha20-Poly1305) that survives an accidental repeat instead of shattering.

ConstructionConfidentialityIntegrityUse it when
AES-ECBBroken (leaks block equality)NoneNever — this is the penguin
AES-CBC (no MAC)Yes (with random IV)None → padding oracle, bit-flipLegacy interop only; never new code
AES-GCMYesYes (tag) — voids on nonce reuseDefault with AES-NI + unique nonces
ChaCha20-Poly1305YesYes (tag) — voids on nonce reuseNo AES-NI; mobile; software speed
AES-GCM-SIVYesYes — survives accidental reuseCan’t guarantee nonce uniqueness

How a senior chooses

The decision tree is short. Reach for AEAD by default — never raw ECB or unauthenticated CBC. Pick AES-GCM on servers with AES-NI; pick ChaCha20-Poly1305 where there’s no AES hardware (mobile, embedded) or you want constant-time-in-software by construction. Then spend your real attention on the nonce, because that is where AEAD actually breaks in production: a deterministic per-key counter beats random generation, and if you can’t guarantee uniqueness across all the processes that share a key, choose a misuse-resistant or large-nonce variant. And don’t roll your own — use the platform’s vetted library (libsodium, the Web Crypto AES-GCM, your language’s standard AEAD), which gets the tag comparison constant-time and the wiring right.

Pick the best fit

A service encrypts session tokens with AES-CBC and ships them to the client. An attacker who can't read the key flips bytes in the ciphertext and finds the decrypted token changes in their favor. Pick the correct fix.

Quiz

Why does the AES-encrypted Linux penguin remain recognizable in ECB mode, even though AES itself is unbroken?

Quiz

You reuse the same (key, nonce) pair for two different messages under AES-GCM. What is the worst consequence?

Order the steps

Order these constructions from most broken to safest default for new code:

  1. 1 AES-ECB (leaks block equality — the penguin)
  2. 2 AES-CBC with no MAC (confidentiality only → padding oracle)
  3. 3 AES-GCM with unique nonces (AEAD: confidentiality + integrity)
  4. 4 AES-GCM-SIV (AEAD that survives accidental nonce reuse)
Recall before you leave
  1. 01
    Explain why raw ECB and raw CBC are both unsafe, and how AEAD fixes the underlying problem.
  2. 02
    What is the nonce-reuse foot-gun in AES-GCM, why is it catastrophic, and how do you avoid it?
Recap

Symmetric encryption uses one shared key for both directions and is fast enough (AES-NI) that TLS uses asymmetric crypto only to agree on the symmetric key, then encrypts bulk traffic symmetrically. A block cipher like AES only transforms one 16-byte block, so a mode of operation decides how the cipher is applied across a message — and that’s where it breaks. ECB encrypts blocks independently and deterministically, leaking structure (the encrypted penguin). CBC chains blocks to fix that but provides confidentiality without integrity, which opens bit-flipping and padding-oracle attacks: encryption that hides content but doesn’t prove it’s untampered is a trap, because the message still decrypts to something. AEAD — Authenticated Encryption with Associated Data — folds both jobs into one primitive: it encrypts, produces an authentication tag over the ciphertext so any tampering fails decryption with no plaintext returned, and can bind clear-text associated data (headers, sequence numbers) to the ciphertext. AES-GCM is the fast pick with AES-NI; ChaCha20-Poly1305 wins without AES hardware; TLS 1.3 mandates AEAD. Both share one lethal precondition: the nonce must never repeat under a key. Reuse leaks the keystream (XOR the ciphertexts) and, in GCM, the authentication subkey — so an attacker recovers plaintext and forges tags from a single repeated nonce, a risk sharpened by GCM’s 96-bit nonce and the birthday bound. Manage nonces as carefully as keys: prefer a deterministic counter, reach for AES-GCM-SIV or XChaCha20-Poly1305 when uniqueness can’t be guaranteed, and never roll your own. So the next time you see “it’s encrypted,” your first question is: encrypted with what mode — and is it authenticated?

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 6 done
Connected lessons

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.