<?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: protocol-design</title><description>Posts tagged protocol-design.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sat, 25 Jul 2026 08:57:56 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/protocol-design/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Signature Was Valid. The Message Was Forged: A Field Guide to Serialization and Canonicalization Attacks</title><link>https://paragmali.com/blog/the-signature-was-valid-the-message-was-forged-a-field-guide/</link><guid isPermaLink="true">https://paragmali.com/blog/the-signature-was-valid-the-message-was-forged-a-field-guide/</guid><description>Why the bytes you sign matter more than the algorithm: hash length extension, ASN.1/DER forgery, JWT alg confusion, and XML signature wrapping -- and the fix.</description><pubDate>Wed, 08 Jul 2026 03:48:01 GMT</pubDate><content:encoded>
A signature, a MAC, and a hash make exactly one promise: they bind a specific string of *bytes* -- never their meaning. Every attack in this guide (hash length extension, ASN.1/DER signature forgery, JWT algorithm confusion, XML signature wrapping) leaves the algorithm flawless and pries open the gap between the bytes that were authenticated and the bytes the application acts on. Each does it with one of just two moves: an **ambiguous encoding**, or an **attacker-influenced binding**. The cure is a three-pattern discipline, not a stronger algorithm: sign the exact bytes the consumer parses; when you must sign structure, use an encoding with exactly one valid form; and bind type, context, and algorithm into what you sign. Master one question and you can read any break in this family on sight: *are the bytes I authenticated exactly the bytes I will act on, and can they mean only one thing?*
&lt;h2&gt;1. Two Lines of Code, Fifteen Years Apart&lt;/h2&gt;
&lt;p&gt;In September 2009, two researchers forged a perfectly valid signature on a Flickr API request without ever learning Flickr&apos;s secret key, and the attack exploited no weakness in MD5&apos;s compression function at all [@flickr-2009]. Fifteen years later, an attacker holding &lt;em&gt;any single assertion&lt;/em&gt; an identity provider had ever signed could walk into a GitLab instance as any user they chose, because the signature that verified covered one identity while the application trusted another [@cve-2024-45409] [@gitlab-advisory]. In both cases the mathematics was flawless. The bytes were the wound.&lt;/p&gt;
&lt;p&gt;That pairing is the whole subject in miniature. A cryptographic signature, a message authentication code, and a hash all make the same narrow promise: they bind one exact string of bytes. They say nothing about what those bytes &lt;em&gt;mean&lt;/em&gt;, and nothing about whether the bytes the verifier checked are the bytes the application will act on. Break either of those unspoken assumptions and you get a valid signature over a forged message, with the algorithm computing exactly what it was told to.&lt;/p&gt;

An attacker with any document signed by the identity provider can &quot;forge a SAML Response/Assertion with arbitrary contents&quot; and &quot;log in as arbitrary user&quot; -- with a signature that verifies. [@cve-2024-45409]
&lt;p&gt;Once you see it this way, the family stops looking like seven unrelated tricks. Every break enters through one of exactly two doors. Call them the &lt;strong&gt;two-move lens&lt;/strong&gt;, and you will spend the rest of this article placing new attacks into one bucket or the other on sight.&lt;/p&gt;

flowchart TD
    A[&quot;A signature, MAC, or hash binds one exact byte string&quot;] --&amp;gt; B{&quot;Where does the attacker&apos;s wedge enter?&quot;}
    B --&amp;gt;|Move 1| C[&quot;Ambiguous encoding: many byte strings mean one thing, or one byte string parses two ways&quot;]
    B --&amp;gt;|Move 2| D[&quot;Attacker-influenced binding: the authenticated span, or the verify rule, is under the adversary&apos;s control&quot;]
    C --&amp;gt; E[&quot;The bytes the verifier checked are not the bytes the application acts on&quot;]
    D --&amp;gt; E
&lt;p&gt;Move one is an &lt;strong&gt;ambiguous encoding&lt;/strong&gt;: many different byte strings decode to the same meaning, or one byte string parses two different ways. Move two is an &lt;strong&gt;attacker-influenced binding&lt;/strong&gt;: the span that actually gets authenticated, or the rule used to verify it, is something the adversary can steer. Flickr was move two: hash length extension extends the authenticated span to swallow the attacker&apos;s appended bytes. Bleichenbacher&apos;s 2006 forgery was the clean move one, a lax encoding that a lenient verifier read as a valid one [@bleichenbacher-2006]. The GitLab break was move two, riding on move one. A third break we will meet shortly, the JWT &lt;code&gt;alg:none&lt;/code&gt; bypass, is move two in its purest form: the message tells the verifier how to verify it [@mclean-2015].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is not &quot;insecure deserialization.&quot; The bugs in this family live at the boundary where cryptography meets parsing, not where an attacker ships a serialized object that runs code on load (Python &lt;code&gt;pickle&lt;/code&gt;, Java &lt;code&gt;readObject&lt;/code&gt;, PHP &lt;code&gt;unserialize&lt;/code&gt;). Object-injection remote code execution is a distinct topic with a distinct fix. Here, the payload is always just data, and the entire drama is which bytes got signed versus which bytes got believed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is Part 3 of a field guide for protocol designers. Part 1 built the vocabulary of security definitions, showing that a &lt;em&gt;provably fine&lt;/em&gt; primitive still sinks a protocol when the deployment hands an adversary a capability the proof never modeled (&lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;Secure Against Whom?&lt;/a&gt;). This part is that thesis pointed at one specific seam: the serialization. We assume you are comfortable with the idea that &quot;secure&quot; means &quot;secure against a named adversary,&quot; and we build from there.&lt;/p&gt;
&lt;p&gt;If neither MD5&apos;s compression function, nor RSA&apos;s modular exponentiation, nor SAML&apos;s signature algorithm was ever broken, then where exactly did the wedge go in? To answer that, we have to go back to two inventions that created the gap, one from 1989 and one whose canonical form was standardized in 1994, both engineered for entirely good reasons.&lt;/p&gt;
&lt;h2&gt;2. Where the Gap Comes From&lt;/h2&gt;
&lt;p&gt;Kerckhoffs said it in 1883 and Shannon formalized it in 1949: put all the secrecy in the key, and assume the adversary knows everything else [@kerckhoffs-1883] [@shannon-1949]. That principle governs &lt;em&gt;where&lt;/em&gt; a system&apos;s security must live -- in the key, never in the obscurity of the algorithm. Historically framed for secrecy systems, it says nothing about the moment you have to serialize a &lt;em&gt;structured message&lt;/em&gt; you must authenticate: an API request, a certificate, an identity assertion.&lt;/p&gt;
&lt;p&gt;The instant a structure has to become bytes on a wire, the rule that turns structure into bytes becomes attack surface. This family of breaks is not new. It is as old as the sentence &quot;we need to sign this serialized data.&quot;&lt;/p&gt;
&lt;p&gt;Two inventions, one from 1989 and one whose canonical form was standardized in 1994, built the gap into the foundations. Neither was a mistake. Each solved a real problem and quietly left a door ajar.&lt;/p&gt;
&lt;p&gt;The first is the way almost every hash function you have used is built.&lt;/p&gt;

An iterated hash design, introduced independently by Ralph Merkle and Ivan Damgard at CRYPTO 1989. The padded message is split into fixed-size blocks, and a compression function folds them one at a time into a running chaining value. The *final chaining value is published as the digest*. MD5, SHA-1, and the SHA-2 family are all Merkle-Damgard hashes.
&lt;p&gt;Write it as a recurrence and the shape of every later attack is already visible. Starting from a fixed initial value $s_0$, the hash computes $s_i = f(s_{i-1}, m_i)$ for each padded block $m_i$, and emits the last state as the digest, $H(m) = s_n$ [@merkle-1989] [@damgard-1989] [@cryptoeng]. Merkle and Damgard proved something valuable: if the compression function $f$ resists collisions, so does the whole iterated hash. To make the proof work, they appended the message length inside the final padding, a trick called Merkle-Damgard strengthening.&lt;/p&gt;
&lt;p&gt;Here is the door. The digest &lt;em&gt;is&lt;/em&gt; the internal state. Nothing about $s_n$ is secret or scrambled beyond what the public compression function did to it. If you know a digest and how many bytes preceded it, you can set your own machine&apos;s state to that digest and &lt;em&gt;keep hashing&lt;/em&gt; from where the original left off.The appended length field was added to stop &lt;em&gt;collision&lt;/em&gt; ambiguities between messages that pad to the same blocks. It does nothing to conceal the internal state. That is precisely why a length-extension attacker needs to know the length of the secret: only to reconstruct the &quot;glue&quot; padding, never to recover the state itself [@bowes].&lt;/p&gt;
&lt;p&gt;The second invention is how we write down the things we sign so two machines agree byte-for-byte.&lt;/p&gt;

An encoding rule under which every abstract value has exactly one legal byte representation. That makes &quot;is this the encoding of X?&quot; a single yes-or-no question. The Distinguished Encoding Rules (DER) of ASN.1, standardized by the ITU-T as X.690, are the archetype, and they carry the signed structures inside X.509 certificates and PKCS#1 signatures [@x690] [@rfc8017].
&lt;p&gt;ASN.1 dates to the 1980s, and its canonical DER form was standardized as ITU-T X.690 in 1994. DER exists for a genuine reason: if you are going to hash and sign a structured object like a certificate, every implementation must serialize it to &lt;em&gt;identical&lt;/em&gt; bytes or no two of them will ever agree on a signature. DER is the &quot;exactly one valid byte string&quot; discipline done right. Its siblings, the more permissive Basic Encoding Rules (BER), allow many byte strings for the same value, which is convenient for streaming and a loaded gun for signature verification, as we will see [@x690].&lt;/p&gt;

timeline
    title The byte-vs-meaning gap, 1989 to 2025
    1989 : Merkle-Damgard hashing
    1994 : X.690 DER canonical encoding
    1997 : HMAC standardized (RFC 2104)
    2005 : XML signature wrapping named
    2006 : Bleichenbacher e-3 RSA forgery
    2009 : Flickr length-extension forgery
    2015 : JWT alg confusion and alg none
    2025 : ruby-saml parser differential
