<?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: schnorr</title><description>Posts tagged schnorr.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sat, 25 Jul 2026 08:57:57 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/schnorr/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Discrete Log Held; The Proofs Leaked: A Field Guide to Commitments and Sigma Protocols</title><link>https://paragmali.com/blog/the-discrete-log-held-the-proofs-leaked-a-field-guide-to-com/</link><guid isPermaLink="true">https://paragmali.com/blog/the-discrete-log-held-the-proofs-leaked-a-field-guide-to-com/</guid><description>How the discrete log stayed hard for forty years while the proofs built on it kept breaking: Pedersen commitments, Schnorr, Sigma protocols, and Fiat-Shamir.</description><pubDate>Mon, 13 Jul 2026 20:58:24 GMT</pubDate><content:encoded>
A discrete-log zero-knowledge proof is one three-move conversation -- commit, challenge, respond -- and for forty years every real break landed on one of those moves, never on the discrete logarithm underneath. Move 1, the commitment: Pedersen hides perfectly and binds on the discrete log, but a trapdoor-holder can equivocate. Move 2, the Fiat-Shamir challenge: hash too little and you get weak Fiat-Shamir, the Frozen Heart bug that forged &quot;zero-knowledge&quot; proofs in Helios (2012) and in Bulletproofs and PlonK (2022) with the math untouched. Move 3, the response: reuse the nonce and the witness falls out by algebra; exploit its linearity across concurrent sessions and the ROS attack forges naive multisignatures in polynomial time. This is the field guide to those three moves -- Pedersen, Schnorr, Sigma protocols, Fiat-Shamir -- how they compose into Bulletproofs, MuSig2, FROST, Signal&apos;s credentials, and Privacy Pass, and every documented way each has been gotten wrong.
&lt;h2&gt;1. The Math Was Fine. The Proofs Were Forgeable.&lt;/h2&gt;
&lt;p&gt;In April 2022, Trail of Bits published a coordinated disclosure with an unsettling shape [@tob-part1]. Across three unrelated &quot;zero-knowledge&quot; proof systems -- Girault&apos;s classic proof of knowledge, Bulletproofs (the range proof securing confidential Monero transactions), and PlonK (a modern general-purpose SNARK) -- the proofs were &lt;em&gt;forgeable&lt;/em&gt;. An attacker could produce a convincing proof of a statement that was simply false [@tob-part1], [@tob-girault], [@tob-bp], [@tob-plonk].&lt;/p&gt;
&lt;p&gt;And in none of the three cases had anyone broken the discrete logarithm, or factoring, or any hard problem. The math was untouched. The bug was that a single hash had omitted part of the statement it was supposed to be about [@tob-part1].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In every Frozen Heart case, the discrete logarithm was never solved. The forgery lived entirely in what a hash function failed to bind. Soundness turned out to be a property of the protocol&apos;s plumbing, not of the hard problem beneath it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Bulletproofs instance was the sharpest. Trail of Bits traced it to an insecure Fiat-Shamir generation &lt;em&gt;recommended in the original 2017 Bulletproofs paper itself&lt;/em&gt; -- the flaw was in the specification, not a careless implementer&apos;s shortcut [@tob-bp], [@bulletproofs]. It received a CVE: CVE-2022-29566 [@cve]. A vulnerability class was born, and Trail of Bits named it. The word &quot;Frozen&quot; is the acronym -- FoRging Of ZEro-kNowledge proofs; &quot;Heart&quot; nods to the Fiat-Shamir transform that sits at the heart of these proof systems [@tob-part1].&lt;/p&gt;
&lt;p&gt;Then the twist that makes it a pattern, not an incident: this exact bug had already been named and published a decade earlier. In 2012, David Bernhard, Olivier Pereira, and Bogdan Warinschi showed that the Helios internet-voting system used the same shortcut -- and that its &quot;zero-knowledge&quot; ballot proofs could be transplanted and forged the same way [@bpw]. Two breaks, ten years apart, identical root cause, and the second one shipped in production behind a CVE.&lt;/p&gt;
&lt;p&gt;Now widen the aperture. The discrete logarithm -- the hard problem under Diffie-Hellman, Schnorr, ElGamal, and the whole family -- has resisted forty years of attack; &lt;a href=&quot;https://paragmali.com/blog/nobody-broke-the-discrete-log-a-field-guide-to-diffie-hellma/&quot; rel=&quot;noopener&quot;&gt;nobody broke the discrete log&lt;/a&gt;, as Part 18 argued at length. Yet the proof systems built on top of it break with some regularity. Why?&lt;/p&gt;
&lt;p&gt;Because a discrete-log zero-knowledge proof is not one thing. It is a three-move conversation -- &lt;strong&gt;commit, challenge, respond&lt;/strong&gt; -- and each move is a separate attack surface. The prover sends a commitment, the verifier sends a random challenge, the prover sends a response, and one equation decides the outcome.&lt;/p&gt;
&lt;p&gt;Frozen Heart and Helios are failures of the second move, the challenge. Reusing a signature nonce -- the catastrophe that &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;hands over a private key when one number repeats&lt;/a&gt; (Part 8) -- is a failure of the third move, the response. A trapdoor that lets a committer change its mind after the fact is a failure of the first move, the commitment. Three moves, three ways to leak, and not one of them is the discrete logarithm.&lt;/p&gt;
&lt;p&gt;That is the whole thesis of this article, stated once so you can hold it through everything that follows: &lt;strong&gt;the discrete log held; the three moves leaked.&lt;/strong&gt; The hard problem underneath these systems has never fallen. The conversation built on top of it has failed, repeatedly, in ways that are precise, named, and preventable.&lt;/p&gt;
&lt;p&gt;The reach of these three moves is easy to underestimate. The same commit-challenge-respond skeleton runs under Bitcoin&apos;s Taproot multisignatures, under Monero&apos;s confidential transaction amounts, under Signal&apos;s private group membership, and under the digital-identity credentials now standardizing at the IETF. Master the three moves and you have a lens that resolves an entire branch of applied cryptography -- and a checklist that separates a deployment that ships from one that ships a CVE.&lt;/p&gt;
&lt;p&gt;Those three moves were not invented together, and the cleanest one was invented first and deployed last -- frozen for nearly two decades by a patent [@schnorr-patent]. To see why the conversation has exactly three moves, and why each one leaks, we start where the vocabulary was born.&lt;/p&gt;
&lt;h2&gt;2. The Three-Move Conversation&lt;/h2&gt;
&lt;p&gt;Strip away the applications -- the multisignatures, the range proofs, the credentials -- and there is one shape underneath all of them. Learn that shape precisely, and every guarantee and every failure in this article becomes a statement about one of its three parts.&lt;/p&gt;
&lt;p&gt;The archetype is Schnorr&apos;s protocol for proving you know a discrete logarithm. You have a public key $y = g^x$ in a group of prime order $q$ generated by $g$, and you want to convince a verifier that you know the secret exponent $x$ without revealing it. The conversation has exactly three moves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Commit.&lt;/strong&gt; Pick a fresh random nonce $r$ and send the commitment $a = g^r$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Challenge.&lt;/strong&gt; The verifier picks a random $e$ and sends it back.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Respond.&lt;/strong&gt; Send $z = r + e\cdot x$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The verifier accepts if and only if $g^z = a\cdot y^e$. Check the algebra: $g^{r + e x} = g^r\cdot (g^x)^e = a\cdot y^e$. It works whenever the prover really knows $x$.&lt;/p&gt;

A three-move public-coin proof -- commitment, then a random challenge, then a response -- in which the verifier checks a single equation. It must satisfy two properties: special soundness and special honest-verifier zero-knowledge. Schnorr&apos;s proof of knowledge of a discrete logarithm is the archetype [@damgard].

sequenceDiagram
    participant P as Prover (knows x)
    participant V as Verifier (knows y)
    Note over P: pick fresh random nonce r
    Note over P: commitment a holds g to the r
    P-&amp;gt;&amp;gt;V: send commitment a
    Note over V: pick random challenge e
    V-&amp;gt;&amp;gt;P: send challenge e
    Note over P: response z holds r plus e times x
    P-&amp;gt;&amp;gt;V: send response z
    Note over V: check g to the z equals a times y to the e
&lt;p&gt;Why is this a &lt;em&gt;proof&lt;/em&gt;, and why does it reveal nothing? Both answers are visible by inspection, which is exactly why Schnorr is the teaching example.&lt;/p&gt;
&lt;h3&gt;Two accepting transcripts hand you the secret&lt;/h3&gt;
&lt;p&gt;Suppose a prover produces two accepting conversations that share the same commitment $a$ but have different challenges $e_1 \ne e_2$, with responses $z_1$ and $z_2$. Then $g^{z_1} = a\cdot y^{e_1}$ and $g^{z_2} = a\cdot y^{e_2}$. Divide the two equations and the $a$ cancels: $g^{z_1 - z_2} = y^{e_1 - e_2}$. Since $y = g^x$, the exponents must match, and you can solve for the witness:&lt;/p&gt;
&lt;p&gt;$$x = \frac{z_1 - z_2}{e_1 - e_2} \pmod q.$$&lt;/p&gt;
&lt;p&gt;This is &lt;strong&gt;special soundness&lt;/strong&gt;, and the algorithm that recovers $x$ is called the &lt;em&gt;extractor&lt;/em&gt;. It is what makes Schnorr a proof of &lt;em&gt;knowledge&lt;/em&gt;: a prover who can answer two different challenges on the same commitment demonstrably possesses $x$, because we could run the extractor and read it out [@damgard].&lt;/p&gt;

The property that two accepting transcripts sharing a commitment but with different challenges yield the witness through an efficient extractor. It is what upgrades a Sigma protocol from &quot;the statement is true&quot; to &quot;the prover actually knows a witness&quot; [@damgard].
&lt;p&gt;Hold on to that extractor. In Section 6 we will watch an attacker run exactly this formula -- same $a$, two different $e$ values -- against a signer who reused a nonce, and read out the private key. The defender&apos;s soundness proof and the attacker&apos;s key-recovery are the same three lines of algebra.&lt;/p&gt;
&lt;h3&gt;One accepting transcript hands you nothing&lt;/h3&gt;
&lt;p&gt;Now run the trick the other way. Pick the response $z$ and the challenge $e$ &lt;em&gt;first&lt;/em&gt;, both at random, and then define the commitment as $a = g^z\cdot y^{-e}$. By construction $g^z = a\cdot y^e$, so this transcript passes verification perfectly -- and you built it without knowing $x$ at all. The distribution of $(a, e, z)$ produced this way is identical to a real run.&lt;/p&gt;

The property that any accepting transcript can be produced without the witness by choosing the response and challenge first and back-solving for the commitment. The algorithm that does this is the simulator, and its existence proves the exchange reveals nothing about the secret [@damgard].
&lt;p&gt;That algorithm is the &lt;em&gt;simulator&lt;/em&gt;, and its existence is the proof that the conversation leaks nothing about $x$: anything a verifier sees, it could have generated by itself. The whole edifice of zero knowledge -- the idea that &quot;reveals nothing&quot; can be made mathematically precise by demanding a simulator -- traces to Goldwasser, Micali, and Rackoff in 1985 [@gmr]. Part 1 covers those &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;security definitions&lt;/a&gt; in full; here we take them as the bedrock they are.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A Sigma protocol is one three-move conversation -- commit, challenge, respond -- whose two guarantees are both visible by inspection. Two accepting transcripts on the same commitment extract the secret (special soundness); a single transcript can be simulated without the secret (special honest-verifier zero knowledge). Two transcripts hand you the key; one transcript hands you nothing. That knife-edge is the entire game.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The distinction the extractor forces is worth naming. A Sigma protocol is a &lt;strong&gt;proof of knowledge&lt;/strong&gt; -- the prover provably &lt;em&gt;possesses&lt;/em&gt; a witness -- not merely a &lt;strong&gt;proof of membership&lt;/strong&gt;, which would only assert that the statement is true. The gap matters: &quot;this public key has a corresponding private key&quot; is trivially true for every well-formed key, but &quot;I know that private key&quot; is the thing a signature must establish.&lt;/p&gt;

A proof that the prover actually possesses a witness, formalized by the existence of an extractor that could recover the witness from a successful prover. It is strictly stronger than a proof of membership, which asserts only that the statement is true [@damgard].

The simulator is not just a proof device; it is a construction tool. To prove &quot;I know the secret behind public key A OR the one behind B&quot; without revealing which, the prover *honestly* runs the branch it can answer and *simulates* the branch it cannot -- choosing that branch&apos;s challenge and response first and back-solving for its commitment, exactly as SHVZK allows. A shared challenge, split between the branches, ties them together so a cheater cannot simulate both at once. That is the Cramer-Damgard-Schoenmakers OR-proof, and it is why the toolkit composes into disjunctions, ring signatures, and selective-disclosure credentials [@cds].
&lt;p&gt;Here is that whole story in code. The extractor is not a metaphor; it is a five-line function that turns two transcripts into a private key.&lt;/p&gt;
&lt;p&gt;{`
// A toy prime-order group: the order-q subgroup of the integers mod p.
// p = 2039 is prime, q = 1019 is prime, and g = 4 has order exactly q.
const p = 2039n, q = 1019n, g = 4n;
const mod = (a, m) =&amp;gt; ((a % m) + m) % m;
function powmod(base, exp, m) {
  let r = 1n; base = mod(base, m);
  while (exp &amp;gt; 0n) { if (exp &amp;amp; 1n) r = mod(r * base, m); base = mod(base * base, m); exp &amp;gt;&amp;gt;= 1n; }
  return r;
}
const invModQ = (a) =&amp;gt; powmod(a, q - 2n, q);   // inverse mod prime q, via Fermat&lt;/p&gt;
&lt;p&gt;const x = 777n;                                 // the secret witness (never sent)
const y = powmod(g, x, p);                      // public key y = g^x&lt;/p&gt;
&lt;p&gt;// One honest run with nonce r and challenge e:
function transcript(r, e) {
  const a = powmod(g, r, p);                    // commit
  const z = mod(r + e * x, q);                  // respond
  return { a, e, z };
}
const verify = (t) =&amp;gt; powmod(g, t.z, p) === mod(t.a * powmod(y, t.e, p), p);&lt;/p&gt;
&lt;p&gt;const r = 314n;                                 // ONE nonce, reused across two challenges
const t1 = transcript(r, 11n);
const t2 = transcript(r, 90n);                  // same a, different e -- the fatal pair
console.log(&apos;t1 verifies = &apos; + verify(t1) + &apos;, t2 verifies = &apos; + verify(t2));&lt;/p&gt;
&lt;p&gt;// The extractor: x = (z1 - z2) / (e1 - e2) mod q
const xRecovered = mod((t1.z - t2.z) * invModQ(mod(t1.e - t2.e, q)), q);
console.log(&apos;recovered x = &apos; + xRecovered + &apos;, true x = &apos; + x + &apos;, match = &apos; + (xRecovered === x));
`}&lt;/p&gt;
&lt;p&gt;That is the knife-edge in code: two transcripts hand you the key, one hands you nothing -- so every guarantee and every failure ahead is a statement about one of the three moves. But this clean picture took eleven years and five separate ideas to assemble, and the cleanest idea of all sat frozen behind a patent.&lt;/p&gt;
&lt;h2&gt;3. Five Ideas and a Patent (1985-1996)&lt;/h2&gt;
&lt;p&gt;The three-move conversation was assembled one idea at a time, mostly by people who were not trying to build it. And the sharpest tension in the story is this: the cleanest, smallest scheme in the whole toolkit was invented in 1989 and could not be legally deployed at scale until its patent expired around 2008 [@schnorr-patent]. The best piece shipped last. Here is how five disconnected papers became a coherent toolkit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Goldwasser, Micali, and Rackoff (1985)&lt;/strong&gt; gave the field its vocabulary. Their paper on the knowledge complexity of interactive proof systems defined zero knowledge through the simulator argument -- a proof reveals nothing if a simulator can produce the same transcript without the secret [@gmr]. Without this definition, none of the three moves would be analyzable; &quot;reveals nothing&quot; would be a slogan rather than a theorem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fiat and Shamir (1986)&lt;/strong&gt; contributed two things in one paper. First, a practical zero-knowledge identification scheme based on factoring. Second, and far more consequential, the &lt;em&gt;transform&lt;/em&gt; that bears their name: replace the verifier&apos;s random challenge with the output of a hash function, and the interactive protocol collapses into a non-interactive one -- a signature [@fiat-shamir]. This is the birth of Move 2, and it arrived as a heuristic, a plausible trick without a proof. That gap between &quot;plausible&quot; and &quot;proven&quot; is where three decades of trouble would later live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schnorr (1989)&lt;/strong&gt; produced the efficient discrete-log archetype we dissected in Section 2: three moves, special soundness by inspection, and -- once you apply Fiat-Shamir -- the compact Schnorr signature.Schnorr&apos;s US Patent 4,995,082, granted in 1991, is widely reported to have expired around 2008. That is why patent-free DSA and later ECDSA colonized the internet through the 1990s, 2000s, and 2010s while Schnorr-proper waited, and why Bitcoin&apos;s BIP-340 could finally standardize native Schnorr signatures only in 2020 [@schnorr-patent], [@bip340]. Part 17 tells that &lt;a href=&quot;https://paragmali.com/blog/the-math-held-the-interface-leaked-a-field-guide-to-digital-/&quot; rel=&quot;noopener&quot;&gt;signature story&lt;/a&gt;. The irony is total: the scheme with the cleanest security argument was the one the market could not touch, so the industry standardized on messier alternatives and lived with them for twenty years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pedersen (1991)&lt;/strong&gt; was working on verifiable secret sharing, not commitments, when he handed the field its Move 1 workhorse.The Pedersen commitment debuted as a &lt;em&gt;byproduct&lt;/em&gt; of a non-interactive, information-theoretically secure verifiable secret sharing scheme [@pedersen]. The toolkit&apos;s most-used commitment arrived as a side effect of a paper about something else -- a recurring pattern in this history. His commitment $C = g^m\cdot h^r$ is perfectly hiding, computationally binding, and additively homomorphic [@pedersen] -- three properties we will spend all of Section 4 unpacking, because the entire range-proof line stands on the third one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cramer, Damgard, and Schoenmakers (1994)&lt;/strong&gt; turned Schnorr from a single protocol into a toolkit. Their proofs-of-partial-knowledge construction composes Sigma protocols with AND and OR: prove you know &lt;em&gt;one&lt;/em&gt; of two secrets without revealing which, by honestly answering the branch you know and &lt;em&gt;simulating&lt;/em&gt; the branch you do not [@cds].The name &quot;Sigma protocol&quot; was coined later, in Cramer&apos;s 1996 doctoral thesis. The letter is said to trace the three-move zig-zag of the conversation -- commitment down, challenge across, response back -- though the mnemonic is folklore. The OR-proof trick reuses the very simulator that proves zero knowledge, now as a constructive building block [@cds]. This is the deep reuse the article keeps returning to -- the simulator that proves a single proof leaks nothing is the same object that fakes the clauses you cannot answer in a disjunction. Okamoto&apos;s 1992 identification scheme sharpens the point: it is the witness-indistinguishable sibling of Schnorr, with a two-generator, Pedersen-shaped first move $y = g^{x_1} h^{x_2}$.Because many witness pairs $(x_1, x_2)$ open the same $y = g^{x_1} h^{x_2}$, an Okamoto transcript never reveals &lt;em&gt;which&lt;/em&gt; pair the prover used -- the scheme is witness-indistinguishable by construction [@okamoto]. That is exactly the property a CDS OR-proof leans on to hide which clause is the real one, and it is why the Pedersen shape shows up again the moment you want to hide a witness among many.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pointcheval and Stern (1996)&lt;/strong&gt; finally proved that Fiat-Shamir signatures are unforgeable -- in the random-oracle model -- with the forking lemma: rewind a successful forger, feed it a different challenge on the same commitment, and its two answers extract the discrete log, contradicting hardness [@pointcheval-stern-96], [@pointcheval-stern]. The proof works, but it is &lt;em&gt;loose&lt;/em&gt;: it loses a quadratic factor, and it does not extend cleanly to many sessions running at once. That looseness is not a blemish to be polished away later -- it is the first visible crack of the concurrency failure that Section 6 tears wide open.&lt;/p&gt;

flowchart LR
    A[&quot;1982 Chaum blinding&quot;] --&amp;gt; B[&quot;1985 GMR zero knowledge&quot;]
    B --&amp;gt; C[&quot;1986 Fiat-Shamir transform&quot;]
    C --&amp;gt; D[&quot;1989 Schnorr identification&quot;]
    D --&amp;gt; E[&quot;1991 Pedersen commitment&quot;]
    E --&amp;gt; F[&quot;1994 CDS OR-proofs&quot;]
    F --&amp;gt; G[&quot;1996 forking lemma&quot;]
    G --&amp;gt; H[&quot;2018 Bulletproofs&quot;]
    H --&amp;gt; I[&quot;2021 ROS and MuSig2&quot;]
    I --&amp;gt; J[&quot;2024 FROST RFC 9591&quot;]
    J --&amp;gt; K[&quot;2026 BBS drafts&quot;]
&lt;p&gt;By 1996 the conversation was complete and had a name. The rest of the story is what happened when people started &lt;em&gt;building&lt;/em&gt; with it -- and the failures organize themselves, cleanly, one per move. Start with the move you make first: the commitment.&lt;/p&gt;
&lt;h2&gt;4. Move 1: What You Bind&lt;/h2&gt;
&lt;p&gt;Every proof begins by pinning down a value you are not yet ready to reveal. A commitment is the cryptographic envelope: you seal a value now and open it later, and the sealing must satisfy two opposing demands. It must be &lt;strong&gt;hiding&lt;/strong&gt; -- the sealed envelope leaks nothing about what is inside -- and &lt;strong&gt;binding&lt;/strong&gt; -- you cannot swap the contents after sealing. There are two standard ways to build one, and they sit at opposite corners of a trade-off you cannot escape.&lt;/p&gt;

