<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Parag Mali - tag: encrypt-then-mac</title><description>Posts tagged encrypt-then-mac.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sat, 25 Jul 2026 08:57:59 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/encrypt-then-mac/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Tag Verified, the Cipher Held, the Forgery Went Through: A Field Guide to Message Authentication and Safe Composition</title><link>https://paragmali.com/blog/the-tag-verified-the-cipher-held-the-forgery-went-through-a-/</link><guid isPermaLink="true">https://paragmali.com/blog/the-tag-verified-the-cipher-held-the-forgery-went-through-a-/</guid><description>Why HMAC, KMAC, GMAC, and Poly1305 never broke -- but Flickr, Lucky Thirteen, and 184 GCM servers did. A field guide to MACs and Encrypt-then-MAC.</description><pubDate>Sat, 11 Jul 2026 01:12:01 GMT</pubDate><content:encoded>
A MAC makes forgery hard; three deployment assumptions keep it hard, and every famous break -- Flickr [@flickr-advisory], Lucky Thirteen [@cve-2013-0169], POODLE [@cve-2014-3566], the SSH plaintext-recovery attack [@apw-2009], and 184 nonce-repeating HTTPS servers [@woot16-bock] -- broke one of those assumptions, not the math. Pick the right family (PRF-based HMAC and KMAC take a reusable key and need no nonce; Wegman-Carter GMAC and Poly1305 run at line rate but demand a one-time key [@wc-1981]), verify in constant time, and always Encrypt-then-MAC over the ciphertext with an independent key and a strongly unforgeable tag [@bn-2000]. This guide traces the primitive from Carter-Wegman universal hashing in 1979 [@cw-1979] to committing AEAD in 2025 [@rfc-9771], names every failure by root cause, and ends with a decision tree and a paste-in review checklist. The one rule underneath all of it: authenticate the ciphertext, the length, and the context -- not just the message.
&lt;h2&gt;1. A Forgery Without a Broken Cipher&lt;/h2&gt;
&lt;p&gt;In 2009, two researchers forged valid Flickr API signatures without knowing Flickr&apos;s secret key, without finding a single MD5 collision, and without touching a cipher [@flickr-advisory]. Flickr had done what feels obviously correct -- prepend a secret to each request and hash the result -- and that one instinct silently handed signing power to anyone who had ever seen a single signed URL. The hash worked exactly as designed. The way Flickr &lt;em&gt;used&lt;/em&gt; it was the vulnerability.&lt;/p&gt;
&lt;p&gt;That gap -- between a primitive that is sound and a deployment that is not -- is the subject of this article, and it is worth stating as a claim you can test against every page that follows.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A message authentication code makes forgery hard; keeping it hard is the surrounding protocol&apos;s job. Every deployed MAC ships a proof, and every proof quietly assumes three things the deployment must supply: (1) the right &lt;strong&gt;key discipline&lt;/strong&gt; for its family, (2) a &lt;strong&gt;constant-time verify&lt;/strong&gt;, and (3) &lt;strong&gt;composition over the right bytes, in the right order, binding the right context&lt;/strong&gt;. Read that way, the entire failure catalogue is one sentence repeated -- the math never broke; a deployment broke one of these three assumptions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before we can prove that thesis, we need the object it is about. Informally, a MAC is a small tag you attach to a message so the receiver can tell the message came from someone who holds the shared secret key, and that nobody altered it in transit.&lt;/p&gt;

A MAC is a keyed algorithm that takes a secret key and a message and produces a short fixed-size tag. Anyone holding the same key can recompute the tag to verify that the message is authentic and unmodified. Unlike a plain hash, a MAC is keyed, so an attacker without the key cannot produce a valid tag; unlike a digital signature, the same secret both makes and checks the tag, so there is no public verifiability and no non-repudiation.
&lt;p&gt;Now pair Flickr with a second break that fails a &lt;em&gt;different&lt;/em&gt; assumption. In 2013, Lucky Thirteen recovered plaintext from TLS connections that used a perfectly correct HMAC -- the attack timed &lt;em&gt;how&lt;/em&gt; the MAC was checked, not whether the MAC was strong [@cve-2013-0169]. Two production systems, two respected primitives, two failures -- and in neither case did the cryptography break. Flickr fed the hash the wrong bytes in the wrong construction; TLS checked a correct tag on a timing-leaky path.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; MD5 is badly broken for collision resistance, yet the Flickr forgery did not use a collision. HMAC was and is unbroken, yet Lucky Thirteen still bled plaintext. In both cases the primitive met its specification and the deployment violated an assumption the specification silently depended on. Hold onto that pattern -- it is the whole catalogue in miniature.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By the end of this field guide you will have a decision procedure and a review checklist that catch every failure in this article before it ships.The original Flickr advisory by Duong and Rizzo is no longer served from its source host; the citation here points to a 2009 Wayback Machine snapshot, which is the durable primary record [@flickr-advisory]. But a checklist without the underlying model is just cargo-culting. So start with the model. If the hash was not broken, what exactly is a MAC supposed to promise, and against whom?&lt;/p&gt;
&lt;h2&gt;2. What a MAC Actually Is&lt;/h2&gt;
&lt;p&gt;Strip away the mystique and a MAC is three algorithms and one game.&lt;/p&gt;
&lt;p&gt;The three algorithms are &lt;code&gt;KeyGen&lt;/code&gt;, &lt;code&gt;Tag&lt;/code&gt;, and &lt;code&gt;Verify&lt;/code&gt;. &lt;code&gt;KeyGen&lt;/code&gt; samples a secret key $k$. &lt;code&gt;Tag&lt;/code&gt; maps a key and a message $m$ to a tag $t$. &lt;code&gt;Verify&lt;/code&gt; takes $k$, $m$, and a candidate $t$ and returns accept or reject. The whole design question is: how hard is it for someone without $k$ to make &lt;code&gt;Verify&lt;/code&gt; say accept on a message the key-holder never authorized?&lt;/p&gt;
&lt;p&gt;&quot;How hard&quot; was made precise in 1988. Shafi Goldwasser, Silvio Micali, and Ronald Rivest gave the first rigorous adversarial definition of authentication -- existential unforgeability under adaptive chosen-message attack -- while analyzing digital signatures, and MACs inherit it directly [@gmr-1988]. The game is deliberately generous to the attacker.&lt;/p&gt;

The adversary may ask for tags on any messages it likes, adaptively, as many as it wants. It wins if it then outputs a valid tag on any *new* message it never queried. A MAC is EUF-CMA secure if every efficient adversary wins only with negligible probability. The name is exact: &quot;existential&quot; means the forgery can be on any message the attacker chooses, and &quot;adaptive chosen-message&quot; means it sees each answer before picking its next query.
&lt;p&gt;EUF-CMA is the usual bar, but composition needs a slightly stronger one, and the difference will decide whether a whole protocol stands or falls in Section 6.&lt;/p&gt;