&lt;p&gt;The third piece of history is younger, and it is the moment the industry first wrote the problem down as a &lt;em&gt;class&lt;/em&gt; rather than a one-off bug. In 2005, Michael McIntosh and Paula Austel at IBM published the first formal treatment of XML signature element wrapping: a genuinely signed element could be relocated inside a document so that verification found it in one place while the application consumed a forged element somewhere else [@mcintosh-2005]. They named the attacker-influenced-binding move for documents two decades before it produced an unauthenticated GitLab administrator takeover in 2025.&lt;/p&gt;
&lt;p&gt;Both roots were built for good reasons -- a collision-resistance proof and an interoperable, unambiguous encoding -- and both left the same gap between bytes and meaning. So what happens when a working engineer reaches for the &lt;em&gt;obvious&lt;/em&gt; way to authenticate a message with these tools? They build a trap. Three of them, one for each obvious shortcut.&lt;/p&gt;
&lt;h2&gt;3. Three Obvious Designs, Three Traps&lt;/h2&gt;
&lt;p&gt;Give an engineer a hash, a signature, and a deadline, and each of the two foundations above suggests an obvious shortcut. All three shortcuts authenticate something &lt;em&gt;near&lt;/em&gt; the thing they should have, and the small gap between &quot;near&quot; and &quot;exactly&quot; is where every attack in this article lives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trap one: prove you know a secret by hashing it in front of the message.&lt;/strong&gt; Before HMAC existed, the natural way to build a keyed authentication tag was to concatenate the secret with the message and hash the result: &lt;code&gt;MAC = H(secret || message)&lt;/code&gt;. It feels airtight. An attacker who has never seen the secret cannot reproduce the hash, so a matching tag looks like proof of knowledge. But recall that in a Merkle-Damgard hash the digest &lt;em&gt;is&lt;/em&gt; the internal state after absorbing &lt;code&gt;secret || message || padding&lt;/code&gt;. Publishing the tag hands the attacker the machine mid-computation.&lt;/p&gt;

Given only the tag $t = H(\text{secret} \,\|\, m)$ and the *length* of the secret -- but not the secret itself -- an attacker can compute a valid tag for a longer message $H(\text{secret} \,\|\, m \,\|\, \text{pad} \,\|\, m&apos;)$ for attacker-chosen bytes $m&apos;$. The published digest is loaded back in as the hash&apos;s internal state, and hashing simply continues from there. It works against any plain &quot;output equals full state&quot; Merkle-Damgard hash [@bowes].
&lt;p&gt;&lt;strong&gt;Trap two: verify an RSA signature by looking for the right structure inside it.&lt;/strong&gt; A PKCS#1 v1.5 signature, once you undo the RSA math, is a byte string that is supposed to be a specific padded encoding wrapping a small ASN.1 structure called &lt;code&gt;DigestInfo&lt;/code&gt;, which names the hash algorithm and carries the hash [@rfc8017]. The correct check is exact: re-encode the expected structure to its canonical DER bytes and compare the whole thing, byte for byte. The tempting shortcut is to &lt;em&gt;parse&lt;/em&gt;: scan the decrypted block, find a &lt;code&gt;DigestInfo&lt;/code&gt; somewhere inside it, pull out the hash, and compare only that. A lenient verifier silently changes the question from &quot;is this &lt;em&gt;the&lt;/em&gt; canonical encoding?&quot; to &quot;does &lt;em&gt;a&lt;/em&gt; valid &lt;code&gt;DigestInfo&lt;/code&gt; appear somewhere in here?&quot; [@bleichenbacher-2006].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trap three: sign an XML element and let the verifier find it later by name.&lt;/strong&gt; XML Signature, standardized by the W3C in 2002, does not sign &quot;the document.&quot; It signs whatever elements a &lt;code&gt;&amp;lt;Reference&amp;gt;&lt;/code&gt; points at, typically by an &lt;code&gt;Id&lt;/code&gt; attribute, after running them through a canonicalization transform [@xmldsig-2002] [@xmldsig]. Verification asks &quot;is there a correctly signed element with this &lt;code&gt;Id&lt;/code&gt;?&quot; The application, separately, asks &quot;what does the assertion say?&quot; Nothing in the design forces those two questions to resolve to the &lt;em&gt;same element&lt;/em&gt;. That gap is the seed of every signature-wrapping attack [@mcintosh-2005].&lt;/p&gt;
&lt;p&gt;Traps two and three share a deeper shape that has its own name.&lt;/p&gt;

A situation in which two implementations, or two passes of one implementation, derive different structure or meaning from the *same* input bytes. When one parser is the verifier and another is the consumer, the object that got checked and the object that gets acted on are no longer guaranteed to be the same object [@bishopfox-2020].
&lt;p&gt;Each of these designs shipped, and each was trusted for years, because each &lt;em&gt;works perfectly on honest input&lt;/em&gt;. Honest input is the trap. The verifier and the consumer agree on every well-formed message anyone generates by accident, and diverge only on the adversarial message nobody tested.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; In all three traps, verification confirmed &lt;em&gt;a&lt;/em&gt; valid reading of the bytes, not &lt;em&gt;the&lt;/em&gt; single canonical one, and it authenticated a span the attacker could later move, extend, or reinterpret. That one sentence is the whole failure catalog. Everything that follows is the same wound in five different bodies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These leaks did not announce themselves. They surfaced one at a time, over fifteen years, and each new break forced a specific defense that the next generation promptly outgrew. Line the breaks up in order and they beat like a metronome: an obvious design, a named forgery, a fix, then a more ambitious design that reopens the identical gap. Here is that sequence, generation by generation.&lt;/p&gt;
&lt;h2&gt;4. Five Generations, One Shape&lt;/h2&gt;
&lt;p&gt;The genealogy of this subject is easy to misread as &quot;each fix replaced the previous primitive.&quot; It did not. Hashes did not replace signatures, and JSON did not replace XML because XML was cryptographically weaker. What actually happened is a steady rise in &lt;em&gt;ambition&lt;/em&gt;.&lt;/p&gt;

Read the five generations as one trend, not a list. Each authenticates *more structure* than the last: a raw string, then a cryptographic structure, then a whole document, then a self-describing token, then a binary remote-procedure-call message. Every added layer of structure opened a new way for the authenticated bytes to drift from the consumed bytes. The primitive was never the variable that changed. The ambition was.
&lt;h3&gt;Generation 1 -- The keyed hash done wrong&lt;/h3&gt;
&lt;p&gt;The simplest thing to authenticate is a raw string, and the simplest keyed tag is &lt;code&gt;H(secret || message)&lt;/code&gt;. In September 2009, Thai Duong and Juliano Rizzo showed that Flickr&apos;s API used exactly this over MD5, and that an attacker could append arbitrary parameters to a signed request and produce a tag that verified, without ever knowing the secret [@flickr-2009]. MD5&apos;s compression function was not the casualty. The construction was.&lt;/p&gt;

flowchart TD
    S[&quot;Sender computes the tag as H of secret then message&quot;] --&amp;gt; T[&quot;Published tag equals the final Merkle-Damgard state&quot;]
    T --&amp;gt; A[&quot;Attacker reads the tag and guesses the secret length&quot;]
    A --&amp;gt; R[&quot;Reload the tag as the hash internal state&quot;]
    R --&amp;gt; X[&quot;Keep hashing: glue padding, then attacker bytes m-prime&quot;]
    X --&amp;gt; F[&quot;Valid tag for secret, message, padding, m-prime, with the secret never known&quot;]
&lt;p&gt;The mechanism is worth feeling in code, not just reading. A &quot;resumable&quot; hash publishes its whole state as the digest, so the tag is a saved game the attacker loads and continues.&lt;/p&gt;
&lt;p&gt;{`
// A toy &quot;output equals full internal state&quot; hash, in the Merkle-Damgard spirit.
// The tag IS the state, so anyone can resume hashing from it. (Padding elided.)
function toyCompress(state, block) {
  let s = state &amp;gt;&amp;gt;&amp;gt; 0;
  for (const ch of block) s = (Math.imul(s, 31) + ch.charCodeAt(0)) &amp;gt;&amp;gt;&amp;gt; 0;
  return s; // final state == published digest
}
function toyHash(msg) { return toyCompress(0x9e3779b1, msg); }&lt;/p&gt;
&lt;p&gt;const SECRET = &quot;hunter2&quot;;                 // attacker never sees this
const tag = toyHash(SECRET + &quot;&amp;amp;role=guest&quot;);
console.log(&quot;server tag:&quot;, tag);&lt;/p&gt;
&lt;p&gt;// Attacker knows ONLY the tag and the length of (secret + message).
// They reload the tag as state and keep hashing their own bytes:
const forged = toyCompress(tag, &quot;&amp;amp;role=admin&quot;);&lt;/p&gt;
&lt;p&gt;// What the server computes over the extended message, WITH the secret:
const serverRecompute = toyCompress(toyHash(SECRET + &quot;&amp;amp;role=guest&quot;), &quot;&amp;amp;role=admin&quot;);
console.log(&quot;forged == server:&quot;, forged === serverRecompute); // true, no secret needed
`}&lt;/p&gt;
&lt;p&gt;The fix was to stop signing with a construction whose output is a resumable state. Bellare, Canetti, and Krawczyk had already shown how in 1996 [@bck-1996].&lt;/p&gt;