A protocol to lock in a value now and reveal it later, such that the commitment leaks nothing about the value (hiding) and cannot be opened to a different value than the one committed (binding). No scheme can make both properties perfect at once [@damgard].
&lt;h3&gt;The Pedersen corner&lt;/h3&gt;
&lt;p&gt;Pedersen&apos;s commitment lives in a group of prime order $q$ with two generators $g$ and $h$, where nobody knows the discrete logarithm $\log_g h$. To commit to a message $m$, pick a random blinding scalar $r$ and publish&lt;/p&gt;
&lt;p&gt;$$C = g^m\cdot h^r.$$&lt;/p&gt;
&lt;p&gt;It has three properties, and each one matters downstream [@pedersen]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Perfectly hiding.&lt;/strong&gt; Because $r$ is uniform, $h^r$ is a uniform group element, so $C$ is uniformly distributed regardless of $m$. Even an adversary with unlimited computing power learns &lt;em&gt;nothing&lt;/em&gt; about $m$ from $C$. This is information-theoretic, not merely computational.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Computationally binding.&lt;/strong&gt; Opening $C$ to two different messages means finding $(m, r) \ne (m&apos;, r&apos;)$ with $g^m h^r = g^{m&apos;} h^{r&apos;}$, which rearranges to $\log_g h = (m - m&apos;)/(r&apos; - r)$. In other words, breaking binding &lt;em&gt;is&lt;/em&gt; computing a discrete logarithm. The binding reduction is not near the hard problem; it &lt;em&gt;is&lt;/em&gt; the hard problem [@pedersen].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Additively homomorphic.&lt;/strong&gt; Multiply two commitments and the exponents add: $C(m_1, r_1)\cdot C(m_2, r_2) = C(m_1 + m_2,\ r_1 + r_2)$. You can add committed values without opening them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That third property is the one the whole range-proof line stands on, so hold it in mind. But first, the precision that must never be smoothed over.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A Pedersen commitment is perfectly &lt;em&gt;hiding&lt;/em&gt; and only &lt;em&gt;computationally&lt;/em&gt; binding. Anyone who knows the trapdoor $\log_g h$ can &lt;strong&gt;equivocate&lt;/strong&gt;: open a single commitment to any message they like, by solving one linear equation for a matching blinder. The security of the whole scheme rests on nobody knowing that trapdoor -- which makes the generation of $h$ a security-critical step, not a formality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Equivocation is the entire Move-1 failure class. If the party who set up the parameters secretly chose $h = g^t$ and kept $t$, then for any commitment $C = g^m h^r$ they can produce a blinder $r&apos;$ that opens it to a different message $m&apos;$ -- because $C = g^{m + t r}$, and they can solve $m&apos; + t r&apos; \equiv m + t r$ for $r&apos;$. The defense is to make the trapdoor un-knowable to everyone.The second generator $h$ must have unknown $\log_g h$, so it is derived by a nothing-up-my-sleeve procedure -- hash a fixed string to a curve point, so that no party could have planted a trapdoor. Part 16 covers &lt;a href=&quot;https://paragmali.com/blog/the-doorway-into-the-group-a-field-guide-to-hashing-a-string/&quot; rel=&quot;noopener&quot;&gt;hashing a string into the group&lt;/a&gt;; the takeaway here is that &quot;pick $h$ honestly&quot; is a real engineering requirement, not a throwaway line.&lt;/p&gt;
&lt;h3&gt;The hash corner&lt;/h3&gt;
&lt;p&gt;The dual construction throws away the algebra. Commit to $m$ by publishing &lt;code&gt;H(m ‖ r)&lt;/code&gt; for a random $r$ and a collision-resistant hash &lt;code&gt;H&lt;/code&gt;. This is computationally hiding and computationally binding -- and its binding rests on collision resistance, not on the discrete logarithm, which makes it the post-quantum-friendly choice. What it gives up is homomorphism: there is no way to add two hash commitments and get a commitment to the sum. Hashes shred structure by design.&lt;/p&gt;
&lt;p&gt;Why can you not have everything -- perfect hiding &lt;em&gt;and&lt;/em&gt; perfect binding? Because it is impossible. If a commitment is perfectly hiding, then $C$ is statistically independent of $m$, which means some opening to every message must exist; a computationally unbounded opener could always equivocate, so binding cannot also be perfect [@damgard]. Pedersen sits at the perfectly-hiding end; hash commitments sit near the perfectly-binding end; nothing sits at both.&lt;/p&gt;

flowchart TD
    Q{&quot;What does the job need?&quot;}
    Q --&amp;gt;|&quot;add or range-prove values&quot;| PED[&quot;Pedersen commitment g to the m times h to the r&quot;]
    Q --&amp;gt;|&quot;post-quantum binding, no algebra&quot;| HASH[&quot;Hash commitment H of m with r&quot;]
    PED --&amp;gt; P1[&quot;perfectly hiding&quot;]
    PED --&amp;gt; P2[&quot;computationally binding on the discrete log&quot;]
    PED --&amp;gt; P3[&quot;additively homomorphic&quot;]
    PED --&amp;gt; P4[&quot;trapdoor holder can equivocate&quot;]
    HASH --&amp;gt; H1[&quot;computationally hiding and binding&quot;]
    HASH --&amp;gt; H2[&quot;not homomorphic&quot;]
    HASH --&amp;gt; H3[&quot;post-quantum from collision resistance&quot;]
