Origins: encryption and authentication were separate problems

For most of cryptographic practice, confidentiality and integrity were designed independently. A cipher hid the message; a MAC, or message authentication code, proved it had not been altered. Combining them was left to the protocol designer, and there were three obvious ways to do it:

  • Encrypt-then-MAC: encrypt, then authenticate the ciphertext. Provably sound.
  • MAC-then-encrypt: authenticate the plaintext, then encrypt both. What SSL and TLS chose.
  • Encrypt-and-MAC: do both over the plaintext independently. What chose.

Only the first is safe in general, and the industry picked the other two. The reason the choice mattered took a decade of attacks to become obvious: in MAC-then-encrypt, the receiver must decrypt before it can verify, so it is processing attacker-controlled bytes before it knows whether they are authentic. Every padding-oracle attack lives in that gap.

The record is specific:

  • The 2002 padding-oracle result showed that leaking whether padding was valid leaks plaintext, one byte at a time.
  • (2011) exploited predictable initialisation vectors in TLS 1.0's chaining.
  • (2013) extracted plaintext from the timing difference between valid and invalid padding, so even implementations that returned identical error messages leaked through how long they took to fail.
  • (2014) forced a downgrade to SSL 3.0, then used its underspecified padding to read bytes.

Each fix made the code more delicate: constant-time padding checks are genuinely hard to write, and a single early return reintroduces the leak. The pattern is the real lesson: a construction that requires every implementer to be careful will eventually meet one who is not.

What AEAD changed

, authenticated encryption with associated data, makes the combination part of the primitive rather than the protocol. One key, one call, one output, and decryption fails as a unit: either the tag verifies and you get plaintext, or it does not and you get nothing to process.

The "associated data" half matters and is frequently skipped over: it authenticates headers that must be visible but must not be altered (record type, version, sequence number) binding the encrypted payload to its context. Without it, an attacker can leave the ciphertext intact and change the envelope around it.

The two dominant constructions:

  • -: counter mode with a Galois-field authenticator. Very fast where processors carry AES and carry-less multiply instructions, which is nearly all server hardware.
  • ChaCha20-Poly1305: a stream cipher with a separate authenticator, designed for constant-time software implementation. Faster where AES instructions are absent, which is much of the mobile and embedded world.

TLS 1.3 removed every non-AEAD suite from the protocol entirely, not deprecated but removed, which is why its cipher list is five entries rather than hundreds.

Two ways to encrypt a record

Once a TLS handshake has agreed on keys, every record of application data has to be both encrypted, so an eavesdropper cannot read it, and authenticated, so an attacker cannot tamper with it. There are two broad ways a cipher suite does this, and the mode token in the suite name tells you which.

An AEAD cipher (authenticated encryption with associated data), such as AES-GCM, AES-, or ChaCha20-Poly1305 - AES being the Advanced Encryption Standard and CCM its Counter with CBC-MAC mode - does both jobs in a single primitive. You give it the plaintext and some associated data, and it returns ciphertext plus an authentication tag. Decryption verifies the tag and the data together, and fails as one operation if anything was altered.

A CBC cipher (cipher block chaining) does only encryption. To get integrity, the suite pairs it with a separate (hash-based message authentication code), named by the trailing hash in the suite (for example the SHA in TLS_RSA_WITH_AES_128_CBC_SHA). The two operations are bolted together by the protocol, and the order in which they happen turns out to matter enormously.

Why MAC-then-encrypt went wrong

TLS originally used MAC-then-encrypt (MAC being the message authentication code): compute the HMAC over the plaintext, append it, pad the result to a block boundary, then encrypt the whole thing with CBC. The problem is that the receiver has to decrypt and remove padding before it can check the MAC, so malformed padding and a bad MAC are detected at different stages. An attacker who can see whether a record failed on padding or on the MAC, whether by an error message or just by timing, learns one bit about the plaintext per probe. Repeated, that bit becomes the whole message.