The Hash-based Message Authentication Code, analyzed by Bellare, Canetti, and Krawczyk in 1996 and standardized in RFC 2104 in 1997. HMAC nests two keyed hash passes, $\text{HMAC}(K, m) = H((K_0 \oplus \text{opad}) \,\|\, H((K_0 \oplus \text{ipad}) \,\|\, m))$, where $K_0$ is the secret key fitted to one full hash block. It is length-extension-immune not because it has no resumable state, but because its outer input is a fixed one-key-block-plus-one-inner-digest structure, and forging a new inner digest requires the secret [@rfc2104] [@bck-1996] [@fips198-1].
&lt;p&gt;Two details of that definition carry the whole argument, and both are routinely mangled. First, $K_0$: FIPS 198-1 defines it as the key preprocessed to exactly one $B$-byte hash block -- the key itself when it is already $B$ bytes, its hash padded with zeros when it is longer than a block, and the key padded with zeros when it is shorter [@fips198-1]. The exclusive-or is always against that block $K_0$, never against a raw key of arbitrary length.&lt;/p&gt;
&lt;p&gt;Second, the reason HMAC resists length extension. It is tempting, and wrong, to say HMAC &quot;has no internal state to resume.&quot; It does; an HMAC tag is an ordinary hash output, so you can load it back as a Merkle-Damgard state and keep hashing. The immunity is &lt;em&gt;structural&lt;/em&gt;. A genuine HMAC tag is the outer hash of exactly two things: one $B$-byte key block $(K_0 \oplus \text{opad})$ followed by exactly one inner digest. Resume from a tag, append your own bytes, and what you have computed is a valid HMAC of &lt;em&gt;no well-formed message at all&lt;/em&gt;, because a real HMAC&apos;s outer input is never longer than one key block plus one inner digest.This is the subtlest true statement in the subject. An HMAC tag &lt;em&gt;can&lt;/em&gt; be resumed as a Merkle-Damgard state, so the folk explanation &quot;there is no state to resume&quot; is false. What resuming actually yields is a valid tag for no message, because the outer hash&apos;s input is pinned at one key block plus one inner digest. Immunity is a property of that fixed structure, not of missing state [@fips198-1]. To forge a tag for a genuinely &lt;em&gt;new&lt;/em&gt; message you would have to produce that message&apos;s inner digest, and the inner hash begins with the secret-derived block $(K_0 \oplus \text{ipad})$. So you need $K_0$, which means you need the secret [@rfc2104] [@fips198-1].&lt;/p&gt;
&lt;p&gt;The single most-botched fact in this whole subject is the boundary between what is extendable and what is not, so here it is drawn exactly [@bowes].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;th&gt;Length-extendable?&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;MD5, SHA-1, SHA-256, SHA-512 over secret then message&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The digest is the full final Merkle-Damgard state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMAC with any of those hashes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Outer input is fixed at one key block plus one inner digest; resuming a tag yields a tag for no message, and a new inner digest needs the secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-3, SHAKE, KMAC&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Sponge construction; the capacity is never output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-512/256, SHA-384 (truncated SHA-2)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Part of the final state is withheld from the output&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; Vulnerable: a bare &lt;code&gt;SHA-256(secret || message)&lt;/code&gt; used as a MAC. Not vulnerable: HMAC-SHA256, the SHA-3 family, and truncated SHA-2 such as SHA-512/256. Note what this does &lt;em&gt;not&lt;/em&gt; say. SHA-256 is not &quot;broken,&quot; and &lt;strong&gt;HMAC-SHA256 is not length-extendable.&lt;/strong&gt; The flaw is the &lt;em&gt;construction&lt;/em&gt; &lt;code&gt;H(secret || m)&lt;/code&gt;, never the hash inside it [@bowes] [@rfc2104].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here the reader&apos;s model should shift. Hashing &lt;code&gt;secret || message&lt;/code&gt; felt like proof of knowledge; it was nothing of the sort, because the tag &lt;em&gt;is&lt;/em&gt; the primitive&apos;s own continuation, offered to the attacker for free.Flickr shipped &lt;code&gt;MD5(secret || params)&lt;/code&gt; in 2009, twelve years after RFC 2104 (1997) standardized HMAC, the exact fix for this exact bug. Folklore about &quot;just hash the secret with it&quot; outlived its own refutation by more than a decade [@flickr-2009] [@rfc2104]. The compression function was never touched. The construction leaked.&lt;/p&gt;

Ron Bowes published `hash_extender`, which automates the glue-padding arithmetic for MD5, SHA-1, the SHA-256 and SHA-512 families, and more. Feed it a captured `H(secret || m)` tag, a guess for the secret length, and the bytes you want to append, and it prints both the forged tag and the exact padded message that produces it [@bowes].
&lt;h3&gt;Generation 2 -- Signing a cryptographic structure&lt;/h3&gt;
&lt;p&gt;Raise the ambition: now you are signing not a raw string but a structured object, an RSA signature carrying an ASN.1 &lt;code&gt;DigestInfo&lt;/code&gt;. The lenient verifier from trap two turns this into a forgery machine. In 2006, Daniel Bleichenbacher demonstrated at the CRYPTO rump session that if a verifier parses the decrypted PKCS#1 v1.5 block for a &lt;code&gt;DigestInfo&lt;/code&gt; instead of checking the exact padded encoding, and if the RSA public exponent is 3, an attacker can craft a value whose cube looks like a valid signature with garbage in the space the lenient parser ignores [@bleichenbacher-2006] [@valsorda]. No private key, no broken RSA, just a parser that accepted &lt;em&gt;a&lt;/em&gt; structure instead of &lt;em&gt;the&lt;/em&gt; encoding.Bleichenbacher bridges Part 1 and Part 3 of this guide. His 1998 padding oracle (Part 1) and his 2006 signature forgery are the same lesson told twice: the RSA math was always fine, and the wound was in how an implementation &lt;em&gt;handled the bytes around it&lt;/em&gt; [@valsorda].&lt;/p&gt;
&lt;p&gt;The lesson should have stuck. It did not. Eight years later, in September 2014, Antoine Delignat-Lavaud of Inria&apos;s Prosecco team and, independently, Intel Security&apos;s Advanced Threat Research team found the identical flaw shipping in Mozilla&apos;s NSS library, the code behind Firefox, Thunderbird, and Chrome on several platforms [@mfsa-2014-73] [@cve-2014-1568]. NVD records it as a failure to &quot;properly parse ASN.1 values,&quot; a &quot;signature malleability&quot; issue [@cve-2014-1568].The break was nicknamed &quot;BERserk&quot; because the verifier accepted lax &lt;strong&gt;BER&lt;/strong&gt; encodings where the specification demanded the single canonical &lt;strong&gt;DER&lt;/strong&gt; form. The name is the diagnosis: the gap between &quot;a valid encoding&quot; and &quot;the canonical encoding&quot; is the whole bug [@mfsa-2014-73].&lt;/p&gt;

A signature scheme is *malleable* if, given a valid signature or the structure inside it, an attacker can derive a different signature the verifier still accepts, without the private key. NVD logs BERserk (CVE-2014-1568) verbatim as a &quot;signature malleability&quot; issue. A *non-malleable* scheme -- RSA-PSS, or any strongly unforgeable (SUF-CMA) signature -- forecloses this, which is exactly why it is the durable fix for the low-exponent forgeries [@cve-2014-1568] [@rfc8017].
&lt;p&gt;The fix is the discipline from trap two, stated positively: never parse a signature to find its structure. Re-encode the expected &lt;code&gt;DigestInfo&lt;/code&gt; to canonical DER, then compare the full block byte for byte, and prefer RSA-PSS, whose verification has no lenient-parse foothold [@rfc8017]. Windows code signing gets this right in production by signing a DER-encoded PKCS#7 structure (&lt;a href=&quot;https://paragmali.com/blog/authenticode-and-catalog-files-the-crypto-foundation-under-w/&quot; rel=&quot;noopener&quot;&gt;Authenticode and catalog files&lt;/a&gt;), and the X.509 certificates every TLS stack validates ride on the same DER discipline (&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;
&lt;h3&gt;Generation 3 -- Signing a whole document&lt;/h3&gt;
&lt;p&gt;Raise the ambition again: sign an entire XML document, flexibly, so a signature can cover part of a message and travel with it. This is XML Signature, and its reference-by-&lt;code&gt;Id&lt;/code&gt; model from trap three is the most durable failure in the subject.&lt;/p&gt;

An attack in which a genuinely signed XML element is relocated, duplicated, or wrapped inside a document so that signature verification still finds and validates the original element by its reference, while the application&apos;s business logic reads a *different*, attacker-authored element. The signed node and the consumed node are not the same node. McIntosh and Austel formalized it in 2005 [@mcintosh-2005].

flowchart TD
    D[&quot;Signed SAML response from the identity provider&quot;] --&amp;gt; W[&quot;Attacker keeps the signed assertion and injects a forged assertion&quot;]
    W --&amp;gt; V{&quot;Verifier: is there a valid signature for this Id?&quot;}
    V --&amp;gt;|Finds the genuine signed assertion| OK[&quot;Signature check passes&quot;]
    OK --&amp;gt; C[&quot;Application reads identity from the forged assertion instead&quot;]
    C --&amp;gt; R[&quot;Logged in as an arbitrary user&quot;]
&lt;p&gt;In 2012, Juraj Somorovsky and colleagues tested this against real SAML frameworks in &quot;On Breaking SAML: Be Whoever You Want to Be.&quot; Of the 14 major frameworks they examined, 11 fell to at least one wrapping variant, and only two -- Microsoft SharePoint 2010 and SimpleSAMLphp -- resisted every variant they threw at them [@somorovsky-2012]. Eleven of fourteen is the number that matters: this was a systemic property of the sign-a-tree-by-reference design, not one vendor&apos;s slip.&lt;/p&gt;
&lt;p&gt;Then in 2018, Kelby Ludwig at Duo Security found a subtler variant. Because canonicalization handles XML comments inconsistently, inserting a comment inside a signed field could make the verifier and the application read &lt;em&gt;different text&lt;/em&gt; from the same signed element. As Ludwig put it, &quot;multiple different-but-similar XML documents can have the same exact signature,&quot; a cluster catalogued as CVE-2017-11427 and its siblings [@duo-2018].&lt;/p&gt;
&lt;p&gt;XML Signature can be patched case by case, but each patch closes one wrapping shape and leaves the model intact. That is why, as we are about to see, the same attack was still taking over logins in 2025. For new designs, this generation is a road that leads nowhere good.&lt;/p&gt;
&lt;h3&gt;Generation 4 -- Signing a self-describing token&lt;/h3&gt;
&lt;p&gt;Raise the ambition once more: a token that carries its own metadata, including which algorithm secured it. This is JOSE and the JSON Web Token. In 2015, Tim McLean showed what happens when you let a message choose how it is checked [@mclean-2015] [@cve-2015-9235].&lt;/p&gt;

A class of JWT and JOSE attack in which the token declares, in its unauthenticated header, which algorithm the verifier should use. Setting the header algorithm to `none` asks the verifier to accept an unsigned token. Switching it from `RS256` (an RSA signature) to `HS256` (an HMAC) can trick a verifier into using the *public* RSA key as an HMAC secret, a value the attacker also holds. The message chooses its own verification rule [@mclean-2015].

sequenceDiagram
    participant A as Attacker
    participant V as Verifier
    Note over V: Holds the RSA public key, expects RS256
    A-&amp;gt;&amp;gt;V: Take a real token, change the header alg to HS256
    Note over A: Sign the token using the RSA public key bytes as the HMAC secret
    A-&amp;gt;&amp;gt;V: Send the forged HS256 token
    V-&amp;gt;&amp;gt;V: Reads alg from the token, selects HMAC-SHA256
    V-&amp;gt;&amp;gt;V: Uses the public key as the HMAC key and recomputes the tag
    V--&amp;gt;&amp;gt;A: Tag matches, token accepted as genuine