SUF-CMA forbids even a *new tag on an old message*. The adversary wins if it outputs any message-tag pair that verifies and was not itself the result of a tag query -- including a second, different valid tag on a message it already asked about. Every SUF-CMA MAC is EUF-CMA, but not conversely: a MAC with malleable tags can be existentially unforgeable yet still let an attacker maul a valid tag into another valid tag.
&lt;p&gt;Formally, if we write the tag oracle as $\mathsf{Tag}_k(\cdot)$ and let $Q$ be the set of message-tag pairs it returned, the strong-forgery advantage is&lt;/p&gt;
&lt;p&gt;$$\mathsf{Adv}^{\text{suf-cma}}_{\mathsf{MAC}}(\mathcal{A}) ;=; \Pr!\left[,(m,t) \leftarrow \mathcal{A}^{\mathsf{Tag}_k(\cdot)} : \mathsf{Verify}_k(m,t)=\text{accept} ;\wedge; (m,t)\notin Q,\right].$$&lt;/p&gt;
&lt;p&gt;A MAC is good when this probability stays negligible for every efficient $\mathcal{A}$. That single inequality is the promise Flickr&apos;s construction could not keep -- an attacker produced an accepted tag on a message that was never in $Q$.&lt;/p&gt;
&lt;p&gt;Four primitives live near this definition, and confusing them is the source of real bugs. An &lt;strong&gt;unkeyed hash&lt;/strong&gt; has no $k$, so anyone can recompute it -- it detects accidental corruption, never a motivated forger; this series covers hashes and their length-extension quirk in Part 10.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;MAC&lt;/strong&gt; is symmetric: the same secret makes and checks the tag. A &lt;strong&gt;digital signature&lt;/strong&gt; is asymmetric -- a private key signs, a public key verifies -- which buys public verifiability and non-repudiation that a MAC deliberately does not provide (Part 3 develops &lt;a href=&quot;https://paragmali.com/blog/the-signature-was-valid-the-message-was-forged-a-field-guide/&quot; rel=&quot;noopener&quot;&gt;what the authenticated bytes must actually cover&lt;/a&gt;). An &lt;strong&gt;AEAD&lt;/strong&gt; bundles a MAC together with encryption so one call delivers secrecy and integrity at once; Part 7 is &lt;a href=&quot;https://paragmali.com/blog/the-aead-decision-matrix-seven-ciphers-three-edges-one-choic/&quot; rel=&quot;noopener&quot;&gt;the decision matrix for choosing among AEADs&lt;/a&gt;, and this article is the authentication half underneath it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Because a MAC key both signs and verifies, a valid tag proves only that &lt;em&gt;someone holding the shared key&lt;/em&gt; produced it -- it cannot tell you &lt;em&gt;which&lt;/em&gt; party, and it cannot be shown to a third party as proof. If you need &quot;Alice, and only Alice, provably sent this,&quot; you need a signature, not a MAC. Reaching for a MAC where you needed non-repudiation is a design error no amount of key length fixes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The definitions say a good MAC is unforgeable. Yet the very first keyed MACs people built were forgeable in ways the definition had not yet been written to outlaw. Stranger still, the airtight, provably unforgeable answer arrived &lt;em&gt;before&lt;/em&gt; the practical, broken ones. Where did the idea come from?&lt;/p&gt;
&lt;h2&gt;3. Origins: Universal Hashing and One-Time Authentication&lt;/h2&gt;
&lt;p&gt;Here is the twist that reorders the whole story. The provably secure MAC was invented first, in 1979 and 1981, and then the field spent two decades shipping forgeable ones anyway. Keep that inversion in mind as the arc unfolds.&lt;/p&gt;

timeline
    title Message authentication, 1979 to 2025
    1979 : Carter-Wegman universal hashing
    1981 : One-time authentication, tag is hash plus one-time pad
    1985 : CBC-MAC standardized in FIPS 113
    1996 : HMAC nests the hash to stop length extension
    2000 : Encrypt-then-MAC formalized by Bellare and Namprempre
    2001 : Krawczyk indicts the SSL and SSH orderings
    2004 : GMAC revives Wegman-Carter at line speed
    2005 : Poly1305 brings it to fast software
    2006 : HMAC proven a PRF without collision resistance
    2009 : Flickr forgery and the SSH plaintext recovery
    2013 : Lucky Thirteen timing oracle
    2016 : KMAC keyed sponge and 184 nonce-repeating servers
    2018 : TLS 1.3 record layer becomes AEAD-only
    2023 : Terrapin breaks a correct channel
    2025 : RFC 9771 standardizes commitment vocabulary
&lt;p&gt;The starting point is a purely combinatorial object. Larry Carter and Mark Wegman, at IBM Research, asked what happens if you pick a hash function at random from a carefully built family, rather than fixing one function everyone knows [@cw-1979].&lt;/p&gt;

A universal family of hash functions has the property that, for any two fixed distinct inputs, a function drawn uniformly at random from the family maps them to the same output with only small, bounded probability. The bound comes from counting the functions in the family, not from any assumption about how much computing power an attacker has. It is combinatorial security, not cryptographic security -- which is exactly why it needs no unproven hardness conjecture.
&lt;p&gt;Two years later, Wegman and Carter turned that object into an authenticator [@wc-1981]. The construction is startlingly small: draw a secret hash $h$ from a strongly universal family, draw a secret one-time pad $s$, and send&lt;/p&gt;
&lt;p&gt;$$t ;=; h(m) ,\oplus, s.$$&lt;/p&gt;
&lt;p&gt;The pad $s$ hides the value $h(m)$ completely, like a one-time pad hides a plaintext, and the universal property bounds an attacker&apos;s chance of guessing a valid tag for a &lt;em&gt;different&lt;/em&gt; message. The result is unconditional: it holds against an adversary with unlimited computing power, assuming nothing about factoring, discrete logs, or the strength of any cipher.&lt;/p&gt;

Authenticate a message as the exclusive-or of a universal hash of the message and a fresh secret pad: the tag reveals nothing about the hash key, and forging a tag on a new message requires guessing a value the universal bound keeps small. The security is information-theoretic and tight -- but the pad must be used exactly once. Authenticate a second message under the same pad and the guarantee evaporates.
&lt;p&gt;Take the smallest instance. Fix a prime $p$, keep a secret pair $(a, b)$, and tag a message $m$ as $t = (a\cdot m + b) \bmod p$. An attacker who sees one pair $(m_1, t_1)$ learns one linear equation in two unknowns; every candidate slope $a$ has exactly one consistent $b$, so the tag on any different $m_2$ stays uniform over all $p$ values. The best forgery succeeds with probability $1/p$. Beautiful, and useless twice: a second pair $(m_2, t_2)$ gives a second equation, the line is pinned, and every future tag is computable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;One-time&quot; is not a restriction bolted onto Wegman-Carter authentication -- it &lt;em&gt;is&lt;/em&gt; the security proof. The pad&apos;s single use is what buys the information-theoretic guarantee. That makes it a feature in 1981 and a landmine in 2004, when the same construction is revived at network line speed and a fast deployment reuses the pad. Every GMAC and Poly1305 nonce rule you will ever read is this 1981 constraint wearing modern clothes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the airtight answer existed. Yet it authenticates only one message per key, which is impractical for a general-purpose MAC.The two ideas are separate papers: the 1979 work introduced universal classes of hash functions as a combinatorial tool [@cw-1979]; the 1981 follow-up applied them to authentication and set equality [@wc-1981]. Universal hashing came first; authentication was the application. If the unconditional construction was this good but this restrictive, what did engineers reach for instead -- and why did the &quot;obvious&quot; keyed hashes they built break?&lt;/p&gt;
&lt;h2&gt;4. Early Approaches and Why They Broke&lt;/h2&gt;
&lt;p&gt;When engineers needed a keyed MAC in the 1990s, most reached for the hash they already had and glued a secret to it. The most natural glue is to put the key in front: compute &lt;code&gt;H(secret || message)&lt;/code&gt; and ship the digest as the tag. It looks unforgeable -- you cannot compute the hash without the secret. It is also exactly what Flickr shipped, and it is broken by a property of the hash that has nothing to do with its collision resistance.&lt;/p&gt;
&lt;p&gt;The property is length extension, and it is a direct consequence of how Merkle-Damgard hashes -- MD5, SHA-1, and most of SHA-2 -- are built [@length-ext-wiki]. Such a hash absorbs the message block by block into an internal state, and it emits &lt;em&gt;that entire internal state&lt;/em&gt; as the digest. The digest is therefore a complete, resumable snapshot: given &lt;code&gt;H(secret || message)&lt;/code&gt;, you can set a fresh hash&apos;s state to that value and keep absorbing more bytes, as if you had known the secret all along.&lt;/p&gt;

Given the digest of `secret || message` and the length of the secret, an attacker can compute the digest of `secret || message || padding || extra` for attacker-chosen `extra`, without knowing the secret. The attack works because a Merkle-Damgard digest is the hash&apos;s full internal state, so it can be resumed. The only wrinkle is the glue `padding` the original hash appended internally, whose bytes are fully determined by the known lengths.
&lt;p&gt;The forgery is mechanical once you see the state as resumable.&lt;/p&gt;

sequenceDiagram
    participant A as Attacker
    participant V as Verifier
    A-&amp;gt;&amp;gt;V: Observe one signed request, message m and tag t
    Note over A: The tag t is the full hash state after absorbing key and m
    A-&amp;gt;&amp;gt;A: Resume from t, append glue padding then chosen extra bytes
    A-&amp;gt;&amp;gt;A: Keep hashing to get t2 for the extended message, no key used
    A-&amp;gt;&amp;gt;V: Submit extended message with tag t2
    Note over V: Verifier recomputes over key plus the extended message
    V--&amp;gt;&amp;gt;A: State matches, forged request accepted
&lt;p&gt;The demonstration below builds a toy Merkle-Damgard hash whose digest is its internal state -- the essential property -- and forges a secret-prefix tag with no secret, then shows that nesting the hash (the HMAC idea) shuts the door.&lt;/p&gt;
&lt;p&gt;{`&lt;/p&gt;
Toy Merkle-Damgard hash: absorb one byte at a time; the digest IS the state.
(The mixer is illustrative, not secure -- the STRUCTURE is the point.)
&lt;p&gt;def compress(state, byte):
    return ((state * 1000003) ^ byte) &amp;amp; 0xFFFFFFFFFFFF   # 48-bit toy state&lt;/p&gt;
&lt;p&gt;def toy_hash(data, iv=0xA5A5A5A5A5A5):
    state = iv
    for byte in data:
        state = compress(state, byte)
    return state                      # digest == resumable internal state&lt;/p&gt;
&lt;p&gt;secret = b&quot;opensesame&quot;                # attacker does NOT know this...
def prefix_mac(msg):                  # ...but does know len(secret) == 10
    return toy_hash(secret + msg)&lt;/p&gt;
1) Attacker observes ONE legitimate (message, tag) pair:
&lt;p&gt;seen = b&quot;amount=10&quot;
tag  = prefix_mac(seen)&lt;/p&gt;
2) Attacker resumes from the tag and appends -- the secret is never touched:
&lt;p&gt;extra = b&quot;&amp;amp;amount=1000000&quot;
forged = tag
for byte in extra:
    forged = compress(forged, byte)&lt;/p&gt;
3) The verifier recomputes over secret + (seen + extra):
&lt;p&gt;print(&quot;secret-prefix forgery accepted:&quot;, forged == prefix_mac(seen + extra))&lt;/p&gt;
The HMAC-style fix: nest, so the outer pass re-absorbs the inner digest.
&lt;p&gt;def nested_mac(msg):
    inner = toy_hash(secret + msg)
    return toy_hash(secret + inner.to_bytes(6, &quot;big&quot;))&lt;/p&gt;
&lt;p&gt;resumed = nested_mac(seen)
for byte in extra:
    resumed = compress(resumed, byte)
print(&quot;nested (HMAC-style) forgery accepted:&quot;, resumed == nested_mac(seen + extra))
`}&lt;/p&gt;
&lt;p&gt;The first line prints &lt;code&gt;True&lt;/code&gt;, the second &lt;code&gt;False&lt;/code&gt;. That single flip is the difference between the Flickr construction and HMAC, and it is Aha number one for this article.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;H(secret || message)&lt;/code&gt; over any Merkle-Damgard hash is not a MAC. It authenticates nothing an attacker who has seen one tag cannot forge, regardless of how strong the underlying hash is. MD5&apos;s collision weakness is irrelevant here -- the Flickr forgery used no collision at all [@flickr-advisory]. If you find &lt;code&gt;md5(secret + data)&lt;/code&gt; or &lt;code&gt;sha256(secret + data)&lt;/code&gt; in a code review, you have found a length-extension forgery waiting to happen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The mirror construction, secret-suffix &lt;code&gt;H(message || secret)&lt;/code&gt;, resists length extension because the secret is absorbed last.Secret-suffix is not safe either: since an attacker who finds two messages colliding under the raw hash gets a tag collision for free, &lt;code&gt;H(m || k)&lt;/code&gt; reduces to the hash&apos;s collision resistance, so it dies with MD5 and SHA-1 -- the opposite failure mode from secret-prefix, same verdict [@length-ext-wiki]. Neither ad-hoc glue is a MAC.&lt;/p&gt;
&lt;p&gt;The block-cipher world made a parallel mistake. CBC-MAC, standardized as FIPS 113 in 1985, runs a block cipher in CBC mode with a zero IV and keeps the final ciphertext block as the tag [@fips-113]. It carries an unstated assumption: every message is the same fixed length. Bellare, Kilian, and Rogaway turned the failure of that assumption into a theorem in 1994 -- secure for fixed-length messages, trivially forgeable once lengths vary [@bkr-1994].&lt;/p&gt;
&lt;p&gt;The splice is mechanical. Query the tag of a one-block message $m_1$ to get $t_1 = E_k(m_1)$; then the two-block message $m_1 ,\Vert, (m_1 \oplus t_1)$ has tag $E_k\big((m_1 \oplus t_1)\oplus t_1\big) = E_k(m_1) = t_1$ -- a valid tag on a message never queried, produced with no key.&lt;/p&gt;
&lt;p&gt;Both failures teach the same lesson, and it organizes the rest of this guide: &lt;em&gt;a raw hash is not a MAC, and raw CBC-MAC is not a MAC for variable-length messages.&lt;/em&gt; Both fixes point the same direction -- stop exposing resumable internal state, and make the tag depend on length. From here the field split into two lineages. Which one wins?&lt;/p&gt;
&lt;h2&gt;5. The Evolution: Two Lineages Diverge&lt;/h2&gt;
&lt;p&gt;The field did not pick one winner. It split into two families that both survive today, and choosing between them is still the single most consequential decision a practitioner makes about authentication. One family keeps a computational assumption and buys a reusable key; the other keeps the 1981 one-time pad and buys blazing speed.&lt;/p&gt;
&lt;h3&gt;The PRF lineage: HMAC and CMAC&lt;/h3&gt;
&lt;p&gt;Bellare, Canetti, and Krawczyk fixed the secret-prefix disaster in 1996 by nesting the hash so the attacker never sees a resumable inner state [@bck-1996]. Standardized a year later as RFC 2104, HMAC computes two keyed passes [@rfc-2104]:&lt;/p&gt;
&lt;p&gt;$$\mathsf{HMAC}(k, m) ;=; H\big((k \oplus \text{opad}) ,\Vert, H((k \oplus \text{ipad}) ,\Vert, m)\big).$$&lt;/p&gt;
&lt;p&gt;The outer hash re-absorbs the inner digest, so a resumed inner state is useless -- length extension cannot reach the output.&lt;/p&gt;

RFC 2104 also fixes how the key becomes block-sized before those two XORs: a key longer than the hash&apos;s block size is first hashed down to a digest, and a shorter key is zero-padded up to the block size [@rfc-2104]. That is why HMAC accepts a key of any length -- it normalizes every key to exactly one block before mixing in `ipad` and `opad`.

flowchart TD
    K[Secret key k] --&amp;gt; IP[k XOR ipad]
    M[Message m] --&amp;gt; INNER[Inner hash of ipad key then message]
    IP --&amp;gt; INNER
    INNER --&amp;gt; ID[Inner digest, never emitted directly]
    K --&amp;gt; OP[k XOR opad]
    ID --&amp;gt; OUTER[Outer hash of opad key then inner digest]
    OP --&amp;gt; OUTER
    OUTER --&amp;gt; T[HMAC tag]
&lt;p&gt;The deepest fact about HMAC is also the most misunderstood, and it took another decade to prove. In 2006 Bellare showed HMAC is a pseudorandom function under the &lt;em&gt;sole&lt;/em&gt; assumption that the hash&apos;s compression function is a PRF -- collision resistance is not required at all [@bellare-2006].&lt;/p&gt;

A keyed function is a PRF if no efficient adversary, allowed to query it on inputs of its choice, can distinguish its outputs from those of a truly random function. A PRF makes an immediate MAC: to forge a tag on a fresh message is to predict a value that, by assumption, looks uniformly random, which succeeds only with negligible probability. Unforgeability falls out of pseudorandomness.
&lt;p&gt;This is why the collision breaks of MD5 and SHA-1 never touched HMAC-MD5 or HMAC-SHA-1. Collisions attack collision resistance; HMAC never needed it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The folklore &quot;HMAC needs a collision-resistant hash&quot; is false, and it has been false since 2006 [@bellare-2006]. HMAC needs the compression function to behave like a PRF -- a different, and for MD5 and SHA-1 still-standing, assumption. This is not a licence to use MD5 in new systems, but it is why the practical world did not catch fire when SHA-1 collisions arrived.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The block-cipher branch got the same repair. CMAC, from Iwata and Kurosawa&apos;s OMAC and standardized as NIST SP 800-38B, derives two subkeys from the block cipher and mixes one into the final block, killing the variable-length splice that sank raw CBC-MAC while keeping a single key [@omac-2003] [@sp-800-38b]. It is the block-cipher PRF-MAC, useful where a device already has AES hardware but no hash.&lt;/p&gt;
&lt;p&gt;The survival of HMAC-SHA-1 is the cleanest real-world confirmation of the 2006 proof: SHA-1&apos;s collision resistance collapsed publicly, yet no HMAC-SHA-1 forgery followed, because the security reduction never invoked collision resistance in the first place [@bellare-2006].&lt;/p&gt;
&lt;h3&gt;The sponge refinement: KMAC&lt;/h3&gt;
&lt;p&gt;SHA-3 changed the substrate. A Keccak sponge splits its state into a public rate and a secret capacity, and it never emits the capacity [@fips-202]. That single property makes the HMAC nesting trick unnecessary: you can key a sponge by simply absorbing the key first.&lt;/p&gt;

A sponge function absorbs input into a large internal state divided into a public &quot;rate&quot; (the bytes that interact with input and output) and a secret &quot;capacity&quot; (bytes never revealed). Because the capacity is never output, prepending a key and absorbing it yields a keyed authenticator with no length-extension surface and no double hashing -- the key is protected by the same hidden capacity that protects the hash.
&lt;p&gt;NIST SP 800-185 builds KMAC on cSHAKE this way: prepend the key, absorb, squeeze [@sp-800-185]. Keying is native, domain separation is built in through customization strings, and the tag length is a free parameter because the sponge is an extendable-output function. It is, conceptually, the cleanest MAC in the standard catalogue -- but conceptual cleanliness is not deployment.&lt;/p&gt;

KMAC is a real, standardized primitive [@sp-800-185], implemented in OpenSSL 3.0 and later (as the `KMAC-128`/`KMAC-256` EVP_MAC) [@openssl-kmac], as well as BouncyCastle [@bouncycastle-kmac] and Botan [@botan-kmac]. It is not academic-only. But its production footprint is thin next to HMAC, GMAC, and Poly1305, for reasons that are historical inertia rather than cryptography: SHA-2 and HMAC are welded into TLS, IPsec, JWT, HKDF, and hardware everywhere, and SHA-3 adoption trails. This guide gives KMAC full conceptual treatment and makes no claim about its market share -- no reliable quantitative figure exists in a primary source.
&lt;p&gt;Both HMAC and KMAC share a decisive operational property: a reusable key, no nonce, an unbounded stream of messages under one secret. That is the easy side of assumption one. The other lineage trades it away for speed.&lt;/p&gt;
&lt;h3&gt;The Wegman-Carter lineage: GMAC and Poly1305&lt;/h3&gt;
&lt;p&gt;In 2004 and 2005, two designs dragged the 1981 one-time construction into high-speed networking. Both keep the pattern &lt;code&gt;tag = universal_hash(m) + one_time_pad&lt;/code&gt;, and both solve the &quot;fresh pad per message&quot; problem the same way -- derive the pad from a cipher keystream instead of shipping new randomness.&lt;/p&gt;
&lt;p&gt;GHASH, the core of GCM, is a polynomial universal hash over the finite field $\mathrm{GF}(2^{128})$: it evaluates $\sum_i m_i \cdot H^{,L-i+1}$ at a secret point $H = E_k(0)$, then masks the result with an AES-encrypted counter block [@mcgrew-viega-2004]. Run GCM with an empty plaintext and you get GMAC, an authentication-only MAC over associated data, standardized in NIST SP 800-38D [@sp-800-38d]. Poly1305 evaluates a message polynomial modulo the prime $2^{130}-5$ under a secret $r$, then adds a one-time pad $s$; deployed as ChaCha20-Poly1305, it draws a fresh $(r, s)$ from the cipher keystream for every message [@rfc-8439].&lt;/p&gt;

flowchart TD
    N[Nonce and key] --&amp;gt; KS[Cipher keystream block]
    KS --&amp;gt; R[One-time hash key r]
    KS --&amp;gt; S[One-time pad s]
    M[Message m] --&amp;gt; P[Polynomial hash of m under r]
    R --&amp;gt; P
    P --&amp;gt; ADD[Add the one-time pad s]
    S --&amp;gt; ADD
    ADD --&amp;gt; T[Authentication tag]
    T -.reuse the same nonce.-&amp;gt; LEAK[Two tags cancel the pad, leaking hash of m1 minus hash of m2]
&lt;p&gt;This is where the 1981 constraint re-enters as a deployment contract. The pad is safe only if the $(key, nonce)$ pair never repeats. Reuse it and the pad cancels out of the difference of two tags, exactly as the toy line did in Section 3 -- except now the &quot;hash&quot; is a polynomial an attacker can then start solving.&lt;/p&gt;
&lt;p&gt;{`&lt;/p&gt;
A one-time Wegman-Carter MAC: tag = poly_hash(m, r) + s (mod p). Use (r, s) ONCE.
&lt;p&gt;p = (1 &amp;lt;&amp;lt; 130) - 5                          # the Poly1305 prime, 2**130 - 5&lt;/p&gt;
&lt;p&gt;def poly_hash(msg, r):
    acc = 0
    for i in range(0, len(msg), 16):
        chunk = msg[i:i+16]
        limb  = int.from_bytes(chunk, &quot;little&quot;) + (1 &amp;lt;&amp;lt; (8 * len(chunk)))
        acc   = ((acc + limb) * r) % p
    return acc&lt;/p&gt;
In ChaCha20-Poly1305 both r and s come fresh from the keystream, per message.
&lt;p&gt;r = 0x1bf54941aff6bf4afdb1f2a3c4d5e6f7
s = 0x0102030405060708090a0b0c0d0e0f10&lt;/p&gt;
&lt;p&gt;def one_time_tag(msg):
    return (poly_hash(msg, r) + s) % p&lt;/p&gt;
THE MISTAKE: two messages under the SAME (r, s).
&lt;p&gt;m1, m2 = b&quot;transfer 10 dollars now&quot;, b&quot;transfer 9999 bucks now&quot;
t1, t2 = one_time_tag(m1), one_time_tag(m2)&lt;/p&gt;
The pad s cancels in the difference -- a relation that does not depend on s at all:
&lt;p&gt;lhs = (t1 - t2) % p
rhs = (poly_hash(m1, r) - poly_hash(m2, r)) % p
print(&quot;pad cancelled, tag difference is key-independent:&quot;, lhs == rhs)
`}&lt;/p&gt;
&lt;p&gt;It prints &lt;code&gt;True&lt;/code&gt;: subtract two tags produced under the same one-time key and the secret pad vanishes, handing the attacker a clean algebraic relation in the hash key. That is the first step of the &quot;forbidden attack&quot; the failure catalogue will name -- and it is why the distinction below is not pedantry.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;master key versus one-time key&lt;/strong&gt; split is the heart of the fork. HMAC and KMAC take a &lt;em&gt;master&lt;/em&gt; key and authenticate forever under it. GMAC and Poly1305 take a &lt;em&gt;one-time&lt;/em&gt; key per message; the ChaCha20-Poly1305 keystream manufactures that one-time key so the master key stays put, but the per-message pad is still strictly single-use. Confuse the two and you get a catastrophe, not a slow degradation.&lt;/p&gt;
&lt;p&gt;The whole construction catalogue, side by side:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table 1 -- MAC constructions, and what each one assumes.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Core idea&lt;/th&gt;
&lt;th&gt;Key model&lt;/th&gt;
&lt;th&gt;Strength&lt;/th&gt;
&lt;th&gt;Weakness&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Carter-Wegman one-time [@wc-1981]&lt;/td&gt;
&lt;td&gt;1981&lt;/td&gt;
&lt;td&gt;$\mathsf{UH}(m) \oplus \text{pad}$&lt;/td&gt;
&lt;td&gt;One-time pad per message&lt;/td&gt;
&lt;td&gt;Information-theoretic, no hardness assumption&lt;/td&gt;
&lt;td&gt;Pad is strictly single-use&lt;/td&gt;
&lt;td&gt;Foundational, revived&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret-prefix $H(k \Vert m)$ [@length-ext-wiki]&lt;/td&gt;
&lt;td&gt;1990s&lt;/td&gt;
&lt;td&gt;Prepend key, hash&lt;/td&gt;
&lt;td&gt;Reusable&lt;/td&gt;
&lt;td&gt;Trivial to build&lt;/td&gt;
&lt;td&gt;Length-extension forgeable&lt;/td&gt;
&lt;td&gt;Superseded, insecure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret-suffix $H(m \Vert k)$ [@length-ext-wiki]&lt;/td&gt;
&lt;td&gt;1990s&lt;/td&gt;
&lt;td&gt;Append key, hash&lt;/td&gt;
&lt;td&gt;Reusable&lt;/td&gt;
&lt;td&gt;No length extension&lt;/td&gt;
&lt;td&gt;Reduces to a hash collision&lt;/td&gt;
&lt;td&gt;Superseded, insecure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBC-MAC [@fips-113]&lt;/td&gt;
&lt;td&gt;1985&lt;/td&gt;
&lt;td&gt;CBC chain, keep last block&lt;/td&gt;
&lt;td&gt;Reusable&lt;/td&gt;
&lt;td&gt;First standardized block-cipher MAC&lt;/td&gt;
&lt;td&gt;Variable-length splicing forgery&lt;/td&gt;
&lt;td&gt;Historical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMAC [@rfc-2104]&lt;/td&gt;
&lt;td&gt;1996&lt;/td&gt;
&lt;td&gt;Nested keyed hash&lt;/td&gt;
&lt;td&gt;Reusable, no nonce&lt;/td&gt;
&lt;td&gt;PRF not collision resistance, length-ext immune&lt;/td&gt;
&lt;td&gt;Serial, no line-rate shortcut&lt;/td&gt;
&lt;td&gt;Active, default PRF-MAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CMAC [@sp-800-38b]&lt;/td&gt;
&lt;td&gt;2005&lt;/td&gt;
&lt;td&gt;CBC-MAC plus final subkey&lt;/td&gt;
&lt;td&gt;Reusable, no nonce&lt;/td&gt;
&lt;td&gt;Fixes CBC-MAC with one key&lt;/td&gt;
&lt;td&gt;Block-cipher birthday ceiling&lt;/td&gt;
&lt;td&gt;Active, niche&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GMAC / GHASH [@sp-800-38d]&lt;/td&gt;
&lt;td&gt;2004&lt;/td&gt;
&lt;td&gt;Polynomial hash over $\mathrm{GF}(2^{128})$&lt;/td&gt;
&lt;td&gt;One-time pad per message&lt;/td&gt;
&lt;td&gt;Carry-less-multiply hardware (PCLMULQDQ/PMULL)&lt;/td&gt;
&lt;td&gt;Nonce reuse is fatal&lt;/td&gt;
&lt;td&gt;Active, inside GCM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poly1305 [@rfc-8439]&lt;/td&gt;
&lt;td&gt;2005&lt;/td&gt;
&lt;td&gt;Polynomial hash mod $2^{130}-5$&lt;/td&gt;
&lt;td&gt;One-time key per message&lt;/td&gt;
&lt;td&gt;Fast in pure software, cipher-agnostic&lt;/td&gt;
&lt;td&gt;One-time key per message&lt;/td&gt;
&lt;td&gt;Active, inside ChaCha20-Poly1305&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KMAC [@sp-800-185]&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Keyed sponge, cSHAKE&lt;/td&gt;
&lt;td&gt;Reusable, no nonce&lt;/td&gt;
&lt;td&gt;Native keying, domain separation, XOF tag&lt;/td&gt;
&lt;td&gt;Lightly deployed&lt;/td&gt;
&lt;td&gt;Active, niche&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;You can now build an unforgeable tag two ways: a reusable-key PRF or a one-time-key universal hash. But a perfect tag attached in the wrong place, in the wrong order, or over the wrong bytes is still useless. How do you &lt;em&gt;combine&lt;/em&gt; a MAC with a cipher without reopening the door you just closed?&lt;/p&gt;
&lt;h2&gt;6. Composition Done Right, and the Verification Contract&lt;/h2&gt;
&lt;p&gt;SSL chose one order for combining its cipher and its MAC. SSH chose another. Both were quietly indicted by a theorem years before either was broken in the field. This section is where the second aha moment lands: strength was never the variable -- discipline and order were.&lt;/p&gt;
&lt;p&gt;Bellare and Namprempre formalized the question in 2000 by splitting integrity into two notions [@bn-2000]. Integrity of plaintexts (INT-PTXT) says an attacker cannot make the receiver accept a plaintext the sender never sent. Integrity of ciphertexts (INT-CTXT) is stronger: the attacker cannot even produce a &lt;em&gt;new ciphertext&lt;/em&gt; the receiver will accept. Their result is the load-bearing one: applying the MAC to the ciphertext -- Encrypt-then-MAC -- yields both IND-CCA confidentiality and INT-CTXT, and it is the only one of the three generic orders that reaches those strong notions.&lt;/p&gt;

Encrypt the plaintext, then compute the MAC over the resulting *ciphertext* (plus any associated data and length), and send ciphertext and tag together. On receipt, verify the tag *before* touching the ciphertext; if it fails, discard without decrypting. EtM is the only generic composition order that provably delivers integrity of ciphertexts, and it does so only under two preconditions: the MAC must be strongly unforgeable and the encryption and MAC keys must be independent.
&lt;p&gt;Those two preconditions are not decoration. If the MAC is only EUF-CMA rather than SUF-CMA, the guarantee weakens from INT-CTXT down to INT-PTXT, because a malleable tag lets an attacker produce a different valid ciphertext for the same plaintext [@bn-2000]. And the keys must be independent -- reusing one key for both encryption and authentication voids the proof.&lt;/p&gt;
&lt;p&gt;A year later Krawczyk sharpened the verdict [@krawczyk-2001]. He proved that any secure-channel protocol required to work with &lt;em&gt;any&lt;/em&gt; secure cipher and &lt;em&gt;any&lt;/em&gt; secure MAC must use Encrypt-then-MAC, and he built a perfectly secret cipher that becomes totally insecure under MAC-then-Encrypt -- SSL&apos;s order -- and under Encrypt-and-MAC -- SSH&apos;s order.&lt;/p&gt;

&quot;[A]ny secure channels protocol designed to work with any combination of secure encryption ... and secure MAC must use the encrypt-then-authenticate method.&quot; -- Hugo Krawczyk, The Order of Encryption and Authentication for Protecting Communications, 2001 [@krawczyk-2001]
&lt;p&gt;Here is the precision that separates an expert from a slogan-repeater: Krawczyk &lt;em&gt;also&lt;/em&gt; proved that MAC-then-Encrypt is secure for specific cipher modes, namely &lt;a href=&quot;https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw/&quot; rel=&quot;noopener&quot;&gt;CBC and CTR&lt;/a&gt; [@krawczyk-2001]. MtE is therefore &lt;em&gt;fragile&lt;/em&gt; and &lt;em&gt;not generically secure&lt;/em&gt; -- it holds only when you can prove something extra about the exact cipher -- but it was never unconditionally &quot;broken.&quot; Say &quot;MtE is broken&quot; in an interview and you are wrong; say &quot;MtE is not generically secure, so do not use it in new designs&quot; and you are exactly right.&lt;/p&gt;

flowchart TD
    subgraph EtM[&quot;Encrypt-then-MAC, generically secure&quot;]
        E1[Plaintext] --&amp;gt; E2[Encrypt]
        E2 --&amp;gt; E3[Ciphertext] --&amp;gt; E4[MAC the ciphertext]
        E4 --&amp;gt; E5[Receiver verifies tag first, then decrypts]
    end
    subgraph MtE[&quot;MAC-then-Encrypt, fragile&quot;]
        M1[Plaintext] --&amp;gt; M2[MAC the plaintext]
        M2 --&amp;gt; M3[Encrypt plaintext and tag together]
        M3 --&amp;gt; M4[Receiver decrypts first, only then verifies]
    end
    subgraph EM[&quot;Encrypt-and-MAC, fragile&quot;]
        A1[Plaintext] --&amp;gt; A2[Encrypt]
        A1 --&amp;gt; A3[MAC the plaintext, tag sent in the clear]
        A2 --&amp;gt; A4[Receiver decrypts, and the tag leaks plaintext]
    end
&lt;p&gt;The diagram shows the mechanism behind every &lt;a href=&quot;https://paragmali.com/blog/they-read-your-plaintext-without-breaking-your-cipher-a-fiel/&quot; rel=&quot;noopener&quot;&gt;padding-oracle&lt;/a&gt; and SSH break at once: in the two fragile orders, the receiver must &lt;em&gt;decrypt or parse before it can verify&lt;/em&gt;, so the very act of processing attacker-chosen bytes leaks information. Encrypt-then-MAC verifies first and refuses to decrypt a single byte of an unauthenticated ciphertext.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Authenticate the ciphertext, not the plaintext. Encrypt-then-MAC over the ciphertext -- with an independent key, a strongly unforgeable tag, and coverage of the associated data and length -- is the only generic order that lets the receiver reject a forgery before it decrypts anything. Every composition break in the catalogue is a system that decrypted or parsed before it verified.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Table 2 -- Composition orders, ranked by what they generically guarantee.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Order&lt;/th&gt;
&lt;th&gt;Idea&lt;/th&gt;
&lt;th&gt;Generic security&lt;/th&gt;
&lt;th&gt;Weakness&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;MAC-then-Encrypt [@krawczyk-2001]&lt;/td&gt;
&lt;td&gt;MAC the plaintext, then encrypt both&lt;/td&gt;
&lt;td&gt;Not generic, secure only for CBC or CTR&lt;/td&gt;
&lt;td&gt;Decrypt-before-verify padding and timing oracles&lt;/td&gt;
&lt;td&gt;Superseded, fragile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt-and-MAC [@apw-2009]&lt;/td&gt;
&lt;td&gt;Encrypt, and MAC the plaintext separately&lt;/td&gt;
&lt;td&gt;Not generic&lt;/td&gt;
&lt;td&gt;Tag over plaintext leaks, MAC-covered length field abused&lt;/td&gt;
&lt;td&gt;Superseded, fragile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt-then-MAC [@bn-2000]&lt;/td&gt;
&lt;td&gt;MAC the ciphertext&lt;/td&gt;
&lt;td&gt;IND-CCA plus INT-CTXT&lt;/td&gt;
&lt;td&gt;Needs SUF-CMA, independent keys, constant-time verify&lt;/td&gt;
&lt;td&gt;Active, canonical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD [@rfc-8446]&lt;/td&gt;
&lt;td&gt;EtM internalized plus nonce management&lt;/td&gt;
&lt;td&gt;IND-CCA plus INT-CTXT by construction&lt;/td&gt;
&lt;td&gt;Key-commitment gap&lt;/td&gt;
&lt;td&gt;Active, default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Committing AEAD [@rfc-9771]&lt;/td&gt;
&lt;td&gt;AEAD plus key and context commitment&lt;/td&gt;
&lt;td&gt;Adds commitment&lt;/td&gt;
&lt;td&gt;Still standardizing, cost profiling&lt;/td&gt;
&lt;td&gt;Frontier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The order is only half of correct composition. The other half arrived not from a theorem but from a field disclosure. In 2009 Nate Lawson found that Google&apos;s Keyczar library compared HMAC tags byte by byte and returned as soon as two bytes differed [@keyczar-2009]. That early exit turns verification time into a side channel: a forged tag that matches more leading bytes takes measurably longer to reject, so an attacker can recover the correct tag one byte at a time by timing responses.&lt;/p&gt;
&lt;p&gt;{`&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Q0 -- Yes --&amp;gt; ASCON[Ascon, SP 800-232]
Q0 -- No --&amp;gt; Q1&amp;amp;#123;&quot;Is there a ciphertext to authenticate?&quot;&amp;amp;#125;
Q1 -- No --&amp;gt; HMAC[HMAC-SHA-256, reusable key, no nonce]
Q1 -- Yes --&amp;gt; Q2&amp;amp;#123;&quot;Can you guarantee a unique nonce per key?&quot;&amp;amp;#125;
Q2 -- No --&amp;gt; SIV[AES-GCM-SIV, misuse-resistant]
Q2 -- Yes --&amp;gt; Q3&amp;amp;#123;&quot;Is AES-NI hardware available?&quot;&amp;amp;#125;
Q3 -- Yes --&amp;gt; GCM[AES-GCM, rekey before the per-key ceiling]
Q3 -- No --&amp;gt; CHACHA[ChaCha20-Poly1305]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read the tree as the three assumptions in order. If there is no ciphertext to bind -- key derivation, a token, a one-time password -- use HMAC-SHA-256 with a key of at least 128 bits, a full-length tag, and a constant-time verify; there is no nonce to mismanage, so assumption one is nearly free. This is the right tool for HKDF [@rfc-5869], JWT&apos;s &lt;code&gt;HS256&lt;/code&gt; [@rfc-7518], TOTP, HOTP, and PBKDF2.&lt;/p&gt;
&lt;p&gt;If there &lt;em&gt;is&lt;/em&gt; a ciphertext, do not hand-roll a MAC plus cipher -- reach for an AEAD, which internalizes Encrypt-then-MAC so you cannot mis-order it. Pick ChaCha20-Poly1305 when you lack AES hardware acceleration, especially on mobile and embedded targets, where its per-key budget is effectively unbounded [@rfc-8439]. Pick AES-GCM when you have AES-NI, and rekey before the $2^{23}$-packet ceiling on high-volume links [@rfc-9001]. If you cannot &lt;em&gt;guarantee&lt;/em&gt; nonce uniqueness at scale, step down to AES-GCM-SIV, which degrades gracefully instead of catastrophically on reuse [@rfc-8452].&lt;/p&gt;
&lt;p&gt;Two niche branches sit off the main tree. Use GMAC for authentication-only data, such as MACsec framing, with a guaranteed-unique nonce [@sp-800-38d] [@ieee-802-1ae]. Use KMAC when you are already in a Keccak or SHA-3 stack, or when you need domain separation or variable-length tags [@sp-800-185]. And on constrained or side-channel-sensitive hardware, use Ascon [@sp-800-232].&lt;/p&gt;
&lt;p&gt;Then there are the rules that apply &lt;em&gt;always&lt;/em&gt;, regardless of which box the tree lands in: Encrypt-then-MAC or an AEAD that internalizes it; independent encryption and MAC keys; a constant-time compare; a tag of at least 128 bits with documented truncation floors [@rfc-4868]; authentication that covers the associated data, the length, the version, and the session context; a committing AEAD when one ciphertext might be opened under multiple keys [@bellare-hoang-2022]; and a misuse-resistant AEAD when nonce uniqueness cannot be guaranteed [@rfc-8452].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Review authentication code against the checklist &lt;em&gt;and its negation&lt;/em&gt;. The checklist tells you what a correct design does; the misuse list tells you what every named CVE did instead. Run any authentication path past both -- if it matches even one line of the misuse list, you have found your next vulnerability before an attacker does.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The negation is the misuse catalogue -- each row is a line of the checklist inverted, and it maps one-to-one onto a named break from Section 7.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table 6 -- Common misuse, the break it produced, and the fix.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse in real code&lt;/th&gt;
&lt;th&gt;Named break it caused&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Secret-prefix $H(k \Vert m)$ MAC&lt;/td&gt;
&lt;td&gt;Flickr length extension [@flickr-advisory]&lt;/td&gt;
&lt;td&gt;Use HMAC or KMAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reusing a GCM nonce or Poly1305 one-time key&lt;/td&gt;
&lt;td&gt;Joux, Nonce-Disrespecting [@woot16-bock]&lt;/td&gt;
&lt;td&gt;Guarantee uniqueness or use AES-GCM-SIV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt; or &lt;code&gt;memcmp&lt;/code&gt; tag comparison&lt;/td&gt;
&lt;td&gt;Keyczar timing leak [@keyczar-2009]&lt;/td&gt;
&lt;td&gt;Use a constant-time compare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAC-then-Encrypt or Encrypt-and-MAC in new code&lt;/td&gt;
&lt;td&gt;Lucky Thirteen, POODLE, SSH recovery [@cve-2013-0169]&lt;/td&gt;
&lt;td&gt;Use an AEAD (Encrypt-then-MAC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Over-truncated tags&lt;/td&gt;
&lt;td&gt;Forgery near the $2^{-t}$ floor [@rfc-4868]&lt;/td&gt;
&lt;td&gt;Keep the tag at least 128 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One shared key for encryption and MAC&lt;/td&gt;
&lt;td&gt;Voids the Encrypt-then-MAC proof [@bn-2000]&lt;/td&gt;
&lt;td&gt;Derive independent keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MACing the plaintext instead of the ciphertext&lt;/td&gt;
&lt;td&gt;Padding oracles [@vaudenay-2002]&lt;/td&gt;
&lt;td&gt;MAC the ciphertext&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting AD, length, version, context&lt;/td&gt;
&lt;td&gt;Terrapin [@terrapin-site]&lt;/td&gt;
&lt;td&gt;Bind the full context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Treating &quot;MAC verified&quot; as &quot;key committed&quot;&lt;/td&gt;
&lt;td&gt;Invisible salamanders [@bellare-hoang-2022]&lt;/td&gt;
&lt;td&gt;Use a committing AEAD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Run every authentication path past these ten lines: - Is the tag over the &lt;strong&gt;ciphertext&lt;/strong&gt; (Encrypt-then-MAC), never the plaintext? - Are the encryption and MAC keys &lt;strong&gt;independent&lt;/strong&gt;? - Is the MAC &lt;strong&gt;strongly unforgeable&lt;/strong&gt; (SUF-CMA), not merely EUF-CMA? - Is the tag comparison &lt;strong&gt;constant-time&lt;/strong&gt; (&lt;code&gt;compare_digest&lt;/code&gt;, &lt;code&gt;timingSafeEqual&lt;/code&gt;, &lt;code&gt;ConstantTimeCompare&lt;/code&gt;)? - Is the &lt;strong&gt;whole&lt;/strong&gt; decrypt-and-verify path constant-time, not just the compare? - Is the tag at least &lt;strong&gt;128 bits&lt;/strong&gt;, with any truncation documented against the birthday bound? - For a one-time-key MAC (GMAC, Poly1305), is nonce uniqueness &lt;strong&gt;guaranteed&lt;/strong&gt;, or are you on a misuse-resistant mode? - Does the authenticated data cover &lt;strong&gt;length, version, ordering, and session context&lt;/strong&gt;? - Is there any &lt;strong&gt;&lt;code&gt;H(secret || message)&lt;/code&gt;&lt;/strong&gt; secret-prefix construction? (Replace with HMAC or KMAC.) - Could one ciphertext open under &lt;strong&gt;two keys&lt;/strong&gt;? If so, use a committing AEAD. Any single failure here is a plausible next CVE.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That checklist and its negation are the working payoff of the whole field guide. Before the conclusion ties them back to the thesis, seven specific beliefs produce most of the remaining real-world authentication bugs -- and each is a corollary of the same one sentence.&lt;/p&gt;
&lt;h2&gt;13. Frequently Corrected Misconceptions&lt;/h2&gt;


No. As Section 5 established, Bellare&apos;s 2006 proof rests HMAC&apos;s security on a single property of the underlying compression function -- that it is itself a PRF -- and not on collision resistance [@bellare-2006]. Since a collision attack targets a property the proof never relies on, HMAC-SHA-1 and HMAC-MD5 kept standing when the SHA-1 and MD5 collision breaks landed. Use HMAC-SHA-256 in new systems for margin, but understand that the reason is prudence, not a collision threat.


No, and the difference is the most dangerous one to miss. HMAC takes a reusable key with no nonce. GMAC and Poly1305 are Wegman-Carter one-time-key MACs [@wc-1981]: they need a fresh, unique per-message key, and reusing it is catastrophic, not a gentle degradation. The 184 HTTPS servers caught repeating GCM nonces were forgeable precisely because a one-time construction was treated like a reusable one [@woot16-bock]. Same tag length, completely different key contract.


No. HMAC exists to defeat length extension on Merkle-Damgard hashes, but a Keccak sponge never emits its capacity, so it has no length-extension surface and keys natively -- just use KMAC [@sp-800-185]. In fact you cannot build HMAC on a SHAKE extendable-output function anyway; Python&apos;s `hmac` module notes that XOFs such as SHAKE-128 and SHAKE-256 cannot be used with HMAC [@python-hmac]. KMAC is the SHA-3-native answer.


No -- it is fragile, which is a different and more precise claim. Krawczyk proved MAC-then-Encrypt is *not generically secure*, yet also proved it *is* secure for CBC and CTR modes specifically [@krawczyk-2001]. So it was never unconditionally broken; it is simply deprecated for new designs, where a modern AEAD or Encrypt-then-MAC removes the cipher-specific precondition it depends on. Reach for one of those instead, but do not call MtE &quot;broken.&quot;


Not by itself, and the overstatement matters. A single $(key, nonce)$ reuse immediately leaks a tag relation and the XOR of the two plaintexts [@joux-2006]. Recovering the subkey $H$ outright is a taller order: a single reuse leaves a root-finding equation with several possible solutions for $H$, so narrowing them to the one true value takes at least a second collision, and usually more. So &quot;one reuse recovers the key&quot; is wrong; &quot;one reuse is already a break, and more reuse recovers the key&quot; is right.


No -- there is a real-world counterexample. In 2009 Google&apos;s Keyczar library did a byte-wise tag comparison with an early exit, and that timing difference let an attacker recover valid tags one byte at a time [@keyczar-2009]. The comparison time leaked the secret the MAC existed to protect. Constant-time compare is a correctness requirement, not an optimization, and every major platform ships a named primitive for it.


No. AEAD handles the encrypted channel, but plenty of jobs have no ciphertext to bind and still need authentication. HKDF key derivation [@rfc-5869], JWT&apos;s `HS256` [@rfc-7518], TOTP and HOTP one-time passwords, and PBKDF2 are all standalone HMAC. The standalone MAC did not disappear with AEAD; it moved to the places where you are authenticating keys, tokens, or messages rather than protecting a ciphertext.

&lt;p&gt;Every myth here is a corollary of one sentence, which the conclusion now restates with the whole catalogue behind it.&lt;/p&gt;
&lt;h2&gt;14. The Math Never Broke&lt;/h2&gt;
&lt;p&gt;Return to Flickr, now fully understood. Two researchers forged valid API signatures with no key, no collision, and no cipher work, because Flickr&apos;s secret-prefix &lt;code&gt;H(secret || params)&lt;/code&gt; construction handed out its resumable hash state as the tag [@flickr-advisory]. The hash met its specification exactly. The deployment broke assumption three -- the wrong construction over the wrong bytes -- and that was enough.&lt;/p&gt;
&lt;p&gt;Line up the rest of the catalogue and the pattern is total. Lucky Thirteen and POODLE broke order and timing, decrypting before verifying [@cve-2013-0169] [@cve-2014-3566]. The SSH plaintext-recovery attack broke order, authenticating the plaintext and acting on a MAC-covered length field [@apw-2009]. The 184 nonce-repeating GCM servers broke key discipline, reusing a one-time pad [@woot16-bock]. Terrapin broke context, leaving the transcript and sequence numbers unbound even though the cipher, the MAC, and the order were all correct [@terrapin-site]. Not one of them broke AES, SHA-2, or a polynomial hash. Every single failure broke the contract around the primitive.&lt;/p&gt;
&lt;p&gt;That is the whole thesis, proven by the evidence rather than asserted: a MAC makes forgery hard, and three deployment assumptions -- the right key discipline, a constant-time verify, and composition over the right bytes in the right order binding the right context -- are what keep it hard. The decision tree and the checklist in Section 12 are simply those three assumptions made executable. Carry them, and their negation, into every review.&lt;/p&gt;
&lt;p&gt;The frontier moves the bar again: soon &quot;the tag verified&quot; should also mean &quot;this ciphertext commits to one key and one context,&quot; the committing-AEAD work this series develops further in Part 7. And the deeper machinery lives in the siblings -- &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;the security definitions&lt;/a&gt; of Part 1, &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;the one-time-key generation&lt;/a&gt; of Part 2, the canonicalization discipline of Part 3, the block modes of Part 5, the padding oracles of Part 6, and the AEAD decision matrix of Part 7. This part is the authentication spine that runs through all of them.&lt;/p&gt;

The math never broke; a deployment broke one of three assumptions. Authenticate the ciphertext, the length, and the context -- not just the message.
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;message-authentication-and-safe-composition&quot; keyTerms={[
  { term: &quot;Message Authentication Code (MAC)&quot;, definition: &quot;A keyed algorithm producing a short tag so a shared-key holder can verify a message is authentic and unmodified&quot; },
  { term: &quot;EUF-CMA&quot;, definition: &quot;Existential unforgeability under adaptive chosen-message attack; no forgery on a fresh message after querying tags&quot; },
  { term: &quot;SUF-CMA&quot;, definition: &quot;Strong unforgeability; not even a new valid tag on a previously queried message, the notion Encrypt-then-MAC requires&quot; },
  { term: &quot;Universal hash function&quot;, definition: &quot;A family whose random member collides any fixed pair with small, provable probability, with no hardness assumption&quot; },
  { term: &quot;Pseudorandom function (PRF)&quot;, definition: &quot;A keyed function indistinguishable from random; the sole assumption HMAC&apos;s security rests on&quot; },
  { term: &quot;Keyed sponge&quot;, definition: &quot;A sponge keyed by absorbing the key first; its hidden capacity gives native keying with no length extension, as in KMAC&quot; },
  { term: &quot;Encrypt-then-MAC&quot;, definition: &quot;Compute the tag over the ciphertext and verify before decrypting; the only generically secure composition order&quot; },
  { term: &quot;One-time key&quot;, definition: &quot;The per-message key of a Wegman-Carter MAC (GMAC, Poly1305); reuse is catastrophic, not a slow leak&quot; },
  { term: &quot;Key commitment&quot;, definition: &quot;The property that a ciphertext decrypts validly under only one key; standard AEAD does not provide it&quot; }
]} questions={[
  { q: &quot;What are the three deployment assumptions every MAC relies on?&quot;, a: &quot;Right key discipline for its family, a constant-time verify, and composition over the right bytes and order binding the right context.&quot; },
  { q: &quot;Why did HMAC-SHA-1 survive SHA-1 collisions?&quot;, a: &quot;HMAC&apos;s security reduces to the compression function being a PRF, not to collision resistance, as Bellare proved in 2006.&quot; },
  { q: &quot;Why is reusing a Poly1305 or GMAC nonce catastrophic?&quot;, a: &quot;The one-time pad cancels in the difference of two tags, leaking a key-independent relation that enables forgery.&quot; },
  { q: &quot;Is MAC-then-Encrypt broken?&quot;, a: &quot;No; it is fragile and not generically secure, though Krawczyk proved it secure for CBC and CTR. Avoid it in new designs.&quot; },
  { q: &quot;What does Encrypt-then-MAC require to be secure?&quot;, a: &quot;A strongly unforgeable (SUF-CMA) MAC, independent encryption and MAC keys, a constant-time verify path, and coverage of associated data and length.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>message-authentication</category><category>hmac</category><category>poly1305</category><category>gmac</category><category>authenticated-encryption</category><category>encrypt-then-mac</category><category>applied-cryptography</category><category>aead</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>They Read Your Plaintext Without Breaking Your Cipher: A Field Guide to Padding Oracles</title><link>https://paragmali.com/blog/they-read-your-plaintext-without-breaking-your-cipher-a-fiel/</link><guid isPermaLink="true">https://paragmali.com/blog/they-read-your-plaintext-without-breaking-your-cipher-a-fiel/</guid><description>A padding oracle reads your plaintext without touching your key. Why CBC, Vaudenay, Lucky13, and POODLE are one bug -- and why Encrypt-then-MAC ends it.</description><pubDate>Wed, 08 Jul 2026 20:09:31 GMT</pubDate><content:encoded>
A padding oracle lets an attacker read your plaintext without ever touching your key -- and it is not a flaw in AES, in CBC, or even in PKCS#7. It is what you get whenever a receiver reveals *whether an attacker-chosen ciphertext decrypted to well-formed plaintext* before it has proven that ciphertext authentic. That one leaked bit (&quot;was the padding valid?&quot;), plus CBC&apos;s malleability, recovers plaintext one byte at a time -- about 128 guesses per byte -- as Serge Vaudenay showed in 2002 and Juliano Rizzo and Thai Duong weaponized against ASP.NET in 2010.&lt;p&gt;This field guide follows the &lt;em&gt;same&lt;/em&gt; bug leaking through progressively quieter channels: a loud error (Vaudenay), coarse timing (Canvel), statistical timing under unified errors (Lucky Thirteen), SSL 3.0&apos;s unchecked padding (POODLE), and the oracle re-created by Lucky Thirteen&apos;s own fix (CVE-2016-2107). Every point fix lost. The single structural cure is to invert the order: &lt;strong&gt;authenticate before you decrypt-and-unpad.&lt;/strong&gt; Encrypt-then-MAC proves it attains IND-CCA plus INT-CTXT; AEAD, mandated by TLS 1.3, ships it with no knob to misset. The article ends with ranked decision rules and a design-review checklist for 2026.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;1. The Break That Never Touched the Key&lt;/h2&gt;
&lt;p&gt;In September 2010, two researchers pointed a few thousand malformed requests at an ordinary ASP.NET application and walked away with its authentication tickets and its &lt;code&gt;web.config&lt;/code&gt; -- the site&apos;s master secrets -- without ever attacking its AES-256 encryption or going near the key [@rizzo_duong_woot2010]. It had done nothing more than return one error when a decrypted request had bad padding and a different error when the padding was fine but the data was not. That one-bit difference is a decryption machine: the attackers never broke the cipher, they turned the server&apos;s error handling into the decryption function.&lt;/p&gt;
&lt;p&gt;Sit with the paradox, because the rest of this article lives inside it. AES-256 was, and is, intact. Nobody factored anything, guessed a key, or exploited a weakness in the block cipher&apos;s rounds. The plaintext walked out the door one byte at a time purely because the server answered a question it should never have been willing to answer: &lt;em&gt;was this ciphertext, which I did not create, well-formed after I decrypted it?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Juliano Rizzo and Thai Duong automated that question against &lt;code&gt;WebResource.axd&lt;/code&gt; and &lt;code&gt;ScriptResource.axd&lt;/code&gt; handlers and recovered Forms-authentication tickets and server files. The United States National Vulnerability Database catalogs it as CVE-2010-3332, the &quot;ASP.NET Padding Oracle Vulnerability,&quot; and Microsoft shipped an out-of-band patch to close it [@cve_2010_3332]. This was not a lab curiosity. It was, at the time, the largest deployed instance of a bug that had been sitting in plain sight for eight years.&lt;/p&gt;

A padding oracle is an attack class, not a cipher weakness. A receiver decrypts an attacker-chosen ciphertext, checks whether the recovered plaintext has valid padding, and reveals the verdict -- through an error message, a timing difference, or any observable behavior. That single accept/reject bit, repeated, becomes a plaintext-recovery oracle that never attacks the underlying cipher or key.
&lt;p&gt;The reframe is worth stating in the sharpest possible terms, because once you hold it you will see this bug everywhere.&lt;/p&gt;

The attacker never attacks the cipher. They turn the receiver&apos;s error handling into the decryption function -- &quot;decrypt, then validate&quot; becomes &quot;decrypt, then confess.&quot;
&lt;p&gt;This is Part 6 of a field guide for protocol designers. It assumes the malleability of CBC mode from Part 5 (the lever the attack pulls) and the &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;chosen-ciphertext adversary&lt;/a&gt; from Part 1 (this &lt;em&gt;is&lt;/em&gt; that adversary, deployed and winning). By the end you will carry one diagnostic sentence and drop every named break in this article -- Vaudenay, Lucky Thirteen, POODLE, and tomorrow&apos;s application-layer oracle -- onto it on sight: &lt;em&gt;when a ciphertext you did not create arrives, what does the receiver reveal about it before it has proven the ciphertext authentic?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If AES was intact and the key was never touched, then the machine that recovered the plaintext was built entirely out of the server&apos;s own responses. That idea did not begin with CBC, or even with symmetric cryptography. To see the atom clearly, go back to the first time anyone realized that a receiver&apos;s verdict on validity is itself a decryption function.&lt;/p&gt;
&lt;h2&gt;2. A Validity Check Is a Decryption Oracle&lt;/h2&gt;
&lt;p&gt;The year is 1998, the place is Bell Labs, and Daniel Bleichenbacher is staring at an SSL server that is far too honest. When the server receives an RSA-encrypted message, it decrypts and checks whether the result has the structure that the PKCS #1 v1.5 standard demands. If the structure is wrong, it says so. Bleichenbacher realized that this single yes/no answer -- &lt;em&gt;does the decrypted message have valid PKCS #1 formatting?&lt;/em&gt; -- is enough to recover the plaintext.&lt;/p&gt;
&lt;p&gt;With roughly $2^{20}$ adaptively chosen queries, he could extract an RSA-protected session key without factoring the modulus and without ever learning the private key [@bleichenbacher1998]. The server&apos;s &quot;is this well-formed?&quot; answer &lt;em&gt;was&lt;/em&gt; the attacker&apos;s decryption function. This is the seed of everything that follows.&lt;/p&gt;
&lt;p&gt;It is also the moment to nail down a boundary that expert readers will check for. Bleichenbacher&apos;s attack is an &lt;strong&gt;RSA PKCS #1 v1.5&lt;/strong&gt; oracle -- a different primitive with different arithmetic. It is the conceptual ancestor of the CBC padding-oracle class &quot;in spirit,&quot; but it is never itself a CBC padding oracle [@bleichenbacher1998].Calling Bleichenbacher &quot;the first CBC padding oracle&quot; is a common and consequential error. His target is RSA public-key encryption; the CBC class is symmetric. The shared idea -- a structural validity verdict on attacker-chosen ciphertext leaks the plaintext -- is what makes him the ancestor, not the mechanism. The distinction matters because the family tree has two branches, and confusing them muddles the fix.&lt;/p&gt;

In an adaptive chosen-ciphertext attack, the adversary submits ciphertexts of its choosing and learns something from the receiver&apos;s reaction to each one. A scheme is IND-CCA2 secure only if those reactions leak nothing that helps distinguish or recover plaintext. A padding oracle is precisely a receiver whose reaction leaks -- so it is a live, deployed IND-CCA2 break, the security notion this series defines in Part 1.
&lt;p&gt;The whole subject lives inside that definition. If a receiver&apos;s response to a ciphertext it did not create tells the attacker anything, the game is already lost -- the only question is how expensive the win is.&lt;/p&gt;

Bleichenbacher&apos;s oracle never died; it changed clothes. **DROWN** (2016) revived it by using a server&apos;s SSLv2 support to attack modern TLS [@drown_attack2016]. **ROBOT** (2017 to 2018) found the same PKCS #1 v1.5 oracle live in load balancers and TLS stacks from major vendors, nearly two decades on [@robot_2018]. And the **Marvin Attack** (2023) showed the timing variant still breaks RSA implementations &quot;previously thought immune&quot; [@marvin_attack2023]. All three are RSA-side breaks -- a different primitive from the CBC story told here -- and they appear only as this signpost so nobody mistakes them for CBC oracles. The lesson each one re-teaches is the same: a validity verdict on attacker-chosen input is a decryption oracle, whatever the cipher.
&lt;p&gt;Four years after Bleichenbacher, Serge Vaudenay carried the idea across the aisle from public-key to symmetric cryptography. In &quot;Security Flaws Induced by CBC Padding,&quot; presented at EUROCRYPT 2002, he showed that a receiver which decrypts a CBC ciphertext and reveals whether the padding is valid becomes a plaintext-recovery oracle against SSL, IPSEC, and WTLS [@vaudenay2002]. This is the founding result of the class -- the moment &quot;confidentiality without integrity&quot; was shown to be, in the general case, a decryption oracle waiting to be asked.&lt;/p&gt;
&lt;p&gt;The object it manipulates had arrived on schedule: PKCS #7 padding, the append-&lt;code&gt;n&lt;/code&gt;-bytes-each-equal-to-&lt;code&gt;n&lt;/code&gt; scheme, was standardized as RFC 2315 in the same era, so the manipulable surface and the attack template appeared together [@rfc2315]. Vaudenay&apos;s procedure is documented in cryptography&apos;s standard texts as the canonical illustration of chosen-ciphertext insecurity [@katz_lindell_imc].&lt;/p&gt;

timeline
    title Padding oracles, 1998 to 2022
    1998 : Bleichenbacher RSA PKCS number 1 oracle
    2000 : Bellare-Namprempre composition theorem
    2002 : Vaudenay CBC padding oracle
    2003 : Canvel timing oracle vs TLS
    2010 : ASP.NET break by Rizzo and Duong
    2013 : Lucky Thirteen statistical timing
    2014 : POODLE and RFC 7366 Encrypt-then-MAC
    2016 : CVE-2016-2107, the fix re-creates the oracle
    2018 : TLS 1.3 removes CBC, mandates AEAD
    2022 : BCP 195 recommends against CBC
&lt;p&gt;Vaudenay&apos;s real contribution was noticing that CBC makes the symmetric version &lt;em&gt;cleaner&lt;/em&gt; than Bleichenbacher&apos;s RSA original: flip a byte, watch the verdict, recover a byte. To believe that -- and to earn the right to use it on every later break -- you have to see the arithmetic. It is simpler than it sounds, and once you see it you cannot un-see it in any decrypt-then-check system you review.&lt;/p&gt;
&lt;h2&gt;3. The Atom: Vaudenay&apos;s Oracle, Worked Byte by Byte&lt;/h2&gt;
&lt;p&gt;Everything in this article is one move, performed once here and then reused by every named break with only the &lt;em&gt;channel&lt;/em&gt; changed. Derive it in full and the rest of the piece can move fast. There are three steps: understand the padding, understand the lever, then watch the recovery.&lt;/p&gt;
&lt;h3&gt;Step one: three padding formats, disambiguated once&lt;/h3&gt;
&lt;p&gt;Block ciphers operate on whole blocks, so a message that does not fill the last block must be padded, and the padding must be self-describing so the receiver can strip it. PKCS #7 is the canonical scheme.&lt;/p&gt;

To pad a message to a block boundary, append `n` bytes, each equal to the value `n`. Need three bytes of padding? Append `03 03 03`. Need one? Append `01`. If the message is already block-aligned, append a *full extra block* of padding (for a 16-byte AES block, sixteen bytes of `10`). To unpad, read the final byte `n`, verify that the last `n` bytes all equal `n`, and strip them. Standardized in RFC 2315 [@rfc2315].
&lt;p&gt;Here is the trap that catches expert readers, so disambiguate it now and never again: not every named attack operates on literal PKCS #7 bytes. Three formats matter, and mitigations for one do not automatically cover another.&lt;/p&gt;
&lt;p&gt;PKCS #7 (RFC 2315) requires every pad byte to equal the pad length [@rfc2315]. TLS 1.0 through 1.2 use a PKCS #7-&lt;em&gt;like&lt;/em&gt; scheme where every pad byte equals &lt;code&gt;length - 1&lt;/code&gt;, and -- this detail will return with Lucky Thirteen -- the record is MAC&apos;d, &lt;em&gt;then&lt;/em&gt; padded, &lt;em&gt;then&lt;/em&gt; encrypted [@rfc5246]. SSL 3.0 is the dangerous outlier: its pad bytes are &lt;em&gt;arbitrary&lt;/em&gt;, and only the final length byte is constrained, so there is effectively no padding content to check at all [@rfc6101].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Padding format&lt;/th&gt;
&lt;th&gt;Pad-byte rule&lt;/th&gt;
&lt;th&gt;What the receiver checks&lt;/th&gt;
&lt;th&gt;Break that targets it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;PKCS #7 (RFC 2315)&lt;/td&gt;
&lt;td&gt;Append &lt;code&gt;n&lt;/code&gt; bytes each equal to &lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Length byte and every pad byte&lt;/td&gt;
&lt;td&gt;Vaudenay&apos;s canonical oracle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS 1.0 to 1.2 (RFC 5246)&lt;/td&gt;
&lt;td&gt;Every pad byte equals &lt;code&gt;length - 1&lt;/code&gt;; MAC then pad then encrypt&lt;/td&gt;
&lt;td&gt;Length and pad bytes, after decrypt&lt;/td&gt;
&lt;td&gt;Lucky Thirteen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL 3.0 (RFC 6101)&lt;/td&gt;
&lt;td&gt;Pad bytes arbitrary; only the final length byte constrained&lt;/td&gt;
&lt;td&gt;Length byte only; pad bytes unchecked&lt;/td&gt;
&lt;td&gt;POODLE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The DES-era ancestor is PKCS #5, re-published as RFC 8018, which is the same scheme fixed at an 8-byte block; PKCS #7 generalizes it to any block size up to 255 [@rfc8018]. The two names describe one idea, which is why you see both in old code.&lt;/p&gt;
&lt;p&gt;That single table is why &quot;just make the padding check uniform&quot; was never a complete fix -- it says nothing about SSL 3.0, where there is no pad content to make uniform.&lt;/p&gt;
&lt;h3&gt;Step two: CBC malleability, the lever&lt;/h3&gt;

In CBC decryption, each plaintext block is $P_i = D_k(C_i) \oplus C_{i-1}$: decrypt the current ciphertext block with the block cipher, then XOR the previous ciphertext block. Because that XOR is the last step, an attacker who flips a bit in $C_{i-1}$ deterministically flips the *same* bit in $P_i$ -- steering the plaintext without any knowledge of the key. Part 5 derives this in full; here it is the lever the oracle pulls.

flowchart TD
    Ci[&quot;Target ciphertext block Ct&quot;] --&amp;gt; Dk[&quot;Block cipher decryption Dk&quot;]
    Dk --&amp;gt; Inter[&quot;Intermediate block, equals Dk of Ct&quot;]
    Cprev[&quot;Previous ciphertext block, attacker controls it&quot;] --&amp;gt; XORnode((&quot;XOR&quot;))
    Inter --&amp;gt; XORnode
    XORnode --&amp;gt; Pt[&quot;Recovered plaintext block Pt&quot;]
    Attacker[&quot;Attacker flips byte j of the previous block&quot;] -.-&amp;gt; Cprev
    Attacker -.-&amp;gt; Result[&quot;Byte j of Pt flips by the same amount, no key needed&quot;]
&lt;p&gt;Hold the two pieces together: the receiver will tell you whether a decrypted block has valid padding, and you can steer any byte of that decrypted block by editing the block in front of it. Those two facts are a decryption oracle.&lt;/p&gt;
&lt;h3&gt;Step three: the recovery, last byte first&lt;/h3&gt;
&lt;p&gt;Take a target ciphertext block $C_t$ whose plaintext you want. Prepend a block $C&apos;$ that you fully control, and ask the receiver to decrypt the pair. The receiver computes an intermediate block $I = D_k(C_t)$ and returns the plaintext $P = I \oplus C&apos;$, then checks its padding. You do not know $I$, but you can search for it one byte at a time.&lt;/p&gt;
&lt;p&gt;Fix your attention on the last byte. Vary $C&apos;$&apos;s last byte over all 256 possible values and watch the verdict. For exactly one value, the decrypted last byte becomes &lt;code&gt;0x01&lt;/code&gt;, which is valid single-byte padding, and the receiver accepts. At that instant you know that $I[\text{last}] \oplus C&apos;[\text{last}] = \texttt{0x01}$, so the intermediate byte is $I[\text{last}] = C&apos;[\text{last}] \oplus \texttt{0x01}$. XOR that against the &lt;em&gt;real&lt;/em&gt; previous ciphertext byte and you have recovered a true plaintext byte -- and AES was never touched.&lt;/p&gt;
&lt;p&gt;One honest wrinkle hides inside &quot;about 128 guesses.&quot; A guess can occasionally produce valid &lt;em&gt;longer&lt;/em&gt; padding by luck -- for instance if the plaintext already ended in &lt;code&gt;0x02&lt;/code&gt;, a crafted byte could yield &lt;code&gt;02 02&lt;/code&gt;. So the careful attacker perturbs the second-to-last byte to confirm the intended &lt;code&gt;01&lt;/code&gt; interpretation. That rare double-check is why the average is ~128 rather than a clean 128, and why worst-case is 256.&lt;/p&gt;
&lt;p&gt;To peel the next byte, target two-byte padding: set the known last byte to decrypt to &lt;code&gt;0x02&lt;/code&gt; (you can, because you now know its intermediate), and brute-force the second-to-last byte until the receiver sees &lt;code&gt;02 02&lt;/code&gt;. March inward -- &lt;code&gt;03 03 03&lt;/code&gt;, then &lt;code&gt;04 04 04 04&lt;/code&gt; -- and a full block falls. The cost is about &lt;strong&gt;128 guesses per byte on average and 256 in the worst case&lt;/strong&gt; [@vaudenay2002].&lt;/p&gt;

sequenceDiagram
    participant A as Attacker
    participant O as Oracle receiver
    Note over A,O: Goal, learn the last byte of target block Ct
    A-&amp;gt;&amp;gt;O: Send chosen block Cprime, then Ct
    O-&amp;gt;&amp;gt;O: Decrypt, inspect last-byte padding
    O--&amp;gt;&amp;gt;A: invalid padding
    Note over A: Increment last byte of Cprime, retry
    A-&amp;gt;&amp;gt;O: The value making the last plaintext byte 0x01
    O-&amp;gt;&amp;gt;O: Padding parses as one valid byte
    O--&amp;gt;&amp;gt;A: valid padding
    Note over A,O: Intermediate byte is that guess XOR 0x01
    Note over A: True plaintext byte is intermediate XOR real previous byte
&lt;p&gt;Run the atom yourself. The simulation below models the oracle over a fixed intermediate block -- no real cipher, so you can watch the one-in-256 accept that leaks a byte.&lt;/p&gt;
&lt;p&gt;{`
// A toy padding oracle over a fixed 16-byte &quot;intermediate&quot; block.
// No real crypto: we model I = D_k(C_t) as a known array so you can watch
// the 1-in-256 accept that leaks one plaintext byte.
const BLOCK = 16;&lt;/p&gt;
&lt;p&gt;// Pretend these are the secret intermediate bytes I = D_k(C_t).
const intermediate = [0x8a,0x2f,0x11,0x77,0x4c,0x93,0xe0,0x05,
                      0xbb,0x6d,0x19,0xa4,0x3c,0xf8,0x52,0x7e];
// The real previous-ciphertext block (known to the attacker).
const realPrev = [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
                  0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10];&lt;/p&gt;
&lt;p&gt;// Oracle: is the last decrypted byte valid 0x01 padding?
function oracle(cprime) {
  const lastPlain = intermediate[BLOCK-1] ^ cprime[BLOCK-1];
  return lastPlain === 0x01;   // the single leaked bit
}&lt;/p&gt;
&lt;p&gt;// Attack: brute-force the last byte of a controlled block.
let guesses = 0;
const cprime = new Array(BLOCK).fill(0);
for (let g = 0; g &amp;lt; 256; g++) {
  guesses++;
  cprime[BLOCK-1] = g;
  if (oracle(cprime)) {
    const intByte = g ^ 0x01;                    // I[last] = guess XOR 0x01
    const plainByte = intByte ^ realPrev[BLOCK-1];
    console.log(&quot;Accepted after &quot; + guesses + &quot; guesses&quot;);
    console.log(&quot;Recovered intermediate byte: 0x&quot; + intByte.toString(16));
    console.log(&quot;Recovered plaintext byte:    0x&quot; + plainByte.toString(16));
    break;
  }
}
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; One reliable accept/reject bit on attacker-chosen ciphertext, plus CBC&apos;s malleability, is a complete decryption function -- about 128 guesses per byte, and the key is never touched. Every named break in the rest of this article is that same atom, with only the channel that carries the bit changed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is Aha number one, and it dismantles the mental model most engineers start with. AES-256&apos;s strength was never the variable. The variable was what the receiver was willing to say about a ciphertext it had not authenticated. Before moving on, one point of vocabulary, because it changes how you diagnose the bug.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A padding oracle is an &lt;em&gt;attack class&lt;/em&gt; -- a side channel against a &lt;em&gt;construction&lt;/em&gt; (CBC-mode encryption composed with a MAC), not a weakness in a primitive. The block cipher (AES) and the MAC (HMAC) are the primitives, and they are doing exactly what they promise. The oracle lives in how they are composed and in what the receiver reveals. Say &quot;attack class&quot; and &quot;construction,&quot; never &quot;the padding-oracle primitive.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The atom needs exactly one thing from the receiver: a distinguishable answer to &quot;was the padding valid?&quot; Vaudenay read that answer off a loud error message. The next fifteen years are the story of the field muffling that answer, one channel at a time -- and the same bit escaping through the next-quietest one every single time.&lt;/p&gt;
&lt;h2&gt;4. One Distinguisher, Four Quieter Channels&lt;/h2&gt;
&lt;p&gt;The atom never changes. What changes is &lt;em&gt;how the accept/reject bit is observed&lt;/em&gt; -- and each time the field silences one channel, the bit re-emerges through a quieter one. Here is the entire failure catalog as one descending staircase, with POODLE branching off to the side.&lt;/p&gt;

flowchart TD
    A[&quot;Loud padding-error message (Vaudenay, ASP.NET)&quot;] --&amp;gt;|&quot;Unify the alert&quot;| B[&quot;Coarse timing gap (Canvel)&quot;]
    B --&amp;gt;|&quot;Compute the MAC anyway&quot;| C[&quot;Statistical timing (Lucky Thirteen)&quot;]
    C --&amp;gt;|&quot;Constant-time AES-NI fast path&quot;| D[&quot;Fix re-creates the oracle (CVE-2016-2107)&quot;]
    D --&amp;gt;|&quot;Verify authenticity first&quot;| F[&quot;Encrypt-then-MAC and AEAD, no verdict to leak&quot;]
    P[&quot;Unchecked padding by spec (POODLE, SSL 3.0)&quot;] --&amp;gt;|&quot;Retire SSL 3.0 and CBC&quot;| F
&lt;p&gt;&lt;strong&gt;Channel one -- the loud error message (Vaudenay 2002, ASP.NET 2010).&lt;/strong&gt; The first receivers simply returned a distinct &quot;padding error,&quot; trivially observable. Vaudenay read it straight off the response, and eight years later Rizzo and Duong read it off differing HTTP responses from ASP.NET applications, recovering authentication tickets and &lt;code&gt;web.config&lt;/code&gt; at scale [@rizzo_duong_woot2010] [@cve_2010_3332]. The obvious countermeasure: return one indistinguishable error for both padding failures and data failures. That closes the loudest channel and opens the next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Channel two -- coarse timing (Canvel et al. 2003).&lt;/strong&gt; Once OpenSSL returned the &lt;em&gt;same&lt;/em&gt; &lt;code&gt;bad_record_mac&lt;/code&gt; alert for padding and MAC failures, Brice Canvel, Alain Hiltgen, Serge Vaudenay, and Martin Vuagnoux showed the verdict still leaked through &lt;em&gt;when&lt;/em&gt; the alert arrived: a receiver that accepted the padding went on to compute the MAC, while one that rejected padding could bail early, and the resulting millisecond gap reconstituted the oracle [@canvel2003]. They intercepted a password over a live OpenSSL TLS channel. This is the first concrete demonstration of the pattern that organizes the whole subject. The standards response, TLS 1.1 (RFC 4346), mandated uniform alert handling and explicit per-record initialization vectors [@rfc4346]. The countermeasure: make the decryption path constant-time -- compute the MAC even on padding failure so both paths take equal time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Channel three -- statistical timing, Lucky Thirteen (AlFardan and Paterson, 2013).&lt;/strong&gt; This is the break that proved constant-time is &lt;em&gt;the&lt;/em&gt; hard problem, and it is worth binding tightly to the thesis, because it is not merely &quot;a timing attack.&quot; TLS uses MAC-then-encrypt with a specific receiver order: decrypt, &lt;em&gt;strip the padding&lt;/em&gt;, then run HMAC over what remains. The trouble is that &quot;what remains&quot; has a length that depends on how many bytes the receiver treated as padding -- and HMAC&apos;s running time depends on that length, because the hash compression function runs one extra time at certain length thresholds. So the attacker&apos;s &lt;em&gt;guess about the padding&lt;/em&gt; changes the MAC-computation time by a few tens of CPU cycles [@lucky13_2013].&lt;/p&gt;

A comparison (or, more broadly, a code path) is constant-time when its running time does not depend on secret data or on how far into the data a difference occurs. A tag check that returns early on the first mismatched byte leaks *where* the mismatch is; a constant-time check XORs all bytes and inspects the accumulated difference once. Lucky Thirteen forced constant-time unpadding-and-MAC on the whole industry, and CVE-2016-2107 shows how easily a hand-written fast path reintroduces the oracle -- there, through a missing length check that produced a distinguishable alert rather than a timing gap.
&lt;p&gt;Nadhem AlFardan and Kenneth Paterson amplified that sub-microsecond difference statistically across the network, recovering a full plaintext block in about $2^{23}$ TLS sessions, dropping to roughly $2^{13}$ sessions per byte with favorable positioning, across OpenSSL, GnuTLS, PolarSSL, and more (CVE-2013-0169) [@lucky13_2013] [@cve_2013_0169]. Bind it to the ordering: MAC-then-encrypt &lt;em&gt;forces&lt;/em&gt; a length-dependent MAC, so the timing leak is a property of the order of operations, not a stray implementation slip.The name &quot;Lucky Thirteen&quot; comes from the 13 bytes TLS feeds into the HMAC before the record body: a 5-byte TLS record header plus an 8-byte sequence number. Their alignment against the 64-byte hash-compression boundary is exactly what opens the timing gap. A number that sounds whimsical is really a byte-counting accident. The next countermeasure was obvious and genuinely hard: make the MAC and the unpadding constant-time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Channel four -- unchecked structure, POODLE (Moller, Duong, and Kotowicz 2014).&lt;/strong&gt; Here the staircase forks into a parallel branch. SSL 3.0&apos;s pad bytes are &lt;em&gt;arbitrary by specification&lt;/em&gt; -- only the final length byte is constrained -- so there is no padding check to harden, no message to unify, no timing to flatten [@rfc6101]. An attacker who maneuvers a target byte into the last position of a block learns it from &lt;em&gt;MAC pass or fail alone&lt;/em&gt;, needing neither an error message nor a timing measurement, at an expected cost of 256 SSL 3.0 requests per byte [@poodle_writeup] [@cve_2014_3566]. The name expands to Padding Oracle On Downgraded Legacy Encryption, and this is where a persistent misconception must be corrected.POODLE&apos;s downgrade is the &lt;em&gt;enabler&lt;/em&gt;, not the bug. The downgrade only forces a modern client back onto vulnerable SSL 3.0; the vulnerability is the unchecked pad. The proof is TLS-POODLE (CVE-2014-8730), which hit TLS stacks that copied SSL 3.0&apos;s lax padding check and works with &lt;em&gt;no downgrade at all&lt;/em&gt; [@cve_2014_8730]. Frame POODLE as a padding oracle first, a downgrade second. Google&apos;s write-up and the accompanying announcement documented the mechanism and effort in full [@poodle_writeup] [@google_poodle_blog]. Because there is no check to make uniform, the only fix is to remove SSL 3.0, and CBC, from the wire entirely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Channel five -- the oracle the fix re-created (CVE-2016-2107, 2016).&lt;/strong&gt; This is the single clearest proof of the thesis. OpenSSL&apos;s AES-NI assembly fast path, written to make the Lucky Thirteen countermeasure constant-time, mishandled a length check -- and re-opened a padding oracle. It was found with the TLS-Attacker fuzzing methodology [@somorovsky_fuzzing2016], and the National Vulnerability Database records the cause in words worth quoting exactly.&lt;/p&gt;

This vulnerability [CVE-2016-2107] exists because of an incorrect fix for CVE-2013-0169.
&lt;p&gt;The constant-time patch for Lucky Thirteen re-created the very oracle it was written to close, and OpenSSL shipped the repair in 1.0.2h and 1.0.1t [@cve_2016_2107] [@openssl_secadv_20160503]. It did not travel alone. Amazon&apos;s s2n library added a Lucky Thirteen countermeasure that still leaked, revived as Lucky Microseconds by Martin Albrecht and Kenneth Paterson -- and the randomized delays s2n had added as masking were part of the problem [@lucky_microseconds2016].&lt;/p&gt;
&lt;p&gt;Then, in 2019, Craig Young&apos;s Zombie POODLE and GOLDENDOODLE and a large Internet scan by Robert Merget and colleagues found CBC padding oracles still live in 1.83% of the Alexa Top Million, distinguishable by the &lt;em&gt;content&lt;/em&gt; of server responses with no precise timing required at all [@merget2019] [@qualys_zombie_poodle2019].&lt;/p&gt;
&lt;p&gt;Zombie POODLE is an invalid-padding, valid-MAC oracle; GOLDENDOODLE is the mirror image, a valid-padding, invalid-MAC oracle. Different verdicts, same atom.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Leak channel&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Primary source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Vaudenay&lt;/td&gt;
&lt;td&gt;2002&lt;/td&gt;
&lt;td&gt;Distinct padding error&lt;/td&gt;
&lt;td&gt;MtE-CBC observable branch&lt;/td&gt;
&lt;td&gt;[@vaudenay2002]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canvel et al.&lt;/td&gt;
&lt;td&gt;2003&lt;/td&gt;
&lt;td&gt;Coarse timing&lt;/td&gt;
&lt;td&gt;MAC computed vs skipped&lt;/td&gt;
&lt;td&gt;[@canvel2003]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ASP.NET (Rizzo, Duong)&lt;/td&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;Distinct HTTP error&lt;/td&gt;
&lt;td&gt;Padding vs data error revealed&lt;/td&gt;
&lt;td&gt;[@rizzo_duong_woot2010] [@cve_2010_3332]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lucky Thirteen&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Statistical timing&lt;/td&gt;
&lt;td&gt;Strip-then-MAC length dependence&lt;/td&gt;
&lt;td&gt;[@lucky13_2013] [@cve_2013_0169]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POODLE&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;MAC pass or fail on shifted byte&lt;/td&gt;
&lt;td&gt;SSL 3.0 unchecked padding&lt;/td&gt;
&lt;td&gt;[@poodle_writeup] [@cve_2014_3566]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS-POODLE&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;Same, no downgrade&lt;/td&gt;
&lt;td&gt;TLS stacks copying the lax check&lt;/td&gt;
&lt;td&gt;[@cve_2014_8730]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2016-2107&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Distinguishable alert (bad_record_mac vs record_overflow)&lt;/td&gt;
&lt;td&gt;Constant-time fix slipped a length check&lt;/td&gt;
&lt;td&gt;[@cve_2016_2107]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zombie POODLE, GOLDENDOODLE&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Content differences in responses&lt;/td&gt;
&lt;td&gt;Deployed CBC-HMAC long tail, 1.83%&lt;/td&gt;
&lt;td&gt;[@merget2019] [@qualys_zombie_poodle2019]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never return a distinct &quot;bad padding&quot; error, and never let padding validity change a branch or the timing of the response. Any distinguishable decryption outcome on attacker-chosen ciphertext -- an error string, a millisecond, a TCP reset, a difference in response content -- is a padding oracle. The channel does not matter; the observability does.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One clarification sharpens the definition by contrast. BEAST (2011) is often filed next to these breaks, but it is &lt;em&gt;not&lt;/em&gt; a padding oracle: it is a &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;predictable-initialization-vector&lt;/a&gt;, chosen-plaintext attack on CBC [@beast_2011], and it belongs to the IV story of Part 2 and Part 5, not here.The tell is the direction of information flow. A padding oracle recovers &lt;em&gt;unknown&lt;/em&gt; plaintext from the receiver&apos;s verdict on the attacker&apos;s ciphertext. BEAST confirms &lt;em&gt;guessed&lt;/em&gt; plaintext by exploiting a predictable IV. Different precondition, different fix -- naming it here only sharpens what a padding oracle is.&lt;/p&gt;
&lt;p&gt;Five generations, five channels, one bit. Every fix silenced the loudest remaining leak, and every fix was beaten by the next-quietest -- including a fix that became its own oracle. Whack-a-mole is not a strategy; it is a symptom. And the symptom points at a diagnosis the field had actually proved correct two years before Vaudenay&apos;s attack, and simply had not deployed.&lt;/p&gt;
&lt;h2&gt;5. The Bug Is the Order, Not the Padding&lt;/h2&gt;
&lt;p&gt;Stop looking at the padding. A padding oracle is not a padding bug; it is the consequence of &lt;em&gt;checking padding on data you have not authenticated&lt;/em&gt;. Change what you check first, and the oracle has nothing to answer. To see why, you need the piece the attack has been quietly relying on: the MAC, and the three ways to combine it with encryption.&lt;/p&gt;

A MAC is a keyed tag that proves a message is authentic and unmodified: only a holder of the secret key can produce a tag that verifies. HMAC is the deployed instance. A valid MAC on a ciphertext means the ciphertext is genuine -- it came from someone with the key, and nothing tampered with it. Part 3 covers HMAC&apos;s structure; here the point is simply *when* you check it.
&lt;p&gt;In 2000, Mihir Bellare and Chanathip Namprempre formalized the three &quot;generic composition&quot; ways to bolt a MAC onto an encryption scheme and proved exactly what each one buys [@bellare_namprempre2000]. Each maps cleanly onto a real protocol.&lt;/p&gt;

Compute the MAC over the *plaintext*, then encrypt the plaintext, MAC, and padding together. On receipt the order is forced: decrypt, strip padding, *then* verify the MAC. Because the receiver must decrypt and unpad before it can check authenticity, the padding verdict is observable by construction. This is SSL and TLS&apos;s original choice -- and the reason SSL/TLS, and only SSL/TLS, became a fifteen-year catalog of padding oracles.

Encrypt the plaintext, then compute the MAC over the *ciphertext* (and the IV), and transmit ciphertext plus tag. On receipt, verify the tag *first*; decrypt only if it passes. A ciphertext the attacker altered fails the MAC and is discarded before any padding logic runs -- so there is no padding verdict to leak, through any channel. This is IPsec ESP&apos;s design and the shape RFC 7366 retrofits onto TLS.
&lt;p&gt;The third sibling, encrypt-and-MAC, tags the plaintext and sends the tag alongside the ciphertext; SSH chose it. It still forces decryption before verification, and because the tag is over the plaintext it can leak plaintext equality, so it is no cure [@bellare_kohno_namprempre_ssh2002].&lt;/p&gt;

flowchart TD
    subgraph MtE[&quot;MAC-then-encrypt (SSL and TLS)&quot;]
        M1[&quot;Decrypt&quot;] --&amp;gt; M2[&quot;Strip padding&quot;] --&amp;gt; M3[&quot;Verify MAC&quot;]
    end
    subgraph EnM[&quot;Encrypt-and-MAC (SSH)&quot;]
        E1[&quot;Decrypt&quot;] --&amp;gt; E2[&quot;Verify MAC over plaintext&quot;]
    end
    subgraph EtM[&quot;Encrypt-then-MAC (IPsec ESP)&quot;]
        T1[&quot;Verify MAC over IV and ciphertext&quot;] --&amp;gt; T2[&quot;Decrypt&quot;] --&amp;gt; T3[&quot;Strip padding&quot;]
    end
&lt;p&gt;Look at where the MAC check sits. In the first two, unauthenticated ciphertext reaches the decrypt-and-parse machinery &lt;em&gt;before&lt;/em&gt; anyone has proven it genuine. In the third, a forged ciphertext dies at the door.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Composition&lt;/th&gt;
&lt;th&gt;MAC covers&lt;/th&gt;
&lt;th&gt;Receiver order&lt;/th&gt;
&lt;th&gt;IND-CCA (generic)&lt;/th&gt;
&lt;th&gt;INT-CTXT (generic)&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;MAC-then-encrypt&lt;/td&gt;
&lt;td&gt;Plaintext&lt;/td&gt;
&lt;td&gt;Decrypt, unpad, verify&lt;/td&gt;
&lt;td&gt;Not guaranteed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;SSL and TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt-and-MAC&lt;/td&gt;
&lt;td&gt;Plaintext&lt;/td&gt;
&lt;td&gt;Decrypt, verify&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt-then-MAC&lt;/td&gt;
&lt;td&gt;Ciphertext and IV&lt;/td&gt;
&lt;td&gt;Verify, decrypt, unpad&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;IPsec ESP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Now the theorem, stated precisely. Encrypt-then-MAC, composed from an IND-CPA encryption scheme and a strongly unforgeable MAC (SUF-CMA), generically attains both IND-CCA and integrity of ciphertexts -- the strongest guarantees of the three -- while MAC-then-encrypt and encrypt-and-MAC do not generically reach integrity of ciphertexts [@bellare_namprempre2000]. Strong unforgeability is the load-bearing word: with only ordinary unforgeability, a second valid tag on the same ciphertext is a fresh accepted ciphertext, and the guarantee weakens to integrity of &lt;em&gt;plaintexts&lt;/em&gt;. In practice the qualifier is free, because HMAC is strongly unforgeable, so the deployed guarantee is exactly as stated. That is the whole game: a construction with integrity of ciphertexts rejects any forged ciphertext &lt;em&gt;before&lt;/em&gt; decryption, so a padding oracle cannot form.&lt;/p&gt;
&lt;p&gt;One honest caveat, because the sloppy version of this claim is wrong. Hugo Krawczyk proved in 2001 that MAC-then-encrypt &lt;em&gt;is&lt;/em&gt; secure when the encryption is CBC or counter mode, under stated conditions [@krawczyk2001]. So MtE is not &quot;always broken.&quot;Krawczyk&apos;s result is conditional: MtE with CBC or CTR is provably secure &lt;em&gt;in the model&lt;/em&gt;, which means Canvel, Lucky Thirteen, and POODLE are implementation, side-channel, and underspecified-padding failures -- not refutations of a theorem. The precise statement guards against the overreach &quot;MtE is broken.&quot; What is true is subtler and more useful: MtE is conditionally provable and practically fragile, because it leaves unauthenticated ciphertext reaching the parser, and holding the model&apos;s assumptions in compiled code is the hard part [@krawczyk2001]. The practitioner&apos;s takeaway is not &quot;the theorem was wrong&quot; but &quot;the theorem&apos;s assumptions are unreasonably hard to preserve in real code -- so change the order and stop needing them.&quot; The practitioner literature has said as much for years [@ferguson_schneier_kohno_ce].&lt;/p&gt;
&lt;p&gt;Watch the difference on a single tampered ciphertext. The MtE receiver leaks a distinguishable outcome; the EtM receiver returns one indistinguishable failure before it parses anything.&lt;/p&gt;
&lt;p&gt;{`
// Two toy receivers over the SAME tampered ciphertext.
// No real crypto: &quot;decrypt&quot; and &quot;macOk&quot; are stand-ins so you can see
// what each receiver reveals.
function decrypt(ct)  { return { paddingValid: false }; } // tampering broke the pad
function macOk(ct)    { return ct.tag === &quot;authentic&quot;; }  // forged tag fails&lt;/p&gt;
&lt;p&gt;const tampered = { tag: &quot;forged&quot; };&lt;/p&gt;
&lt;p&gt;// MAC-then-encrypt: decrypt and check padding BEFORE the MAC.
function mteReceive(ct) {
  const p = decrypt(ct);
  if (!p.paddingValid) return &quot;PADDING_ERROR&quot;;  // leaks the padding verdict
  if (!macOk(ct))      return &quot;MAC_ERROR&quot;;
  return &quot;OK&quot;;
}&lt;/p&gt;
&lt;p&gt;// Encrypt-then-MAC: verify the MAC FIRST; never touch padding on forgeries.
function etmReceive(ct) {
  if (!macOk(ct)) return &quot;FAIL&quot;;                // one indistinguishable outcome
  const p = decrypt(ct);
  if (!p.paddingValid) return &quot;FAIL&quot;;
  return &quot;OK&quot;;
}&lt;/p&gt;
&lt;p&gt;console.log(&quot;MtE says:&quot;, mteReceive(tampered)); // PADDING_ERROR (distinguishable)
console.log(&quot;EtM says:&quot;, etmReceive(tampered)); // FAIL (nothing to learn)
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The bug is the &lt;em&gt;order&lt;/em&gt;, not the padding. Silencing channels is whack-a-mole; checking authenticity &lt;em&gt;first&lt;/em&gt; -- Encrypt-then-MAC, and its successor AEAD -- removes the precondition that unauthenticated ciphertext ever reaches a padding check, so the oracle has nothing left to answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is Aha number two, and it reorganizes the entire catalog. Every generation before Encrypt-then-MAC silenced a channel while leaving the precondition intact; the last two remove the precondition itself.&lt;/p&gt;

flowchart TD
    G1[&quot;Gen 1, distinct padding error&quot;] --&amp;gt; G2[&quot;Gen 2, unified alert&quot;]
    G2 --&amp;gt; G3[&quot;Gen 3, constant-time MtE&quot;]
    G3 --&amp;gt; G4[&quot;Gen 4, Encrypt-then-MAC&quot;]
    G4 --&amp;gt; G5[&quot;Gen 5, AEAD&quot;]
    G1 -.-&amp;gt; S[&quot;Silenced a channel, precondition intact&quot;]
    G2 -.-&amp;gt; S
    G3 -.-&amp;gt; S
    G4 -.-&amp;gt; R[&quot;Removed the precondition, authenticity first&quot;]
    G5 -.-&amp;gt; R

The three compositions were not tried in sequence; they were chosen in *parallel* by different protocols and coexisted for years. MAC-then-encrypt went into SSL and TLS, encrypt-and-MAC into SSH, Encrypt-then-MAC into IPsec ESP [@bellare_namprempre2000]. Only the MtE branch became the fifteen-year catalog. IPsec&apos;s Encrypt-then-MAC branch was structurally immune from the start, which is the quiet punchline of the whole story: the fix TLS reached for in 2014 was, in effect, &quot;become IPsec&quot; -- the negotiated `encrypt_then_mac` extension of RFC 7366 [@rfc7366].
&lt;p&gt;AEAD (RFC 5116) is Encrypt-then-MAC&apos;s standardized successor: one primitive, authenticity intrinsic, no composition knob to misset [@rfc5116]. The theorem was published in 2000. Vaudenay&apos;s attack landed in 2002. And the fix did not reach the deployed TLS base until 2014 to 2018. The rest of this article is what &quot;check authenticity first&quot; looks like when it finally shipped at Internet scale -- and why, even now, the class is not entirely dead.&lt;/p&gt;
&lt;h2&gt;6. The State of the Art: Closed by Construction&lt;/h2&gt;
&lt;p&gt;The modern answer is boring, and that is exactly the point. Use a primitive where a padding oracle &lt;em&gt;cannot exist&lt;/em&gt;, because authenticity is checked first and there is no block padding at all.&lt;/p&gt;

An AEAD primitive takes a key, a nonce, a plaintext, and optional associated data, and produces a ciphertext with an integral authentication tag. Decryption verifies the tag as part of the operation and returns *nothing* -- a single indistinguishable failure -- if it does not match. There is no separate MAC to order, and for the stream-based AEADs in wide use there is no block padding to be oracular. Standardized as an interface in RFC 5116 [@rfc5116].
&lt;p&gt;Two AEADs carry nearly all modern traffic. &lt;strong&gt;AES-GCM&lt;/strong&gt; is the default where AES hardware exists: it is AES in counter mode for encryption, plus a GHASH tag over the ciphertext and associated data, verified before any plaintext is released. &lt;strong&gt;ChaCha20-Poly1305&lt;/strong&gt; is the default where AES hardware does not exist -- Google deployed it in Chrome on Android because it runs about three times faster than AES-GCM on devices without AES acceleration, and it is constant-time in portable software by design [@rfc8439] [@google_chacha_blog2014]. Both are counter-mode stream constructions, and that structural fact, not the label &quot;AEAD,&quot; is why they are immune.A stream construction has &lt;em&gt;no block padding at all&lt;/em&gt; -- the keystream is simply truncated to the plaintext length. So the object Vaudenay&apos;s atom acts on, a padded final block whose validity the receiver checks, does not exist. There is no surface. This is why &quot;GCM has a tag, could it have a padding oracle?&quot; answers itself: there is nothing to pad.&lt;/p&gt;
&lt;p&gt;Then the standards removed the choice. TLS 1.3 (RFC 8446, 2018) removes CBC cipher suites entirely and mandates AEAD, and QUIC inherits that record layer, so a conformant modern stack cannot express the padding-oracle precondition [@rfc8446].&lt;/p&gt;
&lt;p&gt;For the deployed base that could not jump straight to 1.3, RFC 7366 (2014) retrofits Encrypt-then-MAC onto TLS 1.0 through 1.2 as a negotiated extension -- extension type 22 -- citing Bellare-Namprempre and Krawczyk by name as the reason MtE &quot;is no longer regarded as secure&quot; [@rfc7366]. And BCP 195 (RFC 9325, 2022) codifies the operational rule: prefer TLS 1.3, prefer AEAD suites on 1.2, and recommend against CBC [@rfc9325].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;How it works&lt;/th&gt;
&lt;th&gt;What it buys&lt;/th&gt;
&lt;th&gt;Status 2024 to 2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;AES-GCM&lt;/td&gt;
&lt;td&gt;AES-CTR plus GHASH tag, verified first&lt;/td&gt;
&lt;td&gt;No padding surface, fastest with AES hardware&lt;/td&gt;
&lt;td&gt;Default (TLS 1.3, QUIC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;ChaCha20 plus Poly1305, verified first&lt;/td&gt;
&lt;td&gt;Constant-time in software, ~3x without AES-NI&lt;/td&gt;
&lt;td&gt;Co-default, WireGuard, OpenSSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt-then-MAC (RFC 7366)&lt;/td&gt;
&lt;td&gt;MAC over IV and ciphertext, verified first&lt;/td&gt;
&lt;td&gt;Structural fix for legacy CBC&lt;/td&gt;
&lt;td&gt;Compose-by-hand rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constant-time CBC-HMAC&lt;/td&gt;
&lt;td&gt;Harden the MtE order in place&lt;/td&gt;
&lt;td&gt;Nothing new; fragile&lt;/td&gt;
&lt;td&gt;Deprecated (BCP 195)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Misuse-resistant, committing AEAD&lt;/td&gt;
&lt;td&gt;AES-GCM-SIV, committing wrappers&lt;/td&gt;
&lt;td&gt;Nonce-repeat safety, key commitment&lt;/td&gt;
&lt;td&gt;Ascending, niche&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Padding-oracle detection&lt;/td&gt;
&lt;td&gt;TLS-Attacker, padcheck scanners&lt;/td&gt;
&lt;td&gt;Measures residual exposure&lt;/td&gt;
&lt;td&gt;Active tooling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The honest status has two levels. At the primitive level the class is &lt;em&gt;closed&lt;/em&gt;: TLS 1.3 has no CBC-HMAC record layer, so it has no padding oracle. But CBC-HMAC survives in the long tail -- legacy TLS, VPN appliances, load balancers, IoT stacks, and home-grown application crypto -- which is why padding oracles are still discovered, in 1.83% of the top million as recently as the 2019 scan, exploitable without precise timing [@merget2019].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Treat any distinguishable decryption error on a CBC-HMAC endpoint as a reportable finding, not a performance bug to smooth over. The lesson of five generations is that the construction, not any single leak, is the problem: CBC-HMAC is the thing to &lt;em&gt;retire&lt;/em&gt;. Every hour spent hardening its constant-time behavior is an hour not spent migrating to AEAD, which needs no such hardening.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&quot;Use AEAD&quot; is the entire answer for greenfield code. But engineers inherit constraints -- a mode they cannot change, a library without AEAD, a protocol mid-migration -- so the real question is not &quot;what is best&quot; but &quot;what are my options, ranked, and exactly when does each apply?&quot;&lt;/p&gt;
&lt;h2&gt;7. How to Not Have a Padding Oracle, Ranked&lt;/h2&gt;
&lt;p&gt;There are three ways to keep a padding oracle out of your system, in strict order of durability -- and the ranking is a theorem, not a taste. The yardstick is one question: &lt;em&gt;is authenticity checked before anything parses the plaintext?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First choice: AEAD.&lt;/strong&gt; Authenticity is intrinsic, there is no padding, and there are &lt;em&gt;zero composition knobs&lt;/em&gt; for an implementer to misset. This is the default, full stop. Its own frontier is a genuinely different problem, not a padding successor: nonce reuse under AES-GCM is catastrophic -- the &quot;forbidden attack,&quot; demonstrated against real TLS and IPMI stacks by Böck et al. (WOOT &apos;16) [@bock_nonce_woot2016] -- and standard AEAD is not key-committing, so one ciphertext can be made to open to different valid plaintexts under different keys [@albertini_committing2022]. Those are addressed by AES-GCM-SIV for nonce-misuse resistance and by committing AEAD constructions, which the CFRG&apos;s property vocabulary now names formally [@rfc8452] [@rfc9771]. Treat that as a one-line signpost, not a reason to hesitate on AEAD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second choice: Encrypt-then-MAC by hand.&lt;/strong&gt; Correct, but only if you get three things right -- an &lt;em&gt;independent&lt;/em&gt; MAC key (never the encryption key), coverage of &lt;em&gt;both&lt;/em&gt; the IV and the ciphertext, and a &lt;em&gt;constant-time&lt;/em&gt; tag comparison performed &lt;em&gt;before&lt;/em&gt; any decrypt or unpad. That is the RFC 7366 shape [@rfc7366]. Three knobs an implementer can misset is precisely the argument for reaching for AEAD&apos;s zero knobs whenever you can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third choice, and only if forced: constant-time MtE / CBC-HMAC.&lt;/strong&gt; This is the &quot;make the existing order safe in place&quot; path, and it is the cautionary option to &lt;em&gt;retire&lt;/em&gt;, not to choose. Lucky Thirteen, Lucky Microseconds, and CVE-2016-2107 are the evidence that it fights the construction: MtE keeps unauthenticated ciphertext reaching the parser, so the distinguisher re-emerges through whatever the CPU or the compiler leaves open [@lucky13_2013] [@cve_2016_2107].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;AES-GCM&lt;/th&gt;
&lt;th&gt;ChaCha20-Poly1305&lt;/th&gt;
&lt;th&gt;Encrypt-then-MAC (CBC-HMAC)&lt;/th&gt;
&lt;th&gt;Constant-time MtE (CBC-HMAC)&lt;/th&gt;
&lt;th&gt;AES-GCM-SIV&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Authenticity checked first?&lt;/td&gt;
&lt;td&gt;Yes (intrinsic)&lt;/td&gt;
&lt;td&gt;Yes (intrinsic)&lt;/td&gt;
&lt;td&gt;Yes (verify tag first)&lt;/td&gt;
&lt;td&gt;No (decrypt and unpad first)&lt;/td&gt;
&lt;td&gt;Yes (intrinsic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Padding-oracle surface&lt;/td&gt;
&lt;td&gt;None (stream)&lt;/td&gt;
&lt;td&gt;None (stream)&lt;/td&gt;
&lt;td&gt;None (rejected pre-unpad)&lt;/td&gt;
&lt;td&gt;Full (precondition survives)&lt;/td&gt;
&lt;td&gt;None (stream)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composition knobs to misset&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce or IV reuse behavior&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;td&gt;IV must be unpredictable&lt;/td&gt;
&lt;td&gt;IV must be unpredictable&lt;/td&gt;
&lt;td&gt;Graceful (equality leak only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status 2024 to 2026&lt;/td&gt;
&lt;td&gt;Active default&lt;/td&gt;
&lt;td&gt;Active co-default&lt;/td&gt;
&lt;td&gt;Compose-by-hand rule&lt;/td&gt;
&lt;td&gt;Deprecated (BCP 195)&lt;/td&gt;
&lt;td&gt;Ascending, niche&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Only the first two options remove the &lt;em&gt;precondition&lt;/em&gt;; the third merely silences the loudest channel. That is the whole ranking, and it maps onto the parallel history from the last section: AEAD and Encrypt-then-MAC are what IPsec effectively had from the start, while constant-time MtE is the fifteen-year attempt to make SSL/TLS&apos;s original order safe without changing it.&lt;/p&gt;
&lt;p&gt;So the durable options all share one property: they refuse to let unauthenticated ciphertext reach a parser. That raises a deeper question a careful reader should now be asking. &lt;em&gt;Why&lt;/em&gt; is one accept/reject bit enough to be fatal in the first place -- and is Encrypt-then-MAC &lt;em&gt;provably&lt;/em&gt; enough to stop it, or just empirically better so far?&lt;/p&gt;
&lt;h2&gt;8. Why a Bit Is Enough, and Why the Fix Is a Theorem&lt;/h2&gt;
&lt;p&gt;Two boundaries frame the whole subject: how &lt;em&gt;little&lt;/em&gt; leakage the attacker needs, which is frighteningly little, and how &lt;em&gt;hard&lt;/em&gt; the defender&apos;s guarantee actually is, which is a proof rather than a hope.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The lower bound: one reliable bit suffices.&lt;/strong&gt; A single accept/reject bit per query, combined with CBC&apos;s malleability, recovers plaintext at about 128 guesses per byte, and the cipher is never attacked [@vaudenay2002]. The consequence is a hard limit on defense that every engineer should internalize: there is no &quot;leak too small to matter,&quot; only &quot;too noisy to measure cheaply.&quot; The bit is information-theoretically sufficient; the only variable is how many queries it takes to read it reliably.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Lucky Thirteen turned a difference of a few tens of CPU cycles -- invisible to a human, buried in network jitter -- into full plaintext recovery by amplifying it statistically over many sessions [@lucky13_2013]. If a one-bit distinguisher exists at all, &quot;make it quieter&quot; only raises the query count; it never reaches zero. The only defense that reduces the leak to &lt;em&gt;nothing&lt;/em&gt; is removing the precondition, so no bit is ever produced.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;The impossibility in the middle.&lt;/strong&gt; An unauthenticated, malleable mode is provably not IND-CCA2. CBC without integrity is a decryption oracle by construction, so no amount of error-message hygiene or timing equalization can rescue MAC-then-encrypt CBC against a determined side-channel adversary. The impossibility lives in the &lt;em&gt;precondition itself&lt;/em&gt; -- in letting a malleable ciphertext be decrypted before it is authenticated -- which is why the deployed padding oracle is exactly the &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;IND-CCA2 break&lt;/a&gt; this series defines in Part 1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The upper bound: the fix is a hard theorem.&lt;/strong&gt; The best achievable defense is a proved one.&lt;/p&gt;

A scheme has integrity of ciphertexts when an adversary, even after seeing many valid ciphertexts, cannot produce *any* new ciphertext the receiver accepts as valid. This is the property that closes the oracle: if every forged ciphertext is rejected before decryption, the receiver never reveals a padding verdict on attacker-chosen input. Encrypt-then-MAC and AEAD provide it; MAC-then-encrypt does not, generically.
&lt;p&gt;Encrypt-then-MAC with an IND-CPA cipher and a strongly unforgeable MAC (SUF-CMA) generically attains IND-CCA plus integrity of ciphertexts, so a forged ciphertext is rejected with overwhelming probability &lt;em&gt;before&lt;/em&gt; decryption -- the oracle is closed by theorem, not by patch [@bellare_namprempre2000]. Krawczyk marks the honest boundary of the alternative: MAC-then-encrypt is conditionally provable for CBC and counter mode, so the real-world MtE breaks are implementation and side-channel failures, not refutations of a theorem -- but Encrypt-then-MAC needs no such conditions [@krawczyk2001].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; At the level of construction design there is no gap. The lower bound -- any observable padding verdict yields byte-at-a-time decryption -- is &lt;em&gt;met&lt;/em&gt; by removing the precondition, and the upper bound -- reject forgeries before decryption, IND-CCA plus INT-CTXT -- is &lt;em&gt;achieved&lt;/em&gt; by Encrypt-then-MAC and AEAD. Best-possible and best-achieved coincide. Every remaining gap is implementation and deployment, not mathematics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is Aha number three, and it is a humbler note than it first sounds. If the design problem is provably solved, why do scanners still find these oracles in production, and why did a correct construction get re-broken by its own patch? Because &quot;solved on paper&quot; and &quot;solved in a compiled binary on a specific CPU, deployed to a billion devices&quot; are different sentences -- and the space between them is where the class still lives.&lt;/p&gt;
&lt;h2&gt;9. Where Padding Oracles Still Live&lt;/h2&gt;
&lt;p&gt;The construction-level problem is closed; the &lt;em&gt;class&lt;/em&gt; is not. Here are four places the 2002 bug remains exploitable, and one frontier that would end the whack-a-mole.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constant-time is a moving target.&lt;/strong&gt; Even with the correct construction, a specific compiled binary on a specific CPU can reintroduce a secret-dependent timing difference through branch prediction, cache behavior, or an assembly fast path -- and the countermeasure can also slip in a plainer way. CVE-2016-2107 is the canonical evidence: OpenSSL&apos;s AES-NI fast path, written to make the fix constant-time, dropped a length check and re-created the oracle -- leaking not through timing but through a distinguishable alert (bad_record_mac versus record_overflow), which is why TLS-Attacker found it by clustering responses rather than measuring cycles [@cve_2016_2107]. The strongest partial answer is formally verified, secret-independent cryptographic code -- HACL* and EverCrypt prove memory safety, functional correctness, &lt;em&gt;and&lt;/em&gt; secret independence, and ship in Firefox and NSS [@hacl_star]. But that covers primitives, not yet whole record layers end to end, so the verification frontier is real.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The deployed CBC-HMAC long tail.&lt;/strong&gt; Legacy TLS, VPN appliances, load balancers, and IoT stacks that cannot move to TLS 1.3 keep MAC-then-encrypt CBC alive. The 2019 scan put the exposure at 1.83% of the top million, exploitable without precise timing, and the population shrinks year over year rather than vanishing -- a trend the SSL Pulse dashboard tracks [@merget2019] [@ssl_pulse].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Application-layer oracles.&lt;/strong&gt; This is the part TLS 1.3 does not reach, and it deserves its own aside because it is where the next decade of these bugs will be found.&lt;/p&gt;

Tibor Jager and Juraj Somorovsky broke the W3C XML Encryption standard with the same oracle, decrypting XML in SOAP and web-services stacks by watching how an endpoint reacted to manipulated ciphertext -- proof that the mechanism operates far outside TLS [@jager_somorovsky_xmlenc2011]. The application layer is full of the same shape: JWE and JOSE `A128CBC-HS256` tokens, encrypted cookies, and ViewState-style tokens reproduce the 2002 bug, often with no MAC at all, above TLS where network scanners cannot see them. A separate but adjacent hazard, Efail, exploited a CBC *malleability gadget* in S/MIME and a CFB gadget in OpenPGP to exfiltrate plaintext directly -- not a padding-verdict oracle, but the same &quot;unauthenticated malleable ciphertext&quot; precondition wearing different clothes [@efail2018].
&lt;p&gt;&lt;strong&gt;Format and parse oracles beyond padding.&lt;/strong&gt; Generalize once more: &lt;em&gt;any&lt;/em&gt; observable post-decryption structural verdict -- a JSON parse success, a protobuf validity check, a decompression outcome -- is a padding oracle by another name whenever it acts on unauthenticated ciphertext. The padding was only ever the first structure anyone checked. Jager and Somorovsky already proved the mechanism outside padding entirely [@jager_somorovsky_xmlenc2011], and systematic detection of &quot;decrypt-then-parse&quot; leaks is not yet automated the way TLS padding-oracle scanning now is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AEAD&apos;s own frontier.&lt;/strong&gt; The successor primitive has two sharp edges of its own -- catastrophic nonce reuse in AES-GCM [@bock_nonce_woot2016] and the lack of key commitment [@albertini_committing2022] -- which matter most in exactly the places large systems trip: nonce management across many distributed senders, and multi-key or multi-recipient contexts like JWT and envelope encryption. Both are addressed &lt;em&gt;separately&lt;/em&gt; today, by AES-GCM-SIV for misuse resistance and by committing constructions that RFC 9771&apos;s vocabulary now specifies, but no single ubiquitous primitive yet delivers misuse-resistant &lt;em&gt;and&lt;/em&gt; committing &lt;em&gt;and&lt;/em&gt; fast &lt;em&gt;and&lt;/em&gt; online at once [@rfc8452] [@rfc9771]. That convergence is the open engineering target, and it is a different problem from the padding oracle, not a continuation of it.&lt;/p&gt;
&lt;p&gt;Every one of these is the same sentence wearing new clothes: unauthenticated, attacker-chosen input reaching a check whose verdict is observable. Which means the practical guide almost writes itself -- it is the thesis made operational.&lt;/p&gt;
&lt;h2&gt;10. Decision Rules: Use X With These Params in Case Y&lt;/h2&gt;
&lt;p&gt;Everything above collapses into a short decision procedure and a shorter list of nevers.&lt;/p&gt;

flowchart TD
    Start[&quot;Need authenticated encryption&quot;] --&amp;gt; Q1{&quot;Greenfield design?&quot;}
    Q1 --&amp;gt;|&quot;Yes&quot;| Q2{&quot;AES hardware present?&quot;}
    Q2 --&amp;gt;|&quot;Yes&quot;| GCM[&quot;AES-GCM, unique 96-bit nonce&quot;]
    Q2 --&amp;gt;|&quot;No&quot;| ChaCha[&quot;ChaCha20-Poly1305&quot;]
    Q1 --&amp;gt;|&quot;No, stuck with CBC&quot;| Q3{&quot;Can you retire CBC?&quot;}
    Q3 --&amp;gt;|&quot;Yes&quot;| GCM
    Q3 --&amp;gt;|&quot;No&quot;| EtM[&quot;Encrypt-then-MAC, RFC 7366, verify first&quot;]
    GCM --&amp;gt; Q4{&quot;Nonce uniqueness hard?&quot;}
    Q4 --&amp;gt;|&quot;Yes&quot;| SIV[&quot;AES-GCM-SIV&quot;]
    Q4 --&amp;gt;|&quot;No&quot;| Done[&quot;Ship it&quot;]
&lt;p&gt;The rules, in order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Default to a vetted AEAD, from a library you cannot misuse.&lt;/strong&gt; Use AES-GCM with a unique 96-bit nonce and fewer than $2^{32}$ messages per key where the CPU has AES-NI; use ChaCha20-Poly1305 where there is no AES hardware, or when you want constant-time behavior from portable software [@rfc8439]. In application code, reach for libsodium&apos;s &lt;code&gt;secretbox&lt;/code&gt; or Google Tink rather than raw primitives, and prefer any API where you &lt;em&gt;cannot&lt;/em&gt; express &quot;decrypt, then check&quot; -- the safest libraries never hand you plaintext before verification.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If nonce uniqueness is hard to guarantee&lt;/strong&gt; across many independent senders or under snapshot and rollback risk, use AES-GCM-SIV, so a nonce repeat degrades gracefully instead of catastrophically [@rfc8452].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If a ciphertext may be opened under more than one key&lt;/strong&gt; -- JWT and JOSE, envelope encryption, abuse reporting -- use a committing AEAD [@albertini_committing2022] [@rfc9771].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you must compose by hand,&lt;/strong&gt; use Encrypt-then-MAC: encrypt, then compute the MAC over the IV and ciphertext with an &lt;em&gt;independent&lt;/em&gt; MAC key, and verify the tag in constant time &lt;em&gt;before&lt;/em&gt; you decrypt or unpad [@rfc7366].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On a legacy TLS 1.2 CBC deployment you cannot yet retire,&lt;/strong&gt; negotiate RFC 7366 Encrypt-then-MAC, prefer AEAD suites, and follow BCP 195 [@rfc9325].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never&lt;/strong&gt; return a distinct &quot;bad padding&quot; error; never branch or vary timing on padding validity; never decrypt before verifying; never reuse the encryption key as the MAC key; never leave the IV out of the MAC; and never ship SSL 3.0 or non-RFC-7366 CBC.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are stuck at the fourth rule, the shape below is the reference. It is dependency-free and non-cryptographic on purpose: copy the &lt;em&gt;order of operations&lt;/em&gt;, not the stand-in primitives.&lt;/p&gt;
&lt;p&gt;{`
// Reference SHAPE for a correct Encrypt-then-MAC open().
// constantTimeEqual, hmac, cbcDecrypt, removePkcs7 are stand-ins, NOT real crypto.
function constantTimeEqual(a, b) {
  if (a.length !== b.length) return false;
  let diff = 0;
  for (let i = 0; i &amp;lt; a.length; i++) diff |= a[i] ^ b[i];
  return diff === 0;   // no early exit: time is independent of where they differ
}&lt;/p&gt;
&lt;p&gt;function open(kEnc, kMac, iv, ciphertext, tag) {
  // 1. Authenticate FIRST, over IV concat ciphertext, in constant time.
  const expected = hmac(kMac, iv.concat(ciphertext));
  if (!constantTimeEqual(tag, expected)) return &quot;FAIL&quot;;  // one outcome, always&lt;/p&gt;
&lt;p&gt;  // 2. Only authentic ciphertext ever reaches decryption and unpadding.
  const padded = cbcDecrypt(kEnc, iv, ciphertext);
  const plain = removePkcs7(padded);
  return plain === null ? &quot;FAIL&quot; : plain;                // same FAIL either way
}&lt;/p&gt;
&lt;p&gt;// Stand-ins so the snippet runs (NOT secure):
function hmac(k, data)          { return data.map((b) =&amp;gt; (b ^ k) &amp;amp; 0xff); }
function cbcDecrypt(k, iv, c)   { return c; }
function removePkcs7(p)         { return p; }&lt;/p&gt;
&lt;p&gt;console.log(open(1, 2, [9, 9], [4, 5, 6], [7, 7, 7])); // FAIL: forged tag rejected first
`}&lt;/p&gt;
&lt;p&gt;Every misuse in real code maps one-to-one onto a named break in the catalog. That mapping is the practical guide as a grid.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse seen in code&lt;/th&gt;
&lt;th&gt;The named break it reproduces&lt;/th&gt;
&lt;th&gt;The fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Distinct &quot;bad padding&quot; error&lt;/td&gt;
&lt;td&gt;Vaudenay, ASP.NET&lt;/td&gt;
&lt;td&gt;Verify authenticity first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-constant-time unpad and MAC&lt;/td&gt;
&lt;td&gt;Lucky Thirteen&lt;/td&gt;
&lt;td&gt;AEAD, or constant-time EtM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unchecked pad bytes&lt;/td&gt;
&lt;td&gt;POODLE&lt;/td&gt;
&lt;td&gt;Retire SSL 3.0 and CBC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-NI fast-path length slip&lt;/td&gt;
&lt;td&gt;CVE-2016-2107&lt;/td&gt;
&lt;td&gt;Verified constant-time, or AEAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verifying the MAC after decrypting&lt;/td&gt;
&lt;td&gt;The whole class&lt;/td&gt;
&lt;td&gt;Encrypt-then-MAC ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce reuse under AES-GCM&lt;/td&gt;
&lt;td&gt;The forbidden attack&lt;/td&gt;
&lt;td&gt;Unique nonces, or AES-GCM-SIV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App-layer encrypt with no MAC&lt;/td&gt;
&lt;td&gt;JWE and cookie oracles&lt;/td&gt;
&lt;td&gt;A committing AEAD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Use a vetted AEAD: AES-GCM with unique 96-bit nonces and the under-$2^{32}$-messages-per-key cap where AES hardware exists, or ChaCha20-Poly1305 where it does not. If you must keep CBC, negotiate RFC 7366 Encrypt-then-MAC with an independent MAC key and a constant-time verify performed first. Never ship SSL 3.0 or non-RFC-7366 CBC. That is the entire checklist, because the lesson is a single sentence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Detection is cheap enough to wire into a pipeline, and you should, because content-difference oracles need no timing rig to find [@merget2019].&lt;/p&gt;

Point an open-source padding-oracle scanner at your own hosts. Craig Young&apos;s `padcheck` and the TLS-Attacker project&apos;s `TLS-Padding-Oracles` both probe for the invalid-padding-versus-invalid-MAC content differences that Zombie POODLE and GOLDENDOODLE exploit, and they run without a precise timing setup [@padcheck] [@tls_padding_oracles]. A clean result on a CBC endpoint is reassuring; any distinguishable response is a reportable finding, not a tuning opportunity.
&lt;p&gt;The checklist is short because the lesson is one sentence. Before restating it, clear the confident, wrong sentences that keep this bug alive in design meetings.&lt;/p&gt;
&lt;h2&gt;11. Misconceptions, Named and Corrected&lt;/h2&gt;
&lt;p&gt;The padding oracle survives mostly because a handful of confident, wrong sentences keep getting said in design meetings. Here they are, corrected.&lt;/p&gt;


The strength of AES is irrelevant to this attack, because the key is never attacked. A padding oracle recovers plaintext from the receiver&apos;s error handling -- a distinguishable answer to &quot;was the padding valid?&quot; -- combined with CBC&apos;s malleability [@vaudenay2002]. The ASP.NET break recovered authentication tickets and `web.config` with AES-256 fully intact [@rizzo_duong_woot2010]. &quot;The cipher is strong&quot; and &quot;the system is secure&quot; are different claims.


No. The same accept/reject bit re-emerges through the next-quietest channel. Once the error message was unified, it leaked through coarse timing (Canvel et al., 2003) and then through statistical timing under unified errors (Lucky Thirteen, 2013), and through unchecked structure entirely (POODLE, 2014) [@canvel2003] [@lucky13_2013] [@poodle_writeup]. Silencing one channel just moves the leak; only removing the precondition closes it.


No -- and the overstatement matters. Krawczyk proved MAC-then-encrypt conditionally secure for CBC and counter mode, so the real-world breaks are implementation, side-channel, and underspecified-padding failures, not refutations of a theorem [@krawczyk2001]. What is true is that MtE is *fragile in practice*: it leaves unauthenticated ciphertext reaching the parser, so constant-time MtE is a fight against the construction. The fix is to change the order, not to declare a theorem false.


It is a padding oracle; the downgrade is only the enabler that forces a client onto vulnerable SSL 3.0, whose pad bytes are unchecked by specification [@poodle_writeup] [@rfc6101]. The proof is TLS-POODLE (CVE-2014-8730), the same oracle against TLS stacks that copied the lax check, with no downgrade at all [@cve_2014_8730]. Frame POODLE as a padding oracle first, a downgrade second.


No. Bleichenbacher (1998) is an RSA PKCS #1 v1.5 oracle -- a different primitive with different arithmetic [@bleichenbacher1998]. Vaudenay (2002) is the first *CBC* instance [@vaudenay2002]. Bleichenbacher is the ancestor &quot;in spirit,&quot; because he first showed that a structural validity verdict is a decryption oracle, but calling his attack a CBC padding oracle confuses two branches of the family tree.


No. AES-GCM and ChaCha20-Poly1305 are counter-mode stream constructions with no block padding, and they verify the authentication tag before releasing any plaintext, so the Vaudenay atom has no surface to act on [@rfc8439]. GCM&apos;s own hazard is nonce reuse, which is a different problem -- the &quot;forbidden attack&quot; -- not a padding oracle [@bock_nonce_woot2016].


Only if authenticity is checked *first* and the unpadding is *also* constant-time. A constant-time compare on a MAC you verify *after* decrypting still leaves the padding check reachable, which is the whole class -- and a hand-written fast path can reintroduce the oracle anyway, as CVE-2016-2107 did, through a missing length check observable as a distinguishable alert rather than a timing leak [@cve_2016_2107]. The safe pattern is verify-then-decrypt, or an AEAD that gives you no other option.

&lt;p&gt;Every correction points at the same root: the padding was never the bug. Time to say the sentence the whole article was built to earn.&lt;/p&gt;
&lt;h2&gt;Decrypt, Then Confess&lt;/h2&gt;
&lt;p&gt;Return to that ASP.NET server in September 2010. AES-256 intact. The key never touched, never even approached. And the plaintext -- authentication tickets, &lt;code&gt;web.config&lt;/code&gt;, the site&apos;s master secrets -- gone, one byte at a time, because the server answered a question it should never have been asked before it authenticated the ciphertext [@rizzo_duong_woot2010].&lt;/p&gt;
&lt;p&gt;Line up the catalog and the shape is unmistakable. Bleichenbacher, Vaudenay, Canvel, ASP.NET, Lucky Thirteen, POODLE, CVE-2016-2107, Zombie POODLE: in every case the cipher did exactly what it promised, and the variable was always the &lt;em&gt;order of operations&lt;/em&gt;, leaking through a quieter channel each time it was silenced. A loud error became coarse timing became statistical timing became unchecked structure became a fix that re-created its own oracle. Five generations chased the bit; the bit kept escaping, because the precondition -- unauthenticated, malleable ciphertext reaching a padding check -- was never removed.&lt;/p&gt;
&lt;p&gt;The resolution the field finally deployed is a single move: verify authenticity &lt;em&gt;first&lt;/em&gt;. Encrypt-then-MAC proves it -- IND-CCA plus integrity of ciphertexts, the strongest of the three generic compositions, published two years &lt;em&gt;before&lt;/em&gt; Vaudenay&apos;s attack [@bellare_namprempre2000]. AEAD ships it as one primitive with no knob to misset, and TLS 1.3 makes it the only option a conformant stack can express [@rfc8446].&lt;/p&gt;
&lt;p&gt;This mirrors the running lesson of the series: Part 1&apos;s IND-CCA2 adversary is not a chalkboard abstraction but the exact adversary that walked out of that ASP.NET server, and Part 5&apos;s warning that confidentiality is not integrity is precisely the gap a padding oracle drives through.&lt;/p&gt;

When a ciphertext you did not create arrives, what does your receiver reveal about it before it has proven the ciphertext authentic? Encrypt-then-MAC and AEAD are the discipline of making the answer nothing.
&lt;p&gt;Carry that one question into every protocol you design or review. Drop Lucky Thirteen onto it, drop POODLE onto it, drop the next application-layer JWE oracle onto it: each is a non-empty answer, and the fix is always the same shape. The cipher was never the weak link, and no future cipher will be. That is why this is Part 6 of a field guide to &lt;em&gt;protocol design&lt;/em&gt;, not a chapter on block ciphers.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;padding-oracles-field-guide&quot; keyTerms={[
  { term: &quot;Padding oracle&quot;, definition: &quot;An attack class where a receiver&apos;s observable verdict on the padding of attacker-chosen ciphertext becomes a plaintext-recovery oracle, never touching the key.&quot; },
  { term: &quot;PKCS#7 padding&quot;, definition: &quot;Append n bytes each equal to n to fill the final block; a full extra block when already aligned (RFC 2315).&quot; },
  { term: &quot;CBC malleability&quot;, definition: &quot;Because each plaintext block is D_k(C_i) XOR C_(i-1), flipping a ciphertext byte flips the same plaintext byte deterministically.&quot; },
  { term: &quot;Chosen-ciphertext attack (IND-CCA2)&quot;, definition: &quot;An adversary that submits ciphertexts and learns from the receiver&apos;s reactions; a padding oracle is a deployed instance.&quot; },
  { term: &quot;Message authentication code (MAC)&quot;, definition: &quot;A keyed tag proving a message is authentic and unmodified; HMAC is the deployed instance.&quot; },
  { term: &quot;MAC-then-encrypt (MtE)&quot;, definition: &quot;MAC the plaintext, then encrypt; the receiver decrypts and unpads before verifying, so the padding verdict is observable by construction. SSL and TLS&apos;s original choice.&quot; },
  { term: &quot;Encrypt-then-MAC (EtM)&quot;, definition: &quot;MAC the ciphertext and IV, verify first, decrypt only authentic ciphertext; attains IND-CCA plus INT-CTXT. IPsec ESP and RFC 7366.&quot; },
  { term: &quot;INT-CTXT (integrity of ciphertexts)&quot;, definition: &quot;The adversary cannot produce any new ciphertext the receiver accepts; the property that closes the oracle.&quot; },
  { term: &quot;AEAD&quot;, definition: &quot;Authenticated Encryption with Associated Data: one primitive that verifies authenticity as part of decryption and returns nothing on failure (RFC 5116).&quot; },
  { term: &quot;Constant-time comparison&quot;, definition: &quot;A check whose running time does not depend on secret data or on where a difference occurs; the countermeasure Lucky Thirteen forced, whose hand-written implementation CVE-2016-2107 later showed to be fragile.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>padding-oracle</category><category>cbc</category><category>tls</category><category>authenticated-encryption</category><category>aead</category><category>encrypt-then-mac</category><category>lucky-thirteen</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>