&lt;p&gt;Here is the corner choice as a table you can keep next to a design review.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Pedersen &lt;code&gt;g^m · h^r&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Hash &lt;code&gt;H(m ‖ r)&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hiding&lt;/td&gt;
&lt;td&gt;Perfect (information-theoretic)&lt;/td&gt;
&lt;td&gt;Computational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binding&lt;/td&gt;
&lt;td&gt;Computational (discrete log)&lt;/td&gt;
&lt;td&gt;Computational (collision resistance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Homomorphic&lt;/td&gt;
&lt;td&gt;Yes, additively&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-quantum&lt;/td&gt;
&lt;td&gt;No -- Shor breaks binding&lt;/td&gt;
&lt;td&gt;Yes -- binding survives Shor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commitment size&lt;/td&gt;
&lt;td&gt;~32 bytes (one ristretto255 point)&lt;/td&gt;
&lt;td&gt;~32 bytes (one SHA-256 output)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equivocation risk&lt;/td&gt;
&lt;td&gt;Trapdoor holder of &lt;code&gt;log_g h&lt;/code&gt; opens to anything&lt;/td&gt;
&lt;td&gt;None -- needs a hash collision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best suited for&lt;/td&gt;
&lt;td&gt;Confidential amounts, range proofs, sums&lt;/td&gt;
&lt;td&gt;PQ binding, timestamps, no algebra needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The homomorphism is not a curiosity; it is the concrete cash-in of Move 1. Committed values that add correctly are exactly what confidential transactions need -- inputs and outputs whose hidden amounts must balance -- and they are the value commitments that a range proof later proves lie in a valid range. Section 7 follows that thread from Pedersen to Bulletproofs. First, watch the homomorphism and the equivocation trapdoor in code.&lt;/p&gt;
&lt;p&gt;{`
const p = 2039n, q = 1019n, g = 4n;
const mod = (a, m) =&amp;gt; ((a % m) + m) % m;
function powmod(base, exp, m) {
  let r = 1n; base = mod(base, m);
  while (exp &amp;gt; 0n) { if (exp &amp;amp; 1n) r = mod(r * base, m); base = mod(base * base, m); exp &amp;gt;&amp;gt;= 1n; }
  return r;
}
// In a real system NOBODY knows t; we plant it here only to demonstrate equivocation.
const t = 503n;                                  // the trapdoor: t = log_g h
const h = powmod(g, t, p);
const commit = (m, r) =&amp;gt; mod(powmod(g, m, p) * powmod(h, r, p), p);&lt;/p&gt;
&lt;p&gt;// 1) Additive homomorphism: C(m1,r1) * C(m2,r2) opens to (m1+m2, r1+r2)
const C1 = commit(100n, 7n), C2 = commit(250n, 9n);
const product = mod(C1 * C2, p);
const summed  = commit(mod(100n + 250n, q), mod(7n + 9n, q));
console.log(&apos;homomorphism holds = &apos; + (product === summed));&lt;/p&gt;
&lt;p&gt;// 2) Equivocation: the trapdoor holder opens ONE commitment to a different message
const m = 100n, r = 7n, C = commit(m, r);
const mFake = 999n;
const rFake = mod(r + (m - mFake) * powmod(t, q - 2n, q), q);   // solve m&apos;+t r&apos; = m+t r
console.log(&apos;same C reopened to &apos; + mFake + &apos; = &apos; + (commit(mFake, rFake) === C));
`}&lt;/p&gt;
&lt;p&gt;A Pedersen commitment binds a value the verifier cannot see. But an interactive proof about it needs a live verifier to send a random challenge -- and live verifiers do not scale to signatures or blockchains. The fix is to fake the verifier with a hash. That single fix is the most-broken move in the toolkit.&lt;/p&gt;
&lt;h2&gt;5. Move 2: What Goes Into the Hash&lt;/h2&gt;
&lt;p&gt;The transform that makes the whole toolkit shippable is one line of code -- and that one line has been written wrong, in production, for thirty years.&lt;/p&gt;
&lt;p&gt;The interactive Sigma protocol needs a live verifier to supply a random challenge $e$. That is fine for a login handshake and useless for a signature or a blockchain, where there is no verifier standing by. Fiat and Shamir&apos;s fix was to &lt;em&gt;derive&lt;/em&gt; the challenge from a hash of the transcript so far: compute $e = H(\dots)$ yourself and proceed [@fiat-shamir]. In the random-oracle model -- where the hash is treated as a truly random function -- the hash &lt;em&gt;is&lt;/em&gt; the verifier, and the interactive proof becomes a non-interactive one.&lt;/p&gt;

A method to make an interactive Sigma protocol non-interactive by replacing the verifier&apos;s random challenge with a hash of the transcript. The prover computes the challenge itself, turning a proof of knowledge into a signature or an on-chain proof [@fiat-shamir].

An idealization that treats a hash function as a truly random function answering consistently to repeated queries. It is the model in which Fiat-Shamir soundness is provable; Part 10 covers the [hash machinery](/blog/the-fingerprint-two-files-shared-a-field-guide-to-cryptograp/) it idealizes [@fiat-shamir].
&lt;p&gt;Everything now rides on what you put inside that hash. And this is where the failures cluster, because the answer is not obvious and the wrong answer still runs.&lt;/p&gt;
&lt;h3&gt;The decision rule&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Strong Fiat-Shamir&lt;/strong&gt; binds everything the proof is about:&lt;/p&gt;
&lt;p&gt;$$e = H(\texttt{domain_separator} ,|, \text{statement} ,|, \text{public params} ,|, a ,|, \text{transcript}).$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weak Fiat-Shamir&lt;/strong&gt; hashes only the commitment, $e = H(a)$, and omits the statement. That omission is the bug. When the challenge does not depend on the statement, a proof built for one statement can be transplanted onto another -- a statement it was never made for -- and it still verifies [@bpw]. The extreme case is a signature: if the message is not in the hash, a signature on &quot;pay 5&quot; is a valid signature on &quot;pay 5000,&quot; because verification never touches the message.&lt;/p&gt;

flowchart TD
    A[&quot;Sigma commitment a and statement x&quot;] --&amp;gt; B{&quot;What enters the challenge hash?&quot;}
    B --&amp;gt;|&quot;weak FS: only a&quot;| W[&quot;e is H of a&quot;]
    B --&amp;gt;|&quot;strong FS: statement, params, a&quot;| S[&quot;e is H of x with params and a&quot;]
    W --&amp;gt; W2[&quot;challenge ignores the statement&quot;]
    W2 --&amp;gt; W3[&quot;transplant the proof onto a false x prime&quot;]
    W3 --&amp;gt; W4[&quot;forgery verifies&quot;]
    S --&amp;gt; S2[&quot;challenge binds the exact statement&quot;]
    S2 --&amp;gt; S3[&quot;transplant recomputes a different e&quot;]
    S3 --&amp;gt; S4[&quot;forgery rejected&quot;]
&lt;p&gt;You can watch the transplant happen and then watch the fix stop it. In the model below, the &quot;statement&quot; is the signed message; weak Fiat-Shamir lets one signature verify against any message, and strong Fiat-Shamir binds it.&lt;/p&gt;
&lt;p&gt;{`
const p = 2039n, q = 1019n, g = 4n;
const mod = (a, m) =&amp;gt; ((a % m) + m) % m;
function powmod(base, exp, m) {
  let r = 1n; base = mod(base, m);
  while (exp &amp;gt; 0n) { if (exp &amp;amp; 1n) r = mod(r * base, m); base = mod(base * base, m); exp &amp;gt;&amp;gt;= 1n; }
  return r;
}
function H(str) { let h = 0n; for (const ch of str) h = mod(h * 131n + BigInt(ch.charCodeAt(0)), q); return mod(h, q); }
const x = 777n, y = powmod(g, x, p);
const r = 421n;   // nonce hardcoded only so the demo is deterministic&lt;/p&gt;
&lt;p&gt;// WEAK Fiat-Shamir: the challenge hashes only the commitment a (message omitted)
const signWeak = () =&amp;gt; { const a = powmod(g, r, p); const e = H(a.toString()); return { a, z: mod(r + e * x, q) }; };
const verifyWeak = (msg, s) =&amp;gt; { const e = H(s.a.toString()); return powmod(g, s.z, p) === mod(s.a * powmod(y, e, p), p); };
const sigW = signWeak();
console.log(&apos;weak  valid on original  = &apos; + verifyWeak(&apos;alice pays 5&apos;, sigW));
console.log(&apos;weak  valid on FORGED    = &apos; + verifyWeak(&apos;alice pays 5000&apos;, sigW));  // true -&amp;gt; universal forgery&lt;/p&gt;
&lt;p&gt;// STRONG Fiat-Shamir: the challenge binds the public key AND the message AND a
const signStrong = (msg) =&amp;gt; { const a = powmod(g, r, p); const e = H(y.toString() + &apos;|&apos; + msg + &apos;|&apos; + a.toString()); return { a, z: mod(r + e * x, q) }; };
const verifyStrong = (msg, s) =&amp;gt; { const e = H(y.toString() + &apos;|&apos; + msg + &apos;|&apos; + s.a.toString()); return powmod(g, s.z, p) === mod(s.a * powmod(y, e, p), p); };
const sigS = signStrong(&apos;alice pays 5&apos;);
console.log(&apos;strong valid on original = &apos; + verifyStrong(&apos;alice pays 5&apos;, sigS));
console.log(&apos;strong valid on FORGED   = &apos; + verifyStrong(&apos;alice pays 5000&apos;, sigS)); // false -&amp;gt; rejected
`}&lt;/p&gt;
&lt;h3&gt;The same bug, forged twice, a decade apart&lt;/h3&gt;
&lt;p&gt;This is not a hypothetical. It is the single most-repeated real-world break in the toolkit, and it has a body count.&lt;/p&gt;
&lt;p&gt;In 2012, Bernhard, Pereira, and Warinschi formalized the weak-versus-strong distinction and showed that the Helios internet-voting system used weak Fiat-Shamir. Its ballot proofs -- the &quot;zero-knowledge&quot; proofs that a vote is well-formed -- could be transplanted and forged [@bpw]. The paper&apos;s title is a warning label: &lt;em&gt;How Not to Prove Yourself&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ten years later, in April 2022, Trail of Bits disclosed the same class of bug in three live systems at once: Girault&apos;s proof of knowledge, Bulletproofs, and PlonK [@tob-part1], [@tob-girault], [@tob-bp], [@tob-plonk]. The Bulletproofs instance was traced to an insecure Fiat-Shamir generation recommended in the original 2017 paper, and it carried CVE-2022-29566 [@tob-bp], [@cve], [@bulletproofs].Note the attribution carefully: &quot;Girault&quot; is the name of an &lt;em&gt;affected scheme&lt;/em&gt; (a proof of knowledge in an RSA group), not the researcher. The disclosure was by Jim Miller at Trail of Bits [@tob-part1], [@tob-girault].&lt;/p&gt;

&quot;We&apos;ve dubbed this class of vulnerabilities Frozen Heart. The word frozen is an acronym for FoRging Of ZEro kNowledge proofs ... a mistake in the original academic paper, in which the authors recommend an insecure Fiat-Shamir generation.&quot; -- Trail of Bits [@tob-part1]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Helios in 2012 and Frozen Heart in 2022 are the same failure: a challenge hash that omitted the statement. In neither case did anyone break the discrete logarithm. The security lived in one line of the hash, and that line was wrong -- in a shipped e-voting system and in a peer-reviewed range-proof paper -- ten years apart [@bpw], [@tob-part1].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Bind the whole statement and the challenge is safe. But Fiat-Shamir also turns the Schnorr &lt;em&gt;identification&lt;/em&gt; protocol into the Schnorr &lt;em&gt;signature&lt;/em&gt; -- and that hands the third move, the response, a fresh way to leak. This one does not even need a mistake in the hash. It needs a mistake in a single random number.&lt;/p&gt;
&lt;h2&gt;6. Move 3: How the Response Behaves&lt;/h2&gt;
&lt;p&gt;The response $z = r + e\cdot x$ is the only message in the whole conversation that touches the secret, and it protects that secret with exactly one thing: a fresh, secret, one-time nonce $r$. Everything the third move can suffer is a story about that nonce -- a number that is never even transmitted, yet is where the key most often escapes.&lt;/p&gt;
&lt;p&gt;Start with the bridge that Fiat-Shamir just built. Apply the transform to Schnorr&apos;s identification protocol and you get the Schnorr signature: the commitment $a$ and response $z$ are the signature, and the challenge is $e = H(y ,|, m ,|, a)$. Read that carefully, because it is one of the article&apos;s quiet revelations: &lt;strong&gt;a Schnorr signature is a non-interactive proof of knowledge of the private key.&lt;/strong&gt; Signing is proving you know $x$; the message rides along in the challenge hash [@bip340].&lt;/p&gt;
&lt;h3&gt;Reuse the nonce, lose the key&lt;/h3&gt;
&lt;p&gt;Now recall the extractor from Section 2. Two accepting transcripts that share the commitment $a$ but differ in the challenge $e$ yield the witness $x = (z_1 - z_2)/(e_1 - e_2)$. That was the &lt;em&gt;defender&apos;s&lt;/em&gt; proof that Schnorr is a proof of knowledge. Turn it around: if a signer produces two signatures with the same nonce $r$, they produce two transcripts with the same $a = g^r$ and different challenges. The attacker runs the same three lines of algebra the extractor runs, and the private key falls out.&lt;/p&gt;
&lt;p&gt;This is the identical failure as a reused ECDSA nonce -- the catastrophe covered in Parts 8 and 17 -- because both signature schemes hide the key behind a per-signature nonce, and both surrender it the moment the nonce repeats.There is a sharp distinction worth carrying. The reused-nonce break in &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;Part 8&lt;/a&gt; happens &lt;em&gt;within&lt;/em&gt; a single scheme when one nonce is used for two signatures. The ROS break below happens &lt;em&gt;across&lt;/em&gt; many concurrent sessions of a multi-party scheme, exploiting the linearity of the response rather than a literal repeat. Same move, two different exploits. A biased nonce -- not repeated, just slightly non-uniform -- leaks the key more slowly, through lattice attacks that aggregate many partial leaks. The discipline is absolute: nonces must be fresh, secret, and uniform, whether drawn from a &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;well-seeded CSPRNG or derived deterministically&lt;/a&gt; as Part 2 describes.&lt;/p&gt;
&lt;h3&gt;Why concurrency is the hard part&lt;/h3&gt;
&lt;p&gt;Pointcheval and Stern&apos;s forking lemma proves single-session Schnorr unforgeable in the random-oracle model: rewind a forger, give it a fresh challenge on the same commitment, and its two answers extract the discrete log, which is assumed hard [@pointcheval-stern]. The proof works, but it pays a price -- a quadratic loss in tightness -- and, more importantly, the rewinding trick does not compose when many sessions run at once. You cannot cleanly rewind one of a hundred interleaved conversations without disturbing the rest.&lt;/p&gt;

The rewinding technique of Pointcheval and Stern that turns a random-oracle forger into a discrete-log solver by running it twice with different challenges on the same commitment. It gives Schnorr and Fiat-Shamir their ROM security proof, but loosely -- with a quadratic security loss -- and only for the single-session setting [@pointcheval-stern].
&lt;p&gt;That gap between &quot;one session is provably secure&quot; and &quot;many concurrent sessions are secure&quot; stayed hidden for two decades. Then it was pried open.&lt;/p&gt;
&lt;h3&gt;The ROS attack&lt;/h3&gt;
&lt;p&gt;In 2021, Benhamouda, Lepoint, Loss, Orru, and Raykova published a polynomial-time solution to the ROS problem, and with it broke naive two-round Schnorr multisignatures and blind Schnorr signatures [@ros]. The mechanism is pure linear algebra. Open enough concurrent signing sessions -- more than about $\log_2 p$ of them -- collect the responses, and set up a linear system whose solution forges a signature on a message the signers never agreed to. No discrete logarithm is solved anywhere in the attack.&lt;/p&gt;

&quot;Random inhomogeneities in an Overdetermined, Solvable system of linear equations.&quot; It is solvable in polynomial time once the number of concurrent sessions exceeds roughly $\log_2 p$, which is exactly what breaks naive linear Schnorr multi-signatures and blind signatures. It is emphatically NOT &quot;Random Oracle Substitution,&quot; a common web mistranslation [@ros].
&lt;p&gt;The 2021 result had a predecessor. In 2019, Drijvers and co-authors had already shown, at IEEE S&amp;amp;P, that the same naive two-round multisignatures were insecure under concurrency -- their attack ran in sub-exponential time using Wagner&apos;s generalized-birthday (k-sum) algorithm [@drijvers], [@wagner]. The 2021 ROS work sharpened sub-exponential into polynomial. Together they closed the case: naive linear Schnorr multisignatures are not merely hard to prove secure under concurrency; they are concretely broken.&lt;/p&gt;

flowchart TD
    Z[&quot;response z holds r plus e times x&quot;] --&amp;gt; B{&quot;how is the nonce misused?&quot;}
    B --&amp;gt;|&quot;reuse or bias one nonce&quot;| R1[&quot;two transcripts share commitment a&quot;]
    R1 --&amp;gt; R2[&quot;extractor recovers x from two responses&quot;]
    B --&amp;gt;|&quot;open many concurrent sessions&quot;| C1[&quot;responses are linear across sessions&quot;]
    C1 --&amp;gt; C2[&quot;ROS solves the linear system&quot;]
    C2 --&amp;gt; C3[&quot;forge a signature, no discrete log solved&quot;]

No discrete log is ever solved. The response simply leaks across concurrent sessions, and linear algebra does the rest.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The simple &quot;everyone sends a nonce, everyone sends a response&quot; two-round multisignature is forgeable under concurrent sessions -- sub-exponentially since Drijvers (S&amp;amp;P 2019), and in polynomial time since ROS (2021). Use MuSig2 (n-of-n) or FROST (t-of-n), which are built specifically to defeat this attack [@drijvers], [@ros].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Move 1 can be equivocated, Move 2 can be transplanted, Move 3 can be solved by linear algebra -- and not one of these touched the discrete logarithm. That is the thesis, proven three times. The story from here is how each sub-lineage climbed &lt;em&gt;out&lt;/em&gt; of its break, while the three moves kept running underneath.&lt;/p&gt;
&lt;h2&gt;7. What Superseded What (and What Never Did)&lt;/h2&gt;
&lt;p&gt;Here is the classic mistake this article exists to correct: there is no single &quot;what superseded what&quot; ladder for this toolkit. The three moves are a &lt;em&gt;substrate&lt;/em&gt;. Supersession happens in the &lt;em&gt;composites&lt;/em&gt; built on top of them, and there are exactly &lt;strong&gt;three construction ladders&lt;/strong&gt; -- one per application: range proofs, multi-party Schnorr, and anonymous credentials. Every rung was forced by a break or a cost on a specific move, never by an advance against the discrete logarithm.&lt;/p&gt;
&lt;p&gt;The one change to the foundation itself -- weak Fiat-Shamir hardened to strong -- is not a fourth ladder but a fix applied in place, so we take it first.&lt;/p&gt;

flowchart TD
    subgraph FOUND[&quot;Foundations, never superseded, Move 2 hardened weak to strong in place&quot;]
        F1[&quot;Move 1 Pedersen&quot;]
        F2[&quot;Move 2 Fiat-Shamir, hardened weak to strong&quot;]
        F3[&quot;Move 3 Schnorr response&quot;]
    end
    FOUND --&amp;gt; RP[&quot;Range proofs&quot;]
    FOUND --&amp;gt; MP[&quot;Multi-party Schnorr&quot;]
    FOUND --&amp;gt; AC[&quot;Anonymous credentials&quot;]
    RP --&amp;gt; RP1[&quot;Borromean 2015&quot;]
    RP1 --&amp;gt;|&quot;superseded by&quot;| RP2[&quot;Bulletproofs 2018&quot;]
    RP2 --&amp;gt;|&quot;superseded by&quot;| RP3[&quot;Bulletproofs plus 2022&quot;]
    MP --&amp;gt; MP1[&quot;naive two-round&quot;]
    MP1 --&amp;gt;|&quot;superseded by&quot;| MP2[&quot;three-round&quot;]
    MP2 --&amp;gt;|&quot;superseded by&quot;| MP3[&quot;MuSig2 and FROST&quot;]
    AC --&amp;gt; AC1[&quot;Chaum blind signature&quot;]
    AC1 --&amp;gt;|&quot;superseded by&quot;| AC2[&quot;CL-Idemix and U-Prove&quot;]
    AC2 --&amp;gt;|&quot;shipped instead&quot;| AC3[&quot;KVAC, BBS, Privacy Pass&quot;]
&lt;h3&gt;Foundation hardening: weak Fiat-Shamir becomes strong&lt;/h3&gt;
&lt;p&gt;The one part of the foundation that changed was never superseded -- it was hardened in place. Weak Fiat-Shamir was the default misreading of the heuristic; Helios in 2012 and Frozen Heart in 2022 forged proofs under it [@bpw], [@tob-part1], [@cve]. Strong Fiat-Shamir -- bind the whole statement -- is what the random-oracle proof had silently assumed all along. The two generations differ only in what enters one hash, and the discrete logarithm is untouched in both. Move 2 was not replaced by a better construction; it was corrected to the instantiation it always needed.&lt;/p&gt;
&lt;h3&gt;Ladder A -- range proofs: cashing in the homomorphism&lt;/h3&gt;
&lt;p&gt;This ladder cashes in Move 1&apos;s homomorphism, and it is the one with dramatic size numbers. The textbook starting point is a bit-decomposition OR-proof: to show a committed value lies in $[0, 2^n)$, prove each of its $n$ bits is either 0 or 1 with an OR-proof. It works and it is $O(n)$ -- linear in the bit width, which is a lot of bytes.&lt;/p&gt;
&lt;p&gt;Monero&apos;s RingCT initially shipped Borromean ring range proofs (Maxwell and Poelstra, 2015), which packed the bit proofs into ring signatures at roughly 6 kB per output -- the dominant cost of a confidential transaction [@monero]. Then &lt;strong&gt;Bulletproofs&lt;/strong&gt; (Bunz, Bootle, Boneh, Poelstra, Wuille, and Maxwell, 2018) reduced the range statement to an inner-product relation and compressed it with a recursive argument that halves the vectors each round, yielding an $O(\log n)$ proof of about 600 to 700 bytes -- with &lt;strong&gt;no trusted setup&lt;/strong&gt; [@bulletproofs], [@monero].&lt;/p&gt;
&lt;p&gt;Monero&apos;s own documentation reports the deployment cut transaction size by at least 80 percent [@monero]. &lt;strong&gt;Bulletproofs+&lt;/strong&gt; (2022) shrank the constant further with a weighted inner-product argument [@bulletproofs-plus].&lt;/p&gt;
&lt;p&gt;The honest footnote on this ladder is the one Section 5 set up: Bulletproofs also &lt;em&gt;broke&lt;/em&gt;, via weak Fiat-Shamir, as CVE-2022-29566 [@tob-bp], [@cve]. That was a Move-2 failure inside a Move-1 construction, and it was fixed by strong Fiat-Shamir -- not by Bulletproofs+. The size ladder and the security fix are independent axes on the same object.&lt;/p&gt;
&lt;h3&gt;Ladder B -- multi-party Schnorr: climbing out of the ROS break&lt;/h3&gt;
&lt;p&gt;This ladder is Move 3 recovering from the concurrency break of Section 6. The naive two-round multisignature -- everyone sums nonces, everyone sums responses -- fell to Drijvers (2019) and then ROS (2021) [@drijvers], [@ros]. The first fix was to spend a round: three-round commit-then-reveal schemes make each signer commit to its nonce before revealing it, which restores concurrent security at the cost of an extra round trip [@musig2].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MuSig2&lt;/strong&gt; (Nick, Ruffing, and Seurin, 2021) got back to two rounds by a clever trick: each signer publishes &lt;em&gt;two&lt;/em&gt; nonces, and the effective nonce is a hash-weighted combination of them, so the combined nonce is a &lt;em&gt;nonlinear&lt;/em&gt; function of the inputs -- exactly what defeats the linear-algebra ROS forgery [@musig2]. &lt;strong&gt;FROST&lt;/strong&gt; (standardized as RFC 9591 in June 2024) then added genuine threshold signing [@rfc9591].&lt;/p&gt;
&lt;p&gt;The distinction that matters: MuSig2, standardized as BIP-327, is &lt;strong&gt;n-of-n&lt;/strong&gt; -- every signer must participate, and the output is a single 64-byte signature indistinguishable from a single-key one [@bip327], [@musig2]. FROST is the &lt;strong&gt;t-of-n threshold&lt;/strong&gt; scheme, where any $t$ of $n$ signers suffice [@rfc9591]. They are different tools for different policies, not two names for the same thing.&lt;/p&gt;
&lt;h3&gt;Ladder C -- anonymous credentials: the elegant generation that lost&lt;/h3&gt;
&lt;p&gt;This ladder is the strange one, because the most elegant generation is the one that did &lt;em&gt;not&lt;/em&gt; win. It begins with Chaum&apos;s blind signatures -- blind, let the issuer sign, unblind -- introduced in 1982 [@chaum-blind] and set out as a whole transaction system in his 1985 paper, the root of unlinkable credentials, though single-show [@chaum-cacm]. It climbs to the multi-attribute systems: Camenisch-Lysyanskaya (Idemix) and Brands (U-Prove), which let a holder prove statements about many attributes and were genuinely beautiful cryptography [@cl-2001], [@cl-2004], [@uprove]. And then, mostly, it did not ship.&lt;/p&gt;

Camenisch-Lysyanskaya / Idemix and Brands / U-Prove were the theoretically complete multi-attribute credentials of the 2000s -- standardized (U-Prove under ISO/IEC 18370-2 and released under Microsoft&apos;s Open Specification Promise) and mathematically lovely [@uprove], [@cl-2001], [@cl-2004]. Yet they were largely never deployed at population scale, losing on cost, complexity, and integration friction. The systems that shipped instead were pragmatic ones -- Signal&apos;s keyed-verification credentials, BBS, and Privacy Pass. This is the rare case in cryptography where the most elegant design is not the one that wins -- a deployment failure, not a cryptographic break. The exception proves the rule: Direct Anonymous Attestation, a Camenisch-Lysyanskaya-family credential presented through a Sigma proof of possession, shipped in billions of TPM security chips -- precisely because its target was fixed hardware with a captive issuer, exactly the friction the general-purpose systems could not overcome [@daa], [@tpm2-spec].
&lt;p&gt;The winners are three deployed corners, not one. &lt;strong&gt;Signal KVAC&lt;/strong&gt; (2020) is a keyed-verification credential where the issuer is also the verifier, built on an algebraic MAC and a Sigma-protocol presentation proof, deployed in Signal&apos;s private groups [@signal], [@kvac]. &lt;strong&gt;BBS&lt;/strong&gt; signatures are the publicly-verifiable corner, a multi-message credential now in IETF drafts [@bbs-draft].&lt;/p&gt;
&lt;p&gt;And &lt;strong&gt;Privacy Pass / Apple Private Access Tokens&lt;/strong&gt;, standardized at the IETF in June 2024, are the unlinkable single-use authorization-token corner -- the deployed realization of Chaum&apos;s blind-token idea, in which a client redeems an anonymously issued token at an origin that cannot link it back to issuance [@rfc9576], [@cloudflare-pat]. All three are Sigma proofs over the same three moves, filled in for three different trust models; Section 8 attaches the parameters to each.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every real-world break lives on one of the three moves. The discrete logarithm was never broken. The commitment equivocates, the challenge transplants, the response leaks -- and the composites climb their ladders while Pedersen, Fiat-Shamir, and Schnorr keep running underneath, never superseded.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is the whole picture as one matrix.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Lineage / move&lt;/th&gt;
&lt;th&gt;Key idea&lt;/th&gt;
&lt;th&gt;Size or cost&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;Weak Fiat-Shamir&lt;/td&gt;
&lt;td&gt;1986&lt;/td&gt;
&lt;td&gt;Move 2&lt;/td&gt;
&lt;td&gt;hash only the commitment&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;Superseded (insecure)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong Fiat-Shamir&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Move 2&lt;/td&gt;
&lt;td&gt;bind the whole statement&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bit-decomposition range proof&lt;/td&gt;
&lt;td&gt;classic&lt;/td&gt;
&lt;td&gt;Move 1&lt;/td&gt;
&lt;td&gt;OR-prove each bit&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Historical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Borromean range proof&lt;/td&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;Move 1&lt;/td&gt;
&lt;td&gt;ring signatures over bits&lt;/td&gt;
&lt;td&gt;~6 kB/output&lt;/td&gt;
&lt;td&gt;Superseded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulletproofs&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Move 1&lt;/td&gt;
&lt;td&gt;recursive inner-product&lt;/td&gt;
&lt;td&gt;~600-700 B, O(log n)&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulletproofs+&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;Move 1&lt;/td&gt;
&lt;td&gt;weighted inner-product&lt;/td&gt;
&lt;td&gt;shorter constant&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Naive two-round multisig&lt;/td&gt;
&lt;td&gt;2010s&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;sum nonces and responses&lt;/td&gt;
&lt;td&gt;2 rounds, broken&lt;/td&gt;
&lt;td&gt;Superseded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Three-round multisig&lt;/td&gt;
&lt;td&gt;2006&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;commit-then-reveal nonces&lt;/td&gt;
&lt;td&gt;3 rounds&lt;/td&gt;
&lt;td&gt;Superseded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MuSig2 (BIP-327)&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;two nonces, nonlinear combine&lt;/td&gt;
&lt;td&gt;2 rounds, n-of-n&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FROST (RFC 9591)&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;threshold nonce commitments&lt;/td&gt;
&lt;td&gt;2 rounds, t-of-n&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chaum blind signature&lt;/td&gt;
&lt;td&gt;1982&lt;/td&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;blind, sign, unblind&lt;/td&gt;
&lt;td&gt;single-show&lt;/td&gt;
&lt;td&gt;Historical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CL-Idemix / U-Prove&lt;/td&gt;
&lt;td&gt;2001&lt;/td&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;multi-attribute proofs&lt;/td&gt;
&lt;td&gt;multi-show (Idemix); single-show (U-Prove)&lt;/td&gt;
&lt;td&gt;Niche (undeployed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signal KVAC&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;algebraic MAC, issuer verifies&lt;/td&gt;
&lt;td&gt;deployed&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BBS&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;pairing, multi-message&lt;/td&gt;
&lt;td&gt;~80 B signature&lt;/td&gt;
&lt;td&gt;Standardizing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy Pass / Apple PAT&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;unlinkable single-use token&lt;/td&gt;
&lt;td&gt;RFC 9576/9577/9578&lt;/td&gt;
&lt;td&gt;Deployed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Three ladders, three break-driven climbs, over a foundation that was itself hardened in place -- three ascents on one unchanging three-move base. Which rung is at the top of each ladder today -- and with which exact parameters -- is the difference between a design review that passes and one that ships a CVE.&lt;/p&gt;
&lt;h2&gt;8. State of the Art (2024-2026): What Actually Ships&lt;/h2&gt;
&lt;p&gt;The frontier is not one best algorithm. It is a layered stack: three foundational moves that were never superseded, and above them a handful of composites that ship at scale, each with a standard and a parameter set. If you have to build with this in 2026, here is the state of the art with the numbers attached.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The moves, still the state of the art as primitives.&lt;/strong&gt; A Pedersen commitment is a single 32-byte point on ristretto255. Strong Fiat-Shamir is the correct instantiation now baked into every standard below. And Schnorr-proper, standardized as &lt;strong&gt;BIP-340&lt;/strong&gt;, is a 64-byte signature [@bip340]; Bitcoin&apos;s Taproot upgrade activated it at block 709,632 in November 2021 [@bip341], [@taproot-wiki], and native Schnorr has been available for every spend since. These are not legacy pieces waiting to be replaced; they are the load-bearing base of everything else.&lt;/p&gt;

BIP-340 is Schnorr-proper over the secp256k1 curve [@bip340]. EdDSA and its instance Ed25519 are also Schnorr-*family* signatures -- same linear response structure -- but with different encoding, different nonce derivation, and a different curve. They are cousins, not the same standard, and conflating them causes real interoperability and security-review confusion. When someone says &quot;Schnorr signature,&quot; ask which one.
&lt;p&gt;&lt;strong&gt;MuSig2 / BIP-327&lt;/strong&gt; is the deployed answer for n-of-n multisignatures: a two-round protocol producing a single 64-byte Schnorr signature that is indistinguishable on-chain from a single-key spend, implemented in &lt;code&gt;libsecp256k1&lt;/code&gt; and &lt;code&gt;secp256k1-zkp&lt;/code&gt; [@bip327], [@musig2]. Every co-signer participates, and the verifier does one ordinary Schnorr check.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FROST / RFC 9591&lt;/strong&gt; (June 2024) is the most consequential &lt;em&gt;new&lt;/em&gt; standard in the window: a two-round t-of-n threshold Schnorr signature, specified across five ciphersuites -- Ed25519, ristretto255, Ed448, P-256, and secp256k1 [@rfc9591]. On its own, FROST assumes signers do not vanish mid-protocol; &lt;strong&gt;ROAST&lt;/strong&gt; (CCS 2022) wraps it to keep signing alive even when some signers are faulty or offline, giving fault tolerance for asynchronous deployments [@roast].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scheme&lt;/th&gt;
&lt;th&gt;Nonce structure&lt;/th&gt;
&lt;th&gt;Rounds&lt;/th&gt;
&lt;th&gt;Concurrent-secure&lt;/th&gt;
&lt;th&gt;Fault-tolerant&lt;/th&gt;
&lt;th&gt;Signature&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;Naive two-round&lt;/td&gt;
&lt;td&gt;sum the nonces&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;No (Drijvers, ROS)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;64 B&lt;/td&gt;
&lt;td&gt;Broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Three-round&lt;/td&gt;
&lt;td&gt;commit then reveal&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;64 B&lt;/td&gt;
&lt;td&gt;Superseded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MuSig2 (BIP-327)&lt;/td&gt;
&lt;td&gt;two nonces, nonlinear&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;64 B (n-of-n)&lt;/td&gt;
&lt;td&gt;Deployed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FROST (RFC 9591)&lt;/td&gt;
&lt;td&gt;threshold commitments&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;64 B (t-of-n)&lt;/td&gt;
&lt;td&gt;Standardized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FROST + ROAST&lt;/td&gt;
&lt;td&gt;ROAST wrapper&lt;/td&gt;
&lt;td&gt;2+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;64 B (t-of-n)&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

A zero-knowledge proof that a committed value lies in a fixed interval such as $[0, 2^n)$ without revealing the value. Bulletproofs are the deployed transparent construction, proving a Pedersen-committed amount is in range in about 600 to 700 bytes [@bulletproofs].
&lt;p&gt;&lt;strong&gt;Bulletproofs and Bulletproofs+&lt;/strong&gt; are the state of the art for &lt;em&gt;transparent&lt;/em&gt; range proofs -- logarithmic size, no trusted setup -- and they secure confidential amounts in Monero, where Bulletproofs deployed in October 2018 and Bulletproofs+ followed in 2022 [@bulletproofs], [@bulletproofs-plus], [@monero]. They are the default whenever you need a range proof without a setup ceremony and can accept linear verification time. This is the immediate cash-in of Move 1&apos;s homomorphism at production scale.&lt;/p&gt;
&lt;p&gt;On the credential side, three systems ship, and they occupy different economic corners.&lt;/p&gt;

An anonymous credential in which the issuer and the verifier are the same party, so the credential can be built on a fast algebraic MAC instead of a public-key signature. The holder presents it with a Sigma-protocol proof of knowledge, revealing only the attributes it chooses [@kvac], [@signal].
&lt;p&gt;&lt;strong&gt;Signal KVAC&lt;/strong&gt; is the keyed-verification credential in production in Signal&apos;s private group system: the issuer is also the verifier, so it runs on an algebraic MAC with no pairings, and presentation is a Sigma-protocol proof of knowledge [@kvac], [@signal]. It is the standing counter-example to &quot;elegant but undeployed.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BBS and Blind BBS&lt;/strong&gt;BBS takes its name from the 2004 Boneh-Boyen-Shacham short group signature scheme it descends from, by way of the later BBS+ variant [@bbs-origin]. are the publicly-verifiable answer: multi-message, selective-disclosure credentials over the BLS12-381 pairing curve, with roughly 80-byte signatures, now in &lt;code&gt;draft-irtf-cfrg-bbs-signatures-10&lt;/code&gt; (8 January 2026) and Blind BBS &lt;code&gt;draft-03&lt;/code&gt; [@bbs-draft], [@blind-bbs]. BBS is the standardizing choice for W3C Verifiable Credentials and identity wallets, where a third party must verify what an issuer signed. KVAC and BBS are parallel answers -- private-issuer economy versus public verifier -- not one replacing the other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Privacy Pass&lt;/strong&gt;, deployed at population scale as &lt;strong&gt;Apple Private Access Tokens&lt;/strong&gt;, is the third corner: a different shape from the other two -- an unlinkable single-use authorization token rather than a multi-attribute credential. A client anonymously obtains a token backed by some attestation and later redeems it at an origin that cannot link the redemption to issuance.&lt;/p&gt;
&lt;p&gt;It was standardized at the IETF in June 2024 across three documents -- &lt;strong&gt;RFC 9576&lt;/strong&gt; for the architecture, &lt;strong&gt;RFC 9578&lt;/strong&gt; for issuance, and &lt;strong&gt;RFC 9577&lt;/strong&gt; for the &lt;code&gt;PrivateToken&lt;/code&gt; HTTP authentication scheme [@rfc9576], [@rfc9578], [@rfc9577]. Apple shipped it in iOS 16 and macOS Ventura in 2022 with hardware-backed device attestation, and Cloudflare uses it to drop the CAPTCHA for attested devices [@cloudflare-pat].Direct Anonymous Attestation (2004), the deployed exception detailed in Section 7, ships in two hardware generations -- TPM 1.2 RSA-DAA and TPM 2.0 ECDAA -- and adds per-verifier basename linkability, so a device proves it is genuine without becoming trackable [@daa], [@tpm2-spec].&lt;/p&gt;
&lt;p&gt;Underneath, it is still commit, challenge, respond over the discrete log. Keep it distinct from KVAC and BBS: it is single-&lt;em&gt;show&lt;/em&gt; authorization, not multi-attribute selective disclosure, and its privately-verifiable issuance is a verifiable oblivious PRF whose Chaum-Pedersen mechanism Part 20 owns and this section only signposts.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Verifiability&lt;/th&gt;
&lt;th&gt;Cryptographic base&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;U-Prove / Idemix&lt;/td&gt;
&lt;td&gt;Public&lt;/td&gt;
&lt;td&gt;Discrete log, RSA, pairings&lt;/td&gt;
&lt;td&gt;multi-kB&lt;/td&gt;
&lt;td&gt;Standardized, largely undeployed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signal KVAC&lt;/td&gt;
&lt;td&gt;Keyed (issuer verifies)&lt;/td&gt;
&lt;td&gt;Algebraic MAC, no pairings&lt;/td&gt;
&lt;td&gt;Compact&lt;/td&gt;
&lt;td&gt;Deployed in Signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BBS / Blind BBS&lt;/td&gt;
&lt;td&gt;Public&lt;/td&gt;
&lt;td&gt;Pairings on BLS12-381&lt;/td&gt;
&lt;td&gt;~80 B signature&lt;/td&gt;
&lt;td&gt;Standardizing (IETF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy Pass / Apple PAT&lt;/td&gt;
&lt;td&gt;Public or keyed, per token type&lt;/td&gt;
&lt;td&gt;VOPRF or blind RSA issuance&lt;/td&gt;
&lt;td&gt;Small single-use token&lt;/td&gt;
&lt;td&gt;Deployed (iOS 16, macOS Ventura)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;This is the same primitive Part 20 built on: an &lt;a href=&quot;https://paragmali.com/blog/the-server-helped-and-never-saw-a-field-guide-to-oblivious-p/&quot; rel=&quot;noopener&quot;&gt;oblivious PRF&apos;s verifiability&lt;/a&gt; rests on a Chaum-Pedersen Sigma proof -- and Privacy Pass&apos;s privately-verifiable issuance is exactly that VOPRF. The deployed &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;anonymous-credential systems&lt;/a&gt; are Sigma proofs of knowledge of a signature, 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; is the Sigma-protocol credential already shipping in TPM silicon. Every composite in this section is the three moves with the parameters filled in.&lt;/p&gt;
&lt;p&gt;Every one of these is a defensible point on a Pareto frontier, not a single winner -- which is exactly why the toolkit is a toolkit. To choose between them, and between the whole family and a general-purpose proof system, you need the line where Sigma plus Fiat-Shamir stops being the right tool.&lt;/p&gt;
&lt;h2&gt;9. Competing Approaches and Where the Line Is&lt;/h2&gt;
&lt;p&gt;The toolkit&apos;s real competition is the general-purpose proof system -- the SNARK or STARK that can prove &lt;em&gt;any&lt;/em&gt; circuit. Knowing when &lt;em&gt;not&lt;/em&gt; to reach for one is half of using this toolkit well.&lt;/p&gt;
&lt;p&gt;A bespoke Sigma proof plus strong Fiat-Shamir is transparent (it needs only the random-oracle assumption, no setup ceremony), tiny for narrow statements (a constant number of group elements, growing only with the number of clauses), and cheap to prove. A pairing-based SNARK gives a constant-size proof and near-constant verification, but at the price of a trusted setup: Groth16 lands near 200 bytes and PLONK near 400 [@groth16], [@plonk].&lt;/p&gt;
&lt;p&gt;A STARK is transparent and plausibly post-quantum, but its proofs run to tens or hundreds of kilobytes. The Bulletproofs authors made the transparency contrast explicit: their range proofs need no trusted setup, unlike the succinct pairing SNARKs [@bulletproofs].&lt;/p&gt;
&lt;p&gt;The decision rule falls out of the shape of your statement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;fixed algebraic relation&lt;/strong&gt; -- &quot;I know a discrete log,&quot; &quot;this Pedersen value is in range,&quot; &quot;one of these $n$ public keys signed&quot; -- wants a hand-verified Sigma proof with strong Fiat-Shamir. It is smaller, faster, and setup-free.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;arbitrary circuit&lt;/strong&gt; -- &quot;this value is the output of running this program&quot; -- is where you reach for a SNARK or STARK, accepting the setup or the proof size in exchange for expressiveness you cannot get from a fixed relation.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Proof size (64-bit range)&lt;/th&gt;
&lt;th&gt;Trusted setup&lt;/th&gt;
&lt;th&gt;Prover&lt;/th&gt;
&lt;th&gt;Verify&lt;/th&gt;
&lt;th&gt;Post-quantum&lt;/th&gt;
&lt;th&gt;Aggregation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bit-decomposition OR&lt;/td&gt;
&lt;td&gt;O(n), large&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Weak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Borromean&lt;/td&gt;
&lt;td&gt;~6 kB/output&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&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;Bulletproofs&lt;/td&gt;
&lt;td&gt;~0.6-0.7 kB, O(log n)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n), batchable&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulletproofs+&lt;/td&gt;
&lt;td&gt;smaller constant, O(log n)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n), batchable&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNARK (Groth16/PLONK)&lt;/td&gt;
&lt;td&gt;~0.2 kB (Groth16) to ~0.4 kB (PLONK), O(1)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;O(circuit)&lt;/td&gt;
&lt;td&gt;~O(1)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Circuit-dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STARK&lt;/td&gt;
&lt;td&gt;tens to hundreds of kB&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;polylog&lt;/td&gt;
&lt;td&gt;Plausibly&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two more axes decide the corner. &lt;strong&gt;Commitment choice&lt;/strong&gt; is the Section 4 recap: need to add committed values, pick Pedersen; need post-quantum binding or no algebra, pick a hash commitment. And &lt;strong&gt;interactivity&lt;/strong&gt; is a genuine fork: an interactive Sigma protocol is &lt;em&gt;deniable&lt;/em&gt; -- because anyone could have simulated the transcript, it proves nothing to a third party -- and needs no random-oracle assumption, while Fiat-Shamir buys you a signature or an on-chain proof at the cost of making the proof transferable to everyone forever. Deniability versus non-interactivity is a real tension we return to in the open problems.&lt;/p&gt;

The challenge move is the shared weak point of proof systems well beyond this toolkit. PlonK -- a general-purpose SNARK, not a Sigma protocol -- was hit by the *same* weak Fiat-Shamir bug as Bulletproofs in the 2022 Frozen Heart disclosure [@tob-plonk], [@tob-part1]. And in 2025, Khovratovich, Rothblum, and Soukhanov demonstrated a *practical* soundness break against a GKR-based commercial proof system, tricking it into certifying false statements regardless of which hash function instantiates Fiat-Shamir [@khovratovich], [@quanta]. This boundary keeps the article honest: weak-challenge failures are not unique to Sigma protocols -- the move that most often leaks is the same across the whole family of proof systems.
&lt;p&gt;The line is drawn by the shape of your statement and the trust you can assume. But even the right tool, used perfectly, runs into limits that no parameter choice can buy its way out of.&lt;/p&gt;
&lt;h2&gt;10. Theoretical Limits&lt;/h2&gt;
&lt;p&gt;The discrete log has held for forty years. But &quot;the hard problem is hard&quot; is not the same claim as &quot;your proof is secure,&quot; and the gap between them is where this section lives. Every limit below survives even if the discrete logarithm stays hard forever.&lt;/p&gt;
&lt;p&gt;Start with the good news, because it is genuinely good. For the &lt;em&gt;interactive&lt;/em&gt; protocol, special soundness gives a tight, unconditional extractor -- two transcripts and three lines of algebra recover the witness, no assumptions -- and SHVZK gives a &lt;em&gt;perfect&lt;/em&gt; simulator [@damgard]. Interactive Schnorr is about as close to an ideal proof of knowledge as anything in cryptography gets. The trouble starts when you make it non-interactive, or run many copies at once.&lt;/p&gt;
&lt;h3&gt;Soundness is only ROM-deep&lt;/h3&gt;
&lt;p&gt;Fiat-Shamir soundness is provable, but only in the random-oracle model, via the forking lemma [@pointcheval-stern]. In the standard model it is &lt;em&gt;uninstantiable&lt;/em&gt;: Goldwasser and Kalai (FOCS 2003) exhibited schemes whose Fiat-Shamir transform is insecure for &lt;em&gt;every&lt;/em&gt; concrete hash function you could plug in [@gk-focs]. That was a theoretical construction for two decades. Then in 2025, Khovratovich, Rothblum, and Soukhanov turned it practical, making a GKR-based commercial proof system accept false statements regardless of the hash instantiating the transform [@khovratovich], [@quanta].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Khovratovich-Rothblum-Soukhanov result targets Fiat-Shamir applied to GKR-style succinct arguments, not the short algebraic Sigma statements that strong Fiat-Shamir fully binds [@khovratovich]. It does not forge a Schnorr signature. What it does is harden the general lesson: Fiat-Shamir soundness is a property of the random-oracle idealization, and the more your proof leans on the hash doing something a real hash may not do, the thinner the ice [@gk-focs].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Forking is loose, and looseness is the seam&lt;/h3&gt;
&lt;p&gt;The random-oracle reduction is not tight. Rewinding a forger to extract the discrete log loses roughly a quadratic factor in the security bound [@pointcheval-stern]. This is not sloppy proof engineering; it is inherent to the rewinding technique.The quadratic looseness and the concurrency failure are the same fact seen twice. Rewinding extracts from &lt;em&gt;one&lt;/em&gt; session by replaying it with a fresh challenge; it does not compose when many sessions interleave, because you cannot rewind one without disturbing the others. That is precisely the seam the ROS attack pries open [@pointcheval-stern], [@ros]. One line of work sidesteps rewinding entirely: the Fischlin transform (2005) uses an &lt;em&gt;online&lt;/em&gt;, straight-line extractor that reads the witness out of a single transcript by inspecting the prover&apos;s oracle queries, so its extraction composes concurrently and carries more cleanly into the quantum random-oracle model -- at the price of larger proofs, which is why it coexists with Fiat-Shamir rather than replacing it [@fischlin]. And because rewinding does not compose across concurrent sessions, the single-session forking proof simply does not extend to the multi-party concurrent setting.&lt;/p&gt;
&lt;p&gt;That gap has a sharp quantitative edge. Naive linear Schnorr multisignatures and blind signatures are secure only while the number of concurrent open sessions stays below roughly $\log_2 p$; above that threshold, the ROS forgery runs in polynomial time with no discrete log solved [@ros]. MuSig2&apos;s two-nonce nonlinear combination is the construction that evades the bound -- not a discrete-log advance, just a change that makes the response nonlinear so the linear system has no solution [@musig2].&lt;/p&gt;
&lt;h3&gt;Two settled negatives&lt;/h3&gt;
&lt;p&gt;Some limits are not open questions; they are proven impossibilities you must design around.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No commitment is both perfectly hiding and perfectly binding.&lt;/strong&gt; The Move-1 trade-off of Section 4 is fundamental, not an artifact of Pedersen [@damgard]. You choose a corner; you do not get both.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;None of this is post-quantum.&lt;/strong&gt; Every guarantee in the article rests on the hardness of the discrete logarithm or of pairing problems, and Shor&apos;s algorithm breaks all of them on a sufficiently large quantum computer. No parameter choice rescues it -- a bigger group buys nothing against Shor. The hard problem that held against classical attackers for forty years is exactly the one a quantum computer dissolves.&lt;/li&gt;
&lt;/ol&gt;

The discrete log is hard, and this multisignature is secure under concurrency, are simply different claims -- and the second one was false until MuSig2 made the combined nonce nonlinear.
&lt;p&gt;Those are the settled negatives. The open frontiers -- where the field is still moving -- are next, and the sharpest is the quantum cliff the whole toolkit stands at the edge of.&lt;/p&gt;
&lt;h2&gt;11. Open Problems&lt;/h2&gt;
&lt;p&gt;The toolkit is deployed and load-bearing, and it still has open edges sharp enough to end careers on. Here is what is genuinely unsolved, and which of it should keep a protocol designer awake.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Standard-model Fiat-Shamir.&lt;/strong&gt; A universal, hash-instantiable, tight Fiat-Shamir is believed impossible in general. The 2003 uninstantiability result and the 2025 practical break together bound the ambition: the realistic goal is not a universal fix but per-protocol &lt;em&gt;correlation intractability&lt;/em&gt; -- proving a specific hash is good enough for a specific proof system [@gk-focs], [@khovratovich]. Straight-line alternatives such as the Fischlin transform trade larger proofs for cleaner, rewinding-free extraction, but they are not a universal drop-in replacement [@fischlin]. For the short algebraic statements in this article, strong Fiat-Shamir remains the right practice; for succinct arguments, the question is live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Post-quantum Sigma protocols.&lt;/strong&gt; This is the sharpest frontier, because it is where the whole toolkit meets its deadline. Discrete-log zero knowledge dies to Shor -- but the three-move &lt;em&gt;template&lt;/em&gt; does not. It reappears over other hardness assumptions.The Sigma template outlives its discrete-log instantiation. Fiat-Shamir-with-aborts over lattices is the shape behind Dilithium-style signatures, and MPC-in-the-head -- instantiated as Picnic -- builds a zero-knowledge signature from a hash and a secret-sharing protocol, needing no structured hardness assumption at all [@picnic]. Commit, challenge, respond survives the move to a post-quantum world; only the algebra underneath changes. What does &lt;em&gt;not&lt;/em&gt; yet transfer cleanly is the efficient, verifiable, partially-oblivious machinery: post-quantum range proofs and post-quantum multi-show credentials exist but are large and immature compared to Bulletproofs and BBS. The &lt;a href=&quot;https://paragmali.com/blog/the-thirty-year-migration-ships-in-a-pip-install-how-post-qu/&quot; rel=&quot;noopener&quot;&gt;thirty-year migration to post-quantum cryptography&lt;/a&gt; is the sibling part that tracks this deadline in full.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Round-optimal blind Schnorr after ROS.&lt;/strong&gt; Plain two-round blind Schnorr is inherently ROS-vulnerable [@ros]. Getting a secure blind signature in few rounds needs extra structure: clause or boosting variants that break the linearity, a proof in the algebraic group model (Fuchsbauer, Plouviez, and Seurin), or a different scheme entirely [@fps]. There is no known plain, standard-model, round-optimal blind Schnorr that survives concurrency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Threshold Schnorr, hardened.&lt;/strong&gt; RFC 9591 standardized two-round threshold Schnorr for custody in June 2024, and FROST is now entering production custody [@rfc9591], but stronger guarantees remain research targets: adaptive security (against an attacker who corrupts signers mid-protocol), proofs that do not lean on the algebraic group model, and distributed key generation that scales asynchronously to large signer sets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Anonymous-credential revocation at population scale&lt;/strong&gt; without breaking unlinkability is still mostly finessed rather than solved -- deployed systems lean on short-lived credentials that simply expire, because a true privacy-preserving revocation list that scales to millions of holders is hard to make both efficient and unlinkable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deniability versus non-interactivity.&lt;/strong&gt; A Fiat-Shamir&apos;d proof is transferable -- anyone who receives it is convinced, forever. An interactive Sigma proof is deniable -- because it could have been simulated, it convinces only the live verifier. Getting both properties at once is a per-application tension, not a solved problem.&lt;/p&gt;
&lt;p&gt;None of these block you from shipping today; they tell you what to watch. What you ship comes down to a small, unglamorous set of rules that separate a correct deployment from a forged one.&lt;/p&gt;
&lt;h2&gt;12. Use X With These Parameters In Case Y&lt;/h2&gt;
&lt;p&gt;Here are the rules a reviewer can apply without re-deriving anything, organized -- of course -- by the three moves, in the order you make them.&lt;/p&gt;
&lt;h3&gt;Move 1: pick the commitment by the job&lt;/h3&gt;
&lt;p&gt;Need to add or range-prove committed values -- confidential amounts, homomorphic tallies? Use a &lt;strong&gt;Pedersen commitment on ristretto255&lt;/strong&gt;, and derive the second generator $h$ by hashing a fixed string to a curve point so that nobody holds the trapdoor $\log_g h$. A known trapdoor is silent equivocation; the generator setup is security-critical, not boilerplate. Need post-quantum binding, or you are committing to something with no algebra -- a Merkle leaf, a coin flip? Use a &lt;strong&gt;hash commitment&lt;/strong&gt; &lt;code&gt;H(m ‖ r)&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Move 2: get the challenge right, every time&lt;/h3&gt;
&lt;p&gt;Use &lt;strong&gt;strong Fiat-Shamir&lt;/strong&gt;: hash the full statement, all public parameters, the prior transcript, and a domain separator. The operational habit that prevents the entire Frozen Heart class is one sentence: for every proof, write down &quot;what is this proof &lt;em&gt;about&lt;/em&gt;?&quot; and confirm each item on that list is inside the hash. The statement is about a public key? The public key goes in the hash. It is about a specific message or ciphertext? That goes in too. Weak Fiat-Shamir is what happens when that list is incomplete [@tob-part1].&lt;/p&gt;
&lt;h3&gt;Move 3: nonce discipline&lt;/h3&gt;
&lt;p&gt;Nonces must be fresh, secret, and uniform per session. Draw them from a well-seeded CSPRNG, or derive them deterministically with the tagged-hash discipline BIP-340 specifies so a broken RNG cannot repeat or bias them [@bip340]. In MuSig2 and FROST, honor the two-nonce and preprocessing rules exactly, and never reuse a nonce commitment across sessions -- the multi-party schemes defeat ROS only if the nonce rules are followed [@musig2], [@rfc9591].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Move 1 -- is your second generator $h$ from a nothing-up-my-sleeve hash, with no known trapdoor? Move 2 -- write down what the proof is about, and verify every item is inside the challenge hash, behind a domain separator. Move 3 -- is every nonce fresh, secret, and uniform, and are the MuSig2 or FROST nonce rules followed to the letter? Three questions, in order. Most Frozen-Heart and nonce-reuse incidents fail one of them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Then pick the composite&lt;/h3&gt;

flowchart TD
    START{&quot;What are you building?&quot;}
    START --&amp;gt;|&quot;commit to a value&quot;| COMMIT{&quot;need to add or range-prove it?&quot;}
    COMMIT --&amp;gt;|&quot;yes&quot;| C1[&quot;Pedersen on ristretto255&quot;]
    COMMIT --&amp;gt;|&quot;no, need PQ binding&quot;| C2[&quot;Hash commitment&quot;]
    START --&amp;gt;|&quot;prove a range&quot;| C3[&quot;Bulletproofs plus, aggregated, strong FS&quot;]
    START --&amp;gt;|&quot;multi-party signature&quot;| SIG{&quot;all signers or t of n?&quot;}
    SIG --&amp;gt;|&quot;all n&quot;| C4[&quot;MuSig2 BIP-327&quot;]
    SIG --&amp;gt;|&quot;t of n&quot;| C5[&quot;FROST RFC 9591 plus ROAST&quot;]
    START --&amp;gt;|&quot;anonymous credential&quot;| CRED{&quot;who verifies?&quot;}
    CRED --&amp;gt;|&quot;the issuer itself&quot;| C6[&quot;Signal KVAC&quot;]
    CRED --&amp;gt;|&quot;any third party&quot;| C7[&quot;BBS&quot;]
    START --&amp;gt;|&quot;arbitrary circuit&quot;| C8[&quot;SNARK or STARK&quot;]
&lt;p&gt;The composite rules, stated plainly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Range proof, small and transparent:&lt;/strong&gt; Bulletproofs+, aggregated, with strong Fiat-Shamir binding [@bulletproofs-plus].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All parties co-sign one key (n-of-n):&lt;/strong&gt; MuSig2, standardized as BIP-327 [@bip327].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Any t of n signers suffice:&lt;/strong&gt; FROST, RFC 9591, run with a distributed key generation -- and add ROAST if a faulty signer must not be able to stall the protocol [@rfc9591], [@roast].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authenticate your own users anonymously (you issue and verify):&lt;/strong&gt; Signal KVAC [@kvac], [@signal].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A third party must verify selective disclosure:&lt;/strong&gt; BBS [@bbs-draft].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A fixed algebraic relation, tiny and setup-free:&lt;/strong&gt; a hand-rolled Sigma proof with strong Fiat-Shamir, using a vetted library -- never your own group arithmetic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An arbitrary circuit:&lt;/strong&gt; a SNARK or STARK, accepting the setup or proof-size cost.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anything that must stay secure through the quantum transition:&lt;/strong&gt; do not rely on these guarantees; plan a post-quantum migration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the implementations, use the libraries that encode these defaults: &lt;code&gt;bitcoin-core/secp256k1&lt;/code&gt; and &lt;code&gt;secp256k1-zkp&lt;/code&gt; for BIP-340 and MuSig2 [@bip340], [@bip327]; FROST implementations validated against the RFC 9591 test vectors [@rfc9591]; &lt;code&gt;dalek-cryptography&lt;/code&gt; for Bulletproofs; Signal&apos;s &lt;code&gt;libsignal&lt;/code&gt; and zkgroup for KVAC [@signal]; BBS libraries tracking the CFRG drafts [@bbs-draft]; and Damgard&apos;s notes together with Trail of Bits&apos; ZKDocs when you need the correct Fiat-Shamir recipe written out [@damgard], [@tob-part1].&lt;/p&gt;
&lt;p&gt;Most of the ways this goes wrong are the same handful of mistakes phrased for different audiences -- and they are worth naming head-on, because reviewers find them in real code every week.&lt;/p&gt;
&lt;h2&gt;13. Common Misuse, and the Questions Engineers Ask&lt;/h2&gt;
&lt;p&gt;The antipattern catalog is short because the failures are few and repeat. Reviewers find these in real code, and every one maps to a move.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Misuse&lt;/th&gt;
&lt;th&gt;Move&lt;/th&gt;
&lt;th&gt;Root cause&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;Weak Fiat-Shamir in real code&lt;/td&gt;
&lt;td&gt;Move 2&lt;/td&gt;
&lt;td&gt;statement omitted from the challenge hash&lt;/td&gt;
&lt;td&gt;bind the full statement and parameters (strong FS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reused or biased nonce&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;two transcripts share a commitment; extractor recovers the key&lt;/td&gt;
&lt;td&gt;fresh, uniform, per-session nonces; hedged derivation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assuming Pedersen is perfectly binding&lt;/td&gt;
&lt;td&gt;Move 1&lt;/td&gt;
&lt;td&gt;it is only computationally binding&lt;/td&gt;
&lt;td&gt;design for computational binding; protect the trapdoor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equivocation via a known &lt;code&gt;log_g h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move 1&lt;/td&gt;
&lt;td&gt;someone knows the trapdoor&lt;/td&gt;
&lt;td&gt;derive &lt;code&gt;h&lt;/code&gt; by nothing-up-my-sleeve hash-to-curve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rolling your own OR-proof or CDS composition&lt;/td&gt;
&lt;td&gt;all three&lt;/td&gt;
&lt;td&gt;simulator and challenge-splitting are subtle&lt;/td&gt;
&lt;td&gt;use a vetted implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Naive linear multisig under concurrency&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;linear responses solved by ROS&lt;/td&gt;
&lt;td&gt;MuSig2 (n-of-n) or FROST (t-of-n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Treating credentials as &quot;just signatures&quot;&lt;/td&gt;
&lt;td&gt;Move 1-3&lt;/td&gt;
&lt;td&gt;they are Sigma proofs with selective disclosure&lt;/td&gt;
&lt;td&gt;model unlinkability and presentation proofs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;ROS means Random Oracle Substitution&quot;&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;mistranslated acronym&lt;/td&gt;
&lt;td&gt;it is an overdetermined, solvable linear system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflating BIP-340 with EdDSA&lt;/td&gt;
&lt;td&gt;Move 3&lt;/td&gt;
&lt;td&gt;different encoding, nonce, and curve&lt;/td&gt;
&lt;td&gt;pin the exact standard you mean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;


No. A Pedersen commitment is perfectly *hiding* and only *computationally* binding. A party who knows the trapdoor $\log_g h$ can open a single commitment to any message they like, by solving one linear equation for a matching blinder [@pedersen], [@damgard]. This is why the second generator must be produced so that nobody knows that trapdoor, and why &quot;Pedersen is perfectly binding&quot; is a dangerous thing to write in a spec.


The full statement, all public parameters, the prior transcript, and a domain separator -- not just the commitment [@bpw]. Omitting the statement is weak Fiat-Shamir, the bug behind both the Helios voting break in 2012 and the Frozen Heart disclosures in 2022 [@bpw], [@tob-part1]. The habit that prevents it: write down what the proof is about, and confirm each item is inside the hash.


Yes, and seeing that is worth the price of admission. A Schnorr signature is Fiat-Shamir applied to the Schnorr identification protocol, with the verifier&apos;s challenge replaced by a hash of the public key, message, and commitment [@fiat-shamir], [@bip340]. A signature *is* a non-interactive proof of knowledge of the private key; the message just rides along in the challenge.


Don&apos;t. The CDS construction for AND/OR composition relies on splitting the challenge and simulating the branches you cannot answer, and getting the challenge algebra or the simulator subtly wrong breaks soundness or zero knowledge [@cds]. Use a vetted implementation; this is exactly the code that looks simple and is not.


No. It is forgeable under concurrent sessions -- sub-exponentially since Drijvers at S&amp;amp;P 2019, and in polynomial time since the ROS result in 2021 [@drijvers], [@ros]. Use MuSig2 for n-of-n signing or FROST for t-of-n threshold signing; both are engineered specifically to defeat that attack [@musig2], [@rfc9591].


No. They are Sigma-protocol proofs of knowledge of a signature or an algebraic MAC, and they support selective disclosure and unlinkability -- a plain signature has neither [@bbs-draft], [@signal]. Treating a credential presentation as an ordinary signature verification misses the entire privacy machinery that is the point of the primitive. Privacy Pass and Apple&apos;s Private Access Tokens are the single-*show* authorization-token cousin -- unlinkable, but a one-time &quot;you are allowed&quot; token rather than a multi-attribute selective-disclosure credential [@rfc9576].


No, and the mistranslation is everywhere online. ROS is &quot;Random inhomogeneities in an Overdetermined, Solvable system of linear equations&quot; -- a linear-algebra object, not an oracle trick [@ros]. The attack forges by solving a system of equations over responses collected across concurrent sessions, with no discrete logarithm ever solved.

&lt;h3&gt;The discrete log held; the three moves leaked&lt;/h3&gt;
&lt;p&gt;Follow the whole failure catalog back and it lands in one place. In April 2022, three unrelated proof systems were forgeable at once, and the discrete logarithm was untouched in all three [@tob-part1]. A decade earlier, the same weak-Fiat-Shamir bug had forged ballot proofs in Helios [@bpw]. A reused nonce hands over a private key by the same algebra that proves Schnorr sound [@bip340]. The ROS attack forges naive multisignatures in polynomial time by solving a linear system, no hard problem broken [@ros]. A Pedersen commitment equivocates the instant someone knows a trapdoor [@pedersen].&lt;/p&gt;
&lt;p&gt;Four decades, and the discrete logarithm never fell. Every named break -- Frozen Heart, Helios, nonce reuse, ROS, Drijvers, equivocation -- landed on one of the three moves built on top of it.&lt;/p&gt;
&lt;p&gt;And the composites climbed while the foundations stayed put. Borromean gave way to Bulletproofs and Bulletproofs+ [@bulletproofs], [@bulletproofs-plus]; naive multisignatures gave way to MuSig2 and FROST [@musig2], [@rfc9591]; Chaum&apos;s blind signatures gave way, in deployment, to Signal KVAC, BBS, and Privacy Pass [@signal], [@bbs-draft], [@rfc9576]. Not one of those ladders replaced Pedersen, Schnorr, or Fiat-Shamir. The three moves keep running underneath every rung, exactly as they were written between 1985 and 1996 -- and where the foundation moved at all, it was Move 2 being hardened in place, weak Fiat-Shamir corrected to strong, never a piece torn out and replaced.&lt;/p&gt;
&lt;p&gt;This is the frame the series keeps returning to from a new angle each time. Part 18 argued that &lt;a href=&quot;https://paragmali.com/blog/nobody-broke-the-discrete-log-a-field-guide-to-diffie-hellma/&quot; rel=&quot;noopener&quot;&gt;nobody broke the discrete log&lt;/a&gt;; Part 17 that the math held and the interface leaked. Here, the math held and the &lt;em&gt;conversation&lt;/em&gt; leaked -- because a discrete-log zero-knowledge proof was never one thing. It was always three moves, and each one is a separate promise that can be broken without touching the problem underneath.&lt;/p&gt;

The discrete log held. The three moves leaked.
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;commitments-and-sigma-protocols-pedersen-schnorr-and-the-dis&quot; keyTerms={[
  { term: &quot;Sigma protocol&quot;, definition: &quot;A three-move public-coin proof -- commit, challenge, respond -- with special soundness and special honest-verifier zero knowledge, checked by one verification equation.&quot; },
  { term: &quot;Special soundness&quot;, definition: &quot;Two accepting transcripts sharing a commitment but with different challenges yield the witness via an extractor; what makes a Sigma protocol a proof of knowledge.&quot; },
  { term: &quot;SHVZK&quot;, definition: &quot;Special honest-verifier zero knowledge: any accepting transcript can be simulated without the witness by choosing the response and challenge first, proving the exchange reveals nothing.&quot; },
  { term: &quot;Pedersen commitment&quot;, definition: &quot;C = g^m times h^r: perfectly hiding, computationally binding on the discrete log, additively homomorphic -- and NOT perfectly binding, since a trapdoor holder can equivocate.&quot; },
  { term: &quot;Fiat-Shamir transform&quot;, definition: &quot;Replacing the interactive verifier&apos;s challenge with a hash of the transcript, making a Sigma protocol non-interactive. Strong FS binds the whole statement; weak FS omits it and is forgeable.&quot; },
  { term: &quot;Forking lemma&quot;, definition: &quot;The rewinding proof that turns a random-oracle forger into a discrete-log solver, giving Schnorr its ROM security -- loosely, with a quadratic loss, and only for single sessions.&quot; },
  { term: &quot;ROS problem&quot;, definition: &quot;Random inhomogeneities in an Overdetermined, Solvable system of linear equations; solvable in polynomial time above about log2(p) concurrent sessions, breaking naive linear Schnorr multi and blind signatures.&quot; },
  { term: &quot;MuSig2 vs FROST&quot;, definition: &quot;MuSig2 (BIP-327) is n-of-n two-round Schnorr multisig with a nonlinear two-nonce trick; FROST (RFC 9591) is t-of-n threshold Schnorr. Both defeat ROS.&quot; },
  { term: &quot;KVAC vs BBS&quot;, definition: &quot;Signal KVAC is a keyed-verification credential (issuer verifies, algebraic MAC, deployed); BBS is a publicly-verifiable multi-message credential on BLS12-381 (standardizing).&quot; },
  { term: &quot;Privacy Pass / PAT&quot;, definition: &quot;An unlinkable single-use authorization token (RFC 9576/9577/9578, June 2024), deployed as Apple Private Access Tokens; the single-show cousin of credentials, not multi-attribute selective disclosure.&quot; }
]} questions={[
  { q: &quot;A proof verifies but the statement is false, and nobody solved the discrete log. Which move failed, and how?&quot;, a: &quot;Move 2, the challenge. Weak Fiat-Shamir omitted the statement from the hash, so a valid proof was transplanted onto a false statement -- the Frozen Heart and Helios bug.&quot; },
  { q: &quot;Two Schnorr signatures share a nonce. What can an attacker compute, and why is it the same math as a soundness proof?&quot;, a: &quot;The private key, via x = (z1 - z2)/(e1 - e2). That is exactly the special-soundness extractor from two transcripts on the same commitment, run by the attacker instead of the security proof.&quot; },
  { q: &quot;Why is a naive two-round Schnorr multisignature insecure under concurrency when single-session Schnorr is proven secure?&quot;, a: &quot;The forking-lemma proof does not compose across concurrent sessions, and the linear response lets the ROS attack solve a system over many open sessions to forge -- with no discrete log solved. MuSig2&apos;s nonlinear two-nonce trick evades it.&quot; },
  { q: &quot;Is a Pedersen commitment perfectly binding, and what is the security-critical setup step?&quot;, a: &quot;No -- it is perfectly hiding and only computationally binding. The critical step is deriving the second generator h with an unknown discrete log, by hashing to the curve, so no party holds the equivocation trapdoor.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>commitments</category><category>sigma-protocols</category><category>pedersen-commitment</category><category>schnorr</category><category>fiat-shamir</category><category>zero-knowledge-proofs</category><category>bulletproofs</category><category>applied-cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>The Math Held; The Interface Leaked: A Field Guide to Digital Signatures</title><link>https://paragmali.com/blog/the-math-held-the-interface-leaked-a-field-guide-to-digital-/</link><guid isPermaLink="true">https://paragmali.com/blog/the-math-held-the-interface-leaked-a-field-guide-to-digital-/</guid><description>ECDLP and RSA held; the nonce, the canonical form, and the verifier broke instead. A field guide to ECDSA, EdDSA, RSA-PSS, determinism, and malleability.</description><pubDate>Sun, 12 Jul 2026 17:41:17 GMT</pubDate><content:encoded>
**A signature scheme&apos;s hardness assumption almost never breaks in the field.** The elliptic-curve discrete logarithm and RSA have held for decades. What breaks instead are the three things the scheme hands the caller: the per-signature **nonce**, the signature&apos;s **canonical form**, and the **verifier&apos;s acceptance rule**.&lt;p&gt;ECDSA outsources all three, and the same untouched-math scheme produced four categorically different disasters: a reused nonce that gave up the PlayStation 3 master key from two signatures [@ps3-27c3-2010], a biased nonce that recovered a PuTTY key in about sixty (a &lt;em&gt;statistical&lt;/em&gt; attack: one few-bit-biased signature does &lt;strong&gt;not&lt;/strong&gt; leak your key) [@cve-2024-31497], a malleable &lt;code&gt;(r, n-s)&lt;/code&gt; that let anyone alter a Bitcoin transaction id [@bip-340], and a verifier that accepted &lt;code&gt;(0, 0)&lt;/code&gt; for any message (&quot;Psychic Signatures&quot;) [@cve-2022-21449].&lt;/p&gt;
&lt;p&gt;The history since ECDSA is one sustained move to pull each surface back &lt;em&gt;inside&lt;/em&gt; the scheme: RFC 6979 derives the nonce deterministically, hedged signing mixes a little randomness back in to survive fault attacks, and Ed25519 and BIP-340 Schnorr make deterministic nonces, a fixed encoding, and a byte-exact verifier native by design -- the clean scheme that sat patent-frozen from 1989 until 2008.&lt;/p&gt;
&lt;p&gt;RSA-PSS answers the same &quot;form&quot; question on the RSA track with the tightest proof of any deployed signature. In 2026 these coexist by niche, and every one of them falls to Shor. Signing safely is a stack: the right scheme, a nonce you need not trust the RNG for, a canonical form, and a strict verifier. The math held; the interface leaked.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;1. Two Signatures, One Nonce, and the Master Key Fell Out&lt;/h2&gt;
&lt;p&gt;In December 2010, at the 27th Chaos Communication Congress, a group called fail0verflow put a single slide on the screen that ended the security of the PlayStation 3 [@ps3-27c3-2010]. Sony had signed every piece of PS3 firmware with ECDSA, a scheme whose security rests on a discrete logarithm no one has ever solved for a 256-bit curve.&lt;/p&gt;
&lt;p&gt;Sony&apos;s engineers did not need to fail. They reused the same secret random number -- the per-signature nonce -- for every signature, and any two signatures that share a nonce give up the private key by grade-school algebra. The curve was pristine. The master key fell out of two subtractions.&lt;/p&gt;
&lt;p&gt;Twelve years later, the failure jumped one layer up the stack. Java shipped an ECDSA &lt;em&gt;verifier&lt;/em&gt; that accepted the signature &lt;code&gt;(r, s) = (0, 0)&lt;/code&gt; for any message and any public key -- &quot;Psychic Signatures,&quot; CVE-2022-21449, a CVSS 7.5 flaw that forged TLS handshakes, signed JWTs, SAML assertions, and WebAuthn responses alike [@cve-2022-21449], [@neil-madden-2022]. One break leaked a private key through the &lt;em&gt;nonce&lt;/em&gt;; the other waved a forgery through at the &lt;em&gt;verifier&lt;/em&gt;. Neither touched the underlying hardness assumption. Nobody solved a discrete log. Nobody factored a modulus.&lt;/p&gt;
&lt;p&gt;That is the pattern this article is about, worth stating as a question you can carry through every section: &lt;strong&gt;when a signer or verifier meets a value it did not create, what secret does it leak, or what forgery does it wave through?&lt;/strong&gt; The answer is almost never &quot;the math was weak.&quot; A signature scheme is only as safe as three things it hands the caller and refuses to decide for you: the per-signature &lt;strong&gt;nonce&lt;/strong&gt;, the signature&apos;s &lt;strong&gt;canonical form&lt;/strong&gt;, and the &lt;strong&gt;verifier&apos;s acceptance rule&lt;/strong&gt;. ECDSA outsources all three at once.&lt;/p&gt;
&lt;p&gt;That is why one scheme -- its elliptic-curve discrete logarithm fully intact -- spawned four categorically different disasters: the reused nonce of the PS3, the drained Android Bitcoin wallets, the malleable transactions that unsettled Bitcoin, and the psychic verifier in Java. You half-know these headlines already. This is the one sentence underneath all of them.&lt;/p&gt;

The math held; the interface leaked.
&lt;p&gt;The rest of signature history since ECDSA is one effort to pull those three surfaces back &lt;em&gt;inside&lt;/em&gt; the scheme, so the caller can no longer get them wrong. To see why a shared random number hands over a private key -- and why &quot;just use Ed25519&quot; is a real answer but not the whole one -- you first have to be precise about what a signature promises, and the three jobs ECDSA quietly delegates to you.&lt;/p&gt;
&lt;h2&gt;2. What a Signature Actually Is (and the Three Things ECDSA Hands You)&lt;/h2&gt;
&lt;p&gt;Everyone knows the one-liner: sign with the private key, verify with the public key. That one-liner hides three decisions the scheme refuses to make for you. Start with the part that is simple.&lt;/p&gt;
&lt;p&gt;A digital signature scheme is three algorithms. &lt;code&gt;KeyGen -&amp;gt; (pk, sk)&lt;/code&gt; produces a public verification key and a private signing key. &lt;code&gt;Sign(sk, m) -&amp;gt; sigma&lt;/code&gt; turns a message and the private key into a signature. &lt;code&gt;Verify(pk, m, sigma) -&amp;gt; {accept, reject}&lt;/code&gt; lets anyone holding the public key check the pair. The word &lt;em&gt;anyone&lt;/em&gt; is the whole point.&lt;/p&gt;
&lt;p&gt;A message authentication code also proves a message is authentic, but it verifies with the same secret used to produce it, so only insiders can check it. A signature is &lt;strong&gt;publicly verifiable&lt;/strong&gt; and therefore &lt;strong&gt;non-repudiable&lt;/strong&gt;: the entire world can confirm it, and only the holder of &lt;code&gt;sk&lt;/code&gt; could have produced it.&lt;/p&gt;

A triple of algorithms: KeyGen produces a public key and a private key; Sign maps a private key and a message to a signature; Verify maps a public key, a message, and a signature to accept or reject. Unlike a message authentication code, it is publicly verifiable and non-repudiable, since verification needs only the public key.
&lt;p&gt;Public verifiability is what makes a signature strictly stronger than a MAC, and strictly slower. A MAC can rest on a single shared secret and a fast keyed hash. A signature cannot rest on any shared secret, because the verifier must be the whole world, so it has to be built on a public-key hardness assumption -- and pays for it in both compute and signature size.&lt;/p&gt;
&lt;h3&gt;The bar every scheme is measured against&lt;/h3&gt;
&lt;p&gt;&quot;Secure&quot; needs a precise meaning, and cryptographers settled on one in 1988. Goldwasser, Micali, and Rivest defined &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;existential unforgeability under adaptive chosen-message attack&lt;/strong&gt;&lt;/a&gt;, or EUF-CMA: an adversary who may ask you to sign any messages it likes, adaptively, still cannot produce a valid signature on a single &lt;em&gt;new&lt;/em&gt; message it never queried [@gmr-1988]. That is a demanding bar, and every scheme in this article is designed to clear it.&lt;/p&gt;
&lt;p&gt;But notice the exact word: a new &lt;em&gt;message&lt;/em&gt;. EUF-CMA says nothing about whether there might be more than one valid signature on a message you &lt;em&gt;did&lt;/em&gt; sign. Tighten the game by one word and you get &lt;strong&gt;strong unforgeability&lt;/strong&gt;, SUF-CMA: the adversary cannot produce any new valid &lt;em&gt;signature&lt;/em&gt;, even on an already-signed message. The gap between &quot;new message&quot; and &quot;new signature&quot; looks pedantic. It is the entire malleability story that unravels a Bitcoin transaction in a later section, so it is worth planting the seed now.&lt;/p&gt;

The standard security goal for signatures: after obtaining signatures on messages of its choice, an adversary still cannot produce a valid signature on any message it did not query. It forbids forging a new message but says nothing about producing a second signature on an old one.

A stronger goal that forbids even a new signature on an already-signed message. The difference from EUF-CMA is one word -- new message versus new signature -- and it is exactly the property that signature malleability violates.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; EUF-CMA does not promise there is only one valid signature per message. A scheme can be perfectly unforgeable in the textbook sense and still let anyone manufacture a second, different signature on a message you already signed. Whether that matters depends on whether your protocol ever treats the signature itself as an identifier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The thesis, stated as the scheme&apos;s interface&lt;/h3&gt;
&lt;p&gt;Here is the organizing idea of the field guide. A signature scheme hands the caller three things and quietly makes each one the caller&apos;s responsibility. ECDSA is the worked example that outsources all three.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surface 1, the nonce.&lt;/strong&gt; To sign, ECDSA needs a fresh secret random number for every signature. Supply a bad one and you leak the private key. &lt;strong&gt;Surface 2, the canonical form.&lt;/strong&gt; A valid ECDSA signature &lt;code&gt;(r, s)&lt;/code&gt; has an evil twin &lt;code&gt;(r, n-s)&lt;/code&gt; that also verifies, so the caller must decide whether those are &quot;the same&quot; signature. &lt;strong&gt;Surface 3, the verifier.&lt;/strong&gt; ECDSA&apos;s acceptance rule involves modular inversions and range checks, and the specification left enough slack that two conformant-looking verifiers can disagree about which signatures are valid. Every deployed break in this article lives in one of these three surfaces.&lt;/p&gt;

flowchart TD
    KG[KeyGen] --&amp;gt; SK[Private key sk]
    KG --&amp;gt; PK[Public key pk]
    M[Message] --&amp;gt; SG[Sign]
    SK --&amp;gt; SG
    N[&quot;Surface 1: the per-signature nonce you must supply&quot;] --&amp;gt; SG
    SG --&amp;gt; SIG[&quot;Signature r and s&quot;]
    SIG --&amp;gt; CF[&quot;Surface 2: is r and s the same as r and n minus s?&quot;]
    CF --&amp;gt; VF[Verify]
    PK --&amp;gt; VF
    M --&amp;gt; VF
    VR[&quot;Surface 3: the byte-exact acceptance rule&quot;] --&amp;gt; VF
    VF --&amp;gt; OUT{&quot;Accept or reject&quot;}
&lt;p&gt;The academic description of ECDSA that stands in for the paywalled ANSI standard spells out this division of labor: the scheme defines the arithmetic and leaves nonce generation, encoding choices, and verifier strictness to the implementer [@johnson-menezes-vanstone-2001], [@ansi-x962-1999]. The cleaner schemes we reach later pull these surfaces back inside the definition. BIP-340 Schnorr, for instance, specifies its verifier down to the byte precisely so the caller cannot get surface 3 wrong [@bip-340].&lt;/p&gt;
&lt;p&gt;The table below is the entire article compressed into one grid: read across each row to watch a surface move from &quot;the caller&apos;s problem&quot; to &quot;the scheme&apos;s problem.&quot;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Plain ECDSA&lt;/th&gt;
&lt;th&gt;RFC 6979 / hedged ECDSA&lt;/th&gt;
&lt;th&gt;Ed25519&lt;/th&gt;
&lt;th&gt;BIP-340 Schnorr&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Caller supplies from an RNG&lt;/td&gt;
&lt;td&gt;Pulled inside: derived from key and message&lt;/td&gt;
&lt;td&gt;Pulled inside: native deterministic&lt;/td&gt;
&lt;td&gt;Pulled inside: synthetic and hedged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Canonical form&lt;/td&gt;
&lt;td&gt;Caller must enforce low-&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Caller must enforce low-&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pulled inside: strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; on decode&lt;/td&gt;
&lt;td&gt;Pulled inside: fixed 64-byte form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Verifier rule&lt;/td&gt;
&lt;td&gt;Caller implements, easy to botch&lt;/td&gt;
&lt;td&gt;Caller implements, easy to botch&lt;/td&gt;
&lt;td&gt;Mostly specified (RFC 8032)&lt;/td&gt;
&lt;td&gt;Pulled inside: specified at the byte level&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two of those three surfaces were invented before ECDSA existed: the nonce in 1985, the &quot;form&quot; problem in 1978. To understand why a modern scheme still hands them to you, we have to go back to the moment signatures were only an idea.&lt;/p&gt;
&lt;h2&gt;3. Where Signatures Came From (1976 to 1996)&lt;/h2&gt;
&lt;p&gt;In 1976, two Stanford researchers could describe exactly what a signature must do and had no way to build one. Whitfield Diffie and Martin Hellman&apos;s &quot;New Directions in Cryptography&quot; named the requirement -- a private operation that anyone can verify with public information, giving the world a way to hold a signer to their message -- but it offered no algorithm to realize it [@diffie-hellman-1976]. The problem was posed before it was solved, and the two ways it eventually got solved are the two tracks this whole article runs along.&lt;/p&gt;
&lt;h3&gt;Track A: the trapdoor line&lt;/h3&gt;
&lt;p&gt;Two years later, RSA gave the first concrete construction. Signing is the private-key operation, verifying is the public-key operation, and &lt;a href=&quot;https://paragmali.com/blog/rsa-is-a-trapdoor-not-a-cryptosystem-oaep-pss-and-the-25-yea/&quot; rel=&quot;noopener&quot;&gt;the trapdoor&lt;/a&gt; -- factoring is hard, so inverting the public operation without the private exponent is infeasible -- makes the asymmetry real [@rsa-1978].&lt;/p&gt;
&lt;p&gt;It worked, and it &lt;em&gt;immediately&lt;/em&gt; exhibited surface 2. Textbook RSA, which signs the bare message as $\sigma = m^d \bmod N$, is existentially forgeable: pick any $\sigma$, compute $m = \sigma^e \bmod N$, and you have a valid pair for a message you did not choose but that is still &quot;signed.&quot;&lt;/p&gt;
&lt;p&gt;It is also multiplicatively malleable, because $\sigma_1 \cdot \sigma_2 \bmod N$ is a valid signature on $m_1 \cdot m_2 \bmod N$. The raw trapdoor has no notion of which messages are legitimately structured, so the caller must impose that structure. Getting that structure right, with a proof, took fifteen years and is the subject of a later section.&lt;/p&gt;
&lt;h3&gt;Track B: the discrete-log line&lt;/h3&gt;
&lt;p&gt;In 1985, Taher ElGamal built the first signature whose hardness rests on the discrete logarithm problem rather than factoring [@elgamal-1985]. It also introduced the load-bearing footgun that defines the rest of this article: a &lt;strong&gt;fresh secret nonce&lt;/strong&gt; drawn for every single signature. Learn that nonce, or reuse it, and the private key follows. This is where surface 1 enters the lineage, and DSA and ECDSA inherit it unchanged.&lt;/p&gt;
&lt;h3&gt;The engine and the yardstick&lt;/h3&gt;
&lt;p&gt;Two ideas from the late 1980s frame everything after. In 1986, Fiat and Shamir showed how to turn an interactive identification protocol -- prove you know a secret without revealing it -- into a non-interactive signature, by replacing the verifier&apos;s random challenge with a hash of the commitment and the message [@fiat-shamir-1986]. That transform is the engine underneath Schnorr, DSA, and EdDSA, and it comes with a catch we return to in the limits section: its security is argued in the random-oracle model, treating the hash as an ideal random function.&lt;/p&gt;
&lt;p&gt;Then, in 1988, GMR gave the field its yardstick, EUF-CMA, the definition every later scheme is measured against [@gmr-1988].&lt;/p&gt;

A method that converts an interactive public-coin identification protocol into a non-interactive signature by replacing the verifier&apos;s random challenge with a hash of the commitment and the message. Its security is argued in the random-oracle model, which treats the hash function as an ideal random function.
&lt;h3&gt;The clean template, and the patent that froze it&lt;/h3&gt;
&lt;p&gt;In 1989, Claus-Peter Schnorr published the scheme that, in hindsight, got everything right. The signer commits to a random point $R = kG$, computes a challenge $e = H(R \mathbin{|} m)$ by hashing the commitment and message, and answers with $s = k + ex$, where $x$ is the private key. Verification is a single linear check. That linearity is not a detail; it is the reason the scheme is provable, batchable, aggregatable, and non-malleable, and it is the direct ancestor of EdDSA and BIP-340 [@schnorr-1989-1991].&lt;/p&gt;
&lt;p&gt;There was one problem, and it bent the entire history. Schnorr patented the scheme (US 4,995,082, invented 1989, granted 1991, expired 2008) [@patent-us4995082]. NIST, which needed a royalty-free national standard, could not use it. So the best-behaved discrete-log signature ever designed sat frozen for two decades while the government standardized around something else. The clean scheme was invented first and deployed last.&lt;/p&gt;
&lt;p&gt;Schnorr&apos;s work appears twice in the literature: at CRYPTO &apos;89 as &quot;Efficient Identification and Signatures for Smart Cards,&quot; and in the Journal of Cryptology in 1991 as &quot;Efficient Signature Generation by Smart Cards.&quot; They are the same line of work at two venues; cite either, and do not treat one date as a correction of the other.&lt;/p&gt;

timeline
    title From an idea to a standard, 1976 to 2020
    1976 : Diffie and Hellman name the requirement, no algorithm
    1978 : RSA, the first construction, Track A
    1985 : ElGamal introduces the per-signature nonce, Track B
    1986 : Fiat-Shamir turns identification into signatures
    1989 : Schnorr, clean and linear, but patented until 2008
    1994 : DSA standardized as FIPS 186
    1999 : ECDSA moves DSA onto elliptic curves
    2011 : Ed25519 bakes determinism in
    2013 : RFC 6979 retrofits deterministic nonces
    2020 : BIP-340 Schnorr, byte-exact
&lt;p&gt;With the clean scheme locked behind a patent, the U.S. government needed a signature standard of its own. The choices it made under patent pressure are the reason the worse-behaved scheme shipped to the entire internet first.&lt;/p&gt;
&lt;h2&gt;4. Padding, DSA, and the Fateful Nonce&lt;/h2&gt;
&lt;p&gt;Two tracks, two ways to get it wrong first. RSA had to learn not to sign the bare message. The discrete-log camp had to standardize around a secret it could not afford to leak.&lt;/p&gt;
&lt;h3&gt;Track A cleans up its form&lt;/h3&gt;
&lt;p&gt;The fix for textbook RSA&apos;s forgeability is a rule you will hear again: never sign the bare message. Hash it first, then pad the hash into a full-width, unpredictable representative that fills the modulus. Done right, this kills existential forgery -- an attacker can no longer start from a random $\sigma$ and back out a validly structured message -- and it destroys the multiplicative structure that let signatures be multiplied together.&lt;/p&gt;
&lt;p&gt;The workhorse encoding is PKCS#1 v1.5, a deterministic padding standardized in the early 1990s and still ubiquitous [@rfc-8017]. Full-Domain Hash was the conceptual bridge: hash the message across the entire width of the modulus. And in 1996, Bellare and Rogaway&apos;s Probabilistic Signature Scheme (PSS) added a random salt and, with it, the tightest security proof any deployed signature would ever get [@bellare-rogaway-pss-1996], [@rsa-1978].&lt;/p&gt;
&lt;p&gt;We give PSS its own section later; for now, note only that Track A&apos;s first footgun was the &lt;em&gt;form&lt;/em&gt; of what it signed, and Track A fixed it by structuring that form.&lt;/p&gt;
&lt;h3&gt;Track B standardizes, under patent pressure&lt;/h3&gt;
&lt;p&gt;The discrete-log camp went a different way, and politics shaped the outcome. In 1991 NIST proposed the Digital Signature Algorithm, and in 1994 it became FIPS 186 [@wikipedia-dsa]. DSA is an ElGamal and Schnorr relative, deliberately structured to sidestep both the RSA patent and Schnorr&apos;s patent, credited to David Kravitz and assigned to the NSA [@patent-us5231668]. In dodging the clean patented scheme, DSA inherited ElGamal&apos;s design decision without modification: a fresh secret nonce for every signature.&lt;/p&gt;
&lt;p&gt;Keep the DSA dates distinct: proposed in 1991, standardized as FIPS 186 in 1994. The DSA patent (Kravitz, US 5,231,668) was filed in 1991 and granted in 1993. These four dates get conflated constantly.&lt;/p&gt;
&lt;h3&gt;Track B goes to curves&lt;/h3&gt;
&lt;p&gt;Through the late 1990s, Certicom and the ANSI X9F1 committee moved DSA onto elliptic curves. The &lt;a href=&quot;https://paragmali.com/blog/the-curve-was-hard-the-gap-was-soft-a-field-guide-to-using-e/&quot; rel=&quot;noopener&quot;&gt;elliptic-curve discrete logarithm&lt;/a&gt; is harder per bit than the finite-field version, so ECDSA gets equivalent security from much smaller keys -- 256-bit curves for 128-bit security, against 3072-bit fields for RSA and finite-field DSA. The template was otherwise unchanged, and so was the fateful decision: the &lt;strong&gt;same fresh secret nonce, once per signature&lt;/strong&gt; [@johnson-menezes-vanstone-2001].&lt;/p&gt;
&lt;p&gt;The normative ECDSA standard, ANSI X9.62-1999, is paywalled, so this article cites the open Johnson-Menezes-Vanstone description as its stand-in and never quotes X9.62 text directly. One more scoping note that matters for later decision rules: ECDSA lives on two different families of curves in practice -- the NIST P-curves used across web PKI, and secp256k1, the curve Bitcoin and Ethereum chose. The nonce discipline is identical on both.&lt;/p&gt;
&lt;p&gt;By the end of the 1990s, an enormous share of the world&apos;s authentication rested on Track B: DSA and ECDSA, both carrying a per-signature secret that had to be unique, unpredictable, and never leaked -- and that the &lt;em&gt;caller&lt;/em&gt;, not the scheme, was responsible for generating perfectly, every single time. Surface 1 was loaded and handed over to millions of implementers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; RSA&apos;s first footgun was the &lt;em&gt;form&lt;/em&gt; of what it signed -- the bare message was forgeable and malleable, and the cure was to structure it. The discrete-log camp&apos;s first footgun was the &lt;em&gt;secret&lt;/em&gt; it signed with -- a per-signature nonce the scheme trusted the caller to get right. Hold both in mind: the rest of the story is these two footguns being pulled, one at a time, back inside the scheme.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The discrete-log camp had built its entire deployed base on a secret the caller had to generate perfectly, every single time. It took exactly one game console, one wallet app, and one SSH client to prove how badly that goes.&lt;/p&gt;
&lt;h2&gt;5. The Nonce Catastrophe: ECDSA in Full&lt;/h2&gt;
&lt;p&gt;Here is the mechanism underneath the PS3 break, the drained Bitcoin wallets, and the 2024 PuTTY disclosure. And here is the precision almost every retelling gets wrong.&lt;/p&gt;
&lt;h3&gt;ECDSA, mechanically&lt;/h3&gt;
&lt;p&gt;Key generation is a private scalar $d$ and its public point $Q = [d]G$, where $G$ generates a group of prime order $n$. To sign a message $m$, ECDSA draws a nonce $k$, forms $R = [k]G$, sets $r = x(R) \bmod n$, and computes&lt;/p&gt;
&lt;p&gt;$$s = k^{-1}\bigl(H(m) + r,d\bigr) \bmod n.$$&lt;/p&gt;
&lt;p&gt;Verification recovers $u_1 = H(m),s^{-1}$ and $u_2 = r,s^{-1}$ and accepts exactly when $x\bigl([u_1]G + [u_2]Q\bigr) \bmod n = r$ [@johnson-menezes-vanstone-2001].Throughout, $H(m)$ is the hash truncated to the leftmost $\text{bitlen}(n)$ bits, not the full digest -- which matters whenever the hash is wider than the order $n$, as with the P-521 curve behind the PuTTY story below. Stare at the signing equation and the danger is obvious.&lt;/p&gt;
&lt;p&gt;The nonce $k$ is the load-bearing secret, and $s$ mixes $k$, the message hash, and the private key $d$ into one linear relation. Anything you learn about $k$ becomes a linear equation in $d$.&lt;/p&gt;

The fresh secret integer that a discrete-log signature draws for each signature, used to form the commitment point. It is the load-bearing secret: because the signature value mixes the nonce with the message hash and the private key, anything an attacker learns about the nonce turns into a linear equation in the private key.
&lt;h3&gt;The repeat case: certain from two signatures&lt;/h3&gt;
&lt;p&gt;Sign two different messages with the &lt;em&gt;same&lt;/em&gt; nonce and the two signatures share the same $r$ (because $r$ depends only on $k$). Subtract the two signing equations and $k$ cancels, leaving a single linear equation you can solve directly for the private key:&lt;/p&gt;
&lt;p&gt;$$d = \frac{s_2,H(m_1) - s_1,H(m_2)}{r,(s_1 - s_2)} \bmod n.$$&lt;/p&gt;
&lt;p&gt;This is not statistical. It is exact, from &lt;em&gt;two&lt;/em&gt; signatures. It is the PlayStation 3: Sony used a constant $k$, so every firmware signature shared an $r$, and two of them were enough [@ps3-27c3-2010]. And it is the 2013 Android Bitcoin thefts: a flaw in the Java &lt;code&gt;SecureRandom&lt;/code&gt; implementation caused the nonce to repeat across signatures generated on affected devices, and real coins drained from wallets whose signing keys were exposed by the collision [@bitcoin-android-2013], [@klyubin-android-2013].&lt;/p&gt;
&lt;p&gt;Keep the PS3 disclosures distinct: fail0verflow presented the constant-nonce ECDSA break at 27C3 in December 2010; George Hotz released a separate PS3 root key in January 2011 [@ps3-jailbreak-wikipedia]. They are related events, not the same one.&lt;/p&gt;
&lt;p&gt;The widely cited figure of roughly 55 BTC lost in the 2013 Android incident comes from secondary reporting; the bitcoin.org primary alert states no loss total. Treat the number as reported, not official.&lt;/p&gt;
&lt;p&gt;The recovery really is grade-school algebra. The demonstration below signs two messages under &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;a reused nonce&lt;/a&gt; on toy-sized parameters, then plays the attacker: given only the public signature values, it recovers the nonce and then the private key.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Illustrative toy sizes, NOT secure. Here r stands in for x([k]G) mod n; // a real attacker reads r straight off the two signatures. const n = 1000003n;                       // small prime &quot;group order&quot; const mod = (a, m) =&amp;gt; ((a % m) + m) % m; function inv(a, m) {                       // modular inverse, extended Euclid   let [or, r] = [mod(a, m), m], [os, s] = [1n, 0n];   while (r !== 0n) { const q = or / r; [or, r] = [r, or - q * r]; [os, s] = [s, os - q * s]; }   return mod(os, m); } // Signer&apos;s secrets (the attacker never sees these): const d = 123456n;                        // private key const k = 654321n;                        // per-signature nonce -- REUSED: the whole bug const r = 42n;                            // r = x([k]G) mod n; same k gives the same r const h1 = 111111n, h2 = 222222n;         // two message hashes // ECDSA signing: s = k^{-1} (h + r*d) mod n const s1 = mod(inv(k, n) * mod(h1 + r * d, n), n); const s2 = mod(inv(k, n) * mod(h2 + r * d, n), n); // Attacker sees only (r, s1, s2, h1, h2). Recover k, then d: const kRec = mod(mod(h1 - h2, n) * inv(mod(s1 - s2, n), n), n); const dRec = mod(mod(s1 * kRec - h1, n) * inv(r, n), n); console.log(&apos;recovered nonce k = &apos; + kRec + &apos;  (true &apos; + k + &apos;)&apos;); console.log(&apos;recovered key   d = &apos; + dRec + &apos;  (true &apos; + d + &apos;)&apos;); console.log(dRec === d ? &apos;PRIVATE KEY RECOVERED from two signatures&apos; : &apos;mismatch&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Two subtractions and two inversions. No discrete logarithm was harmed.&lt;/p&gt;
&lt;h3&gt;The bias case: statistical, and it needs many&lt;/h3&gt;
&lt;p&gt;Now change the failure. Instead of repeating the nonce, suppose the nonce generator is subtly &lt;em&gt;biased&lt;/em&gt; -- say the top few bits are always zero. No two signatures share an $r$, so the two-subtraction trick does not apply, and no single signature hands you the key.&lt;/p&gt;
&lt;p&gt;What you have instead is a stream of signatures that each leak a little information about a secret, which is precisely the &lt;strong&gt;Hidden Number Problem&lt;/strong&gt;: recover a hidden value from many samples that each reveal a few high-order bits. Reformulate the biased signatures as an HNP instance, run lattice reduction, and the private key falls out -- but only after you collect &lt;em&gt;many&lt;/em&gt; signatures.&lt;/p&gt;

The problem of recovering a hidden integer from many samples that each leak only a few of its high-order bits. A stream of biased-nonce signatures is an HNP instance, and lattice reduction solves it. This is why a biased nonce is a statistical attack that needs many signatures rather than a single one.
&lt;p&gt;The canonical modern proof is PuTTY. In April 2024, CVE-2024-31497 disclosed that PuTTY&apos;s ECDSA nonce generator for the NIST P-521 curve was biased so that the first 9 bits of each nonce are zero, enabling full secret key recovery in roughly 60 signatures [@cve-2024-31497]. Sixty. Not two.&lt;/p&gt;
&lt;p&gt;The rigor behind that number is a quarter-century of work: Boneh and Venkatesan introduced the Hidden Number Problem in 1996 [@boneh-venkatesan-1996]; Howgrave-Graham and Smart turned it into the first practical lattice recovery of DSA keys from partial nonces in 2001 [@howgrave-graham-smart-2001]; Nguyen and Shparlinski made ECDSA key recovery from a few leaked nonce bits rigorous in 2003 [@nguyen-shparlinski-2003]; and by 2020, LadderLeak recovered keys with under one bit of nonce leakage per signature [@ladderleak-2020].&lt;/p&gt;
&lt;p&gt;Bleichenbacher&apos;s much-cited biased-nonce DSA attack has no standalone peer-reviewed paper; it circulated through talks and other authors&apos; write-ups. Cite the mechanism through the Nguyen-Shparlinski and LadderLeak lattice literature, not a fabricated Bleichenbacher reference.&lt;/p&gt;
&lt;h3&gt;The precision that almost every retelling gets wrong&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A repeated nonce leaks the private key with certainty from just two signatures. A few-bit-biased nonce is different: a statistical lattice attack that needs many signatures, not one. Conflating repeat with bias is the single most corrected misconception in this corner of cryptography.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack&lt;/th&gt;
&lt;th&gt;Signatures needed&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Certainty&lt;/th&gt;
&lt;th&gt;Canonical example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Repeated nonce&lt;/td&gt;
&lt;td&gt;Two&lt;/td&gt;
&lt;td&gt;Elementary algebra, two linear equations&lt;/td&gt;
&lt;td&gt;Exact, deterministic&lt;/td&gt;
&lt;td&gt;PS3 (2010); Android wallets (2013)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biased nonce&lt;/td&gt;
&lt;td&gt;Many (tens to thousands)&lt;/td&gt;
&lt;td&gt;Hidden Number Problem, lattice reduction&lt;/td&gt;
&lt;td&gt;Statistical&lt;/td&gt;
&lt;td&gt;PuTTY (roughly 60); LadderLeak (under one bit)&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; A reused nonce is certain from two signatures, and the fix is to never reuse it. A biased nonce is a statistical HNP or lattice attack that needs many signatures, and the fix is unbiased generation or a constant-time implementation. They have different signature counts, different math, and different cures. Do not conflate them, and do not claim that a single few-bit-biased signature leaks the key.&lt;/p&gt;
&lt;/blockquote&gt;

Each biased signature gives a linear relation of the form $k_i \equiv a_i + b_i\,d \pmod n$ where the unknown nonce $k_i$ is known to be small (its top bits are zero). Stack these relations as rows of a lattice whose short vectors correspond to the small nonces. Running LLL or BKZ finds a short vector, which reveals the nonces and hence $d$. The fewer bits each signature leaks, the more signatures and the more lattice-reduction effort you need -- hence &quot;statistical, and it needs many.&quot;
&lt;h3&gt;Two root causes hide under the word &quot;bias&quot;&lt;/h3&gt;
&lt;p&gt;There is a second fork that decides which cure you need. A &lt;em&gt;generation&lt;/em&gt; failure -- a bad RNG that repeats or biases the nonce -- is cured by not depending on the RNG at all, which is the deterministic signing of the next section. But a &lt;em&gt;usage&lt;/em&gt; failure is different: even a perfectly generated nonce leaks if the code that consumes it, the scalar multiplication $[k]G$, runs in time or draws power that depends on the nonce&apos;s bit-length. That is a side channel, and no amount of clever nonce &lt;em&gt;generation&lt;/em&gt; closes it.&lt;/p&gt;
&lt;p&gt;In 2019, both Minerva and TPM-Fail recovered ECDSA keys from exactly this timing leak, then finished the job with the same lattice machinery as the bias case [@minerva-2019], [@tpm-fail-2019]. The cure there is constant-time scalar multiplication.&lt;/p&gt;

flowchart TD
    K[&quot;The per-signature nonce k&quot;] --&amp;gt; G[&quot;Generation failure: how k is produced&quot;]
    K --&amp;gt; U[&quot;Usage failure: how k is consumed&quot;]
    G --&amp;gt; R[&quot;Repeat: same k twice, key from two signatures&quot;]
    G --&amp;gt; B[&quot;Bias: predictable bits, key from many signatures&quot;]
    U --&amp;gt; T[&quot;Timing or EM leak of k during scalar multiplication&quot;]
    R --&amp;gt; C1[&quot;Cure: deterministic nonce, RFC 6979&quot;]
    B --&amp;gt; C1
    T --&amp;gt; C2[&quot;Cure: constant-time scalar multiplication&quot;]
&lt;p&gt;So the random-number generator was the enemy in three of these four stories. The obvious fix is a better RNG. The actual fix, the one the whole field converged on, was to use no randomness at all.&lt;/p&gt;
&lt;h2&gt;6. The Fix for a Randomness Disaster Was Less Randomness&lt;/h2&gt;
&lt;p&gt;The counterintuitive move: if a bad random nonce is what kills you, stop rolling the dice. Derive the nonce deterministically from the one thing that is already secret and the one thing that is already unique.&lt;/p&gt;
&lt;h3&gt;RFC 6979, the retrofit&lt;/h3&gt;
&lt;p&gt;Thomas Pornin&apos;s RFC 6979 does exactly that. It derives the nonce as a deterministic function of the private key and the message hash, driving an HMAC-based deterministic random bit generator so that signing consumes &lt;em&gt;no&lt;/em&gt; runtime entropy. &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;A broken system RNG&lt;/a&gt; can no longer cause a repeat or a generation bias, because there is no RNG in the signing path [@rfc-6979].&lt;/p&gt;
&lt;p&gt;The elegance is that nothing downstream changes: a deterministic signature verifies under the &lt;em&gt;same&lt;/em&gt; verifier as a randomized one, so this is a drop-in with zero protocol change. The RFC states the goal plainly -- signers &quot;do not need access to a source of high-quality randomness&quot; -- and warns that even slight biases in nonce generation may be turned into attacks, exactly the HNP lineage from the previous section [@rfc-6979].&lt;/p&gt;

Deriving the per-signature nonce as a deterministic function of the private key and the message, via an HMAC-based deterministic random bit generator, so that signing needs no runtime entropy and a failing RNG can neither repeat nor bias the nonce. The resulting signature verifies under the ordinary verifier.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The whole class of PS3 and Android failures came from trusting a random-number generator to produce a perfect nonce. Deterministic signing removes the generator from the signing path entirely. Then the field adds a measured dose of randomness back for a different reason.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;But determinism is not unconditionally safer&lt;/h3&gt;
&lt;p&gt;Determinism trades one threat model for another. A fully deterministic signer is a &lt;em&gt;fixed, replayable target&lt;/em&gt;. Feed it the same key and message twice and it does exactly the same computation, which is the ideal setup for a &lt;strong&gt;differential fault attack&lt;/strong&gt;: run the signer twice, glitch one of the two runs with a voltage or clock disturbance, and diff the correct output against the faulted one to recover the key.&lt;/p&gt;
&lt;p&gt;Poddebniak and colleagues demonstrated this against deterministic signature schemes in 2018 [@poddebniak-fault-2018]. NIST took the point: FIPS 186-5 approves deterministic ECDSA yet explicitly flags deterministic schemes as being of particular concern for fault attacks [@fips-186-5].&lt;/p&gt;

Randomized signing is vulnerable to RNG failure but resists differential fault attacks, because two signings of the same message differ. Deterministic signing is immune to RNG failure but hands an attacker a replayable target. Neither extreme dominates. Poddebniak et al. showed the fault channel is real, and the FIPS 186-5 warning is the standards body saying so out loud. The resolution is not to pick a side but to combine them.
&lt;h3&gt;The resolution: hedged signing&lt;/h3&gt;
&lt;p&gt;Hedged signing mixes a little fresh randomness &lt;em&gt;back in&lt;/em&gt; on top of the deterministic derivation. RFC 6979&apos;s own section on variants sanctions adding extra input to the nonce derivation, noting that the result is then no longer deterministic (EC)DSA [@rfc-6979]. The payoff is that a hedged signer survives &lt;em&gt;both&lt;/em&gt; failure modes: if the RNG dies, the deterministic core still produces a safe, distinct nonce; if the RNG works, the fresh entropy makes each signing unrepeatable and defeats the differential fault attack.&lt;/p&gt;
&lt;p&gt;This is where modern libraries land by default. Go&apos;s &lt;code&gt;crypto/ecdsa&lt;/code&gt; hedges: its documentation says signatures &quot;are not deterministic, but entropy is mixed with the private key and the message,&quot; and that &quot;if rand is nil, Sign will produce a deterministic signature according to RFC 6979&quot; [@go-crypto-ecdsa]. BoringSSL mixes in additional entropy the same way [@boringssl-ecdsa], and the security analysis of hedged Fiat-Shamir signatures under faults gives this posture a formal footing [@aranha-hedged-fs-2019].&lt;/p&gt;

Mixing a small amount of fresh randomness into the deterministic nonce derivation, so the signer survives both RNG failure and single fault attacks. It is explicitly not deterministic (EC)DSA, and it is the default in libraries such as Go crypto/ecdsa and BoringSSL.
&lt;p&gt;One caution: it fixes only the &lt;em&gt;generation&lt;/em&gt; surface. The &lt;em&gt;usage&lt;/em&gt; leak from the previous section -- the timing side channel during scalar multiplication -- is untouched by how you derive the nonce, and its cure remains constant-time code. Three cures, three distinct threats.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nonce strategy&lt;/th&gt;
&lt;th&gt;Nonce source&lt;/th&gt;
&lt;th&gt;Survives RNG failure?&lt;/th&gt;
&lt;th&gt;Resists the fault attack?&lt;/th&gt;
&lt;th&gt;Where it lands&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Randomized (classic ECDSA)&lt;/td&gt;
&lt;td&gt;Fresh RNG each signature&lt;/td&gt;
&lt;td&gt;No, the RNG is the whole risk&lt;/td&gt;
&lt;td&gt;Yes, runs differ&lt;/td&gt;
&lt;td&gt;The PS3 and Android failure mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic (RFC 6979)&lt;/td&gt;
&lt;td&gt;HMAC-DRBG of key and message&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No, a replayable target&lt;/td&gt;
&lt;td&gt;Verifies under the ordinary verifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hedged (RFC 6979 variants)&lt;/td&gt;
&lt;td&gt;Deterministic core plus fresh entropy&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Go and BoringSSL default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;RFC 6979 pulled surface 1 back inside the scheme, but only surface 1, and only as a patch bolted onto a design that still leaks the other two. A cleaner scheme would make deterministic nonces, a fixed encoding, and a byte-exact verifier all native. That scheme already existed. It had been waiting on a patent to expire for two decades.&lt;/p&gt;
&lt;h2&gt;7. The Breakthrough: Schnorr Done Right&lt;/h2&gt;
&lt;p&gt;Everything RFC 6979 retrofitted, one scheme had baked in from birth. It was invented in 1989 and could not be deployed until 2008, because it was patented.&lt;/p&gt;
&lt;h3&gt;Why Schnorr is clean&lt;/h3&gt;
&lt;p&gt;Recall Schnorr&apos;s response, $s = k + ex$. That single linear equation is what makes the scheme provable, batchable, and aggregatable. Provable, because the linear structure is what the Pointcheval-Stern forking lemma needs to reduce forgery to the discrete-log problem in the random-oracle model [@pointcheval-stern-1996-2000]. Batchable, because a linear verification equation lets you check many signatures as one combined equation, the subject of a later section. Aggregatable, because linear things add, which is why threshold and multi-signature schemes like FROST and MuSig2 build directly on Schnorr.&lt;/p&gt;
&lt;p&gt;The Fiat-Shamir transform is still the engine turning the underlying identification protocol into a signature; Schnorr is simply the cleanest identification protocol to feed it.&lt;/p&gt;
&lt;h3&gt;EdDSA and Ed25519 pull all three surfaces inside&lt;/h3&gt;
&lt;p&gt;In 2011, Bernstein, Duif, Lange, Schwabe, and Yang published EdDSA, and its edwards25519 instantiation, Ed25519, is the scheme that pulls all three surfaces inside the definition by design [@bernstein-eddsa-2011].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surface 1, the nonce, is native and deterministic.&lt;/strong&gt; Ed25519 computes the nonce as $r = H(\text{prefix} \mathbin{|} M) \bmod L$, hashing a secret key-derived prefix together with the message. There is no runtime RNG in the signing path, so the entire PS3 and Android failure class is structurally impossible rather than merely discouraged. The designers said so directly, and named the incident:&lt;/p&gt;

Signatures are generated deterministically ... directly relevant to the recent collapse of the Sony PlayStation 3 security system.
&lt;p&gt;&lt;strong&gt;Surface 2, the encoding, is fixed.&lt;/strong&gt; An Ed25519 signature is a fixed 64-byte string, a point encoding followed by a scalar, and RFC 8032 mandates a strict range check that the scalar satisfy $0 \le S &amp;lt; L$ on decode. Enforcing that check makes a strict Ed25519 implementation strongly unforgeable, closing the malleability surface at the encoding level [@rfc-8032].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surface 3, the arithmetic, is safe by construction.&lt;/strong&gt; edwards25519 uses a complete twisted-Edwards addition law with no exceptional cases, so the constant-time implementation is the natural one, and there are no special points where a naive verifier misbehaves [@bernstein-eddsa-2011]. Determinism here is &lt;em&gt;structural&lt;/em&gt;, not a patch bolted on afterward, which is the real reason &quot;why determinism helps&quot; is a property of the design rather than a library option.&lt;/p&gt;

A Schnorr signature instantiated on a complete twisted-Edwards curve with a key-prefixed deterministic nonce and a fixed encoding. Ed25519 is its edwards25519 instantiation: constant-time by construction, misuse-resistant by design, and strongly unforgeable under a strict verifier.

A nonce derived by hashing a secret key-derived prefix together with the message, so it is deterministic, unique per message, and never drawn from a runtime random-number generator. Hashing the public key into the challenge additionally hardens the scheme in multi-user and related-key settings.
&lt;p&gt;Key-prefixing -- folding the public key into the hash that produces the challenge -- is a small design choice with outsized value: it hardens the scheme against multi-user and related-key attacks that a bare Schnorr challenge would leave open.&lt;/p&gt;

flowchart TD
    M[Message] --&amp;gt; SCH
    SK[Private key] --&amp;gt; SCH
    subgraph SCH[&quot;Inside EdDSA and BIP-340&quot;]
        N[&quot;Surface 1: deterministic key-prefixed nonce&quot;]
        F[&quot;Surface 2: fixed canonical encoding&quot;]
        V[&quot;Surface 3: byte-exact verifier&quot;]
    end
    SCH --&amp;gt; SIG[&quot;One valid signature, no caller choices&quot;]
    SIG --&amp;gt; OK{&quot;Accept or reject, deterministically&quot;}
&lt;h3&gt;BIP-340: Schnorr over secp256k1, with the verifier pulled inside&lt;/h3&gt;
&lt;p&gt;Bitcoin needed Schnorr on its own curve, secp256k1, and in 2020 BIP-340 delivered it: 64-byte signatures, 32-byte x-only public keys, and, most importantly for surface 3, a verifier specified down to the byte. The specification states that the verification algorithm is &quot;completely specified ... at the byte level,&quot; the guarantee whose full payoff we reach when we return to verifier strictness later [@bip-340].&lt;/p&gt;
&lt;p&gt;BIP-340 also adopts the hedged idea natively: it derives a &lt;em&gt;synthetic&lt;/em&gt; nonce and notes that the added randomness is only supplemental to security, so a faulty RNG cannot break it. Bitcoin&apos;s Taproot upgrade deployed it in November 2021 [@optech-taproot-2021].&lt;/p&gt;
&lt;p&gt;Now the punchline. The scheme that makes deterministic nonces, a fixed encoding, and a byte-exact verifier native is Schnorr, invented in 1989. It could not be deployed until its patent expired in 2008, which is exactly why the worse-behaved ECDSA shipped to the entire internet two decades before its clean successor. Ed25519 arrived in 2011, BIP-340 in 2020. The best-behaved design was first on paper and last in production.&lt;/p&gt;
&lt;p&gt;The two-decade gap between the clean scheme&apos;s invention (US 4,995,082, 1989) and its deployment -- Ed25519 in 2011, BIP-340 in 2020 -- is a patent artifact, not a technical one.&lt;/p&gt;
&lt;p&gt;One honest caveat keeps this from being a silver bullet. Plain Ed25519 has no hedging knob: it is purely deterministic, so on hardware where an attacker can inject faults it is the &lt;em&gt;harder-hit&lt;/em&gt; fault target, and hardened embedded implementations re-inject randomness exactly as the hedged-signing analysis recommends [@poddebniak-fault-2018], [@aranha-hedged-fs-2019]. This is coexistence, not a coronation. Ed25519 and BIP-340 close all three surfaces on the discrete-log track. But the other 1976 track, RSA, had its own reckoning with the &lt;em&gt;form&lt;/em&gt; of a signature, and its answer came with something the discrete-log schemes still lack: a tight proof.&lt;/p&gt;
&lt;h2&gt;8. RSA-PSS: Probabilistic Signing with a Tight Proof&lt;/h2&gt;
&lt;p&gt;On the discrete-log track, determinism was the virtue. On the RSA track, the opposite move -- adding randomness -- bought the best security proof any deployed signature has.&lt;/p&gt;
&lt;h3&gt;PSS, mechanically&lt;/h3&gt;
&lt;p&gt;Never sign the bare message. The EMSA-PSS encoding hashes the message, mixes in a fresh random salt, and builds a full-width encoded message before the RSA private-key operation. Concretely, it forms $H = \mathrm{Hash}(\text{padding} \mathbin{|} \mathrm{Hash}(M) \mathbin{|} \text{salt})$, masks the salt-bearing data block against a mask generated from $H$, and assembles the encoded message as maskedDB followed by $H$ followed by the trailer byte &lt;code&gt;0xbc&lt;/code&gt;. The signature is that encoded message raised to the private exponent modulo $n$ [@rfc-8017], [@bellare-rogaway-pss-1996]. Verification recomputes the whole structure and checks it exactly, rejecting anything that does not fit the padding shape.&lt;/p&gt;

flowchart LR
    M[Message] --&amp;gt; MH[&quot;mHash = Hash of M&quot;]
    SALT[&quot;Random salt&quot;] --&amp;gt; H2[&quot;H = Hash of padding, mHash, salt&quot;]
    MH --&amp;gt; H2
    H2 --&amp;gt; DB[&quot;maskedDB = data block xor mask from H&quot;]
    DB --&amp;gt; EM[&quot;EM = maskedDB then H then 0xbc&quot;]
    H2 --&amp;gt; EM
    EM --&amp;gt; SIG[&quot;Signature = EM to the d mod n&quot;]
&lt;h3&gt;Why probabilistic buys a tight proof&lt;/h3&gt;
&lt;p&gt;Bellare and Rogaway proved PSS existentially unforgeable with a &lt;em&gt;tight&lt;/em&gt; reduction to the RSA problem in the random-oracle model: a forger against PSS can be turned into an algorithm that inverts RSA with essentially the same success probability and running time [@bellare-rogaway-pss-1996].&lt;/p&gt;
&lt;p&gt;That word &lt;em&gt;tight&lt;/em&gt; is the whole prize. A &lt;strong&gt;tight&lt;/strong&gt; reduction loses almost nothing between the scheme&apos;s security and the underlying hard problem, so a concrete key size gives you concrete confidence; a &lt;strong&gt;loose&lt;/strong&gt; reduction loses a large factor, so you either accept a weaker guarantee or inflate parameters to compensate. By contrast, PKCS#1 v1.5&apos;s signature padding has only a heuristic argument, not a tight proof. Among deployed signatures, PSS&apos;s guarantee is the gold standard, and it sets up the proof-quality spectrum we return to in the limits section.&lt;/p&gt;

The Probabilistic Signature Scheme encoding for RSA, which injects a fresh random salt into the padding before the RSA private-key operation and has the verifier recompute and check that structure exactly. The salt is what makes its security reduction to the RSA problem tight, unlike the deterministic PKCS#1 v1.5 padding.
&lt;p&gt;Full-Domain Hash was PSS&apos;s provable but loose predecessor: hash the message across the full width of the modulus, then apply the private operation. PSS keeps that idea and adds the random salt, and the salt is precisely what upgrades the reduction from loose to tight.&lt;/p&gt;
&lt;h3&gt;The deployment reality, stated exactly&lt;/h3&gt;
&lt;p&gt;&quot;Is RSA-PSS widely deployed?&quot; has a split answer, and blurring the split is a common error. TLS 1.3 makes RSA-PSS mandatory to implement for the online handshake signature: the &lt;code&gt;CertificateVerify&lt;/code&gt; message uses the &lt;code&gt;rsa_pss_rsae_*&lt;/code&gt; schemes, and a compliant implementation must support them [@rfc-8446]. That is a specification requirement.&lt;/p&gt;
&lt;p&gt;Separately, as a matter of deployment, most Web-PKI &lt;em&gt;certificates&lt;/em&gt; -- the long-lived signatures a certificate authority puts on the certs in the chain -- are still PKCS#1 v1.5. So the handshake signature your browser makes live and the &lt;a href=&quot;https://paragmali.com/blog/a-perfect-signature-for-a-certificate-that-should-never-have/&quot; rel=&quot;noopener&quot;&gt;certificate-chain signatures&lt;/a&gt; it validates are &lt;em&gt;different&lt;/em&gt; signatures with &lt;em&gt;different&lt;/em&gt; padding, and the honest answer separates them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The handshake CertificateVerify signature is RSA-PSS by TLS 1.3 requirement. The certificate-chain signatures a certificate authority produces are, in practice, still mostly PKCS#1 v1.5. Same connection, two padding schemes, two different signatures. Do not answer &quot;does the web use PSS?&quot; without saying which of the two you mean.&lt;/p&gt;
&lt;/blockquote&gt;

This is the boundary never to blur. PKCS#1 v1.5 *encryption* padding is the classic Bleichenbacher decryption oracle, a genuine break of the encryption scheme, and a companion part in this series on RSA owns that story. PKCS#1 v1.5 *signatures* are a different animal: the scheme is not broken.&lt;p&gt;Its real-world failures were &lt;em&gt;implementation&lt;/em&gt; bugs in lax verifiers -- most famously the low-exponent forgery that a sloppy parser allows when the public exponent is small, and its 2014 re-occurrence known as BERserk. Those are parsing bugs, not a break of the signature math. That low-exponent forgery has no standalone peer-reviewed paper, so cite the mechanism and the secondary write-ups, never a fabricated primary. The practical rule stands regardless: use RSA-PSS for new designs [@rfc-8017].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The cost profile that decides where PSS fits&lt;/h3&gt;
&lt;p&gt;RSA&apos;s economics are lopsided and they determine where it belongs. Verification uses a tiny public exponent, usually 65537, so it is very fast. Signing uses the full-width private exponent, so it is slow, and the signatures are large: 256 bytes at RSA-2048, 384 bytes at RSA-3072, an order of magnitude bigger than a 64-byte elliptic-curve signature. That makes RSA-PSS excellent where you verify far more often than you sign, such as certificates and firmware images, and painful where you sign constantly. We put real numbers on this in the state-of-the-art section.&lt;/p&gt;
&lt;p&gt;PSS pinned RSA&apos;s &lt;em&gt;form&lt;/em&gt; with a proof. But the discrete-log schemes still had an unresolved question about form, one that let anyone alter a signature without the key and briefly threatened to break Bitcoin.&lt;/p&gt;
&lt;h2&gt;9. Malleability, Canonicalization, and Strong Unforgeability&lt;/h2&gt;
&lt;p&gt;Recall the promise EUF-CMA does &lt;em&gt;not&lt;/em&gt; make: that there is only one valid signature per message. For ECDSA there are always at least two, and the caller was left to notice.&lt;/p&gt;
&lt;h3&gt;Surface 2: ECDSA malleability, mechanically&lt;/h3&gt;
&lt;p&gt;Take any valid ECDSA signature &lt;code&gt;(r, s)&lt;/code&gt;. Now negate the scalar: &lt;code&gt;(r, n - s)&lt;/code&gt; also verifies, and it is a different signature on the &lt;em&gt;same&lt;/em&gt; message, producible by anyone with no private key at all. The reason is visible in the verification equation. Replacing $s$ with $n - s$ is replacing it with $-s \bmod n$, which flips the signs of both $u_1$ and $u_2$, negating the recovered point to $-R$. On these curves a point and its negation share an $x$-coordinate, so $x(-R) \bmod n = x(R) \bmod n = r$, and the check still passes.&lt;/p&gt;
&lt;p&gt;BIP-340 states the fact plainly: &quot;If (r,s) is a valid ECDSA signature then (r,n-s) is also valid&quot; [@bip-340]. So standard ECDSA meets EUF-CMA but not SUF-CMA -- exactly why that one-word distinction was worth planting seven sections ago.&lt;/p&gt;

The existence of a second valid signature on an already-signed message that anyone can derive without the private key. For ECDSA, whenever (r, s) is valid so is (r, n - s). It is the reason plain ECDSA satisfies EUF-CMA but fails SUF-CMA.
&lt;h3&gt;The protocol fallout: Bitcoin transaction malleability&lt;/h3&gt;
&lt;p&gt;Malleability is harmless until a protocol treats the signature as an identifier. Bitcoin did. A transaction id is a hash computed over the whole transaction &lt;em&gt;including its signatures&lt;/em&gt;, so flipping &lt;code&gt;s&lt;/code&gt; to &lt;code&gt;n - s&lt;/code&gt; changes the txid without changing what the transaction does. Anything that referenced an unconfirmed transaction by its id -- a chain of dependent spends, a naive payment tracker -- could be broken by a third party rewriting the signature in flight [@decker-wattenhofer-2014].&lt;/p&gt;
&lt;p&gt;The honest Mt. Gox nuance: transaction malleability was real and was publicly blamed for the 2014 collapse, but Decker and Wattenhofer measured the malleability activity on the network and found it insufficient to account for the losses, and later analysis attributes most of the missing coins to a wallet compromise around 2011. Real and blamed, but not the sole cause.&lt;/p&gt;
&lt;h3&gt;The fix: low-s canonicalization restores SUF-CMA&lt;/h3&gt;
&lt;p&gt;If two signatures differ only by $s$ versus $n - s$, pick one. The &lt;strong&gt;low-s&lt;/strong&gt; rule requires $s \le n/2$, so exactly one of the twins is standard and the other is rejected on sight. Bitcoin moved this from proposal to policy across BIP-62 and BIP-146, and it restores strong unforgeability in the system that needed it most [@bip-62], [@bip-146]. Ed25519&apos;s analogue is the strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; range check that RFC 8032 mandates on decode, which is what makes a strict Ed25519 implementation SUF-CMA [@rfc-8032].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If any part of your protocol hashes over or deduplicates by the signature itself -- transaction ids, caches, idempotency keys, replay checks -- plain EUF-CMA lets an attacker manufacture a second valid signature and break your assumption. Enforce low-s or a strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; range check, or choose a SUF-CMA-by-design scheme such as BIP-340.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Surface 3: the verifier, which closed last and most embarrassingly&lt;/h3&gt;
&lt;p&gt;The third surface failed in two distinct shapes. The first is a &lt;em&gt;permissive&lt;/em&gt; verifier. In 2022, Java&apos;s ECDSA verification accepted the all-zero signature &lt;code&gt;(r, s) = (0, 0)&lt;/code&gt; for any message and any key, because it skipped the range check that forbids zero. &quot;Psychic Signatures,&quot; CVE-2022-21449, scored CVSS 7.5 and forged anything built on Java&apos;s signatures: TLS, signed JWTs, SAML assertions, OIDC id tokens, and WebAuthn responses [@cve-2022-21449], [@neil-madden-2022].&lt;/p&gt;
&lt;p&gt;The second shape is more subtle: &lt;em&gt;honest disagreement&lt;/em&gt;. &quot;Taming the Many EdDSAs&quot; showed that verifiers all conforming to RFC 8032 nonetheless split on adversarial inputs -- cofactored versus cofactorless verification equations, &lt;a href=&quot;https://paragmali.com/blog/the-signature-was-valid-the-message-was-forged-a-field-guide/&quot; rel=&quot;noopener&quot;&gt;non-canonical point encodings&lt;/a&gt;, small-order points -- so the same signature could be valid to one library and invalid to another [@chalkias-many-eddsas-2020]. For a consensus system that is a disaster, and ZIP-215 answers it by pinning a byte-exact validation profile on which single and batch verification always agree [@zip-215].&lt;/p&gt;
&lt;p&gt;Edwards curves have a cofactor, so the verification equation can be written to multiply through by it (cofactored) or not (cofactorless). RFC 8032 notes the cofactorless check is sufficient but not required, and that latitude is exactly what let conformant verifiers disagree until ZIP-215 fixed a single byte-exact rule.&lt;/p&gt;
&lt;p&gt;BIP-340 treats this surface as an engineering requirement rather than an afterthought, which is why its verifier is specified to the byte:&lt;/p&gt;

The verification algorithm must be completely specified at the byte level. This guarantees that nobody can construct a signature that is valid to some verifiers but not all.
&lt;h3&gt;The evidence, in one table&lt;/h3&gt;
&lt;p&gt;Every named break in this article splits cleanly across the three surfaces. Read the &quot;Surface&quot; column and the thesis stops being a slogan and becomes a body count.&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;Surface&lt;/th&gt;
&lt;th&gt;Sub-type&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;The rule it teaches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;PlayStation 3&lt;/td&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Repeat&lt;/td&gt;
&lt;td&gt;One constant nonce across all signatures&lt;/td&gt;
&lt;td&gt;Never reuse a nonce; two signatures leak the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android Bitcoin wallets&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Broken SecureRandom repeated the nonce&lt;/td&gt;
&lt;td&gt;Do not trust the RNG; derive the nonce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitcoin txid, Mt. Gox era&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;2. Canonical form&lt;/td&gt;
&lt;td&gt;Malleability&lt;/td&gt;
&lt;td&gt;(r, n - s) is a free second signature&lt;/td&gt;
&lt;td&gt;Enforce low-s; treat SUF-CMA as the bar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minerva, TPM-Fail&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Usage, timing&lt;/td&gt;
&lt;td&gt;Nonce bit-length leaked during scalar mult&lt;/td&gt;
&lt;td&gt;Constant-time scalar multiplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many EdDSAs, ZIP-215&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;3. Verifier&lt;/td&gt;
&lt;td&gt;Disagreement&lt;/td&gt;
&lt;td&gt;Conformant verifiers split on edge cases&lt;/td&gt;
&lt;td&gt;Specify the verifier at the byte level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Psychic Signatures&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;3. Verifier&lt;/td&gt;
&lt;td&gt;Permissive&lt;/td&gt;
&lt;td&gt;Verifier accepted (0, 0) for any message&lt;/td&gt;
&lt;td&gt;Range-check the signature; reject zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PuTTY&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Bias&lt;/td&gt;
&lt;td&gt;Top 9 bits of each P-521 nonce were zero&lt;/td&gt;
&lt;td&gt;Unbiased or deterministic nonce generation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Notice what is missing from the root-cause column: not once did anyone solve an elliptic-curve discrete logarithm or factor a modulus. That is Aha 1, complete. Fix the nonce, fix the form, fix the verifier, and you have a signature you can trust one at a time. But the modern world verifies millions at once, and the obvious way to batch them is quietly forgeable.&lt;/p&gt;
&lt;h2&gt;10. Batch Verification, and the One Condition That Makes It Safe&lt;/h2&gt;
&lt;p&gt;Schnorr&apos;s linearity offers a tempting shortcut: instead of checking a thousand signatures one by one, check their sum once. Done naively, that shortcut lets an attacker slip forgeries past you.&lt;/p&gt;
&lt;h3&gt;The technique&lt;/h3&gt;
&lt;p&gt;Because Schnorr, EdDSA, and BIP-340 verification is a single linear equation, a batch of $u$ signatures can be checked as one random linear combination of those equations. That collapses $u$ separate double-scalar multiplications into a single large multi-scalar multiplication, whose cost is sublinear in the number of distinct bases, which is where the throughput win comes from. The original Ed25519 paper reports a batch of 64 signatures verifying at under 134,000 cycles per signature, against 273,364 cycles for a single verification, roughly a twofold speedup [@bernstein-eddsa-2011].&lt;/p&gt;

Checking many signatures with one combined equation instead of one equation each, by exploiting the linearity of Schnorr-type verification. It is safe only when each signature&apos;s contribution is weighted by an independent random coefficient; the naive equal-weight sum is forgeable.
&lt;h3&gt;The non-negotiable safety condition&lt;/h3&gt;
&lt;p&gt;The coefficients must be &lt;strong&gt;randomized&lt;/strong&gt;. Here is why the naive all-ones sum fails. A signature verifies exactly when its verification residual is zero. If you add the residuals with equal weight, an attacker can submit two invalid signatures whose residuals are equal and opposite, so they cancel in the sum: the batch equation holds even though neither signature is valid.&lt;/p&gt;
&lt;p&gt;Weight each residual by an independent random coefficient and the errors no longer cancel except with negligible probability. BIP-340 derives its batch coefficients from a CSPRNG seeded over &lt;em&gt;all&lt;/em&gt; of the inputs, so an attacker cannot predict the weights and engineer a set of residuals that sums to zero without each signature being individually valid [@bip-340].&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Illustrative model. A signature is VALID iff its verification residual is 0. const n = 1000003n; const mod = (a, m) =&amp;gt; ((a % m) + m) % m; // Attacker forges two invalid signatures whose residuals are equal and opposite. const D = 777n;                 // the cancelling error the attacker chooses const e1 = mod(D, n);           // residual of forged signature 1 (nonzero: invalid) const e2 = mod(-D, n);          // residual of forged signature 2 (nonzero: invalid) console.log(&apos;signature 1 valid on its own? &apos; + (e1 === 0n));   // false console.log(&apos;signature 2 valid on its own? &apos; + (e2 === 0n));   // false // Naive all-ones batch: accept iff the plain sum of residuals is 0 const allOnes = mod(e1 + e2, n); console.log(&apos;all-ones batch residual = &apos; + allOnes + (allOnes === 0n ? &apos;  -&amp;gt; ACCEPTED, forgery slips through&apos; : &apos;  -&amp;gt; rejected&apos;)); // Randomized batch: weight each residual by a per-input random coefficient const a1 = 31337n, a2 = 424242n;   // seeded by a CSPRNG over ALL inputs const rand = mod(a1 * e1 + a2 * e2, n); console.log(&apos;randomized batch residual = &apos; + rand + (rand === 0n ? &apos;  -&amp;gt; accepted&apos; : &apos;  -&amp;gt; REJECTED, forgery caught&apos;));&lt;/code&gt;}&lt;/p&gt;

flowchart TD
    IN[&quot;u signatures to verify&quot;] --&amp;gt; SEED[&quot;Seed a CSPRNG over all u inputs&quot;]
    SEED --&amp;gt; COEF[&quot;Random coefficients a1 through au&quot;]
    COEF --&amp;gt; MSM[&quot;One multi-scalar multiplication&quot;]
    MSM --&amp;gt; OK{&quot;Batch equation holds?&quot;}
    OK --&amp;gt;|yes| ACC[&quot;Accept all&quot;]
    OK --&amp;gt;|no| FALL[&quot;Fall back to individual verification&quot;]
    IN --&amp;gt; BAD[&quot;Naive all-ones coefficients&quot;]
    BAD --&amp;gt; CANCEL[&quot;Attacker makes two residuals cancel&quot;]
    CANCEL --&amp;gt; FORGE[&quot;Invalid batch accepted, a forgery&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Naive all-ones batch verification is forgeable, because an attacker can craft invalid signatures whose verification errors cancel in the sum. Randomize the coefficients, seeded by a CSPRNG over every input, or do not batch at all.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What batching does not give you, and where it is not used&lt;/h3&gt;
&lt;p&gt;A failed batch tells you only that &lt;em&gt;some&lt;/em&gt; signature in it is bad, not which one, so you must fall back to individual verification to find the culprit -- a real cost when batches are large. Batching is also a Schnorr-family privilege: BIP-340 notes that standardized ECDSA &quot;cannot be verified more efficiently in batch,&quot; so the ECDSA deployments that dominate the web get no speedup here [@bip-340]. And adoption is narrower than the technique&apos;s fame suggests.&lt;/p&gt;
&lt;p&gt;Bitcoin Core does not batch-verify signatures in consensus validation as of 2024 through 2026: the determinism and simplicity of validating each signature independently outweigh the throughput gain. Batch verification is a real optimization for specific high-volume verifiers, not a universal default.&lt;/p&gt;
&lt;p&gt;That is the deployed toolkit: ECDSA, Ed25519, BIP-340 Schnorr, and RSA-PSS, each with its surfaces pulled in to a different degree. Which one should you reach for in 2026? The honest answer is not a single winner.&lt;/p&gt;
&lt;h2&gt;11. State of the Art and Competing Approaches (2024 to 2026)&lt;/h2&gt;
&lt;p&gt;There is no single best signature scheme to crown. The state of the art is four deployed constructions, each dominant in a niche, plus a set of signposts pointing at the frontier. This is coexistence with selective supersession, not a ladder where each rung replaces the last.&lt;/p&gt;
&lt;h3&gt;The four, per niche&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Deterministic or hedged ECDSA&lt;/strong&gt; leads by raw deployment. It is what FIPS, TLS, and the secp256k1 chains run, and modern libraries hedge it by default [@rfc-6979], [@go-crypto-ecdsa]. &lt;strong&gt;Ed25519&lt;/strong&gt; is the misuse-resistant greenfield default: deterministic nonce, fixed encoding, constant-time by construction [@rfc-8032], [@bernstein-eddsa-2011]. &lt;strong&gt;BIP-340 Schnorr&lt;/strong&gt; leads for consensus and threshold work, being byte-exact, SUF-CMA by design, and linear enough to aggregate [@bip-340]. &lt;strong&gt;RSA-PSS&lt;/strong&gt; leads verify-heavy and interoperability-bound settings with its tight proof and cheap verification [@rfc-8017], [@rfc-8446].&lt;/p&gt;
&lt;p&gt;Of the classical schemes, only finite-field DSA is withdrawn for new signatures by FIPS 186-5; deterministic ECDSA is &lt;em&gt;approved&lt;/em&gt;, which is not the same as &lt;em&gt;mandated&lt;/em&gt; [@fips-186-5].&lt;/p&gt;

FIPS 186-5 (2023) approves ECDSA, EdDSA, and deterministic ECDSA, and withdraws finite-field DSA for new signatures. Two practical consequences follow. First, &quot;approved&quot; is not &quot;mandated&quot;: you may hedge, and the default posture in compliant libraries is randomized or hedged signing rather than bare determinism. Second, EdDSA is now a first-class FIPS citizen, so choosing Ed25519 no longer forces you outside the compliance boundary the way it once did [@fips-186-5].
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;ECDSA (hedged)&lt;/th&gt;
&lt;th&gt;Ed25519&lt;/th&gt;
&lt;th&gt;BIP-340 Schnorr&lt;/th&gt;
&lt;th&gt;RSA-PSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hardness&lt;/td&gt;
&lt;td&gt;ECDLP (P-256, secp256k1)&lt;/td&gt;
&lt;td&gt;ECDLP (edwards25519)&lt;/td&gt;
&lt;td&gt;ECDLP (secp256k1)&lt;/td&gt;
&lt;td&gt;RSA, related to factoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature size&lt;/td&gt;
&lt;td&gt;64 to 72 bytes (DER)&lt;/td&gt;
&lt;td&gt;64 bytes&lt;/td&gt;
&lt;td&gt;64 bytes&lt;/td&gt;
&lt;td&gt;256 bytes at 2048, 384 at 3072&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public key size&lt;/td&gt;
&lt;td&gt;33 bytes compressed&lt;/td&gt;
&lt;td&gt;32 bytes&lt;/td&gt;
&lt;td&gt;32 bytes, x-only&lt;/td&gt;
&lt;td&gt;256 to 384 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce or randomness&lt;/td&gt;
&lt;td&gt;Hedged or RFC 6979&lt;/td&gt;
&lt;td&gt;Native deterministic&lt;/td&gt;
&lt;td&gt;Synthetic, hedged&lt;/td&gt;
&lt;td&gt;Fresh random salt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Malleable by default?&lt;/td&gt;
&lt;td&gt;Yes, enforce low-s&lt;/td&gt;
&lt;td&gt;No under strict S range&lt;/td&gt;
&lt;td&gt;No, byte-exact&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch verification&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proof quality&lt;/td&gt;
&lt;td&gt;Idealized only&lt;/td&gt;
&lt;td&gt;Loose, forking lemma&lt;/td&gt;
&lt;td&gt;Loose, forking lemma&lt;/td&gt;
&lt;td&gt;Tight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign versus verify&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;td&gt;Slow sign, fast verify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;FIPS, secp256k1 chains&lt;/td&gt;
&lt;td&gt;Greenfield default&lt;/td&gt;
&lt;td&gt;Consensus, threshold&lt;/td&gt;
&lt;td&gt;RSA interop, verify-heavy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Benchmarks, read correctly&lt;/h3&gt;
&lt;p&gt;The reference measurements everyone quotes for Ed25519 come from the eBATS and eBACS public benchmarking effort [@bernstein-eddsa-2011], [@ebacs-bench].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ed25519 operation&lt;/th&gt;
&lt;th&gt;Cost, eBATS reference measurement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Sign one message&lt;/td&gt;
&lt;td&gt;87,548 cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify one signature&lt;/td&gt;
&lt;td&gt;273,364 cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify in a batch of 64&lt;/td&gt;
&lt;td&gt;under 134,000 cycles per signature&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; Two facts most first courses skip. RSA verifies far faster than it signs, often by more than an order of magnitude, because the public exponent is tiny and the private exponent is full width, whereas elliptic-curve schemes are balanced between the two operations. And rankings flip with the implementation: a build with hand-tuned P-256 assembly can make ECDSA out-run Ed25519, the reverse of the eBATS ordering. Never quote a cross-scheme benchmark as if it were a property of the mathematics. Measure the library you ship, on the CPU you run.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Signposts at the frontier&lt;/h3&gt;
&lt;p&gt;Four constructions sit just outside the four-scheme core and deserve a pointer rather than a section. &lt;strong&gt;XEdDSA&lt;/strong&gt; lets you sign with an X25519 Diffie-Hellman key, a specialized key-reuse trick that is deployed at scale in the Signal protocol and the applications built on it, so it is peripheral by role but anything but academic [@xeddsa-spec].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MuSig2&lt;/strong&gt; and &lt;strong&gt;FROST&lt;/strong&gt; are two-round multi-signature and threshold-Schnorr protocols that produce an ordinary 64-byte signature indistinguishable from a single-signer one [@musig2-2020], [@rfc-9591]. &lt;strong&gt;BLS&lt;/strong&gt; offers pairing-based aggregation and anchors Ethereum consensus; a companion part in this series on hashing to curves covers it.&lt;/p&gt;
&lt;p&gt;And the horizon is &lt;strong&gt;post-quantum&lt;/strong&gt;: NIST finalized &lt;a href=&quot;https://paragmali.com/blog/the-thirty-year-migration-ships-in-a-pip-install-how-post-qu/&quot; rel=&quot;noopener&quot;&gt;ML-DSA&lt;/a&gt; in FIPS 204 and SLH-DSA in FIPS 205 in August 2024, both with signatures ranging from kilobytes to tens of kilobytes, an order of magnitude or more larger than anything in the table above [@fips-204], [@fips-205].&lt;/p&gt;
&lt;p&gt;XEdDSA is de-randomized like plain EdDSA, so absent hedging it inherits the same differential-fault exposure, which the hedged Fiat-Shamir analysis addresses [@aranha-hedged-fs-2019].&lt;/p&gt;
&lt;p&gt;Every row of those tables assumes the hardness holds. That assumption has a precise shape, and a known expiration date.&lt;/p&gt;
&lt;h2&gt;12. Theoretical Limits&lt;/h2&gt;
&lt;p&gt;Here is what a first course skips: none of these schemes has an unconditional proof, their proofs differ enormously in quality, and every one of them dies to the same quantum algorithm.&lt;/p&gt;
&lt;h3&gt;Conditional security, three assumptions&lt;/h3&gt;
&lt;p&gt;Every guarantee in this article is conditional. The elliptic-curve schemes rest on the hardness of the elliptic-curve discrete logarithm; RSA-PSS rests on the RSA problem, which is no harder than factoring; and all of the Fiat-Shamir schemes rest additionally on the &lt;strong&gt;random-oracle model&lt;/strong&gt;, a heuristic that proves security while treating the hash function as an ideal random function that no real hash exactly is. &quot;Provably secure&quot; here always means &quot;secure if these assumptions hold.&quot; That is not a weakness peculiar to signatures; it is the honest status of essentially all deployed public-key cryptography.&lt;/p&gt;
&lt;h3&gt;The tightness spectrum&lt;/h3&gt;
&lt;p&gt;The interesting frontier is not whether the proofs exist but how much they lose. A reduction converts an attacker on the scheme into an algorithm against the hard problem, and the &lt;em&gt;tightness&lt;/em&gt; of that conversion is how much security leaks in the translation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scheme&lt;/th&gt;
&lt;th&gt;Reduction (random-oracle model)&lt;/th&gt;
&lt;th&gt;Tightness&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RSA-PSS&lt;/td&gt;
&lt;td&gt;To the RSA problem, Bellare-Rogaway&lt;/td&gt;
&lt;td&gt;Tight&lt;/td&gt;
&lt;td&gt;The best guarantee of any deployed signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schnorr, EdDSA&lt;/td&gt;
&lt;td&gt;To discrete log via the forking lemma, Pointcheval-Stern&lt;/td&gt;
&lt;td&gt;Loose, quadratic loss&lt;/td&gt;
&lt;td&gt;Seurin showed the loss is essentially optimal, a barrier not a gap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(EC)DSA&lt;/td&gt;
&lt;td&gt;Only with an idealized conversion function, Fersch-Kiltz-Poettering&lt;/td&gt;
&lt;td&gt;Idealized&lt;/td&gt;
&lt;td&gt;Secure because ECDLP holds, not because the scheme is clean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;RSA-PSS has a tight reduction to RSA [@bellare-rogaway-pss-1996]. Schnorr and EdDSA have only a loose, quadratic-loss reduction through the forking lemma [@pointcheval-stern-1996-2000], and Seurin proved that this loss is essentially the best any algebraic reduction can do, so it is a genuine barrier rather than a proof waiting to be improved [@seurin-2012]. Later work made the multi-user Schnorr reduction explicit, with a loss on the order of the number of hash queries [@kiltz-masny-pan-2016]. And (EC)DSA has the weakest guarantee of all: the only known proofs must model its conversion function as an idealized, bijective random oracle [@fersch-kiltz-poettering-2016].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Deployment and proof quality are inversely correlated here. The most-deployed signature in the world, ECDSA, has the least satisfying proof: it is secure because the elliptic-curve discrete logarithm is hard, not because the scheme is clean. And tight proof or loose, every scheme in this article falls to Shor&apos;s algorithm. The destination is post-quantum, not a better curve.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The classical hardness bounds are near-optimal but unproven. Elliptic-curve discrete log costs about 2^128 operations for a 256-bit curve by Pollard&apos;s rho, which is optimal in the generic-group model, while factoring costs sub-exponential effort by the general number field sieve. Neither hardness is proven, and RSA inversion is not even proven equivalent to factoring.&lt;/p&gt;
&lt;h3&gt;The one bound that is not conjectural&lt;/h3&gt;
&lt;p&gt;There is exactly one limit in this section that is not an open question. Shor&apos;s algorithm solves both factoring and the discrete logarithm in quantum polynomial time, so a cryptographically relevant quantum computer breaks &lt;em&gt;every&lt;/em&gt; scheme in this article at once [@shor-1997]. No curve size and no modulus length helps, because Shor&apos;s cost is polynomial in the key size. The elliptic-curve schemes and RSA-PSS fall together.&lt;/p&gt;

For encryption, an adversary can record ciphertext today and decrypt it after building a quantum computer, so the harvest-now-decrypt-later threat is urgent. Signatures authenticate in real time, so a signature forged after a quantum computer exists cannot retroactively forge yesterday&apos;s authenticated session. The exception is long-lived keys: code-signing roots, firmware, and certificate roots that must stay valid for decades have to outlive Shor, which is exactly where post-quantum signatures are being adopted first [@shor-1997].
&lt;p&gt;If the theory is this settled -- conditional proofs of differing quality and a known quantum cliff -- then everything still up to you lives in the code. Here is that part, compressed into rules you can apply on Monday.&lt;/p&gt;
&lt;h2&gt;13. The Practical Guide: Decision Rules&lt;/h2&gt;
&lt;p&gt;Every rule below is one row of the failure catalog, inverted. If you remember nothing else, remember the decision tree.&lt;/p&gt;

flowchart TD
    START{&quot;What constrains you?&quot;} --&amp;gt; G[&quot;Greenfield, no constraint&quot;]
    START --&amp;gt; F[&quot;FIPS or compliance&quot;]
    START --&amp;gt; R[&quot;Must interoperate with RSA&quot;]
    START --&amp;gt; B[&quot;Bitcoin or byte-exact consensus&quot;]
    START --&amp;gt; T[&quot;Threshold or multisig&quot;]
    G --&amp;gt; GE[&quot;Ed25519 with a strict verifier&quot;]
    F --&amp;gt; FE[&quot;Hedged ECDSA P-256 or FIPS-approved Ed25519, enforce low-s&quot;]
    R --&amp;gt; RE[&quot;RSA-PSS as PS256, at least RSA-3072&quot;]
    B --&amp;gt; BE[&quot;BIP-340 Schnorr, or low-s strict-DER ECDSA if legacy&quot;]
    T --&amp;gt; TE[&quot;FROST or MuSig2, never deterministic nonces&quot;]
&lt;h3&gt;Use X, with these parameters, in this case&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Greenfield, no FIPS constraint.&lt;/strong&gt; Reach for &lt;strong&gt;Ed25519&lt;/strong&gt;, verified with a strict profile that enforces the canonical &lt;code&gt;S &amp;lt; L&lt;/code&gt; range and a ZIP-215-style byte-exact rule [@rfc-8032], [@zip-215]. You get deterministic nonces, a fixed encoding, and constant-time arithmetic for free.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FIPS-constrained.&lt;/strong&gt; Use &lt;strong&gt;deterministic or hedged ECDSA on P-256&lt;/strong&gt; following RFC 6979 and its hedged variant, or FIPS 186-5-approved &lt;strong&gt;Ed25519&lt;/strong&gt;. Enforce low-s, and insist on a constant-time library so you do not reopen the Minerva timing surface [@rfc-6979], [@fips-186-5].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RSA interoperability for a new design.&lt;/strong&gt; Use &lt;strong&gt;RSA-PSS&lt;/strong&gt; (the &lt;code&gt;PS256&lt;/code&gt; scheme), never PKCS#1 v1.5 for new work. Set the salt length equal to the hash length, use a matching MGF hash, and choose at least RSA-3072 for 128-bit security [@rfc-8017].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bitcoin or byte-exact consensus.&lt;/strong&gt; Use &lt;strong&gt;BIP-340 Schnorr&lt;/strong&gt;, as deployed in Taproot. If you are stuck with legacy ECDSA, enforce low-s and strict DER encoding so transactions are not malleable [@bip-340], [@bip-146].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Threshold or multisig.&lt;/strong&gt; Use &lt;strong&gt;FROST&lt;/strong&gt; or &lt;strong&gt;MuSig2&lt;/strong&gt; over BIP-340, and &lt;em&gt;never&lt;/em&gt; use deterministic nonces in multi-party signing -- a deterministic nonce in an interactive protocol can be replayed across differing co-signer inputs and leak the share [@rfc-9591], [@musig2-2020], [@bip-340].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Always.&lt;/strong&gt; Hedge the nonce whenever the platform can face fault injection. Domain-separate your keys per scheme: BIP-340 warns that reusing one key across RFC-6979 ECDSA and BIP-340 can leak it through cross-scheme nonce coincidences [@bip-340]. Verify strictly, and when any part of your protocol hashes over signatures, treat SUF-CMA, not merely EUF-CMA, as the bar.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Key parameters&lt;/th&gt;
&lt;th&gt;Because of (named break)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Greenfield, no FIPS&lt;/td&gt;
&lt;td&gt;Ed25519&lt;/td&gt;
&lt;td&gt;Strict &lt;code&gt;S &amp;lt; L&lt;/code&gt;, ZIP-215 verifier&lt;/td&gt;
&lt;td&gt;Psychic Signatures, Many EdDSAs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FIPS-constrained&lt;/td&gt;
&lt;td&gt;Hedged ECDSA P-256, or FIPS Ed25519&lt;/td&gt;
&lt;td&gt;RFC 6979 or hedged, low-s, constant-time&lt;/td&gt;
&lt;td&gt;PS3, PuTTY, Minerva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSA interop, new design&lt;/td&gt;
&lt;td&gt;RSA-PSS as PS256&lt;/td&gt;
&lt;td&gt;Salt length equals hash length, RSA-3072 or larger&lt;/td&gt;
&lt;td&gt;v1.5 low-exponent forgeries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitcoin or consensus&lt;/td&gt;
&lt;td&gt;BIP-340 Schnorr (Taproot)&lt;/td&gt;
&lt;td&gt;Or low-s and strict DER if legacy ECDSA&lt;/td&gt;
&lt;td&gt;Bitcoin txid malleability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threshold or multisig&lt;/td&gt;
&lt;td&gt;FROST or MuSig2&lt;/td&gt;
&lt;td&gt;Never deterministic nonces in multi-party&lt;/td&gt;
&lt;td&gt;Reused-nonce key recovery&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; Ed25519 for anything greenfield. Hedged ECDSA on P-256 for FIPS and secp256k1 chains. RSA-PSS for RSA interoperability. BIP-340 Schnorr for consensus and threshold. Whatever you pick, verify strictly and hedge the nonce wherever faults are possible.&lt;/p&gt;
&lt;/blockquote&gt;

Generate an Ed25519 keypair, then sign and verify a message, with OpenSSL 3.x:&lt;ul&gt;
&lt;li&gt;Create the private key: &lt;code&gt;openssl genpkey -algorithm ed25519 -out sk.pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Extract the public key: &lt;code&gt;openssl pkey -in sk.pem -pubout -out pk.pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Sign: &lt;code&gt;openssl pkeyutl -sign -inkey sk.pem -rawin -in message.txt -out sig.bin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify: &lt;code&gt;openssl pkeyutl -verify -pubin -inkey pk.pem -rawin -in message.txt -sigfile sig.bin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To force RSA-PSS rather than PKCS#1 v1.5 when signing with an RSA key: &lt;code&gt;openssl dgst -sha256 -sign rsa.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -out sig.bin message.txt&lt;/code&gt;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Turn each of those rules around and you get the antipatterns that keep showing up in real code review: the misuse catalog.&lt;/p&gt;
&lt;h2&gt;14. Common Misuse, and the One Sentence Underneath&lt;/h2&gt;
&lt;p&gt;Every break in this article was a violated rule from the last section. Here they are as the antipatterns a reviewer actually finds in real code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A caller-supplied nonce from a weak or predictable RNG.&lt;/strong&gt; This is the Android wallets and, in its biased form, PuTTY [@bitcoin-android-2013], [@cve-2024-31497].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A nonce reused across messages or keys.&lt;/strong&gt; This is the PS3, and it is certain from two signatures [@ps3-27c3-2010].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-constant-time scalar multiplication.&lt;/strong&gt; This is Minerva and TPM-Fail: even a perfect nonce leaks through timing [@minerva-2019], [@tpm-fail-2019].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping the range, subgroup, or canonical-s checks.&lt;/strong&gt; This reopens malleability, and for a permissive verifier it reopens forgery [@bip-340].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treating EUF-CMA as if it meant non-malleability.&lt;/strong&gt; This is Bitcoin transaction-id malleability: the scheme was unforgeable and the protocol still broke [@bip-340].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Naive all-ones batch coefficients.&lt;/strong&gt; A forgeable batch, defeated by errors that cancel in the sum [@bip-340].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A lax or permissive verifier.&lt;/strong&gt; This is Psychic Signatures, which accepted the all-zero signature for any message [@cve-2022-21449].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PKCS#1 v1.5 for new designs, or blurring the encryption-versus-signature split.&lt;/strong&gt; Use RSA-PSS, and never confuse the v1.5 decryption oracle with the unbroken v1.5 signature scheme [@rfc-8017].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signing without domain separation.&lt;/strong&gt; One key across two schemes can leak through cross-scheme nonce coincidences [@bip-340].&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; First, an ECDSA nonce drawn from a dubious or caller-supplied RNG instead of a derived or hedged one. Second, a missing low-s or strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; check, so signatures are malleable. Third, a verifier that does not range-check the signature values and will therefore accept degenerate inputs. Those three account for most of the named breaks in this article.&lt;/p&gt;
&lt;/blockquote&gt;


No, and this is the most repeated mistake in the area. A *repeated* nonce leaks the key with certainty from just two signatures -- that is the PS3 break. A *biased* nonce, where the top few bits are always zero, is a statistical attack: it takes many signatures and lattice reduction, framed as the Hidden Number Problem, to recover the key, as PuTTY&apos;s roughly-sixty-signature break showed. &quot;One few-bit-biased signature leaks your key&quot; is false.


No. Deriving the nonce deterministically removes the RNG-failure disaster, but it turns the signer into a fixed, replayable target for fault attacks: glitch one of two identical signings and diff the outputs. The resolution is hedged signing -- mix a little fresh randomness back in -- which most modern libraries do by default.


No, but it is nuanced. Standard Ed25519 can be malleable at the group level unless the verifier enforces the canonical `S &amp;lt; L` range check that RFC 8032 mandates on decode. Enforce it, as strict libraries do, and Ed25519 is strongly unforgeable; skip it and you inherit a malleability surface.


Encryption. The v1.5 *encryption* padding is the classic Bleichenbacher decryption oracle. The v1.5 *signature* scheme is not broken; its real failures were implementation bugs in lax verifiers, such as low-exponent forgeries, not a break of the signature math. Even so, use RSA-PSS for new designs.


Ed25519 for anything greenfield: deterministic nonces, a fixed 64-byte encoding, constant-time arithmetic, and strong unforgeability under strict verification. Use ECDSA only where a standard or platform compels it, such as FIPS environments or secp256k1 chains, and then with RFC 6979 or hedged nonces, low-s enforcement, and a constant-time library.


If any part of your protocol hashes over or deduplicates by the signature itself -- transaction ids, caches, replay checks -- you need SUF-CMA, because plain EUF-CMA lets an attacker produce a different valid signature on the same message. Enforce low-s or a strict `S &amp;lt; L` range, or choose a SUF-CMA-by-design scheme like BIP-340.


No. Shor&apos;s algorithm breaks both discrete log and factoring in polynomial time, so ECDSA, EdDSA, BIP-340, and RSA-PSS all fall to a cryptographically relevant quantum computer, and no curve or modulus size helps. The destination is post-quantum signatures such as ML-DSA and SLH-DSA, finalized in August 2024 and being adopted first for long-lived keys.

&lt;h3&gt;The one sentence underneath&lt;/h3&gt;
&lt;p&gt;Return to that slide at 27C3. Sony&apos;s curve was pristine; two signatures gave up the master key [@ps3-27c3-2010]. Now line up the rest of the body count. Across three decades, almost no deployed break ever solved an elliptic-curve discrete logarithm or factored a modulus. Attackers reused a nonce, biased one [@cve-2024-31497], leaked one through timing [@minerva-2019], flipped a signature&apos;s &lt;code&gt;s&lt;/code&gt; to &lt;code&gt;n - s&lt;/code&gt;, or accepted the all-zero signature for any message [@cve-2022-21449]. The hardness assumption sat untouched through all of it.&lt;/p&gt;
&lt;p&gt;That is why &quot;signing done right&quot; is not a single choice but a stack: the right scheme for the job, a nonce you do not have to trust the RNG for, a canonical form, and a byte-exact verifier -- and, the deepest lesson the evolution kept teaching, a preference for schemes where the dangerous case simply cannot be reached, which is what Ed25519 and BIP-340 buy you. Remove any one layer and a surface reopens.&lt;/p&gt;
&lt;p&gt;The horizon does not change the shape of the problem, only its arithmetic: post-quantum signatures like ML-DSA and SLH-DSA will hand the same three surfaces to the same callers, at ten to a hundred times the signature size [@fips-204]. The nonce, the form, and the verifier will still be where the security lives.&lt;/p&gt;

A signature is only as safe as the nonce, the form, and the verifier the scheme hands you. The math held; the interface leaked.
&lt;p&gt;Using a signature safely, in the end, is the discipline of never handing an attacker a value your scheme forgot to own.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;digital-signatures-ecdsa-eddsa-rsa-pss&quot; keyTerms={[
  { term: &quot;Per-signature nonce&quot;, definition: &quot;The fresh secret a discrete-log signature draws for each signature; reusing or biasing it leaks the private key.&quot; },
  { term: &quot;EUF-CMA&quot;, definition: &quot;Unforgeability against any new message, even after an adversary obtains signatures on messages of its choice.&quot; },
  { term: &quot;SUF-CMA&quot;, definition: &quot;A stronger goal that also forbids a new signature on an already-signed message; the property malleability violates.&quot; },
  { term: &quot;Signature malleability&quot;, definition: &quot;A second valid signature anyone can derive without the private key, such as the pair with n minus s for ECDSA.&quot; },
  { term: &quot;Hidden Number Problem&quot;, definition: &quot;The lattice problem behind biased-nonce key recovery, and the reason a bias is statistical rather than certain.&quot; },
  { term: &quot;Deterministic signing (RFC 6979)&quot;, definition: &quot;Deriving the nonce from the private key and the message so signing needs no runtime randomness.&quot; },
  { term: &quot;Hedged signing&quot;, definition: &quot;Mixing fresh randomness into a deterministic nonce so the signer survives both RNG failure and fault attacks.&quot; },
  { term: &quot;Low-s canonicalization&quot;, definition: &quot;Requiring the s value to lie in the lower half of its range so only one of the malleable twins is standard.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>digital-signatures</category><category>ecdsa</category><category>eddsa</category><category>rsa-pss</category><category>schnorr</category><category>nonce-reuse</category><category>applied-cryptography</category><category>cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>