&lt;p&gt;The RS256/HS256 confusion is the archetype of the type-confusion move: the attacker does not break either algorithm, but reinterprets a public value (the RSA key) as a secret value (the HMAC key), because the verifier let the token decide which one it was.The &lt;code&gt;none&lt;/code&gt; algorithm is not a bug in the JOSE specification. RFC 7518 defines it as the algorithm for an Unsecured JWS, a legitimate option when integrity is guaranteed by some other layer. It becomes a bypass only when a verifier in a &lt;em&gt;secured&lt;/em&gt; context fails to forbid it [@rfc7518].&lt;/p&gt;
&lt;p&gt;The fix arrived formally in 2020 as RFC 8725, the JSON Web Token Best Current Practices, written after, in its own words, &quot;there have been several widely published attacks on implementations and deployments&quot; [@rfc8725]. Its discipline: pin an explicit algorithm allow-list, bind each key to exactly one algorithm, reject &lt;code&gt;none&lt;/code&gt; in secured contexts, and never trust the algorithm the token names for itself. Windows leans on JWS-style tokens in production, such as the Primary Refresh Token (&lt;a href=&quot;https://paragmali.com/blog/inside-the-primary-refresh-token-the-cryptographic-seam-betw/&quot; rel=&quot;noopener&quot;&gt;Inside the Primary Refresh Token&lt;/a&gt;); the rules a service applies before it trusts such a token are a discipline of their own (&lt;a href=&quot;https://paragmali.com/blog/who-decided-this-token-is-good-a-field-guide-to-conditional-/&quot; rel=&quot;noopener&quot;&gt;Who Decided This Token Is Good?&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Generation 5 -- Signing a binary message&lt;/h3&gt;
&lt;p&gt;The newest ambition is signing binary remote-procedure-call payloads, and it runs straight into a wall the earlier generations only grazed: some formats have &lt;em&gt;no&lt;/em&gt; canonical byte form at all. Protocol Buffers is the clearest case. Its own documentation warns against assuming stable output.&lt;/p&gt;

&quot;Deterministic serialization only guarantees the same byte output for a particular binary. The byte output may change across different versions of the binary.&quot; -- Protocol Buffers documentation [@protobuf]
&lt;p&gt;If two versions of your service can serialize the same logical message to different bytes, then &quot;sign the bytes and compare&quot; is not even well defined: the sender and receiver may disagree on what the bytes &lt;em&gt;are&lt;/em&gt; before any attacker shows up [@protobuf]. The fix is to refuse the premise. Sign the exact bytes you received and never re-serialize them; or, if you must sign structure, move to a format with a specified deterministic form, such as deterministic CBOR (RFC 8949, Section 4.2) or the JSON Canonicalization Scheme (RFC 8785); or sign a hash over explicit, typed fields rather than over a serializer&apos;s mood [@rfc8949] [@rfc8785].&lt;/p&gt;
&lt;p&gt;Five generations, five fixes, one shape. Every fix was a variation on &quot;make the authenticated bytes be exactly, and unambiguously, the bytes you act on.&quot; By the time the field had patched the fifth, it could finally state out loud what the first four had only whispered.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Named break (year)&lt;/th&gt;
&lt;th&gt;Which move&lt;/th&gt;
&lt;th&gt;The fix&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;1 Keyed hash&lt;/td&gt;
&lt;td&gt;Flickr length extension (2009)&lt;/td&gt;
&lt;td&gt;Attacker-influenced binding: extends the span&lt;/td&gt;
&lt;td&gt;HMAC, SHA-3, truncated SHA-2&lt;/td&gt;
&lt;td&gt;Solved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 Crypto structure&lt;/td&gt;
&lt;td&gt;Bleichenbacher e=3 (2006), BERserk (2014)&lt;/td&gt;
&lt;td&gt;Ambiguous encoding: BER versus DER&lt;/td&gt;
&lt;td&gt;Re-encode to DER and byte-compare; RSA-PSS&lt;/td&gt;
&lt;td&gt;Solved if disciplined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 Document&lt;/td&gt;
&lt;td&gt;On Breaking SAML (2012), Duo (2018)&lt;/td&gt;
&lt;td&gt;Attacker-influenced binding plus ambiguous canonicalization&lt;/td&gt;
&lt;td&gt;Sign the exact bytes; avoid for new designs&lt;/td&gt;
&lt;td&gt;Recurring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 Token&lt;/td&gt;
&lt;td&gt;alg none, RS256/HS256 confusion (2015)&lt;/td&gt;
&lt;td&gt;Attacker-influenced binding: message picks the rule&lt;/td&gt;
&lt;td&gt;RFC 8725 allow-list, key-to-algorithm binding&lt;/td&gt;
&lt;td&gt;Solved if disciplined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 Binary or RPC&lt;/td&gt;
&lt;td&gt;protobuf non-determinism&lt;/td&gt;
&lt;td&gt;Ambiguous encoding: no canonical form&lt;/td&gt;
&lt;td&gt;dCBOR, JCS, or sign the received bytes&lt;/td&gt;
&lt;td&gt;Design guidance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;5. Authenticate Bytes, Not Parses&lt;/h2&gt;
&lt;p&gt;Here is the sentence the five generations were spelling out. Cryptography authenticates bytes, so make the bytes you authenticate be &lt;em&gt;exactly&lt;/em&gt; the bytes the consumer parses, make those bytes mean only one thing, and bind the type, context, and algorithm into what you sign so a valid signature can never be replayed into a second meaning. That is not three separate ideas competing for your allegiance. It is one idea with a strict order of preference.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The cure for every break in this family is not a stronger algorithm. It is a change in &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; you sign: authenticate the exact bytes the consumer parses, in an encoding that can mean only one thing, with type, context, and algorithm bound in. The algorithm was never the problem, so a better algorithm was never going to be the answer.&lt;/p&gt;
&lt;/blockquote&gt;

flowchart TD
    Start[&quot;You must authenticate structured data&quot;] --&amp;gt; Q{&quot;Can you sign the exact bytes the consumer parses?&quot;}
    Q --&amp;gt;|Yes, preferred| P1[&quot;Pattern 1: sign-the-encoding, as in JWS, COSE, TLS 1.3&quot;]
    Q --&amp;gt;|No, structure is detached from the wire| P2[&quot;Pattern 2: canonical encoding such as DER, deterministic CBOR, JCS, then byte-compare&quot;]
    P1 --&amp;gt; P3[&quot;Pattern 3, always on top: bind type, context, and algorithm, i.e. domain separation&quot;]
    P2 --&amp;gt; P3
&lt;p&gt;&lt;strong&gt;Pattern one, and the one to reach for first, is to sign the encoding.&lt;/strong&gt;&lt;/p&gt;

The discipline of authenticating the *exact serialized bytes* the consumer will parse, rather than an abstract structure that must be re-serialized to check. Because the signed bytes and the parsed bytes are byte-identical, no re-serialization or parser-differential gap can open between verification and use. JWS signs the base64url `header.payload` string it transmits; COSE signs a CBOR `Sig_structure`; TLS 1.3 signs its transcript directly [@rfc7515] [@rfc9052] [@rfc8446].
&lt;p&gt;When the bytes that were signed and the bytes that get parsed are literally the same bytes, moves one and two have nowhere to stand. There is no second parse to differ, and no separate span to relocate. JWS makes this concrete: the signature covers the exact base64url-encoded &lt;code&gt;header.payload&lt;/code&gt;, so the verifier and the application see identical input [@rfc7515].base64url is the URL-safe base64 variant: it swaps the plus and slash characters for hyphen and underscore and drops the trailing padding, so a token survives being placed in a URL or header without further escaping. COSE does the same for CBOR in constrained and IoT settings via its &lt;code&gt;Sig_structure&lt;/code&gt; [@rfc9052].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pattern two is the disciplined fallback for when you genuinely cannot sign the wire bytes&lt;/strong&gt; -- when a structure must be signed once and survive re-serialization by intermediaries. Then, and only then, use an encoding with exactly one legal form and verify by re-encoding and comparing bytes: DER for ASN.1, deterministic CBOR (RFC 8949, Section 4.2), or JCS for JSON (RFC 8785) [@x690] [@rfc8949] [@rfc8785]. This is strictly weaker than pattern one, because it is only as safe as your canonicalizer, a caveat that becomes a hard limit in Section 8.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pattern three sits on top of whichever of the first two you chose, always.&lt;/strong&gt;&lt;/p&gt;

Binding a signature to its intended type, context, and algorithm by folding those into the signed input, so a signature produced for one purpose cannot be replayed as if produced for another. TLS 1.3 prefixes its signed transcript with a context string (RFC 8446, Section 4.4.3); Ethereum&apos;s EIP-712 prepends a domain separator; RFC 8725 binds each JWT key to a single algorithm [@rfc8446] [@eip712] [@rfc8725].
&lt;p&gt;EIP-712 shows the pattern in one line: the value actually signed is $\texttt{0x1901} ,|, \text{domainSeparator} ,|, \text{hashStruct}(m)$, so a signature for one contract, chain, or message type is meaningless anywhere else [@eip712]. This is what stops the Generation 4 move: if the key is bound to one algorithm and the context is bound into the bytes, the token can no longer talk the verifier into a different rule.&lt;/p&gt;
&lt;p&gt;The gap between the abstract object and the bytes is not a detail; it is the entire subject. A worked example makes it visceral.&lt;/p&gt;
&lt;p&gt;{`
// Two &quot;equal&quot; objects serialize to different bytes -&amp;gt; different tags.
const a = JSON.stringify({ user: &quot;root&quot;, exp: 1710000000 });
const b = JSON.stringify({ exp: 1710000000, user: &quot;root&quot; }); // keys reordered
console.log(&quot;a:&quot;, a);
console.log(&quot;b:&quot;, b);
console.log(&quot;byte-equal:&quot;, a === b);   // false: a signature over &apos;a&apos; will not verify &apos;b&apos;&lt;/p&gt;
&lt;p&gt;// A canonical pass (here, just sorted keys) makes these two agree. A real
// canonicalizer (JCS, RFC 8785) must also pin number and string forms -- see section 8.
function canonical(obj) {
  const keys = Object.keys(obj).sort();
  const parts = keys.map(function (k) { return JSON.stringify(k) + &quot;:&quot; + JSON.stringify(obj[k]); });
  return &quot;{&quot; + parts.join(&quot;,&quot;) + &quot;}&quot;;
}
console.log(&quot;canonical a:&quot;, canonical(JSON.parse(a)));
console.log(&quot;canonical b:&quot;, canonical(JSON.parse(b)));
console.log(&quot;now equal:&quot;, canonical(JSON.parse(a)) === canonical(JSON.parse(b))); // true
`}&lt;/p&gt;
&lt;p&gt;That is the discipline in principle. But principles ship as code, and code ships on deadlines. What does the state of the art actually look like from 2024 to 2026, and, two decades after signature wrapping was first named, is this class of attack finally dead?&lt;/p&gt;
&lt;h2&gt;6. What Is Shipping, and What Is Still Breaking&lt;/h2&gt;
&lt;p&gt;It is not dead. In 2025, two decades after McIntosh and Austel named signature wrapping, the attack produced an &lt;em&gt;unauthenticated administrator&lt;/em&gt; takeover of GitLab instances [@github-seclab-2025] [@portswigger-2025]. The defenders have converged on the right patterns, and the attackers have responded by moving the wedge somewhere the patterns do not yet reach.&lt;/p&gt;
&lt;p&gt;Start with the offense, because the 2024 to 2025 SAML cluster is the two-move lens at its sharpest. In September 2024, ruby-saml failed to verify the signature of a SAML response strictly enough, and NVD records the result plainly: an attacker &quot;can forge a SAML Response/Assertion with arbitrary contents&quot; and &quot;log in as arbitrary user,&quot; which is exactly what happened to GitLab&apos;s SAML single sign-on [@cve-2024-45409] [@gitlab-advisory]. Weeks earlier, GitHub Enterprise Server had shipped a fix for a signature-wrapping bug in its handling of publicly exposed signed federation metadata that granted &quot;site administrator privileges&quot; to an unauthenticated attacker [@cve-2024-6800].&lt;/p&gt;
&lt;p&gt;Then, in March 2025, the ruby-saml story got worse and more instructive. CVE-2025-25291 and CVE-2025-25292 are a &lt;em&gt;parser differential&lt;/em&gt;: the library used two different XML parsers, REXML and Nokogiri, and, as NVD states, &quot;the parsers can generate entirely different document structures from the same XML input,&quot; which &quot;allows an attacker to execute a Signature Wrapping attack&quot; [@cve-2025-25291] [@cve-2025-25292]. GitHub&apos;s Security Lab, which found it, summarized the payoff: an attacker holding a single valid signature can construct assertions themselves and &quot;log in as any user,&quot; with an exploitable instance in GitLab [@github-seclab-2025].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The 2025 ruby-saml break is the purest specimen in this whole article, because it is &lt;em&gt;both&lt;/em&gt; moves fused into one. REXML and Nokogiri parsing the same bytes into different trees is an &lt;strong&gt;ambiguous encoding&lt;/strong&gt; (move one). Using that difference to make the verifier check one assertion while the application consumes another is an &lt;strong&gt;attacker-influenced binding&lt;/strong&gt; (move two). The checked tree is not the consumed tree [@cve-2025-25291] [@github-seclab-2025].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The same shape recurred across the wider SAML space in 2025: samlify allowed an attacker to &quot;forge a SAML Response to authenticate as any user,&quot; rated CVSS 9.9 [@cve-2025-47949], and passport-wsfed-saml2 allowed impersonation of &quot;any user during SAML authentication by crafting a SAMLResponse&quot; using a valid IdP-signed object [@cve-2025-46572].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVE (year)&lt;/th&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Move&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;CVE-2024-45409 (2024)&lt;/td&gt;
&lt;td&gt;ruby-saml with omniauth-saml&lt;/td&gt;
&lt;td&gt;Improper signature verification&lt;/td&gt;
&lt;td&gt;Forge any user; GitLab SSO bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2024-6800 (2024)&lt;/td&gt;
&lt;td&gt;GitHub Enterprise Server&lt;/td&gt;
&lt;td&gt;Signature wrapping&lt;/td&gt;
&lt;td&gt;Unauthenticated site administrator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-25291 and CVE-2025-25292 (2025)&lt;/td&gt;
&lt;td&gt;ruby-saml, REXML versus Nokogiri&lt;/td&gt;
&lt;td&gt;Parser differential enabling wrapping&lt;/td&gt;
&lt;td&gt;Unauthenticated GitLab admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-47949 (2025)&lt;/td&gt;
&lt;td&gt;samlify&lt;/td&gt;
&lt;td&gt;Wrapping via injected assertion&lt;/td&gt;
&lt;td&gt;Authenticate as any user (CVSS 9.9)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-46572 (2025)&lt;/td&gt;
&lt;td&gt;passport-wsfed-saml2&lt;/td&gt;
&lt;td&gt;Signature wrapping&lt;/td&gt;
&lt;td&gt;Impersonate any user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The obvious advice, &quot;stop using XML-DSig for new designs,&quot; is correct and nearly useless to the teams shipping these fixes. SAML still fronts enterprise single sign-on across large deployed systems, which is why the 2024 and 2025 breaks landed on GitLab and GitHub Enterprise rather than on toy projects [@cve-2024-45409] [@cve-2024-6800]. Ripping out a federation protocol is a multi-year migration that competes with every other roadmap item, so the pragmatic reality is that hardened SAML libraries have to keep existing. That tension is the honest core of one of this subject&apos;s live open problems.
&lt;p&gt;Now the defense, which is genuinely encouraging. The right patterns are not just written down; they are shipping. RFC 8725 codified the JWT discipline in 2020, and its allow-list-and-bind rules are the baseline new JOSE code is measured against [@rfc8725].&lt;/p&gt;
&lt;p&gt;The deterministic and canonical formats matured into real standards: deterministic CBOR (RFC 8949) and the JSON Canonicalization Scheme (RFC 8785) give designers a specified single form when they truly need one [@rfc8949] [@rfc8785]. And &quot;sign-the-encoding&quot; now anchors an entire generation of software supply-chain provenance: C2PA content credentials sign their manifests as COSE structures [@c2pa] [@rfc9052], Sigstore signs with ephemeral keys and records every signing event in a tamper-resistant transparency log [@sigstore] [@sigstore-docs], and the in-toto and SLSA frameworks carry signed, typed provenance through a build pipeline [@intoto] [@slsa].&lt;/p&gt;
&lt;p&gt;So the score in 2026 is split. On the tokens-and-provenance side, the field adopted sign-the-encoding and the class is receding. On the legacy-XML side, the attackers simply relocated the wedge to the seam between two parsers, where no single canonicalizer can see it. That split sharpens the real design question. When you &lt;em&gt;must&lt;/em&gt; sign structured data, which pattern do you pick, and what is the genuine, still-live tension between them?&lt;/p&gt;
&lt;h2&gt;7. Sign the Encoding, or Canonicalize First?&lt;/h2&gt;
&lt;p&gt;This is the one genuinely live design tension in the whole subject, and the honest framing is that it is a &lt;em&gt;hierarchy&lt;/em&gt;, not a contradiction. Three approaches compete, and history has already ranked them.&lt;/p&gt;
&lt;p&gt;At the top sits &lt;strong&gt;sign-the-encoding&lt;/strong&gt;: JWS, COSE, and the TLS 1.3 transcript all authenticate the exact bytes that get consumed [@rfc7515] [@rfc9052] [@rfc8446]. The middle option is &lt;strong&gt;canonicalize then sign&lt;/strong&gt;: reduce a structure to a single legal byte form (DER, deterministic CBOR, JCS) and sign that [@x690] [@rfc8949] [@rfc8785]. At the bottom, for legacy reasons only, sits &lt;strong&gt;XML-DSig with canonicalization&lt;/strong&gt;, which signs a mutable tree by reference and canonicalizes it at verify time [@xmldsig].&lt;/p&gt;
&lt;p&gt;Score them on what actually matters: which of the two moves each one forecloses, what verification costs, and what the empirical record says.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Move 1: ambiguous encoding&lt;/th&gt;
&lt;th&gt;Move 2: attacker-influenced binding&lt;/th&gt;
&lt;th&gt;Verify cost&lt;/th&gt;
&lt;th&gt;Empirical record&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Sign-the-encoding (JWS, COSE, TLS 1.3)&lt;/td&gt;
&lt;td&gt;Closed: signed bytes are the parsed bytes&lt;/td&gt;
&lt;td&gt;Closed when paired with domain separation&lt;/td&gt;
&lt;td&gt;Low: hash the exact bytes&lt;/td&gt;
&lt;td&gt;Clean where applied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canonicalize then sign (DER, dCBOR, JCS)&lt;/td&gt;
&lt;td&gt;Reduced: one form if the canonicalizer is correct&lt;/td&gt;
&lt;td&gt;Needs separate binding&lt;/td&gt;
&lt;td&gt;Medium: re-encode and byte-compare&lt;/td&gt;
&lt;td&gt;BERserk when done leniently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XML-DSig with C14N (reference by Id)&lt;/td&gt;
&lt;td&gt;Open: canonicalization ambiguity&lt;/td&gt;
&lt;td&gt;Open: reference and wrapping&lt;/td&gt;
&lt;td&gt;High: transforms plus canonicalize&lt;/td&gt;
&lt;td&gt;Worst: unbroken 2005 to 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The ranking falls out of the table. Sign-the-encoding &lt;em&gt;structurally&lt;/em&gt; removes move one, because there is no second serialization to disagree with the first. Canonicalization only &lt;em&gt;reduces&lt;/em&gt; move one, because it is exactly as trustworthy as the canonicalizer, and it does nothing about move two on its own. XML-DSig leaves both moves open, which is why its CVE record runs, unbroken, from 2005 to 2025.&lt;/p&gt;
&lt;p&gt;Two distinctions sharpen the choice. First, &lt;strong&gt;detached versus enveloped signatures&lt;/strong&gt;: a signature can wrap the data (enveloping), sit inside it (enveloped), or travel beside it (detached). The more the signature and the data can be rearranged relative to each other, the more room move two has to operate, which is precisely XML-DSig&apos;s problem.&lt;/p&gt;
&lt;p&gt;Second, and more often confused, &lt;strong&gt;message-layer versus transport-layer integrity&lt;/strong&gt;. TLS protects the &lt;em&gt;channel&lt;/em&gt;; it says nothing about a forged assertion carried &lt;em&gt;inside&lt;/em&gt; that channel. Every SAML break above happened over perfectly good TLS. The twenty-year SChannel story is about transport-layer integrity (&lt;a href=&quot;https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm/&quot; rel=&quot;noopener&quot;&gt;Rotating Every Cipher&lt;/a&gt;), a different and non-substitutable guarantee from a signed message that must survive being forwarded.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer signing the exact bytes the consumer parses. Treat canonicalize-then-sign as a spec-pinned, high-risk fallback, used only when a structure genuinely must be signed apart from its wire bytes, and always verified by re-encoding and byte-comparing, never by lenient parsing [@rfc8785] [@rfc8446].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;History has &lt;em&gt;largely&lt;/em&gt; adjudicated this tension in favor of the top of the hierarchy. But &quot;largely&quot; is carrying real weight in that sentence, because sometimes you inherit a rich format you cannot replace and a canonicalizer you cannot fully trust. Which raises the uncomfortable question the next section has to face: is there anything in this subject we cannot escape, no matter how much discipline we bring?&lt;/p&gt;
&lt;h2&gt;8. The Wall You Cannot Out-Discipline&lt;/h2&gt;
&lt;p&gt;Most of this article is about discipline: check the canonical form, sign the exact bytes, bind the context. But some of the subject is not a habit you can perfect. It is a wall, and it helps to know exactly where the wall stands.&lt;/p&gt;
&lt;p&gt;The first stone is provable. Length extension is not a bug in MD5 or SHA-256; it is a structural property of any hash whose output &lt;em&gt;is&lt;/em&gt; its full internal state. You cannot fix it by validating harder, adding checks, or trying again more carefully. You can only fix it by changing the construction: nest the hash (HMAC), switch to a sponge that never outputs its capacity (SHA-3), or withhold part of the state (truncated SHA-2) [@rfc2104] [@bowes]. No amount of care rescues &lt;code&gt;H(secret || m)&lt;/code&gt;, because the leak is in the shape, not the effort.Length extension is not a collision attack. Collision resistance -- the difficulty of finding two inputs with the same digest -- is a separate primitive property, treated in a sibling part of this guide. A hash can be flawlessly collision-resistant and still be trivially length-extendable, because the two properties constrain entirely different things [@bowes].&lt;/p&gt;
&lt;p&gt;The second stone is deeper, and it comes from language-theoretic security. When you canonicalize a rich format, you are asking a program to &lt;em&gt;fully recognize&lt;/em&gt; a complex input language, and that is not always a solvable problem.&lt;/p&gt;

For complex input languages, &quot;the problem of full recognition of valid or expected inputs may be UNDECIDABLE, in which case no amount of input-checking code or testing will suffice to secure the program.&quot; -- LangSec [@langsec]
&lt;p&gt;Read that carefully, because it is the ceiling on the entire canonicalization strategy [@langsec]. If deciding whether an input is well-formed can be undecidable for expressive grammars, then two independent parsers of such a grammar are not guaranteed to agree, ever, and no test suite can prove they do. Parser differentials like the 2025 REXML-versus-Nokogiri break are therefore not fully eliminable for rich formats [@cve-2025-25291]. Canonicalization can only ever be as trustworthy as the weakest recognizer that touches the document, and for a format like XML that recognizer is very complex indeed.&lt;/p&gt;
&lt;p&gt;The third stone follows from the second: for genuinely rich formats, a &lt;em&gt;universal&lt;/em&gt; canonical form may simply not exist. The JSON Canonicalization Scheme is instructive here, because even for the comparatively tame grammar of JSON it has to special-case string and number subtypes, spelling out in its Appendix E how stream- and schema-based parsers must treat numeric and date-like values so they do not diverge [@rfc8785]. Protocol Buffers refuses the goal outright: its documentation states the byte output is not stable across versions [@protobuf]. If the tamest formats need footnotes and the binary ones decline entirely, &quot;just canonicalize everything&quot; is not a general escape.&lt;/p&gt;
&lt;p&gt;The fourth stone is the one this whole guide rests on. A signature can never carry meaning that the encoding does not pin down. The primitive authenticates a string of bytes; if those bytes admit two readings, the signature endorses both, and no cryptographic strength changes that. The gap between bytes and meaning is &lt;em&gt;intrinsic&lt;/em&gt; to &quot;authenticate bytes,&quot; not an implementation defect waiting for a patch.&lt;/p&gt;
&lt;p&gt;So the reader who concluded, at the end of Section 5, &quot;fine, we will just canonicalize everything and sign the exact bytes, and be safe&quot; has to take one step back. Where you control the format and can sign the wire bytes, you really are safe from this class. Where you inherit a rich legacy format and must canonicalize a mutable tree, you are working under a proven ceiling, and the best you can do is shrink the attack surface, never close it. If that wall is real, the honest question is where this class is &lt;em&gt;still&lt;/em&gt; failing today, and what, if anything, could move the wall even slightly.&lt;/p&gt;
&lt;h2&gt;9. Where It Is Still Failing&lt;/h2&gt;
&lt;p&gt;The settled parts of this subject are settled &lt;em&gt;engineering&lt;/em&gt;. What follows is the honestly contested frontier, the places where a thoughtful designer in 2026 still has to make a judgment call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Universal canonicalization for rich formats.&lt;/strong&gt; JCS gives JSON a specified single form, but adoption is thin and its own Appendix E shows how many Unicode and numeric edge cases survive even for JSON [@rfc8785].A related canonicalization cousin bites one layer up, at &lt;em&gt;identifiers&lt;/em&gt;: Unicode normalization and homoglyph folding decide whether two usernames that look identical on screen -- a Latin letter and its Cyrillic look-alike, or a name carrying an invisible combining mark -- resolve to the same account. Get it wrong and one byte string quietly impersonates another before any signature is even checked. For richer grammars there is no comparable standard, and Section 8 explains why there may never be one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is XML-DSig salvageable?&lt;/strong&gt; The unbroken chain of wrapping and canonicalization CVEs from 2005 to 2025 is strong evidence, though not a proof, that the reference-by-&lt;code&gt;Id&lt;/code&gt; model cannot be made safe by patching, and that new systems should not adopt it [@somorovsky-2012] [@cve-2025-25291]. Reasonable engineers still disagree about whether hardened SAML libraries can be trusted or must be phased out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parser-differential defense across trust boundaries.&lt;/strong&gt; This is the deepest of the open problems, and the root of the 2025 GitLab bypass: any time two recognizers see the same bytes -- a proxy and an origin, a web application firewall and a backend, REXML and Nokogiri, an identity provider and a service provider -- they can disagree, and an attacker lives in the disagreement [@github-seclab-2025]. The same shape shows up one layer up as HTTP request smuggling [@kettle-2019] and JSON interoperability bugs from duplicate keys [@bishopfox-2020]. It is the systems-level restatement of the LangSec ceiling, and no one has a general defense.&lt;/p&gt;
&lt;p&gt;The next three problems are less famous but more tractable, and each has real, partial progress worth understanding in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Signing streamed, partial, or very large messages.&lt;/strong&gt; Sign-the-encoding wants the &lt;em&gt;exact transmitted bytes&lt;/em&gt;, but a streaming consumer must act on a &lt;em&gt;prefix&lt;/em&gt; before the whole message, and its signature, even exist, and you cannot buffer a multi-gigabyte asset in memory just to canonicalize it. The state of the art is Merkle-tree or hash-chain signing: hash each chunk, build a tree over the chunk hashes, and sign only the root, so every chunk carries a logarithmic-size inclusion proof and verifies on its own.&lt;/p&gt;
&lt;p&gt;C2PA deploys exactly this as its &lt;code&gt;c2pa.hash.bmff.v3&lt;/code&gt; assertion, a Merkle-tree hash over the ISO-BMFF &lt;code&gt;mdat&lt;/code&gt; box, so fragmented or streamed media segments (fMP4, DASH, HLS) each verify independently against one signed manifest, itself a detached COSE &lt;code&gt;Sign1&lt;/code&gt; structure [@c2pa] [@rfc9052]. It is strong and shipping for content-addressed, chunkable data. What stays unsettled is a low-latency, exact-byte guarantee for &lt;em&gt;arbitrary, non-chunkable&lt;/em&gt; streams: the guarantee is per chunk, not per message, and the chunk boundaries themselves become security-relevant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Making typed and context binding the default rather than an add-on.&lt;/strong&gt; Domain separation is almost always &lt;em&gt;advisory&lt;/em&gt;. Most formats let a signature omit its type, context, or algorithm, so binding is something a careful designer remembers rather than something the format enforces. A minority get it right structurally: EIP-712 folds a type hash and a domain separator inside the signed bytes, $\texttt{0x1901} ,|, \text{domainSeparator} ,|, \text{hashStruct}(m)$, and TLS 1.3&apos;s CertificateVerify signs 64 fixed octets, a role-specific context string, a separator, and the transcript hash, so a signature made in one role cannot be replayed in another [@eip712] [@rfc8446].&lt;/p&gt;
&lt;p&gt;JOSE is the cautionary opposite: it carries &lt;code&gt;alg&lt;/code&gt; in-band, so the token still names its own verification rule, and RFC 8725 can only make binding a &lt;em&gt;discipline&lt;/em&gt; -- enumerate algorithms, bind each key to one, reject &lt;code&gt;none&lt;/code&gt; -- not a structural property [@rfc8725]. That is why &lt;code&gt;alg:none&lt;/code&gt; keeps returning as a zombie default: RFC 7518 legalizes the &lt;code&gt;none&lt;/code&gt; algorithm, and verification APIs historically read the algorithm from the token, so the insecure path is reachable out of the box on every fresh implementation [@rfc7518].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Formally verified parsers, serializers, and canonicalizers.&lt;/strong&gt; For a &lt;em&gt;fixed&lt;/em&gt; grammar, the only route past the LangSec ceiling is a machine-checked proof that a parser recognizes exactly its grammar, round-trips with its serializer, and is &lt;em&gt;non-malleable&lt;/em&gt;, meaning each value has one accepted encoding. EverParse, presented at USENIX Security 2019, generates such parsers in the F* proof assistant and demonstrated them for TLS 1.0 through 1.3 in miTLS and for the ASN.1 DER payload of PKCS#1 RSA signatures [@everparse-2019]. The same line of work has since produced verified parsers for other network protocols, including QUIC [@everparse-quic], and has shipped inside the Windows kernel network stack, both steps beyond that original paper [@everparse-pldi-2022].&lt;/p&gt;
&lt;p&gt;ASN1*, at CPP 2023, built a verified non-malleable DER parser on top of EverParse, giving the X.509 and PKI format its unique-encoding guarantee by proof rather than by convention [@asn1star-2023]. The catch matches the theory exactly: verified non-malleable parsers ship for constrained &lt;em&gt;binary&lt;/em&gt; grammars, but a verified &lt;em&gt;canonicalizer&lt;/em&gt; for a &lt;em&gt;rich text&lt;/em&gt; format -- XML C14N, or JSON with the JCS Appendix E edge cases -- does not exist at industry scale, precisely as the no-universal-canonical-form limit predicts [@rfc8785].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Open problem&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;th&gt;What has been tried&lt;/th&gt;
&lt;th&gt;Best partial result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Signing streamed / very large messages&lt;/td&gt;
&lt;td&gt;Sign-the-encoding needs the exact bytes, but you cannot buffer a multi-gigabyte stream to canonicalize it&lt;/td&gt;
&lt;td&gt;Merkle-tree / chunk signing; C2PA Merkle-over-&lt;code&gt;mdat&lt;/code&gt; with a detached COSE &lt;code&gt;Sign1&lt;/code&gt; manifest&lt;/td&gt;
&lt;td&gt;Solved for chunkable, content-addressed data; open for low-latency non-chunkable streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typed / context binding by default&lt;/td&gt;
&lt;td&gt;Most formats leave domain separation optional, so designers forget it&lt;/td&gt;
&lt;td&gt;EIP-712 and TLS 1.3 bind it structurally; RFC 8725 binds it by discipline&lt;/td&gt;
&lt;td&gt;Structural in a few designs; still opt-in and zombie-prone in JOSE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formally verified parsers and canonicalizers&lt;/td&gt;
&lt;td&gt;Only a machine-checked proof escapes the LangSec ceiling for a fixed grammar&lt;/td&gt;
&lt;td&gt;EverParse (F*) and ASN1* verified non-malleable DER parsers&lt;/td&gt;
&lt;td&gt;Ships for binary grammars; no industry-scale verified canonicalizer for rich text&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

You can apply the two-move lens to a report you have never seen. Find two things in the advisory: the *authenticated span* (which exact bytes did the signature or MAC cover?) and the *verification rule* (how did the code decide the check passed?). Then ask which one the attacker touched. If they changed which bytes get parsed, or made one byte string parse two ways, it is move one. If they moved the authenticated span, or talked the verifier into a different rule, it is move two. Almost every advisory in this family resolves in one sentence once you ask those two questions.
&lt;p&gt;And then the callback that reframes everything. Suppose every system above had already migrated to post-quantum signatures, swapping RSA and ECDSA for ML-DSA (FIPS 204, module-lattice) or SLH-DSA (FIPS 205, stateless hash-based). How many of these breaks would that have prevented? &lt;em&gt;Almost none.&lt;/em&gt; Flickr&apos;s length extension, &lt;code&gt;alg:none&lt;/code&gt;, the RS256-to-HS256 confusion, and every SAML wrapping from 2005 to 2025 are all orthogonal to the signature algorithm: swap the primitive and the attack computes the same correct answer over the same wrong bytes [@fips204] [@fips205].&lt;/p&gt;
&lt;p&gt;The two exceptions prove the rule. Bleichenbacher&apos;s 2006 forgery and BERserk are RSA &lt;code&gt;e=3&lt;/code&gt; &lt;em&gt;malleability&lt;/em&gt; forgeries, and a non-malleable scheme -- RSA-PSS, or any strongly unforgeable (SUF-CMA) post-quantum signature -- already stops them, which is exactly why this article prescribed RSA-PSS as the BERserk fix; simply raising the public exponent to 65537 also denies the low-exponent construction its foothold [@rfc8017] [@valsorda]. Everything that remains is a bytes-not-meaning failure no algorithm transition can touch. That is the most important thing to carry out of this article, and it collapses the whole subject into one operational question, plus a checklist you can apply on Monday morning.&lt;/p&gt;
&lt;h2&gt;10. The Discipline, Made Operational&lt;/h2&gt;
&lt;p&gt;Everything above collapses into one decision procedure and one checklist of anti-patterns. Read the tree top to bottom and stop at the first rule that matches your situation.&lt;/p&gt;

flowchart TD
    Root[&quot;Choose the first rule that matches&quot;] --&amp;gt; Start{&quot;What are you authenticating?&quot;}
    Start --&amp;gt;|Shared-secret integrity| M[&quot;Use HMAC or KMAC, never H of secret then message&quot;]
    Start --&amp;gt;|You control the serialization| SE[&quot;Sign-the-encoding: JWS, COSE, TLS 1.3&quot;]
    Start --&amp;gt;|Structure detached from the wire| CE[&quot;Canonical encoding: DER, dCBOR, JCS, then byte-compare&quot;]
    Start --&amp;gt;|Binary or RPC such as protobuf| PB[&quot;Sign the exact received bytes or a hash of typed fields&quot;]
    Start --&amp;gt;|XML or SAML| XM[&quot;Prefer not, if forced use one parser and verify the consumed element&quot;]
    M --&amp;gt; Bind[&quot;Then always bind type, context, and algorithm&quot;]
    SE --&amp;gt; Bind
    CE --&amp;gt; Bind
    PB --&amp;gt; Bind
    XM --&amp;gt; Bind
&lt;p&gt;&lt;strong&gt;1. Shared-secret integrity?&lt;/strong&gt; Use HMAC (RFC 2104) or a SHA-3-based KMAC. Never build a MAC as &lt;code&gt;H(secret || m)&lt;/code&gt;. If some constraint forces a bare hash, choose one that is not length-extendable: SHA-512/256 or the SHA-3 family [@rfc2104] [@bowes].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Do you control the serialization, and does the consumer parse exactly what you signed?&lt;/strong&gt; Then sign the encoding: JWS, COSE, or the TLS 1.3 transcript, and you have closed move one by construction [@rfc7515] [@rfc9052] [@rfc8446].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Must you sign a structure detached from its wire bytes?&lt;/strong&gt; Use a canonical encoding -- DER, deterministic CBOR (RFC 8949), or JCS (RFC 8785) -- and verify by re-encoding and byte-comparing, never by lenient parsing [@x690] [@rfc8949] [@rfc8785].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Binary or RPC payloads such as protobuf?&lt;/strong&gt; Never sign freshly re-serialized bytes, because there is no canonical form. Sign the exact bytes you received, or sign a hash over explicit, typed fields with a domain separator [@protobuf].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Always, on top of rules 2 through 4, bind type, context, and algorithm.&lt;/strong&gt; For JWT specifically: pin an explicit &lt;code&gt;alg&lt;/code&gt; allow-list (prefer &lt;code&gt;EdDSA&lt;/code&gt; or &lt;code&gt;ES256&lt;/code&gt;), bind each key to exactly one algorithm, reject &lt;code&gt;none&lt;/code&gt; in secured contexts, validate &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;aud&lt;/code&gt;, and &lt;code&gt;exp&lt;/code&gt;, and treat header fields like &lt;code&gt;kid&lt;/code&gt;, &lt;code&gt;jku&lt;/code&gt;, and &lt;code&gt;x5u&lt;/code&gt; as attacker-controlled, since they invite path traversal and server-side request forgery [@rfc8725].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. XML or SAML?&lt;/strong&gt; Prefer not, for new designs. If you are stuck with it, use a vetted and patched library, parse the document exactly once and verify the &lt;em&gt;same&lt;/em&gt; element your application will consume, pin the schema, enforce &lt;code&gt;Id&lt;/code&gt; uniqueness, disable DTD and external-entity processing, reject comments and stray whitespace inside security-relevant nodes, and never let two different parsers touch one signed document [@somorovsky-2012] [@duo-2018] [@cve-2025-25291].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. RSA signatures?&lt;/strong&gt; Prefer RSA-PSS. If you must verify PKCS#1 v1.5, re-encode the expected &lt;code&gt;DigestInfo&lt;/code&gt; to strict DER and compare bytes, and never combine a public exponent of 3 with a lenient parser [@rfc8017] [@bleichenbacher-2006].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never share a single key across &lt;code&gt;RS256&lt;/code&gt; and &lt;code&gt;HS256&lt;/code&gt;, and never let one key serve two algorithms. The RS256-to-HS256 confusion is alive precisely because a public key that is safe to publish becomes an HMAC secret the attacker also knows the moment the verifier lets the token pick the algorithm. Bind every key to exactly one algorithm [@rfc8725] [@mclean-2015].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rule 5 is worth seeing as code, because the RFC 8725 discipline fits in a short function and every line closes one historical break. Note one easy-to-miss detail: a JWT segment is &lt;em&gt;base64url&lt;/em&gt;, not standard base64, so decoding it with a raw &lt;code&gt;atob&lt;/code&gt; on the segment is itself a latent bug.&lt;/p&gt;
&lt;p&gt;{`
// The header&apos;s &quot;alg&quot; is attacker-controlled. Never trust it -- pin your own set.
const ALLOWED = new Set([&quot;ES256&quot;, &quot;EdDSA&quot;]);   // no RSA-or-HMAC ambiguity in the mix&lt;/p&gt;
&lt;p&gt;// JWT segments are base64url, NOT standard base64: translate -_ to +/ and restore &apos;=&apos; padding.
function decodeSegment(seg) {
  let b64 = seg.replace(/-/g, &quot;+&quot;).replace(/_/g, &quot;/&quot;);
  while (b64.length % 4) b64 += &quot;=&quot;;           // base64url drops padding; put it back
  return JSON.parse(atob(b64));                // now standard base64 -&amp;gt; bytes -&amp;gt; JSON
}&lt;/p&gt;
&lt;p&gt;function verifyJwt(token, expected, keyForAlg, verifySignature) {
  const parts = token.split(&quot;.&quot;);
  const h64 = parts[0], p64 = parts[1], sig = parts[2];
  const header = decodeSegment(h64);&lt;/p&gt;
&lt;p&gt;  if (header.alg === &quot;none&quot;) throw new Error(&quot;alg none rejected in secured context&quot;);
  if (!ALLOWED.has(header.alg)) throw new Error(&quot;algorithm not on the allow-list&quot;);&lt;/p&gt;
&lt;p&gt;  // Bind key to algorithm: one key, one alg. Never reuse a public key as an HMAC secret.
  const key = keyForAlg(header.alg);
  if (!key) throw new Error(&quot;no key bound to this algorithm&quot;);
  if (!verifySignature(header.alg, key, h64 + &quot;.&quot; + p64, sig)) throw new Error(&quot;bad signature&quot;);&lt;/p&gt;
&lt;p&gt;  const claims = decodeSegment(p64);
  const now = Math.floor(Date.now() / 1000);
  if (claims.iss !== expected.iss) throw new Error(&quot;wrong issuer&quot;);
  if (claims.aud !== expected.aud) throw new Error(&quot;wrong audience&quot;);
  if (typeof claims.exp !== &quot;number&quot; || claims.exp &amp;lt; now) throw new Error(&quot;expired or missing exp&quot;);
  return claims;
}
`}&lt;/p&gt;
&lt;p&gt;The other side of the guide is the mirror: every anti-pattern you will meet in real code maps one-to-one to a named break from the failure catalog. If you recognize the left column in a code review, you already know the fix.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse pattern in real code&lt;/th&gt;
&lt;th&gt;Maps to 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;A MAC built as SHA-256 of secret then message&lt;/td&gt;
&lt;td&gt;Flickr, 2009&lt;/td&gt;
&lt;td&gt;HMAC or KMAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verifying an RSA signature by scanning for a DigestInfo&lt;/td&gt;
&lt;td&gt;Bleichenbacher 2006, BERserk 2014&lt;/td&gt;
&lt;td&gt;Re-encode to DER and byte-compare; RSA-PSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trusting the algorithm named in the JWT header&lt;/td&gt;
&lt;td&gt;alg none, RS256/HS256, 2015&lt;/td&gt;
&lt;td&gt;Allow-list; bind each key to one algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verifying one XML element and consuming another&lt;/td&gt;
&lt;td&gt;On Breaking SAML 2012; ruby-saml 2025&lt;/td&gt;
&lt;td&gt;One parser; verify the consumed element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing freshly re-serialized protobuf bytes&lt;/td&gt;
&lt;td&gt;protobuf non-determinism&lt;/td&gt;
&lt;td&gt;Sign received bytes or a typed-field hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two parsers touching one signed document&lt;/td&gt;
&lt;td&gt;ruby-saml 2025, REXML versus Nokogiri&lt;/td&gt;
&lt;td&gt;A single recognizer across the trust boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;On Windows, the HMAC, hashing, and signing calls this guide keeps invoking are not abstractions; they live in a specific place in the platform (&lt;a href=&quot;https://paragmali.com/blog/cng-architecture-bcrypt-ncrypt-ksps/&quot; rel=&quot;noopener&quot;&gt;the CNG architecture behind BCrypt and NCrypt&lt;/a&gt;), and choosing the right one is the first line of every rule above.&lt;/p&gt;
&lt;p&gt;Notice that every entry in both tables is the same rule wearing different clothes: make the bytes you authenticate be exactly the bytes you act on, and make them mean only one thing. Which is the single question this entire article has been walking toward, and the one worth pinning above your desk. Before that, a handful of misconceptions deserve to be named and corrected, because they are the folklore that keeps regenerating these bugs.&lt;/p&gt;
&lt;h2&gt;11. Misconceptions, Named and Corrected&lt;/h2&gt;
&lt;p&gt;The folklore around this subject is dense with half-truths, and each one causes real bugs. Here are the seven that do the most damage, every one of them dissolving into the two-move lens.&lt;/p&gt;

It is exactly the boundary where cryptography meets parsing, which is why it belongs in a cryptography field guide. The algorithm is always fine in these breaks; the attack surface is the *bytes* it was handed. Treating &quot;the crypto&quot; and &quot;the parsing&quot; as separate concerns is what lets the authenticated bytes drift from the consumed bytes in the first place.

No to both. The vulnerable thing is the *construction* `SHA-256(secret || message)` used as a MAC, not the hash itself: SHA-256 is not broken, and HMAC-SHA256 is not length-extendable. Section 4 carries the full reason its fixed outer structure makes HMAC immune even though the tag can be resumed, and why truncated SHA-2 and the SHA-3 family are not susceptible either [@bowes] [@rfc2104].

Because it keeps coming back as a zombie default in new libraries, and because its more dangerous cousin, RS256-to-HS256 key confusion, is very much alive. Both are the same move: the message chooses its own verification rule. It recurs by default because RFC 7518 legalizes the `none` algorithm and verify APIs read the algorithm from the token, so the insecure path ships reachable [@rfc7518]. The durable fix is RFC 8725&apos;s discipline of an explicit algorithm allow-list and one key bound to one algorithm [@mclean-2015] [@rfc8725].

No. DER is the canonical form with exactly one legal encoding per value; BER is the permissive form that allows many [@x690]. Verifying a signature by accepting lax BER where the specification requires DER is not a shortcut, it is the BERserk vulnerability, which forged RSA signatures in shipping browsers in 2014 [@mfsa-2014-73].

No. Protocol Buffers&apos; deterministic mode only guarantees stable output for a *particular* binary; the byte output can change across versions of your own software [@protobuf]. That is not a canonical form, so signing the serialized bytes remains undefined across versions. Sign the exact received bytes, or a hash over explicit typed fields, instead.

Almost none of it. The only exceptions are Bleichenbacher&apos;s 2006 forgery and BERserk, RSA `e=3` malleability forgeries that any non-malleable scheme such as RSA-PSS already stops [@rfc8017]; every other break here is orthogonal to the algorithm, so a post-quantum swap computes the same correct answer over the same wrong bytes [@fips204] [@fips205]. Section 9 works through why, and the conclusion returns to it as the point the whole article turns on.

No, and the distinction matters. Insecure deserialization is a code-execution topic: an attacker ships a serialized object that runs code when it is loaded. This family is about *data* whose bytes were authenticated ambiguously or bound loosely. Same word, &quot;serialization,&quot; entirely different failure and fix.
&lt;p&gt;Every one of these dissolves into a single sentence, and delivering that sentence with the full weight of the evidence behind it is the last thing left to do.&lt;/p&gt;
&lt;h2&gt;12. The Bytes Were Always the Wound&lt;/h2&gt;
&lt;p&gt;Line up the whole catalog. Flickr&apos;s forged API signature in 2009. Bleichenbacher&apos;s cube in 2006 and its rerun as BERserk in 2014. The &lt;code&gt;alg:none&lt;/code&gt; bypass and the RS256-to-HS256 confusion in 2015. Every XML signature-wrapping forgery from McIntosh and Austel in 2005 through the ruby-saml parser differential that took over GitLab logins in 2025. Protocol Buffers used as a signing input it was never fit to be. In almost every one of those breaks, the algorithm computed exactly what it was asked to compute. The hash hashed correctly. The RSA verification did precisely what the math prescribes. The signature was valid. The message was forged.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A post-quantum signature would have prevented &lt;em&gt;almost&lt;/em&gt; none of these breaks. The two exceptions -- Bleichenbacher&apos;s 2006 forgery and BERserk -- are RSA &lt;code&gt;e=3&lt;/code&gt; malleability forgeries that a non-malleable scheme like RSA-PSS already stops, which only sharpens the point: everything else rode on the gap between the bytes that were authenticated and the bytes that were consumed, and a stronger, or quantum-resistant, algorithm computes the same correct answer over the same wrong bytes. The encoding gap is orthogonal to the algorithm [@fips204] [@rfc8017].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Part 1 of this guide opened on the same note from the confidentiality side: Bleichenbacher did not break RSA in 1998, and the RSA math was always fine (&lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;Secure Against Whom?&lt;/a&gt;). It is fitting that the same researcher bridges both parts, because it is the same lesson pointed at two different seams. Confidentiality failed when the environment handed an adversary an oracle the proof never modeled. Integrity fails when the serialization hands an adversary a second reading the signature never pinned down. The primitive is not the variable. The bytes are.&lt;/p&gt;
&lt;p&gt;So carry one question out of all of this, and apply it the next time you sign, verify, or review anything: &lt;em&gt;are the bytes I authenticated exactly the bytes I will act on, and can they mean only one thing?&lt;/em&gt; If yes, this entire family of attacks has nowhere to stand. If you are not sure, you have just found the wedge before an attacker did.&lt;/p&gt;
&lt;p&gt;That question survives every algorithm transition the field will ever make. When today&apos;s signatures are replaced by their post-quantum successors, and those by whatever comes after, the lens will still read the breaks, because it was never about the algorithm. That is why serialization and canonicalization deserve a full part of a field guide for protocol designers, and not a footnote at the end of a chapter on hashing.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;serialization-and-canonicalization-attacks&quot; keyTerms={[
  { term: &quot;Merkle-Damgard construction&quot;, definition: &quot;An iterated hash design where a compression function folds padded blocks into a chaining value and the final chaining value is published as the digest&quot; },
  { term: &quot;Hash length extension&quot;, definition: &quot;Given H(secret then message) and the secret length, forging a valid tag for a longer message without knowing the secret, by resuming from the published state&quot; },
  { term: &quot;HMAC&quot;, definition: &quot;A message authentication code nesting two keyed hashes; length-extension-immune because its outer input is fixed at one key block plus one inner digest, and forging a new inner digest needs the secret&quot; },
  { term: &quot;Canonical (distinguished) encoding&quot;, definition: &quot;An encoding rule with exactly one legal byte string per value; DER is the archetype&quot; },
  { term: &quot;Parser differential&quot;, definition: &quot;When two implementations derive different structure from the same bytes, so the checked object and the consumed object diverge&quot; },
  { term: &quot;XML Signature Wrapping&quot;, definition: &quot;Relocating a genuinely signed element so verification finds it while the application consumes an attacker-authored element instead&quot; },
  { term: &quot;Algorithm and key confusion&quot;, definition: &quot;A JOSE attack where the token&apos;s own header chooses the verification rule, enabling alg none or reuse of a public key as an HMAC secret&quot; },
  { term: &quot;Sign-the-encoding&quot;, definition: &quot;Authenticating the exact serialized bytes the consumer parses, so no re-serialization or parser-differential gap can open&quot; },
  { term: &quot;Domain separation&quot;, definition: &quot;Binding type, context, and algorithm into the signed input so a signature cannot be replayed into a different meaning&quot; },
  { term: &quot;Signature malleability&quot;, definition: &quot;When an attacker can derive another accepted signature from a valid one without the key; a non-malleable scheme such as RSA-PSS forecloses it&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>serialization</category><category>canonicalization</category><category>signature-wrapping</category><category>hash-length-extension</category><category>jwt</category><category>asn1-der</category><category>protocol-design</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>