<?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: aead</title><description>Posts tagged aead.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sat, 25 Jul 2026 08:57:55 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/aead/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Standard Was Boring; That Was the Point: A Field Guide to HPKE (RFC 9180)</title><link>https://paragmali.com/blog/the-standard-was-boring-that-was-the-point-a-field-guide-to-/</link><guid isPermaLink="true">https://paragmali.com/blog/the-standard-was-boring-that-was-the-point-a-field-guide-to-/</guid><description>The industry standardized encrypt-to-a-public-key four incompatible ways. HPKE (RFC 9180) is the boring, correct fix -- and its non-goals are the whole discipline.</description><pubDate>Mon, 13 Jul 2026 08:42:43 GMT</pubDate><content:encoded>
**&quot;Encrypt to a public key&quot; sounds like the most basic promise in cryptography, yet the industry standardized it four incompatible ways.** ANSI X9.63, IEEE 1363a, ISO/IEC 18033-2, and SEC 1 all shipped a version of ECIES; two conforming implementations could produce ciphertexts that would not decrypt for each other, none came with shared test vectors, and one standard openly documented that an attacker could forge a second valid ciphertext for the same plaintext and named it &quot;benign malleability&quot; [@sec1], [@maea10]. HPKE (RFC 9180, 2022) is the boring, correct fix: one KEM-DEM construction where a Key Encapsulation Mechanism *generates* a shared secret and its encapsulation, an HKDF key schedule with every public key bound in derives the working keys, and an AEAD seals the data [@rfc9180]. It is IND-CCA2-proven, machine-checked across all four modes (`base`, `psk`, `auth`, `auth_psk`), and shipped with per-ciphersuite test vectors [@lipp20], [@abhklr20]. Because the primitive is correct by construction, HPKE has no known catastrophic real-world break; the entire discipline is knowing its four modes and its non-goals. It does *not* provide forward secrecy against recipient compromise in any mode, `auth` mode is *not* a signature, and its AEADs are *not* key-committing -- and every real HPKE incident to date is one of those non-goals assumed away or a check an implementation dropped [@rfc9180], [@ghsa]. It is the sealing primitive behind Encrypted Client Hello (RFC 9849), MLS, Oblivious HTTP, and Oblivious DNS; the post-quantum path is to swap the KEM (X-Wing / ML-KEM), not to redesign the scheme [@rfc9849], [@xwing]. The math held; the risk moved to the interface.
&lt;h2&gt;1. An Elementary Task the Industry Got Wrong Four Times&lt;/h2&gt;
&lt;p&gt;&quot;Encrypt this to their public key&quot; is the first promise public-key cryptography ever made. It is the one-line pitch of RSA, the thing every engineer assumes was solved and made boring decades ago, a settled corner of the field you reach for without thinking. Yet for roughly twenty-five years there was no single correct way to do it.&lt;/p&gt;
&lt;p&gt;At least four &quot;standard&quot; versions of the Elliptic Curve Integrated Encryption Scheme (ECIES) shipped: ANSI X9.63 in 2001, IEEE 1363a in 2004, ISO/IEC 18033-2 in 2006, and SECG&apos;s SEC 1 version 2 in 2009 [@maea10], [@rfc9180]. They disagreed on the key-derivation function, the message authentication code, whether elliptic-curve points were compressed or uncompressed, and which optional fields went into the hash. Two conforming implementations, handed the same curve and message, could produce ciphertexts that would not decrypt for each other.&lt;/p&gt;
&lt;p&gt;None of the four came with shared test vectors, so two libraries had no ground truth against which to check that they agreed. And one standard openly documented that an attacker could take a valid ciphertext and manufacture a &lt;em&gt;second, different&lt;/em&gt; valid ciphertext for the same plaintext. It named the phenomenon &quot;benign malleability&quot; and deemed it harmless [@sec1].&lt;/p&gt;
&lt;p&gt;The hard part of &quot;encrypt to a public key,&quot; it turned out, was never the cryptography. It was getting everyone to do the same correct thing, with a proof that it was correct and a way to check that two implementations interoperated. That failure was tolerable when public-key encryption was a batch operation between parties who could negotiate a format. It stopped being tolerable the moment the modern web needed to encrypt to a server it had never spoken to.&lt;/p&gt;
&lt;p&gt;Consider what TLS Encrypted Client Hello and Oblivious HTTP actually require. A client must encrypt the sensitive part of its connection -- in Encrypted Client Hello, the server name it is trying to reach -- to a public key it fetched moments earlier, for a server it has exchanged no messages with, with no round trip in which to negotiate anything [@rfc9849], [@rfc9458]. There is no handshake to agree on a KDF or a point encoding. The format has to be right the first time, on the first byte, or the connection reveals exactly what it was trying to hide.&lt;/p&gt;
&lt;p&gt;That is the setting Hybrid Public Key Encryption was built for. HPKE, specified in RFC 9180 by the IRTF&apos;s Crypto Forum Research Group in February 2022, is the boring, correct answer [@rfc9180]. Its headline achievements sound almost too unglamorous to matter: it publishes test vectors, and it comes with a proof that it is secure against chosen-ciphertext attack.&lt;/p&gt;
&lt;p&gt;Those two boring facts are exactly why HPKE matters. Once you accept them, the interesting question is not whether HPKE is secure but a sharper one, worth carrying through every section that follows: &lt;em&gt;you have a stranger&apos;s public key and one shot to send them a secret, with no handshake and no reply -- what does HPKE buy you, and what does it silently refuse to give?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is Part 19 of a field guide for people who design protocols, and it is the part where the primitives from earlier parts -- authenticated encryption, key derivation, elliptic curves, the KEM-DEM idea, digital signatures -- stop being separate ingredients and compose into a single shipped building block. By the end you will be able to look at any &quot;encrypt to a recipient&apos;s public key&quot; design and name, on sight, which of HPKE&apos;s modes it is using and which of HPKE&apos;s non-goals it is quietly assuming away.&lt;/p&gt;
&lt;p&gt;The fix is so unglamorous it is tempting to skip straight to the API. Do not: the shape of HPKE is written in the failures of the four standards it replaced, and you cannot use it safely without seeing what &quot;done right&quot; was correcting. Start with the one idea all four were circling and none quite pinned down: the difference between &lt;em&gt;encrypting&lt;/em&gt; a key and &lt;em&gt;encapsulating&lt;/em&gt; one.&lt;/p&gt;
&lt;h2&gt;2. Encapsulate the Key, Do Not Encrypt It&lt;/h2&gt;
&lt;p&gt;RFC 9180 describes what it does differently in a single line, and that line is the whole conceptual pivot of this article. The traditional combination, it says, has been to &quot;encrypt the symmetric key with the public key.&quot; HPKE instead will &quot;generate the symmetric key and its encapsulation with the public key&quot; [@rfc9180]. That one-word swap -- from &lt;em&gt;encrypt&lt;/em&gt; to &lt;em&gt;encapsulate&lt;/em&gt; -- is the difference between the padding-oracle-prone lineage that broke RSA key transport and a clean, composable, provable construction.&lt;/p&gt;

The traditional combination has been &quot;encrypt the symmetric key with the public key.&quot; HPKE instead will &quot;generate the symmetric key and its encapsulation with the public key.&quot; -- RFC 9180
&lt;p&gt;Build the mental model in three moves. Derive only what the rest of the article needs.&lt;/p&gt;
&lt;h3&gt;Move one: KEM-DEM in one picture&lt;/h3&gt;

The scheme standardized in RFC 9180 for encrypting arbitrary-length messages to a recipient&apos;s public key. It is built from three interchangeable ingredients -- a Key Encapsulation Mechanism (KEM), a Key Derivation Function (KDF), and an Authenticated Encryption with Associated Data (AEAD) scheme -- named together as a ciphersuite.
&lt;p&gt;The word &quot;hybrid&quot; means it splits the work between public-key and symmetric cryptography, and the split follows the KEM-DEM pattern. The KEM does the public-key half; the DEM does the symmetric half.&lt;/p&gt;

A public-key primitive that does not transport a key you chose. Called with the recipient&apos;s public key, `Encap(pkR)` *generates* a fresh random shared secret together with an encapsulation `enc` of it, returning the pair `(shared_secret, enc)`. The recipient runs `Decap(skR, enc)` to recover the same shared secret. You never pick the key; the public-key operation produces it.

The symmetric half of a hybrid scheme: an AEAD that bulk-encrypts the actual payload under a key derived from the KEM&apos;s shared secret. In HPKE the DEM is a single AEAD scheme, and its authenticity tag is intrinsic rather than bolted on.
&lt;p&gt;If you have read Part 14 on &lt;a href=&quot;https://paragmali.com/blog/rsa-is-a-trapdoor-not-a-cryptosystem-oaep-pss-and-the-25-yea/&quot; rel=&quot;noopener&quot;&gt;getting RSA right&lt;/a&gt;, this is the same KEM-DEM skeleton, now instantiated on elliptic curves rather than RSA. The reason to &lt;em&gt;generate&lt;/em&gt; the key rather than &lt;em&gt;encrypt&lt;/em&gt; one is the entire lesson of that part: the moment you encrypt an attacker-influenced key under a public-key operation, you invite a decryption oracle to leak information one query at a time. A KEM removes the attacker&apos;s chosen input from the public-key step. There is nothing to pad, and nothing to probe.&lt;/p&gt;

flowchart TD
    subgraph LEFT[&quot;Encrypt the key (RSA and ElGamal key transport)&quot;]
        A1[&quot;Choose a random symmetric key k&quot;] --&amp;gt; A2[&quot;Encrypt k under the recipient public key&quot;]
        A2 --&amp;gt; A3[&quot;Ship wrapped k plus the symmetric ciphertext&quot;]
    end
    subgraph RIGHT[&quot;Encapsulate the key (KEM-DEM, HPKE)&quot;]
        B1[&quot;Encap(pkR) generates a fresh shared secret&quot;] --&amp;gt; B2[&quot;Returns shared_secret and enc together&quot;]
        B2 --&amp;gt; B3[&quot;Derive keys, then AEAD-seal the payload&quot;]
    end
&lt;h3&gt;Move two: DHKEM, concretely&lt;/h3&gt;
&lt;p&gt;HPKE&apos;s deployed KEM is Diffie-Hellman-based, and it is worth seeing the mechanism once rather than treating it as a black box.&lt;/p&gt;

HPKE&apos;s Diffie-Hellman-based KEM. `Encap(pkR)` generates an ephemeral key pair, computes a Diffie-Hellman value against the recipient&apos;s public key, and derives the shared secret from it; the encapsulation `enc` is the serialized ephemeral public key. The authenticated variants `AuthEncap`/`AuthDecap` additionally fold in a Diffie-Hellman value computed with the sender&apos;s *static* key.
&lt;p&gt;Concretely, &lt;code&gt;Encap(pkR)&lt;/code&gt; generates an ephemeral pair $(sk_E, pk_E)$, computes $\mathrm{DH}(sk_E, pk_R)$, sets &lt;code&gt;enc&lt;/code&gt; to the serialization of $pk_E$, and returns &lt;code&gt;shared_secret = ExtractAndExpand(dh, kem_context)&lt;/code&gt;, where &lt;code&gt;kem_context&lt;/code&gt; already contains both &lt;code&gt;enc&lt;/code&gt; and the recipient key $pk_R$ [@rfc9180]. The recipient recomputes the identical Diffie-Hellman value as $\mathrm{DH}(sk_R, pk_E)$ and derives the same secret. This is exactly the ephemeral-static shape that DHIES introduced in the 1990s, now wearing a modern KDF -- and the decision to fold $pk_R$ into &lt;code&gt;kem_context&lt;/code&gt; is the first appearance of the idea that later kills benign malleability.&lt;/p&gt;

sequenceDiagram
    participant S as Sender (holds pkR)
    participant R as Recipient (holds skR)
    Note over S: generate ephemeral pair skE, pkE
    Note over S: dh holds DH(skE, pkR)
    Note over S: enc holds Serialize(pkE)
    Note over S: shared_secret from ExtractAndExpand(dh, kem_context with enc and pkR)
    S-&amp;gt;&amp;gt;R: send enc (the serialized ephemeral key)
    Note over R: dh holds DH(skR, pkE), the same value
    Note over R: recompute the identical shared_secret
&lt;h3&gt;Move three: the key schedule and the Context&lt;/h3&gt;
&lt;p&gt;The shared secret is not used directly. Both sides run it through a key schedule.&lt;/p&gt;

The HKDF Extract-then-Expand process that turns the KEM&apos;s `shared_secret` into working keys. It *extracts* a pseudorandom key from the shared secret (and, in PSK modes, the PSK), then *expands* that key over a context of the mode identifier, a hash of the pre-shared-key identifier (`psk_id_hash`), and a hash of the application `info` string (`info_hash`) to output the AEAD `key`, a `base_nonce`, and an `exporter_secret`.

A symmetric primitive that provides confidentiality and integrity at once: it encrypts a plaintext and authenticates both the ciphertext and some associated data, so any tampering is detected on decryption. In HPKE it is the DEM that seals the payload; AES-128-GCM, AES-256-GCM, and ChaCha20-Poly1305 are the standard choices.
&lt;p&gt;The HPKE key schedule is, quite literally, a worked example of &lt;a href=&quot;https://paragmali.com/blog/one-secret-is-not-one-key-the-discipline-of-key-derivation-w/&quot; rel=&quot;noopener&quot;&gt;the HKDF construction from Part 13&lt;/a&gt;: extract a pseudorandom key from the shared secret, then expand it into the specific bytes each downstream primitive needs. Its outputs assemble into a &lt;code&gt;Context&lt;/code&gt;. The &lt;code&gt;Context&lt;/code&gt; exposes &lt;code&gt;Seal&lt;/code&gt;/&lt;code&gt;Open&lt;/code&gt; -- a stateful pair in which an incrementing sequence number is combined with &lt;code&gt;base_nonce&lt;/code&gt; (by exclusive-or) to give every message a distinct nonce -- and &lt;code&gt;Export&lt;/code&gt;.&lt;/p&gt;

An HPKE interface that derives fresh, independent secrets of any length from the established context, for keying *other* protocols. It behaves as a variable-length pseudorandom function, so exported secrets are independent of the AEAD key and of each other. Oblivious HTTP and Oblivious DNS use it to key their own message framing instead of calling `Seal` directly.
&lt;p&gt;For a single message you do not need to hold a &lt;code&gt;Context&lt;/code&gt; open at all: the single-shot &lt;code&gt;Seal&lt;/code&gt;/&lt;code&gt;Open&lt;/code&gt; API wraps setup, one encryption, and teardown into one call. Everything above -- encapsulate, derive, seal -- is what those single-shot calls do underneath.&lt;/p&gt;

flowchart TD
    PKR[&quot;Recipient public key pkR&quot;] --&amp;gt; ENCAP[&quot;Encap&quot;]
    ENCAP --&amp;gt; SS[&quot;shared_secret&quot;]
    ENCAP --&amp;gt; ENCV[&quot;enc (sent to recipient)&quot;]
    SS --&amp;gt; KS[&quot;key_schedule (HKDF Extract-then-Expand)&quot;]
    MODE[&quot;mode identifier&quot;] --&amp;gt; KS
    PSK[&quot;psk_id_hash&quot;] --&amp;gt; KS
    INFO[&quot;info_hash&quot;] --&amp;gt; KS
    KS --&amp;gt; KEY[&quot;AEAD key&quot;]
    KS --&amp;gt; NON[&quot;base_nonce&quot;]
    KS --&amp;gt; EXP[&quot;exporter_secret&quot;]
    KEY --&amp;gt; CTX[&quot;Context&quot;]
    NON --&amp;gt; CTX
    EXP --&amp;gt; CTX
    CTX --&amp;gt; SEAL[&quot;Seal / Open (per-message nonce)&quot;]
    CTX --&amp;gt; EXPORT[&quot;Export (key other protocols)&quot;]
&lt;p&gt;The structural model below is a toy, not real cryptography, but it lets you &lt;em&gt;feel&lt;/em&gt; the difference from wrapping a chosen key: &lt;code&gt;encap&lt;/code&gt; invents a random secret and its encapsulation, folds in the recipient&apos;s public key, and shows that a different recipient key yields a different secret from the same encapsulation.&lt;/p&gt;
&lt;p&gt;{`
// STRUCTURAL MODEL, NOT REAL CRYPTO. It only shows the SHAPE of &quot;generate the key&quot;.
function toyMix(str) {                       // a tiny non-cryptographic mixer
  let h = 2166136261 &amp;gt;&amp;gt;&amp;gt; 0;
  for (const ch of str) { h ^= ch.charCodeAt(0); h = Math.imul(h, 16777619) &amp;gt;&amp;gt;&amp;gt; 0; }
  return (h &amp;gt;&amp;gt;&amp;gt; 0).toString(16);
}
function randInt() { return Math.floor(Math.random() * 1e9); }&lt;/p&gt;
&lt;p&gt;// Encap: the PUBLIC-KEY op GENERATES the shared secret. The caller never picks it.
function encap(pkR) {
  const skE = randInt();                     // fresh ephemeral secret
  const enc = &apos;pkE(&apos; + skE + &apos;)&apos;;            // its public value IS the encapsulation
  const dh  = toyMix(enc + &apos;|&apos; + pkR);       // stands in for DH(skE, pkR)
  // kem_context binds BOTH enc and pkR into the derivation -- the anti-malleability trick
  const shared_secret = toyMix(&apos;ss|&apos; + dh + &apos;|&apos; + enc + &apos;|&apos; + pkR);
  return { shared_secret, enc };
}&lt;/p&gt;
&lt;p&gt;const pkR = &apos;recipient-key-A&apos;;
const { shared_secret, enc } = encap(pkR);
console.log(&apos;enc on the wire     = &apos; + enc);
console.log(&apos;shared_secret       = &apos; + shared_secret);&lt;/p&gt;
&lt;p&gt;// Same enc, a DIFFERENT recipient key -&amp;gt; a DIFFERENT secret. A swapped or mangled
// public key simply cannot derive the key that decrypts, because pkR is bound in.
const swapped = toyMix(&apos;ss|&apos; + toyMix(enc + &apos;|recipient-key-B&apos;) + &apos;|&apos; + enc + &apos;|recipient-key-B&apos;);
console.log(&apos;same enc, key B     = &apos; + swapped);
console.log(&apos;secrets differ      = &apos; + (swapped !== shared_secret));
`}&lt;/p&gt;
&lt;p&gt;Now the payoff, and the first place your understanding should shift.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Encapsulate, do not encrypt. Let the public-key operation &lt;em&gt;generate&lt;/em&gt; the symmetric key and its encapsulation together (a KEM), then AEAD-encrypt under a derived key (a DEM). Because the composite is secure against chosen-ciphertext attack whenever both halves are, &quot;encrypt to a public key&quot; stops being an ad-hoc scheme you must re-prove each time and becomes a composition of two well-understood parts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The modularity is the whole point. Cramer and Shoup proved in 2001 that a KEM and a DEM, each secure against chosen-ciphertext attack, compose into a hybrid scheme that is too [@cs01]; a few years later the converse was pinned down, showing both halves essentially &lt;em&gt;have&lt;/em&gt; to be that strong for the composite to be [@hhk06]. Those results turn &quot;is this encrypt-to-a-public-key scheme secure?&quot; into two smaller, reusable questions.RFC 9180 notes in passing that its Base mode and the original Cramer-Shoup scheme are both &quot;named HPKE&quot; -- the name has KEM-DEM in its bones [@rfc9180], [@cs01].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; RFC 9180 is an &lt;em&gt;Informational&lt;/em&gt; IRTF/CFRG document, not a Standards-Track one. Yet it is normatively &lt;em&gt;required&lt;/em&gt; by Standards-Track protocols -- MLS (RFC 9420), Oblivious HTTP (RFC 9458), and Encrypted Client Hello (RFC 9849) all cite it as a mandatory dependency. Its category label undersells how much of the modern stack rests on it [@rfc9180].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This clean picture -- encapsulate, derive, seal -- looks obvious in hindsight. It was not. The KEM-DEM idea took two decades to take its final shape, and the Diffie-Hellman instantiation it rests on fragmented into the four broken standards from the last section before anyone consolidated it. To understand why HPKE makes the exact choices it does, walk the twenty-five years it is the endpoint of.&lt;/p&gt;
&lt;h2&gt;3. From &quot;Encrypt the Key&quot; to &quot;Encapsulate the Key&quot;&lt;/h2&gt;
&lt;p&gt;The story is a twenty-five-year march from &lt;em&gt;encrypt the key&lt;/em&gt; to &lt;em&gt;encapsulate the key&lt;/em&gt;, starting, as RFC 9180 notes, in the early days of public-key cryptography [@rfc9180]. Diffie and Hellman defined the idea of a public key in 1976 but did not say how to encrypt a long message to one [@dh76]. The answers accumulated in layers, each fixing the previous one&apos;s sharpest failure.&lt;/p&gt;

timeline
    title From encrypt-the-key to encapsulate-the-key, 1976 to 2026
    1976 : Diffie and Hellman define public-key cryptography
    1993 : PEM (RFC 1421) encrypts the content key under the recipient public key
    1999 : DHIES derives the key from Diffie-Hellman instead of encrypting it
    2001 : Cramer and Shoup formalize the KEM-DEM pattern
    2009 : SEC 1 v2 completes the four-way ECIES split and names benign malleability
    2022 : RFC 9180 consolidates KEM-DEM with proofs and test vectors
    2026 : Post-quantum hybrid KEMs such as X-Wing drop into the KEM slot
&lt;p&gt;&lt;strong&gt;PEM (RFC 1421, 1993): the traditional combination.&lt;/strong&gt; Privacy Enhanced Mail defined the canonical early hybrid. Pick a content-encryption key, &lt;em&gt;encrypt that key&lt;/em&gt; under the recipient&apos;s public key, and bulk-encrypt the message symmetrically. RFC 9180 opens by naming this exact tradition and positioning HPKE as the departure from it [@rfc1421], [@rfc9180]. It is the naive model most engineers still carry, and the one the previous section asked you to give up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ElGamal (1985) and why Diffie-Hellman resists &quot;encrypt a chosen key.&quot;&lt;/strong&gt; ElGamal&apos;s public-key encryption predates the hybrid schemes and shows the friction directly [@elgamal85]. To &quot;encrypt&quot; a chosen symmetric key under a Diffie-Hellman public key, you must first encode that key as a group element, then pay a roughly twofold ciphertext expansion -- and the result is malleable rather than secure against chosen-ciphertext attack. That malleability is a reasoned inference here, anchored to DHIES&apos;s own claim to offer &quot;stronger security properties than ElGamal,&quot; not a verbatim quote [@dhies_ms].&lt;/p&gt;
&lt;p&gt;The RSA branch of the same idea failed even more loudly. Encrypting a chosen key under RSA with PKCS#1 v1.5 padding is precisely the construction that Bleichenbacher&apos;s attack turned into a decryption oracle, revived two decades later as ROBOT against nearly a third of the top 100 web domains [@rfc8017], [@robot_site], [@robot_eprint]. The part of this series on getting RSA right tells that story in full; both branches teach the same lesson: stop choosing a key and wrapping it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DHIES and DHAES (Abdalla, Bellare, Rogaway, 1999): derive, do not encrypt.&lt;/strong&gt; The academic root of ECIES made the decisive move. Instead of encrypting a key, run an ephemeral-static Diffie-Hellman exchange, push the result through a KDF to get an encryption key and a MAC key, then Encrypt-then-MAC the payload. No key is ever encrypted; it is &lt;em&gt;agreed&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The authors proved the scheme secure against chosen-ciphertext attack under the Oracle Diffie-Hellman assumption, with &quot;no random oracles involved,&quot; and called it &quot;an attractive starting point for developing public-key encryption standards&quot; [@dhaes], [@dhies_ms].DHIES has no single crisp date: the DHAES manuscript appeared as ePrint 1999/007 in 1999, was later renamed DHIES, and its formal analysis was published at CT-RSA 2001. Cite the range, not one year [@dhaes], [@dhies_ms]. That parenthetical -- &quot;an attractive starting point for developing standards&quot; -- turned out to be prophetic in the worst way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cramer and Shoup (2001): the modular abstraction.&lt;/strong&gt; DHIES hinted at a structure; Cramer and Shoup formalized it. A KEM transports a random key, a DEM encrypts the payload, and the composite is secure against chosen-ciphertext attack if both halves are [@cs01]. A few years later the necessity direction was worked out: for the standard hybrid, both halves essentially have to reach that bar [@hhk06]. This is the theory HPKE would eventually standardize. It existed, complete, in 2001.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; By 2001 the field had both a clean, proof-backed academic scheme (DHIES) and the modular abstraction that made its security compositional (KEM-DEM). The striking fact about HPKE is not that someone invented a new idea in 2022. It is that the correct idea sat published and unadopted &lt;em&gt;as a standard&lt;/em&gt; for two decades [@cs01], [@rfc9180].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So by 2001 everything HPKE needed already existed: a concrete, provably secure ephemeral-static scheme, and an abstraction that made &quot;encrypt to a public key&quot; a matter of plugging two proven pieces together. What happened over the following eight years was not progress. It was fragmentation. The same good idea was handed to four different standards bodies, and it came out four incompatible ways.&lt;/p&gt;
&lt;h2&gt;4. The ECIES Zoo&lt;/h2&gt;
&lt;p&gt;Between 2001 and 2009 the DHIES construction was standardized four times -- ANSI X9.63, IEEE 1363a, ISO/IEC 18033-2, and SEC 1 v2 -- collectively called ECIES, and collectively a mess that RFC 9180 politely calls &quot;numerous competing and non-interoperable standards&quot; [@rfc9180], [@maea10]. This is the hook&apos;s promise, paid off in detail. Three defects run through the era, and HPKE was built to close all three.&lt;/p&gt;

flowchart TD
    D[&quot;DHIES (Abdalla, Bellare, Rogaway)&quot;] --&amp;gt; A[&quot;ANSI X9.63 (2001)&quot;]
    D --&amp;gt; I[&quot;IEEE 1363a (2004)&quot;]
    D --&amp;gt; S[&quot;ISO/IEC 18033-2 (2006)&quot;]
    D --&amp;gt; C[&quot;SEC 1 v2 (2009)&quot;]
    A --&amp;gt; Z[&quot;The ECIES zoo: incompatible KDFs, MACs, point encodings, no shared test vectors&quot;]
    I --&amp;gt; Z
    S --&amp;gt; Z
    C --&amp;gt; Z
    Z --&amp;gt; H[&quot;RFC 9180 (2022): one construction, proven, test-vectored&quot;]
&lt;p&gt;&lt;strong&gt;Fragmentation: the &quot;which ECIES?&quot; problem.&lt;/strong&gt; The four standards disagree on the key-derivation function, the MAC, whether elliptic-curve points are compressed or uncompressed, and whether the optional &lt;code&gt;SharedInfo&lt;/code&gt; fields are present. So two conforming implementations often cannot interoperate: an ECIES ciphertext built with the ANSI X9.63 KDF and one built to ISO/IEC 18033-2, for the same curve and message, are byte-incompatible [@rfc9180], [@maea10].The concrete interoperability failure is not hypothetical: swap the KDF or the point-encoding convention between two of the four standards and a ciphertext valid under one is undecryptable under another, for identical inputs [@maea10].&lt;/p&gt;

The four standards -- ANSI X9.63 (2001), IEEE 1363a (2004), ISO/IEC 18033-2 (2006), and SEC 1 version 2 (2009) -- differ across enough independent knobs (KDF, MAC, point-encoding, `SharedInfo`) that &quot;ECIES&quot; names a family of mutually unintelligible dialects rather than one scheme. RFC 9180 points readers to the Gayoso Martinez et al. survey &quot;for a thorough comparison.&quot; That survey and three of the four standards are paywalled, so this article cites them through RFC 9180&apos;s reference section rather than by live link [@rfc9180], [@maea10], [@sec1].
&lt;p&gt;&lt;strong&gt;Benign malleability.&lt;/strong&gt; SEC 1 version 2 documents a specific defect and then waves it off.&lt;/p&gt;

A property of the ECIES standards in which an attacker can transform a valid ciphertext into a different valid ciphertext for the *same* plaintext -- concretely, by replacing the ephemeral point $R$ with $-R$, which shares the same x-coordinate. SEC 1 called it &quot;benign malleability&quot; and &quot;generally deemed harmless,&quot; and offered binding $R$ into `SharedInfo` only as an *optional* countermeasure. Because a ciphertext can be mauled into a second valid ciphertext, the scheme as written is not strictly secure against chosen-ciphertext attack [@sec1].
&lt;p&gt;The word &quot;benign&quot; is doing a great deal of work. A scheme that lets anyone produce a second valid ciphertext for a plaintext they cannot read is, by definition, malleable, and malleability is exactly what security against chosen-ciphertext attack forbids. SEC 1 made the fix -- include $R$ in the hashed context -- optional. Hold that thought: it is the single design decision the next section shows HPKE making mandatory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No proofs, and no test vectors.&lt;/strong&gt; RFC 9180 is blunt about the state it inherited: prior schemes &quot;rely on outdated primitives, lack proofs of IND-CCA2 security, or fail to provide test vectors&quot; [@rfc9180]. The missing test vectors are not a documentation nicety. Without a published set of known-good inputs and outputs, two implementers have no way to discover that their libraries disagree until a ciphertext fails to decrypt in production -- which is precisely how &quot;conformant&quot; implementations drifted apart.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The ECIES era is the standing proof that a primitive can be widely deployed and still be under-specified, unprovable, and mutually incompatible. &quot;We implemented the standard&quot; told you nothing about whether your ciphertext would decrypt on the other side, or whether the scheme met a modern security definition. Both had to be established separately, and neither was [@rfc9180], [@maea10].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The comparison below is the historical spine as a grid. Read the last two columns -- proof status and test vectors -- and the shape of the fix becomes obvious.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Core idea&lt;/th&gt;
&lt;th&gt;Main weakness&lt;/th&gt;
&lt;th&gt;Proof and test vectors&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;RSA key transport (PKCS#1 v1.5)&lt;/td&gt;
&lt;td&gt;Encrypt a chosen key under RSA&lt;/td&gt;
&lt;td&gt;Padding oracle (Bleichenbacher, ROBOT)&lt;/td&gt;
&lt;td&gt;No CCA2 proof; padding fragile&lt;/td&gt;
&lt;td&gt;Deprecated in TLS 1.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElGamal encryption&lt;/td&gt;
&lt;td&gt;Encode a chosen key as a group element&lt;/td&gt;
&lt;td&gt;Malleable; roughly 2x expansion&lt;/td&gt;
&lt;td&gt;No CCA2 proof&lt;/td&gt;
&lt;td&gt;Historical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DHIES / DHAES&lt;/td&gt;
&lt;td&gt;Derive the key from ephemeral-static DH&lt;/td&gt;
&lt;td&gt;Underspecified as a deployable format&lt;/td&gt;
&lt;td&gt;CCA2 proof (ODH); no shared vectors&lt;/td&gt;
&lt;td&gt;Academic root of ECIES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECIES (four standards)&lt;/td&gt;
&lt;td&gt;Standardize DHIES&lt;/td&gt;
&lt;td&gt;Fragmented; benign malleability&lt;/td&gt;
&lt;td&gt;Proof gaps; no shared vectors&lt;/td&gt;
&lt;td&gt;Legacy interop only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HPKE (RFC 9180)&lt;/td&gt;
&lt;td&gt;KEM-DEM, all public keys bound in&lt;/td&gt;
&lt;td&gt;Non-goals must be respected&lt;/td&gt;
&lt;td&gt;IND-CCA2 proof; per-suite vectors&lt;/td&gt;
&lt;td&gt;Current default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HPKE + X-Wing&lt;/td&gt;
&lt;td&gt;Swap DHKEM for a hybrid PQ KEM&lt;/td&gt;
&lt;td&gt;Larger ciphertexts; draft-stage&lt;/td&gt;
&lt;td&gt;Hybrid PQ proof; draft vectors&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Every one of these defects points at the same missing discipline: pin &lt;em&gt;one&lt;/em&gt; construction, bind the ambiguous inputs so malleability becomes impossible, prove the result, and publish vectors so anyone can check interoperation. That list is not a wishlist. It is, almost line for line, the changelog of RFC 9180. Here is what &quot;done right&quot; actually changed.&lt;/p&gt;
&lt;h2&gt;5. &quot;Done Right&quot; Was Discipline, Not Genius&lt;/h2&gt;
&lt;p&gt;There is no eureka moment in this story, and that is the point. HPKE&apos;s virtue is not a clever new algorithm -- the algorithm, KEM-DEM, was twenty years old when RFC 9180 shipped. Its virtue is four consolidating decisions, made once, correctly, with the proof and the test vectors published in the same body of work. Each decision closes one of the previous section&apos;s defects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decision one: abstract the KEM so the primitive is agile.&lt;/strong&gt; Instead of freezing one curve, one KDF, and one MAC into a single scheme, HPKE is parameterized over a ciphersuite of three identifiers: &lt;code&gt;kem_id&lt;/code&gt;, &lt;code&gt;kdf_id&lt;/code&gt;, and &lt;code&gt;aead_id&lt;/code&gt; [@rfc9180]. This looks like a small thing and is not. Fragmentation was the disease of the ECIES era, where each standards body froze a different combination.&lt;/p&gt;
&lt;p&gt;HPKE replaces uncontrolled fragmentation with &lt;em&gt;managed&lt;/em&gt; agility: many ciphersuites, but one construction and one set of rules governing all of them. When a component needs replacing -- as the post-quantum transition will require -- you change an identifier, not the scheme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decision two: bind every public key into the key schedule.&lt;/strong&gt; This is the decisive fix, and RFC 9180 states it in one sentence.&lt;/p&gt;

&quot;HPKE mitigates malleability problems (called benign malleability) in prior public key encryption standards based on ECIES by including all public keys in the context of the key schedule.&quot; -- RFC 9180, Section 9.1
&lt;p&gt;What SEC 1 made optional, HPKE makes mandatory [@rfc9180], [@sec1]. Recall the benign-malleability trick: replace the ephemeral point $R$ with $-R$ to get a second valid ciphertext. In HPKE the public keys are inputs to the derived keys, so a mauled encapsulation derives a &lt;em&gt;different&lt;/em&gt; key schedule and simply fails to produce a valid decryption. The attack does not become hard; it becomes impossible to express. The malleability is not patched -- it is designed out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decision three: mandate a single AEAD as the DEM.&lt;/strong&gt; Gone is the ad-hoc two-key Encrypt-then-MAC with per-standard knobs for the cipher, the MAC, and the tag order. HPKE specifies one AEAD scheme per ciphersuite, so authenticity is intrinsic to the primitive rather than assembled by the caller [@rfc9180]. If the series&apos; part on &lt;a href=&quot;https://paragmali.com/blog/the-tag-verified-the-cipher-held-the-forgery-went-through-a-/&quot; rel=&quot;noopener&quot;&gt;safe authenticated-encryption composition&lt;/a&gt; has a single thesis -- compose it wrong and the tag verifies while the forgery goes through -- then HPKE is simply that &quot;compose it correctly&quot; rule frozen into a specification.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decision four: ship proofs and test vectors, developed alongside the standard.&lt;/strong&gt; This is the part the ECIES era never had. HPKE began as an individual draft in 2019 and was analyzed as it was written [@hpke_draft]. Lipp&apos;s CryptoVerif analysis mechanically checked all four modes; the analysis by Alwen, Blanchet, Hauck, Kiltz, Lipp, and Riepel gave exact security bounds for the authenticated mode and was published at EUROCRYPT 2021; and the two pre-shared-key modes were later closed under the Gap Diffie-Hellman assumption [@lipp20], [@abhklr20], [@psk1480]. Every ciphersuite ships machine-checkable test vectors in the RFC itself [@rfc9180].&lt;/p&gt;

The standard security goal for public-key encryption. An adversary who may submit ciphertexts of its choice to a decryption oracle -- adaptively, even after seeing the challenge -- still cannot tell which of two chosen plaintexts a challenge ciphertext encrypts. It is the bar the ECIES standards could not prove and that malleability violates, and the bar HPKE&apos;s composition meets when its KEM and AEAD do [@cs01], [@hhk06].
&lt;p&gt;Put the four together and your sense of what happened should shift.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &quot;Done right&quot; was consolidation, not invention. Abstract the KEM for agility, bind every public key into the key schedule so benign malleability cannot happen, mandate one AEAD instead of ad-hoc Encrypt-then-MAC, and ship machine-checked proofs &lt;em&gt;and&lt;/em&gt; per-ciphersuite test vectors in the same document. The innovation is rigor -- which is exactly why HPKE is boring, and why boring is the achievement.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The KEM-DEM theory was published in 2001 and simply not adopted as the standard until 2022. HPKE is the merge of a two-decade-old proof track with a standards track: it did not out-think the ECIES authors so much as out-&lt;em&gt;discipline&lt;/em&gt; them, pinning one construction and refusing to ship it without proofs and vectors [@cs01], [@rfc9180].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is even a small tell that HPKE was designed to be built &lt;em&gt;on&lt;/em&gt;, not just used directly.HPKE reserves an export-only AEAD identifier (the value &lt;code&gt;0xFFFF&lt;/code&gt;) for ciphersuites that never call &lt;code&gt;Seal&lt;/code&gt; and only ever use the &lt;code&gt;Export&lt;/code&gt; interface -- a strong hint that the authors expected HPKE to be keyed into other protocols rather than used standalone [@rfc9180].&lt;/p&gt;
&lt;p&gt;Four decisions turn a fragmented, unprovable family into one boring, correct primitive. But &quot;one primitive&quot; is not &quot;one behavior.&quot; HPKE ships a small menu -- four modes -- and choosing the wrong one is the first and most common way to misuse it. The menu is where the design stops being about the math and starts being about what &lt;em&gt;you&lt;/em&gt; are trying to authenticate.&lt;/p&gt;
&lt;h2&gt;6. The Four Modes: HPKE&apos;s Core Menu&lt;/h2&gt;
&lt;p&gt;RFC 9180, Section 5.1, defines exactly four modes, and the difference between them reduces to a single question: &lt;em&gt;what extra secret or identity gets folded into the key schedule?&lt;/em&gt; Get the answer wrong and you either fail to authenticate a sender you needed to, or -- worse -- come to believe you authenticated one you did not [@rfc9180]. There are four modes, never three: the pre-shared-key-plus-authenticated combination is a real, distinct mode that is easy to forget.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Extra input&lt;/th&gt;
&lt;th&gt;What it authenticates&lt;/th&gt;
&lt;th&gt;Use when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mode_base&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Only that the ciphertext is for this recipient key&lt;/td&gt;
&lt;td&gt;You have just the recipient&apos;s public key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mode_psk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x01&lt;/td&gt;
&lt;td&gt;a pre-shared key&lt;/td&gt;
&lt;td&gt;Recipient, plus possession of the PSK&lt;/td&gt;
&lt;td&gt;A symmetric secret already exists out of band&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mode_auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x02&lt;/td&gt;
&lt;td&gt;sender&apos;s static key&lt;/td&gt;
&lt;td&gt;That a specific sender key holder sent it&lt;/td&gt;
&lt;td&gt;The recipient must know which sender key sealed it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mode_auth_psk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x03&lt;/td&gt;
&lt;td&gt;static key and PSK&lt;/td&gt;
&lt;td&gt;Both a sender identity and PSK possession&lt;/td&gt;
&lt;td&gt;You need sender identity and a shared secret&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mode_base&lt;/code&gt; (0x00): nothing extra.&lt;/strong&gt; The sender is anonymous; the derivation authenticates only that the ciphertext was sealed to this recipient&apos;s key. It is the default -- and, tellingly, the mode that every flagship deployment in the next section uses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mode_psk&lt;/code&gt; (0x01): a pre-shared key.&lt;/strong&gt; A symmetric secret known to both parties is folded into the schedule, so a valid decryption additionally proves the sender possessed the PSK.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;mode_psk&lt;/code&gt; authenticates &lt;em&gt;possession of the PSK&lt;/em&gt;, which means a weak PSK is an authentication that anyone who guesses it can forge. RFC 9180 requires a PSK with at least 32 bytes of entropy, and the reason is not arbitrary: a low-entropy PSK opens a partitioning-oracle attack that the non-goals section quantifies. Treat the PSK as key material, never as a human-chosen secret [@rfc9180].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mode_auth&lt;/code&gt; (0x02): the sender&apos;s static key.&lt;/strong&gt; Here &lt;code&gt;Encap&lt;/code&gt;/&lt;code&gt;Decap&lt;/code&gt; are replaced by &lt;code&gt;AuthEncap&lt;/code&gt;/&lt;code&gt;AuthDecap&lt;/code&gt;, which fold a Diffie-Hellman value between the sender&apos;s &lt;em&gt;static&lt;/em&gt; key and the recipient into the shared secret. A valid decryption now proves the message came from the holder of that specific sender key [@rfc9180].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mode_auth_psk&lt;/code&gt; (0x03): both.&lt;/strong&gt; A static sender identity &lt;em&gt;and&lt;/em&gt; a shared secret, folded together.&lt;/p&gt;

flowchart TD
    B[&quot;mode_base 0x00: nothing extra&quot;] --&amp;gt; KS[&quot;key_schedule context&quot;]
    P[&quot;mode_psk 0x01: plus a pre-shared key&quot;] --&amp;gt; KS
    A[&quot;mode_auth 0x02: plus sender static key via AuthEncap&quot;] --&amp;gt; KS
    AP[&quot;mode_auth_psk 0x03: plus PSK and sender static key&quot;] --&amp;gt; KS
    KS --&amp;gt; OUT[&quot;Same construction, four different derived key schedules&quot;]
&lt;p&gt;Now the subtlety that trips up careful engineers. The authenticated modes are a form of &lt;em&gt;signcryption&lt;/em&gt;, and they carry a security distinction imported from that theory.&lt;/p&gt;

A primitive that combines encryption and sender authentication in one operation, cheaper than encrypt-then-sign. Its authentication is defined relative to *insiders* and *outsiders*: an outsider is any third party, while an insider is a party who holds one of the long-term keys. HPKE&apos;s authenticated modes are signcryption, so their guarantees are stated in these terms rather than as a public, third-party-verifiable signature.
&lt;p&gt;The consequence matters: &lt;code&gt;mode_auth&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; a digital signature. Because the recipient shares the Diffie-Hellman secret that authenticated the sender, the recipient could have produced the same ciphertext, so it cannot prove to any third party who the author was -- there is no non-repudiation. The analysis by Alwen, Blanchet, Hauck, Kiltz, Lipp, and Riepel makes this precise with an insider-versus-outsider treatment lifted from signcryption theory [@abhklr20], [@signcryption]. If you want a third party to verify authorship, you want &lt;a href=&quot;https://paragmali.com/blog/the-math-held-the-interface-leaked-a-field-guide-to-digital-/&quot; rel=&quot;noopener&quot;&gt;the digital-signature part of this series&lt;/a&gt;, not HPKE&apos;s &lt;code&gt;auth&lt;/code&gt; mode.&lt;/p&gt;
&lt;p&gt;The security properties proven for each mode, from RFC 9180&apos;s Table 6 via Lipp&apos;s mechanized analysis, are worth seeing side by side [@rfc9180], [@lipp20]:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Base&lt;/th&gt;
&lt;th&gt;PSK&lt;/th&gt;
&lt;th&gt;Auth&lt;/th&gt;
&lt;th&gt;AuthPSK&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Message confidentiality (IND-CCA2)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export secrecy (variable-length PRF)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sender authentication&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra secret assumed&lt;/td&gt;
&lt;td&gt;recipient key secret&lt;/td&gt;
&lt;td&gt;plus PSK secret&lt;/td&gt;
&lt;td&gt;plus sender key secret&lt;/td&gt;
&lt;td&gt;plus sender key and PSK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The structural model below makes the &quot;same machinery, different inputs&quot; claim concrete: it builds a stand-in key-schedule context for each mode and shows the derived key differs, though nothing about the construction changed.&lt;/p&gt;
&lt;p&gt;{`
// STRUCTURAL MODEL, NOT REAL CRYPTO. Shows only that mode changes the derived key.
function toyMix(s){let h=2166136261&amp;gt;&amp;gt;&amp;gt;0;for(const ch of s){h^=ch.charCodeAt(0);h=Math.imul(h,16777619)&amp;gt;&amp;gt;&amp;gt;0;}return (h&amp;gt;&amp;gt;&amp;gt;0).toString(16);}
const shared_secret = &apos;ss-from-KEM&apos;;&lt;/p&gt;
&lt;p&gt;// Real HPKE: Extract a PRK from shared_secret, then Expand over mode + psk_id_hash + info_hash.
// This toy just mixes them (plus the sender key for auth modes) to show the mode changes the key.
function keySchedule(mode, pskId, senderKey) {
  const psk_id_hash = toyMix(&apos;pskid:&apos; + (pskId || &apos;&apos;));
  const info_hash   = toyMix(&apos;info:my-app-v1&apos;);
  const ss = senderKey ? toyMix(shared_secret + &apos;|auth:&apos; + senderKey) : shared_secret;
  return toyMix(mode + &apos;|&apos; + psk_id_hash + &apos;|&apos; + info_hash + &apos;|&apos; + ss);
}&lt;/p&gt;
&lt;p&gt;console.log(&apos;base     -&amp;gt; &apos; + keySchedule(&apos;0x00&apos;, null, null));
console.log(&apos;psk      -&amp;gt; &apos; + keySchedule(&apos;0x01&apos;, &apos;shared-psk&apos;, null));
console.log(&apos;auth     -&amp;gt; &apos; + keySchedule(&apos;0x02&apos;, null, &apos;sender-static-key&apos;));
console.log(&apos;auth_psk -&amp;gt; &apos; + keySchedule(&apos;0x03&apos;, &apos;shared-psk&apos;, &apos;sender-static-key&apos;));
console.log(&apos;all four derive different keys from identical machinery&apos;);
`}&lt;/p&gt;
&lt;p&gt;Choosing a mode answers &quot;who am I authenticating?&quot; It says nothing about which curve, which KDF, which AEAD, and how not to shoot yourself with the nonce. That is the ciphersuite-and-usage layer -- where the test vectors finally earn their keep, and where the known HPKE implementation advisories actually live.&lt;/p&gt;
&lt;h2&gt;7. Ciphersuites and Correct Usage&lt;/h2&gt;
&lt;p&gt;A mode plus a ciphersuite is a complete HPKE configuration. The ciphersuite is three identifiers -- a KEM, a KDF, and an AEAD -- and the correct-usage rules are mostly about a single thing: never let the same nonce protect two messages.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Slot&lt;/th&gt;
&lt;th&gt;Options&lt;/th&gt;
&lt;th&gt;How to choose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;KEM&lt;/td&gt;
&lt;td&gt;DHKEM(X25519, HKDF-SHA256); DHKEM(P-256/P-384/P-521, HKDF-SHA256/384/512); DHKEM(X448, HKDF-SHA512)&lt;/td&gt;
&lt;td&gt;X25519 unless policy requires NIST curves; encapsulation &lt;code&gt;enc&lt;/code&gt; is 32 bytes for X25519, 65 for P-256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KDF&lt;/td&gt;
&lt;td&gt;HKDF-SHA256, HKDF-SHA384, HKDF-SHA512&lt;/td&gt;
&lt;td&gt;Match the KDF hash to the KEM and AEAD strength&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD&lt;/td&gt;
&lt;td&gt;AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305, Export-only (0xFFFF)&lt;/td&gt;
&lt;td&gt;AES-GCM with AES hardware; ChaCha20-Poly1305 without; export-only when you never call &lt;code&gt;Seal&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The three menus map onto earlier parts of this series. DHKEM&apos;s curves are &lt;a href=&quot;https://paragmali.com/blog/the-curve-was-hard-the-gap-was-soft-a-field-guide-to-using-e/&quot; rel=&quot;noopener&quot;&gt;the elliptic curves of the curves part&lt;/a&gt;; pick X25519 for new designs, NIST curves only when a compliance regime demands them. The KDF slot is the HKDF of the key-derivation part, used exactly as designed. The AEAD slot 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 from the AEAD part&lt;/a&gt;: AES-GCM where the CPU has AES instructions, ChaCha20-Poly1305 where it does not. The tag is 16 bytes in every case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The nonce and sequence discipline is the crux.&lt;/strong&gt; Inside a &lt;code&gt;Context&lt;/code&gt;, the per-message nonce is computed as &lt;code&gt;nonce = base_nonce XOR seq&lt;/code&gt;, where &lt;code&gt;seq&lt;/code&gt; is an incrementing sequence number. The &lt;code&gt;Context&lt;/code&gt; is therefore &lt;em&gt;stateful&lt;/em&gt;, and the sequence number must never repeat and must never overflow, because AEAD nonce reuse is catastrophic: for AES-GCM it can leak the authentication key, and for any of these AEADs it breaks confidentiality outright [@rfc9180]. This is the same hazard &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;the nonce-reuse part of this series&lt;/a&gt; covers for AES-GCM, arriving here as a counter you must not let wrap.&lt;/p&gt;
&lt;p&gt;The structural model below computes a few per-message nonces and then deliberately overflows a narrow counter so you can watch a nonce repeat -- which is, in miniature, exactly the implementation bug the misuse section names.&lt;/p&gt;
&lt;p&gt;{`
// STRUCTURAL MODEL, NOT REAL CRYPTO. Toy 8-bit nonce; real HPKE nonces are 96 bits.
const base_nonce = 0xA7;
function nonceFor(seq) { return (base_nonce ^ (seq &amp;amp; 255)).toString(16); }&lt;/p&gt;
&lt;p&gt;for (let seq = 0; seq !== 5; seq++) {
  console.log(&apos;seq &apos; + seq + &apos; -&amp;gt; nonce &apos; + nonceFor(seq));
}&lt;/p&gt;
&lt;p&gt;// A deliberately NARROW counter overflows after 256 messages and REPEATS a nonce.
const width = 256;
const seqA = 3;
const seqWrap = seqA + width;                 // a later message with the same low 8 bits
console.log(&apos;seq &apos; + seqA + &apos;   -&amp;gt; nonce &apos; + nonceFor(seqA));
console.log(&apos;seq &apos; + seqWrap + &apos; -&amp;gt; nonce &apos; + nonceFor(seqWrap) + &apos;   (wrapped)&apos;);
console.log(&apos;nonce reused after overflow = &apos; + (nonceFor(seqA) === nonceFor(seqWrap)));
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer single-shot &lt;code&gt;Seal&lt;/code&gt;/&lt;code&gt;Open&lt;/code&gt; for independent messages -- it sets up, encrypts once, and tears down, so there is no counter to mismanage. If you hold a &lt;code&gt;Context&lt;/code&gt; open, &lt;code&gt;Seal&lt;/code&gt; strictly in order, respect the per-ciphersuite AEAD message-count and input-length limits, and never reuse a &lt;code&gt;Context&lt;/code&gt; across two independent streams. A repeated or overflowed sequence number is a repeated nonce, and a repeated nonce is the end of your confidentiality [@rfc9180].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two operational notes round out the usage rules. First, the AEAD message-count and plaintext-length limits are per-ciphersuite constants, and single-shot &lt;code&gt;Seal&lt;/code&gt;/&lt;code&gt;Open&lt;/code&gt; sidesteps the streaming bookkeeping entirely.When you need to protect &lt;em&gt;many&lt;/em&gt; messages or key a different protocol&apos;s framing, use the &lt;code&gt;Export&lt;/code&gt; interface to derive an independent secret rather than looping &lt;code&gt;Seal&lt;/code&gt; on one long-lived &lt;code&gt;Context&lt;/code&gt; -- this is exactly what Oblivious HTTP and Oblivious DNS do [@rfc9180]. Second, the one default to remember: &lt;code&gt;mode_base&lt;/code&gt; with DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, and AES-128-GCM is the interop-safe choice, with ChaCha20-Poly1305 substituted on hardware without AES acceleration.&lt;/p&gt;
&lt;p&gt;These rules read like an interop checklist because that is precisely what they are, and the only reason they &lt;em&gt;can&lt;/em&gt; be a checklist is the test vectors: you can prove your library agrees with the specification byte for byte before you ship. But a checklist for &lt;em&gt;using&lt;/em&gt; HPKE says nothing about &lt;em&gt;where it has already won&lt;/em&gt;. The strongest evidence that HPKE is the right default is that the modern privacy stack is built on it.&lt;/p&gt;
&lt;h2&gt;8. Deployments as the Proof&lt;/h2&gt;
&lt;p&gt;The best argument that HPKE is state of the art in 2026 is not a benchmark. It is a deployment list. Every &quot;encrypt to a party you have never spoken to&quot; corner of the modern IETF stack runs on HPKE, and they almost all run on the &lt;em&gt;same&lt;/em&gt; mode.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Specification&lt;/th&gt;
&lt;th&gt;What HPKE does&lt;/th&gt;
&lt;th&gt;Mode and API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Encrypted Client Hello&lt;/td&gt;
&lt;td&gt;RFC 9849 (2026)&lt;/td&gt;
&lt;td&gt;Encrypts the inner ClientHello, including the server name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mode_base&lt;/code&gt;, &lt;code&gt;SetupBaseS&lt;/code&gt;/&lt;code&gt;SetupBaseR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messaging Layer Security&lt;/td&gt;
&lt;td&gt;RFC 9420 (2023)&lt;/td&gt;
&lt;td&gt;Encrypts path secrets to tree nodes in TreeKEM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mode_base&lt;/code&gt;, &lt;code&gt;SealBase&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oblivious HTTP&lt;/td&gt;
&lt;td&gt;RFC 9458 (2024)&lt;/td&gt;
&lt;td&gt;Encapsulates requests and responses to a gateway&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mode_base&lt;/code&gt; plus &lt;code&gt;Export&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oblivious DNS over HTTPS&lt;/td&gt;
&lt;td&gt;RFC 9230 (2022)&lt;/td&gt;
&lt;td&gt;Encrypts DNS queries to a target through a proxy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mode_base&lt;/code&gt; plus &lt;code&gt;Export&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Encrypted Client Hello.&lt;/strong&gt; ECH encrypts the sensitive inner ClientHello -- including the server name a censor would want to read -- to the server&apos;s HPKE public key, published in an &lt;code&gt;ECHConfig&lt;/code&gt; and fetched ahead of time. It uses &lt;code&gt;SetupBaseS&lt;/code&gt; on the client and &lt;code&gt;SetupBaseR&lt;/code&gt; on the server [@rfc9849].Currency pin: ECH is now Standards-Track RFC 9849, published in March 2026 by Rescorla, Oku, Sullivan, and Wood, and formerly known as &lt;code&gt;draft-ietf-tls-esni&lt;/code&gt;. It is an RFC, not a draft, and its text references HPKE throughout [@rfc9849].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Messaging Layer Security.&lt;/strong&gt; MLS uses HPKE inside TreeKEM: when a group member updates the ratchet tree, it encrypts the new path secrets to the public keys of the relevant tree nodes, using &lt;code&gt;SealBase&lt;/code&gt; [@rfc9420]. Group-scale forward secrecy and post-compromise security come from the tree and the ratchet, not from HPKE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Oblivious HTTP and Oblivious DNS.&lt;/strong&gt; Both split trust between a relay and a gateway so that no single party sees both who you are and what you asked. The client seals its request to the gateway&apos;s HPKE key; the relay forwards ciphertext it cannot read; the gateway decapsulates and, crucially, uses &lt;code&gt;Export&lt;/code&gt; to key the response rather than looping &lt;code&gt;Seal&lt;/code&gt; [@rfc9458], [@rfc9230]. Oblivious DNS, in 2022, was the first shipped RFC deployment of HPKE.&lt;/p&gt;

flowchart LR
    C[&quot;Client seals request with SetupBaseS&quot;] --&amp;gt; REL[&quot;Relay sees ciphertext and client address, not content&quot;]
    REL --&amp;gt; GW[&quot;Gateway runs SetupBaseR, decrypts, Export-keys the reply&quot;]
    GW --&amp;gt; TGT[&quot;Target sees the request, never the client address&quot;]
&lt;p&gt;Privacy Pass and its Private Access Tokens belong on the list too, at one remove: some deployments run token issuance over Oblivious HTTP (RFC 9578 does not require it), and therefore over HPKE [@rfc9578], [@rfc9458].&lt;/p&gt;
&lt;p&gt;Two observations reinforce the article&apos;s spine. First, every marquee deployment uses &lt;code&gt;mode_base&lt;/code&gt;: sender authentication and forward secrecy are supplied &lt;em&gt;at the protocol layer&lt;/em&gt; -- by the TLS handshake, by the MLS ratchet -- leaving HPKE to be the boring sealing primitive it was designed to be. Second, Oblivious HTTP and Oblivious DNS reach for &lt;code&gt;Export&lt;/code&gt; to key their own framing instead of looping &lt;code&gt;Seal&lt;/code&gt;, exactly the extensibility the export interface was built for. The deployments did not stretch HPKE; they used the parts the design anticipated.&lt;/p&gt;

DHKEM is classical-only, which means every HPKE ciphertext sealed today is exposed to &quot;harvest now, decrypt later&quot;: an adversary can store it and decrypt it once a quantum computer can solve the elliptic-curve Diffie-Hellman problem. The fix is the payoff of Decision one -- swap the KEM, do not redesign HPKE. The leading candidate is X-Wing, a hybrid KEM combining X25519 and ML-KEM-768 that stays secure if *either* component holds, and it comes with a dedicated &quot;Use in HPKE&quot; mapping [@xwing], [@xwing_draft], [@fips203]. The cost is size: an X-Wing encapsulation is roughly 1120 bytes against 32 for DHKEM(X25519) [@xwing_draft06]. Draft-stage ciphersuites also include a pure ML-KEM option, a generic hybrid-KEM framework, and a combined post-quantum suite with a SHA-3-based KDF [@mlkem_draft], [@hybrid_draft], [@barnes_pq]. One caveat matters for the modes section: X-Wing is *not* an authenticated KEM, so the post-quantum path powers `base` and `psk` but not yet `auth` or `auth_psk` [@xwing_draft06].
&lt;p&gt;HPKE won the IETF privacy stack. But &quot;won&quot; invites the question a careful engineer actually asks: won &lt;em&gt;against what&lt;/em&gt;, and when would I still reach for something else? The honest comparison comes next -- along with the one construction people keep miscategorizing as HPKE.&lt;/p&gt;
&lt;h2&gt;9. Competing Approaches, and One That Is Not HPKE&lt;/h2&gt;
&lt;p&gt;HPKE is the right default for &lt;em&gt;new protocol design&lt;/em&gt;, but it is not the only encrypt-to-a-public-key tool, and it is not always the right one. Because all of these building blocks cost about the same -- roughly two elliptic-curve scalar multiplications for the sender, plus an ephemeral value and an AEAD tag of overhead -- the honest comparison is not speed. It is security model, agility, and proof discipline.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Agility and modes&lt;/th&gt;
&lt;th&gt;Proof and shared vectors&lt;/th&gt;
&lt;th&gt;Best suited for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;HPKE (RFC 9180)&lt;/td&gt;
&lt;td&gt;KEM-DEM (DHKEM + KDF + AEAD)&lt;/td&gt;
&lt;td&gt;Full agility; four modes&lt;/td&gt;
&lt;td&gt;Machine-checked; per-suite vectors&lt;/td&gt;
&lt;td&gt;New protocol design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;libsodium &lt;code&gt;crypto_box&lt;/code&gt; / seal&lt;/td&gt;
&lt;td&gt;Fixed X25519 + XSalsa20-Poly1305&lt;/td&gt;
&lt;td&gt;None; mutual-auth, repudiable&lt;/td&gt;
&lt;td&gt;No mechanized proof; partial vectors&lt;/td&gt;
&lt;td&gt;Both ends yours, zero knobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JOSE &lt;code&gt;ECDH-ES&lt;/code&gt; (RFC 7518)&lt;/td&gt;
&lt;td&gt;Ephemeral-static ECDH + Concat KDF&lt;/td&gt;
&lt;td&gt;Some agility; encryption only&lt;/td&gt;
&lt;td&gt;No mechanized proof; partial vectors&lt;/td&gt;
&lt;td&gt;JWE and JWT stacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;X25519 + HKDF-SHA256 + ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;None; file-oriented&lt;/td&gt;
&lt;td&gt;No mechanized proof; has vectors&lt;/td&gt;
&lt;td&gt;Encrypting files to a key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw ECIES&lt;/td&gt;
&lt;td&gt;Ephemeral-static ECDH + Encrypt-then-MAC&lt;/td&gt;
&lt;td&gt;Per-standard knobs&lt;/td&gt;
&lt;td&gt;Proof gaps; no shared vectors&lt;/td&gt;
&lt;td&gt;Legacy and blockchain interop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;libsodium &lt;code&gt;crypto_box&lt;/code&gt; and sealed boxes.&lt;/strong&gt; One fixed suite -- X25519, XSalsa20, Poly1305 -- simple and misuse-resistant, but with no agility, no formal mode menu, and no HPKE-grade test vectors; raw &lt;code&gt;crypto_box&lt;/code&gt; also makes the caller manage nonces, and it is deliberately repudiable and mutually authenticated [@nacl]. Reach for it when you own both ends and want zero knobs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JOSE and COSE &lt;code&gt;ECDH-ES&lt;/code&gt;.&lt;/strong&gt; Ephemeral-static ECDH with the Concat KDF, native to JWE and JWT [@rfc7518]. It has no &lt;code&gt;auth&lt;/code&gt; or &lt;code&gt;psk&lt;/code&gt; modes, uses Concat-KDF rather than HKDF, and ships nothing like HPKE&apos;s vectors. It is chosen by platform gravity, and a COSE-HPKE binding is emerging to bring HPKE &lt;em&gt;into&lt;/em&gt; that world [@cose_hpke].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;age&lt;/code&gt;.&lt;/strong&gt; Wraps a random file key to X25519 recipients with HKDF-SHA256 and ChaCha20-Poly1305 -- the same primitive family as DHKEM(X25519) plus an AEAD, but fixed and file-oriented [@age]. Reach for it to encrypt files to a public key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Raw ECIES&lt;/strong&gt; survives only for interop with existing deployments, such as blockchain wallets and legacy PKI [@sec1]. And &lt;strong&gt;RSA-KEM&lt;/strong&gt; (RFC 5990) is worth one line: it proves the KEM-DEM pattern generalizes beyond Diffie-Hellman, but RFC 9180 defines no RSA-KEM ciphersuite, so it stays a niche [@rfc5990].&lt;/p&gt;
&lt;p&gt;Now the boundary marker, because it is the single most common miscategorization in this space.&lt;/p&gt;

Apple&apos;s PQ3 protocol for iMessage is *KEM-based* -- as Apple describes it, the design combines a hybrid post-quantum KEM (ML-KEM/Kyber-1024 with P-256 ECDH) through HKDF-SHA384, inside a triple-ratchet message scheme [@apple_pq3] -- but it is **not** an RFC 9180 HPKE deployment, and it never invokes HPKE. It solves a different problem: stateful, forward-secret, *interactive* messaging with post-compromise security, where HPKE is single-shot public-key encryption to a static key. PQ3 belongs here only to mark the single-shot-versus-ratchet boundary and to keep it off the HPKE deployment list, where it does not belong. Do not cite it as evidence that HPKE ships in iMessage; it does not.
&lt;p&gt;The ranking has a pattern. The fixed-suite tools win on simplicity, &lt;code&gt;ECDH-ES&lt;/code&gt; wins on platform gravity, and everything else loses to HPKE for &lt;em&gt;new&lt;/em&gt; design, because HPKE has the agility, the modes, the proofs, and the vectors. That makes HPKE sound like a strict upgrade. It is not. Its power comes from a set of things it deliberately refuses to do -- and mistaking those refusals for oversights is where every real HPKE incident begins.&lt;/p&gt;
&lt;h2&gt;10. The Refusals That Look Like Features&lt;/h2&gt;
&lt;p&gt;HPKE&apos;s non-goals are not gaps someone forgot to fill. Several are &lt;em&gt;impossibilities&lt;/em&gt; for a single-shot public-key primitive, and RFC 9180 states them plainly so that no one mistakes the boundary for a bug. Knowing them is the entire discipline this article is teaching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No forward secrecy against recipient compromise, in any mode.&lt;/strong&gt; RFC 9180 is explicit: HPKE ciphertexts &quot;are not forward secret with respect to recipient compromise in any mode,&quot; because only long-term secrets are used on the recipient&apos;s side [@rfc9180]. This is structural, not an oversight.&lt;/p&gt;

The property that compromising a long-term key does not expose past messages. HPKE has none with respect to recipient compromise: because a single-shot ciphertext is encrypted to a static recipient key, anyone who later obtains that key can decrypt every ciphertext ever sent to it. Forward secrecy must come from a ratchet or key rotation *outside* HPKE -- which is exactly why MLS and TLS layer it on top.
&lt;p&gt;&lt;strong&gt;&lt;code&gt;auth&lt;/code&gt; mode is not a signature.&lt;/strong&gt; As the modes section showed, the authenticated modes are signcryption with insider and outsider guarantees, not third-party non-repudiation. The recipient who shares the Diffie-Hellman secret could have produced the same ciphertext, so it cannot prove authorship to anyone else [@abhklr20], [@rfc9180]. If you need a portable proof of who signed, use a digital signature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Not key-committing by default.&lt;/strong&gt; HPKE&apos;s AEADs bind a ciphertext to a plaintext, but not to a &lt;em&gt;unique key&lt;/em&gt;.&lt;/p&gt;

A property by which a ciphertext can be opened successfully under exactly one key. HPKE&apos;s standard AEADs are not key-committing by default, which enables a partitioning-oracle attack: an attacker who can submit ciphertexts and observe success or failure can binary-search a low-entropy secret. The eventual fix is a committing AEAD, whose properties are codified in RFC 9771 [@aead_props].
&lt;p&gt;The concrete danger is a low-entropy PSK. RFC 9180, applying the partitioning-oracle result of Len, Grubbs, and Ristenpart, notes that an attacker can recover a PSK drawn from a set of size $m \cdot k$ in roughly $m + \log k$ oracle queries [@rfc9180], [@lgr20]. That logarithmic search is precisely why the PSK requirement is a floor on &lt;em&gt;entropy&lt;/em&gt;, at least 32 bytes, and not a note about password strength. The committing-AEAD countermeasure is standardized as a set of properties in RFC 9771, awaiting a committing &lt;code&gt;aead_id&lt;/code&gt; [@aead_props].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No replay, reordering, downgrade, or metadata protection.&lt;/strong&gt; HPKE does not detect a replayed ciphertext, enforce message ordering across independent contexts, prevent ciphersuite downgrade, or hide the length or existence of a message. Every one of these is deliberately left to the surrounding protocol [@rfc9180].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Classical-only by default.&lt;/strong&gt; This is the single &lt;em&gt;primitive-level&lt;/em&gt; gap -- and, as the deployment section showed, the only non-goal you close by swapping a component rather than redesigning the scheme.&lt;/p&gt;
&lt;p&gt;It is worth being precise about what is and is not finished. The core security question is &lt;em&gt;closed&lt;/em&gt;: sufficiency and necessity together establish that the KEM-DEM composite reaches IND-CCA2 exactly when its halves do, and all four modes have been machine-checked [@cs01], [@hhk06], [@lipp20], [@abhklr20], [@psk1480].&lt;/p&gt;
&lt;p&gt;The residual gaps are about &lt;em&gt;model and API coverage&lt;/em&gt;, not the core: the tightest authenticated-mode proof covers single-shot use rather than many messages, some results live in the random-oracle model rather than the standard model, and the post-quantum composition result is tightest for the authenticated mode, with the &lt;code&gt;psk&lt;/code&gt; and &lt;code&gt;auth_psk&lt;/code&gt; modes since covered by the PSK-modes analysis and a fully general standard-model post-quantum proof across all four modes still open [@abhklr20], [@psk1480]. Even the definition of IND-CCA for KEMs has known subtleties the analyses had to navigate [@rfc9180], [@bhk09].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The math is finished; the risk moved to the interface. HPKE has no known catastrophic break, so its hazards are misuse of its four modes and its non-goals -- and the post-quantum swap, which changes the KEM and not the scheme, is the proof that the architecture was the achievement. The discipline is knowing what HPKE deliberately does not do.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; HPKE&apos;s most important non-goals look like features waiting to be used: there is &lt;strong&gt;no forward secrecy&lt;/strong&gt; against recipient compromise in any mode, &lt;strong&gt;&lt;code&gt;auth&lt;/code&gt; is not a signature&lt;/strong&gt;, and the AEADs are &lt;strong&gt;not key-committing&lt;/strong&gt;. Assuming any one of the three is present is where real incidents begin [@rfc9180].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every non-goal above is a sentence a designer might wrongly assume was a yes. So the honest failure catalog for HPKE is not a list of broken math -- there is none -- it is a list of those wrong assumptions, made in real code. Here they are, named.&lt;/p&gt;
&lt;h2&gt;11. The Failure and Misuse Catalog&lt;/h2&gt;
&lt;p&gt;HPKE itself has no known catastrophic real-world break, and that absence &lt;em&gt;is&lt;/em&gt; the finding, not a gap in the research. This is a deliberately hedged absence-of-evidence claim: it rests on three machine-checked analyses and on the public advisories to date being implementation bugs, not design flaws -- never a claim that HPKE is &quot;proven secure&quot; for all time [@lipp20], [@abhklr20], [@psk1480]. The catalog is two things: the predecessor ECIES defects HPKE fixed, and the misuse patterns that appear when a non-goal is assumed away.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Non-goal assumed away&lt;/th&gt;
&lt;th&gt;Fix or lesson&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Sequence counter overflow&lt;/td&gt;
&lt;td&gt;Fixed-width counter wraps, nonce repeats&lt;/td&gt;
&lt;td&gt;AEAD nonce uniqueness is the caller&apos;s job&lt;/td&gt;
&lt;td&gt;Wide counter; single-shot; the hpke-rs &lt;code&gt;u64&lt;/code&gt; fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static-key forward-secrecy illusion&lt;/td&gt;
&lt;td&gt;Expecting past secrecy from a static key&lt;/td&gt;
&lt;td&gt;No FS against recipient compromise&lt;/td&gt;
&lt;td&gt;Ratchet or rotate keys above HPKE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Low-entropy PSK&lt;/td&gt;
&lt;td&gt;Guessable PSK enables partitioning oracle&lt;/td&gt;
&lt;td&gt;AEADs are not key-committing&lt;/td&gt;
&lt;td&gt;PSK with at least 32 bytes of entropy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bad ephemeral randomness&lt;/td&gt;
&lt;td&gt;Weak CSPRNG in &lt;code&gt;Encap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HPKE assumes good randomness&lt;/td&gt;
&lt;td&gt;Use a vetted CSPRNG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auth&lt;/code&gt; treated as a signature&lt;/td&gt;
&lt;td&gt;Insider can forge to a third party&lt;/td&gt;
&lt;td&gt;&lt;code&gt;auth&lt;/code&gt; is signcryption, not non-repudiation&lt;/td&gt;
&lt;td&gt;Use a digital signature for authorship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Out-of-order or reused &lt;code&gt;Open&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ordering not enforced by HPKE&lt;/td&gt;
&lt;td&gt;No reordering or replay protection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Seal&lt;/code&gt; in order; put ordering in the AAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export-only misuse&lt;/td&gt;
&lt;td&gt;Calling &lt;code&gt;Seal&lt;/code&gt; on an export-only suite&lt;/td&gt;
&lt;td&gt;Interface contract not enforced&lt;/td&gt;
&lt;td&gt;Error, do not truncate; the hpke-rs fix&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;The first advisory: &lt;code&gt;hpke-rs&lt;/code&gt;, GHSA-g433-pq76-6cmf.&lt;/strong&gt; In February 2026 the &lt;code&gt;hpke-rs&lt;/code&gt; and &lt;code&gt;hpke-rs-rust-crypto&lt;/code&gt; crates received a high-severity advisory bundling four fixes, and the reason it vindicates this article&apos;s spine rather than refuting it is that every fix maps onto a non-goal RFC 9180 had already flagged [@ghsa], [@rfc9180], [@hpke_rs]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequence-number overflow leading to nonce reuse.&lt;/strong&gt; The context counter could overflow; the fix widened it to a 64-bit integer. This is exactly the scenario the earlier structural model wraps in miniature, and exactly the AEAD nonce-uniqueness hazard the usage section warned about. It was reported by Kobeissi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export-only misuse and silent truncation.&lt;/strong&gt; Calling &lt;code&gt;Seal&lt;/code&gt;/&lt;code&gt;Open&lt;/code&gt; on an export-only suite, and requesting an over-long &lt;code&gt;Export&lt;/code&gt;, previously truncated silently; both now return errors. Reported by Arciszewski.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Missing X25519 zero and low-order key check.&lt;/strong&gt; The library did not reject all-zero keys; it now errors on them, closing a small-subgroup foot-gun.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A post-quantum KEM-identifier mapping bug.&lt;/strong&gt; A confusion in the draft X-Wing KEM-id mapping -- harmless in effect, but a foretaste of the ciphersuite-management care the post-quantum transition will demand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The second advisory: &lt;code&gt;@hpke/core&lt;/code&gt; / &lt;code&gt;hpke-js&lt;/code&gt;, CVE-2025-64767.&lt;/strong&gt; Three months earlier, in November 2025, the &lt;code&gt;@hpke/core&lt;/code&gt; package -- the core of the &lt;code&gt;hpke-js&lt;/code&gt; library in the survey below -- drew a &lt;em&gt;critical&lt;/em&gt; advisory (GHSA-73g8-5h73-26h4, CVSS 9.1) for reusing AEAD nonces. The root cause was a concurrency race rather than a counter overflow: the public &lt;code&gt;SenderContext&lt;/code&gt; &lt;code&gt;Seal()&lt;/code&gt; API let two concurrent asynchronous &lt;code&gt;seal()&lt;/code&gt; calls read the same sequence number, so both derived the same nonce and reused it in the AEAD, forfeiting confidentiality and integrity outright. Versions &lt;code&gt;@hpke/core&lt;/code&gt; 1.7.4 and earlier are affected; the fix shipped in 1.7.5. The lesson is the misuse table&apos;s first row reached from a new direction -- nonce uniqueness is the caller&apos;s job -- so a single &lt;code&gt;Context&lt;/code&gt; must never have &lt;code&gt;Seal&lt;/code&gt; called on it concurrently: serialize per-context sealing, or prefer single-shot. It is a second, independent instance of the exact nonce-reuse hazard the usage section named, arrived at by a race instead of an overflow [@hpke_core_cve], [@rfc9180], [@hpke_js].&lt;/p&gt;
&lt;p&gt;Notice what is &lt;em&gt;not&lt;/em&gt; on either advisory: no broken cipher, no discrete-log break, no flaw in the KEM-DEM composition -- only dropped or mishandled checks and a concurrency race, each mapping onto a non-goal or requirement the specification had already named.&lt;/p&gt;
&lt;p&gt;The rest of the catalog is misuse in application code, and it maps onto the table above. Expecting &lt;strong&gt;forward secrecy from a static recipient key&lt;/strong&gt; yields none, in any mode [@rfc9180]. A &lt;strong&gt;low-entropy PSK&lt;/strong&gt; turns &lt;code&gt;mode_psk&lt;/code&gt; into a partitioning oracle, so the PSK needs at least 32 bytes of entropy [@rfc9180], [@lgr20]. &lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;Bad ephemeral randomness&lt;/a&gt;&lt;/strong&gt; can collapse base-mode confidentiality outright, because a predictable ephemeral key undoes the encapsulation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Treating &lt;code&gt;auth&lt;/code&gt; as a signature&lt;/strong&gt; walks into the insider-versus-outsider trap [@abhklr20], and &lt;strong&gt;out-of-order &lt;code&gt;Open&lt;/code&gt; or context reuse&lt;/strong&gt; breaks because applications, not HPKE, must preserve &lt;code&gt;Seal&lt;/code&gt; order and put ordering in the associated data [@rfc9180]. The predecessor ECIES defects -- benign malleability, missing proofs, missing test vectors -- round out the list, each already closed by the four design decisions.&lt;/p&gt;

HPKE has no catastrophic break because the risk moved up a layer. Master the four modes and the non-goals and you have a boring, safe default. The math held; the risk moved to the interface.
&lt;p&gt;Read the catalog back to front and a single pattern falls out: every entry is a non-goal treated as a goal, or an implementation dropping a check the specification demanded. None is a broken cipher. That means the &quot;how to use HPKE safely&quot; guide is not a research problem. It is the non-goals, turned into rules.&lt;/p&gt;
&lt;h2&gt;12. A Practical Decision Guide&lt;/h2&gt;
&lt;p&gt;Everything above collapses into a short decision procedure and a shorter list of things never to do.&lt;/p&gt;

flowchart TD
    START[&quot;Encrypt to a public key&quot;] --&amp;gt; Q1{&quot;New protocol needing agility, modes, proofs?&quot;}
    Q1 --&amp;gt;|Yes| HPKE[&quot;Use HPKE&quot;]
    Q1 --&amp;gt;|No| Q2{&quot;Both ends yours, zero knobs?&quot;}
    Q2 --&amp;gt;|Yes| BOX[&quot;libsodium sealed box&quot;]
    Q2 --&amp;gt;|No| Q3{&quot;JWE or JWT stack?&quot;}
    Q3 --&amp;gt;|Yes| JOSE[&quot;ECDH-ES, or COSE-HPKE as it matures&quot;]
    Q3 --&amp;gt;|No| Q4{&quot;Encrypting files?&quot;}
    Q4 --&amp;gt;|Yes| AGE[&quot;age&quot;]
    Q4 --&amp;gt;|No| ECIES[&quot;Matched ECIES, legacy interop only&quot;]
    HPKE --&amp;gt; M{&quot;Which mode?&quot;}
    M --&amp;gt;|Only the recipient key| BASE[&quot;mode_base&quot;]
    M --&amp;gt;|A shared secret exists| PSK[&quot;mode_psk, PSK at least 32 bytes&quot;]
    M --&amp;gt;|Need a sender identity| AUTH[&quot;mode_auth&quot;]
    M --&amp;gt;|Both| AUTHPSK[&quot;mode_auth_psk&quot;]
&lt;p&gt;&lt;strong&gt;Which building block?&lt;/strong&gt; A new protocol or standard that needs agility, proofs, and modes takes HPKE. Both ends under your control with no options wanted takes a libsodium sealed box. The JWE and JWT world takes &lt;code&gt;ECDH-ES&lt;/code&gt;, or COSE-HPKE as it matures [@cose_hpke]. Encrypting files takes &lt;code&gt;age&lt;/code&gt;. Interop with an existing ECIES deployment takes a matched ECIES configuration and nothing new [@rfc9180].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which HPKE mode?&lt;/strong&gt; Only the recipient&apos;s public key in hand: &lt;code&gt;mode_base&lt;/code&gt;, the deployment default. A pre-shared secret already exists: &lt;code&gt;mode_psk&lt;/code&gt;, with a PSK carrying at least 32 bytes of entropy. The recipient must know that a specific sender key sealed the message: &lt;code&gt;mode_auth&lt;/code&gt;. Both a sender identity and a shared secret: &lt;code&gt;mode_auth_psk&lt;/code&gt;. Never reach for an authenticated mode expecting a third-party-verifiable signature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which ciphersuite?&lt;/strong&gt; The interop-safe classical default is DHKEM(X25519, HKDF-SHA256) with HKDF-SHA256 and AES-128-GCM, substituting ChaCha20-Poly1305 where AES is unaccelerated. If you only ever export, use the export-only AEAD (&lt;code&gt;0xFFFF&lt;/code&gt;). If you are planning for quantum resistance, track the X-Wing and ML-KEM drafts, budget roughly 1.1 KB per encapsulation, and remember that the post-quantum suites give you &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;psk&lt;/code&gt; but not yet &lt;code&gt;auth&lt;/code&gt; [@xwing_draft06].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A worked path to shipping.&lt;/strong&gt; Start with a single-shot &lt;code&gt;Seal&lt;/code&gt;/&lt;code&gt;Open&lt;/code&gt; for one message, which removes the entire counter-management problem. Reach for &lt;code&gt;Export&lt;/code&gt; only when you are keying another protocol&apos;s framing. And before you trust your integration, reproduce one of RFC 9180&apos;s published test vectors end to end: if your library produces the specified bytes, you have proof it interoperates -- the very check the ECIES era could not run. The mature libraries below make this easy.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;OpenSSL 3.2+&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;Native HPKE via &lt;code&gt;OSSL_HPKE_CTX&lt;/code&gt; [@openssl_blog], [@openssl_api]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BoringSSL&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;HPKE used in the Chrome and ECH stack [@boringssl]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare CIRCL&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;Early production HPKE; powers OHTTP and ODoH [@cloudflare]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rust-hpke&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;RFC 9180-conformant crate [@rust_hpke]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hpke-rs&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Formally oriented; ships a draft X-Wing KEM id [@hpke_rs]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hpke-js&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;Browser, Node.js, and Deno; full RFC 9180 suites; require &lt;code&gt;@hpke/core&lt;/code&gt; 1.7.5 or later (fixes CVE-2025-64767) [@hpke_js], [@hpke_core_cve]&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 &lt;code&gt;mode_base&lt;/code&gt; single-shot with DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, and AES-128-GCM -- or ChaCha20-Poly1305 without AES hardware. Use &lt;code&gt;Export&lt;/code&gt; to key your own framing rather than looping &lt;code&gt;Seal&lt;/code&gt;. Track X-Wing and ML-KEM for the post-quantum transition, and budget about 1.1 KB per encapsulation when it lands [@rfc9180], [@xwing_draft06].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The list of nevers is short and worth memorizing: never expect forward secrecy from a static recipient key; never treat &lt;code&gt;auth&lt;/code&gt; as a signature; never reuse a &lt;code&gt;Context&lt;/code&gt; across independent streams or let the sequence counter overflow; never use a low-entropy PSK; never open one ciphertext under multiple keys and assume it committed to one; and never skip your library&apos;s test-vector check.&lt;/p&gt;

Run your library against RFC 9180&apos;s Appendix A vectors for the exact ciphersuite you ship. A mismatch on the first byte tells you -- before any traffic flows -- that your point encoding, your `info` handling, or your KDF wiring disagrees with the standard. This is the single check the four ECIES standards could never offer, and it is the reason &quot;conformant&quot; now implies &quot;interoperable.&quot;
&lt;p&gt;The checklist is short because the lesson is one sentence. Before restating it, clear away the confident-but-wrong beliefs that keep regenerating the misuse catalog in code review.&lt;/p&gt;
&lt;h2&gt;13. The Misconceptions, Corrected&lt;/h2&gt;
&lt;p&gt;HPKE gets misused mostly because a handful of confident, wrong sentences keep getting said in design meetings. Here they are, corrected.&lt;/p&gt;


No -- not with respect to recipient compromise, in any mode. RFC 9180 states that HPKE ciphertexts are not forward secret against recipient compromise, because only long-term secrets are used on the recipient&apos;s side [@rfc9180]. Forward secrecy has to come from a ratchet or key rotation layered above HPKE, which is exactly what MLS and TLS do.


No. `auth` mode is signcryption, not a third-party-verifiable signature: the recipient cannot prove authorship to anyone else, for the reason the modes section works through. If you need portable proof of who signed, use a digital signature [@abhklr20].


A `Context` is stateful: an incrementing sequence number derives each per-message nonce. Seal strictly in order, never reuse a context across independent streams, respect the per-ciphersuite AEAD message-count limit, and never let the sequence counter overflow -- any of those repeats a nonce, which is catastrophic [@rfc9180]. For independent messages, prefer single-shot `Seal`/`Open` and avoid the bookkeeping entirely.


Not by default -- DHKEM is classical-only. The migration path is to drop a hybrid KEM such as X-Wing (X25519 with ML-KEM-768) into the KEM slot, secure if either component holds [@xwing], [@fips203]. Those suites are still draft-stage, add roughly 1.1 KB per encapsulation, and are not authenticated KEMs yet, so they cover `base` and `psk` but not `auth` [@xwing_draft06].


`crypto_box` is one fixed suite (X25519, XSalsa20, Poly1305), deliberately repudiable and mutually authenticated, with manual nonce handling for the raw API [@nacl]. It is excellent when you own both ends and want no options. HPKE wins for new protocol design, because it brings algorithm agility, four formal modes, machine-checked proofs, and test vectors [@rfc9180].


No -- PQ3 is KEM-based but not HPKE; it is a ratcheted, interactive scheme rather than single-shot encryption, as the earlier PQ3 aside details [@apple_pq3]. Do not list it as an HPKE deployment.


Only if you use low-entropy pre-shared keys or open one ciphertext under multiple candidate keys. HPKE&apos;s AEADs are not key-committing by default, which is why a weak PSK enables a partitioning oracle; keep the PSK entropy at 32 bytes or more [@rfc9180], [@lgr20]. A committing AEAD, whose properties are codified in RFC 9771, may eventually arrive as a new `aead_id` [@aead_props].

&lt;p&gt;Every correction points at the same root: the primitive was never the risk. It is time to say the sentence the whole article was built to earn.&lt;/p&gt;
&lt;h2&gt;14. The Math Held; The Risk Moved to the Interface&lt;/h2&gt;
&lt;p&gt;Return to where this started: an elementary task -- encrypt to a public key -- that the industry got subtly wrong four incompatible times, with no proofs and no shared test vectors, one standard shrugging off a second valid ciphertext as &quot;benign&quot; [@sec1], [@maea10]. HPKE is what that task looks like when it is finally done right.&lt;/p&gt;
&lt;p&gt;The evidence has accumulated into a single claim. HPKE is one KEM-DEM construction: a Key Encapsulation Mechanism generates a shared secret and its encapsulation, an HKDF key schedule with every public key bound in derives the working keys, and an AEAD seals the payload.&lt;/p&gt;
&lt;p&gt;It is proven secure against adaptive chosen-ciphertext attack under standard assumptions, machine-checked across all four modes, test-vectored per ciphersuite, and it designs benign malleability out by making the public-key binding mandatory rather than optional [@rfc9180], [@cs01], [@lipp20], [@abhklr20]. The consequence is the whole thesis: HPKE has no known catastrophic break, so the discipline it demands is not &quot;how do I make it secure&quot; but &quot;do I know its four modes and its non-goals?&quot;&lt;/p&gt;
&lt;p&gt;Every entry in the misuse catalog proved the point. Forward secrecy expected from a static key, a signature expected from &lt;code&gt;auth&lt;/code&gt; mode, safety expected from a non-committing AEAD, a fresh nonce expected from an overflowed counter -- each was a non-goal assumed away, not a broken cipher [@rfc9180], [@ghsa].&lt;/p&gt;
&lt;p&gt;And the post-quantum migration is the architecture&apos;s final vindication: the one primitive-level gap, quantum resistance, is closed by swapping the KEM for a hybrid like X-Wing, not by redesigning the scheme [@xwing]. That is the exact payoff of having abstracted the KEM in the first place. It echoes this series&apos; running lesson from the KEM-DEM and digital-signature parts: the math held, and the interface is where the work -- and the risk -- actually lives.&lt;/p&gt;
&lt;p&gt;So carry one question out of this article and apply it to any encrypt-to-a-public-key design you review.&lt;/p&gt;

Which of the four modes is this, and which of HPKE&apos;s non-goals am I about to assume it has?
&lt;p&gt;The primitive was never the weak link, and the post-quantum swap shows it never will be. That is why this is Part 19 of a field guide to &lt;em&gt;protocol design&lt;/em&gt;, not a chapter about any one cipher. HPKE is the clean seam where the primitives of the earlier parts -- authenticated encryption, key derivation, elliptic curves, KEM-DEM, signatures -- become a protocol. The math is finished, the vectors are published, and the only thing left to get wrong is you.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;hpke-rfc9180-kem-based-hybrid-public-key-encryption&quot; keyTerms={[
  { term: &quot;HPKE&quot;, definition: &quot;The RFC 9180 standard for encrypting to a public key, built from a KEM, a KDF, and an AEAD named together as a ciphersuite.&quot; },
  { term: &quot;KEM&quot;, definition: &quot;A key encapsulation mechanism: Encap generates a fresh shared secret and its encapsulation from the recipient&apos;s public key; you never choose the key.&quot; },
  { term: &quot;DEM&quot;, definition: &quot;The data encapsulation mechanism: an AEAD that bulk-encrypts the payload under a key derived from the KEM&apos;s shared secret.&quot; },
  { term: &quot;DHKEM&quot;, definition: &quot;HPKE&apos;s Diffie-Hellman KEM, using an ephemeral-static exchange and binding the encapsulation and recipient key into the derivation.&quot; },
  { term: &quot;Key schedule&quot;, definition: &quot;HPKE&apos;s HKDF process: Extract a pseudorandom key from the shared secret, then Expand it over mode, psk_id_hash, and info_hash to yield the AEAD key, base_nonce, and exporter_secret.&quot; },
  { term: &quot;AEAD&quot;, definition: &quot;Authenticated encryption with associated data: confidentiality and integrity in one primitive; the DEM half of HPKE.&quot; },
  { term: &quot;IND-CCA2&quot;, definition: &quot;Security against adaptive chosen-ciphertext attack; the bar HPKE meets and that benign malleability violates.&quot; },
  { term: &quot;Benign malleability&quot;, definition: &quot;The ECIES defect where an attacker forges a second valid ciphertext for the same plaintext; HPKE kills it by binding all public keys into the key schedule.&quot; },
  { term: &quot;Signcryption&quot;, definition: &quot;Combined encryption and sender authentication with insider/outsider guarantees; HPKE&apos;s auth modes are signcryption, not third-party signatures.&quot; },
  { term: &quot;Key commitment&quot;, definition: &quot;An AEAD property binding a ciphertext to one key; HPKE is not committing by default, enabling partitioning oracles against low-entropy PSKs.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>hpke</category><category>rfc-9180</category><category>kem-dem</category><category>public-key-encryption</category><category>applied-cryptography</category><category>post-quantum</category><category>aead</category><author>noreply@paragmali.com (Parag Mali)</author></item><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>The AEAD Decision Matrix: Seven Ciphers, Three Edges, One Choice</title><link>https://paragmali.com/blog/the-aead-decision-matrix-seven-ciphers-three-edges-one-choic/</link><guid isPermaLink="true">https://paragmali.com/blog/the-aead-decision-matrix-seven-ciphers-three-edges-one-choic/</guid><description>AES-GCM, ChaCha20-Poly1305, CCM, OCB3, GCM-SIV, AEGIS, and Ascon, compared by the three sharp edges that decide every deployment: nonce, hardware, commitment.</description><pubDate>Thu, 09 Jul 2026 15:04:00 GMT</pubDate><content:encoded>
Every modern AEAD makes the same three promises, and they differ only at the sharp edges where those promises stop. Three edges decide a real deployment: the **nonce contract** (catastrophic on reuse, or misuse-resistant), the **performance and hardware profile** (AES-NI plus CLMUL, constant-time software, or a few thousand gates), and **commitment** (does one ciphertext bind to exactly one key?). &quot;Which AEAD should I use?&quot; is not a ranking from worst to best. It is a function: default to AES-GCM or ChaCha20-Poly1305, and escalate to AES-GCM-SIV, AES-CCM, Ascon, OCB3, or AEGIS only when a specific edge demands it. Every famous break -- invisible salamanders, nonce reuse on live TLS, `CCM_8` forgeries -- was a deployment stepping on an edge, not a broken cipher.
&lt;h2&gt;1. Two Failures That Should Have Been Impossible&lt;/h2&gt;
&lt;p&gt;In 2018, a team of cryptographers handed Facebook a single encrypted file that decrypted to two completely different, perfectly meaningful images, and whose authentication tag verified correctly for &lt;em&gt;both&lt;/em&gt;, under two different keys [@dodis2018-salamander]. The cipher was AES-GCM, the internet&apos;s default authenticated encryption. Nothing about it was broken. That is the unsettling part: AES-GCM did &lt;em&gt;exactly&lt;/em&gt; what it promised. It had just never promised the thing everyone assumed it did.&lt;/p&gt;
&lt;p&gt;The result defeated Facebook Messenger&apos;s abuse-reporting scheme, which relied on the encrypted attachment being &lt;em&gt;bound&lt;/em&gt; to the key that produced it. The authors called their construction &quot;invisible salamanders&quot; [@dodis2018-salamander]. Your reflex on reading this -- &quot;but it is &lt;em&gt;authenticated&lt;/em&gt;, so how did the tag pass?&quot; -- is the entire subject of this article. The word &quot;authenticated&quot; carries less than you think.&lt;/p&gt;
&lt;p&gt;Now the second failure, because one break only shows one edge. In 2016, researchers scanning the live internet found 184 HTTPS servers reusing a &lt;code&gt;(key, nonce)&lt;/code&gt; pair under AES-GCM [@bock2016]. A repeated nonce in GCM is not a hygiene slip that costs you a little margin. It instantly leaks the XOR of the two plaintexts, and -- via an attack Antoine Joux had described to NIST a decade earlier, in 2006 -- it lets an attacker forge arbitrary messages [@joux2006]. The confidentiality half of that failure is easy to see for yourself.&lt;/p&gt;
&lt;p&gt;{`
// A stream-cipher-style AEAD (GCM, ChaCha20-Poly1305) turns the key+nonce
// into a keystream, then XORs it with the plaintext. Reuse the nonce and
// you reuse the keystream -- so it cancels out of the XOR of two ciphertexts.
const keystream = [0x9e, 0x37, 0xb1, 0xf2, 0x4a];   // same (key, nonce) =&amp;gt; same keystream
const xor = (a, b) =&amp;gt; a.map((x, i) =&amp;gt; x ^ b[i]);&lt;/p&gt;
&lt;p&gt;const p1 = [...&apos;HELLO&apos;].map(c =&amp;gt; c.charCodeAt(0));
const p2 = [...&apos;WORLD&apos;].map(c =&amp;gt; c.charCodeAt(0));&lt;/p&gt;
&lt;p&gt;const c1 = xor(p1, keystream);   // ciphertext 1
const c2 = xor(p2, keystream);   // ciphertext 2, SAME nonce&lt;/p&gt;
&lt;p&gt;// Attacker sees only c1 and c2, never the key or keystream:
const leaked = xor(c1, c2);      // == p1 XOR p2, the keystream is gone
const recovered = xor(leaked, p2);  // and if p2 is ever guessed, p1 falls out
console.log(&apos;c1 XOR c2 leaks P1 XOR P2:&apos;, leaked.join(&apos;,&apos;));
console.log(&apos;recovered P1:&apos;, String.fromCharCode(...recovered));
`}&lt;/p&gt;
&lt;p&gt;Neither of these was a broken cipher. AES-GCM met its specification in both cases. The salamander bound nothing to a key because AES-GCM was never designed to; the TLS servers leaked plaintext because they violated the one contract GCM cannot survive. Both failures were the same shape: a deployment reached for an AEAD and then stepped on the one sharp edge its designers had moved somewhere the deployment could not avoid.&lt;/p&gt;
&lt;p&gt;That is the thesis of this field guide. Authenticated encryption with associated data (AEAD) fused confidentiality and integrity so completely that composing them by hand became a solved problem. The remaining hard problem is choosing &lt;em&gt;among&lt;/em&gt; AEADs, and that choice turns on three edges: the &lt;strong&gt;nonce contract&lt;/strong&gt;, the &lt;strong&gt;performance and hardware profile&lt;/strong&gt;, and the &lt;strong&gt;commitment&lt;/strong&gt; axis.&lt;/p&gt;
&lt;p&gt;Part 1 of this series defined an AEAD&apos;s guarantees as &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;IND-CPA plus INT-CTXT&lt;/a&gt;; commitment is a robustness property &lt;em&gt;beyond&lt;/em&gt; both. Part 6 showed the padding-oracle cost of the botched integrity AEAD was built to remove.The nonce-reuse breaks are widely mis-cited to CVE-2016-0270. That CVE actually names IBM Domino, and the NVD entry itself warns it has been &quot;incorrectly used for GCM nonce reuse issues in other products&quot; [@cve-2016-0270].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &quot;Which AEAD should I use?&quot; is not a ranking. It is a function over three edges: pick the construction whose failure mode your deployment can &lt;em&gt;guarantee&lt;/em&gt; it will never trigger. The salamander triggered the commitment edge; the TLS servers triggered the nonce edge. Same lesson, two edges.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If &quot;authenticated&quot; does not mean &quot;bound to one key,&quot; and &quot;encrypted&quot; does not survive a repeated nonce, then what &lt;em&gt;exactly&lt;/em&gt; does an AEAD promise, and where does each promise stop? To answer that, we have to go back to the moment when &quot;encrypt&quot; and &quot;authenticate&quot; were still two separate calls.&lt;/p&gt;
&lt;h2&gt;2. From Two Calls to One&lt;/h2&gt;
&lt;p&gt;Here is a question that sounds trivial and is not: you have a cipher and you have a message authentication code (MAC). In which order do you apply them? Most engineers guess. The wrong guess shipped in SSL, IPsec, and SSH, and it took the field a decade of theory to sort out which guess was which.&lt;/p&gt;
&lt;p&gt;There are three orderings. &lt;strong&gt;Encrypt-and-MAC&lt;/strong&gt; authenticates the plaintext and appends the tag beside the ciphertext (SSH). &lt;strong&gt;MAC-then-Encrypt&lt;/strong&gt; authenticates the plaintext, then encrypts plaintext and tag together (SSL and, for years, TLS). &lt;strong&gt;Encrypt-then-MAC&lt;/strong&gt; encrypts, then authenticates the &lt;em&gt;ciphertext&lt;/em&gt; (IPsec).&lt;/p&gt;
&lt;p&gt;In 2000, Mihir Bellare and Chanathip Namprempre proved these are not interchangeable: only Encrypt-then-MAC generically preserves both privacy and integrity for &lt;em&gt;any&lt;/em&gt; secure cipher and &lt;em&gt;any&lt;/em&gt; secure MAC [@bn2000]. A year later, Hugo Krawczyk showed the authenticate-then-encrypt method used in SSL is not generically secure while Encrypt-then-MAC is [@krawczyk2001]. Part 6 is the sequel: MAC-then-encrypt is exactly the door a &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; walks through.&lt;/p&gt;

One keyed call that provides confidentiality of the plaintext, integrity of the plaintext, and integrity (but not secrecy) of an extra header called the associated data. The uniform interface was standardized in RFC 5116 [@rfc5116].
&lt;p&gt;The lesson cut deeper than &quot;pick Encrypt-then-MAC.&quot; If two correct primitives can combine into an &lt;em&gt;insecure&lt;/em&gt; whole, hand-composition is a footgun no matter how good the parts are. The fix was to make a &lt;em&gt;single&lt;/em&gt; primitive own both goals, so no protocol designer could botch the join.&lt;/p&gt;
&lt;p&gt;Charanjit Jutla showed the way in 2001 with IAPM and IACBC, modes that delivered message integrity in essentially one pass, almost for free [@jutla2001]. The same year, Phillip Rogaway and coauthors published OCB, a one-pass block-cipher mode built for exactly this [@ocb2001]. Then, in 2002, Rogaway named the primitive: real messages carry a header -- routing bytes, version numbers, sequence counters -- that must be &lt;em&gt;authenticated&lt;/em&gt; but not &lt;em&gt;encrypted&lt;/em&gt;, because intermediaries need to read it. His &quot;Authenticated-Encryption with Associated-Data&quot; formalized that extra input [@rogaway2002-ad].&lt;/p&gt;

The header bytes an AEAD authenticates but does not encrypt: routing information, protocol versions, sequence numbers. Tampering with the associated data makes tag verification fail, but the associated data itself travels in the clear [@rogaway2002-ad].
&lt;p&gt;By 2008, David McGrew closed the loop with RFC 5116, which defined the universal interface every construction in this article implements: &lt;code&gt;AEAD-Encrypt(key, nonce, associated_data, plaintext)&lt;/code&gt; returns &lt;code&gt;ciphertext || tag&lt;/code&gt;, and the matching decrypt either returns the plaintext or a single, uninformative failure [@rfc5116]. One call in, one call out, no way to reverse the order of two primitives because there is only one primitive.&lt;/p&gt;

A value that must be unique for every encryption under a given key. The mode, not the label, sets the exact requirement: some AEADs tolerate only uniqueness, others tolerate randomness, and a few tolerate repetition. Nonce generation is the subject of Part 2 of this series.

timeline
    title The road to the AEAD portfolio
    2000 : Encrypt-then-MAC proven the safe order
    2001 : IAPM and OCB, single-pass native AE
    2002 : Rogaway names AEAD and adds associated data
    2003 : CCM standardized in RFC 3610
    2004 : GCM published by McGrew and Viega
    2006 : Joux forbidden attack, SIV misuse-resistance
    2008 : RFC 5116 fixes the universal interface
    2013 : CAESAR opens, AEGIS announced
    2016 : Nonce reuse found on live TLS servers
    2018 : Invisible salamanders break message franking
    2019 : CAESAR portfolio chosen, OCB2 broken
    2021 : Partitioning oracles weaponize non-commitment
    2025 : NIST standardizes Ascon-AEAD128
&lt;p&gt;The interface was settled by 2008. But an interface is a promise about &lt;em&gt;shape&lt;/em&gt;, not about &lt;em&gt;sharp edges&lt;/em&gt;. Two constructions that satisfy &lt;code&gt;AEAD-Encrypt&lt;/code&gt; to the letter can fail in completely different ways, and the very first AEADs to ship proved it. The one that reached the most devices did not even win on technical merit. It won because of a patent.&lt;/p&gt;
&lt;h2&gt;3. The Welded Modes: CCM and GCM&lt;/h2&gt;
&lt;p&gt;The first AEAD to reach a billion devices was not chosen by a cryptographer. It was chosen, in effect, by a patent lawyer. When IEEE 802.11i (the security amendment that became WPA2) needed an authenticated mode, the elegant candidate was OCB -- but OCB was patent-encumbered, and the working group would not build a wireless standard on it. So 802.11i standardized the plodding, unpatented alternative: &lt;strong&gt;AES-CCM&lt;/strong&gt;. By device count, that decision made CCM one of the most widely deployed AEADs in the world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AES-CCM = Counter mode + CBC-MAC.&lt;/strong&gt; Doug Whiting, Russ Housley, and Niels Ferguson specified it in RFC 3610 in 2003, and NIST blessed it as SP 800-38C the next year [@rfc3610][@sp80038c]. It runs the message through CBC-MAC for the tag, then encrypts with &lt;a href=&quot;https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw/&quot; rel=&quot;noopener&quot;&gt;counter (CTR) mode&lt;/a&gt;: two passes, a strictly serial MAC, and the message length needed up front, which makes streaming awkward. What that price buys is frugality -- nothing but AES, a tiny code footprint, no second primitive to implement.&lt;/p&gt;
&lt;p&gt;That profile is why CCM owns the constrained niche -- Bluetooth Low Energy [@bluetooth-core], Zigbee [@ieee802154], and the TLS &lt;code&gt;CCM_8&lt;/code&gt; ciphersuites. IEEE 802.11i also made CCMP the mandatory data-confidentiality protocol for WPA2 [@ieee80211i], and CCM rode WPA2/CCMP into the overwhelming majority of Wi-Fi hardware over the following decade.Zigbee and 802.15.4 use a variant called CCM*, which additionally permits integrity-only or encryption-only operation, unlike the strict RFC 3610 CCM that always does both. So the same plodding, all-AES mode anchors two very different worlds: the smallest constrained devices and the Wi-Fi layer that WPA2 secures. These are the building blocks from Part 5, welded to a MAC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AES-GCM = Counter mode + GHASH.&lt;/strong&gt; David McGrew and John Viega published Galois/Counter Mode in 2004, and NIST standardized it as SP 800-38D in 2007 [@mcgrew2004-gcm][@sp80038d]. GCM is everything CCM is not: one pass, fully parallelizable, and, on any CPU with AES-NI and CLMUL, blisteringly fast. It is the internet default, and AES-128-GCM is the single mandatory-to-implement (MUST) AEAD cipher suite in TLS 1.3, per RFC 8446 Section 9.1 -- a status the spec grants to no other AEAD [@rfc8446]. To understand every later failure, you need one fact about how its tag works.&lt;/p&gt;

An authenticator of the form $\text{tag} = H_k(\text{message}) + s$, where $H_k$ is a fast keyed universal hash and $s$ is a **one-time** secret mask. It is unforgeable only if $s$ is never reused. Repeat the mask and the algebraic structure of $H_k$ leaks. This is the shared root of both GHASH (AES-GCM) and Poly1305 (ChaCha20-Poly1305) [@bernstein-poly1305].
&lt;p&gt;GCM&apos;s tag is a one-time Wegman-Carter MAC. GHASH evaluates a polynomial over the finite field $\mathrm{GF}(2^{128})$, with the ciphertext and associated-data blocks as coefficients, at a single secret point $H = \operatorname{AES}_k(0^{128})$. It then masks the result with a per-nonce keystream block:&lt;/p&gt;
&lt;p&gt;$$T = \operatorname{GHASH}_H(A, C) \oplus \operatorname{AES}_k(J_0), \qquad H = \operatorname{AES}_k(0^{128})$$&lt;/p&gt;
&lt;p&gt;where $J_0$ is the initial counter block derived from the nonce. The mask $\operatorname{AES}_k(J_0)$ is the one-time secret $s$. It is one-time &lt;em&gt;only because the nonce is unique&lt;/em&gt;. Repeat the nonce and you repeat $J_0$, so you repeat the mask, and now two tags share it -- which turns GHASH&apos;s linear structure into a system of polynomial equations an attacker can solve.&lt;/p&gt;

The universal hash inside AES-GCM: a polynomial in the secret point $H = \operatorname{AES}_k(0^{128})$ evaluated over $\mathrm{GF}(2^{128})$. Because the polynomial is linear in its coefficients, recovering $H$ lets an attacker forge tags for chosen messages [@joux2006].

flowchart LR
    N[&quot;Nonce and counter&quot;] --&amp;gt; CTR[&quot;AES counter mode&quot;]
    K[&quot;Key&quot;] --&amp;gt; CTR
    CTR --&amp;gt; KS[&quot;Keystream&quot;]
    P[&quot;Plaintext&quot;] --&amp;gt; X((&quot;XOR&quot;))
    KS --&amp;gt; X
    X --&amp;gt; C[&quot;Ciphertext&quot;]
    C --&amp;gt; GH[&quot;GHASH polynomial at secret point H&quot;]
    AD[&quot;Associated data&quot;] --&amp;gt; GH
    K --&amp;gt; H[&quot;H is AES of the zero block&quot;]
    H --&amp;gt; GH
    GH --&amp;gt; M((&quot;XOR one-time mask&quot;))
    K --&amp;gt; J[&quot;AES of the first counter block&quot;]
    J --&amp;gt; M
    M --&amp;gt; T[&quot;Authentication tag&quot;]
&lt;p&gt;That single design choice hands GCM two sharp edges the rest of this article pays for. The first is the &lt;strong&gt;nonce contract&lt;/strong&gt;: reuse is not a weakness, it is a detonator.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For every nonce-respecting AEAD -- AES-GCM, ChaCha20-Poly1305, AES-CCM, OCB3, AEGIS, and Ascon -- one repeated &lt;code&gt;(key, nonce)&lt;/code&gt; pair is an immediate, total break of confidentiality for those two messages, and, with a second collision, of authenticity for the whole key [@joux2006].One nonce collision leaks $P_1 \oplus P_2$ right away. Uniquely pinning the secret point $H$ for universal forgery generally needs at least two collisions, since a single pair of equations leaves multiple candidate roots. &quot;One repeat is game over for those messages&quot; is correct; &quot;one repeat exposes $H$&quot; is not quite [@bock2016].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The second edge is quieter and just as dangerous, because it is two different numbers people constantly merge into one.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; SP 800-38D fixes a &lt;strong&gt;per-message&lt;/strong&gt; plaintext limit of $2^{39}-256$ bits (about 64 GiB), set by 32-bit counter-mode block-space exhaustion, and, separately, a &lt;strong&gt;per-key&lt;/strong&gt; invocation cap of roughly $2^{32}$ messages when nonces are random 96-bit values, driven by the birthday bound on nonce collision [@sp80038d]. These are two different mechanisms, not two readings of one bound (the full derivation is in Section 8). The multi-user bounds that justify TLS 1.3&apos;s nonce randomization are tight [@htt2018-gcm]. Enforce both limits. Conflating them is the single most common accuracy error on this primitive.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GCM gave the internet a one-pass, hardware-fast AEAD, and in the same stroke handed it a mode where one repeated nonce is not a bug but a catastrophe. This is the same GCM negotiated inside shipping Windows protocols such as &lt;a href=&quot;https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm/&quot; rel=&quot;noopener&quot;&gt;Schannel TLS&lt;/a&gt; and SMB 3 encryption [@ms-schannel-tls][@ms-smb-encryption]. Antoine Joux saw the danger in 2006 [@joux2006]. It took the rest of the world ten years, and a scan of the live internet, to believe him.&lt;/p&gt;
&lt;h2&gt;4. A Trunk With Branches&lt;/h2&gt;
&lt;p&gt;The family tree of AEAD is not a ladder from worst to best. Draw it that way and none of the modern constructions make sense, because none of them strictly dominates GCM. Draw it correctly and the whole field snaps into focus: it is a &lt;strong&gt;trunk with branches&lt;/strong&gt;. The trunk is linear -- generic composition, then single-pass native AE, then the welded deployed modes CCM and GCM. Then, at GCM, the tree &lt;em&gt;splits&lt;/em&gt;, because GCM&apos;s edges each forced a &lt;em&gt;separate&lt;/em&gt; successor. No later generation wins. That is the entire point.&lt;/p&gt;

flowchart TD
    G0[&quot;Generic composition, Encrypt-then-MAC&quot;] --&amp;gt; G1[&quot;Single-pass native AE, IAPM and OCB&quot;]
    G1 --&amp;gt; G2[&quot;Welded deployed modes, CCM and GCM&quot;]
    G2 --&amp;gt; E1[&quot;Nonce edge&quot;]
    G2 --&amp;gt; E2[&quot;Hardware edge&quot;]
    G2 --&amp;gt; E3[&quot;Commitment edge&quot;]
    E1 --&amp;gt; S1[&quot;SIV and AES-GCM-SIV, misuse-resistant&quot;]
    E2 --&amp;gt; S2[&quot;ChaCha20-Poly1305, constant-time in software&quot;]
    E2 --&amp;gt; S4[&quot;AEGIS and Ascon, hardware and footprint co-design&quot;]
    E3 --&amp;gt; S3[&quot;Committing transforms, added on top&quot;]
&lt;h3&gt;The hardware edge: ChaCha20-Poly1305&lt;/h3&gt;
&lt;p&gt;GHASH is hard to make constant-time without the CLMUL instruction, and table-based AES is both slow and timing-leaky without AES-NI. Around 2013, that was the reality on most phones and ARM chips: no AES hardware, so GCM was either slow or a cache-timing side channel. Daniel Bernstein&apos;s answer kept GCM&apos;s exact nonce-respecting contract but threw out the parts that needed special silicon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ChaCha20&lt;/strong&gt; is an ARX cipher -- addition, rotation, XOR -- with no S-box tables to leak timing, so it is constant-time by construction [@bernstein-chacha]. &lt;strong&gt;Poly1305&lt;/strong&gt; is a one-time Wegman-Carter MAC over a prime field rather than $\mathrm{GF}(2^{128})$, and its one-time key is derived from a ChaCha keystream block [@bernstein-poly1305].Poly1305&apos;s one-time key comes from a ChaCha-derived block, not from AES. ChaCha20-Poly1305 never calls AES at all -- a common misconception, since GCM&apos;s mask does use AES.&lt;/p&gt;
&lt;p&gt;The IETF standardized the pair as an AEAD in RFC 8439 [@rfc8439], and it now protects TLS 1.3, WireGuard, OpenSSH, and the &lt;code&gt;age&lt;/code&gt; file-encryption tool [@wireguard-protocol][@openssh-chacha][@age-spec]. Its normative status is widely overstated: in TLS 1.3, ChaCha20-Poly1305 is a &lt;em&gt;recommended&lt;/em&gt; (SHOULD) cipher suite, not mandatory-to-implement, per RFC 8446 Section 9.1 -- a default by adoption, not by requirement [@rfc8446]. It is not weaker than GCM; it is the same nonce contract on a different, table-free engine.XChaCha20-Poly1305 extends the nonce to 192 bits, large enough that random nonces essentially never collide. That is a &lt;em&gt;mitigation&lt;/em&gt; of nonce-collision anxiety, not misuse-resistance -- reuse the full 192-bit nonce and it fails exactly like GCM.&lt;/p&gt;
&lt;h3&gt;The nonce edge: misuse-resistance and SIV&lt;/h3&gt;
&lt;p&gt;Every mode so far detonates on a repeated nonce, and reuse keeps happening in the real world: cloned virtual machines that resume with identical RNG state, counters reset by a crash, embedded devices with weak entropy at first boot. If the operator cannot guarantee uniqueness, the fix is a mode that &lt;em&gt;survives&lt;/em&gt; the operator&apos;s mistake. In 2006, Phillip Rogaway and Thomas Shrimpton formalized exactly that goal and built the first construction for it [@rs2006].&lt;/p&gt;

An AEAD whose failure under a repeated nonce is graceful, not catastrophic. Repeating a `(key, nonce, associated_data, plaintext)` tuple leaks only whether two encryptions were of the *same* message -- message equality -- and nothing else. It never leaks a plaintext XOR and never hands over the authentication subkey [@rs2006][@rfc8452].
&lt;p&gt;The mechanism is a single clean idea: derive the initialization vector &lt;em&gt;synthetically&lt;/em&gt;, as a pseudorandom function of the entire message, rather than accepting it from the caller. The tag &lt;em&gt;is&lt;/em&gt; the IV.&lt;/p&gt;

A construction in which the initialization vector is computed as a MAC over the associated data and the full plaintext, then reused as the authentication tag. Two different messages under a repeated nonce produce different synthetic IVs, so their keystreams differ and nothing leaks [@rs2006].

flowchart TD
    K[&quot;Key&quot;] --&amp;gt; MAC[&quot;MAC over associated data and full plaintext&quot;]
    AD[&quot;Associated data&quot;] --&amp;gt; MAC
    N[&quot;Nonce&quot;] --&amp;gt; MAC
    P[&quot;Plaintext&quot;] --&amp;gt; MAC
    MAC --&amp;gt; IV[&quot;Synthetic IV, which is also the tag&quot;]
    IV --&amp;gt; CTR[&quot;Counter-mode encryption&quot;]
    K --&amp;gt; CTR
    P --&amp;gt; CTR
    CTR --&amp;gt; C[&quot;Ciphertext&quot;]
    IV --&amp;gt; T[&quot;Tag sent alongside ciphertext&quot;]
&lt;p&gt;That picture also explains the price, which returns as a theorem in Section 8: to compute the IV you must read the whole message first, so SIV cannot emit its first ciphertext byte until it has seen the last plaintext byte. It is inherently two-pass.&lt;/p&gt;
&lt;p&gt;Two deployable forms exist. &lt;strong&gt;AES-SIV&lt;/strong&gt;, standardized by Dan Harkins in RFC 5297, targets deterministic authenticated encryption and key wrapping [@rfc5297]. &lt;strong&gt;AES-GCM-SIV&lt;/strong&gt;, from Shay Gueron, Adam Langley, and Yehuda Lindell in RFC 8452, keeps AES-NI and CLMUL speed while adding misuse-resistance, building on the Gueron-Lindell GCM-SIV design [@rfc8452][@gl2015-gcmsiv].AES-GCM-SIV hashes with POLYVAL, a little-endian sibling of GHASH, and derives a fresh message-authentication and encryption key per nonce -- a structure whose tight multi-user bounds were later established by Bose, Hoang, and Tessaro [@bht2018].&lt;/p&gt;

&quot;...two authenticated encryption algorithms that are nonce misuse resistant -- that is, they do not fail catastrophically if a nonce is repeated.&quot; -- RFC 8452 [@rfc8452]
&lt;p&gt;Read that phrasing carefully, because it is the most misquoted sentence about SIV. &quot;Do not fail catastrophically&quot; is not &quot;do not fail.&quot; A repeated nonce in AES-GCM-SIV still leaks message equality: an attacker learns that two ciphertexts encrypt the same plaintext, which is a real and sometimes serious leak. SIV makes reuse &lt;em&gt;survivable&lt;/em&gt;, never &lt;em&gt;free&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;The elegance detour: OCB3 and the patent that chose a standard&lt;/h3&gt;
&lt;p&gt;Two edges now had answers, each on its own branch. A third construction sits off to the side -- not because it is worse, but because history was unkind to it. &lt;strong&gt;OCB3&lt;/strong&gt;, finalized by Ted Krovetz and Phillip Rogaway in 2011 and standardized as RFC 7253, is arguably the most elegant AEAD ever standardized: one block-cipher key, one pass, fully parallel, roughly one cipher call per block [@kr2011-ocb3][@rfc7253].&lt;/p&gt;
&lt;p&gt;Its mechanism is where the elegance lives. Each block is masked by a key-derived offset -- $\text{Offset}&lt;em&gt;i = \text{Offset}&lt;/em&gt;{i-1} \oplus L_{\text{ntz}(i)}$, a Gray-code walk over precomputed $L$ values -- and wrapped as $C_i = \text{Offset}_i \oplus \operatorname{ENCIPHER}(K, P_i \oplus \text{Offset}_i)$, while a &lt;em&gt;single&lt;/em&gt; running plaintext checksum, $\text{Checksum} = P_1 \oplus P_2 \oplus \cdots$, produces the tag in the &lt;em&gt;same pass&lt;/em&gt; [@rfc7253]. No second key, no separate MAC. It matches or beats GCM in software. And it lost anyway -- not for a technical reason.A widely repeated claim is that OCB3 is &lt;em&gt;inverse-free&lt;/em&gt;. It is not. OCB-DECRYPT recovers each block as $P_i = \text{Offset}_i \oplus \operatorname{DECIPHER}(K, C_i \oplus \text{Offset}_i)$, calling the block-cipher inverse $E_K^{-1}$ (RFC 7253 Section 4.3), so a hardware implementation needs both the AES encrypt &lt;em&gt;and&lt;/em&gt; decrypt circuits [@rfc7253]. Inverse-free is the property of the CTR, stream, and sponge modes -- AES-GCM, AES-CCM, ChaCha20-Poly1305, AEGIS, and Ascon -- and OCB is precisely the lineage that trades it away for single-primitive elegance.&lt;/p&gt;

OCB was patented from birth. When the 802.11i working group picked WPA2&apos;s cipher in 2003, it passed over OCB precisely because of those patents and chose the unencumbered CCM instead. Rogaway later granted free licenses for open-source and non-military use, but the damage was done: every major protocol had already standardized on GCM and ChaCha20-Poly1305 in the years the patents were live. In 2021 Rogaway released OCB into the public domain outright -- too late to matter [@kr2021-ocb]. The honest answer to &quot;when should I reach for OCB3 in 2026?&quot; is &quot;almost never, and the reason is a patent grave, not a design flaw.&quot;
&lt;h3&gt;The dead end: OCB2 and the limits of &quot;provably secure&quot;&lt;/h3&gt;
&lt;p&gt;There is one more branch, and it is a warning. Between OCB1 and OCB3 sat OCB2, an ISO-standardized refinement with a security proof. In 2019, Akiko Inoue, Tetsu Iwata, Kazuhiko Minematsu, and Bertram Poettering broke it outright: universal forgery &lt;em&gt;and&lt;/em&gt; full plaintext recovery, because OCB2 used the XEX* tweakable cipher outside the regime its proof actually covered [@inoue2019-ocb2].&lt;/p&gt;

The OCB2 break is the cautionary counterpoint to every &quot;but it has a proof&quot; argument. OCB2 was standardized by ISO/IEC and carried a published security proof, and it was still broken end to end. A proof secures a *model*; if the construction steps outside the model&apos;s assumptions, the proof guarantees nothing. Tellingly, the same attack left OCB1 and OCB3 untouched [@inoue2019-ocb2] -- a dead-end twig on a branch whose siblings survived.
&lt;p&gt;Two of GCM&apos;s three edges now had dedicated successors, and the family tree looked complete. But every construction so far -- GCM, ChaCha20-Poly1305, even the misuse-resistant SIV family -- quietly shared a third assumption nobody had thought to check: that &quot;authenticated&quot; meant &quot;bound to one key.&quot; In 2018, that assumption broke in production, on the desk of a Facebook security engineer holding a single file with two faces.&lt;/p&gt;
&lt;h2&gt;5. The Commitment Reckoning and the CAESAR Crucible&lt;/h2&gt;
&lt;p&gt;Now we can explain the salamanders. Recall the mechanism from Section 3: a GCM tag is a one-time Wegman-Carter value, $\operatorname{GHASH}_H(A,C) \oplus \operatorname{AES}_k(J_0)$, and GHASH is &lt;em&gt;linear&lt;/em&gt; in its coefficients. An attacker who gets to pick two keys, $k_1$ and $k_2$, can treat &quot;make the tag verify under both&quot; as a system of linear equations over $\mathrm{GF}(2^{128})$ and simply solve it, producing one ciphertext that decrypts to two chosen, meaningful plaintexts and passes verification under each key [@dodis2018-salamander].&lt;/p&gt;
&lt;p&gt;Nothing is broken. GCM guarantees confidentiality and integrity; it never promised that a ciphertext binds to a single key. That promise has a name, and until 2018 almost nobody was asking for it.&lt;/p&gt;

A graded robustness property beyond confidentiality and integrity. CMT-1 means a ciphertext binds to exactly one *key*; CMT-4 means it binds to the full *context* -- key, nonce, associated data, and message. Bellare and Hoang proved that CMT-1 does not imply CMT-4: committing to the key is strictly weaker than committing to everything [@bh2022-commit]. RFC 9771 standardized the vocabulary [@rfc9771].
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &quot;Authenticated&quot; never meant &quot;committed.&quot; An AEAD&apos;s guarantees -- IND-CPA plus INT-CTXT -- say nothing about binding a ciphertext to one key. Commitment is a third, orthogonal axis. That is why every default AEAD was non-committing and nobody noticed for fifteen years: the property was simply outside the definition everyone was proving.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two ideas define the modern state of the art, and both come from &lt;em&gt;refusing&lt;/em&gt; to treat &quot;cipher plus separate hash&quot; as the end of the design space.&lt;/p&gt;
&lt;p&gt;The first is &lt;strong&gt;robustness as an explicit goal&lt;/strong&gt; -- against the operator and against the adversary who supplies the key. The operator side is MRAE from Section 4, elevated from a mode into a principle. The adversary side is committing AEAD, and its timeline is a tidy example of a theoretical curiosity becoming an operational threat.&lt;/p&gt;
&lt;p&gt;Invisible salamanders (2018) showed a non-committing AEAD could defeat message franking [@dodis2018-salamander]. Then, in 2021, Julia Len, Paul Grubbs, and Thomas Ristenpart turned it into a weapon: &lt;strong&gt;partitioning oracle attacks&lt;/strong&gt; use the missing commitment to recover passwords and other low-entropy keys, crafting one ciphertext that decrypts under many candidate keys and using each success or failure to bisect the key space [@len2021-partition].&lt;/p&gt;
&lt;p&gt;Mihir Bellare and Viet Tung Hoang then built the graded CMT-1 through CMT-4 theory and cheap transforms that add full commitment with no ciphertext-size increase [@bh2022-commit]; Ange Albertini and coauthors independently catalogued the abuses and a padding-based fix [@albertini2022], with further theory from John Chan and Phillip Rogaway [@chan2022]. Sanketh Menda and coauthors then showed the gap is portfolio-wide, with context-discovery attacks against CCM, EAX, SIV, GCM, and OCB3 [@menda2023]. RFC 9771 (2025) finally fixed the vocabulary the field had been improvising [@rfc9771].&lt;/p&gt;

A vetted AEAD gives you confidentiality and integrity and stops there. Commitment is a third axis nobody put in the definition -- which is exactly why one ciphertext could wear two faces.
&lt;p&gt;The second idea is &lt;strong&gt;native co-design&lt;/strong&gt;: stop bolting a cipher to a hash and instead build the AEAD &lt;em&gt;around&lt;/em&gt; the hardware or footprint you actually have. Hongjun Wu and Bart Preneel&apos;s AEGIS (2013) is the AES-hardware extreme [@wu2013-aegis].&lt;/p&gt;
&lt;p&gt;It keeps a large internal state -- eight 128-bit words in AEGIS-128L -- seeded directly from the key and nonce; the &lt;strong&gt;AES round function&lt;/strong&gt; updates that state as it absorbs associated data and plaintext, encryption &lt;em&gt;squeezes&lt;/em&gt; a keystream out of the state, and the tag comes from the &lt;em&gt;finalized&lt;/em&gt; state. One primitive, one pass, no separate hash, and the IETF draft records that all variants are inverse-free and built from the AES encryption round [@aegis-draft18].&lt;/p&gt;
&lt;p&gt;That structure is &lt;em&gt;why&lt;/em&gt; AEGIS outruns GCM on AES silicon, and &lt;em&gt;why&lt;/em&gt; nonce reuse is catastrophic: the whole state is a function of &lt;code&gt;(key, nonce)&lt;/code&gt;, so a repeat lets an attacker unwind it. Its commitment story is a nuance, not a headline -- a 128-bit AEGIS tag delivers only about 64-bit committing security, while a 256-bit tag pushes cross-key collisions out of reach, so AEGIS is &lt;em&gt;partially&lt;/em&gt; committing by tag length, neither fully committing nor flatly broken [@aegis-draft18].&lt;/p&gt;
&lt;p&gt;At the other extreme, Ascon -- by Christoph Dobraunig, Maria Eichlseder, Florian Mendel, and Martin Schläffer -- uses a single lightweight permutation in a sponge, giving a tiny gate count and a side-channel-friendly structure for constrained hardware [@ascon2021].&lt;/p&gt;

flowchart LR
    KN[&quot;Key and nonce&quot;] --&amp;gt; ST[&quot;Large internal state, eight 128-bit words&quot;]
    ST --&amp;gt; UP[&quot;AES round function update&quot;]
    AD[&quot;Associated data&quot;] --&amp;gt; UP
    P[&quot;Plaintext&quot;] --&amp;gt; UP
    UP --&amp;gt; KS[&quot;Squeezed keystream&quot;]
    P --&amp;gt; X((&quot;XOR&quot;))
    KS --&amp;gt; X
    X --&amp;gt; C[&quot;Ciphertext&quot;]
    UP --&amp;gt; FIN[&quot;Finalized state&quot;]
    FIN --&amp;gt; T[&quot;Authentication tag&quot;]

A mode built from one public permutation whose state is split into a &quot;rate&quot; (absorbs input and is squeezed for output) and a &quot;capacity&quot; (the hidden security margin). A single permutation absorbs the key, nonce, associated data, and plaintext, then squeezes keystream and a tag -- no separate cipher and hash. Ascon is a sponge [@ascon2021].
&lt;p&gt;What forced this whole portfolio into existence was a contest. The &lt;strong&gt;CAESAR competition&lt;/strong&gt; (2013-2019), organized by Daniel Bernstein, was a public, multi-year, break-it-in-the-open bake-off [@caesar-home]. Its most important decision was structural: rather than crown a single winner, it selected a &lt;em&gt;portfolio across three use cases&lt;/em&gt; -- itself the thesis of this article. The winners were Ascon and ACORN for lightweight use, AEGIS-128 and OCB for high-performance use, and Deoxys-II (first choice) and COLM for defense in depth [@caesar-portfolio].Deoxys-II is built on the TWEAKEY framework for tweakable block ciphers, by Jérémy Jean, Ivica Nikolić, Thomas Peyrin, and Yannick Seurin [@caesar-portfolio].&lt;/p&gt;
&lt;p&gt;Note carefully: &lt;strong&gt;Deoxys-II is a CAESAR winner, not an also-ran&lt;/strong&gt; -- it simply was never deployed at scale. The real also-rans are the candidates eliminated during the competition, including MORUS, which took a certificational break from Tomer Ashur, Maria Eichlseder, and coauthors and did not make the final portfolio [@morus2018].&lt;/p&gt;
&lt;p&gt;The competition ended in 2019 with no single champion, on purpose. So where does that leave a working engineer in 2026, staring at seven names in a crypto library&apos;s documentation? To choose well, you need the current map: exactly what each construction is, where it wins, and where it bites.&lt;/p&gt;
&lt;h2&gt;6. The 2026 Portfolio, Precisely&lt;/h2&gt;
&lt;p&gt;The current portfolio has a stable shape: two defaults and five specialists. By deployment, the two internet defaults are AES-GCM and ChaCha20-Poly1305 -- but normatively they are not equals. In TLS 1.3 only AES-128-GCM is mandatory-to-implement (MUST); AES-256-GCM and ChaCha20-Poly1305 are both &lt;em&gt;recommended&lt;/em&gt; (SHOULD), the same normative level as each other, per RFC 8446 Section 9.1 [@rfc8446]. Everything else is a deliberate escalation. Here is each construction on its edges.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AES-GCM.&lt;/strong&gt; Counter mode plus a one-time GHASH tag; one pass, parallel, &lt;code&gt;O(1)&lt;/code&gt; online state [@mcgrew2004-gcm]. Excels on any CPU with AES-NI and CLMUL, which is every server and most modern clients. Struggles on hardware without those instructions and, above all, on nonce discipline: reuse is fatal and the two ceilings are easy to breach [@sp80038d]. Adoption: the internet default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ChaCha20-Poly1305.&lt;/strong&gt; ARX stream cipher plus a prime-field one-time MAC; constant-time in pure software with no lookup tables [@rfc8439]. Excels precisely where GCM struggles -- phones, embedded ARM, any target without AES hardware. Same nonce contract as GCM, so it struggles on the exact same reuse cliff. Adoption: TLS 1.3, WireGuard, OpenSSH [@wireguard-protocol][@openssh-chacha].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AES-CCM.&lt;/strong&gt; Counter mode plus CBC-MAC; two passes, serial, needs the length up front, but all-AES and tiny [@rfc3610]. Excels in constrained stacks (BLE [@bluetooth-core], Zigbee [@ieee802154]) and inside WPA2 [@ieee80211i]. Struggles on throughput and streaming, and the truncated &lt;code&gt;CCM_8&lt;/code&gt; tag trades a real forgery budget for eight bytes [@rfc3610]. Adoption: enormous by device count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AES-GCM-SIV and AES-SIV.&lt;/strong&gt; Synthetic-IV misuse-resistant modes; two-pass by necessity [@rfc8452][@rfc5297]. Excel when nonce uniqueness cannot be guaranteed: reuse degrades to leaking message equality rather than catastrophe. Struggle on streaming (they must buffer) and still leak equality, so they are survivable, not free. AES-GCM-SIV runs near AES-GCM speed on server hardware, around 0.92 cycles per byte on Broadwell in the original design [@gl2015-gcmsiv]. Adoption: growing where reuse risk is real.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OCB3.&lt;/strong&gt; One block-cipher key, one pass, fully parallel, roughly one cipher call per block -- technically superb [@kr2011-ocb3][@rfc7253]. Excels on elegance and software speed. Struggles on adoption, for the patent reasons in Section 4; it is nonce-respecting like GCM; and, unlike the CTR and stream modes, it is &lt;strong&gt;not&lt;/strong&gt; inverse-free -- decryption calls the AES inverse circuit (RFC 7253 Section 4.3), a genuine hardware cost [@rfc7253]. Adoption: minimal, despite standardization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AEGIS.&lt;/strong&gt; Keystream and authentication driven straight from the AES round function over a large nonce-seeded state; the throughput frontier, around 0.48 cycles per byte for AEGIS-128L on 4 KB messages, faster than CCM, GCM, and OCB [@wu2013-aegis][@aegis-v11]. Excels on raw speed when you control both endpoints. Struggles on maturity and contract: nonce-respecting (catastrophic on reuse, since its whole state is nonce-seeded) and only &lt;em&gt;partially&lt;/em&gt; committing -- about 64-bit committing security at a 128-bit tag, stronger at a 256-bit tag [@aegis-draft18].&lt;/p&gt;
&lt;p&gt;Critically, &lt;strong&gt;AEGIS is still an active Internet-Draft, &lt;code&gt;draft-irtf-cfrg-aegis-aead-18&lt;/code&gt; from October 2025, not an RFC&lt;/strong&gt; -- it states plainly that it &quot;is not an IETF product and is not a standard,&quot; and it expires on 8 April 2026 [@aegis-draft18].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ascon-AEAD128.&lt;/strong&gt; A single 320-bit lightweight permutation in a sponge; tiny state, side-channel-friendly [@ascon2021]. Excels on constrained hardware and gate count. Struggles on server throughput, where AEGIS and GCM win. It is nonce-respecting, but -- unlike the CTR and stream modes -- it commits to its inputs &lt;em&gt;without&lt;/em&gt; any added transform: the tag is squeezed from the sponge&apos;s large hidden capacity after that capacity has already absorbed the key, so one ciphertext binds natively to the key it was produced under. RFC 9771 lists Ascon-AEAD128 as both key-committing and full-committing as-is, not merely as a candidate for a bolt-on fix [@rfc9771]. The currency fact matters here: &lt;strong&gt;NIST SP 800-232 is final (August 2025) and standardizes Ascon-AEAD128&lt;/strong&gt;, a &lt;em&gt;tweaked&lt;/em&gt; variant of CAESAR Ascon-128 using a larger data-absorption rate -- widely reported as 128-bit, up from the CAESAR design&apos;s 64-bit, with the exact parameter in the SP 800-232 body [@sp800232][@nist-ascon-2023]. It is not byte-identical to CAESAR Ascon [@ascon-site].&lt;/p&gt;
&lt;p&gt;A profile only helps if you can actually call the construction, so here is where each one ships as of mid-2026, with each cell cited to that project&apos;s official documentation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Where it ships (libraries and frameworks)&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;OpenSSL/BoringSSL [@openssl-aes][@boringssl-aead], Go &lt;code&gt;crypto/cipher&lt;/code&gt; [@go-cipher], Java JCA [@java-jca], .NET CNG [@dotnet-aesgcm], WebCrypto (the only AEAD in the browser) [@webcrypto], libsodium (hardware-gated) [@libsodium-aegis]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305 / XChaCha20&lt;/td&gt;
&lt;td&gt;OpenSSL/BoringSSL [@openssl-chacha][@boringssl-aead], libsodium (default for random-nonce APIs) [@libsodium-aegis], Go &lt;code&gt;x/crypto&lt;/code&gt; [@go-chacha], WireGuard [@wireguard-protocol], OpenSSH [@openssh-chacha], &lt;code&gt;age&lt;/code&gt; [@age-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-CCM&lt;/td&gt;
&lt;td&gt;mbedTLS [@mbedtls-ccm], wolfSSL [@wolfssl-ccm], Wi-Fi [@ieee80211i] / BLE [@bluetooth-core] / Zigbee [@ieee802154] firmware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;BoringSSL/OpenSSL [@boringssl-aead][@openssl-aes] and several bindings; less universal than the defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-SIV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;miscreant&lt;/code&gt;-lineage (deterministic / key-wrap) libraries [@miscreant]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCB3&lt;/td&gt;
&lt;td&gt;some libraries (public domain since 2021 [@kr2021-ocb]); rarely a default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEGIS&lt;/td&gt;
&lt;td&gt;libsodium (&lt;code&gt;crypto_aead_aegis128l&lt;/code&gt; / &lt;code&gt;aegis256&lt;/code&gt;) [@libsodium-aegis] and a growing set of high-performance libraries; not a TLS suite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ascon-AEAD128&lt;/td&gt;
&lt;td&gt;reference and third-party implementations [@ascon-site]; adoption ramping since SP 800-232 [@sp800232]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two entries on that map decide real architectures. &lt;strong&gt;In the browser, AES-GCM is the only AEAD you can call.&lt;/strong&gt;WebCrypto&apos;s &lt;code&gt;SubtleCrypto.encrypt&lt;/code&gt; recognizes RSA-OAEP, AES-CTR, AES-CBC, and AES-GCM, and AES-GCM is the only one of those that authenticates. Browser JavaScript that needs an AEAD without bundling a crypto library therefore has exactly one option [@webcrypto]. And AEGIS, still pre-RFC, is nonetheless callable in production today through libsodium&apos;s &lt;code&gt;crypto_aead_aegis128l&lt;/code&gt; and &lt;code&gt;crypto_aead_aegis256&lt;/code&gt; APIs -- deployability and standardization are not the same axis [@libsodium-aegis].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Eight constructions -- the seven cipher families, with the SIV line counted in its two deployable forms (AES-GCM-SIV and AES-SIV) -- all linear-time, all vetted, and not one strictly dominates the others. CAESAR chose a portfolio across three use cases on purpose, and NIST added Ascon for the constrained end. There is no &quot;best AEAD,&quot; and any article or vendor that names one is hiding an assumption about your deployment [@caesar-portfolio].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two more items complete the 2026 map. The CAESAR defense-in-depth winners, Deoxys-II and COLM, remain excellent and essentially undeployed [@caesar-portfolio]. And committing transforms -- the fix for the commitment edge -- exist and are cheap, but as of mid-2026 none is &lt;em&gt;mandated&lt;/em&gt; in a shipping standard; RFC 9771 supplies vocabulary, not a required construction [@rfc9771][@bh2022-commit].&lt;/p&gt;
&lt;p&gt;Laid side by side, those eight look interchangeable. They are not, and the tool that makes the difference legible is a single table built on the three edges.&lt;/p&gt;
&lt;h2&gt;7. The Decision Matrix&lt;/h2&gt;
&lt;p&gt;This is the one screen to memorize. Everything in the previous six sections collapses into two tables and a flowchart, all built on the same three edges.&lt;/p&gt;
&lt;p&gt;The first table answers the only question that matters at selection time: for each construction, what happens on the &lt;em&gt;first repeated nonce&lt;/em&gt;, what hardware does it want, and does one ciphertext bind to one key?&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;First repeated nonce&lt;/th&gt;
&lt;th&gt;Hardware profile&lt;/th&gt;
&lt;th&gt;Commitment&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;Catastrophic: leaks P1 XOR P2, enables forgery&lt;/td&gt;
&lt;td&gt;AES-NI + CLMUL fast&lt;/td&gt;
&lt;td&gt;Not committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;Catastrophic: same one-time-MAC cliff&lt;/td&gt;
&lt;td&gt;Constant-time in software&lt;/td&gt;
&lt;td&gt;Not committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-CCM&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;td&gt;All-AES, tiny footprint&lt;/td&gt;
&lt;td&gt;Not committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;Graceful: leaks message equality only&lt;/td&gt;
&lt;td&gt;AES-NI + CLMUL&lt;/td&gt;
&lt;td&gt;Not committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-SIV&lt;/td&gt;
&lt;td&gt;Graceful: leaks message equality only&lt;/td&gt;
&lt;td&gt;All-AES, two-pass&lt;/td&gt;
&lt;td&gt;Not committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCB3&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;td&gt;Fast one-pass software&lt;/td&gt;
&lt;td&gt;Not committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEGIS&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;td&gt;AES round function, fastest&lt;/td&gt;
&lt;td&gt;Not fully committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ascon-AEAD128&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;td&gt;A few thousand gates&lt;/td&gt;
&lt;td&gt;Committing from the sponge, no transform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Read down the commitment column and the reckoning of Section 5 gets sharper than a flat &quot;nobody commits.&quot; Every deployed CTR, stream, and welded construction -- AES-GCM, ChaCha20-Poly1305, AES-CCM, the SIV family, and OCB3 -- is non-committing by default, which is exactly what the salamander and the portfolio-wide context-discovery attacks exploit [@dodis2018-salamander][@menda2023]. Two constructions break the pattern, and it is no accident that both fuse the tag with the cipher state instead of bolting on a separate hash: AEGIS partially commits through tag length, and Ascon commits natively from its sponge [@aegis-draft18][@rfc9771]. So commitment is not a uniform failure across the portfolio -- the commitment edge is a real axis of variation, with Ascon at the strong end, AEGIS in between, and the CTR, stream, and welded modes needing a bolt-on transform. Read the nonce column and only the SIV family survives a repeat, and only by leaking equality [@rfc8452]. The second table adds the structural and performance facts you need once the edges are settled.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Passes / parallel&lt;/th&gt;
&lt;th&gt;Online state&lt;/th&gt;
&lt;th&gt;Nonce&lt;/th&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;Reported speed&lt;/th&gt;
&lt;th&gt;Inverse-free?&lt;/th&gt;
&lt;th&gt;Best suited for&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;1 pass, parallel&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;~1 cpb with AES-NI&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The default, server and client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;1 pass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;Fast in pure software&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Targets without AES hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-CCM&lt;/td&gt;
&lt;td&gt;2 pass, serial&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7 to 13 byte&lt;/td&gt;
&lt;td&gt;128-bit (&lt;code&gt;CCM_8&lt;/code&gt;: 64)&lt;/td&gt;
&lt;td&gt;Modest&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Constrained stacks, WPA2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;2 pass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n)&lt;/code&gt; buffered&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;~0.92 cpb on Broadwell&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Nonce-reuse risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-SIV&lt;/td&gt;
&lt;td&gt;2 pass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n)&lt;/code&gt; buffered&lt;/td&gt;
&lt;td&gt;None or supplied&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;Modest&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Key wrap, deterministic AE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCB3&lt;/td&gt;
&lt;td&gt;1 pass, parallel&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;up to 120-bit&lt;/td&gt;
&lt;td&gt;up to 128-bit&lt;/td&gt;
&lt;td&gt;Fast in software&lt;/td&gt;
&lt;td&gt;No (needs AES decrypt)&lt;/td&gt;
&lt;td&gt;Elegance (now patent-clear)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEGIS&lt;/td&gt;
&lt;td&gt;1 pass, parallel&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;128 or 256-bit&lt;/td&gt;
&lt;td&gt;128 or 256-bit&lt;/td&gt;
&lt;td&gt;~0.48 cpb (AEGIS-128L)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Maximum throughput, both endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ascon-AEAD128&lt;/td&gt;
&lt;td&gt;1 pass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;Small, not server-fast&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;New constrained designs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Reported speeds are platform-dependent and should be read against live cross-platform benchmarks rather than a single headline number; the AEGIS and GCM-SIV figures come from their design papers, and the ECRYPT benchmarking project tracks the rest across many CPUs [@wu2013-aegis][@gl2015-gcmsiv][@bench-aead]. One column earns a second look: OCB3 is the only full-treatment construction that is &lt;em&gt;not&lt;/em&gt; inverse-free, so it alone needs the AES decrypt circuit in hardware -- the honest cost behind its elegance [@rfc7253].&lt;/p&gt;
&lt;p&gt;The tables classify. To &lt;em&gt;choose&lt;/em&gt;, turn them into a procedure. The flowchart below is the thesis rendered as a decision tree: branch on the nonce guarantee first (it is the edge that fails hardest), then on hardware, and treat commitment as an orthogonal final step because no default provides it.&lt;/p&gt;

flowchart TD
    Q1{&quot;Can you guarantee a unique nonce per key?&quot;}
    Q1 --&amp;gt;|No| SIV[&quot;AES-GCM-SIV: move on the nonce edge&quot;]
    Q1 --&amp;gt;|Yes| Q2{&quot;AES-NI and CLMUL on both endpoints?&quot;}
    Q2 --&amp;gt;|Yes| GCM[&quot;AES-GCM&quot;]
    Q2 --&amp;gt;|No| Q2b{&quot;Server or mobile software, or a constrained device?&quot;}
    Q2b --&amp;gt;|Software| CC[&quot;ChaCha20-Poly1305&quot;]
    Q2b --&amp;gt;|Constrained| ASC[&quot;Ascon-AEAD128&quot;]
    SIV --&amp;gt; Q3{&quot;Must one ciphertext bind to exactly one key?&quot;}
    GCM --&amp;gt; Q3
    CC --&amp;gt; Q3
    ASC --&amp;gt; Q3
    Q3 --&amp;gt;|Yes| ADD[&quot;Add a key-commitment transform, unless it already commits like Ascon&quot;]
    Q3 --&amp;gt;|No| DONE[&quot;Ship it&quot;]
&lt;p&gt;The argument is now explicit and testable. &quot;Which AEAD?&quot; reduces to &quot;which edge can your deployment &lt;em&gt;guarantee&lt;/em&gt; it never triggers?&quot; If you cannot guarantee nonce uniqueness, move right on the nonce edge to the SIV family. If you have no AES hardware, move on the hardware edge to ChaCha20-Poly1305 or, for a constrained device, Ascon. If a ciphertext must bind to exactly one key, you must &lt;em&gt;add&lt;/em&gt; something on the commitment edge, because none of the defaults gives it to you. You can even write the function down.&lt;/p&gt;
&lt;p&gt;{`
// &quot;Which AEAD?&quot; is a function of three edges, not a ranking.
function chooseAEAD({ nonceUnique, hasAesHardware, constrained, mustCommitToKey }) {
  let pick;
  if (!nonceUnique) {
    pick = &apos;AES-GCM-SIV&apos;;        // edge 1: cannot guarantee unique nonces
  } else if (hasAesHardware) {
    pick = &apos;AES-GCM&apos;;            // the internet default when AES-NI + CLMUL are present
  } else if (constrained) {
    pick = &apos;Ascon-AEAD128&apos;;      // edge 2: no AES hardware, only a few thousand gates
  } else {
    pick = &apos;ChaCha20-Poly1305&apos;;  // edge 2: no AES hardware, constant-time in software
  }
  // edge 3 is orthogonal: most picks do not commit, so you must ADD a transform --
  // except Ascon, which already commits natively from its sponge (RFC 9771).
  const alreadyCommits = pick === &apos;Ascon-AEAD128&apos;;
  return (mustCommitToKey &amp;amp;&amp;amp; !alreadyCommits)
    ? pick + &apos; + key-commitment transform (this construction does not bind to one key)&apos;
    : pick;
}&lt;/p&gt;
&lt;p&gt;console.log(chooseAEAD({ nonceUnique: true,  hasAesHardware: true,  constrained: false, mustCommitToKey: false }));
console.log(chooseAEAD({ nonceUnique: false, hasAesHardware: true,  constrained: false, mustCommitToKey: false }));
console.log(chooseAEAD({ nonceUnique: true,  hasAesHardware: false, constrained: false, mustCommitToKey: false }));
console.log(chooseAEAD({ nonceUnique: true,  hasAesHardware: true,  constrained: false, mustCommitToKey: true  }));
`}&lt;/p&gt;

&quot;Which AEAD should I use?&quot; is not a ranking from worst to best. It is a function: pick the one whose failure mode your deployment can guarantee it will never trigger.
&lt;p&gt;The table tells you which edge to move. It does not tell you why you sometimes &lt;em&gt;cannot&lt;/em&gt; move all of them at once -- why there is no row that is graceful on reuse &lt;em&gt;and&lt;/em&gt; single-pass &lt;em&gt;and&lt;/em&gt; fully committing &lt;em&gt;and&lt;/em&gt; fastest. That absence is not a gap in the engineering. It is a theorem.&lt;/p&gt;
&lt;h2&gt;8. The Limits Are Theorems&lt;/h2&gt;
&lt;p&gt;Everything so far has been a design choice: move an edge here, pay for it there. Now the hard walls -- the places where no cleverness helps, because a proof says so. There are four, and each maps to an edge; the standard graduate references develop these bounds in full [@boneh-shoup][@katz-lindell].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GCM&apos;s two ceilings, correctly attributed (the nonce edge).&lt;/strong&gt; GCM&apos;s two limits from SP 800-38D are not conservative engineering guesses; they are where the security proof runs out -- and they come from two &lt;em&gt;different&lt;/em&gt; mechanisms people constantly merge into one.&lt;/p&gt;
&lt;p&gt;The per-message limit of $2^{39}-256$ bits (about 64 GiB) is &lt;strong&gt;32-bit counter-mode block-space exhaustion&lt;/strong&gt;, not a GHASH property: with a 96-bit nonce GCM forms the initial counter block $J_0 = \mathrm{IV} \parallel 0^{31} \parallel 1$, the increment function advances only the low 32 bits, so counter mode emits at most $2^{32}-2$ keystream blocks, and $(2^{32}-2)\times 128$ bits $= 2^{39}-256$ bits; exceed it and the counter wraps, repeating a keystream block -- a two-time pad (SP 800-38D Section 5.2.1.1) [@sp80038d].&lt;/p&gt;
&lt;p&gt;The per-key cap of roughly $2^{32}$ messages under random 96-bit nonces is a different quantity entirely: the birthday bound on nonce collision, where with $q$ messages the probability of a repeat scales as $q^2 / 2^{96}$, so $q \approx 2^{32}$ keeps it negligible (SP 800-38D Section 8.3) [@sp80038d]. Hoang, Tessaro, and Thiruvengadam proved these multi-user bounds tight, validating the nonce-randomization mechanism TLS 1.3 uses [@htt2018-gcm].&lt;/p&gt;
&lt;p&gt;Tag length obeys its own bound: for a one-time Wegman-Carter MAC, forgery probability grows with the number of attempts and shrinks with tag length -- exactly why a 64-bit &lt;code&gt;CCM_8&lt;/code&gt; tag is a genuine, quantifiable forgery budget rather than a free optimization [@rfc3610].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Misuse-resistance cannot be online (the nonce edge, again).&lt;/strong&gt; This is the deepest limit in the article, and it explains a design decision that otherwise looks like laziness. Rogaway and Shrimpton proved that a deterministic, nonce-misuse-resistant AEAD &lt;em&gt;cannot&lt;/em&gt; be online or single-pass [@rs2006]. The reason is forced by the definition: to be misuse-resistant, the ciphertext must depend on the &lt;em&gt;entire&lt;/em&gt; plaintext (otherwise two messages sharing a prefix under a repeated nonce would leak that prefix), so the encryptor cannot emit even the first ciphertext byte until it has read the last plaintext byte. There is no implementation trick around it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; You cannot have both streaming and misuse-resistance. It is a theorem, not a missing feature: a misuse-resistant AEAD must read the whole message before it can output any ciphertext. That single impossibility is &lt;em&gt;why&lt;/em&gt; AES-GCM-SIV is two-pass, and why &quot;just make GCM misuse-resistant without slowing it down&quot; is a request for something that provably does not exist [@rs2006].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Commitment is an orthogonal axis (the commitment edge).&lt;/strong&gt; A conventional CTR-based AEAD like GCM is provably not key-committing -- the property is simply absent from &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;IND-CPA and INT-CTXT&lt;/a&gt;, so no amount of using GCM &quot;correctly&quot; produces it. Worse, commitment is not one property but a lattice: Bellare and Hoang proved CMT-1 (bind to the key) does not imply CMT-4 (bind to the full context), a separation RFC 9771 later codified [@bh2022-commit][@rfc9771].&lt;/p&gt;
&lt;p&gt;And the attacks are cheaper than the naive birthday intuition suggests: Menda and coauthors found context-discovery attacks against CCM, EAX, SIV, GCM, and OCB3, including an $O(2^{n/3})$ attack on SIV using Wagner&apos;s k-tree algorithm -- well below the $2^{n/2}$ a birthday bound would suggest [@menda2023].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Releasing plaintext early is a strictly weaker world (all edges).&lt;/strong&gt; The fourth limit is about what your decrypt function is allowed to do before it finishes.&lt;/p&gt;

What an implementation exposes if it emits decrypted plaintext before checking the authentication tag. The formal integrity notion for this setting, INT-RUP, is codified in RFC 9771 Section 4.3.10; security that survives releasing unverified plaintext is strictly stronger than standard AEAD security, and most fast one-pass modes satisfy it only weakly -- which is why the safe API contract is verify-then-release: never act on plaintext until the tag checks out [@rfc9771].
&lt;p&gt;Now assemble the thought experiment. Imagine the ideal AEAD: graceful on nonce reuse &lt;em&gt;and&lt;/em&gt; fully committing &lt;em&gt;and&lt;/em&gt; online/single-pass &lt;em&gt;and&lt;/em&gt; fastest at both the server and constrained extremes. The second limit rules out &quot;misuse-resistant and online&quot; together. The third makes commitment an add-on that the CTR, stream, and welded defaults lack -- Ascon, uniquely, commits natively. The performance extremes pull in opposite directions -- AES round function for servers, a few thousand gates for sensors. You cannot have all of it in one construction, and this is not because nobody has been clever enough. It is because the properties provably conflict.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; No single construction closes all three edges at once. That is not a temporary state of the art -- it is a set of theorems about online-ness, commitment, and hardware. The portfolio is not a failure to converge on a winner; it is the mathematically necessary shape of the solution [@rs2006][@bh2022-commit].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So that is settled, provably. Which turns the interesting question inside out. Not &quot;which mode wins?&quot; but &quot;can we make the &lt;em&gt;robustness&lt;/em&gt; the default instead of the expert&apos;s opt-in?&quot; That is exactly where the field is fighting right now.&lt;/p&gt;
&lt;h2&gt;9. Where AEAD Still Bites&lt;/h2&gt;
&lt;p&gt;The classical problem is closed. For confidentiality and integrity, the bounds are tight and the constructions are vetted. The entire live frontier is about robustness: turning the properties nobody used to ask for into defaults nobody has to remember. Six problems are open, and every one of them has the same shape.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Committing AEAD by default.&lt;/strong&gt; Salamanders and partitioning oracles still bite wherever a committing transform is omitted, which today means almost everywhere [@dodis2018-salamander][@len2021-partition]. The frustrating part is that the fix is cheap: Bellare and Hoang give transforms that add full commitment with no ciphertext-size increase, and Albertini and coauthors give a padding-based alternative [@bh2022-commit][@albertini2022]. Yet as of mid-2026, none is &lt;em&gt;mandated&lt;/em&gt; by a shipping standard. RFC 9771 gives the field a shared vocabulary for the property but does not require any construction to have it [@rfc9771]. The open question is not &quot;how?&quot; but &quot;why is it still opt-in?&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Misuse-resistance by default.&lt;/strong&gt; If nonce reuse is a recurring operational reality, why is the misuse-resistant mode the escalation rather than the default? The answer is the theorem from Section 8: defaulting to SIV means defaulting to two-pass, non-streaming encryption, and a great deal of infrastructure assumes it can encrypt a stream as it arrives [@rs2006]. Whether that trade is worth making by default is an active argument, not a settled one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The release-of-unverified-plaintext trade space.&lt;/strong&gt; Between &quot;buffer everything and verify first&quot; and &quot;stream plaintext out immediately&quot; is a design space that streaming media, large-file transfer, and constrained receivers all care about, and the security definitions there -- INT-RUP and its relatives -- are still maturing [@rfc9771].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hardware co-design versus conservative margin.&lt;/strong&gt; AEGIS already beats AES-GCM on AES-NI hardware, and the appetite for line-rate encryption keeps growing [@wu2013-aegis]. Newer AES-round designs push the throughput frontier even higher, but they trade cryptanalytic maturity for speed, and their standardization status is unsettled -- AEGIS itself is still only an Internet-Draft, not an RFC [@aegis-draft18]. How much margin to spend for how much throughput is an open judgment call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/&quot; rel=&quot;noopener&quot;&gt;Post-quantum reality&lt;/a&gt;, minus the myth.&lt;/strong&gt; This one is mostly a matter of correcting a widespread misconception.&lt;/p&gt;

For symmetric authenticated encryption the honest answer is: no, and you mostly need to change nothing. Grover&apos;s algorithm offers at most a square-root speedup on key search, which halves the effective key length [@grover1996], so a 128-bit key gives about 64 bits of margin against a hypothetical quantum attacker. The remedy is simply to prefer 256-bit keys -- ChaCha20 already uses one, and AES-256 is a configuration flag -- and to size tags with a margin. The post-quantum upheaval lives in key exchange and signatures, not the AEAD record layer, as developed in the post-quantum-cryptography-on-Windows post in this collection. Your record encryption survives the transition largely intact.
&lt;p&gt;&lt;strong&gt;Constant-time without special hardware.&lt;/strong&gt; GHASH is hard to implement in constant time without CLMUL, and table-based AES leaks timing without AES-NI -- the exact side-channel pressure that motivated both ChaCha20-Poly1305 and Ascon [@bernstein-chacha][@ascon2021]. On the smallest devices, where neither special instruction exists and power analysis is a live threat, side-channel-resistant AEAD is still an open engineering problem, and it is much of the reason NIST ran a lightweight competition at all.&lt;/p&gt;
&lt;p&gt;None of these has a finished answer. But notice the shape of every one: they are all attempts to move an &lt;em&gt;edge&lt;/em&gt; to a place where the deployer cannot step on it by mistake -- misuse-resistance you do not have to remember, commitment you do not have to add, side-channel safety you do not have to hand-tune. Which brings us back to the only rule that actually travels.&lt;/p&gt;
&lt;h2&gt;10. The Field Guide: Rules and Parameters&lt;/h2&gt;
&lt;p&gt;Everything above collapses into one default and a short list of deliberate escalations. Follow the ladder and you will never again pick an AEAD by folklore.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Default to &lt;strong&gt;AES-GCM&lt;/strong&gt; with a 96-bit nonce that is unique per key (prefer a counter over a random value), a hard cap well under $2^{32}$ messages per key, at most 64 GiB per message, and a full 128-bit tag. Use &lt;strong&gt;ChaCha20-Poly1305&lt;/strong&gt; under the same contract when you lack AES hardware. Leave the default only when a specific edge forces you, and only in the direction that edge points [@sp80038d][@rfc8439].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The escalation ladder, each rung mapped to the edge it moves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cannot guarantee a unique nonce?&lt;/strong&gt; Escalate on the nonce edge to &lt;strong&gt;AES-GCM-SIV&lt;/strong&gt; [@rfc8452]. Cloned VMs, counters that reset on crash, weak first-boot entropy -- if any of these is in your threat model, the graceful failure is worth the second pass.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A stack mandates it, or the device is tiny?&lt;/strong&gt; Use &lt;strong&gt;AES-CCM&lt;/strong&gt; where WPA2, BLE, or Zigbee require it [@rfc3610], and &lt;strong&gt;Ascon-AEAD128&lt;/strong&gt; for &lt;em&gt;new&lt;/em&gt; constrained designs, now that it is a NIST standard [@sp800232].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deterministic encryption or key wrapping?&lt;/strong&gt; Use &lt;strong&gt;AES-SIV&lt;/strong&gt;, which needs no nonce at all and is built for exactly this [@rfc5297].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You want one-pass, single-primitive elegance?&lt;/strong&gt; &lt;strong&gt;OCB3&lt;/strong&gt; is now patent-clear, though rarely the right call given how entrenched the defaults are [@rfc7253][@kr2021-ocb].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You control both endpoints and need maximum throughput?&lt;/strong&gt; &lt;strong&gt;AEGIS&lt;/strong&gt; is the fastest option, if you can accept a pre-RFC specification [@aegis-draft18][@wu2013-aegis].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Must a ciphertext bind to exactly one key?&lt;/strong&gt; &lt;em&gt;Add&lt;/em&gt; a key-commitment transform -- for password-based encryption, key rotation, message franking, or multi-recipient encryption -- because no default provides it [@bh2022-commit].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;Nonce generation&lt;/a&gt; itself is the subject of Part 2 of this series; do not re-derive it here, but do treat &quot;unique per key&quot; as a hard contract, not a hope. On Windows, the &lt;a href=&quot;https://paragmali.com/blog/cng-architecture-bcrypt-ncrypt-ksps/&quot; rel=&quot;noopener&quot;&gt;CNG architecture&lt;/a&gt; post in this collection shows AES-GCM exposed through the &lt;code&gt;BCryptEncrypt&lt;/code&gt; API with an authenticated-cipher-mode information structure -- a concrete example of the parameters below appearing in a real API.&lt;/p&gt;
&lt;p&gt;Pair the ladder with the support map from Section 6 when you check feasibility: browser JavaScript means AES-GCM through WebCrypto, and a target with no AES hardware means ChaCha20-Poly1305 or, if it is also tiny, Ascon-AEAD128 [@webcrypto].&lt;/p&gt;
&lt;p&gt;The exact operational contract, per construction:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Nonce&lt;/th&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;Per-key limit&lt;/th&gt;
&lt;th&gt;Per-message limit&lt;/th&gt;
&lt;th&gt;On nonce reuse&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;96-bit, unique&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;~`2^32` msgs (random nonce)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2^39 - 256&lt;/code&gt; bits, ~64 GiB&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;96-bit, unique&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;~`2^32` msgs (random nonce)&lt;/td&gt;
&lt;td&gt;~256 GiB&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-CCM&lt;/td&gt;
&lt;td&gt;7 to 13 byte&lt;/td&gt;
&lt;td&gt;up to 128-bit (&lt;code&gt;CCM_8&lt;/code&gt;: 64)&lt;/td&gt;
&lt;td&gt;Set by nonce length&lt;/td&gt;
&lt;td&gt;Set by length field&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;Improved (per-nonce derived keys)&lt;/td&gt;
&lt;td&gt;~64 GiB&lt;/td&gt;
&lt;td&gt;Graceful: message equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-SIV&lt;/td&gt;
&lt;td&gt;None or supplied&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;td&gt;Graceful: message equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCB3&lt;/td&gt;
&lt;td&gt;up to 120-bit&lt;/td&gt;
&lt;td&gt;up to 128-bit&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEGIS&lt;/td&gt;
&lt;td&gt;128 or 256-bit&lt;/td&gt;
&lt;td&gt;128 or 256-bit&lt;/td&gt;
&lt;td&gt;Large (wide nonce)&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ascon-AEAD128&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;Per SP 800-232&lt;/td&gt;
&lt;td&gt;Per SP 800-232&lt;/td&gt;
&lt;td&gt;Catastrophic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The only two numeric limits you must hard-code are GCM&apos;s, because they are the ones people breach silently: cap messages per key well under $2^{32}$ and messages under 64 GiB each [@sp80038d]. ChaCha20-Poly1305&apos;s own ~256 GiB per-message ceiling comes the same way, from its 32-bit block counter over 64-byte blocks ($2^{32}\times 64$ bytes $= 2^{38}$ bytes), per RFC 8439 [@rfc8439]. AES-GCM-SIV&apos;s per-nonce key derivation buys better multi-user bounds, which is the whole reason RFC 8452 derives fresh keys rather than reusing one [@bht2018][@rfc8452].&lt;/p&gt;
&lt;p&gt;Every rung of that ladder has a mirror image: a misuse pattern that punishes the deployment for stepping on the edge it ignored. Each row below maps a real-code mistake to the named break that catches it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse seen in real code&lt;/th&gt;
&lt;th&gt;Edge it triggers&lt;/th&gt;
&lt;th&gt;Named break&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Reusing a &lt;code&gt;(key, nonce)&lt;/code&gt; in GCM or AEGIS&lt;/td&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;Nonce-Disrespecting Adversaries, 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assuming an AEAD binds a ciphertext to one key&lt;/td&gt;
&lt;td&gt;Commitment&lt;/td&gt;
&lt;td&gt;Invisible salamanders; partitioning oracles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Truncating the tag to save bytes (&lt;code&gt;CCM_8&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Integrity budget&lt;/td&gt;
&lt;td&gt;Wegman-Carter forgery bound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Treating AES-GCM-SIV reuse as &quot;free&quot;&lt;/td&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;SIV leaks message equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hand-rolling Encrypt-and-MAC&lt;/td&gt;
&lt;td&gt;Composition&lt;/td&gt;
&lt;td&gt;Bellare-Namprempre; Krawczyk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflating GCM&apos;s two ceilings&lt;/td&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;The two SP 800-38D limits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The evidence for each is in the sections above: nonce reuse on live servers [@bock2016], the salamander and its weaponization [@dodis2018-salamander][@len2021-partition], the tag-length forgery budget [@rfc3610], the SIV equality leak [@rfc8452], the composition theorems [@bn2000][@krawczyk2001], and the two ceilings [@sp80038d]. One extra caution on attribution: the live-TLS nonce-reuse break is repeatedly mis-cited to CVE-2016-0270, which actually names IBM Domino and which the NVD itself flags as commonly misapplied to other products [@cve-2016-0270].&lt;/p&gt;

Search your codebase for any AEAD decryption that runs under an attacker-influenced key: password-based encryption, key wrapping, token decryption, or multi-recipient envelopes. If a decrypt path lets the caller supply or guess the key and then branches on whether decryption succeeded, you are exposed to a partitioning oracle and need a key-commitment transform on top of the AEAD [@len2021-partition][@bh2022-commit].
&lt;p&gt;That ladder is your inoculation against folklore. But folklore is sticky, so let us take the most common misconceptions head-on.&lt;/p&gt;
&lt;h2&gt;11. Seven Beliefs That Get Deployments Broken&lt;/h2&gt;


No. For every nonce-respecting mode -- AES-GCM, ChaCha20-Poly1305, AES-CCM, OCB3, AEGIS, and Ascon -- a single repeated `(key, nonce)` is an immediate, total break: it leaks the XOR of the two plaintexts, and a second collision hands over the authentication subkey for universal forgery [@joux2006][@bock2016]. Only the SIV family degrades gracefully, and only to leaking message equality [@rfc8452].


No. It makes reuse *survivable*, not free. A repeated nonce still leaks message equality -- an attacker learns that two ciphertexts encrypt the same plaintext -- which can matter a great deal in the right context. Use it as a safety net, not a license to stop managing nonces [@rfc8452][@rs2006].


No to both misreadings. It is a different trade-off on the hardware edge, not a weaker cipher: constant-time in pure software with no lookup tables, which makes it the *stronger* choice on hardware without AES-NI, where table-based AES leaks timing. And it is not mandatory-to-implement in TLS 1.3 -- it is a *recommended* (SHOULD) cipher suite, while only AES-128-GCM is the MUST, per RFC 8446 Section 9.1 [@rfc8446]. Recommended, not weaker, not mandatory.


No. Random 96-bit nonces are collision-safe only up to roughly $2^{32}$ messages per key, by the birthday bound. That per-key invocation cap is a different number from the per-message limit of about 64 GiB; enforce both, and prefer a counter over a random nonce when you can [@sp80038d][@htt2018-gcm].


No. Most AEADs are not key-committing. One AES-GCM ciphertext can be built to decrypt to two different meaningful plaintexts under two different keys, with the tag valid both times -- the invisible-salamanders result. If a ciphertext must bind to one key, add a commitment transform [@dodis2018-salamander][@bh2022-commit].


No. AEGIS leads on raw throughput, but it is nonce-respecting (catastrophic on reuse), not fully key-committing, and still an active Internet-Draft rather than an RFC -- it states plainly that it &quot;is not a standard&quot; and expires on 8 April 2026. Fastest is not the same as best for your deployment [@aegis-draft18][@wu2013-aegis].


No. Symmetric authenticated encryption survives the quantum transition. Grover&apos;s algorithm only halves effective key length [@grover1996], so prefer 256-bit keys and size tags with margin. The post-quantum upheaval is in key exchange and signatures, not the AEAD record layer. Separately, note that NIST&apos;s Ascon-AEAD128 is a tweaked variant of the CAESAR Ascon-128, not byte-identical [@sp800232].

&lt;h2&gt;The Function, Not the Ranking&lt;/h2&gt;
&lt;p&gt;Seven ciphers, one interface, three edges. Every break here was a deployment triggering an edge its designers deliberately moved elsewhere. The invisible salamanders triggered the commitment edge that AEAD never promised to close [@dodis2018-salamander]. The 184 TLS servers triggered the nonce edge that GHASH cannot survive [@bock2016]. The &lt;code&gt;CCM_8&lt;/code&gt; forgery budget is the tag-length edge priced in bits [@rfc3610]. The SIV equality leak is the residue of the one edge SIV &lt;em&gt;does&lt;/em&gt; close -- even the fix has a seam [@rfc8452]. And OCB3&apos;s near-total absence is the patent grave, an edge that was never technical at all [@kr2021-ocb].&lt;/p&gt;
&lt;p&gt;Put them together and the thesis is earned rather than asserted. &quot;Which AEAD should I use?&quot; is not a ranking from worst to best, because Section 8 proved there is no best -- no single construction can be misuse-resistant &lt;em&gt;and&lt;/em&gt; single-pass &lt;em&gt;and&lt;/em&gt; fully committing &lt;em&gt;and&lt;/em&gt; fastest at once. It is a function: pick the construction whose failure mode your deployment can &lt;em&gt;guarantee&lt;/em&gt; it will never trigger.&lt;/p&gt;
&lt;p&gt;Master the three edges and you can place any construction on the map, predict any misuse before it ships, and evaluate whatever the next competition invents -- key-derivation-bound AEADs, the TLS record layer&apos;s next mode, or a lightweight winner that does not exist yet. The names will change. The edges will not.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;the-aead-decision-matrix&quot; keyTerms={[
  { term: &quot;AEAD&quot;, definition: &quot;One keyed call giving confidentiality, plaintext integrity, and associated-data integrity&quot; },
  { term: &quot;Associated Data&quot;, definition: &quot;Header bytes authenticated but not encrypted, travelling in the clear&quot; },
  { term: &quot;Nonce&quot;, definition: &quot;A value that must be unique per key for a nonce-respecting mode&quot; },
  { term: &quot;MRAE&quot;, definition: &quot;Misuse-resistant AE, where nonce reuse leaks only message equality&quot; },
  { term: &quot;Wegman-Carter one-time MAC&quot;, definition: &quot;A tag of the form keyed-hash plus one-time mask, the shared root of GHASH and Poly1305&quot; },
  { term: &quot;GHASH&quot;, definition: &quot;GCM&apos;s linear universal hash at a secret point, recovering which forges tags&quot; },
  { term: &quot;Synthetic IV (SIV)&quot;, definition: &quot;An IV derived as a MAC of the whole message, so the tag is the IV&quot; },
  { term: &quot;Key commitment&quot;, definition: &quot;Binding a ciphertext to one key (CMT-1) up to the full context (CMT-4)&quot; },
  { term: &quot;Sponge&quot;, definition: &quot;One permutation that absorbs input and squeezes keystream and tag, as in Ascon&quot; },
  { term: &quot;Inverse-free&quot;, definition: &quot;A decryption path that never calls the block-cipher inverse, true of the CTR, stream, and sponge modes but not OCB3&quot; },
  { term: &quot;RUP&quot;, definition: &quot;Release of Unverified Plaintext, emitting plaintext before the tag is checked&quot; }
]} questions={[
  { q: &quot;What are the three edges that decide an AEAD choice?&quot;, a: &quot;The nonce contract, the performance and hardware profile, and commitment.&quot; },
  { q: &quot;Why is a repeated nonce catastrophic in AES-GCM?&quot;, a: &quot;It reuses the one-time Wegman-Carter mask, leaking the plaintext XOR and, with a second collision, the GHASH subkey for forgery.&quot; },
  { q: &quot;Why can a misuse-resistant AEAD not be single-pass?&quot;, a: &quot;Its ciphertext must depend on the whole message, so it must read the last plaintext byte before emitting any ciphertext, a Rogaway-Shrimpton theorem.&quot; },
  { q: &quot;Does authenticated mean a ciphertext binds to one key?&quot;, a: &quot;No. Commitment is an orthogonal axis, and most AEADs are not key-committing by default.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>aead</category><category>authenticated-encryption</category><category>aes-gcm</category><category>chacha20-poly1305</category><category>nonce-misuse</category><category>key-commitment</category><category>applied-cryptography</category><category>cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>One Number, Used Twice: How a Repeated Nonce Hands Over Your Private Key -- and How Determinism Takes It Back</title><link>https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/</link><guid isPermaLink="true">https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/</guid><description>A repeated ECDSA nonce leaks your private key with grade-school algebra; a reused AES-GCM IV forges ciphertext. Why -- and how determinism fixes both.</description><pubDate>Thu, 09 Jul 2026 10:13:36 GMT</pubDate><content:encoded>
Reuse one number and you can lose everything. A **repeated** ECDSA signing nonce leaks your private key with grade-school algebra: two signatures under the same nonce, one subtraction, one modular inverse, and the key falls out. That is how the Sony PlayStation 3 master key was recovered [@f0f]. A **repeated** AES-GCM initialization vector is just as fatal: it instantly leaks $P_1 \oplus P_2$ and hands an attacker the material to forge authenticated ciphertext [@joux] [@nda]. The fix for both is one counter-intuitive move: stop trusting runtime randomness for the once-per-operation number. Derive it (RFC 6979, EdDSA, hedged signatures) [@rfc6979] [@rfc8032], or make a repeat non-catastrophic (AES-GCM-SIV) [@rfc8452]. One distinction separates understanding from folklore: a *biased* nonce is a *different*, statistical attack needing many signatures, not one [@ns] [@ladderleak].
&lt;h2&gt;1. One Number, Used Twice&lt;/h2&gt;
&lt;p&gt;In December 2010, on stage at the 27th Chaos Communication Congress in Berlin, three researchers working under the name fail0verflow explained how they had recovered the master private key that anchors the entire PlayStation 3 chain of trust [@f0f]. There was no brute force, no exotic side channel, no supercomputer grinding for months. There were two signatures and the kind of algebra you learned before you were old enough to drive. Sony had signed its code with ECDSA, the elliptic-curve digital signature algorithm, and Sony had signed &lt;em&gt;every&lt;/em&gt; firmware image with the &lt;em&gt;same&lt;/em&gt; secret per-signature number.Two events that are often merged into one: fail0verflow &lt;em&gt;disclosed&lt;/em&gt; the constant-nonce flaw at 27C3 in December 2010; George Hotz, known as geohot, &lt;em&gt;separately&lt;/em&gt; published an extracted PS3 root key in January 2011 [@geohot]. The talk revealed the mechanism; the key release came afterward. Keep them distinct.&lt;/p&gt;
&lt;p&gt;Sit with how strange that is. The console&apos;s security rested on a signature scheme that most cryptographers would call sound. AES was not broken. The elliptic curve was not broken. The signature math was not broken. One input was reused, and the entire trust anchor fell out of a schoolbook manipulation that a motivated teenager could reproduce on paper.&lt;/p&gt;
&lt;p&gt;Now the mirror image, in a completely different primitive. In 2016, a team scanning the live internet found 184 public HTTPS servers repeating a different one-time number -- the AES-GCM initialization vector -- and reported that this &quot;fully breaks the authenticity of the connections&quot; [@nda]. Not the confidentiality of a lab toy: the authenticity of real TLS sessions on the open web, letting an attacker forge messages the server would accept as genuine.&lt;/p&gt;
&lt;p&gt;Same disease, entirely different organ. One is a signature that proves who you are; the other is the cipher that guards most of the web&apos;s traffic. Both staked everything on a number that was supposed to be used correctly exactly once.&lt;/p&gt;
&lt;p&gt;That shared bet is the subject of this article. Both primitives depend, silently, on a value that must be unique and unpredictable -- once, per key. The precise question: &lt;em&gt;what, exactly, does each stake on that number, and what is the exact cost of getting it wrong?&lt;/em&gt; For the repeat case, the answer is total and immediate -- and the algebra is short enough to print in full.&lt;/p&gt;

In cryptography, reusing one number can cost you everything.
&lt;p&gt;That is the punchline, and by the end you will have earned it rather than taken it on faith -- the full key-recovery algebra, Antoine Joux&apos;s cipher attack, the statistical cousin that needs only a &lt;em&gt;fraction&lt;/em&gt; of a leaked bit, and the deterministic designs that dissolve the whole failure family. But how can &lt;em&gt;two&lt;/em&gt; signatures undo an entire console&apos;s chain of trust? To see it, we first have to meet the number both of these primitives are quietly betting everything on.&lt;/p&gt;
&lt;h2&gt;2. The Secret Number Both Primitives Depend On&lt;/h2&gt;
&lt;p&gt;Two algorithms, designed decades and a world apart -- a discrete-logarithm signature and a &lt;a href=&quot;https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw&quot; rel=&quot;noopener&quot;&gt;block-cipher mode of authenticated encryption&lt;/a&gt; -- turn out to share a single point of failure. Before we can watch it break, we need to know where the number comes from and why it has to exist at all.&lt;/p&gt;
&lt;p&gt;The signature side is the older lineage. ElGamal&apos;s 1985 signature scheme introduced a per-signature secret exponent [@elgamal], and when the United States standardized the Digital Signature Algorithm as FIPS 186 in 1994 [@fips186], that per-signature secret came along for the ride. Its elliptic-curve descendant, ECDSA, keeps it. The value is called the signing nonce, written $k$, and it is regenerated fresh for every single signature.&lt;/p&gt;

The per-signature secret scalar in (EC)DSA. For each signature the signer draws a new $k$ in the range $[1, n-1]$, where $n$ is the order of the curve&apos;s base point. It must be unique, secret, and uniformly distributed -- exactly once per key. It is never transmitted, yet every signature is computed from it.
&lt;p&gt;The cipher side is younger. When NIST standardized Galois/Counter Mode in SP 800-38D in 2007, the specification required that the initialization vector -- the $\text{IV}$, GCM&apos;s nonce -- be distinct for every message encrypted under a given key [@sp80038d]. Same structural demand, opposite secrecy requirement: the signing nonce must be secret, while the GCM nonce may be completely public. What both must be is &lt;em&gt;fresh&lt;/em&gt;.&lt;/p&gt;

The per-message value GCM mixes in so that encrypting under a fixed key never repeats its keystream. Uniqueness under a given key -- not secrecy -- is the entire contract. A public counter is a perfectly good GCM nonce; a *repeated* one is a catastrophe.
&lt;p&gt;Now put the two governing equations side by side. An ECDSA signature on a message whose hash (as an integer) is $z$, under private key $d$, is the pair $(r, s)$:&lt;/p&gt;
&lt;p&gt;$$r = (k \cdot G)_x \bmod n, \qquad s = k^{-1},(z + r,d) \bmod n$$&lt;/p&gt;
&lt;p&gt;Here $G$ is the fixed base point, $n$ its prime order, $d$ the private key, and $r$ is the x-coordinate of the curve point $k \cdot G$ reduced modulo $n$. The AES-GCM authentication tag on ciphertext $C$ with additional data $A$ is:&lt;/p&gt;
&lt;p&gt;$$T = E_K(J_0) \oplus \mathrm{GHASH}_H(A, C), \qquad H = E_K(0^{128})$$&lt;/p&gt;
&lt;p&gt;$E_K$ is AES under key $K$; $J_0$ is a pre-counter block derived from the IV; $\mathrm{GHASH}_H$ is a polynomial hash over the field $\mathrm{GF}(2^{128})$ keyed by the secret subkey $H$, which is simply AES applied to the all-zero block.&lt;/p&gt;
&lt;p&gt;Look at what both equations hide inside them. The signing equation multiplies the private key $d$ by the public $r$ and masks it behind $k$. The tag equation masks the keyed hash behind the one-time pad $E_K(J_0)$, which is a deterministic function of the nonce. In each case, one freshly chosen value is the only thing standing between a routine operation and a full disclosure. That gives us the single shared contract, short enough to fit on one line: &lt;strong&gt;the number must be unique and unpredictable, exactly once, per key.&lt;/strong&gt;&lt;/p&gt;

This is the in-depth sequel to [Part 2](/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand), which surveyed the two ways cryptographic randomness betrays you -- predictability and repetition -- across many primitives at breadth. Part 2 owns the general taxonomy; this post goes deep on two specific victims, the ECDSA nonce and the GCM IV, and derives the exact algebra of each collapse. We will not re-teach the two-knobs framework here; we will spend it.
&lt;p&gt;None of this was a secret to the designers. Bellare, Goldwasser, and Micciancio analyzed the danger of a poorly generated DSA nonce as early as their 1997 CRYPTO paper, &quot;The DSS case&quot; [@bgm], showing that a linear-congruential nonce generator breaks the scheme.Terminology drifts across modes, so fix it now: a &lt;em&gt;nonce&lt;/em&gt; need only be unique (CTR, GCM); an &lt;em&gt;IV&lt;/em&gt; for CBC must additionally be unpredictable; a &lt;em&gt;salt&lt;/em&gt; is unique and public and feeds a KDF. The mode dictates which property is load-bearing. Part 2 and the block-cipher-modes installment work through the distinctions; here we only need &quot;fresh, once, per key.&quot; The contract was understood from the beginning; nobody misunderstood the rule. They simply could not always keep it.&lt;/p&gt;
&lt;p&gt;The contract says &quot;never repeat.&quot; So begin with the crudest possible violation -- the &lt;em&gt;same&lt;/em&gt; number, twice -- and watch precisely how much it costs.&lt;/p&gt;
&lt;h2&gt;3. Two Signatures, One k, and Your Private Key&lt;/h2&gt;
&lt;p&gt;This is the entire thesis compressed into one page of algebra -- no lattice, no side channel, no probability, just subtraction and one division. How little work does &lt;em&gt;complete&lt;/em&gt; key recovery actually take?&lt;/p&gt;
&lt;p&gt;Start from the fact that makes it possible. The first signature component, $r = (k \cdot G)_x \bmod n$, depends only on the nonce $k$ and the fixed public base point $G$. It does not depend on the message. So if you sign two &lt;em&gt;different&lt;/em&gt; messages under the &lt;em&gt;same&lt;/em&gt; $k$, both signatures carry the &lt;em&gt;identical&lt;/em&gt; $r$. That repeated $r$ is the visible tell -- a fingerprint an attacker can spot by scanning a signature log, with no secret knowledge at all.This is exactly what fail0verflow read off the PlayStation 3: every firmware signature carried the same $r$, the public fingerprint of a constant $k$. You do not need to break anything to notice it; you just look for two signatures with a matching first half.&lt;/p&gt;
&lt;p&gt;Now write the two signing equations under that shared $k$:&lt;/p&gt;
&lt;p&gt;$$s_1 = k^{-1}(z_1 + r,d) \pmod n, \qquad s_2 = k^{-1}(z_2 + r,d) \pmod n$$&lt;/p&gt;
&lt;p&gt;Subtract the second from the first. The private key appears only inside the identical $r,d$ term, so it cancels completely:&lt;/p&gt;
&lt;p&gt;$$s_1 - s_2 = k^{-1}\big((z_1 + r,d) - (z_2 + r,d)\big) = k^{-1}(z_1 - z_2) \pmod n$$&lt;/p&gt;
&lt;p&gt;Every quantity on the right except $k$ is public: the two message hashes $z_1, z_2$ and the two signature halves $s_1, s_2$. So solve for the nonce directly:&lt;/p&gt;
&lt;p&gt;$$k = (z_1 - z_2),(s_1 - s_2)^{-1} \bmod n$$&lt;/p&gt;
&lt;p&gt;One subtraction on top, one modular inverse on the bottom. With $k$ in hand, substitute back into the first equation -- multiply through by $k$, subtract $z_1$, and divide by the public $r$:&lt;/p&gt;
&lt;p&gt;$$s_1 k = z_1 + r,d \implies d = (s_1 k - z_1),r^{-1} \bmod n$$&lt;/p&gt;
&lt;p&gt;That is the private key. Count the cost to the attacker: &lt;strong&gt;two&lt;/strong&gt; signatures, a handful of modular operations,Constant work here means a fixed number of field operations, independent of the key size. A 256-bit curve is no harder to break this way than a 160-bit one, because the attack never touches the key&apos;s length. and the recovery is &lt;em&gt;certain&lt;/em&gt; -- not probable, not &quot;on average,&quot; but exact, every time, the moment the repeat occurs. It solves a two-by-two linear system that the reuse handed over for free.&lt;/p&gt;

flowchart TD
    A[&quot;Two signatures made under the same nonce k&quot;] --&amp;gt; B[&quot;They share the same r, because r depends only on k and G&quot;]
    B --&amp;gt; C[&quot;Subtract the two s equations&quot;]
    C --&amp;gt; D[&quot;The r times d term cancels, leaving k as the only unknown&quot;]
    D --&amp;gt; E[&quot;One modular inverse recovers k&quot;]
    E --&amp;gt; F[&quot;Put k back into either s equation&quot;]
    F --&amp;gt; G[&quot;The private key d falls out, with certainty&quot;]
&lt;p&gt;You do not have to take the algebra on faith -- here is the whole attack as runnable code over a toy curve order, recovering the private key from public values alone:&lt;/p&gt;
&lt;p&gt;{`
// Recover the ECDSA private key d from two signatures sharing nonce k.
function egcd(a, b) {
  if (b === 0n) return [a, 1n, 0n];
  const [g, x, y] = egcd(b, a % b);
  return [g, y, x - (a / b) * y];
}
function modInv(a, n) {
  a = ((a % n) + n) % n;
  const [g, x] = egcd(a, n);
  if (g !== 1n) throw new Error(&apos;no inverse&apos;);
  return ((x % n) + n) % n;
}&lt;/p&gt;
&lt;p&gt;const n = 1103n;   // toy prime &quot;curve order&quot; (real curves use ~256-bit n)
const d = 937n;    // the SECRET key the attacker must not know
const k = 421n;    // the reused nonce (identical for both signatures)
const r = 668n;    // r = (k*G).x mod n -- SHARED because k is shared
const z1 = 123n, z2 = 456n;  // two different message hashes&lt;/p&gt;
&lt;p&gt;// Victim signs both messages with the SAME k:
const s1 = (modInv(k, n) * (z1 + r * d)) % n;
const s2 = (modInv(k, n) * (z2 + r * d)) % n;&lt;/p&gt;
&lt;p&gt;// Attacker sees only public (r, s1, z1, s2, z2). Recover k, then d:
const kRec = ((((z1 - z2) % n) + n) % n) * modInv((((s1 - s2) % n) + n) % n, n) % n;
const dRec = ((((s1 * kRec - z1) % n) + n) % n) * modInv(r, n) % n;&lt;/p&gt;
&lt;p&gt;console.log(&apos;shared r    :&apos;, r.toString());
console.log(&apos;recovered k :&apos;, kRec.toString(), &apos;(true k =&apos;, k.toString() + &apos;)&apos;);
console.log(&apos;recovered d :&apos;, dRec.toString(), &apos;(true d =&apos;, d.toString() + &apos;)&apos;);
console.log(dRec === d ? &apos;PRIVATE KEY RECOVERED from two signatures.&apos; : &apos;mismatch&apos;);
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Two signatures sharing one nonce equal certain, instant, total key recovery -- by hand, with pure algebra. Reuse is not a slope that gradually weakens security; it is a cliff. &quot;Reuse one number equals everything&quot; is a literal statement about ECDSA, not a rhetorical flourish.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is not a museum piece. The PlayStation 3 fell to exactly this -- a &lt;em&gt;constant&lt;/em&gt; $k$ across every firmware signature, two images sharing one $r$, the master key recovered [@f0f]. Section 5 catalogs the rest of the real-world chain, from that constant-nonce break down to biases of less than a single bit.&lt;/p&gt;
&lt;p&gt;Certainty from a literal repeat is the easy case -- easy for an attacker to run, and, as we will see, easy for a defender to eliminate. So attackers asked a sharper and far more dangerous question. What if the nonces are &lt;em&gt;never&lt;/em&gt; equal, only slightly, invisibly &lt;em&gt;skewed&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;4. From Repeat to Bias: The Hidden Number Problem&lt;/h2&gt;
&lt;p&gt;You do not need the nonces to be &lt;em&gt;equal&lt;/em&gt;. You only need them to be a little bit &lt;em&gt;predictable&lt;/em&gt; -- and then to collect enough of them. This is where the story turns, because it dismantles the comforting fix you probably just thought of. If a literal repeat is the danger, surely &quot;make sure the nonces are always different&quot; closes it? It does not come close.&lt;/p&gt;
&lt;p&gt;Here is the shift in viewpoint. In Section 3, a repeat gave us an exact equation and we solved it. Now suppose each nonce is merely &lt;em&gt;skewed&lt;/em&gt; -- say the top few bits are always zero, or the generator leans slightly toward small values. No two nonces are equal, so the certainty attack never triggers. But each signature is now a &lt;em&gt;noisy linear equation&lt;/em&gt; relating the public data to the secret key $d$ modulo $n$, with the unknown nonce constrained to a smaller range than it should occupy. One such equation tells you almost nothing.&lt;/p&gt;
&lt;p&gt;Stack hundreds or thousands of them as the rows of a lattice, though, and the secret $d$ shows up as one unusually short vector hiding in that grid. A reduction algorithm hunts it down, and the key reads straight out.&lt;/p&gt;

The problem of recovering a hidden integer -- here, the private key -- from many samples that each reveal only a few most-significant bits of a related product modulo $n$. Dan Boneh and Ramarathnam Venkatesan introduced it in 1996, and it is the framework underneath every biased-nonce attack on (EC)DSA [@hnp].

An integer grid of points formed by adding and subtracting a fixed set of basis vectors. Biased-nonce recovery constructs a lattice whose unusually short vector encodes the secret key; a reduction algorithm finds that vector. We stay conceptual here -- the internals of LLL and the closest-vector problem belong to their own discussion.
&lt;p&gt;The genealogy is worth knowing, because each step needed &lt;em&gt;less&lt;/em&gt; leakage than the last. Boneh and Venkatesan framed the Hidden Number Problem in 1996 [@hnp]. Around 2000, Daniel Bleichenbacher showed that even a tiny statistical bias in DSA nonces is exploitable through a Fourier-analytic version of the same idea.Bleichenbacher&apos;s seminal biased-nonce result was never published as a standalone paper. It survives as a 2000 IEEE P1363 presentation and a 2005 CRYPTO rump-session talk, &quot;Experiments with DSA,&quot; and is reconstructed through the reference lists of later work such as Nguyen-Shparlinski and LadderLeak. A standalone Bleichenbacher paper on this would be a fabrication; the history genuinely has this shape.&lt;/p&gt;
&lt;p&gt;Nick Howgrave-Graham and Nigel Smart made the lattice attack practical in 2001, recovering keys from partially known nonces [@hgs]. Phong Nguyen and Igor Shparlinski then proved rigorous polynomial-time ECDSA key recovery from just a few leaked nonce bits per signature in 2003 [@ns]. Two decades on, LadderLeak pushed the frontier to its logical extreme, breaking ECDSA with &lt;em&gt;less than one bit&lt;/em&gt; of nonce leakage per signature [@ladderleak].&lt;/p&gt;
&lt;p&gt;Now the distinction that everything downstream depends on. The biased case is &lt;em&gt;not&lt;/em&gt; the repeat case with weaker numbers. It is a different kind of attack with a different cost profile, and conflating them is the single most common way this topic gets mistold.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;em&gt;repeated&lt;/em&gt; nonce leaks the key from &lt;em&gt;two&lt;/em&gt; signatures, with certainty (Section 3). A &lt;em&gt;biased&lt;/em&gt; nonce leaks nothing usable from any single signature; it takes &lt;em&gt;many&lt;/em&gt; signatures -- hundreds, thousands, or more -- aggregated by a lattice or Fourier reduction before the key emerges. &quot;One biased signature reveals your key&quot; is false. Keep the two cases apart or you will misquote the entire failure family.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Laid out side by side, the contrast is stark:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Repeated nonce&lt;/th&gt;
&lt;th&gt;Biased nonce&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Leak per signature&lt;/td&gt;
&lt;td&gt;The entire nonce (matches another)&lt;/td&gt;
&lt;td&gt;A few bits, or a fraction of one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signatures needed&lt;/td&gt;
&lt;td&gt;Two&lt;/td&gt;
&lt;td&gt;Many (hundreds to millions, per bias)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;Subtraction and one modular inverse&lt;/td&gt;
&lt;td&gt;Lattice reduction or Fourier analysis (HNP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;Certain and exact&lt;/td&gt;
&lt;td&gt;Statistical, high-probability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Work&lt;/td&gt;
&lt;td&gt;Schoolbook algebra by hand&lt;/td&gt;
&lt;td&gt;Serious computation on aggregated data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defeated only by&lt;/td&gt;
&lt;td&gt;Distinct nonces&lt;/td&gt;
&lt;td&gt;Removing the bias, not just repeats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart TD
    A[&quot;Many signatures, each nonce slightly biased&quot;] --&amp;gt; B[&quot;Each is a noisy linear equation in the secret d&quot;]
    B --&amp;gt; C[&quot;Stack the equations as rows of a lattice&quot;]
    C --&amp;gt; D[&quot;The secret d hides in one unusually short vector&quot;]
    D --&amp;gt; E[&quot;Lattice reduction finds that short vector&quot;]
    F[&quot;Read the private key d straight out of it&quot;]
    E --&amp;gt; F
&lt;p&gt;The lesson lands hard. If even a &lt;em&gt;fraction of one bit&lt;/em&gt; of bias, harvested across enough signatures, surrenders the key, then &quot;never literally repeat $k$&quot; is nowhere near enough of a rule, and &quot;use a better random number generator&quot; is a treadmill, not a fix. The only question left is how bad this got in real, shipped hardware -- devices built by careful engineers who knew all of this.&lt;/p&gt;
&lt;h2&gt;5. The Catastrophes in the Wild: ECDSA&lt;/h2&gt;
&lt;p&gt;The theory is alarming; the field results are worse. What follows is the evolution told as a body count, and the number to watch is the &lt;em&gt;leak the attacker needed&lt;/em&gt;. It shrinks with almost every entry, from an entire repeated nonce down to a fraction of a single bit, and every generation still won.&lt;/p&gt;
&lt;p&gt;The 2010 PlayStation 3 break sits at one end: a &lt;em&gt;constant&lt;/em&gt; nonce across all firmware signatures, the maximum possible leak, two signatures sufficient [@f0f]. In 2013 the Android &lt;code&gt;SecureRandom&lt;/code&gt; defect produced repeated and predictable signing nonces on mobile devices; because Bitcoin wallets sign transactions with ECDSA, attackers drained the vulnerable ones [@bitcoin]. Google&apos;s own writeup traced the root cause to improper initialization of the underlying PRNG in the Java Cryptography Architecture [@securerandom].The often-repeated figure of roughly 55 BTC stolen in the 2013 Android incident comes from secondary news reporting [@hackernews]. The primary bitcoin.org advisory [@bitcoin] names the flaw and the affected wallet apps but states no loss total. Treat the number as reported, not official.&lt;/p&gt;
&lt;p&gt;Then the leak starts shrinking. Minerva (2020) attacked real smart cards and cryptographic libraries that leaked the &lt;em&gt;bit-length&lt;/em&gt; of the nonce through timing -- a whisper of bias, not a repeat -- and recovered keys from &quot;just 500 signatures for simulated leakage data, 1200 for real cryptographic library data, and 2100 for smartcard data&quot; [@minerva].&lt;/p&gt;
&lt;p&gt;TPM-Fail (disclosed 2019, published at USENIX Security 2020) turned a timing side channel in Intel&apos;s firmware TPM (CVE-2019-11090) and an STMicroelectronics TPM (CVE-2019-16863) into nonce-bit leakage. It recovered an ECDSA key from the Intel fTPM in 4 to 20 minutes and, remotely, &quot;the authentication key of a virtual private network (VPN) server in 5 hours&quot; [@tpmfail] [@moghimi].&lt;/p&gt;
&lt;p&gt;LadderLeak (2020) then broke ECDSA with &lt;em&gt;less than one bit&lt;/em&gt; of nonce leakage per signature, exploiting a Montgomery-ladder side channel in OpenSSL [@ladderleak], a result published at ACM CCS 2020 [@ladderleak_doi]. And in 2024, PuTTY&apos;s use of P-521 exposed a structural bias so clean it needs almost no data at all (CVE-2024-31497) [@putty].&lt;/p&gt;

&quot;The first 9 bits of each ECDSA nonce are zero. This allows for full secret key recovery in roughly 60 signatures.&quot; -- the PuTTY P-521 disclosure [@putty]
&lt;p&gt;Sixty signatures. An attacker who had watched roughly sixty SSH handshakes, or collected sixty signed Git commits, could reconstruct the user&apos;s private key. The affected PuTTY versions ran from 0.68 through 0.80 (fixed in 0.81), and the same biased generator shipped in FileZilla, WinSCP, TortoiseGit, and TortoiseSVN [@putty]. Collect them into one table and the trend is impossible to miss:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Repeat or bias&lt;/th&gt;
&lt;th&gt;Leak per signature&lt;/th&gt;
&lt;th&gt;Signatures needed&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;Sony PS3 firmware&lt;/td&gt;
&lt;td&gt;Repeat (constant k)&lt;/td&gt;
&lt;td&gt;The entire nonce&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[@f0f]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Android Bitcoin wallets&lt;/td&gt;
&lt;td&gt;Repeat / predictable k&lt;/td&gt;
&lt;td&gt;Nonce reused or guessable&lt;/td&gt;
&lt;td&gt;A few&lt;/td&gt;
&lt;td&gt;[@bitcoin] [@securerandom]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Minerva (cards, libraries)&lt;/td&gt;
&lt;td&gt;Bias (nonce bit-length)&lt;/td&gt;
&lt;td&gt;Timing reveals length&lt;/td&gt;
&lt;td&gt;500 to 2100&lt;/td&gt;
&lt;td&gt;[@minerva]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019-2020&lt;/td&gt;
&lt;td&gt;TPM-Fail (Intel, STMicro TPMs)&lt;/td&gt;
&lt;td&gt;Bias (timing)&lt;/td&gt;
&lt;td&gt;Nonce high bits via timing&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;td&gt;[@tpmfail] [@moghimi]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;LadderLeak (OpenSSL ladder)&lt;/td&gt;
&lt;td&gt;Bias (sub-bit)&lt;/td&gt;
&lt;td&gt;Less than one bit&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;td&gt;[@ladderleak]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;PuTTY P-521 (CVE-2024-31497)&lt;/td&gt;
&lt;td&gt;Bias (structural)&lt;/td&gt;
&lt;td&gt;First 9 bits fixed at zero&lt;/td&gt;
&lt;td&gt;~60&lt;/td&gt;
&lt;td&gt;[@putty]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart LR
    A[&quot;1996 HNP framework: bias is exploitable in principle&quot;] --&amp;gt; B[&quot;2010 PS3: a constant nonce, the whole value repeats&quot;]
    B --&amp;gt; C[&quot;2013 Android: predictable and repeated nonces&quot;]
    C --&amp;gt; D[&quot;2020 Minerva and TPM-Fail: a few biased bits via timing&quot;]
    D --&amp;gt; E[&quot;2020 LadderLeak: less than one bit per signature&quot;]
    E --&amp;gt; F[&quot;2024 PuTTY P-521: nine fixed bits, about 60 signatures&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Read the arc left to right and one lesson dominates: the leakage an attacker needs keeps &lt;em&gt;shrinking&lt;/em&gt;, from an entire repeated nonce to less than a single bit. No generator is perfectly unbiased, and -- as Part 2 argued -- you can never prove one is. Any residual bias, given enough signatures, eventually surrenders the key. &quot;Harden the random number generator&quot; buys time; it does not close the hole. That conclusion is the synthesis of the whole table, and it is what forces the search for a structural answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every entry above is a &lt;em&gt;signature&lt;/em&gt; scheme bleeding out its private key through the nonce. You would be forgiven for thinking this is an ECDSA problem. It is not. The exact same structural flaw -- one number that must be used once and is fatal on reuse -- was hiding inside a completely different primitive, the cipher that protects most of the traffic on the modern web.&lt;/p&gt;
&lt;h2&gt;6. The Other Primitive: AES-GCM&apos;s Forbidden Attack&lt;/h2&gt;
&lt;p&gt;Different math, a different decade, a different threat model -- and the exact same fatal dependency on one number used once. The puzzle here is sharper: AES-GCM&apos;s nonce is &lt;em&gt;public&lt;/em&gt;, so how can reusing a value the attacker already knows leak a forgery key? The answer is Antoine Joux&apos;s forbidden attack, and it mirrors the signature story beat for beat.&lt;/p&gt;
&lt;p&gt;Recall the tag from Section 2: $T = E_K(J_0) \oplus \mathrm{GHASH}_H(A, C)$, where $H = E_K(0^{128})$. Two facts about that formula are load-bearing. First, $H$ depends only on the key, so it is fixed for the whole life of that key. Second, $E_K(J_0)$ depends only on the key and the nonce, through the pre-counter block $J_0$. Reuse the nonce and $E_K(J_0)$ comes back &lt;em&gt;identical&lt;/em&gt;.&lt;/p&gt;

The secret 128-bit value $H = E_K(0^{128})$ -- AES applied to the all-zero block -- that keys GCM&apos;s polynomial authenticator over $\mathrm{GF}(2^{128})$. It never changes for a given key, and recovering it lets an attacker forge authentication tags at will.
&lt;p&gt;Now reuse one $(\text{key}, \text{IV})$ pair on two messages. Two things break, and they break separately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The confidentiality break, from a single collision.&lt;/strong&gt; GCM encrypts with counter mode: the ciphertext is the plaintext XORed with a keystream generated from $J_0$. Same key, same IV means the same $J_0$, means the &lt;em&gt;same keystream&lt;/em&gt;. So for two messages encrypted under the reused pair, $C \oplus C&apos; = P \oplus P&apos;$. The keystream cancels and the XOR of the two plaintexts falls out immediately, from just one collision, with no further work. If the attacker knows or guesses one plaintext, the other is fully revealed. Here is that leak as runnable code:&lt;/p&gt;
&lt;p&gt;{`
// Reuse one (key, IV) in a keystream mode -&amp;gt; C XOR C&apos; leaks P XOR P&apos;.
// GCM encrypts with AES-CTR: same (key, IV) reproduces the SAME keystream.
function xor(a, b) { return a.map((x, i) =&amp;gt; x ^ b[i]); }
const bytes = s =&amp;gt; Array.from(new TextEncoder().encode(s));
const hex = a =&amp;gt; a.map(x =&amp;gt; x.toString(16).padStart(2, &apos;0&apos;)).join(&apos;&apos;);&lt;/p&gt;
&lt;p&gt;// A fixed keystream stands in for AES-CTR(key, IV): identical for both messages.
const keystream = [0x9e,0x37,0x5a,0xc1,0x02,0xbb,0x44,0xd0,0x71,0x6f,0x88,0x2a];&lt;/p&gt;
&lt;p&gt;const P1 = bytes(&apos;transfer=10 &apos;).slice(0, keystream.length);
const P2 = bytes(&apos;transfer=9999&apos;).slice(0, keystream.length);&lt;/p&gt;
&lt;p&gt;const C1 = xor(P1, keystream);   // ciphertext 1
const C2 = xor(P2, keystream);   // ciphertext 2, SAME keystream&lt;/p&gt;
&lt;p&gt;// Attacker never sees the key or keystream -- only C1 and C2:
const leak = xor(C1, C2);        // == P1 XOR P2, the keystream cancels
console.log(&apos;C1 XOR C2 :&apos;, hex(leak));
console.log(&apos;P1 XOR P2 :&apos;, hex(xor(P1, P2)), &apos;(identical -&amp;gt; keystream is gone)&apos;);&lt;/p&gt;
&lt;p&gt;// If the attacker also knows P1, then P2 falls out immediately:
console.log(&apos;recover P2:&apos;, new TextDecoder().decode(new Uint8Array(xor(leak, P1))));
`}&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The authenticity break, from the tag difference.&lt;/strong&gt; This is Joux&apos;s contribution. Write the two tags under the reused pair and XOR them. The identical blind $E_K(J_0)$ cancels exactly:&lt;/p&gt;
&lt;p&gt;$$T \oplus T&apos; = \mathrm{GHASH}_H(A, C) \oplus \mathrm{GHASH}_H(A&apos;, C&apos;)$$&lt;/p&gt;
&lt;p&gt;Every block of $A, C, A&apos;, C&apos;$ is observable on the wire, and $\mathrm{GHASH}_H$ is a polynomial in $H$ over $\mathrm{GF}(2^{128})$ with those blocks as coefficients. So the right-hand side is a &lt;em&gt;known polynomial in the one unknown $H$&lt;/em&gt;, and it equals the observed left-hand side. Move everything to one side and you have a polynomial equation whose roots are candidate values for $H$; factoring it over $\mathrm{GF}(2^{128})$ yields that candidate set [@joux].&lt;/p&gt;
&lt;p&gt;Recover $H$ and, since $E_K(J_0) = T \oplus \mathrm{GHASH}_H(A, C)$ falls out too, the attacker can compute a valid tag for &lt;em&gt;any&lt;/em&gt; ciphertext they like under that nonce. That is a &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-&quot; rel=&quot;noopener&quot;&gt;universal forgery&lt;/a&gt;.&lt;/p&gt;

Antoine Joux&apos;s 2006 attack on GCM: reusing a $(\text{key}, \text{IV})$ pair cancels the tag&apos;s $E_K(J_0)$ blind, turning the difference of two tags into a known polynomial over $\mathrm{GF}(2^{128})$ whose roots are candidates for the subkey $H$. The name reflects that GCM&apos;s unique-IV rule is not a suggestion -- breaking it is forbidden [@joux].

flowchart TD
    A[&quot;Tag T equals E_K(J0) XOR GHASH_H of the message&quot;] --&amp;gt; B[&quot;J0 comes from the IV, H comes only from the key&quot;]
    B --&amp;gt; C[&quot;Reuse the same key and IV on a second message&quot;]
    C --&amp;gt; D[&quot;E_K(J0) is identical in both tags, so it cancels under XOR&quot;]
    D --&amp;gt; E[&quot;T XOR T-prime is a known polynomial in H over GF(2 to the 128)&quot;]
    E --&amp;gt; F[&quot;Its roots are the candidate values for the subkey H&quot;]
    F --&amp;gt; G[&quot;Two or more collisions pin H, enabling universal forgery&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;em&gt;single&lt;/em&gt; $(\text{key}, \text{IV})$ collision leaks $P_1 \oplus P_2$ immediately and unconditionally -- confidentiality is gone at once. Recovering the subkey $H$ is a &lt;em&gt;separate&lt;/em&gt; step: one collision yields a &lt;em&gt;set of candidate roots&lt;/em&gt;, and pinning $H$ uniquely generally needs &lt;em&gt;two or more&lt;/em&gt; collisions. &quot;One reuse instantly recovers the authentication key&quot; overstates it. &quot;One reuse instantly destroys confidentiality and opens the road to forgery&quot; is exact.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is not confined to whiteboards. In 2016 the Nonce-Disrespecting Adversaries team scanned live TLS and &quot;identified 184 HTTPS servers repeating nonces, which fully breaks the authenticity of the connections&quot; [@nda].Joux&apos;s result is his 2006 public comment to NIST, whose embedded document title reads &quot;Authentication Failures in NIST version of GCM&quot; [@joux]. It is sometimes miscited as IACR ePrint 2006/187 -- but that number is a different paper, on HMAC and NMAC, by Kim, Biryukov, Preneel, and Hong [@eprint187]. Cite the NIST comment, not the ePrint. These were production HTTPS endpoints, not lab toys, and their nonce reuse let anyone forge authenticated records against them.The canonical live-TLS evidence for GCM nonce reuse is that paper [@nda], not CVE-2016-0270, which concerns IBM Domino rather than OpenSSL. If you see that CVE cited as the OpenSSL GCM-reuse bug, it is a misattribution.&lt;/p&gt;
&lt;p&gt;Two primitives, one disease: a once-per-operation number that is death on reuse. If the flaw is structural rather than a coding slip, then &quot;be more careful with your randomness&quot; cannot be the cure -- Section 5 already showed that treadmill has no end. The fix has to be architectural. What would that even look like?&lt;/p&gt;
&lt;h2&gt;7. Make the Number Deterministic&lt;/h2&gt;
&lt;p&gt;The cure for a randomness disaster turned out to be &lt;em&gt;less&lt;/em&gt; randomness. That sentence should feel wrong -- we have spent five sections watching bad randomness destroy keys, so surely the answer is &lt;em&gt;better&lt;/em&gt; randomness. It is not, and seeing why is the moment the whole subject clicks into place.&lt;/p&gt;
&lt;p&gt;Go back to what actually failed. The signing equation needs a $k$ that is unique and unpredictable. Every real break came from the &lt;em&gt;runtime source&lt;/em&gt; of that $k$: a constant, a repeated PRNG output, a biased generator, a timing leak. So Thomas Pornin&apos;s RFC 6979 asks a disarming question -- what if we never draw $k$ from a runtime source at all? Instead, &lt;em&gt;derive&lt;/em&gt; it, deterministically, from two things the signer already holds: the private key and the message [@rfc6979].&lt;/p&gt;

A signature whose per-message nonce is computed as a pseudorandom function of the private key and the message, using no runtime randomness at all. The same $(\text{key}, \text{message})$ pair always yields the same nonce, and therefore the same signature. RFC 6979 specifies this for (EC)DSA; EdDSA builds it into the scheme [@rfc6979] [@rfc8032].
&lt;p&gt;Concretely, RFC 6979 runs an HMAC-based deterministic random bit generator, seeded from the private key $x$ and the message hash, and rejection-samples its output into the valid range $[1, n-1]$. The two helper encodings simply turn the key and the hash into fixed-width byte strings so the HMAC has well-defined inputs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;h  = H(message)                              # hash the message
V  = 0x01 0x01 ... 0x01                       # hlen bytes of 0x01
K  = 0x00 0x00 ... 0x00                       # hlen bytes of 0x00
K  = HMAC(K, V || 0x00 || int2octets(x) || bits2octets(h))
V  = HMAC(K, V)
K  = HMAC(K, V || 0x01 || int2octets(x) || bits2octets(h))
V  = HMAC(K, V)
loop:
    T = &quot;&quot;                                     # accumulate qlen bits of output
    while bitlen(T) &amp;lt; qlen:                    # one HMAC block if qlen &amp;lt;= hlen; more if longer
        V = HMAC(K, V);  T = T || V
    k = bits2int(T)
    if 1 &amp;lt;= k &amp;lt;= n-1:  return k               # in range -&amp;gt; use it
    K = HMAC(K, V || 0x00);  V = HMAC(K, V)    # else reseed and retry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at what this buys. The generator is seeded by the private key and the message, and nothing else, and two consequences close the &lt;em&gt;generation&lt;/em&gt; side of the Part-A attack surface.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;repeat&lt;/strong&gt; case dies because two &lt;em&gt;different&lt;/em&gt; messages produce two &lt;em&gt;unrelated&lt;/em&gt; nonces, while the same message reproduces the same signature harmlessly -- there is no runtime generator whose collision could hand you a shared $k$. The &lt;strong&gt;bias&lt;/strong&gt; case dies wherever the bias was &lt;em&gt;born in a generator&lt;/em&gt;: with no runtime generator in the loop, there is nothing to skew and no entropy pool to under-initialize. Every Section-5 break that lived in the generator -- the repeated $k$ on the PS3, the predictable $k$ from a weak &lt;code&gt;SecureRandom&lt;/code&gt; -- has simply been removed from the design, because there is no generator left to fail.&lt;/p&gt;

Deterministic signers &quot;do not need access to a source of high-quality randomness.&quot; -- RFC 6979 [@rfc6979]
&lt;p&gt;And the compatibility story is almost too good: a verifier cannot even tell. The verification equation is unchanged, because a signature is still just a valid $(r, s)$ pair; only the signer&apos;s method of choosing $k$ changed. Deployments can switch to deterministic signing unilaterally. You do not have to take the &quot;same input, same output&quot; claim on trust either:&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Deterministic nonce k = PRF(private key, message). // Real RFC 6979 uses HMAC-DRBG; this toy uses a small deterministic mixer // only to show the SHAPE: same (key,msg) =&amp;gt; same k; different msg =&amp;gt; unrelated k. const MASK = (1n &amp;lt;&amp;lt; 64n) - 1n; const n = (1n &amp;lt;&amp;lt; 61n) - 1n;              // toy prime modulus, k in [1, n-1] function rotl(x, r) { return ((x &amp;lt;&amp;lt; r) | (x &amp;gt;&amp;gt; (64n - r))) &amp;amp; MASK; } function prf(key, msg) {                  // deterministic, consults no RNG   let h = 1469598103934665603n;           // 64-bit offset basis   for (const ch of (key + &apos;|&apos; + msg)) {     h = (h ^ BigInt(ch.charCodeAt(0))) &amp;amp; MASK;     h = (h * 1099511628211n) &amp;amp; MASK;       // FNV-style multiply     h = rotl(h, 13n) ^ (h &amp;gt;&amp;gt; 7n);          // extra diffusion so 1 char avalanches   }   return (h % (n - 1n)) + 1n;              // map into [1, n-1] } const sk = &apos;private-key-0xA1B2&apos;; console.log(&apos;k(pay Bob 10) :&apos;, prf(sk, &apos;pay Bob 10&apos;).toString()); console.log(&apos;k(pay Bob 10) :&apos;, prf(sk, &apos;pay Bob 10&apos;).toString(), &apos;&amp;lt;- identical, no RNG consulted&apos;); console.log(&apos;k(pay Bob 11) :&apos;, prf(sk, &apos;pay Bob 11&apos;).toString(), &apos;&amp;lt;- unrelated&apos;); console.log(&apos;No source of runtime randomness was consulted.&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;The same idea has a sibling that was born deterministic. EdDSA, and its popular instantiation Ed25519, does not bolt determinism on -- it designs the runtime randomness out from the start, computing its per-signature secret as a hash of a key-derived prefix and the message [@rfc8032].That is why Ed25519 has no separate &quot;use RFC 6979&quot; mode: there is no runtime nonce anywhere in the scheme to bias or repeat. Determinism is not an option you enable; it is the definition.&lt;/p&gt;
&lt;p&gt;Standards bodies followed the evidence: FIPS 186-5, the current US signature standard, now approves deterministic ECDSA &quot;as specified in IETF RFC 6979&quot; [@fips1865] [@fips1865pdf]. There is even a quiet operational bonus. A deterministic signer is &lt;em&gt;testable&lt;/em&gt;: you can ship known-answer test vectors and check byte-for-byte that an implementation is correct, which a randomized signer can never offer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The fix for a randomness failure was to remove the randomness. Derive the nonce from the private key and the message, and there is no runtime generator left to repeat or bias -- both failure modes vanish together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Determinism looks like a clean, total victory. It is narrower than that, and the boundary matters. What it closes is the &lt;em&gt;generation&lt;/em&gt; side of Part A. It does nothing for the &lt;em&gt;usage&lt;/em&gt; side: recall that Minerva, TPM-Fail, and LadderLeak never touched a generator -- they read the nonce&apos;s leading bits out of the &lt;em&gt;timing&lt;/em&gt; of the scalar multiplication $k \cdot G$, and that leak is present no matter how $k$ was chosen [@minerva] [@tpmfail] [@ladderleak]. Determinism fixes &lt;em&gt;generation&lt;/em&gt;, not &lt;em&gt;usage&lt;/em&gt;; the defense against that class is a &lt;em&gt;constant-time&lt;/em&gt; scalar multiplication, a separate discipline that RFC 6979 neither provides nor promises. Worse, determinism can even &lt;em&gt;add&lt;/em&gt; exposure: by making signing perfectly repeatable, we hand a fault-injection attacker a perfectly repeatable target, a hazard RFC 6979&apos;s own security considerations acknowledge [@rfc6979]. If Go&apos;s standard library knows this well enough to refuse pure determinism on purpose, we had better find out what it is afraid of.&lt;/p&gt;
&lt;h2&gt;8. The Catch, and the Hedge&lt;/h2&gt;
&lt;p&gt;Here is a fact that should stop you: Go&apos;s standard library signs ECDSA &lt;em&gt;non-deterministically on purpose&lt;/em&gt;, and its source code says so in a comment. After seven sections arguing that runtime randomness is the enemy, one of the most widely used standard-library signers refuses to remove it. That is not carelessness. It is a second threat model.&lt;/p&gt;
&lt;p&gt;Determinism closed the randomness door, but it opened a different one. A deterministic signer computes the &lt;em&gt;same&lt;/em&gt; signature every time for a given input, which means an attacker who can run it twice on the same message gets two identical computations to compare -- and comparison is exactly the lever a fault attack pulls.&lt;/p&gt;

An attack that induces a hardware fault -- a voltage glitch, a clock glitch, a laser pulse -- during a computation, then compares the faulty output against a correct one to solve for the secret. Against a *deterministic* signer the attacker can obtain a correct signature and a faulted signature for the identical input, which is precisely what makes the difference exploitable.
&lt;p&gt;This is not hypothetical. Aranha, Orlandi, Takahashi, and Zaverucha proved that de-randomized Fiat-Shamir schemes -- EdDSA among them -- are vulnerable to differential fault attacks [@hedged]. And the standard itself agrees: FIPS 186-5 warns that fault attacks are &quot;of particular concern for ... deterministic signature schemes, as well as embedded or IoT devices and smartcards&quot; [@fips1865pdf]. The very property that made determinism testable and repeatable -- same input, same output -- is what hands a fault attacker a fixed target to hammer.&lt;/p&gt;
&lt;p&gt;The resolution is not to retreat to randomized signing, which reintroduces every repeat and bias failure. It is to take a small step back toward randomness, on purpose and in a controlled way: &lt;strong&gt;hedged signatures.&lt;/strong&gt;&lt;/p&gt;

A signature whose nonce is derived from the private key, the message, *and* a fresh random value $r$: $k = H(sk, m, r)$. If the random source fails, the construction degrades to the deterministic -- still safe -- case. If a fault is injected, the fresh $r$ means the signer is no longer a fixed, replayable target.
&lt;p&gt;Read that definition twice, because it is the whole trick. Hedging is not &quot;randomized signing with extra steps.&quot; It is a design that is safe &lt;em&gt;whichever&lt;/em&gt; thing goes wrong. Lose your entropy source entirely and you fall back to RFC 6979&apos;s determinism, which we already proved kills repeat and bias. Keep your entropy and you also get a moving target that defeats the fault attack determinism exposed. The randomness is present but no longer load-bearing.&lt;/p&gt;

&quot;Signatures generated by this package are not deterministic, but entropy is mixed with the private key and the message, achieving the same level of security in case of randomness source failure.&quot; -- Go `crypto/ecdsa` [@goecdsa]
&lt;p&gt;That is why Go hedges by default, and it is not alone. BoringSSL&apos;s FIPS module passes &quot;a SHA512 hash of the private key and digest as additional data into the RBG ... a hardening measure against entropy failure&quot; [@boringssl]. RFC 6979 itself sanctions hedged variants in its Section 3.6 [@rfc6979], and FIPS 186-5 specifies an &quot;Extra Random Bits&quot; method that draws a nonce at least 64 bits longer than needed so any statistical bias becomes negligible [@fips1865pdf].&lt;/p&gt;
&lt;p&gt;The honest caveats: hedging still consumes &lt;em&gt;some&lt;/em&gt; entropy, and its security proofs cover &lt;em&gt;specific&lt;/em&gt; fault classes rather than all conceivable faults. It is a well-chosen trade, not a free lunch.&lt;/p&gt;
&lt;p&gt;The right mental model is three parallel choices selected by threat model, not a podium with one winner:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Randomized&lt;/th&gt;
&lt;th&gt;Deterministic (RFC 6979 / EdDSA)&lt;/th&gt;
&lt;th&gt;Hedged&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RNG needed at signing time&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Some (not load-bearing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immune to repeats&lt;/td&gt;
&lt;td&gt;No (RNG can collide)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immune to bias&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resists fault injection&lt;/td&gt;
&lt;td&gt;Yes (moving target)&lt;/td&gt;
&lt;td&gt;No (fixed target)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Known-answer test vectors&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standardized&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (RFC 6979, FIPS 186-5)&lt;/td&gt;
&lt;td&gt;Yes (RFC 6979 3.6; FIPS 186-5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shipped by default in&lt;/td&gt;
&lt;td&gt;Legacy code&lt;/td&gt;
&lt;td&gt;Many libraries&lt;/td&gt;
&lt;td&gt;Go &lt;code&gt;crypto/ecdsa&lt;/code&gt;, BoringSSL FIPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best when&lt;/td&gt;
&lt;td&gt;Rarely the right pick&lt;/td&gt;
&lt;td&gt;No fault threat in scope&lt;/td&gt;
&lt;td&gt;Hardware, embedded, smartcard, HSM&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; For any new (EC)DSA signer -- especially on hardware, embedded, smartcard, or HSM targets where fault injection is realistic -- hedge it: derive the nonce from the key, the message, and a little fresh entropy. You keep repeat and bias immunity even if the random source fails, and you deny the fixed target a fault attack needs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the signature side does not settle on a single champion. It settles on a &lt;em&gt;matched set&lt;/em&gt; of choices, each correct for a different adversary. That is a satisfying place to land -- but it is only half the patient. What is the symmetric-encryption analogue for the cipher we left bleeding in Section 6?&lt;/p&gt;
&lt;h2&gt;9. Fixing the Cipher: Nonce-Misuse-Resistant AEAD&lt;/h2&gt;
&lt;p&gt;The signature fix was &quot;derive the number.&quot; The cipher&apos;s fix rhymes: &lt;em&gt;derive the IV from the message itself&lt;/em&gt;, so that the worst a repeat can leak is the fact that two messages were byte-for-byte identical. Same instinct, symmetric setting.&lt;/p&gt;
&lt;p&gt;The idea predates AES-GCM-SIV. Phillip Rogaway and Thomas Shrimpton gave it a provable-security treatment in 2006, defining deterministic authenticated encryption and the Synthetic IV construction that realizes it [@rs_eprint] [@rs_doi]. It was standardized as AES-SIV in RFC 5297 [@rfc5297].&lt;/p&gt;

An initialization vector computed *from the message* -- together with the key and associated data -- rather than drawn at random. Because the IV is a function of the plaintext, two different plaintexts get different IVs automatically, and only byte-identical messages ever collide.

An authenticated cipher that, if a nonce is ever repeated, leaks at most whether two identical $(\text{key}, \text{nonce}, \text{plaintext}, \text{associated data})$ inputs were the same -- never the key, never arbitrary plaintext. Reuse degrades it gracefully rather than catastrophically.
&lt;p&gt;The modern instantiation is AES-GCM-SIV (RFC 8452) [@rfc8452]. It computes a keyed hash of the &lt;em&gt;entire&lt;/em&gt; plaintext and associated data using POLYVAL, and folds that into a synthetic IV that doubles as the authentication tag; the synthetic IV then seeds ordinary AES-CTR encryption.POLYVAL is a little-endian relative of GHASH, chosen deliberately so AES-GCM-SIV can reuse the same carry-less multiplication hardware that already accelerates AES-GCM. The parallel to GCM is by design, not coincidence.&lt;/p&gt;
&lt;p&gt;Two more refinements matter: fresh message-authentication and message-encryption keys are &lt;em&gt;derived per nonce&lt;/em&gt; from the master key, which also pushes back the birthday bound that limits plain GCM and raises the safe message limit to roughly $2^{50}$ per key [@gcmsiv_spec]. The price is structural and unavoidable: because the tag depends on the whole plaintext, you cannot emit the first ciphertext byte until you have read the last plaintext byte. AES-GCM-SIV is inherently &lt;em&gt;two-pass and not online&lt;/em&gt;.&lt;/p&gt;

flowchart TD
    A[&quot;Master key and nonce&quot;] --&amp;gt; B[&quot;Derive per-message auth and encryption keys&quot;]
    P[&quot;Plaintext and associated data&quot;] --&amp;gt; C[&quot;POLYVAL hash under the derived auth key&quot;]
    B --&amp;gt; C
    C --&amp;gt; D[&quot;Synthetic IV, which doubles as the authentication tag&quot;]
    D --&amp;gt; E[&quot;Seed AES-CTR encryption with the synthetic IV&quot;]
    B --&amp;gt; E
    E --&amp;gt; F[&quot;Output ciphertext and tag&quot;]
&lt;p&gt;Now state precisely what &quot;misuse-resistant&quot; earns you, because the phrase invites overclaiming. On a nonce repeat, AES-GCM-SIV discloses only whether the two messages &quot;were equal or not,&quot; which RFC 8452 identifies as &quot;the minimum amount of information that a deterministic algorithm can leak&quot; [@rfc8452]. Compare that to Section 6, where a single GCM repeat surrendered $P_1 \oplus P_2$ and set up universal forgery. The SIV construction converts a catastrophe into a whisper.&lt;/p&gt;

On nonce reuse, a misuse-resistant AEAD discloses only &quot;whether the messages were equal or not ... the minimum amount of information that a deterministic algorithm can leak.&quot; -- RFC 8452 [@rfc8452]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;Misuse-resistant&quot; does not mean reuse is harmless. On a repeat, AES-GCM-SIV still leaks that two identical messages were sent under the same nonce -- the provable minimum, but not nothing. You get &lt;em&gt;graceful&lt;/em&gt; degradation, not immunity. It is a safety net, not a license to reuse nonces deliberately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is a parallel path worth knowing, though it solves a slightly different problem. XChaCha20-Poly1305 extends the nonce to 192 bits, which makes &lt;em&gt;random&lt;/em&gt; nonce selection collision-safe and lets systems stay stateless without a counter [@xchacha]. That is genuinely useful, but it is not true misuse resistance: an &lt;em&gt;exact&lt;/em&gt; nonce repeat still breaks Poly1305 the way any keystream reuse does. A big nonce makes accidental collisions astronomically unlikely; it does not make a deliberate repeat safe. Collect the options:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scheme&lt;/th&gt;
&lt;th&gt;Passes / online&lt;/th&gt;
&lt;th&gt;On nonce reuse&lt;/th&gt;
&lt;th&gt;Misuse-resistant?&lt;/th&gt;
&lt;th&gt;Nonce size&lt;/th&gt;
&lt;th&gt;Notes&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;1 pass, online&lt;/td&gt;
&lt;td&gt;Catastrophic (Section 6)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;Fastest with a reliable unique counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;2 pass, not online&lt;/td&gt;
&lt;td&gt;Leaks only message equality&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;POLYVAL and per-nonce keys [@rfc8452]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-SIV&lt;/td&gt;
&lt;td&gt;2 pass, not online&lt;/td&gt;
&lt;td&gt;Leaks only message equality&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Vector input&lt;/td&gt;
&lt;td&gt;Deterministic key-wrap [@rfc5297]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;1 pass, online&lt;/td&gt;
&lt;td&gt;Random reuse unlikely; exact reuse breaks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;192-bit&lt;/td&gt;
&lt;td&gt;Stateless random nonces [@xchacha]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two families, one move. On the signature side we stopped trusting runtime randomness by &lt;em&gt;deriving&lt;/em&gt; the number; on the AEAD side we stop trusting it by making a repeat &lt;em&gt;non-catastrophic&lt;/em&gt;. It feels, at last, like the problem is solved. It is not -- and the reasons are not missing features anyone can build. Some of them are walls that no amount of engineering can move.&lt;/p&gt;
&lt;h2&gt;10. Theoretical Limits and the Open Frontier&lt;/h2&gt;
&lt;p&gt;Some of the walls we keep hitting are not missing features that a cleverer engineer will one day supply. They are proofs that you cannot have everything at once.&lt;/p&gt;

First: an online cipher cannot be misuse-resistant. To leak only equality on a repeat, the first bit of ciphertext must depend on the *entire* plaintext, which forces a second pass -- so &quot;streaming&quot; and &quot;misuse-resistant&quot; cannot both hold [@rfc8452]. Second: pure determinism cannot resist faults. The very repeatability that kills nonce reuse is the property a fault attack exploits, so resisting faults means reintroducing some randomness to hedge [@hedged]. Neither wall is a missing feature. Both are theorems. There is no single perfect primitive -- only a matched pair chosen per threat model.
&lt;p&gt;Those two impossibilities explain the shape of everything in Sections 7 through 9. AES-GCM-SIV is two-pass &lt;em&gt;because it has to be&lt;/em&gt;. Hedged signatures exist &lt;em&gt;because&lt;/em&gt; determinism alone cannot cover the fault case. We did not fail to find the one perfect design; the one perfect design is provably not there.&lt;/p&gt;
&lt;p&gt;A third limit is epistemic, and it is the quiet engine under this entire article.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can never prove a generator is unpredictable; you can only fail to prove it predictable. That asymmetry -- argued in &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand&quot; rel=&quot;noopener&quot;&gt;Part 2&lt;/a&gt; -- is why &quot;harden the random number generator&quot; was never a structural fix. LadderLeak made the point unforgettable by winning with less than one bit of bias [@ladderleak]. Deriving the number sidesteps the unprovable question entirely: there is nothing left to certify.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the biased-nonce frontier is still &lt;em&gt;descending&lt;/em&gt;: LadderLeak&apos;s sub-one-bit result is the current floor, not a proven bottom [@ladderleak]. The theoretical minimum bias that remains exploitable is an open question, and history says to bet on &quot;smaller than you think.&quot;&lt;/p&gt;
&lt;p&gt;Then there is the genuinely open ground, where the same disease is re-emerging one abstraction level up. In threshold and multi-party ECDSA, several parties must &lt;em&gt;jointly&lt;/em&gt; produce a nonce that is unbiased and secret from all of them, even when some participants misbehave. That is the Hidden Number Problem wearing a new coat: get the joint nonce generation subtly wrong and the biased-nonce attacks come straight back [@threshold]. On the AEAD side, none of the four ciphers we surveyed is &lt;em&gt;key-committing&lt;/em&gt;, and that gap is exploitable.&lt;/p&gt;

An authenticated cipher whose ciphertext binds to exactly one key, so it cannot be decrypted to a different valid plaintext under a different key. None of AES-GCM, AES-GCM-SIV, AES-SIV, or XChaCha20-Poly1305 is key-committing, which is precisely what enables partitioning-oracle attacks.
&lt;p&gt;Julia Len, Paul Grubbs, and Thomas Ristenpart showed that this non-committing property turns certain systems into partitioning oracles that recover passwords and keys [@len]. There is no drop-in standard for committing AEAD yet -- an active research front. It is worth being precise about what is &lt;em&gt;not&lt;/em&gt; open, though: AES-GCM-SIV and AES-SIV already sit exactly at the minimum-leakage lower bound for the misuse case, because &quot;leaks only equality&quot; is provably optimal for any deterministic scheme [@rfc8452]. That specific gap is closed; you cannot do better, and you do not need to.&lt;/p&gt;
&lt;p&gt;Step back and the emotional arc completes. We began thinking nonce reuse was sloppy hygiene, discovered it was a cliff, thought determinism sealed the cliff, learned determinism opened a fault door, and now find that some of the remaining doors are welded shut by theorems while others open onto genuinely unexplored rooms. None of that theory, however, changes what an engineer should actually do on Monday morning. After all of it, what do you type?&lt;/p&gt;
&lt;h2&gt;11. What a Practitioner Does Today&lt;/h2&gt;
&lt;p&gt;Everything above collapses into a decision tree that fits on an index card. You do not need to re-derive Joux&apos;s polynomial in a code review; you need two questions for signing and two for encryption.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;signing&lt;/strong&gt;: for any (EC)DSA or DSA signer, derive the nonce with RFC 6979 or a hedged variant rather than drawing it at runtime [@rfc6979]. For greenfield systems, prefer Ed25519, which is deterministic by construction [@rfc8032]. If fault injection is a realistic threat -- smart cards, HSMs, IoT silicon an attacker can hold -- hedge, so an entropy failure degrades to safe determinism and a glitch still faces a moving target [@hedged] [@boringssl] [@goecdsa]. And never compute a nonce as &lt;code&gt;rand() mod n&lt;/code&gt; from a raw PRNG without the anti-bias margin FIPS 186-5 specifies as Extra Random Bits [@fips1865pdf]. On hardware whose timing an attacker can measure -- smart cards, TPMs, co-located VMs -- pair all of this with a &lt;em&gt;constant-time&lt;/em&gt; scalar multiplication: RFC 6979 fixes how $k$ is generated, not how it is used, so the Minerva, TPM-Fail, and LadderLeak timing leaks close only when $k \cdot G$ itself runs in constant time [@minerva] [@tpmfail] [@ladderleak].&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;AEAD&lt;/strong&gt;: if you can guarantee a unique nonce -- a reliable, non-resetting counter -- AES-GCM is the fastest choice, provided you cap invocations per key against the birthday bound and rekey before you approach it [@sp80038d] [@rfc8452]. If nonce uniqueness is even slightly uncertain -- multiple writers sharing a key, VM snapshots that replay state, forked or stateless workers -- reach for AES-GCM-SIV [@rfc8452] or the 192-bit-nonce XChaCha20-Poly1305 [@xchacha]. For deterministic key-wrapping, AES-SIV is purpose-built [@rfc5297].&lt;/p&gt;

flowchart TD
    S[&quot;Signing (EC)DSA or DSA&quot;] --&amp;gt; Q1{&quot;Fault injection in scope, such as smartcard or HSM or IoT&quot;}
    Q1 --&amp;gt;|Yes| H[&quot;Hedge: nonce from key, message, and fresh entropy&quot;]
    Q1 --&amp;gt;|No| G{&quot;Greenfield design&quot;}
    G --&amp;gt;|Yes| ED[&quot;Prefer Ed25519&quot;]
    G --&amp;gt;|No| R6[&quot;Deterministic RFC 6979&quot;]
    A[&quot;Choosing an AEAD&quot;] --&amp;gt; Q2{&quot;Reliable unique nonce counter&quot;}
    Q2 --&amp;gt;|Yes| GCM[&quot;AES-GCM, cap invocations and rekey&quot;]
    Q2 --&amp;gt;|No| MR[&quot;AES-GCM-SIV or XChaCha20-Poly1305&quot;]
&lt;p&gt;The same choices read cleanly as a do-and-do-not table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Call this&lt;/th&gt;
&lt;th&gt;Never this&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Signing, general&lt;/td&gt;
&lt;td&gt;RFC 6979 deterministic or hedged&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rand() mod n&lt;/code&gt; from a raw PRNG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing, greenfield&lt;/td&gt;
&lt;td&gt;Ed25519&lt;/td&gt;
&lt;td&gt;Home-rolled DSA nonce logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing, hardware or embedded&lt;/td&gt;
&lt;td&gt;Hedged (key, message, entropy)&lt;/td&gt;
&lt;td&gt;Pure determinism on a fault-exposed device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing, measurable hardware&lt;/td&gt;
&lt;td&gt;Constant-time scalar multiplication&lt;/td&gt;
&lt;td&gt;Variable-time scalar multiplication an attacker can time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD, reliable counter&lt;/td&gt;
&lt;td&gt;AES-GCM, capped per key and rekeyed&lt;/td&gt;
&lt;td&gt;AES-GCM past its invocation limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD, uncertain uniqueness&lt;/td&gt;
&lt;td&gt;AES-GCM-SIV or XChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;Plain AES-GCM with hopeful nonces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic key-wrap&lt;/td&gt;
&lt;td&gt;AES-SIV (RFC 5297)&lt;/td&gt;
&lt;td&gt;A data-plane GCM key reused for wrapping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

These abstractions land on concrete Windows components. Signature and nonce generation run through the Cryptography API: Next Generation stack, whose internals the [CNG architecture article](/blog/cng-architecture-bcrypt-ncrypt-ksps) works through. TLS record nonces are the responsibility of [SChannel](/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm), the platform&apos;s TLS implementation. And every one of those values ultimately draws on the [Windows RNG](/blog/a-key-is-only-as-unguessable-as-the-dice-that-made-it-inside), whose seeding and clone-defense decide whether the &quot;unique and unpredictable, once, per key&quot; contract actually holds in practice. Read those three together and this article&apos;s rules become a specific call stack.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Signing: derive the nonce (RFC 6979), prefer Ed25519, hedge on hardware. AEAD: use AES-GCM only with a guaranteed-unique counter and an invocation cap; otherwise reach for AES-GCM-SIV or a 192-bit-nonce cipher. One instinct sits under both: never trust a runtime draw to be unique and unpredictable &quot;just once.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

Sign the same message twice, with the same key, and compare the two signatures. If they are byte-for-byte identical, the signer is deterministic (RFC 6979 or Ed25519). If they differ, it is randomized or hedged. Ed25519 in most libraries will match exactly; Go&apos;s `crypto/ecdsa` will not, by design. That one comparison tells you which row of the Section 8 table you are standing in -- and whether an entropy failure at runtime would be survivable.
&lt;p&gt;Two rules for two primitives, resting on a single instinct. That instinct is strong enough to design around -- but a handful of persistent myths still surface in code review and threat models, and each one hides a real misunderstanding of the mechanism. Let us dismantle them one at a time.&lt;/p&gt;
&lt;h2&gt;12. Frequently Asked Questions&lt;/h2&gt;


No, and this distinction matters more than almost anything else here. A *repeated* nonce leaks the key from *two* signatures with certainty, through the subtraction in Section 3. A *biased* nonce is statistical: each signature is one noisy equation, and recovery needs *many* of them aggregated by a lattice or Fourier reduction [@ns] [@ladderleak]. One biased signature on its own leaks nothing you can use. &quot;One biased signature reveals your key&quot; is the single most common misstatement in this area.


Not against every adversary. Determinism removes the repeat and generation-side bias failures completely (it does not stop a side-channel leak of $k$ during the scalar multiplication $k \cdot G$, which needs a *constant-time* implementation, not determinism), but it turns the signer into a fixed, replayable target for differential fault analysis -- proven for de-randomized Fiat-Shamir schemes including EdDSA [@hedged], and flagged by FIPS 186-5 as a special concern for deterministic schemes on embedded and smartcard devices [@fips1865pdf]. That is exactly why hedged signatures exist.


No. It degrades gracefully instead of catastrophically. On a repeat it discloses only whether two messages were identical -- the provable minimum for any deterministic scheme -- never the key and never arbitrary plaintext [@rfc8452]. &quot;Graceful&quot; is not &quot;free.&quot; Do not reuse nonces on purpose in the belief that the SIV mode makes it costless.


No, and precision matters. A single collision instantly leaks $P_1 \oplus P_2$ and destroys confidentiality [@joux]. Recovering the authentication subkey $H$ is a *separate* step: one collision yields a set of candidate roots, and pinning $H$ uniquely generally needs two or more collisions [@joux]. Live TLS servers doing this were found forgeable in 2016 [@nda], but the mechanism is &quot;confidentiality gone at once, forgery after enough collisions,&quot; not &quot;one reuse equals the key.&quot;


Because you cannot certify unpredictability, only fail to falsify it -- and sub-bit bias is still fatal across enough signatures. LadderLeak won with less than one bit of leakage [@ladderleak]. Hardening the generator raises the bar; it does not change the structure. Deriving the number does.


Only within the birthday bound, and only if uniqueness genuinely holds. Random 96-bit nonces start colliding near $2^{48}$ messages, which is why usage caps per key exist [@rfc8452]. Worse, multi-writer keys, VM clones, and process forks silently re-emit the same nonce no matter how carefully you generated it. If uniqueness is uncertain, switch to AES-GCM-SIV [@rfc8452] or XChaCha20-Poly1305&apos;s 192-bit nonce [@xchacha]; this reuse has been observed in the wild [@nda].


No. Lattice- and Fiat-Shamir-based post-quantum signatures inherit the same sensitivity to per-signature randomness; several are precisely the de-randomized Fiat-Shamir schemes shown vulnerable to fault attacks [@hedged]. Migrating to post-quantum cryptography re-instantiates nonce discipline -- it does not retire it.

&lt;h2&gt;13. One Cure for Two Catastrophes&lt;/h2&gt;
&lt;p&gt;Return to that stage in Berlin in 2010, but now you can see the whole shape of the thing. The PlayStation 3 master key did not fall to a broken cipher or a weak curve; it fell to &lt;em&gt;one number, used twice&lt;/em&gt;, and two signatures&apos; worth of subtraction [@f0f]. The same shape swept real Bitcoin wallets when Android&apos;s generator repeated nonces [@bitcoin].&lt;/p&gt;
&lt;p&gt;Then the required leak kept shrinking -- bit-length timing on smart cards [@minerva], side channels in TPMs [@tpmfail], less than a single bit in LadderLeak [@ladderleak], nine fixed bits and sixty signatures in PuTTY [@putty] -- and the very same disease surfaced in a cipher, where 184 live TLS servers reusing a GCM nonce were shown forgeable [@nda].&lt;/p&gt;
&lt;p&gt;Every one of those failures shares a single sentence. Both primitives bet everything on a number that had to be unique and unpredictable exactly once, and both collapsed the moment that bet was lost. And the cures share a single sentence too. Deterministic signing derives the number from the key and the message so there is nothing left to repeat or bias [@rfc6979], while a &lt;em&gt;constant-time&lt;/em&gt; scalar multiplication closes the side-channel leaks that determinism never touched; hedged signing keeps a sliver of entropy so a fault has no fixed target [@hedged]; misuse-resistant encryption derives the IV from the message so a repeat leaks only that two messages matched [@rfc8452].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Two primitives, one disease, one cure: stop trusting runtime randomness for the once-per-operation number. Derive it, or make a repeat non-catastrophic. Everything else in this article is a footnote to that sentence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Part 2 of this series taught the two ways randomness betrays you; this was the promised descent into what those betrayals actually cost and how the field designed them away. The punchline you were promised at the start has been earned line by line: in cryptography, reusing one number can cost you everything -- so build systems that never require you to trust yourself to use it only once.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;nonce-reuse-ecdsa-aes-gcm&quot; keyTerms={[
  { term: &quot;Signing nonce (k)&quot;, definition: &quot;The per-signature secret scalar in (EC)DSA; it must be unique, secret, and unpredictable, exactly once per key.&quot; },
  { term: &quot;GCM nonce (IV)&quot;, definition: &quot;The per-message value in AES-GCM; it must be unique under a given key, though it may be public.&quot; },
  { term: &quot;Repeat vs. bias&quot;, definition: &quot;A repeated nonce leaks the key from two signatures with certainty; a biased nonce is statistical and needs many signatures.&quot; },
  { term: &quot;Hidden Number Problem (HNP)&quot;, definition: &quot;Recovering a secret from many samples that each reveal a few bits of a related product mod n; the frame for biased-nonce attacks.&quot; },
  { term: &quot;GHASH subkey (H)&quot;, definition: &quot;The secret AES-of-zero value that keys GCM&apos;s polynomial authenticator; recovering it enables universal forgery.&quot; },
  { term: &quot;Forbidden attack&quot;, definition: &quot;Joux&apos;s GCM attack: reusing an IV cancels the tag blind, exposing a polynomial whose roots are candidates for H.&quot; },
  { term: &quot;Deterministic signature&quot;, definition: &quot;A signature whose nonce is a pseudorandom function of the key and message, using no runtime randomness (RFC 6979, EdDSA).&quot; },
  { term: &quot;Hedged signature&quot;, definition: &quot;A nonce derived from key, message, and fresh entropy; safe if the RNG fails and against fault injection.&quot; },
  { term: &quot;Nonce-misuse-resistant AEAD&quot;, definition: &quot;An AEAD that on nonce reuse leaks at most message equality, never the key or arbitrary plaintext (AES-GCM-SIV, AES-SIV).&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>ecdsa</category><category>aes-gcm</category><category>nonce-reuse</category><category>digital-signatures</category><category>aead</category><category>rfc-6979</category><category>applied-crypto</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>The Ciphertext Was Unbreakable. The Attacker Rewrote It Anyway: A Field Guide to Block Cipher Modes</title><link>https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw/</link><guid isPermaLink="true">https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw/</guid><description>Every block cipher mode -- ECB, CBC, CFB, OFB, CTR -- answers only confidentiality, never integrity. A field guide to why, the famous breaks, and the AEAD fix.</description><pubDate>Wed, 08 Jul 2026 21:51:08 GMT</pubDate><content:encoded>
A block cipher only enciphers one fixed-size block. A &quot;mode of operation&quot; is the wrapper that turns it into a scheme for real messages -- and every mode answers only one of two questions: can the attacker *read* it (confidentiality), or can the attacker *change* it (integrity)? The five SP 800-38A modes -- ECB, CBC, CFB, OFB, CTR -- are all malleable, so an attacker who cannot decrypt your ciphertext can still predictably edit the plaintext, and ECB additionally leaks structure outright (the penguin, Adobe&apos;s 153 million passwords). This field guide hands you one lens -- *which question did this mode answer, and was its IV/nonce contract honored?* -- and uses it to walk each mode&apos;s exact contract, a named failure catalog (Vaudenay, BEAST, Lucky Thirteen, POODLE, Sweet32, Efail, GCM nonce reuse), and the fix: authenticated encryption, whose dominant AES instances (GCM equals CTR plus GHASH, CCM equals CTR plus CBC-MAC) are literally these modes plus a MAC. It closes with the 2024-2026 state of the art (IR 8459, the ECB sunset, Ascon, nonce-misuse-resistant AES-GCM-SIV) and a decision guide for using these primitives correctly.
&lt;h2&gt;1. Two Lines of Code&lt;/h2&gt;
&lt;p&gt;A bank encrypts the message &quot;transfer $9&quot; with AES and a 256-bit key and sends the ciphertext across a network an attacker completely controls. He cannot read a single byte of it. AES is unbroken and will stay unbroken. Yet by flipping a few bits he cannot even decrypt, he turns the message into &quot;transfer $9,000,000,&quot; and the receiver accepts the change as authentic. The encryption was never broken. It simply answered the wrong question.&lt;/p&gt;
&lt;p&gt;How can a ciphertext be unreadable and freely editable at once? The trick is not a flaw in AES but a property of the &lt;em&gt;mode&lt;/em&gt; -- the wrapper that stretches a one-block cipher across a longer message. Many modes turn the cipher into a keystream generator and combine it with the plaintext by XOR, so $C = P \oplus \text{keystream}$. Rearrange the algebra: $P = C \oplus \text{keystream}$. An attacker who XORs a chosen delta into the ciphertext XORs exactly that delta into the recovered plaintext. He needs no key, reads nothing, and the receiver decrypts a perfectly well-formed forgery. That is one half of the story: the integrity question was never answered.&lt;/p&gt;
&lt;p&gt;The other half is uglier, because it fails the &lt;em&gt;first&lt;/em&gt; question too. In 2013, attackers exposed roughly 153 million Adobe account records. The passwords were not hashed. They were &lt;em&gt;encrypted&lt;/em&gt;, with 3DES in ECB mode and no salt [@troyhunt-adobe-2013]. ECB enciphers each block independently, so identical passwords produced identical ciphertext. Combined with the unencrypted password hints Adobe stored beside them, enormous numbers of passwords were reconstructed without anyone ever breaking 3DES [@troyhunt-adobe-2013]. The cipher was fine. The mode leaked the plaintext.&lt;/p&gt;
&lt;p&gt;Two stories, one shape. In each, the cipher did exactly what it promised and the attacker still won. That is because a mode of operation answers up to two independent questions, and the five most famous modes answer only one:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Can the attacker read it?&lt;/strong&gt; That is &lt;em&gt;confidentiality&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can the attacker change it?&lt;/strong&gt; That is &lt;em&gt;integrity&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The five modes standardized for decades -- ECB, CBC, CFB, OFB, CTR -- answer only the first question. ECB answers even that one wrongly. Every named break in this field, past or future, reduces to one diagnostic sentence: &lt;em&gt;which of the two questions did this mode answer, and was its IV/nonce contract honored?&lt;/em&gt; Hold that sentence in your head and the whole subject becomes legible.&lt;/p&gt;

flowchart TD
    Mode[&quot;A block cipher mode of operation&quot;]
    Mode --&amp;gt; C[&quot;Question 1: can the attacker READ it? (confidentiality)&quot;]
    Mode --&amp;gt; I[&quot;Question 2: can the attacker CHANGE it? (integrity)&quot;]
    C --&amp;gt; Cans[&quot;Answered only if the IV or nonce contract is honored&quot;]
    I --&amp;gt; Ians[&quot;Never answered by the five SP 800-38A modes&quot;]
    Cans --&amp;gt; BreakA[&quot;Break type A: violated IV/nonce contract (BEAST, GCM nonce reuse)&quot;]
    Ians --&amp;gt; BreakB[&quot;Break type B: missing integrity answer (Efail, bit-flipping, truncation)&quot;]
&lt;p&gt;This is Part 5 of a field guide for protocol designers. It leans on two earlier parts without re-deriving them: &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-&quot; rel=&quot;noopener&quot;&gt;Part 1&lt;/a&gt; gave us the security definitions this article keeps invoking -- IND-CPA, IND-CCA2, INT-CTXT -- and Part 2 gave us the discipline of generating initialization vectors and nonces. Here we spend those definitions on a single primitive: the block cipher mode.&lt;/p&gt;
&lt;p&gt;If AES was never broken in either story, what actually failed? To answer, you first have to know what a block cipher can and cannot do on its own, and why a mode was mandatory from the very first day it shipped.&lt;/p&gt;
&lt;h2&gt;2. A Cipher That Only Speaks in Blocks&lt;/h2&gt;
&lt;p&gt;A block cipher is smaller than you think. DES (1977) enciphers exactly 64 bits at a time. AES (2001) enciphers exactly 128. Feed it more and it simply refuses. It is a keyed permutation on one fixed-size block and physically cannot accept a message longer than that.&lt;/p&gt;

A keyed, invertible permutation on one fixed-size block of bits, written $E_k : \{0,1\}^n \to \{0,1\}^n$ for a key $k$ and block size $n$ (64 bits for DES, 128 for AES). It maps one $n$-bit input to one $n$-bit output and back. It has no notion of a message, a length, or a stream.
&lt;p&gt;Because real messages are almost never exactly one block long, every use of a block cipher needs a wrapper: a rule for chopping the message into blocks, deciding what each block&apos;s input should be, and stitching the outputs back together. That wrapper is the mode of operation.&lt;/p&gt;

The algorithm that lifts a one-block cipher to messages of arbitrary length. It specifies how plaintext is partitioned, what feeds into each block cipher call, and how the results combine into a ciphertext. The mode -- not the cipher -- decides whether identical plaintext leaks, whether the scheme is parallel, and what the initialization-vector or nonce rule is.
&lt;p&gt;The primitive came first. Horst Feistel&apos;s work at IBM in the early 1970s produced the Feistel network that became DES, a 64-bit keyed permutation blessed as a federal standard in 1977 [@feistel-1973][@fips-46-3]. The 64-bit block chosen here is the exact quantity that dies, 39 years later, to the Sweet32 attack.&lt;/p&gt;
&lt;p&gt;The first wrapper followed almost immediately. In 1976, William Ehrsam, Carl Meyer, John Smith, and Walter Tuchman at IBM filed the patent on Cipher Block Chaining -- the idea of feeding each plaintext block the previous ciphertext block before encryption, so that identical plaintext blocks stop producing identical ciphertext (US Patent 4,074,066, granted 1978) [@cbc-patent-4074066].The patent&apos;s actual title is &quot;Message verification and transmission error detection by block chaining&quot; [@cbc-patent-4074066]. That is historically ironic: CBC as a confidentiality mode provides no message verification -- no integrity -- at all, which is the very gap this article is about.&lt;/p&gt;
&lt;p&gt;Counter mode arrived on paper three years later. Whitfield Diffie and Martin Hellman described encrypting a counter to make a keystream in their 1979 survey &quot;Privacy and Authentication,&quot; turning a block cipher into a parallelizable stream cipher [@diffie-hellman-1979][@rogaway-modes-2011].Counter mode was described &quot;just as early as the basic-four modes,&quot; in Rogaway&apos;s words, &quot;yet for some reason it was not included in the initial batch&quot; [@rogaway-modes-2011]. It waited 22 years for a federal standard. The exact page in Proc. IEEE 67(3) is gated behind IEEE Xplore, so the DOI is the stable handle and Rogaway&apos;s 2011 survey carries the attribution [@diffie-hellman-1979][@rogaway-modes-2011].&lt;/p&gt;
&lt;p&gt;Then the standard fixed the toolkit. In December 1980 the National Bureau of Standards published FIPS 81, &lt;em&gt;DES Modes of Operation&lt;/em&gt;, sanctioning four ways to stretch DES across real traffic: ECB, CBC, CFB, and OFB [@fips-81]. Counter mode was left out. It did not enter a NIST standard until Morris Dworkin&apos;s SP 800-38A re-specified all five modes for the AES era in December 2001, finally adding CTR as the fifth -- 22 years after Diffie and Hellman [@nist-sp-800-38a].&lt;/p&gt;

flowchart LR
    A[&quot;1976: CBC invented at IBM&quot;] --&amp;gt; B[&quot;1979: Counter mode, Diffie-Hellman&quot;]
    B --&amp;gt; C[&quot;1980: FIPS 81 standardizes ECB, CBC, CFB, OFB&quot;]
    C --&amp;gt; D[&quot;1997: BDJR concrete-security proofs&quot;]
    D --&amp;gt; E[&quot;2000-2001: Encrypt-then-MAC proven, SP 800-38A adds CTR&quot;]
    E --&amp;gt; F[&quot;2004-2007: GCM and CCM, CTR plus a MAC&quot;]
    F --&amp;gt; G[&quot;2011-2018: BEAST, Lucky Thirteen, POODLE, Sweet32, Efail&quot;]
    G --&amp;gt; H[&quot;2024-2025: NIST IR 8459, Ascon&quot;]
&lt;p&gt;Here is the load-bearing historical fact. Every one of these five modes is a &lt;em&gt;confidentiality&lt;/em&gt; construction. Integrity -- the second question, &quot;can the attacker change it?&quot; -- was filed under a separate heading called a message authentication code, and in practice it was usually forgotten. FIPS 81 and SP 800-38A specify no integrity mechanism for any of the five [@fips-81][@nist-sp-800-38a]. That separation of concerns, confidentiality here and integrity somewhere else, is the original sin the rest of this story pays for.&lt;/p&gt;
&lt;p&gt;So the toolkit was frozen by 1980 (four modes) and completed by 2001 (a fifth), every one a way to hide data and none a way to protect it from change. The most obvious of the four is also the most broken, and seeing exactly why is the fastest route into the whole subject.&lt;/p&gt;
&lt;h2&gt;3. ECB, and Why &quot;Just Encrypt Each Block&quot; Fails&lt;/h2&gt;
&lt;p&gt;The most obvious wrapper is to apply the one-block permutation to each block on its own: $C_i = E_k(P_i)$. This is Electronic Codebook mode, and it is the design every beginner reinvents in an afternoon. It is also broken in a way you can see with your eyes.&lt;/p&gt;
&lt;p&gt;The flaw is that ECB is &lt;em&gt;deterministic&lt;/em&gt;. It has no initialization vector, no chaining, and no state, so the same plaintext block always encrypts to the same ciphertext block: $P_i = P_j \Rightarrow C_i = C_j$. The plaintext&apos;s block-level structure survives encryption intact. Rogaway&apos;s survey states it flatly: ECB &quot;leak[s] equality of blocks across both block positions and time&quot; and &quot;does not achieve any generally desirable security goal in its own right&quot; [@rogaway-modes-2011].&lt;/p&gt;

flowchart TD
    P1[&quot;Block A: LOVE&quot;] --&amp;gt; C1[&quot;Ciphertext 7a3f&quot;]
    P2[&quot;Block B: HATE&quot;] --&amp;gt; C2[&quot;Ciphertext 9b2e&quot;]
    P3[&quot;Block C: LOVE&quot;] --&amp;gt; C3[&quot;Ciphertext 7a3f&quot;]
    C1 -.-&amp;gt;|&quot;equal blocks leak&quot;| C3
&lt;p&gt;That leak is not a metaphor.The famous &quot;ECB penguin&quot; -- an image of Tux encrypted block-by-block whose outline stays perfectly visible -- is community folklore (the Tux image is by Larry Ewing, 1996) with no research primary. The load-bearing evidence for ECB&apos;s failure is the Adobe 2013 breach and the BDJR theorem, not the meme. In 2013, roughly 153 million Adobe records showed it at scale: passwords encrypted with 3DES in ECB, no salt, so identical passwords produced identical ciphertext and the plaintext structure reconstructed itself without 3DES ever being broken [@troyhunt-adobe-2013].&lt;/p&gt;
&lt;p&gt;Underneath the meme is a theorem. ECB is not IND-CPA.&lt;/p&gt;

The baseline security goal for encryption. An adversary who may request encryptions of any plaintexts it chooses still cannot tell which of two equal-length messages a challenge ciphertext hides. Bellare, Desai, Jokipii, and Rogaway (1997) proved the consequence: any IND-CPA scheme must be randomized or stateful, so a deterministic mode like ECB cannot qualify [@bdjr-1997][@katz-lindell-2020].
&lt;p&gt;The proof is a one-line adversary. Ask the challenger to encrypt a pair of equal blocks $(X, X)$ versus an unequal pair $(X, Y)$ and simply look for a repeated ciphertext block; ECB gives itself away every time [@bdjr-1997]. This is the same reasoning &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-&quot; rel=&quot;noopener&quot;&gt;Part 1&lt;/a&gt; develops in general. ECB is also malleable at block granularity: because blocks are independent, an attacker can cut, paste, and reorder them undetected. It fails both questions at once.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ECB is deterministic, so identical plaintext blocks become identical ciphertext blocks and structured or repeated data leaks through untouched -- the ECB penguin, and Adobe&apos;s roughly 153 million exposed passwords [@troyhunt-adobe-2013]. Reach for ECB only as a raw one-block building block, never to encrypt a message.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That &quot;never&quot; needs one careful qualifier -- it is easy to overstate.&lt;/p&gt;

The rule is *never ECB for messages* -- multi-block, structured, or low-entropy plaintext. The raw single-block permutation is still a legitimate building block: it is the block operation every other mode calls, and it sits at the core of NIST AES Key Wrap and of CMAC/CBC-MAC. NIST&apos;s SP 800-131A Revision 3, the initial public draft published on October 21, 2024, formalizes this by moving ECB to &quot;legacy use&quot; and disallowing it for encrypting secret data, with legacy (decrypt-only) use retained -- a demotion, not a claim that ECB has no valid use anywhere [@nist-sp-800-131a-r3].
&lt;p&gt;You can watch the determinism happen. The toy below is not a real cipher, but like ECB it transforms each block with the same deterministic function, so a repeated plaintext block produces a repeated ciphertext block:&lt;/p&gt;
&lt;p&gt;{`
// A toy deterministic block transform. Not a real cipher -- the point is
// only that the same input block always yields the same output block.
function toyBlockCipher(block) {
  return block.split(&apos;&apos;).reverse().map(function (c) {
    return String.fromCharCode((c.charCodeAt(0) + 7) % 128);
  }).join(&apos;&apos;);
}&lt;/p&gt;
&lt;p&gt;// ECB: split into 4-char blocks and transform each one independently.
function ecb(message) {
  var out = [];
  for (var i = 0; i &amp;lt; message.length; i += 4) {
    out.push(toyBlockCipher(message.slice(i, i + 4)));
  }
  return out;
}&lt;/p&gt;
&lt;p&gt;var blocks = ecb(&apos;LOVEHATELOVE&apos;); // block 1 and block 3 are identical
console.log(blocks);
console.log(&apos;Block 1 equals block 3? &apos; + (blocks[0] === blocks[2]));
// True. The repeated plaintext block leaks as a repeated ciphertext block,
// with no key required to notice the pattern.
`}&lt;/p&gt;
&lt;p&gt;One clarification before moving on.These five are the SP 800-38A &lt;em&gt;confidentiality-only&lt;/em&gt; modes, not &quot;all block cipher modes.&quot; Disk encryption (XTS-AES, SP 800-38E), authentication (CMAC, SP 800-38B), and authenticated encryption (GCM in SP 800-38D, CCM in SP 800-38C) live in separate NIST publications because they pursue different goals. ECB is the degenerate member of the family, and its failure points straight at the fix.&lt;/p&gt;
&lt;p&gt;BDJR&apos;s theorem is also the cure. To be IND-CPA, encryption must be randomized or stateful. So add an initialization vector, and make each block&apos;s encryption depend on the previous one, so that identical plaintext diverges. That is Cipher Block Chaining -- and the start of a forty-year evolution that improves everything except the one thing that matters.&lt;/p&gt;
&lt;h2&gt;4. From CBC to CTR, Generation by Generation&lt;/h2&gt;
&lt;p&gt;Four modes remain, and they line up as a sequence of fixes -- each one repairing the previous mode&apos;s &lt;em&gt;limitation&lt;/em&gt;, and none of them repairing the missing integrity answer. The field got faster, more flexible, and better specified. It stayed exactly as forgeable as the day it started.&lt;/p&gt;
&lt;h3&gt;CBC: chain the blocks&lt;/h3&gt;
&lt;p&gt;CBC seeds the chain with an initialization vector and feeds each plaintext block the previous ciphertext block before encryption: $C_i = E_k(P_i \oplus C_{i-1})$, with $C_0 = \text{IV}$. Decryption runs $P_i = D_k(C_i) \oplus C_{i-1}$. Because every ciphertext block now depends on all plaintext before it, identical plaintext blocks diverge, and ECB&apos;s pattern leak is gone. With an unpredictable IV, CBC is provably IND-CPA [@bdjr-1997].&lt;/p&gt;

The per-message starting value a mode mixes in so that encrypting the same plaintext twice yields different ciphertext. For CBC and CFB the IV must be *unpredictable* -- indistinguishable from random to an attacker -- not merely fresh, because it is the first value fed into the chain [@nist-sp-800-38a]. Part 2 of this series covers how to generate one.
&lt;p&gt;CBC answered the first question correctly and left the second blank, and even its confidentiality carries three sharp contract edges. This one mode seeded most of the Failure Catalog. It is malleable: a controlled flip in ciphertext block $C_i$ deterministically flips the corresponding bits of plaintext block $P_{i+1}$ while randomizing $P_i$ -- the &quot;flip-and-garble&quot; gadget, an attacker editing a later block without the key [@rogaway-modes-2011].&lt;/p&gt;
&lt;p&gt;The three contract edges follow. It needs padding, and a receiver that reveals whether decrypted padding is valid turns that check into a plaintext-recovering oracle. Its IV must be unpredictable, or a chosen-plaintext attacker can distinguish messages. And with a 64-bit block, its ciphertext blocks start colliding after about $2^{32}$ blocks, leaking $P_i \oplus P_j$. Rogaway&apos;s performance verdict is blunt: CBC encryption is &quot;inherently serial,&quot; and he can &quot;identify no important advantages over CTR mode&quot; [@rogaway-modes-2011].Ciphertext stealing (CBC-CS, standardized in an addendum to SP 800-38A) is the no-expansion variant that avoids padding by borrowing bits from the penultimate block. It is a useful trick, not a new security property -- CBC-CS is still malleable and still needs an unpredictable IV.&lt;/p&gt;
&lt;h3&gt;CFB: a self-synchronizing stream&lt;/h3&gt;
&lt;p&gt;CFB turns the block cipher into a stream by encrypting the &lt;em&gt;previous ciphertext&lt;/em&gt; to produce a keystream, then XORing it with the plaintext: $C_i = P_i \oplus E_k(C_{i-1})$. The cipher never touches the plaintext directly, so there is no padding, the mode can operate on sub-block segments (one byte or even one bit at a time), and it self-synchronizes after lost or inserted segments -- a genuinely useful property for noisy character-oriented links [@rogaway-modes-2011].&lt;/p&gt;
&lt;p&gt;The costs are real. Encryption is still serial, and for a segment size $s \lt n$ the mode makes many more block cipher calls per byte -- Rogaway notes $s = 8$ means &quot;16 times the number of blockcipher calls as CBC,&quot; and $s = 1$ means 128 times [@rogaway-modes-2011].Folklore (and even the loose version of this article&apos;s own scope) sometimes says CFB needs only a &lt;em&gt;unique&lt;/em&gt; IV. The primary source is stricter: SP 800-38A Section 5.3 requires the IV for both CBC and CFB to be &lt;em&gt;unpredictable&lt;/em&gt; [@nist-sp-800-38a]. Treat CFB&apos;s IV exactly like CBC&apos;s.&lt;/p&gt;
&lt;p&gt;CFB&apos;s malleability is the middle case, not a pure bit-flip. Because $P_i = C_i \oplus E_k(C_{i-1})$, flipping a ciphertext bit flips exactly the aligned plaintext bit in the &lt;em&gt;current&lt;/em&gt; block -- but that same altered block is the cipher&apos;s input for the next one, so the &lt;em&gt;following&lt;/em&gt; block decrypts to garbage. Efail (2018) weaponized precisely this: Damian Poddebniak and co-authors built malleability &quot;gadgets&quot; in CFB (OpenPGP&apos;s mode), engineered to absorb the garbled block while keeping the surgical edit, that turned an encrypted email into a plaintext-exfiltration channel with no key recovery and no padding oracle [@efail-2018][@efail-site].&lt;/p&gt;
&lt;h3&gt;OFB: a pre-computable stream&lt;/h3&gt;
&lt;p&gt;OFB makes the keystream independent of the message by feeding back the cipher&apos;s &lt;em&gt;own output&lt;/em&gt; instead of the ciphertext: $O_i = E_k(O_{i-1})$, then $C_i = P_i \oplus O_i$. Because the pad depends only on the key and IV, you can compute it before the plaintext arrives, and a single flipped ciphertext bit corrupts only the corresponding plaintext bit, with no error propagation -- attractive for satellite links and digitized voice [@rogaway-modes-2011].&lt;/p&gt;

A number used once: a value that must never repeat under a given key. OFB and CTR need their nonce only to be *unique*, not unpredictable, so a simple counter is a perfectly good nonce [@nist-sp-800-38a]. Reusing one under the same key is catastrophic for every keystream mode, because it reproduces the exact same pad. Part 2 covers how to source these values safely.
&lt;p&gt;That last point is OFB&apos;s undoing. If a $(\text{key}, \text{IV})$ pair ever repeats, the pad repeats, and $C \oplus C&apos; = P \oplus P&apos;$ leaks the XOR of two plaintexts with no key -- a two-time pad [@rogaway-modes-2011]. OFB is also serial in &lt;em&gt;both&lt;/em&gt; directions, so it cannot use hardware parallelism at all, a strictly worse profile than CTR for the same &quot;stream&quot; benefit. And the original FIPS 81 reduced-feedback variants shortened the keystream cycle dangerously, a hazard SP 800-38A removed by defining OFB with full-block feedback only [@fips-81][@nist-sp-800-38a].&lt;/p&gt;
&lt;h3&gt;CTR: the parallel stream that wins&lt;/h3&gt;
&lt;p&gt;CTR keeps OFB&apos;s &quot;encrypt something to make a pad&quot; idea but replaces the serial feedback chain with an &lt;em&gt;independent&lt;/em&gt; per-block counter: $C_i = P_i \oplus E_k(\text{nonce} ,|, i)$. Because each pad block is a standalone function of its counter, the blocks have no data dependency on one another. CTR is fully parallel on both encryption and decryption, random-access (decrypt block $i$ alone), precomputable, inverse-free, and needs no padding. Rogaway estimates it &quot;encrypting at more than 10 times the speed of CBC&quot; in hardware [@rogaway-modes-2011]. Its contract is the simplest of all: the counter must be &lt;em&gt;unique&lt;/em&gt; per key, and unpredictability is explicitly not required [@bdjr-1997][@rogaway-modes-2011].&lt;/p&gt;

Counter mode is &quot;the best and most modern way to achieve privacy-only encryption&quot; and &quot;an important building block for authenticated-encryption schemes.&quot; -- Phillip Rogaway, 2011
&lt;p&gt;And here is the cliff. CTR is the best answer to the &lt;em&gt;first&lt;/em&gt; question, which is exactly what makes its failure the point of the whole story. It is still malleable, and more transparently so than any other mode: $P = C \oplus \text{keystream}$, so flipping any ciphertext bit flips exactly the corresponding plaintext bit, with no key. The &quot;transfer $9&quot; to &quot;transfer $9,000,000&quot; edit from the opening is a single XOR, and the receiver decrypts a perfectly well-formed message. CTR removed every other excuse -- it is fast, parallel, and clean -- so the blank where integrity should be is the only thing left to see.&lt;/p&gt;

flowchart TD
    subgraph CBC[&quot;CBC: serial chaining&quot;]
        cIV[&quot;IV&quot;] --&amp;gt; cx1[&quot;XOR&quot;]
        cP1[&quot;P1&quot;] --&amp;gt; cx1
        cx1 --&amp;gt; cE1[&quot;E_k&quot;] --&amp;gt; cC1[&quot;C1&quot;]
        cC1 --&amp;gt; cx2[&quot;XOR&quot;]
        cP2[&quot;P2&quot;] --&amp;gt; cx2
        cx2 --&amp;gt; cE2[&quot;E_k&quot;] --&amp;gt; cC2[&quot;C2&quot;]
    end
    subgraph CTR[&quot;CTR: independent counters&quot;]
        tK1[&quot;E_k(nonce, 1)&quot;] --&amp;gt; tx1[&quot;XOR&quot;]
        tP1[&quot;P1&quot;] --&amp;gt; tx1
        tx1 --&amp;gt; tC1[&quot;C1&quot;]
        tK2[&quot;E_k(nonce, 2)&quot;] --&amp;gt; tx2[&quot;XOR&quot;]
        tP2[&quot;P2&quot;] --&amp;gt; tx2
        tx2 --&amp;gt; tC2[&quot;C2&quot;]
    end
&lt;p&gt;Lined up as a reference grid, the five modes and their exact contracts look like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Relation&lt;/th&gt;
&lt;th&gt;IV / nonce contract&lt;/th&gt;
&lt;th&gt;Parallelism&lt;/th&gt;
&lt;th&gt;Malleability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ECB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C_i = E_k(P_i)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none (the flaw)&lt;/td&gt;
&lt;td&gt;encrypt and decrypt parallel&lt;/td&gt;
&lt;td&gt;block-level: cut, paste, reorder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C_i = E_k(P_i XOR C_{i-1})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unpredictable IV&lt;/td&gt;
&lt;td&gt;encrypt serial, decrypt parallel&lt;/td&gt;
&lt;td&gt;flip-and-garble (block-coupled)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CFB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C_i = P_i XOR E_k(C_{i-1})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unpredictable IV&lt;/td&gt;
&lt;td&gt;encrypt serial, decrypt parallel&lt;/td&gt;
&lt;td&gt;surgical bit, next block garbled (middle)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OFB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C_i = P_i XOR E_k(O_{i-1})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unique nonce&lt;/td&gt;
&lt;td&gt;serial both ways&lt;/td&gt;
&lt;td&gt;surgical bitwise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CTR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C_i = P_i XOR E_k(counter_i)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unique nonce (not unpredictable)&lt;/td&gt;
&lt;td&gt;encrypt and decrypt parallel&lt;/td&gt;
&lt;td&gt;surgical bitwise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The IV and nonce contracts are SP 800-38A&apos;s; the parallelism and malleability columns follow Rogaway&apos;s summary tables [@nist-sp-800-38a][@rogaway-modes-2011].&lt;/p&gt;
&lt;p&gt;That last column deserves its own magnification, because &quot;malleable&quot; is not one behavior. It runs from surgical (edit one plaintext bit and touch nothing else), through a middle case (edit one bit, but wreck the neighboring block), to block-coupled (your controlled edit lands a block &lt;em&gt;later&lt;/em&gt;):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Malleability class&lt;/th&gt;
&lt;th&gt;What one flipped ciphertext bit does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;CTR, OFB&lt;/td&gt;
&lt;td&gt;surgical&lt;/td&gt;
&lt;td&gt;flips exactly the aligned plaintext bit, with zero collateral&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CFB&lt;/td&gt;
&lt;td&gt;middle case&lt;/td&gt;
&lt;td&gt;flips the aligned bit in the current block, and fully garbles the next block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;block-coupled&lt;/td&gt;
&lt;td&gt;garbles the current block, and flips the aligned bit in the next block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECB&lt;/td&gt;
&lt;td&gt;block-level&lt;/td&gt;
&lt;td&gt;no sub-block control, but whole blocks can be cut, pasted, and reordered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The distinction is operational, not academic: CTR&apos;s surgical malleability is what makes the opening bit-flip a one-line edit, while CFB&apos;s &quot;flip here, garble there&quot; is the exact structure Efail&apos;s exfiltration gadgets were built around [@rogaway-modes-2011][@efail-2018]. Read as an evolution, the same five tell a story of steady improvement on every axis but one:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Key idea&lt;/th&gt;
&lt;th&gt;Limitation that drove the next step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ECB&lt;/td&gt;
&lt;td&gt;1980&lt;/td&gt;
&lt;td&gt;encrypt each block independently&lt;/td&gt;
&lt;td&gt;deterministic; leaks block equality (not IND-CPA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;1976 / 1980&lt;/td&gt;
&lt;td&gt;chain each block with the previous ciphertext&lt;/td&gt;
&lt;td&gt;serial; needs padding; unpredictable IV; 64-bit birthday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CFB&lt;/td&gt;
&lt;td&gt;1980&lt;/td&gt;
&lt;td&gt;encrypt the previous ciphertext into a keystream&lt;/td&gt;
&lt;td&gt;serial; sub-block sizes multiply cipher calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OFB&lt;/td&gt;
&lt;td&gt;1980&lt;/td&gt;
&lt;td&gt;encrypt the feedback into a message-independent pad&lt;/td&gt;
&lt;td&gt;serial both ways; reuse is a two-time pad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CTR&lt;/td&gt;
&lt;td&gt;1979 / 2001&lt;/td&gt;
&lt;td&gt;encrypt an independent counter&lt;/td&gt;
&lt;td&gt;the best confidentiality mode, yet still no integrity&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; Not one of the four evolutions from ECB added integrity. CBC, CFB, OFB, and CTR each improved chaining, padding, error behavior, or parallelism. Every one of them left the second question -- can the attacker change it? -- completely blank.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One honest caveat: CFB, OFB, and CTR are not a strict quality ladder. They are three parallel answers to CBC&apos;s &quot;turn the block cipher into a stream,&quot; standardized together, with CTR the clear winner [@rogaway-modes-2011]. The linear arrow is a teaching device.&lt;/p&gt;
&lt;p&gt;Line the four up and the pattern is undeniable. The bug was never &quot;which chaining?&quot; It was the blank where the second answer should be. Naming that blank correctly is the pivot the whole field turned on.&lt;/p&gt;
&lt;h2&gt;5. Malleability Is the Real Bug&lt;/h2&gt;
&lt;p&gt;Here is the reframe that organizes the entire subject: confidentiality is not security. A mode that perfectly hides your message while letting an attacker predictably rewrite it has not handed you a weaker guarantee. It has handed you a &lt;em&gt;different&lt;/em&gt; one, and mistaking the two is the single most repeated error in applied cryptography.&lt;/p&gt;

The property that an attacker can transform a ciphertext into a predictable transformation of the underlying plaintext, without knowing the key or the plaintext. A malleable scheme may be perfectly confidential and still let an attacker edit the message. All five SP 800-38A modes are malleable.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every one of the five modes answers only confidentiality. The missing integrity answer &lt;em&gt;is&lt;/em&gt; malleability, and malleability is the real bug. The fix is authenticated encryption -- not a faster or cleverer confidentiality mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a provable statement, not a mood. A confidentiality-only mode is not IND-CCA2 and not non-malleable; IND-CPA says nothing about an active attacker who edits ciphertext in transit [@bdjr-1997].&lt;/p&gt;
&lt;p&gt;The mechanism differs per mode, and that difference is your diagnostic. In the pure keystream modes (CTR and OFB) the relation is $P = C \oplus \text{keystream}$, so flipping ciphertext bit $i$ flips plaintext bit $i$ exactly, with no collateral. CFB is the middle case: the flip lands surgically in the current block but garbles the &lt;em&gt;next&lt;/em&gt; one, because CFB feeds each ciphertext block back through the cipher. In CBC the coupling runs the other way -- a flip in $C_i$ predictably edits $P_{i+1}$ while randomizing $P_i$. &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-&quot; rel=&quot;noopener&quot;&gt;Part 1&lt;/a&gt; develops why this is a failure of the &lt;em&gt;goal&lt;/em&gt;, not the construction.&lt;/p&gt;

The stronger targets a confidentiality-only mode provably fails. IND-CCA2 (indistinguishability under adaptive chosen-ciphertext attack) hands the adversary a decryption oracle for any ciphertext but the challenge; non-malleability forbids turning one ciphertext into a predictably related one. Because all five SP 800-38A modes are malleable, none is IND-CCA2. Part 1 of this series formalizes both goals -- here they are exactly what a MAC restores.
&lt;p&gt;You can run the attack from the opening. The demo below encrypts a payment instruction with a toy keystream, then, knowing only the message format, rewrites the amount by XORing a delta into the ciphertext. No key is ever touched:&lt;/p&gt;
&lt;p&gt;{`
// Toy CTR: P = C XOR keystream. Editing C edits P, with no key. NOT real crypto.
function xorBytes(a, b) { return a.map(function (x, i) { return x ^ b[i]; }); }
function toBytes(s) { return Array.from(s).map(function (c) { return c.charCodeAt(0); }); }
function toStr(b) { return b.map(function (x) { return String.fromCharCode(x); }).join(&apos;&apos;); }&lt;/p&gt;
&lt;p&gt;var plaintext = &apos;amount:0000009&apos;;                 // the honest instruction
var keystream = toBytes(&apos;SECRETPADSECRET&apos;).slice(0, plaintext.length);&lt;/p&gt;
&lt;p&gt;// Sender encrypts. The attacker sees only C -- never the key, never the keystream.
var C = xorBytes(toBytes(plaintext), keystream);&lt;/p&gt;
&lt;p&gt;// The attacker knows the fixed message FORMAT and wants this instead:
var target = &apos;amount:9000000&apos;;                    // same length, this is the &quot;9 -&amp;gt; 9,000,000&quot; edit
var delta = xorBytes(toBytes(plaintext), toBytes(target));  // computable from the format alone&lt;/p&gt;
&lt;p&gt;// He XORs the delta straight into the ciphertext.
var forged = xorBytes(C, delta);&lt;/p&gt;
&lt;p&gt;// The receiver decrypts with the real keystream and sees the forgery.
console.log(&apos;Receiver reads: &apos; + toStr(xorBytes(forged, keystream)));  // amount:9000000
console.log(&apos;Keys or keystream used by the attacker: none.&apos;);
`}&lt;/p&gt;
&lt;p&gt;The same edit, drawn as it happens on the wire:&lt;/p&gt;

sequenceDiagram
    participant S as Sender
    participant A as Attacker
    participant R as Receiver
    S-&amp;gt;&amp;gt;A: Ciphertext of a nine-dollar transfer
    Note over A: Cannot read it, AES is unbroken
    Note over A: But P is C XOR keystream, so flipping ciphertext bit i flips plaintext bit i
    A-&amp;gt;&amp;gt;A: XOR a chosen delta into the amount bytes
    A-&amp;gt;&amp;gt;R: Modified ciphertext
    Note over R: Decrypts to a nine-million transfer, perfectly well-formed
    R-&amp;gt;&amp;gt;R: Accepts the forgery as authentic

Confidentiality without integrity is not weaker encryption. It is a different, largely illusory guarantee.
&lt;p&gt;Now turn the lens on the historical record. Every famous break of these modes is one of two diagnoses -- a missing integrity answer, or a violated IV/nonce contract:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Break&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Adobe [@troyhunt-adobe-2013]&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;ECB&lt;/td&gt;
&lt;td&gt;confidentiality answered wrongly (determinism)&lt;/td&gt;
&lt;td&gt;never ECB for messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Padding oracle, Vaudenay [@vaudenay-2002]&lt;/td&gt;
&lt;td&gt;2002&lt;/td&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;no integrity plus a distinguishable padding check&lt;/td&gt;
&lt;td&gt;verify before decrypting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BEAST [@bard-2004][@cve-2011-3389]&lt;/td&gt;
&lt;td&gt;2011&lt;/td&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;violated IV contract (predictable IV)&lt;/td&gt;
&lt;td&gt;IVs must be unpredictable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lucky Thirteen [@lucky-thirteen-2013]&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;wrong composition order plus a timing leak&lt;/td&gt;
&lt;td&gt;Encrypt-then-MAC, constant time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POODLE [@poodle-2014][@cve-2014-3566]&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;CBC (SSL 3.0)&lt;/td&gt;
&lt;td&gt;padding oracle after a downgrade&lt;/td&gt;
&lt;td&gt;retire the mode and the fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweet32 [@sweet32-2016][@cve-2016-2183]&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;CBC (64-bit)&lt;/td&gt;
&lt;td&gt;block-size birthday bound&lt;/td&gt;
&lt;td&gt;no 64-bit ciphers for bulk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efail [@efail-2018]&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;CBC / CFB&lt;/td&gt;
&lt;td&gt;missing integrity (malleability gadget)&lt;/td&gt;
&lt;td&gt;authenticate the ciphertext&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Truncation, Smyth-Pironti [@smyth-pironti-2013]&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;TLS record (any mode)&lt;/td&gt;
&lt;td&gt;missing integrity of length and framing (omission)&lt;/td&gt;
&lt;td&gt;authenticate the end, not just the bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCM nonce reuse [@joux-2006][@nonce-disrespecting-2016]&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;CTR / GCM&lt;/td&gt;
&lt;td&gt;violated nonce contract&lt;/td&gt;
&lt;td&gt;never repeat a (key, nonce)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Most of these breaks edit or leak bytes, but the subtlest one deletes them. Ben Smyth and Alfredo Pironti showed at WOOT 2013 that silently &lt;em&gt;truncating&lt;/em&gt; a TLS stream -- cutting it short at an attacker-chosen point -- changes the meaning a web application infers from a &quot;complete&quot; response, because the record layer authenticated the bytes it carried but never the fact that the message had &lt;em&gt;ended&lt;/em&gt; [@smyth-pironti-2013]. That is an integrity failure of omission, and it is why the fix in Section 10 authenticates length and end-of-stream, not just content. Most of the rest are TLS history, which the &lt;a href=&quot;https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm&quot; rel=&quot;noopener&quot;&gt;SChannel post&lt;/a&gt; tells from the deployment side.Precision on GCM nonce reuse: one repeated $(\text{key}, \text{nonce})$ &lt;em&gt;immediately&lt;/em&gt; leaks $P_1 \oplus P_2$, but uniquely recovering the GHASH subkey $H$ generally needs two or more collisions. Never say &quot;one reuse leaks $H$&quot; [@joux-2006].The routinely mis-cited CVE-2016-0270 actually names IBM Domino, not OpenSSL, and NVD warns it &quot;has been incorrectly used for GCM nonce reuse issues in other products.&quot; The deployed GCM-nonce forgery is the Nonce-Disrespecting Adversaries paper, whose byline is exactly five authors: Boeck, Zauner, Devlin, Somorovsky, and Jovanovic [@cve-2016-0270][@nonce-disrespecting-2016].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A bare confidentiality mode lets an attacker edit your plaintext undetected -- bit-for-bit in CTR and OFB, a surgical bit plus a garbled next block in CFB, and block-coupled in CBC. Use an AEAD, or Encrypt-then-MAC and verify the tag &lt;em&gt;before&lt;/em&gt; you decrypt a single byte.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The fix is to add the missing answer, and the order in which you add it turns out to matter. Bellare and Namprempre (2000) and Krawczyk (2001) proved that &lt;strong&gt;Encrypt-then-MAC&lt;/strong&gt; -- encrypt, then MAC the ciphertext, and verify the tag before decrypting -- generically yields both IND-CPA and INT-CTXT, a scheme that is confidential &lt;em&gt;and&lt;/em&gt; unforgeable, while MAC-then-encrypt and encrypt-and-MAC are fragile [@bellare-namprempre-2000][@krawczyk-2001].&lt;/p&gt;

A keyed tag that proves a message was not altered and came from someone holding the key. Anyone with the key can compute the tag over a message; without it, forging a valid tag is infeasible. HMAC, CMAC, and CBC-MAC are common constructions. A MAC supplies the integrity answer a confidentiality mode leaves blank.

SSL and early TLS shipped MAC-then-encrypt. The theory said Encrypt-then-MAC was the safe generic choice. That debate was not settled by proof alone -- it was settled by a decade of breaks (BEAST, Lucky Thirteen, POODLE) that all exploited the MAC-then-encrypt-plus-CBC-padding surface, until TLS 1.3 removed CBC outright and mandated authenticated encryption [@krawczyk-2001][@rfc-8446-tls13].
&lt;p&gt;Better still, fuse the two answers into one primitive.&lt;/p&gt;

A single primitive that provides confidentiality *and* integrity at once, and can also authenticate unencrypted &quot;associated data&quot; such as headers. It answers both questions in one object, so a modified ciphertext is rejected before any plaintext is released.
&lt;p&gt;The punchline binds this whole article together: the dominant &lt;em&gt;AES&lt;/em&gt; AEADs are literally these modes plus a MAC. &lt;strong&gt;GCM equals CTR plus GHASH&lt;/strong&gt;, a polynomial hash over $GF(2^{128})$ [@mcgrew-viega-2004][@nist-sp-800-38d]. &lt;strong&gt;CCM equals CTR plus CBC-MAC&lt;/strong&gt; [@rfc-3610-ccm][@nist-sp-800-38c]. The 1979 counter idea and the 1976 chaining idea, welded into a primitive that finally answers both questions -- the &lt;a href=&quot;https://paragmali.com/blog/the-connection-that-refused-to-downgrade-twenty-five-years-o&quot; rel=&quot;noopener&quot;&gt;SMB 3.1.1 post&lt;/a&gt; shows GCM and CCM running in a real protocol.&lt;/p&gt;

flowchart TD
    P[&quot;Plaintext&quot;] --&amp;gt; CTR[&quot;CTR encryption&quot;]
    CTR --&amp;gt; C[&quot;Ciphertext&quot;]
    C --&amp;gt; M[&quot;MAC over ciphertext and associated data&quot;]
    M --&amp;gt; T[&quot;Authentication tag&quot;]
    C --&amp;gt; W[&quot;Transmit ciphertext plus tag&quot;]
    T --&amp;gt; W
    W --&amp;gt; V{&quot;Tag valid?&quot;}
    V --&amp;gt;|&quot;no&quot;| X[&quot;Reject, decrypt nothing&quot;]
    V --&amp;gt;|&quot;yes&quot;| D[&quot;Decrypt and release plaintext&quot;]
&lt;p&gt;One boundary to keep honest: not every modern AEAD is built from these modes. ChaCha20-Poly1305 (a stream cipher plus Poly1305) and Ascon (a sponge permutation) are first-class AEADs that are deliberately &lt;em&gt;not&lt;/em&gt; SP 800-38A constructions [@rfc-8439-chacha20][@nist-sp-800-232]. The lineage claim is bounded to the AES AEADs.&lt;/p&gt;
&lt;p&gt;With integrity finally bolted on, every entry in the catalog retroactively dies -- a modified ciphertext fails the tag before a byte is decrypted. So the field declared victory and shipped AEAD everywhere. Then it discovered that AEAD did not repeal the IV/nonce contract. It &lt;em&gt;sharpened&lt;/em&gt; it -- which is exactly where the state of the art picks up.&lt;/p&gt;
&lt;h2&gt;6. The Field Is Retiring Confidentiality-Only Modes&lt;/h2&gt;
&lt;p&gt;The modern answer to &quot;which block cipher mode should I use?&quot; is &lt;em&gt;no bare confidentiality mode at all.&lt;/em&gt; It is a choice of AEAD, and between 2024 and 2026 that answer stopped being folklore and became written policy. The tell is what the standards bodies did &lt;em&gt;not&lt;/em&gt; do. Faced with four decades of breaks, nobody proposed a sixth confidentiality mode. Every document below reaches instead for authentication.&lt;/p&gt;
&lt;p&gt;Start with the audit. NIST IR 8459, published in September 2024 by Nicky Mouha and Morris Dworkin, is the first formal review of the entire SP 800-38 series, and it reads less like a specification than a post-mortem: it works through the family mode by mode, documenting why each keeps failing, and its recommendations point at authenticated encryption rather than a patched mode -- down to disallowing ECB for the very job it keeps losing at, encrypting secrets [@nist-ir-8459]. An audit whose answer to &quot;which new mode?&quot; is &quot;stop adding modes&quot; is this whole section in miniature.&lt;/p&gt;
&lt;p&gt;The policy machinery had already started turning. In April 2023 NIST&apos;s Crypto Publication Review Board published its decision to revise SP 800-38A itself, with goals that read like a confession: to &quot;limit the approval of the Electronic Codebook (ECB) mode,&quot; to &quot;provide guidance on the importance of incorporating authentication, where feasible,&quot; to fold in &quot;three variations of ciphertext stealing for Cipher Block Chaining mode,&quot; and -- once a stronger technique exists -- to &quot;consider deprecating the modes in SP 800-38A&quot; [@csrc-sp38a-revision-2023]. The document that defines the five modes is contemplating its own retirement, and names authenticated encryption as the successor it is waiting on.&lt;/p&gt;
&lt;p&gt;SP 800-131A Revision 3 supplies the compliance lever. Its initial public draft of October 21, 2024 disallows ECB for encrypting secret data and relegates it to legacy (decrypt-only) use; the draft&apos;s own phrase is &quot;the retirement of ECB as a confidentiality mode of operation&quot; [@nist-sp-800-131a-r3]. For a FIPS-bound product that is an effective-on-finalization change, not a suggestion.&lt;/p&gt;
&lt;p&gt;Where the field is heading is just as clear from what it &lt;em&gt;standardized&lt;/em&gt;. Ascon, finalized as NIST SP 800-232 in August 2025, is the new AEAD for the constrained, low-power class for which earlier standards specified AES-CCM -- and it is a sponge permutation, pointedly &lt;em&gt;not&lt;/em&gt; an SP 800-38A block cipher mode [@nist-sp-800-232]. &quot;Migrate to AEAD&quot; does not mean &quot;migrate to a mode.&quot; And for the one contract even AEADs still impose, AES-GCM-SIV (RFC 8452, 2019) is the standardized hedge: a nonce-misuse-resistant scheme that degrades gracefully instead of catastrophically when a nonce repeats [@rfc-8452-gcm-siv].&lt;/p&gt;

An AEAD that stays secure even when a nonce is accidentally reused, leaking at most whether two identical messages were sent under the same nonce, instead of collapsing into a two-time pad and forgery. AES-GCM-SIV reaches this by deriving its internal counter from a MAC of the whole message, so a repeat is a mild, bounded leak rather than a catastrophe [@rfc-8452-gcm-siv][@gcm-siv-spec-2017].

AES-GCM-SIV is engineered to be &quot;nonce misuse resistant -- that is, [it does] not fail catastrophically if a nonce is repeated.&quot; -- RFC 8452
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Document&lt;/th&gt;
&lt;th&gt;What it does&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;NIST IR 8459 [@nist-ir-8459]&lt;/td&gt;
&lt;td&gt;audits the whole SP 800-38 series; steers ECB and secrets toward AEAD&lt;/td&gt;
&lt;td&gt;published, Sept 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decision to revise SP 800-38A [@csrc-sp38a-revision-2023]&lt;/td&gt;
&lt;td&gt;limit ECB, add CBC ciphertext stealing, urge authentication, weigh deprecation&lt;/td&gt;
&lt;td&gt;published, Apr 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SP 800-131A Rev. 3 [@nist-sp-800-131a-r3]&lt;/td&gt;
&lt;td&gt;moves ECB to legacy (decrypt-only) use; disallowed for encrypting secrets on publication&lt;/td&gt;
&lt;td&gt;draft (Oct 21, 2024 IPD)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ascon (SP 800-232) [@nist-sp-800-232]&lt;/td&gt;
&lt;td&gt;a permutation AEAD for constrained devices, deliberately not a 38A mode&lt;/td&gt;
&lt;td&gt;final, Aug 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV (RFC 8452) [@rfc-8452-gcm-siv]&lt;/td&gt;
&lt;td&gt;a nonce-misuse-resistant AEAD hedge&lt;/td&gt;
&lt;td&gt;published, 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The migration is credible &lt;em&gt;now&lt;/em&gt;, rather than merely aspirational, because the substrate already moved. TLS 1.3 (RFC 8446, 2018) removed every CBC-mode cipher suite and makes AES-GCM its mandatory-to-implement AEAD [@rfc-8446-tls13]; QUIC protects every packet with an AEAD, binding the packet number directly into the AEAD nonce, with no unauthenticated-confidentiality option at all [@rfc-9001-quic-tls].&lt;/p&gt;
&lt;p&gt;The performance objection that once favored CBC also evaporated: with AES-NI and the carry-less multiply instruction PCLMULQDQ, AES-GCM fell from roughly 3.7 cycles per byte in optimized software toward about 1, so the fast path and the safe path are now the same path [@krovetz-rogaway-ae-2011][@gcm-siv-spec-2017]. The 2024-2026 standards are simply retiring the confidentiality-only modes to match a deployment reality that migrated years earlier.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The modern &quot;which mode?&quot; question is really &quot;which AEAD?&quot; The five SP 800-38A confidentiality-only modes now survive as &lt;em&gt;internals&lt;/em&gt; of authenticated encryption and as legacy interoperability options -- not as something you deploy on purpose.&lt;/p&gt;
&lt;/blockquote&gt;

Not every SP 800-38 sibling is a confidentiality mode. SP 800-38G defines FF1 and FF3, *format-preserving* encryption that turns a 16-digit card number into another 16-digit string so it still fits a legacy database column [@nist-sp-800-38g]. Different goal, and it came with its own break: Durak and Vaudenay gave a practical total recovery of FF3 over small domains, a slide attack exploiting the design&apos;s &quot;bad domain separation&quot; in roughly $O(N^{11/6})$ chosen plaintexts [@durak-vaudenay-2017]. NIST responded with FF3-1, shrinking the tweak from 64 to 56 bits and raising the minimum domain to one million [@nist-sp-800-38g-r1]. Signposted here, not surveyed -- it is not one of the five.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Reach for a vetted AEAD -- AES-GCM or ChaCha20-Poly1305 -- give it a unique nonce per key, cap your messages under the nonce limit, and drop to a raw confidentiality mode only as an Encrypt-then-MAC building block when an AEAD is genuinely unavailable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The cross-references write themselves: the CBC-to-GCM migration is the story the &lt;a href=&quot;https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm&quot; rel=&quot;noopener&quot;&gt;SChannel post&lt;/a&gt; tells for Windows TLS, and GCM and CCM running in a shipping protocol is what the &lt;a href=&quot;https://paragmali.com/blog/the-connection-that-refused-to-downgrade-twenty-five-years-o&quot; rel=&quot;noopener&quot;&gt;SMB 3.1.1 post&lt;/a&gt; shows. So the strategic direction is settled: move to AEAD. But &quot;AEAD&quot; is not one thing. The moment you have to &lt;em&gt;ship&lt;/em&gt;, the question sharpens from &quot;which mode?&quot; to &quot;which AEAD, with which parameters, for which platform?&quot; -- and that has a real, defensible answer.&lt;/p&gt;
&lt;h2&gt;7. When You Must Pick a Mode, and Which AEAD&lt;/h2&gt;
&lt;p&gt;Two comparisons matter in practice. The first is the intra-family one folklore gets wrong. The second is the one you actually face when you ship.&lt;/p&gt;
&lt;h3&gt;CBC versus CTR: a choice that changes nothing that matters&lt;/h3&gt;
&lt;p&gt;Both are confidentiality-only, both are malleable, and both are still everywhere. On the merits CTR is superior: fully parallel on encryption and decryption, random-access, and governed by the simplest contract (a unique nonce). CBC survives on forty years of deployment inertia, not on technical advantage -- Rogaway, having surveyed all five modes, reports &quot;no important advantages over CTR mode&quot; for CBC [@rogaway-modes-2011].&lt;/p&gt;
&lt;p&gt;But here is the point most tuning guides miss: swapping CBC for CTR fixes nothing that matters, because neither answers the second question. You trade a serial mode with padding for a parallel mode without it, and you keep every bit of the malleability. If your reason for the swap is security, you are solving the wrong problem.&lt;/p&gt;
&lt;h3&gt;Which AEAD&lt;/h3&gt;
&lt;p&gt;This is the comparison with real stakes, and every option is a defensible answer to a different constraint. AES-GCM is the throughput default on modern CPUs, hardware-accelerated by AES-NI and CLMUL, and brittle exactly where humans err -- a single reused nonce is catastrophic [@nist-sp-800-38d]. ChaCha20-Poly1305 is the software default: constant-time by construction, with no AES hardware and no cache-timing hazard [@rfc-8439-chacha20]. AES-CCM is CTR plus CBC-MAC, two-pass and small-footprint [@rfc-3610-ccm][@nist-sp-800-38c]; those documents define CCM, and its compact profile is why the IEEE wireless standards adopt it -- WPA2 (802.11i) [@krovetz-rogaway-ae-2011] and IEEE 802.15.4 low-power wireless [@rfc-4944][@rfc-9031] both build their link-layer security on it. AES-GCM-SIV is the nonce-misuse-resistant hedge [@rfc-8452-gcm-siv]. Ascon is the lightweight standard for microcontrollers [@nist-sp-800-232].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AEAD&lt;/th&gt;
&lt;th&gt;Built from&lt;/th&gt;
&lt;th&gt;Passes&lt;/th&gt;
&lt;th&gt;Constant-time&lt;/th&gt;
&lt;th&gt;Nonce-reuse penalty&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;AES-GCM [@nist-sp-800-38d]&lt;/td&gt;
&lt;td&gt;CTR + GHASH&lt;/td&gt;
&lt;td&gt;1, online&lt;/td&gt;
&lt;td&gt;needs AES-NI, CLMUL&lt;/td&gt;
&lt;td&gt;catastrophic (forgery)&lt;/td&gt;
&lt;td&gt;server bulk, TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305 [@rfc-8439-chacha20]&lt;/td&gt;
&lt;td&gt;stream + Poly1305&lt;/td&gt;
&lt;td&gt;1, online&lt;/td&gt;
&lt;td&gt;by construction (software)&lt;/td&gt;
&lt;td&gt;catastrophic&lt;/td&gt;
&lt;td&gt;mobile, no AES hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-CCM [@rfc-3610-ccm]&lt;/td&gt;
&lt;td&gt;CTR + CBC-MAC&lt;/td&gt;
&lt;td&gt;2, not online&lt;/td&gt;
&lt;td&gt;needs AES-NI&lt;/td&gt;
&lt;td&gt;catastrophic&lt;/td&gt;
&lt;td&gt;WPA2, IoT, small code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV [@rfc-8452-gcm-siv]&lt;/td&gt;
&lt;td&gt;POLYVAL-SIV + CTR&lt;/td&gt;
&lt;td&gt;2, not online&lt;/td&gt;
&lt;td&gt;needs AES-NI, CLMUL&lt;/td&gt;
&lt;td&gt;graceful (leak equality)&lt;/td&gt;
&lt;td&gt;nonce-uncertain systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ascon [@nist-sp-800-232]&lt;/td&gt;
&lt;td&gt;sponge permutation&lt;/td&gt;
&lt;td&gt;1 (duplex)&lt;/td&gt;
&lt;td&gt;easy to protect&lt;/td&gt;
&lt;td&gt;catastrophic&lt;/td&gt;
&lt;td&gt;microcontrollers, IoT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The performance folklore is worth correcting too. The cleanest apples-to-apples software measurement, Krovetz and Rogaway at FSE 2011 on an Intel i5 &quot;Clarkdale,&quot; found CCM near 4.2 cycles per byte, GCM near 3.7, OCB near 1.5, and raw CTR near 1.3 [@krovetz-rogaway-ae-2011]. Read that as an &lt;em&gt;ordering&lt;/em&gt; under fixed effort, not a modern absolute: with mature AES-NI and CLMUL, AES-GCM reaches roughly 1 cycle per byte on current x86 [@gcm-siv-spec-2017]. The durable lesson is that hardware, not algorithm choice, moved GCM from 3.7 to 1 -- which is the whole reason CTR-based AEADs won.&lt;/p&gt;

The two non-38A AEADs from Section 5, ChaCha20-Poly1305 and Ascon, sit within a broader research lineage -- OCB, EAX, SIV/AES-SIV, IACBC/IAPM, McOE -- that is real but out of scope here; OCB is the elegant one-pass design that patents kept out of deployment for years [@rfc-7253-ocb].
&lt;p&gt;Every column wins on some axis and loses on another; none is unconditionally best. And the reasons &lt;em&gt;why&lt;/em&gt; no single scheme dominates are not engineering accidents. They are theorems. To use any of these correctly, you have to know the walls they are all pressed against.&lt;/p&gt;
&lt;h2&gt;8. You Cannot Get Integrity From a Confidentiality Mode&lt;/h2&gt;
&lt;p&gt;The two-question frame is not merely good advice. It is a theorem, and it draws a hard line no amount of clever engineering crosses.&lt;/p&gt;
&lt;p&gt;Start with the central impossibility. IND-CPA does not imply IND-CCA2 or non-malleability; BDJR proved ECB is not even IND-CPA and that CBC and CTR &lt;em&gt;are&lt;/em&gt; IND-CPA with proper IVs and nonces, but IND-CPA says nothing about an active attacker editing ciphertext [@bdjr-1997]. Bellare and Namprempre sharpened the target, separating INT-PTXT from INT-CTXT and proving that only Encrypt-then-MAC generically delivers IND-CPA plus INT-CTXT [@bellare-namprempre-2000][@krawczyk-2001].&lt;/p&gt;
&lt;p&gt;The consequence is a lower bound, not a tip: you cannot patch integrity into a bare CTR, CBC, CFB, or OFB mode, because malleability is a property of the confidentiality &lt;em&gt;goal&lt;/em&gt;, not a bug in any &lt;em&gt;construction&lt;/em&gt;. This is the reasoning &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-&quot; rel=&quot;noopener&quot;&gt;Part 1&lt;/a&gt; sets up in full.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &quot;Confidentiality does not imply integrity&quot; is a theorem, not advice. IND-CPA does not imply IND-CCA2 or non-malleability, so you cannot patch integrity into a bare CTR, CBC, CFB, or OFB mode. It is a property of the goal, not a bug in the construction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The second wall is the block size.&lt;/p&gt;

The threshold at which collisions among random $n$-bit values become likely: around $2^{n/2}$ of them. For a block cipher it means ciphertext blocks start repeating -- and leaking $P_i \oplus P_j$ -- after roughly $2^{n/2}$ blocks, regardless of key length. This is the ceiling behind Sweet32 and the GCM nonce cap.
&lt;p&gt;Ciphertext-collision leakage grows like $q^2 / 2^n$ in the number of blocks $q$, so security degrades at about $q \approx 2^{n/2}$ blocks: roughly $2^{32}$ blocks (about 32 GiB) for a 64-bit cipher, which is the Sweet32 ceiling, and $2^{64}$ blocks for AES [@bdjr-1997][@sweet32-2016]. A provable ceiling, independent of key size -- doubling the key does not move it.&lt;/p&gt;
&lt;p&gt;GCM inherits its own two ceilings, and conflating them is a common error. Per SP 800-38D, a single message is capped at $2^{39} - 256$ bits (about 64 GiB), and, under random 96-bit nonces, the number of invocations per key must stay below about $2^{32}$ to keep the nonce-collision probability negligible [@nist-sp-800-38d]. One is a length limit; the other is a count limit. They are different numbers that both bite.&lt;/p&gt;
&lt;p&gt;The last wall is the sharpest, because no computational assumption stands behind it. Reusing a $(\text{key}, \text{nonce})$ in any keystream mode leaks $C_1 \oplus C_2 = P_1 \oplus P_2$ -- a two-time pad. You can watch it recover a plaintext with nothing but XOR:&lt;/p&gt;
&lt;p&gt;{`
// Reusing a (key, nonce) reproduces the SAME keystream. That is a two-time pad.
function xorBytes(a, b) { return a.map(function (x, i) { return x ^ b[i]; }); }
function toBytes(s) { return Array.from(s).map(function (c) { return c.charCodeAt(0); }); }
function toStr(b) { return b.map(function (x) { return String.fromCharCode(x); }).join(&apos;&apos;); }&lt;/p&gt;
&lt;p&gt;var p1 = &apos;attack at dawn&apos;;
var p2 = &apos;defend the fort&apos;;
var n = Math.min(p1.length, p2.length);
var ks = toBytes(&apos;REUSEDPADREUSEDPAD&apos;).slice(0, n);   // the SAME pad used twice -- the bug&lt;/p&gt;
&lt;p&gt;var c1 = xorBytes(toBytes(p1).slice(0, n), ks);
var c2 = xorBytes(toBytes(p2).slice(0, n), ks);&lt;/p&gt;
&lt;p&gt;// The attacker never has the key. He XORs the two ciphertexts; the pad cancels:
var leaked = xorBytes(c1, c2);                         // equals p1 XOR p2&lt;/p&gt;
&lt;p&gt;// With a crib -- a good guess at p1 -- he recovers p2 outright:
var crib = toBytes(&apos;attack at dawn&apos;).slice(0, n);
console.log(&apos;Recovered from a crib: &apos; + toStr(xorBytes(leaked, crib)));  // defend the for
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Reusing a $(\text{key}, \text{nonce})$ leaks $P_1 \oplus P_2$ with no computational assumption to fall back on -- no key length saves you. In GCM it is worse: repeated nonces also expose the GHASH subkey $H$ -- uniquely, once two or more collisions accumulate -- enabling universal forgery [@joux-2006][@nist-sp-800-38d].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The core science, then, is closed. &quot;Confidentiality does not imply integrity&quot; is settled, reuse is an information-theoretic floor, and the block-size wall is provable. If the theory is finished, what is left to work on? More than you would guess -- and it is where the modes still bite.&lt;/p&gt;
&lt;h2&gt;9. Where Modes Still Bite&lt;/h2&gt;
&lt;p&gt;The confidentiality-versus-integrity fight is won. The live frontier is one level up: not &quot;is it confidential?&quot; but &quot;how &lt;em&gt;resilient&lt;/em&gt; is the authenticated scheme when humans and hardware misbehave?&quot; Four problems are genuinely open, and one popular worry is not a problem at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make nonce-misuse-resistance the default, not the hedge.&lt;/strong&gt; AES-GCM fails catastrophically on nonce reuse, yet reuse -- from stateless senders, VM clones, restarted counters -- is among the most common real misuses [@nist-sp-800-38d]. AES-GCM-SIV delivers graceful degradation at roughly 1 cycle per byte and up to $2^{50}$ messages per key, &quot;well suited for real world applications that need a nonce-misuse resistant Authenticated Encryption scheme&quot; [@gcm-siv-spec-2017].&lt;/p&gt;
&lt;p&gt;But it is two-pass and offline, and it remains opt-in. The open problem is an AEAD that is online, single-pass, parallel, roughly 1 cycle per byte, &lt;em&gt;and&lt;/em&gt; misuse-resistant at once -- and, harder, getting TLS and KMS libraries to make it the default. NIST IR 8459 flags nonce management across the whole SP 800-38 series as a first-order concern [@nist-ir-8459].&lt;/p&gt;

The property that binds a ciphertext to the single key and context that produced it, so it cannot be made to decrypt validly under a second key. Standard AEAD security says nothing about it, which is why AES-GCM and ChaCha20-Poly1305 lack it -- and why a ciphertext can be crafted to open to two different valid plaintexts under two different keys.
&lt;p&gt;&lt;strong&gt;Key commitment and robustness.&lt;/strong&gt; AES-GCM and ChaCha20-Poly1305 are not key-committing: a single ciphertext can be built to decrypt to valid plaintexts under &lt;em&gt;two&lt;/em&gt; different keys [@key-commitment-2022]. Real systems assume otherwise, so this breaks password-based encryption, message franking, and envelope schemes.&lt;/p&gt;
&lt;p&gt;Partitioning-oracle attacks turned that gap into practical password recovery against Shadowsocks proxies; the same study only &lt;em&gt;surveyed&lt;/em&gt; protocols such as OPAQUE as potentially vulnerable rather than breaking them [@partitioning-oracles-2021]. The &quot;invisible salamanders&quot; attack built AES-GCM ciphertexts that decrypt to valid files under two keys, defeating message franking [@fast-franking-2018], a technique later generalized across file formats [@key-commitment-2022]. A genuine lower bound -- compactly committing AE is provably impossible at AES-GCM&apos;s exact performance profile [@fast-franking-2018] -- meets a cheap upper bound -- committing GCM and GCM-SIV variants at no ciphertext expansion [@committing-ae-2022]. The bounds nearly meet, yet the deployed default still sits on the wrong side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constant-time implementation.&lt;/strong&gt; CBC-decrypt-then-MAC timing gave us Lucky Thirteen, and table-based AES and GHASH leak through cache [@lucky-thirteen-2013]. This is effectively solved on hardware with AES-NI and CLMUL, and ChaCha20-Poly1305 is constant-time by design [@rfc-8439-chacha20]. It stays a live hazard on microcontrollers and throughout the legacy CBC base.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The forty-year migration.&lt;/strong&gt; A vast deployed base still runs CBC and 64-bit block ciphers, keeping Sweet32 and padding oracles alive. TLS 1.3 removed CBC-mode cipher suites and mandates AEAD [@rfc-8446-tls13], and IR 8459 audits the whole series [@nist-ir-8459], but embedded firmware, file-format crypto, database encryption, and private protocols lag by years. This is an engineering and logistics problem, not a mathematical one -- and it is the one that keeps the Failure Catalog growing.&lt;/p&gt;

A widespread belief holds that quantum computers break block cipher modes. To first order they do not: Grover&apos;s algorithm gives only a quadratic speedup on key search, so doubling the key (AES-256) restores the margin. The post-quantum churn is in key exchange and signatures, not in symmetric modes or AEADs [@nist-ir-8105].
&lt;p&gt;None of these is a hole in the confidentiality-versus-integrity theory. All are the gap between the ideal AEAD and the one you can install today -- which is exactly the gap a practitioner has to bridge. So here are the rules that bridge it.&lt;/p&gt;
&lt;h2&gt;10. Use X With These Params In Case Y&lt;/h2&gt;
&lt;p&gt;Everything so far collapses into one rule and a short decision tree you can apply without re-deriving a theorem. The rule: use a vetted AEAD, not a raw confidentiality mode [@ferguson-schneier-kohno-2010].&lt;/p&gt;
&lt;p&gt;The decision guide, in priority order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Default:&lt;/strong&gt; AES-GCM with a 96-bit nonce, unique per key -- prefer a deterministic counter -- and a hard cap of fewer than $2^{32}$ messages per key [@nist-sp-800-38d].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cannot guarantee unique nonces:&lt;/strong&gt; AES-GCM-SIV [@rfc-8452-gcm-siv], or XChaCha20-Poly1305, which widens the RFC 8439 ChaCha20-Poly1305 construction to a 192-bit random nonce [@rfc-8439-chacha20].The 192-bit-nonce XChaCha20 variant is specified in the IRTF draft draft-irtf-cfrg-xchacha, not in RFC 8439, which standardizes only the 96-bit ChaCha20-Poly1305 [@rfc-8439-chacha20]. The wider nonce is exactly what lets you pick nonces at random without tracking a counter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No fast AES hardware, or you need constant-time software:&lt;/strong&gt; ChaCha20-Poly1305 [@rfc-8439-chacha20].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Constrained or IoT, small code budget:&lt;/strong&gt; Ascon-AEAD128 [@nist-sp-800-232] or AES-CCM [@rfc-3610-ccm].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Need key or context commitment&lt;/strong&gt; (password-based encryption, franking, envelope, rotation): a committing AEAD [@committing-ae-2022].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Must drop to a raw mode&lt;/strong&gt; (interoperability or a FIPS boundary): CTR plus Encrypt-then-MAC (CMAC or HMAC) with a unique nonce, or CBC only with a fresh &lt;em&gt;unpredictable&lt;/em&gt; IV plus Encrypt-then-MAC plus constant-time padding [@nist-sp-800-38a][@bellare-namprempre-2000].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming or large objects:&lt;/strong&gt; do not ship one monolithic AEAD blob you cannot buffer. Split the stream into records, give each a unique per-chunk nonce, and authenticate a sequence number plus an explicit end-of-stream marker, so a dropped, reordered, or truncated record is caught rather than silently accepted. This is the record-protocol pattern DTLS 1.3 uses -- rejecting duplicates through &quot;a sliding receive window&quot; -- and that QUIC uses by binding each packet number into its AEAD nonce; together they close truncation and replay, the two integrity gaps a bare mode cannot see [@smyth-pironti-2013][@rfc-9147-dtls13][@rfc-9001-quic-tls].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never:&lt;/strong&gt; ECB for messages, a 64-bit block cipher for bulk data, a reused nonce, or &quot;encryption&quot; with no integrity tag.&lt;/li&gt;
&lt;/ol&gt;

flowchart TD
    Start[&quot;Need to encrypt a message&quot;] --&amp;gt; Q1{&quot;Is a vetted AEAD available?&quot;}
    Q1 --&amp;gt;|&quot;no&quot;| Raw[&quot;CTR or CBC plus Encrypt-then-MAC, verify tag first&quot;]
    Q1 --&amp;gt;|&quot;yes&quot;| Q2{&quot;Can you guarantee unique nonces?&quot;}
    Q2 --&amp;gt;|&quot;no&quot;| NMR[&quot;AES-GCM-SIV or XChaCha20-Poly1305 (192-bit nonce)&quot;]
    Q2 --&amp;gt;|&quot;yes&quot;| Q3{&quot;Fast AES hardware present?&quot;}
    Q3 --&amp;gt;|&quot;no&quot;| Chacha[&quot;ChaCha20-Poly1305&quot;]
    Q3 --&amp;gt;|&quot;yes&quot;| Q4{&quot;Constrained device?&quot;}
    Q4 --&amp;gt;|&quot;yes&quot;| Small[&quot;Ascon-AEAD128 or AES-CCM&quot;]
    Q4 --&amp;gt;|&quot;no&quot;| GCM[&quot;AES-GCM, 96-bit nonce, fewer than 2^32 messages per key&quot;]
&lt;p&gt;When you do touch a raw mode, the contract is the whole game. &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand&quot; rel=&quot;noopener&quot;&gt;Part 2&lt;/a&gt; covers how to &lt;em&gt;generate&lt;/em&gt; these values; this table says what each mode &lt;em&gt;requires&lt;/em&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Consequence of violation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ECB&lt;/td&gt;
&lt;td&gt;none (and that is the flaw)&lt;/td&gt;
&lt;td&gt;determinism leaks block equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBC&lt;/td&gt;
&lt;td&gt;unpredictable and unique IV&lt;/td&gt;
&lt;td&gt;a predictable IV enables BEAST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CFB&lt;/td&gt;
&lt;td&gt;unpredictable and unique IV&lt;/td&gt;
&lt;td&gt;a predictable IV enables chosen-plaintext distinguishing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OFB&lt;/td&gt;
&lt;td&gt;unique nonce&lt;/td&gt;
&lt;td&gt;reuse is a two-time pad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CTR&lt;/td&gt;
&lt;td&gt;unique nonce (unpredictability not required)&lt;/td&gt;
&lt;td&gt;reuse is a two-time pad&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Those requirements come straight from SP 800-38A [@nist-sp-800-38a]; the raw bytes that satisfy them come from a CSPRNG, which the &lt;a href=&quot;https://paragmali.com/blog/a-key-is-only-as-unguessable-as-the-dice-that-made-it-inside&quot; rel=&quot;noopener&quot;&gt;Windows CSPRNG post&lt;/a&gt; covers, and the real API calls that wire AES-CBC and AES-GCM together live in the &lt;a href=&quot;https://paragmali.com/blog/cng-architecture-bcrypt-ncrypt-ksps&quot; rel=&quot;noopener&quot;&gt;CNG post&lt;/a&gt;. Finally, every common misuse maps one-to-one to a named break and its fix:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse&lt;/th&gt;
&lt;th&gt;Named break&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;ECB for structured data&lt;/td&gt;
&lt;td&gt;Adobe 2013 [@troyhunt-adobe-2013]&lt;/td&gt;
&lt;td&gt;AEAD over AES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;static or predictable IV&lt;/td&gt;
&lt;td&gt;BEAST [@cve-2011-3389]&lt;/td&gt;
&lt;td&gt;fresh unpredictable IV, or an AEAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reused GCM nonce&lt;/td&gt;
&lt;td&gt;Nonce-Disrespecting Adversaries [@nonce-disrespecting-2016]&lt;/td&gt;
&lt;td&gt;counter nonces, or GCM-SIV / XChaCha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAC-then-encrypt or encrypt-and-MAC&lt;/td&gt;
&lt;td&gt;Lucky Thirteen [@lucky-thirteen-2013]&lt;/td&gt;
&lt;td&gt;Encrypt-then-MAC, verify before decrypt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;no integrity at all&lt;/td&gt;
&lt;td&gt;Efail [@efail-2018]&lt;/td&gt;
&lt;td&gt;an AEAD, or Encrypt-then-MAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;64-bit block cipher for bulk&lt;/td&gt;
&lt;td&gt;Sweet32 [@sweet32-2016]&lt;/td&gt;
&lt;td&gt;a 128-bit-block AEAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;truncation with no length integrity&lt;/td&gt;
&lt;td&gt;Truncation, Smyth-Pironti [@smyth-pironti-2013]&lt;/td&gt;
&lt;td&gt;authenticate length and end-of-stream&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

Run `openssl s_client -connect example.com:443 -tls1_3` and read the `Cipher` line: a modern server reports an AEAD such as `TLS_AES_128_GCM_SHA256` or `TLS_CHACHA20_POLY1305_SHA256`. If you instead see a suite with `CBC` in the name, you are looking at a legacy, malleable-mode configuration that belongs on the migration list.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Read every mode as a building block with an exact IV/nonce contract, and read every break as a missing integrity answer or a violated contract. Default to an AEAD; touch a raw mode only under Encrypt-then-MAC discipline, and only when an AEAD is off the table.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notice that every rule is the same rule wearing different clothes: pick the primitive that answers &lt;em&gt;both&lt;/em&gt; questions, and honor its IV/nonce contract. The folklore that gets this wrong is dense enough to deserve its own section.&lt;/p&gt;
&lt;h2&gt;11. Misconceptions, Named and Corrected&lt;/h2&gt;
&lt;p&gt;Seven half-truths cause real incidents. Each dissolves under the same two-question lens.&lt;/p&gt;


No. All five SP 800-38A modes are malleable, so an attacker who cannot read your ciphertext can still predictably edit the plaintext -- bit-for-bit in the additive keystream modes CTR and OFB, block-coupled in CBC, and a mix of the two in CFB. Confidentiality and integrity are different guarantees; you need a MAC or an AEAD to get the second one, and encryption alone does not provide it.


Only as a building block, never as a message-encryption mode. The raw single-block permutation is legitimate inside key wrap and CMAC or CBC-MAC. But ECB is deterministic, so the moment your data has any structure or repetition it leaks block equality -- which is why NIST is moving ECB to legacy use [@nist-sp-800-131a-r3]. The rule is &quot;never ECB for messages,&quot; not &quot;ECB has no use anywhere.&quot;


No. A random IV fixes ECB&apos;s determinism, but it adds no integrity -- the mode is still malleable. And the requirement is not uniform: CBC and CFB need an *unpredictable* IV, while CTR and OFB need only a *unique* nonce [@nist-sp-800-38a]. Getting that distinction wrong is exactly what BEAST exploited.


No. Under random 96-bit nonces you must stay below roughly $2^{32}$ invocations per key to keep the nonce-collision probability negligible [@nist-sp-800-38d]. Past that, a repeat becomes likely, and a repeated GCM nonce fails catastrophically. If you cannot count, use AES-GCM-SIV or XChaCha20-Poly1305 instead.


That fixes nothing that matters. Both are confidentiality-only and both are malleable; swapping one for the other trades performance characteristics, not security [@rogaway-modes-2011]. The fix is an AEAD, not a different malleable mode.


Not under nonce reuse. A single repeated $(\text{key}, \text{nonce})$ leaks $P_1 \oplus P_2$, and with two or more collisions it recovers the GHASH subkey $H$, enabling universal forgery -- Joux&apos;s &quot;forbidden attack,&quot; which was later found live on real TLS servers [@joux-2006][@nonce-disrespecting-2016]. GCM is only as strong as its nonce discipline.


Order matters. MAC-then-encrypt and encrypt-and-MAC are fragile and produced Lucky Thirteen and the padding-oracle family [@lucky-thirteen-2013]. The proven-safe generic composition is Encrypt-then-MAC: authenticate the ciphertext and verify the tag *before* you decrypt [@bellare-namprempre-2000].

&lt;p&gt;Every one dissolves into the same sentence -- which is where this started, and where it ends.&lt;/p&gt;
&lt;h2&gt;Two Questions, Forty Years&lt;/h2&gt;
&lt;p&gt;Return to the opening. A ciphertext AES kept perfectly secret, rewritten in transit by an attacker who never read a byte of it, and accepted as authentic -- because the mode answered only the first of two questions. That was not an exotic failure. It was the ordinary shape of every break in this article.&lt;/p&gt;
&lt;p&gt;Run the catalog back through the lens and the pattern is total. Adobe [@troyhunt-adobe-2013], Vaudenay&apos;s padding oracle [@vaudenay-2002], BEAST [@cve-2011-3389], Lucky Thirteen [@lucky-thirteen-2013], POODLE [@cve-2014-3566], Sweet32 [@cve-2016-2183], Efail [@efail-2018], and GCM nonce reuse [@nonce-disrespecting-2016]: in every case, AES or DES did exactly what it promised, and the variable was always &lt;em&gt;which question the design forgot&lt;/em&gt; or &lt;em&gt;which IV/nonce contract it violated&lt;/em&gt;. Not one of these was a cipher break.&lt;/p&gt;
&lt;p&gt;Read that way, the 2024-2026 state of the art is a single move. The field stopped iterating confidentiality modes and standardized the &lt;em&gt;second&lt;/em&gt; answer. Authenticated encryption is the destination -- and its dominant AES instances, GCM and CCM, are literally these very modes plus a MAC, while ChaCha20-Poly1305 and Ascon supply a parallel lineage that is not [@nist-ir-8459][@nist-sp-800-232]. The frontier moved with it: nonce-misuse resistance and key commitment are the properties the deployed defaults still lack.&lt;/p&gt;
&lt;p&gt;The payoff of the field guide is the same as its premise. Master the five not as interchangeable dropdown options but as building blocks with exact contracts, and every break -- past or future -- becomes readable on sight. You do not need to memorize the next CVE. You need one test, and you already have it: &lt;em&gt;which question did this mode answer, and was its IV/nonce contract honored?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That lens outlives every mode, every cipher, and every standard revision. Which is why this is Part 5 of a field guide to protocol design, not a chapter on five diagrams.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;block-cipher-modes-ecb-to-ctr&quot; keyTerms={[
  { term: &quot;Block cipher&quot;, definition: &quot;A keyed, invertible permutation on one fixed-size block of bits (64 for DES, 128 for AES).&quot; },
  { term: &quot;Mode of operation&quot;, definition: &quot;The wrapper that lifts a one-block cipher to messages of arbitrary length.&quot; },
  { term: &quot;IND-CPA&quot;, definition: &quot;Indistinguishability under chosen-plaintext attack; it forces secure encryption to be randomized or stateful.&quot; },
  { term: &quot;Initialization Vector (IV)&quot;, definition: &quot;A per-message starting value; it must be unpredictable for CBC and CFB.&quot; },
  { term: &quot;Nonce&quot;, definition: &quot;A number used once per key; CTR and OFB need it unique, not unpredictable.&quot; },
  { term: &quot;Malleability&quot;, definition: &quot;An attacker can turn a ciphertext into a predictable change of the plaintext, with no key.&quot; },
  { term: &quot;Message Authentication Code (MAC)&quot;, definition: &quot;A keyed integrity tag that supplies the answer a confidentiality mode omits.&quot; },
  { term: &quot;AEAD&quot;, definition: &quot;Authenticated Encryption with Associated Data; confidentiality and integrity in one primitive.&quot; },
  { term: &quot;Birthday bound&quot;, definition: &quot;Collisions among n-bit values become likely near 2 to the n over 2; the block-size ceiling.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>block-cipher-modes</category><category>aes</category><category>authenticated-encryption</category><category>aead</category><category>cryptography</category><category>cbc</category><category>ctr</category><category>gcm</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><item><title>Secure Against Whom? The Security Definitions Every Protocol Designer Needs</title><link>https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/</link><guid isPermaLink="true">https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/</guid><description>A protocol designer&apos;s field guide to IND-CPA, IND-CCA2, EUF-CMA, AEAD, and the random oracle model, and why the wrong adversary model sinks real protocols.</description><pubDate>Sat, 04 Jul 2026 15:17:50 GMT</pubDate><content:encoded>
A security definition is a contract with two clauses: a **goal** (what the adversary must fail to do) bound to an **adversary model** (what the adversary is allowed to do). Nothing is ever &quot;secure&quot; in the abstract, only secure *relative to that contract*. Most real-world breaks are not weak math. They are a construction that met a *weaker* target than its deployment&apos;s adversary demanded: Bleichenbacher and POODLE handed attackers a decryption oracle, AES-GCM nonce reuse handed them a repeated nonce, invisible salamanders handed them a second key. This guide builds the vocabulary generation by generation, from IND-CPA and IND-CCA2 through EUF-CMA, SUF-CMA, AEAD, and committing encryption, plus the two proof settings (random oracle versus standard model) and the reduction that ties goals to hard problems. The operational lesson is one habit: **state your deployment&apos;s adversary model first, pick the matching target second, and choose the primitive last.**
&lt;h2&gt;1. The Primitive Was Fine&lt;/h2&gt;
&lt;p&gt;In 1998, Daniel Bleichenbacher did not break RSA. Factoring stayed hard, the keys stayed secret, and the math was exactly as sound the day after his attack as the day before. Yet with about a million carefully malformed messages, he could recover the secrets of an SSL session, because the server, trying to be helpful, answered one question every time it rejected a ciphertext: was the padding valid? [@bleichenbacher1998]&lt;/p&gt;
&lt;p&gt;That single leaked bit is a decryption oracle. RSA as used in SSL wrapped the session key in PKCS#1 v1.5 padding, and a server that reports &quot;bad padding&quot; differently from &quot;bad key&quot; tells the attacker, ciphertext by ciphertext, whether a chosen value decrypts into the padded structure. Bleichenbacher showed that roughly $2^{20}$ adaptive queries turn that trickle into the full plaintext [@bleichenbacher1998].&lt;/p&gt;
&lt;p&gt;The tempting reading is &quot;an RSA bug.&quot; It is not. Nineteen years later, in 2017, researchers pointed the identical attack at modern TLS stacks and load balancers, and it &lt;em&gt;still worked&lt;/em&gt; against products from several major vendors [@robot_2018] [@robot_site]. They called it ROBOT, the Return Of Bleichenbacher&apos;s Oracle Threat. The industry had spent two decades patching individual symptoms, tightening one timing difference here, one error message there, without ever changing what was actually wrong.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ROBOT is the clearest evidence in this article that the failure is structural, not incidental. Every point fix removed one &lt;em&gt;instance&lt;/em&gt; of the oracle. None of them changed the fact that PKCS#1 v1.5 encryption was deployed where an attacker can observe decryption behavior, and it was never built to survive that. You cannot patch your way out of the wrong security target. You have to change the target [@robot_2018].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So what, exactly, does &quot;secure&quot; mean, and secure against whom? That question is not rhetorical. It is the entire discipline. A construction is secure only against a specified adversary trying to achieve a specified goal, and the moment the deployment hands that adversary a capability the specification never modeled, the guarantee evaporates no matter how sound the underlying math.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A security definition is a contract with two clauses: a &lt;strong&gt;goal&lt;/strong&gt; (what the adversary must fail to achieve) bound to an &lt;strong&gt;adversary model&lt;/strong&gt; (what the adversary is allowed to do). &quot;Secure&quot; without a named adversary model is not a weak claim. It is not a claim at all.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This guide makes you fluent in that contract. We build the vocabulary the way the field built it, one generation at a time, and prove the thesis with evidence: a catalog of named, dated breaks in which a &lt;em&gt;provably fine&lt;/em&gt; primitive sank a real protocol because the environment granted an adversary the proof never covered. Bleichenbacher and ROBOT are the first entry.&lt;/p&gt;
&lt;p&gt;By the end you will read any construction as goal plus adversary model plus proof setting, and see why that one habit would have prevented the entire catalog. For the deployment story of how Windows TLS stacks rotated ciphers over twenty years, see &lt;a href=&quot;https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm&quot; rel=&quot;noopener&quot;&gt;SChannel and the twenty-year algorithm&lt;/a&gt;.&lt;/p&gt;

A construction is never secure in the abstract. It is secure only relative to a named adversary model.
&lt;p&gt;To see why a correct primitive was not enough, we have to go back to a time when &quot;secure&quot; had no definition at all.&lt;/p&gt;
&lt;h2&gt;2. Before &quot;Secure&quot; Meant Anything&lt;/h2&gt;
&lt;p&gt;For most of history, a cipher was &quot;secure&quot; because nobody important had broken it yet. That is survivorship bias wearing the costume of a guarantee. It tells you nothing about the attacks nobody has tried, and it cannot distinguish a strong design from one that simply has not attracted a competent adversary. Every classical cipher, from simple substitution to Vigenère to Enigma, was &quot;secure&quot; by this standard right up until the morning it was not.&lt;/p&gt;
&lt;p&gt;The one durable gain from that era is Kerckhoffs&apos;s principle: assume the adversary knows every detail of your algorithm, and put all the secrecy in the key.Auguste Kerckhoffs stated this in 1883 [@kerckhoffs1883]. It is why &quot;security through obscurity&quot; is a slur, and why modern designs publish their algorithms and keep only keys secret. But note what it still does not supply: a definition of what the attacker must &lt;em&gt;fail&lt;/em&gt; to do. It is a rule about where to hide the secret, not a definition of success. It leaves &quot;secure&quot; undefined.&lt;/p&gt;
&lt;p&gt;That gap survived the single most important idea in the field&apos;s modern history. In 1976, Whitfield Diffie and Martin Hellman opened public-key cryptography, showing that two strangers could agree on a secret over a channel an eavesdropper fully controls, and sketching digital signatures along the way [@dh1976]. It was a genuine discontinuity. But it delivered &lt;em&gt;mechanisms&lt;/em&gt;, not a definition of what an attacker must fail to achieve. &quot;Secure&quot; still meant &quot;we tried and could not break it.&quot;&lt;/p&gt;
&lt;p&gt;You cannot prove a scheme meets a goal you never wrote down. So the founding question of modern cryptography is not &quot;how do we encrypt?&quot; It is &quot;what must the attacker fail at, and what is the attacker allowed to do while failing?&quot; Shafi Goldwasser and Silvio Micali answered it in 1982 and 1984 with &lt;em&gt;semantic security&lt;/em&gt;: a ciphertext must leak nothing computationally useful about its plaintext, not even a single predicate, to any efficient adversary who already knows partial information [@gm1984] [@gm1982stoc].&lt;/p&gt;
&lt;p&gt;They also gave an equivalent, more usable form, indistinguishability, which we meet as IND-CPA in the next section. The award committee agreed the shift mattered: Goldwasser and Micali received the 2012 Turing Award for it [@turing2012].&lt;/p&gt;
&lt;p&gt;Two words in that definition carry enormous weight. &quot;Efficient&quot; means the adversary runs in probabilistic polynomial time; we do not defend against an attacker with unbounded computation, because for public-key cryptography that is hopeless. &quot;Negligible&quot; means the adversary&apos;s edge over guessing shrinks faster than any inverse polynomial in the security parameter.Probabilistic polynomial time (PPT) and negligible functions are the two dials of computational security. A function is negligible if it eventually falls below $1/n^c$ for every constant $c$. Together they let us say &quot;no realistic adversary wins by more than a vanishing margin&quot; instead of the impossible &quot;no adversary ever wins.&quot; Security stops being a binary property of an algorithm and becomes a statement about a &lt;em&gt;game&lt;/em&gt; between a challenger and a bounded adversary.&lt;/p&gt;

This article numbers the generations of definitions by *idea*, not by calendar. Confidentiality against a passive attacker comes first, then against an active one, then integrity, then their union, then commitment. The field did not invent them in that order: unforgeability for signatures (1988) was defined *before* adaptive chosen-ciphertext security (1991). Where the dates cross, we flag it. The dependency that matters here is conceptual, not temporal.
&lt;p&gt;Before we build the first game, there is one loose end. An airtight, provably unbreakable definition of secrecy was already on the books, and it predated all of this by decades. Why did the field not simply use Shannon&apos;s?&lt;/p&gt;
&lt;h2&gt;3. The Perfect Cipher We Cannot Afford&lt;/h2&gt;
&lt;p&gt;Claude Shannon gave cryptography a mathematically perfect definition of secrecy in 1949, and the one-time pad achieves it, unconditionally, forever [@shannon1949]. Not &quot;we think it is hard to break.&quot; Provably impossible to break, even against an adversary with infinite computing power.&lt;/p&gt;

An encryption scheme has perfect secrecy if the ciphertext is statistically independent of the plaintext: observing the ciphertext leaves the adversary&apos;s probability distribution over possible messages exactly where it started. Formally, $\Pr[M = m \mid C = c] = \Pr[M = m]$ for every message and ciphertext. No amount of computation helps, because the information is simply not present in the ciphertext.
&lt;p&gt;The one-time pad reaches this bar with almost embarrassing simplicity. Take a key $k$ that is uniformly random and as long as the message $m$, and send $c = m \oplus k$. Because each key bit is a fresh coin flip, every plaintext of that length is an equally plausible explanation of the ciphertext, so the ciphertext reveals nothing. Decryption is the same XOR again.&lt;/p&gt;
&lt;p&gt;So why is the internet not built on one-time pads? Shannon proved the catch in the same paper: any perfectly secret cipher needs at least as much key entropy as message entropy, $H(K) \ge H(M)$ [@shannon1949]. The key must be as long as everything you will ever send, and it does nothing to solve the problem of getting that key to the other party in the first place. Two strangers who have never met cannot pre-share gigabytes of secret pad, which is exactly the situation every TLS handshake faces.&lt;/p&gt;
&lt;p&gt;There are two more ceilings, and they matter for the rest of this guide. The first is that the perfection is contingent on the pad being used &lt;em&gt;once&lt;/em&gt;. Reuse a pad across two messages and $c_1 \oplus c_2 = m_1 \oplus m_2$, which leaks the XOR of the plaintexts and, with a little language structure, both messages.This is not hypothetical. The VENONA project decrypted Soviet cables for years precisely because the Soviets reused one-time-pad key material under wartime production pressure [@venona]. &quot;Never reuse the key&quot; is a lesson written in decades of intercepted traffic, and it returns almost verbatim in the AES-GCM nonce story later.&lt;/p&gt;
&lt;p&gt;The second ceiling is quieter and more important for our thesis. The one-time pad offers zero integrity. It is maximally &lt;em&gt;malleable&lt;/em&gt;: flip any bit of the ciphertext and you flip exactly the corresponding bit of the plaintext, and the receiver has no way to notice. Perfect secrecy says nothing whatsoever about whether a message arrived unmodified.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is one of the most expensive misconceptions in applied cryptography, so meet it early. Confidentiality (nobody can read it) and integrity (nobody can change it undetected) are &lt;em&gt;different goals&lt;/em&gt;. A scheme can have perfect confidentiality and no integrity at all, as the one-time pad demonstrates. Assuming encryption implies integrity is the root cause of an entire class of breaks we catalog in Generation 4.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The way out of the key-length ceiling is to relax the adversary. Shannon&apos;s model grants the attacker unlimited computation. If we instead demand only that a &lt;em&gt;computationally bounded&lt;/em&gt; adversary fail, we can hope for short keys, public keys, and eventually integrity, provided we state precisely what &quot;fail&quot; means. That relaxation is the whole computational project the previous section set up.&lt;/p&gt;
&lt;p&gt;The relaxation bites immediately, and its first bite is instructive. Consider deterministic &quot;textbook&quot; RSA, where a message always encrypts to the same ciphertext under a given public key. An adversary who suspects the plaintext is one of two values does not need to break RSA at all. The public key lets anyone encrypt, so the attacker encrypts both candidates and compares them to the challenge ciphertext. Whichever matches is the answer.&lt;/p&gt;
&lt;p&gt;Goldwasser and Micali turned this into a theorem: no deterministic public-key scheme can be semantically secure [@gm1984]. Randomization is not good hygiene; it is mandatory, a consequence of the definition itself. That randomness has to come from somewhere trustworthy, its own deep subject that a later part of this series takes up.&lt;/p&gt;
&lt;p&gt;With the passive adversary finally pinned down as a game, we can write the first modern definition precisely, and then watch an attacker walk straight through its one blind spot.&lt;/p&gt;
&lt;h2&gt;4. Five Generations, One Recurring Story&lt;/h2&gt;
&lt;p&gt;The next four decades of cryptography follow one pattern so regularly that you can predict the next entry. A definition fixes a goal and an adversary model. A deployment then hands the attacker a capability the model never granted, whether a decryption oracle, a repeated nonce, or a second valid key. A named, dated break follows. And the field responds by writing a stronger definition that pulls the new capability inside the game. Watch for that shape. By Generation 3 you will feel it coming.&lt;/p&gt;

timeline
    title From perfect secrecy to committing encryption
    1949 : Shannon perfect secrecy, unbounded adversary
    1976 : Diffie-Hellman public-key crypto, no definition of success
    1984 : Goldwasser-Micali semantic security and IND-CPA
    1988 : Goldwasser-Micali-Rivest unforgeability, EUF-CMA
    1990 to 1991 : Naor-Yung CCA1, Rackoff-Simon CCA2, Dolev-Dwork-Naor non-malleability
    1993 : Bellare-Rogaway random oracle model
    1998 : Bleichenbacher break, relations map, Cramer-Shoup, ROM uninstantiability
    2000 to 2006 : Bellare-Namprempre AE, Rogaway AEAD, SIV nonce-misuse resistance
    2018 to 2021 : Invisible salamanders and partitioning oracles, committing AE
    2024 : ML-KEM in FIPS 203, NIST Accordion effort
&lt;h3&gt;Generation 1: confidentiality against a passive adversary (IND-CPA, 1984)&lt;/h3&gt;
&lt;p&gt;Goldwasser and Micali&apos;s indistinguishability game is the founding contract of modern cryptography, and it is worth stating exactly.&lt;/p&gt;

A scheme is IND-CPA secure if no efficient adversary can tell which of two self-chosen, equal-length messages was encrypted, even while free to encrypt anything else it likes. The adversary submits $m_0$ and $m_1$; the challenger flips a secret bit $b$ and returns the challenge $\mathrm{Enc}(m_b)$; the adversary outputs a guess $b&apos;$. Its advantage is $\lvert \Pr[b&apos; = b] - \tfrac{1}{2} \rvert$, and security means that advantage stays negligible. This is the indistinguishability form of semantic security.
&lt;p&gt;In the public-key setting the adversary already holds the public key, so its chosen-plaintext power is free: it can encrypt anything it wants. That is exactly what dooms determinism. If encryption is a plain function, the adversary encrypts $m_0$ itself and checks whether the result equals the challenge.&lt;/p&gt;
&lt;p&gt;{`
// A toy &quot;deterministic&quot; encryption: the same message always maps to the same ciphertext.
function encryptDeterministic(pk, m) {
  // stand-in for textbook RSA: a fixed, public, invertible transform
  return &quot;CT[&quot; + pk + &quot;:&quot; + m + &quot;]&quot;;
}&lt;/p&gt;
&lt;p&gt;// The IND-CPA adversary, given only the public key and the challenge ciphertext.
function adversary(pk, challenge) {
  const m0 = &quot;attack at dawn&quot;;
  const m1 = &quot;retreat at dusk&quot;;
  // The public key lets the adversary encrypt candidates itself.
  const guessForM0 = encryptDeterministic(pk, m0);
  // Re-encrypt m0 and compare. No key recovery, no math broken.
  return (challenge === guessForM0) ? &quot;m0&quot; : &quot;m1&quot;;
}&lt;/p&gt;
&lt;p&gt;const pk = &quot;PUBLIC-KEY&quot;;
const challengerSecretlyChose = &quot;attack at dawn&quot;;   // this is m0
const challenge = encryptDeterministic(pk, challengerSecretlyChose);
console.log(&quot;Adversary guesses:&quot;, adversary(pk, challenge));
console.log(&quot;Advantage over guessing: 1/2, the maximum. Deterministic PKE cannot be IND-CPA.&quot;);
`}&lt;/p&gt;
&lt;p&gt;The adversary wins every time, with the maximal advantage of one half. The fix, forced by the definition rather than by taste, is randomization: a good scheme encrypts the same message to freshly random-looking ciphertexts, so re-encrypting a guess tells the attacker nothing.&lt;/p&gt;
&lt;p&gt;IND-CPA is the right target for exactly one adversary: a passive one, who watches ciphertexts go by but never learns how the receiver reacts to them. That is its blind spot. Real servers decrypt attacker-supplied ciphertexts and &lt;em&gt;do something observable&lt;/em&gt;: return an error, take a branch, spend measurable time. Each reaction is a decryption oracle the IND-CPA game never handed out.&lt;/p&gt;
&lt;p&gt;Bleichenbacher&apos;s 1998 attack, from the opening of this article, is precisely this failure: an IND-CPA-grade target deployed where the adversary can watch decryption behavior [@bleichenbacher1998]. To survive that attacker, the definition itself must give the adversary a decryption oracle.&lt;/p&gt;
&lt;h3&gt;Generation 2: confidentiality against an active adversary (IND-CCA1, IND-CCA2, non-malleability, 1990 to 1998)&lt;/h3&gt;
&lt;p&gt;The strengthening arrived in two steps. Naor and Yung (1990) gave the adversary a decryption oracle it may query only &lt;em&gt;before&lt;/em&gt; seeing the challenge, the &quot;lunchtime attack&quot; or CCA1, and built the first provably chosen-ciphertext-secure scheme using double encryption plus a zero-knowledge proof that both ciphertexts hold the same plaintext [@ny1990]. A year later Rackoff and Simon (1991) removed the timing restriction: the oracle is available before &lt;em&gt;and&lt;/em&gt; after the challenge, forbidding only a query on the challenge ciphertext itself. That is IND-CCA2, still the gold-standard confidentiality target today [@rs1991].&lt;/p&gt;

IND-CCA2 is the IND-CPA game plus a decryption oracle the adversary may call adaptively both before and after receiving the challenge $c^{*}$, with the single restriction that it may not ask for the decryption of $c^{*}$ itself. A scheme that meets it withstands exactly the padding-oracle and reaction-attack environments that sink IND-CPA-only deployments.

sequenceDiagram
    participant A as Adversary
    participant C as Challenger
    A-&amp;gt;&amp;gt;C: Two equal-length messages m0 and m1
    C-&amp;gt;&amp;gt;C: Flip secret bit b, encrypt the chosen message
    C-&amp;gt;&amp;gt;A: Challenge ciphertext c-star
    A--&amp;gt;&amp;gt;C: IND-CCA2 only, decrypt any ciphertext except c-star
    C--&amp;gt;&amp;gt;A: The corresponding plaintext
    A-&amp;gt;&amp;gt;C: Final guess b-prime
    Note over A,C: Advantage measures how far a correct guess beats one half
&lt;p&gt;Dolev, Dwork, and Naor (1991) added an orthogonal axis that turns out to matter just as much.&lt;/p&gt;

A scheme is non-malleable if, given a ciphertext of some message $m$, no efficient adversary can produce a ciphertext of a *meaningfully related* message such as $m+1$ or $2m$. Indistinguishability says the adversary learns nothing from a ciphertext; non-malleability says the adversary cannot *transform* it into a ciphertext of a related plaintext, which is a distinct, active threat such as raising a sealed bid by one dollar [@ddn1991].
&lt;p&gt;By 1998 the notions had multiplied into a zoo, and Bellare, Desai, Pointcheval, and Rogaway mapped it. They proved every implication and separation among the goals of indistinguishability and non-malleability crossed with the attacks CPA, CCA1, and CCA2, and the headline result is that IND-CCA2 and non-malleability under CCA2 are the same property [@bdpr1998]. That equivalence is the reward of definition-first thinking: hit the single target IND-CCA2 and you get non-malleability with no separate proof.&lt;/p&gt;
&lt;p&gt;Notice the chronology. Rackoff and Simon defined the adaptive oracle in 1991, seven years before Bleichenbacher showed a real SSL server &lt;em&gt;being&lt;/em&gt; that oracle. The definition did not chase the break; it predicted the entire class of break. What was missing in 1998 was not the theory but the discipline of choosing the target the deployment demanded. And yet IND-CCA2, for all its strength, guarantees only confidentiality. It promises nothing about whether a message you &lt;em&gt;can&lt;/em&gt; read arrived unmodified, or whether an attacker who cannot read it can still inject one the receiver will accept.&lt;/p&gt;
&lt;h3&gt;Generation 3: integrity as a first-class goal (EUF-CMA and SUF-CMA, 1988 onward)&lt;/h3&gt;
&lt;p&gt;Here the calendar folds back. Goldwasser, Micali, and Rivest defined unforgeability in 1988, three years before adaptive chosen-ciphertext security, by carrying the game-based, reduction-oriented definition style from secrecy to authenticity: the same challenger, oracle access, and negligible success bound, now applied to a forgery game rather than to indistinguishability [@gmr1988].&lt;/p&gt;

Give the adversary the verification key and a signing oracle it may query on any messages it chooses. The adversary wins if it outputs a valid signature on a message it never asked the oracle to sign. A signature or MAC scheme is EUF-CMA secure if every efficient adversary wins with only negligible probability. This is the target behind HMAC, RSA-PSS, ECDSA, and Ed25519.
&lt;p&gt;There is a strictly stronger sibling that the deployment reality of the decade made important.&lt;/p&gt;

SUF-CMA changes only the winning condition: the adversary wins if it produces any valid message-signature *pair* the oracle never produced, including a brand-new signature on a message that was already signed. SUF-CMA therefore forbids the signature malleability that EUF-CMA tolerates. Every SUF-CMA scheme is EUF-CMA, but not the reverse [@katzlindell].
&lt;p&gt;Why does the gap between them matter? Because EUF-CMA lets a third party take a valid signature and &lt;em&gt;maul&lt;/em&gt; it into a different valid signature on the same message. Wherever a protocol treats a signature or ciphertext as a unique identifier, that malleability bites. Early Bitcoin is the textbook case: an attacker could re-encode an ECDSA signature, changing a transaction&apos;s identifier without invalidating it, which disrupted systems that assumed the identifier was fixed. MtGox famously halted withdrawals citing malleability, though the same analysis found it did not actually account for the exchange&apos;s lost funds [@decker2014]. That is exactly the attack SUF-CMA forbids and EUF-CMA permits.&lt;/p&gt;
&lt;p&gt;A subtler cousin is verifying a signature without binding its context (which key, which algorithm, which audience), which invites cross-protocol confusion. For a real incident where a stolen signing key met permissive validation, see &lt;a href=&quot;https://paragmali.com/blog/forged-from-2016-how-storm-0558-turned-one-stolen-signing-ke&quot; rel=&quot;noopener&quot;&gt;how Storm-0558 turned one signing key into forged tokens&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A MAC or signature protects a token. It is not, by itself, a secure channel. For that we need confidentiality and integrity together, composed in a way we can prove correct.&lt;/p&gt;
&lt;h3&gt;Generation 4: authenticated encryption (AE and AEAD, 2000 to 2006)&lt;/h3&gt;
&lt;p&gt;Practitioners had been bolting encryption and MACs together by instinct, and instinct chose badly. Bellare and Namprempre (2000) settled the matter with definitions and proofs [@bn2000].&lt;/p&gt;

INT-PTXT (plaintext integrity) means the adversary cannot force decryption to accept a plaintext that was never legitimately encrypted. INT-CTXT (ciphertext integrity), strictly stronger, means the adversary cannot produce *any* valid ciphertext the encryptor never output. INT-CTXT is what real channels want, because it rejects forged ciphertexts before they are ever decrypted.
&lt;p&gt;Their verdict on the three ways to glue encryption to a MAC is one of the most consequential engineering results in the field: &lt;strong&gt;Encrypt-then-MAC&lt;/strong&gt; (encrypt, then authenticate the ciphertext) generically delivers IND-CCA together with INT-CTXT, while &lt;strong&gt;MAC-then-Encrypt&lt;/strong&gt; and &lt;strong&gt;Encrypt-and-MAC&lt;/strong&gt; do not [@bn2000]. Order is not a detail. It is the difference between a secure channel and a broken one.&lt;/p&gt;

AEAD is a single combined target: confidentiality (IND-CPA) plus ciphertext integrity (INT-CTXT) over a nonce-based interface that takes a key, a nonce, associated data, and a message, and returns a ciphertext and a tag. The associated data (packet headers, sequence numbers, routing context) is authenticated but not encrypted. Rogaway named and formalized it in 2002, and RFC 5116 turned it into the deployable interface every modern channel uses.
&lt;p&gt;Rogaway (2002) added the associated-data interface [@rogaway2002], McGrew&apos;s RFC 5116 (2008) standardized it as the API protocol designers actually call [@rfc5116], and Rogaway and Shrimpton (2006) defined the fallback for when nonces cannot be trusted [@rs2006siv].&lt;/p&gt;

A nonce-misuse-resistant scheme degrades gracefully when a nonce repeats: instead of collapsing, it leaks only whether two (nonce, associated data, message) triples were identical. The SIV construction achieves this by deriving the tag from the plaintext first, then using that tag as the encryption nonce. This is the guarantee behind AES-GCM-SIV.
&lt;p&gt;Now the breaks, and they line up with the theory exactly. TLS chose MAC-then-Encrypt with CBC, the very order Bellare and Namprempre flagged, and decrypted before verifying. That handed attackers a padding-and-timing decryption oracle twice: &lt;strong&gt;Lucky Thirteen&lt;/strong&gt; (2013) recovered plaintext from tiny timing differences in MAC and padding checks [@lucky13_2013] [@lucky13_site], and &lt;strong&gt;POODLE&lt;/strong&gt; (2014) forced a downgrade to SSL 3.0, whose CBC padding is not checkably authenticated, then peeled secrets off byte by byte [@poodle_cve]. Both are wrong-composition-order failures wearing different clothes.&lt;/p&gt;
&lt;p&gt;The second Gen-4 landmine is the nonce contract, and it is the most destructive single mistake in applied cryptography.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Reuse one (key, nonce) pair in AES-GCM and you do not lose a little security, you lose all of it. GHASH evaluates a polynomial whose coefficients are the ciphertext blocks at the secret authentication key $H$, so two messages under one nonce cancel the block-cipher mask and leave a polynomial equation in $H$. The attacker factors that polynomial to recover $H$ and can then forge a valid tag on &lt;em&gt;any&lt;/em&gt; message. This is Joux&apos;s &quot;forbidden attack,&quot; a universal forgery rather than a slow leak [@joux_gcm]. VM clones, snapshot rollbacks, and stateless senders are the usual culprits.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even without reuse, GCM has a hard ceiling.AES-GCM&apos;s standard nonce is 96 bits. With random nonces, birthday collisions on that nonce cap a single key at about $2^{32}$ messages, the bound NIST SP 800-38D fixes in section 8.3 to keep the collision probability negligible. A separate limit, the 32-bit block counter that the 96-bit nonce leaves inside the 128-bit block, caps any one message at about $2^{32}$ blocks (roughly 64 GiB). NIST warns that exceeding either cap can compromise both confidentiality and integrity [@sp800_38d]. The proof was always conditioned on unique nonces and a bounded message count; the environment routinely violates both.&lt;/p&gt;
&lt;p&gt;Which surfaces the deepest omission of all. AE and AEAD guarantee that a ciphertext is unforgeable and hides its plaintext. They never required it to commit to the &lt;em&gt;key&lt;/em&gt; that produced it.&lt;/p&gt;
&lt;h3&gt;Generation 5: committing encryption, binding a ciphertext to its key (2018 to 2021, still open)&lt;/h3&gt;

An AEAD scheme is key-committing if a single ciphertext cannot be made to decrypt validly under two different keys. Stronger notions bind more of the input: context commitment binds the key, nonce, and associated data, while the strongest level also binds the message. Bellare and Hoang grade these from CMT-1 (key only) up to CMT-4, which commits to the entire input tuple of key, nonce, associated data, and message. It is a binding property, not a privacy property, and standard AEAD never required it.
&lt;p&gt;The break that named the goal is vivid. For AES-GCM you can craft one ciphertext that decrypts to two different, meaningful plaintexts under two chosen keys, because its GHASH tag can be made to collide across keys.Dodis, Grubbs, Ristenpart, and Woodage called the hidden second message an &quot;invisible salamander,&quot; after a test image they smuggled past Facebook&apos;s abuse-reporting checks. The playful name marks a serious result: a reported ciphertext could be made to &quot;say&quot; different things to sender, receiver, and moderator. That broke Facebook&apos;s encrypted-message franking, the mechanism that lets a recipient report abuse in a way a moderator can verify [@salamanders2018].&lt;/p&gt;
&lt;p&gt;Three years later the same missing property was weaponized for a different goal. Len, Grubbs, and Ristenpart showed that a single ciphertext valid under many keys turns password guessing into a binary search: each query rules out half the candidate key space at once. These &lt;em&gt;partitioning oracles&lt;/em&gt; sped online attacks against Shadowsocks proxies and reached into key-management and end-to-end-encrypted settings [@partitioning2021]. Bellare and Hoang formalized the graded commitment hierarchy and proved that committing to the key alone (CMT-1) does not imply committing to the full context (CMT-4), so partial fixes leave a real gap [@bh2022]; Menda and colleagues then demonstrated concrete context-discovery and commitment attacks that exploit that gap in real settings [@menda2023].&lt;/p&gt;
&lt;p&gt;Generation 5 is not superseded. It is the live frontier, being written into standards right now, and we return to it in the state of the art.&lt;/p&gt;
&lt;p&gt;That is five turns of one wheel. It is worth seeing the wheel itself.&lt;/p&gt;

flowchart TD
    A[&quot;A definition fixes a goal and an adversary model&quot;] --&amp;gt; B[&quot;A deployment hands the attacker a capability outside that model&quot;]
    B --&amp;gt; C{&quot;Does the attack win inside the old game?&quot;}
    C --&amp;gt;|&quot;No, the game never granted that capability&quot;| D[&quot;A named, dated break follows in the wild&quot;]
    D --&amp;gt; E[&quot;The field writes a stronger definition that grants the capability&quot;]
    E --&amp;gt; A
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every arrow in that chain has the same shape. A definition names a goal and an adversary model; a deployment grants the attacker a capability outside that model; a dated break follows; a stronger definition absorbs the capability. Learning the definitions is learning to spot the next missing capability before an attacker does.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two tables make the pattern concrete. The first is the failure catalog, where each row is a construction that met a weaker target than its environment demanded.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Break&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Capability the model ignored&lt;/th&gt;
&lt;th&gt;Missing target&lt;/th&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bleichenbacher, then ROBOT&lt;/td&gt;
&lt;td&gt;1998, 2017&lt;/td&gt;
&lt;td&gt;RSA PKCS#1 v1.5 encryption&lt;/td&gt;
&lt;td&gt;Decryption and padding oracle&lt;/td&gt;
&lt;td&gt;IND-CCA2&lt;/td&gt;
&lt;td&gt;You cannot patch out a missing target&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;TLS CBC, MAC-then-Encrypt&lt;/td&gt;
&lt;td&gt;Timing decryption oracle&lt;/td&gt;
&lt;td&gt;AEAD (Encrypt-then-MAC)&lt;/td&gt;
&lt;td&gt;Composition order is provable, not a matter of taste&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;SSL 3.0 CBC, MAC-then-Encrypt&lt;/td&gt;
&lt;td&gt;Padding oracle via forced downgrade&lt;/td&gt;
&lt;td&gt;AEAD / IND-CCA2&lt;/td&gt;
&lt;td&gt;The adversary model must include downgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM nonce reuse&lt;/td&gt;
&lt;td&gt;ongoing&lt;/td&gt;
&lt;td&gt;AES-GCM AEAD&lt;/td&gt;
&lt;td&gt;Repeated (key, nonce) pair&lt;/td&gt;
&lt;td&gt;Nonce-misuse resistance&lt;/td&gt;
&lt;td&gt;Unique nonces are a load-bearing assumption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invisible salamanders&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;AES-GCM message franking&lt;/td&gt;
&lt;td&gt;A second valid key&lt;/td&gt;
&lt;td&gt;Key commitment (CMT)&lt;/td&gt;
&lt;td&gt;AEAD never promised one key per ciphertext&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partitioning oracles&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Non-committing AEAD&lt;/td&gt;
&lt;td&gt;One ciphertext valid under many keys&lt;/td&gt;
&lt;td&gt;Key commitment (CMT)&lt;/td&gt;
&lt;td&gt;Missing commitment enables key and password search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The second table lays out the targets themselves, in order of increasing strength, so you can name any construction as a goal bound to an adversary model.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Adversary model&lt;/th&gt;
&lt;th&gt;Strength relation&lt;/th&gt;
&lt;th&gt;Deployed instance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;IND-CPA&lt;/td&gt;
&lt;td&gt;Confidentiality&lt;/td&gt;
&lt;td&gt;Passive, chosen-plaintext&lt;/td&gt;
&lt;td&gt;Weakest confidentiality&lt;/td&gt;
&lt;td&gt;Building block only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IND-CCA2&lt;/td&gt;
&lt;td&gt;Confidentiality&lt;/td&gt;
&lt;td&gt;Active, adaptive decryption oracle&lt;/td&gt;
&lt;td&gt;Implies IND-CPA, equals NM-CCA2&lt;/td&gt;
&lt;td&gt;ML-KEM, RSA-OAEP, Cramer-Shoup (standard-model reference)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EUF-CMA&lt;/td&gt;
&lt;td&gt;Authenticity&lt;/td&gt;
&lt;td&gt;Adaptive chosen-message&lt;/td&gt;
&lt;td&gt;Base authenticity&lt;/td&gt;
&lt;td&gt;HMAC, ECDSA, Ed25519&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SUF-CMA&lt;/td&gt;
&lt;td&gt;Authenticity&lt;/td&gt;
&lt;td&gt;Adaptive chosen-message&lt;/td&gt;
&lt;td&gt;Strictly stronger than EUF-CMA&lt;/td&gt;
&lt;td&gt;Ed25519, HMAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD&lt;/td&gt;
&lt;td&gt;Confidentiality and integrity&lt;/td&gt;
&lt;td&gt;Active, nonce-respecting&lt;/td&gt;
&lt;td&gt;Combines IND-CPA and INT-CTXT&lt;/td&gt;
&lt;td&gt;AES-GCM, ChaCha20-Poly1305&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Committing AE&lt;/td&gt;
&lt;td&gt;Binds ciphertext to key and context&lt;/td&gt;
&lt;td&gt;Active, multi-key&lt;/td&gt;
&lt;td&gt;Strictly stronger than AEAD&lt;/td&gt;
&lt;td&gt;AWS Encryption SDK, Accordion (pending)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Five times we have said a scheme &quot;meets&quot; a target. It is time to say what &lt;em&gt;meets&lt;/em&gt; actually means, and why you should believe it.&lt;/p&gt;
&lt;h2&gt;5. What &quot;Provably Secure&quot; Actually Proves&lt;/h2&gt;
&lt;p&gt;We have been defining games. A game tells you what it means to win. It does not tell you that nobody can. So how do you prove a negative, that no efficient adversary wins, without enumerating every possible attack? The answer is the single idea that unifies everything in this guide: a proof by reduction.&lt;/p&gt;

To prove a scheme meets a target, assume some problem is hard (integer factoring, the decisional Diffie-Hellman problem, Module-LWE) or that a lower-level primitive is secure. Then exhibit an efficient algorithm, the reduction, that turns *any* adversary who wins the security game into a solver for that hard problem. If the problem really is hard, no such solver exists, so no such adversary exists either. Security becomes a conditional theorem: break the scheme and you have broken the assumption.
&lt;p&gt;Read that structure carefully, because it is doing something subtle. It never claims the scheme is unbreakable. It claims something you can actually establish: that breaking the scheme is at least as hard as a problem the world&apos;s cryptanalysts have failed to crack for decades. The proof relocates all the risk onto one clearly labeled assumption, where everyone can study it. The hard problems themselves (factoring, discrete logarithms, lattice problems) are the subject of later parts of this series; here they are simply the anchors a reduction reaches for.&lt;/p&gt;

flowchart LR
    A[&quot;Efficient adversary A wins the security game&quot;] --&amp;gt; R[&quot;Reduction R runs A as a black box&quot;]
    R --&amp;gt; S[&quot;R becomes an algorithm that solves the hard problem&quot;]
    S --&amp;gt; X[&quot;But the problem is assumed hard, so no such solver exists&quot;]
    X --&amp;gt; Z[&quot;Therefore no efficient adversary A can exist&quot;]
&lt;p&gt;This is the only known way to make &quot;secure&quot; falsifiable, and it &lt;em&gt;composes&lt;/em&gt;, which is what makes it practical. Bellare and Namprempre&apos;s result that IND-CPA plus INT-CTXT implies IND-CCA is itself a reduction you can bolt onto others: prove your encryption IND-CPA and your MAC strongly unforgeable, glue them Encrypt-then-MAC, and the channel&apos;s IND-CCA security follows with no fresh from-scratch proof [@bn2000] [@bonehshoup]. Large guarantees get assembled from small proven parts.&lt;/p&gt;

A security proof does not promise that nobody can break the scheme. It promises that breaking it is as hard as a problem we have collectively failed to solve for decades.
&lt;p&gt;But a proof is only as good as two things: the assumption it rests on, and how &lt;em&gt;tightly&lt;/em&gt; it reduces.&lt;/p&gt;

A reduction is tight if the solver it builds is almost as effective as the adversary it consumes, running in similar time with similar success probability. It is loose if it loses a factor, for instance a multiple of the number of queries or users. A reduction that loses a factor $L$ means a scheme advertised at $k$-bit security actually delivers only about $k - \log_2 L$ bits at the chosen parameters. Looseness is not academic; you pay it back in larger keys and slower handshakes.
&lt;p&gt;Neal Koblitz and Alfred Menezes pressed exactly this point in their &quot;Another Look&quot; critique: many published reductions are loose, or rest on bespoke, barely-studied assumptions, and calling a scheme &quot;provably secure&quot; invites readers to hear &quot;unbreakable&quot; when the honest claim is much narrower [@km2007]. We take up their argument in full when we reach the limits. For now it is enough to know that the tightness of a proof is an operational number, not a footnote.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &quot;Provably secure&quot; means &lt;em&gt;conditionally&lt;/em&gt; secure: secure relative to a named hardness assumption and a named adversary model, and only as tightly as the reduction allows. It converts a vague hope into a precise, falsifiable claim whose single point of failure is written on the label. That is far more than folklore offers, and far less than &quot;unbreakable.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This method is not a museum piece. It is the machine still producing the definitions that shipped last year. Here is what it built between 2024 and 2026.&lt;/p&gt;
&lt;h2&gt;6. The Definitions That Shipped: 2024 to 2026&lt;/h2&gt;
&lt;p&gt;A quantum-resistant standard published in 2024 is built directly on the 1984, 1991, and 1999 sequence: an IND-CPA scheme, upgraded to IND-CCA2, by a transform invented to plumb exactly that gap.&lt;/p&gt;
&lt;p&gt;On August 13, 2024, NIST published FIPS 203, standardizing ML-KEM, a key-encapsulation mechanism whose security rests on the hardness of Module-LWE and which is, in the standard&apos;s own words, believed to be secure even against adversaries who possess a quantum computer [@fips203].&lt;/p&gt;

A KEM is the modern shape of public-key confidentiality. Rather than encrypting a chosen message, encapsulation generates a fresh random shared secret together with a ciphertext that carries it; decapsulation recovers the secret from the ciphertext using the private key. The shared secret then keys a symmetric AEAD channel. The target you want from a KEM is IND-CCA2, because an active network attacker can submit chosen ciphertexts to decapsulation.
&lt;p&gt;ML-KEM reaches IND-CCA2 the way Fujisaki-Okamoto transforms do: start from an IND-CPA lattice scheme, then make decapsulation &lt;em&gt;recompute and compare&lt;/em&gt;. It derives the encryption randomness deterministically from the message, re-encrypts on the way in, and on any ciphertext that does not re-encrypt exactly returns a pseudorandom key instead of the real one (implicit rejection), so a decapsulation failure is invisible to the caller [@fips203] [@fo1999]. That single move strips out the malleability an adaptive attacker would exploit, turning a passive-secure scheme into an active-secure one.&lt;/p&gt;
&lt;p&gt;The transform&apos;s soundness in both the random-oracle and quantum-random-oracle settings is the subject of a careful modular analysis by Hofheinz, Hövelmanns, and Kiltz, which also shows the quantum bound is not tight [@hhk2017].The Quantum Random Oracle Model (QROM) lets the adversary query the idealized hash in superposition, which is what a real quantum attacker holding the hash&apos;s public code could do. Proving the FO transform secure in the QROM is harder than in the classical setting and currently costs a looser bound. The full story belongs to a later post-quantum installment of this series.&lt;/p&gt;
&lt;p&gt;The price of the strong target is not CPU, it is bandwidth. Google reports that an ML-KEM key exchange transmits about 1 KB per peer against 32 bytes for X25519, an over-thirty-fold increase that splits the TLS ClientHello across packets and adds roughly 4 percent to median desktop handshake latency [@chrome2024].&lt;/p&gt;
&lt;p&gt;That cost is being paid at scale regardless. Cloudflare reported that nearly 2 percent of all TLS 1.3 connections already used post-quantum key exchange in early 2024 [@cloudflare2024], Signal&apos;s PQXDH was the first large-scale deployment of post-quantum key establishment in messaging [@signal_pqxdh], and Apple&apos;s iMessage PQ3 layers ongoing ML-KEM rekeying on top for what it calls Level 3 security [@apple_pq3].&lt;/p&gt;

ML-KEM ships in *hybrid* mode, combining a classical key exchange such as X25519 with the post-quantum one so a break of either component alone is survivable. The driver is &quot;harvest now, decrypt later&quot;: an adversary recording encrypted traffic today could decrypt it once a quantum computer arrives, so confidentiality that must outlive the next decade needs the post-quantum target now. The Windows-side rollout is covered in [the thirty-year migration that ships in a pip install](/blog/the-thirty-year-migration-ships-in-a-pip-install-how-post-qu) and [post-quantum cryptography on Windows](/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati).
&lt;p&gt;The same AEAD target ships in three deployed instances that differ not in what they guarantee but in the deployment reality they suit. All three hit AEAD; the discriminating axis is how they behave when a nonce repeats [@rfc8439] [@rfc8452] [@sp800_38d].&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;AES-GCM-SIV&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Target met&lt;/td&gt;
&lt;td&gt;AEAD (IND-CPA + INT-CTXT)&lt;/td&gt;
&lt;td&gt;AEAD&lt;/td&gt;
&lt;td&gt;AEAD plus nonce-misuse resistance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structure&lt;/td&gt;
&lt;td&gt;Counter mode plus GHASH&lt;/td&gt;
&lt;td&gt;ChaCha20 stream plus Poly1305&lt;/td&gt;
&lt;td&gt;Tag derived from plaintext, then counter mode, via POLYVAL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passes&lt;/td&gt;
&lt;td&gt;One pass, online, parallelizable&lt;/td&gt;
&lt;td&gt;One pass, online&lt;/td&gt;
&lt;td&gt;Two passes, not online&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best hardware&lt;/td&gt;
&lt;td&gt;AES-NI and carryless multiply&lt;/td&gt;
&lt;td&gt;none needed, fast in software&lt;/td&gt;
&lt;td&gt;AES-NI and carryless multiply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce reuse&lt;/td&gt;
&lt;td&gt;Catastrophic (Joux forgery)&lt;/td&gt;
&lt;td&gt;Catastrophic (keystream reuse)&lt;/td&gt;
&lt;td&gt;Graceful, leaks only equality of inputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-key limit&lt;/td&gt;
&lt;td&gt;about $2^{32}$ invocations at a 96-bit nonce&lt;/td&gt;
&lt;td&gt;same nonce-uniqueness requirement&lt;/td&gt;
&lt;td&gt;far higher tolerance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key-committing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best suited for&lt;/td&gt;
&lt;td&gt;TLS and QUIC with AES hardware&lt;/td&gt;
&lt;td&gt;mobile and embedded, no AES hardware&lt;/td&gt;
&lt;td&gt;stateless senders, key wrap, uncertain nonces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spec&lt;/td&gt;
&lt;td&gt;NIST SP 800-38D&lt;/td&gt;
&lt;td&gt;RFC 8439&lt;/td&gt;
&lt;td&gt;RFC 8452&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; Pick AES-256-GCM when you have AES hardware and can guarantee unique nonces. Pick ChaCha20-Poly1305 when you cannot count on AES hardware, as on many mobile and embedded targets [@rfc8439]. Pick AES-GCM-SIV when nonce uniqueness is at risk, for example stateless senders, key wrapping, or anything that can be cloned or rolled back [@rfc8452]. None of the three commits to its key, so add that property separately if you need it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The newest target on this list, key commitment from Generation 5, is being written into standards as you read this. In May 2025 the IRTF&apos;s Crypto Forum Research Group published RFC 9771, which gives consensus, game-based names for Key Commitment and Full Commitment alongside related properties such as nonce hiding, multi-user security, and quantum security. A spec can finally &lt;em&gt;name&lt;/em&gt; the property it needs instead of gesturing at it [@rfc9771].&lt;/p&gt;
&lt;p&gt;NIST is scoping an actual mode. Its Accordion effort targets a tweakable, variable-input-length strong pseudorandom permutation with a reduction proof and key commitment as a stated requirement, laid out across NIST IR 8537 in 2024 [@ir8537], the requirements report NIST IR 8552 in April 2025 [@ir8552], and a pre-draft call for comments on SP 800-197A that proposes three modes (Acc128, Acc256, and a beyond-birthday variant) built from the HCTR2 technique [@sp800197a] [@hctr2] [@nist_news_accordion].&lt;/p&gt;
&lt;p&gt;You do not have to wait for it: the AWS Encryption SDK already enforces key commitment through a commitment policy, a production deployment that predates any standard [@aws_esdk].&lt;/p&gt;
&lt;p&gt;Why does commitment need a whole new mode rather than a bigger tag? Partly because a binding guarantee inherits the birthday bound: $n$-bit commitment security needs roughly a $2n$-bit commitment, about 256 bits for 128-bit security, so a standard 128-bit tag is undersized for the job [@bh2022]. And breaking commitment is a &lt;em&gt;binding&lt;/em&gt; problem, which the sketch below shows is a weapon rather than a curiosity.&lt;/p&gt;
&lt;p&gt;{`
// Intuition only, not a real attack. A non-committing &quot;AEAD&quot; whose verifier
// happens to accept a crafted ciphertext under MANY candidate keys.
function accepts(candidateKey, craftedCiphertext) {
  // In a committing scheme this would be true for at most one key.
  // Here the attacker built a ciphertext valid for a whole set of keys.
  return craftedCiphertext.validKeys.includes(candidateKey);
}&lt;/p&gt;
&lt;p&gt;// The attacker packs &quot;half the remaining candidates&quot; into one ciphertext.
function craftFor(keySubset) {
  return { validKeys: keySubset };
}&lt;/p&gt;
&lt;p&gt;let candidates = [];
for (let i = 0; i &amp;lt; 1024; i++) candidates.push(&quot;key#&quot; + i);&lt;/p&gt;
&lt;p&gt;let queries = 0;
while (candidates.length &amp;gt; 1) {
  const half = candidates.slice(0, Math.floor(candidates.length / 2));
  const ct = craftFor(half);
  queries++;
  // One online guess rules out half the space, committing or not.
  candidates = accepts(candidates[0], ct) ? half : candidates.slice(half.length);
}
console.log(&quot;Recovered key:&quot;, candidates[0], &quot;in&quot;, queries, &quot;online queries over 1024 candidates.&quot;);
`}&lt;/p&gt;
&lt;p&gt;Ten queries to search a thousand keys, because each ciphertext answers &quot;is your key in this set?&quot; A committing scheme makes that impossible: one ciphertext accepts under at most one key, so each guess can only ever eliminate a single candidate.&lt;/p&gt;
&lt;p&gt;All of this, classical and post-quantum, symmetric and public-key, is proven in one of two settings, under one of two styles of definition. Which one you trust changes what the proof is worth.&lt;/p&gt;
&lt;h2&gt;7. Two Styles of Definition, Two Places to Prove Them&lt;/h2&gt;
&lt;p&gt;Two long-running tensions run underneath every definition so far, and neither is resolved. The first is &lt;em&gt;how&lt;/em&gt; you state a goal. The second is &lt;em&gt;where&lt;/em&gt; you prove it.&lt;/p&gt;
&lt;p&gt;Everything up to this point has used indistinguishability games: define two worlds and demand that no efficient adversary can tell them apart. That style fits standalone primitives beautifully. But there is a second style, simulation-based (or real-versus-ideal) security, which asks a different question: is there a simulator that, with no access to the secrets, can produce a transcript indistinguishable from a real interaction? If so, the real protocol leaks nothing the ideal one would not, because anything the adversary learns it could have generated itself.&lt;/p&gt;
&lt;p&gt;Simulation is the natural language for zero-knowledge proofs, secure multiparty computation, and composable settings, where the strongest framework is Universal Composability.Universal Composability, due to Ran Canetti [@canetti2001uc], proves a protocol secure even when arbitrarily many copies of it and other protocols run concurrently, by comparing the real execution to an ideal functionality. It is the strongest simulation-based framework, and the price of that strength is that UC-secure protocols are harder to build. We only point at it here. Simulation generally implies the matching indistinguishability notion but not the reverse, so the two are complementary lenses rather than rivals [@bonehshoup]. For these simulation-based definitions in deployed systems, see &lt;a href=&quot;https://paragmali.com/blog/the-age-gate-that-doesnt-know-your-age-how-anonymous-credent&quot; rel=&quot;noopener&quot;&gt;the age gate that does not know your age&lt;/a&gt; and &lt;a href=&quot;https://paragmali.com/blog/direct-anonymous-attestation-the-zero-knowledge-proof-alread&quot; rel=&quot;noopener&quot;&gt;direct anonymous attestation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The second tension is the setting in which a proof lives, and it is the one most often misunderstood.&lt;/p&gt;

The random oracle model proves a scheme secure while treating a hash function as if it were a truly random function that everyone can query but nobody can predict. It is a *proof setting*, a modeling assumption you adopt in order to prove a goal such as IND-CCA2, and it sits beside the standard model, which assumes only that standard problems are hard. The random oracle model is emphatically **not** a security definition. It never occupies the same slot as IND-CPA or EUF-CMA; it is where you prove those goals, not what you prove.
&lt;p&gt;Bellare and Rogaway introduced it in 1993 precisely because it buys efficient, strong proofs for practical schemes: OAEP, PSS, and the FO transform behind ML-KEM all live here [@br1993]. The standard model asks for less faith and is therefore more trustworthy when you can afford it.&lt;/p&gt;
&lt;p&gt;Cramer and Shoup proved in 1998 that IND-CCA2 is achievable in the standard model, under the decisional Diffie-Hellman assumption, with no random oracle at all, settling that the strongest confidentiality target is not hostage to the heuristic [@cramershoup1998]. The two settings coexist as a trade of assurance against efficiency, and the post-quantum era stacks the quantum random oracle model on top.Beyond the deployed targets in this guide, the literature refines these notions further along both axes. RCCA (Canetti-Krawczyk-Nielsen) [@ckn2003rcca] and the related gCCA and mCCA variants relax IND-CCA2 for settings such as re-randomizable encryption. On the hash-function side, UCE is a standard-model assumption for discharging hash-based proofs without a random oracle, while indifferentiability is an ideal-model framework showing that a concrete construction built from an ideal primitive behaves like a random oracle, the tool that justifies random-oracle-style design rather than replacing it. These academic refinements are surveyed in later parts of this series; here they are pointers only.&lt;/p&gt;
&lt;p&gt;Here is the whole thing in one picture: two proof settings, the random oracle model and the standard model, with the major schemes sorted by where each one is proven.&lt;/p&gt;

flowchart TD
    subgraph ROM[&quot;Random oracle model&quot;]
        O[&quot;RSA-OAEP (IND-CCA2) and RSA-PSS (EUF-CMA), strong game-based goals proven in the ROM&quot;]
        K[&quot;ML-KEM, IND-CCA2 KEM via the FO transform in the quantum ROM&quot;]
    end
    subgraph STD[&quot;Standard model&quot;]
        CS[&quot;Cramer-Shoup, IND-CCA2 under decisional Diffie-Hellman&quot;]
        UC[&quot;Universal-composability zero knowledge, simulation based&quot;]
    end

The random oracle model and the standard model are not rival definitions of secure. They are two settings in which you prove the same goal.
&lt;p&gt;Which raises the uncomfortable question the next section confronts. If the random oracle model is only a heuristic, exactly how much is a random-oracle proof worth?&lt;/p&gt;
&lt;h2&gt;8. What a Proof Cannot Promise&lt;/h2&gt;
&lt;p&gt;Now the uncomfortable part. A scheme can be provably secure in the random oracle model and broken under &lt;em&gt;every&lt;/em&gt; real hash function you could ever plug in.&lt;/p&gt;
&lt;p&gt;That is not a worry, it is a theorem. Canetti, Goldreich, and Halevi constructed signature and encryption schemes that carry airtight proofs in the random oracle model yet become insecure the moment the oracle is replaced by any concrete, efficiently computable hash [@cgh2004]. Their construction is deliberately contrived, engineered to detect when its oracle has a short description and to misbehave when it does. But contrived is enough: it proves that a random-oracle proof cannot, in general, guarantee security once instantiated. The proof is real; the security does not always survive contact with a real hash.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Treat a random-oracle proof as strong evidence, not as a certificate. It says a scheme has no &lt;em&gt;structural&lt;/em&gt; weakness of the kind the proof rules out, which is reassuring for OAEP, PSS, and ML-KEM. It does not say that every hash instantiation is safe, because we know constructions where none is [@cgh2004]. Calibrate your trust to the setting the proof actually used.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The honest reading is not to discard random-oracle proofs. Almost everything efficient we deploy has one, and the counterexamples above are artificial rather than schemes anyone would design. The honest reading is to calibrate. That is also the spirit of Koblitz and Menezes&apos;s &quot;Another Look&quot; critique, which is often misread as an attack on provable security and is better understood as a discipline for reading proofs [@km2007].&lt;/p&gt;

Before you trust a &quot;provably secure&quot; label, ask four questions. What exactly are the goal and adversary model, stated in full? What assumption does the reduction rest on, and how well studied is it? How tight is the reduction, and what security does it actually deliver at the deployed parameters? And in which setting does the proof live, random oracle or standard model? None of these questions refutes a proof. Each tells you what the proof is worth [@km2007].
&lt;p&gt;Even celebrated proofs need repair. The original OAEP argument had a gap in its chosen-ciphertext proof that went unnoticed for years before it was found and fixed for the RSA case.Victor Shoup showed in 2001 that the original OAEP proof did not establish IND-CCA2 in the generality claimed. The result was recovered for RSA specifically, but only after the community re-examined a proof everyone had trusted [@shoup2001]. A proof is an artifact, and artifacts can contain bugs. If the proofs themselves can carry errors, &quot;it has a proof&quot; is the beginning of due diligence, not the end.&lt;/p&gt;
&lt;p&gt;Underneath all of it sits the deepest limit of the field. Essentially every computational definition in this guide rests on an &lt;em&gt;unproven&lt;/em&gt; assumption: that factoring is hard, that discrete logarithms are hard, that Module-LWE is hard. A genuine, unconditional proof that any of them is hard would resolve questions on the order of P versus NP, which nobody can do [@bonehshoup].&lt;/p&gt;
&lt;p&gt;So &quot;provably secure&quot; always means &lt;em&gt;conditionally&lt;/em&gt; secure. There is no unconditional computational security above the Shannon bound we started with. The one-time pad remains the only thing proven secure against an unbounded adversary, and it is the one thing we cannot afford to use.&lt;/p&gt;
&lt;p&gt;None of this refutes the method. It maps the edge of what is settled. And the edge is exactly where today&apos;s research is moving.&lt;/p&gt;
&lt;h2&gt;9. Where the Definitions Are Still Moving&lt;/h2&gt;
&lt;p&gt;The chain is not closed. Each open problem below is a place where the adversary model is still being enlarged, which is another way of saying the thesis is a roadmap rather than a finished map.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standardizing committing AEAD.&lt;/strong&gt; The deployed triad commits to nothing, yet message franking, multi-recipient encryption, key rotation, key-management services, and password-based encryption all need it. The Accordion effort is scoping the mode now, but whether the default becomes key commitment (CMT-1) or full context commitment (CMT-4) is still open, and the two differ in both cost and in what they defend [@ir8537] [@sp800197a].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tight, deployment-honest reductions.&lt;/strong&gt; Loose reductions force larger keys than the assumption alone requires, and the gap is worst in the multi-user setting, where one attacker faces millions of sessions at once. The Section 5 rule makes the cost concrete: if a reduction loses a factor equal to a billion ($2^{30}$) sessions, a headline 128-bit guarantee shrinks to about 98 bits at the deployed parameters.RFC 9771 names multi-user security as a distinct property precisely because a bound that is comfortable for one user can dissolve across a billion of them. Tight multi-user proofs are an active target, not a solved problem [@rfc9771]. How much the community should discount a loose proof remains contested rather than settled [@km2007].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-quantum definitions in the quantum random oracle model.&lt;/strong&gt; What &quot;IND-CCA2 against a quantum adversary&quot; actually costs is still being pinned down. ML-KEM ships with a proof whose bound is not tight, and the right model for a superposition decryption oracle is unsettled [@hhk2017] [@rfc9771].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The random-oracle-versus-standard-model question.&lt;/strong&gt; Canetti, Goldreich, and Halevi proved the random oracle model is in principle uninstantiable, yet the efficient schemes running the internet are proved there, and Cramer and Shoup showed the standard model can reach the same target at higher cost [@cgh2004] [@cramershoup1998]. There is no consensus on how much to trust the heuristic; the honest position is calibrated trust, not resolution.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every entry on this list is a place where a future deployment could hand an attacker a capability no current definition names, exactly as message franking did in 2018. That is why the discipline is not &quot;memorize the acronyms.&quot; It is a habit you apply before you choose a primitive.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is exactly why mastery here is not the acronyms. It is a workflow, and it fits on one screen.&lt;/p&gt;
&lt;h2&gt;10. The Field Guide: Choose the Target Before the Primitive&lt;/h2&gt;
&lt;p&gt;Everything in this guide collapses into one workflow you can run in a design review this afternoon: name what the adversary is allowed to do, pick the target that survives that adversary, and only then choose a concrete mode and its parameters. The tree below is the whole method.&lt;/p&gt;

flowchart TD
    Q1{&quot;Can the adversary observe decryption or accept/reject?&quot;}
    Q1 --&amp;gt;|&quot;Yes&quot;| CCA[&quot;Require IND-CCA2, never IND-CPA-only&quot;]
    Q1 --&amp;gt;|&quot;No, just encrypting a channel&quot;| AEAD[&quot;Use AEAD by default&quot;]
    AEAD --&amp;gt; Q2{&quot;Can you guarantee unique nonces?&quot;}
    Q2 --&amp;gt;|&quot;Yes, with AES hardware&quot;| GCM[&quot;AES-256-GCM, 96-bit random nonce, cap under 2 to the 32 per key&quot;]
    Q2 --&amp;gt;|&quot;No&quot;| SIV[&quot;AES-GCM-SIV, nonce-misuse resistant&quot;]
    CCA --&amp;gt; Q3{&quot;Must one ciphertext bind to one key?&quot;}
    Q3 --&amp;gt;|&quot;Yes&quot;| CMT[&quot;Require committing AE, CMT-4 or a vetted transform&quot;]
&lt;p&gt;The decision rules, stated the way you would apply them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;If any adversary can observe decryption or accept/reject behavior, require IND-CCA2, never IND-CPA-only.&lt;/strong&gt; That is the Bleichenbacher and ROBOT lesson: a reacting server is a decryption oracle [@bleichenbacher1998].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you are encrypting a channel, use AEAD by default.&lt;/strong&gt; AES-256-GCM with 96-bit random nonces and a hard message cap well under $2^{32}$ per key [@sp800_38d]; ChaCha20-Poly1305 where there is no AES hardware [@rfc8439]; AES-GCM-SIV when nonce uniqueness cannot be guaranteed [@rfc8452].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you must use RSA encryption at all, use RSA-OAEP, never PKCS#1 v1.5,&lt;/strong&gt; or drop RSA key transport entirely as TLS 1.3 did [@br1994oaep] [@bleichenbacher1998].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you need authenticity only, use HMAC or Ed25519 for EUF-CMA,&lt;/strong&gt; and require SUF-CMA wherever a signature or token must be unique [@bonehshoup] [@decker2014]. Ed25519 delivers SUF-CMA only when verification enforces the RFC 8032 canonical-S range check, rejecting any scalar $S$ outside $[0, L)$; libraries that skip it stay malleable [@rfc8032].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If a ciphertext must bind to exactly one key or context, require a committing AE:&lt;/strong&gt; a CMT-4 construction or a vetted committing transform, never the raw triad. This is the multi-recipient, key-rotation, franking, password-based, and key-management case [@rfc9771] [@aws_esdk] [@bh2022].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you need post-quantum key establishment, use ML-KEM (FIPS 203) in hybrid mode&lt;/strong&gt; with a classical KEM during the transition [@fips203].&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment condition&lt;/th&gt;
&lt;th&gt;Required target&lt;/th&gt;
&lt;th&gt;Concrete choice and parameters&lt;/th&gt;
&lt;th&gt;Anti-pattern it prevents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Adversary sees decryption or accept/reject&lt;/td&gt;
&lt;td&gt;IND-CCA2&lt;/td&gt;
&lt;td&gt;ML-KEM, RSA-OAEP, or an AEAD channel&lt;/td&gt;
&lt;td&gt;Bleichenbacher, ROBOT, POODLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel, unique nonces, AES hardware&lt;/td&gt;
&lt;td&gt;AEAD&lt;/td&gt;
&lt;td&gt;AES-256-GCM, 96-bit random nonce, cap under $2^{32}$ per key&lt;/td&gt;
&lt;td&gt;Unauthenticated or malleable ciphertext&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel, no AES hardware&lt;/td&gt;
&lt;td&gt;AEAD&lt;/td&gt;
&lt;td&gt;ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;Slow or side-channel-prone software AES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce uniqueness not guaranteed&lt;/td&gt;
&lt;td&gt;AEAD plus misuse resistance&lt;/td&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;Joux forbidden-attack forgery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy RSA encryption required&lt;/td&gt;
&lt;td&gt;IND-CCA2&lt;/td&gt;
&lt;td&gt;RSA-OAEP, never PKCS#1 v1.5&lt;/td&gt;
&lt;td&gt;Padding-oracle decryption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature or token must be unique&lt;/td&gt;
&lt;td&gt;SUF-CMA&lt;/td&gt;
&lt;td&gt;Ed25519&lt;/td&gt;
&lt;td&gt;Signature-malleability confusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One ciphertext must bind to one key&lt;/td&gt;
&lt;td&gt;Committing AE (CMT)&lt;/td&gt;
&lt;td&gt;Committing transform or Accordion mode&lt;/td&gt;
&lt;td&gt;Invisible salamanders, partitioning oracles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confidentiality must outlive a quantum computer&lt;/td&gt;
&lt;td&gt;IND-CCA2, post-quantum&lt;/td&gt;
&lt;td&gt;Hybrid X25519 plus ML-KEM&lt;/td&gt;
&lt;td&gt;Harvest-now-decrypt-later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Every rule above has a mirror image, a mistake that turns up in code review after code review. Here is the misuse catalog, each entry the negation of a rule you just read.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nonce reuse under AES-GCM or ChaCha20-Poly1305,&lt;/strong&gt; the single most destructive AEAD error, because it is a universal forgery via the Joux attack rather than a slow degradation [@joux_gcm]. Clones, snapshots, and stateless senders are the usual sources; reach for AES-GCM-SIV or a strictly managed random nonce with a per-key cap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The wrong composition order,&lt;/strong&gt; MAC-then-Encrypt or Encrypt-and-MAC; only Encrypt-then-MAC or a dedicated AEAD is generically safe, and history already paid for this with POODLE and Lucky Thirteen [@bn2000].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treating AES-GCM as key-committing.&lt;/strong&gt; It commits to nothing; assuming otherwise is the salamander and partitioning-oracle bug [@salamanders2018] [@partitioning2021].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unbounded messages per key,&lt;/strong&gt; silently walking past the birthday ceiling that GCM&apos;s 96-bit nonce imposes [@sp800_38d].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verifying a signature without binding its context,&lt;/strong&gt; which EUF-CMA never promised to cover and which invites cross-protocol confusion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trusting a random-oracle proof as a standard-model guarantee,&lt;/strong&gt; when it is a heuristic to be calibrated rather than a certificate [@cgh2004].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For where these targets are actually invoked as concrete API calls on Windows, from BCrypt to key storage providers, see &lt;a href=&quot;https://paragmali.com/blog/cng-architecture-bcrypt-ncrypt-ksps&quot; rel=&quot;noopener&quot;&gt;the CNG architecture behind BCrypt, NCrypt, and KSPs&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; State your deployment&apos;s adversary model first, choose the matching target second, and pick the concrete primitive and its parameters last. Every rule and every anti-pattern above is a corollary of that one habit.&lt;/p&gt;
&lt;/blockquote&gt;

Bleichenbacher and ROBOT: require IND-CCA2 and drop PKCS#1 v1.5. Lucky Thirteen and POODLE: use an AEAD with Encrypt-then-MAC, not MAC-then-Encrypt CBC. AES-GCM nonce reuse: cap messages per key and use AES-GCM-SIV where uniqueness is uncertain. Invisible salamanders and partitioning oracles: require a committing (CMT) mode. Every break in the catalog was preventable by choosing the target the environment demanded before choosing the primitive.
&lt;p&gt;One habit generated every rule and every warning on this page: name the adversary first.&lt;/p&gt;
&lt;h2&gt;11. Misconceptions, Named and Corrected&lt;/h2&gt;
&lt;p&gt;The fastest way to internalize the thesis is to correct the folklore it contradicts.&lt;/p&gt;


Only against a *passive* adversary who never sees how the receiver reacts to a ciphertext. The moment a server decrypts attacker-supplied input and behaves observably differently on success and failure, you need IND-CCA2. That gap is exactly what Bleichenbacher&apos;s attack exploited [@bleichenbacher1998].


Only under a strict nonce-uniqueness contract and a per-key message cap. Reusing a (key, nonce) pair is a universal forgery, not a minor leak [@joux_gcm]; NIST caps GCM at about $2^{32}$ invocations per key at a 96-bit nonce [@sp800_38d]; and GCM commits to no key, so it is unsafe wherever commitment is assumed [@salamanders2018].


It is strong evidence, not a guarantee. Canetti, Goldreich, and Halevi built schemes provably secure in the random oracle model that are insecure under every real hash [@cgh2004]. Trust a random-oracle proof, but calibrate that trust to the setting it used.


No. Confidentiality and integrity are different goals; a one-time pad has perfect confidentiality and zero integrity, since flipping a ciphertext bit flips the plaintext bit undetected. Use an AEAD, and compose Encrypt-then-MAC if you must build it yourself [@bn2000].


No. SUF-CMA also forbids producing a *new* valid signature on an already-signed message, which EUF-CMA permits. The gap bites whenever a signature or ciphertext is treated as a unique token, as Bitcoin transaction malleability showed [@decker2014] [@bonehshoup].


No. It means *conditionally* secure, relative to a named hardness assumption and adversary model, and only as tightly as the reduction allows. A loose reduction delivers fewer bits of security than the headline number suggests [@km2007].


No. It is a proof setting, a heuristic that idealizes a hash as a random function, and it sits beside the standard model, not beside goals like IND-CPA or EUF-CMA. Cramer and Shoup showed the strongest confidentiality target is reachable without it [@br1993] [@cramershoup1998].

&lt;h2&gt;The Contract Behind the Cipher&lt;/h2&gt;
&lt;p&gt;We began with a puzzle: Daniel Bleichenbacher decrypted SSL sessions without ever breaking RSA. The answer runs through every section since. &quot;Secure&quot; is not a property an algorithm has. It is a contract, a goal bound to an adversary model, and a construction honors that contract only against the adversary the contract names.&lt;/p&gt;
&lt;p&gt;Bleichenbacher, POODLE, Lucky Thirteen, AES-GCM nonce reuse, invisible salamanders, partitioning oracles: not one of them was weak math. Each was a construction meeting a weaker target than its deployment&apos;s adversary demanded, after the environment handed the attacker an oracle, a repeated nonce, or a second key the proof never granted.&lt;/p&gt;
&lt;p&gt;That is why the vocabulary matters. IND-CPA and IND-CCA2 name what confidentiality means against a passive versus an active adversary. EUF-CMA and SUF-CMA name authenticity with and without malleability. AEAD names their union; committing AE names what AEAD forgot. The random oracle model and the standard model name where a proof lives, and a reduction names the single assumption on which the whole guarantee rests. Learn to read a construction as goal plus adversary model plus proof setting, and the failure catalog stops being a list of surprises and becomes a list of predictions.&lt;/p&gt;
&lt;p&gt;This is Part 1 of a field guide, and it deliberately taught no primitive in depth. That was the point. The remaining parts of this series will build RSA, Diffie-Hellman, elliptic curves, lattices, and hash functions, and every one of them will be measured against exactly the targets defined here. The math will change from part to part. The contract will not.&lt;/p&gt;
&lt;p&gt;The RSA math was always fine. What the field lacked in 1998 was the habit of naming the adversary before trusting the primitive. Now you have it.&lt;/p&gt;

State the adversary model first, choose the matching target second, and pick the primitive last. That one habit would have prevented every break in this article.
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;secure-against-whom-security-definitions&quot; keyTerms={[
  { term: &quot;IND-CPA&quot;, definition: &quot;Indistinguishability under chosen-plaintext attack; confidentiality against a passive adversary.&quot; },
  { term: &quot;IND-CCA2&quot;, definition: &quot;Adaptive chosen-ciphertext security; confidentiality against an adversary with a decryption oracle before and after the challenge.&quot; },
  { term: &quot;EUF-CMA&quot;, definition: &quot;Existential unforgeability under chosen-message attack; no forgery on a new message.&quot; },
  { term: &quot;SUF-CMA&quot;, definition: &quot;Strong unforgeability; no new signature even on an already-signed message.&quot; },
  { term: &quot;AEAD&quot;, definition: &quot;Authenticated encryption with associated data; confidentiality plus ciphertext integrity over a nonce interface.&quot; },
  { term: &quot;INT-CTXT&quot;, definition: &quot;Ciphertext integrity; the adversary cannot produce any valid ciphertext the encryptor never output.&quot; },
  { term: &quot;Key commitment&quot;, definition: &quot;A ciphertext binds to exactly one key or context; graded CMT-1 through CMT-4.&quot; },
  { term: &quot;Random oracle model&quot;, definition: &quot;A proof setting that idealizes a hash as a truly random function; not a security definition.&quot; },
  { term: &quot;Reduction&quot;, definition: &quot;A proof that turns any adversary against the scheme into a solver for a hard problem.&quot; },
  { term: &quot;Nonce-misuse resistance&quot;, definition: &quot;Graceful degradation when a nonce repeats, as in AES-GCM-SIV.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>security-definitions</category><category>authenticated-encryption</category><category>provable-security</category><category>ind-cca2</category><category>aead</category><category>post-quantum-cryptography</category><category>protocol-design</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>