This is a padding oracle, and it is not theoretical. A series of attacks over the years, including BEAST, Lucky 13, and POODLE, all exploited CBC record protection in TLS. Each one was patched with increasingly delicate constant-time code, but the underlying construction kept producing new variants. The lesson the industry drew was to stop using it.

Why AEAD is the default now

AEAD removes the whole class of problem. There is no separate padding step to probe and no separate MAC stage to time, because verification and decryption are one operation that either succeeds or fails. TLS 1.3 takes this to its conclusion and permits AEAD ciphers only; CBC suites are simply not part of TLS 1.3. In TLS 1.2 both exist, which is why the decoder flags a CBC-with-HMAC-SHA1 suite as weak even when the cipher itself is AES.

When you see GCM, CCM, or POLY1305 in a suite name, you are looking at AEAD. When you see CBC, you are looking at the older construction, and on a modern service it should be a fallback at best.

The obligation AEAD carries: never repeat a nonce

AEAD trades the padding-oracle class for one sharp requirement. A key and nonce pair must never be reused. In AES-GCM the consequence is severe and worth stating plainly: repeating a nonce under the same key leaks the XOR of the two plaintexts and allows recovery of the authentication subkey, which lets an attacker forge messages afterwards. This is not a degradation; it is a break.

Where that goes wrong in practice:

  • Random 96-bit nonces at very high volume, where birthday collisions stop being theoretical. This is what motivated XChaCha20, whose 192-bit nonce makes a collision far less likely, and AES-GCM-SIV, which tolerates reuse far better.
  • Counters that reset, typically after a restore, a snapshot rollback, or a device that resumes with the same key material.
  • Stateless designs where two nodes share a key and independently pick nonces.

TLS 1.3 avoids all of it by deriving the nonce from the sequence number rather than transmitting it, so reuse cannot happen without a protocol-level fault. Anyone building their own record layer must solve this deliberately.

Record size is the other tradeoff. Each record carries a tag, 16 bytes for both dominant constructions, so many small records cost proportionally more overhead than a few large ones, while large records add latency because nothing can be delivered until the whole record verifies.

Where the choice appears in practice

  • TLS libraries pick the default suite list, and modern versions have made AEAD the only option worth having.
  • Disk and volume encryption frequently uses length-preserving modes rather than AEAD, because a sector cannot grow to hold a tag, a genuine reason to see non-AEAD constructions in current systems.
  • Application-level encryption (libsodium, Tink, cloud key-management SDKs) where the honest guidance is to use a library that manages nonces rather than assembling primitives.
  • Protocols with their own record layers (QUIC, , SSH) now use AEAD by default, all deriving nonces from counters for exactly the reason above, though SSH still offers AES-CTR with a separate MAC.

The rule that survives every context: use AEAD, let a library own the nonce, and treat any design where you choose nonces yourself as one that needs review.

The tradeoff AEAD still asks

AEAD is not free of sharp edges; it just moves them. Every AEAD encryption needs a nonce, a number used once, and the security of GCM (Galois/Counter Mode) in particular collapses if the same nonce is ever reused with the same key. TLS manages nonces for you with a per-connection counter, so this is a danger for systems that build their own framing rather than for TLS itself, but it is the reason AEAD designs care so much about nonce construction.

The other tradeoff is the authentication tag length. The standard GCM and Poly1305 suites use a 16-byte tag. The CCM_8 suites truncate that to 8 bytes to save overhead on constrained links, which measurably weakens integrity. That is why TLS_AES_128_CCM_8_SHA256 is a real TLS 1.3 AEAD suite that (the Internet Assigned Numbers Authority) still declines to mark recommended: the mode is modern, but the short tag is a compromise the general internet does not need to make.

Sources

The protocol behaviour described above is normative, and the two attacks that drove it are documented with their numbers. These are where to re-check both.