<?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: rsa-pss</title><description>Posts tagged rsa-pss.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sat, 25 Jul 2026 08:57:56 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/rsa-pss/rss.xml" rel="self" type="application/rss+xml"/><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><item><title>RSA Is a Trapdoor, Not a Cryptosystem: OAEP, PSS, and the 25-Year Padding-Oracle Lineage</title><link>https://paragmali.com/blog/rsa-is-a-trapdoor-not-a-cryptosystem-oaep-pss-and-the-25-yea/</link><guid isPermaLink="true">https://paragmali.com/blog/rsa-is-a-trapdoor-not-a-cryptosystem-oaep-pss-and-the-25-yea/</guid><description>Textbook RSA is a trapdoor, not a cryptosystem. A field guide to OAEP, PSS, PKCS#1 v1.5, and the Bleichenbacher-ROBOT-Marvin padding-oracle lineage, done right.</description><pubDate>Sat, 11 Jul 2026 09:07:03 GMT</pubDate><content:encoded>
**RSA is a trapdoor permutation, not a cryptosystem** -- and almost every real-world RSA break came from treating the bare trapdoor as if it were already secure. Textbook RSA is deterministic and malleable. PKCS#1 v1.5 *encryption* turns &quot;is this padding valid?&quot; into a decryption oracle that leaked through ever-quieter channels for 25 years, from Bleichenbacher (1998) [@bleichenbacher98] through DROWN [@drown16] and ROBOT [@robot18] to Marvin&apos;s pure-timing attack (2023) [@marvin23]. &quot;Done right&quot; is a stack that must all hold at once: the right scheme (**OAEP** for encryption, **PSS** for new signatures), the right parameters (2048-bit floor, `e = 65537`, CSPRNG primes), and a **constant-time, fault-checked implementation** -- because security is a property of the scheme *and* its implementation, not of the math. And keep one split absolute: v1.5 *signatures* are unbroken and still dominant (verify strictly), while v1.5 *encryption* must be retired.
&lt;h2&gt;1. The Modulus Was Never Factored&lt;/h2&gt;
&lt;p&gt;In 2018, three researchers signed a message with the private key behind &lt;code&gt;facebook.com&lt;/code&gt;&apos;s TLS certificate [@robot18]. They never factored Facebook&apos;s 2,048-bit modulus. Nobody has ever publicly factored a 2,048-bit modulus -- the public record stands at 829 bits, and that took roughly 2,700 core-years [@rsa250].&lt;/p&gt;
&lt;p&gt;Instead, they asked one of Facebook&apos;s front-end servers the same yes-or-no question a few hundred thousand times -- &lt;em&gt;is this padding valid?&lt;/em&gt; -- and let the pattern of answers spell out the secret [@robot18]. The attack they used was already nineteen years old [@robotsite]. Five years later, a Red Hat engineer reproduced the same break against software that was supposed to be immune, using nothing but a stopwatch, and called it Marvin [@marvin23].&lt;/p&gt;
&lt;p&gt;Notice what did not happen. No prime was recovered. No number was factored. The RSA problem -- invert &lt;code&gt;c = m^e mod N&lt;/code&gt; without the private key -- stood exactly as hard the day after as the day before. What broke was the server&apos;s &lt;em&gt;reaction&lt;/em&gt;: the way it answered a question about a ciphertext it did not create.&lt;/p&gt;
&lt;p&gt;That is the whole subject in one sentence. Textbook RSA -- the bare &lt;code&gt;m^e mod N&lt;/code&gt; you meet in a first course -- is a &lt;strong&gt;trapdoor permutation, not a cryptosystem&lt;/strong&gt;. It is a beautiful one-way function with a secret shortcut, and nothing more.&lt;/p&gt;
&lt;p&gt;Everything that turns it into secure encryption or a secure signature lives &lt;em&gt;around&lt;/em&gt; the permutation, in the padding, the parameters, and the code that runs the operation. Almost every famous RSA disaster of the last three decades is a variation on one theme: someone used the bare trapdoor as if it were already a cryptosystem, or let the machinery meant to secure it confess -- through an error message, a network timeout, a microsecond of timing, or an injected fault -- whether a check had passed.&lt;/p&gt;

The math was never broken. The padding check told the attacker whether it passed.
&lt;p&gt;So here is the diagnostic question this article keeps asking, the one that unlocks the entire failure catalog: &lt;strong&gt;when a ciphertext or signature it did not create arrives, what does the receiver reveal about it -- through its answer, its timing, or its faults -- before and after it has checked the padding?&lt;/strong&gt; Four names you may know as headlines -- Bleichenbacher, DROWN, ROBOT, Marvin -- are four answers to that question, each leaking the same single bit through a quieter channel than the last. To see why one yes-or-no question about padding is enough to reconstruct a session key, you first have to see what RSA actually &lt;em&gt;is&lt;/em&gt;, and what it is not.&lt;/p&gt;
&lt;h2&gt;2. A Trapdoor for Diffie-Hellman&apos;s Challenge&lt;/h2&gt;
&lt;p&gt;In 1976, Whitfield Diffie and Martin Hellman wrote down the shape of a future that did not yet have an engine. Their paper &lt;em&gt;New Directions in Cryptography&lt;/em&gt; described public-key encryption and, remarkably, the idea of a digital signature -- a value only you can produce but anyone can check [@dh76]. What they could not supply was a concrete &lt;em&gt;trapdoor&lt;/em&gt;: a function easy to compute in one direction, hard to invert, yet effortless to invert if you hold a secret. Their paper is a challenge with a hole in it, and the hole is shaped exactly like RSA.&lt;/p&gt;
&lt;p&gt;A year later, Ron Rivest, Adi Shamir, and Leonard Adleman filled it. Pick two large primes $p$ and $q$ and set $N = pq$. Choose a public exponent $e$, and compute a private exponent $d$ with $ed \equiv 1 \pmod{\lambda(N)}$. Publish $(N, e)$; keep $d$, $p$, and $q$ secret. To encrypt a message represented as a number $m &amp;lt; N$, raise it to the public exponent; to decrypt, raise the result to the private one [@rsa78]:&lt;/p&gt;
&lt;p&gt;$$c = m^e \bmod N, \qquad m = c^d \bmod N.$$&lt;/p&gt;
&lt;p&gt;Why does the second operation undo the first? Because of Euler&apos;s theorem (1763): for any $m$ coprime to $N$, $m^{\phi(N)} \equiv 1 \pmod N$, with the totient $\phi(N) = (p-1)(q-1)$. Its Carmichael-function refinement (Carmichael, 1910) gives the same identity for the smaller $\lambda(N) = \mathrm{lcm}(p-1, q-1)$. Since $ed \equiv 1 \pmod{\lambda(N)}$, we have $ed = 1 + k\lambda(N)$ for some integer $k$, so $c^d = m^{ed} = m \cdot (m^{\lambda(N)})^k \equiv m \pmod N$. The exponents cancel, and the plaintext falls out -- but only for someone who could compute $d$, and computing $d$ requires $\lambda(N)$, which requires the factors of $N$. That is the trapdoor: the factorization of $N$ is the secret that inverts the permutation.The original 1978 paper used $\phi(N) = (p-1)(q-1)$, Euler&apos;s totient. Modern standards use the smaller Carmichael function $\lambda(N) = \mathrm{lcm}(p-1, q-1)$, which yields a smaller valid $d$ and the same correctness. Either works; $\lambda(N)$ is now the convention in FIPS 186-5.&lt;/p&gt;

A function that is easy to evaluate in the forward direction, computationally hard to invert without a secret, and easy to invert with it. RSA&apos;s forward map is x maps to x raised to the e, modulo N; the trapdoor that inverts it is the factorization of N. A trapdoor permutation is a primitive, not a complete encryption scheme -- it says nothing about hiding partial information or resisting tampering.
&lt;p&gt;The elegance that made RSA famous is that the &lt;em&gt;same&lt;/em&gt; operation both encrypts and signs. Swap the roles of the exponents: to sign a message, raise it to the private exponent, $s = m^d \bmod N$, an act only the key holder can perform; to verify, raise the signature to the public exponent and check that $s^e \equiv m \pmod N$, which anyone can do [@rsa78]. One modular exponentiation, read in two directions, is both a lock only you can open and a seal only you can stamp. Boneh&apos;s survey calls this duality the source of both RSA&apos;s reach and its long catalogue of misuse [@boneh99].&lt;/p&gt;
&lt;p&gt;Two engineering choices from this era matter later, because each returns as an attack surface. The first is the public exponent. Almost every deployed RSA key uses $e = 65537$.65537 is the Fermat prime $F_4 = 2^{16} + 1$. Written in binary it is &lt;code&gt;1&lt;/code&gt; followed by fifteen &lt;code&gt;0&lt;/code&gt;s and a final &lt;code&gt;1&lt;/code&gt; -- a Hamming weight of just 2 -- so public-key operations cost only sixteen squarings and one multiplication. Small enough to be fast, large enough to dodge the low-exponent traps of $e = 3$. The second is how the private operation is computed. Rather than one exponentiation modulo $N$, implementations work modulo $p$ and modulo $q$ separately and recombine, using the Chinese Remainder Theorem for roughly a fourfold speedup.&lt;/p&gt;

A technique for computing the private operation c raised to the d, modulo N, by working separately modulo p and modulo q on half-size numbers and then recombining the two halves. It cuts the cost of decryption and signing by about four times. Because it splits the secret operation across the two prime factors, it also opens a physical-fault attack surface that the single-modulus form does not have.
&lt;p&gt;Hold those two choices in mind: the small public exponent and the CRT shortcut will each come back to bite an implementation that treats them casually. But the deepest seed was planted in the 1978 paper itself, and it was invisible at the time.&lt;/p&gt;
&lt;p&gt;Rivest, Shamir, and Adleman demonstrated the trapdoor on &lt;em&gt;raw&lt;/em&gt; message numbers. That bare application is deterministic, algebraically malleable, and carries structure an attacker can exploit -- three properties that, in 1978, looked like harmless features of a clean mathematical object. One operation, elegant enough to both lock and sign. So why is calling that operation directly -- what cryptographers now dismiss as &quot;textbook RSA&quot; -- treated as a bug in every serious codebase?&lt;/p&gt;
&lt;h2&gt;3. Why Textbook RSA Is Not a Cryptosystem&lt;/h2&gt;
&lt;p&gt;Encryption has a minimum bar -- lower than most people think -- and textbook RSA fails to clear it. The bar is called &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;IND-CPA&lt;/a&gt;: an adversary who picks two plaintexts and receives the encryption of one cannot tell which, better than a coin flip. (Part 1 of this series builds that definition carefully; here we only need its consequences.) Bare RSA loses this game three separate ways, each a direct consequence of the naked permutation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It is deterministic.&lt;/strong&gt; The same plaintext always encrypts to the same ciphertext, because $m^e \bmod N$ is a function with no randomness in it. That is fatal whenever the message space is small or guessable. Suppose a server encrypts a one-word trading instruction, either &lt;code&gt;BUY&lt;/code&gt; or &lt;code&gt;SELL&lt;/code&gt;, under a known public key. An eavesdropper does not need to decrypt anything: they encrypt both candidate words, compare against the captured ciphertext, and read the plaintext with zero queries. Determinism can never be semantic security -- this is structural, not a missing optimization you can bolt on later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It is malleable.&lt;/strong&gt; RSA is multiplicatively homomorphic: multiply a ciphertext by $k^e$ and the plaintext is silently multiplied by $k$.&lt;/p&gt;

A cipher is malleable when an attacker can transform a ciphertext into another valid ciphertext whose plaintext is a predictable function of the original -- without decrypting anything. RSA satisfies the multiplicative relation: the encryption of m times the encryption-form of k decrypts to m times k, modulo N. Useful for some protocols, catastrophic for confidentiality, and the exact algebraic lever Bleichenbacher later pulls.
&lt;p&gt;That relation is not a curiosity; it is the crowbar behind the entire padding-oracle lineage. The demonstration below uses toy primes so the arithmetic is legible in a browser, but the algebra is identical at 2,048 bits.&lt;/p&gt;
&lt;p&gt;{`
// Toy RSA: p=61, q=53, N=3233, e=17, d=2753. NOT secure sizes -- for illustration.
function modpow(base, exp, mod) {
  base = base % mod; let r = 1n;
  while (exp &amp;gt; 0n) {
    if (exp &amp;amp; 1n) r = (r * base) % mod;
    base = (base * base) % mod; exp &amp;gt;&amp;gt;= 1n;
  }
  return r;
}
const N = 3233n, e = 17n, d = 2753n;
const m = 42n;&lt;/p&gt;
&lt;p&gt;// 1) Deterministic: encrypting the same message twice gives the same ciphertext.
const c1 = modpow(m, e, N), c2 = modpow(m, e, N);
console.log(&apos;encrypt 42 twice -&amp;gt;&apos;, c1.toString(), c2.toString(), &apos;| identical?&apos;, c1 === c2);&lt;/p&gt;
&lt;p&gt;// 2) Malleable: multiply the ciphertext by k^e and the plaintext scales by k.
const k = 2n;
const cForged = (c1 * modpow(k, e, N)) % N;      // attacker never decrypts
const mForged = modpow(cForged, d, N);           // what the victim would recover
console.log(&apos;tampered ct decrypts to&apos;, mForged.toString(), &apos;= (42*2) mod N =&apos;, ((m * k) % N).toString());
`}&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It leaks structure for small exponents and short messages.&lt;/strong&gt; If a message is short enough that $m^e &amp;lt; N$, no modular reduction ever happens, so recovering $m$ is a plain integer $e$-th root -- no factoring required.&lt;/p&gt;
&lt;p&gt;Johan Hastad showed in 1988 that if the same message is broadcast to $e$ recipients under $e = 3$, the plaintext falls out by the Chinese Remainder Theorem [@hastad88]. Don Coppersmith sharpened this in 1996 into a lattice method that recovers a message whenever the unknown part is smaller than $N^{1/e}$, breaking &quot;stereotyped&quot; messages with a small hidden field [@coppersmith97]. Every one of these is an attack on &lt;em&gt;textbook&lt;/em&gt; RSA, not on the RSA problem itself -- the factoring assumption stays intact while the plaintext walks out the front door [@boneh99].&lt;/p&gt;
&lt;p&gt;So a trapdoor permutation is not a cryptosystem. To become one, it needs a transformation applied to the message &lt;em&gt;before&lt;/em&gt; the permutation -- padding -- and that padding has to do three separable jobs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A trapdoor becomes a cryptosystem only when the padding does three separable jobs: (1) add &lt;strong&gt;randomness&lt;/strong&gt;, so equal plaintexts produce different ciphertexts; (2) add &lt;strong&gt;checkable redundancy&lt;/strong&gt;, so the receiver can detect a tampered or malformed ciphertext; and (3) -- the job the field kept forgetting -- be &lt;strong&gt;implemented so that no observable behaviour reveals whether that check passed.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first two jobs are about the &lt;em&gt;scheme&lt;/em&gt;. The third is about the &lt;em&gt;implementation&lt;/em&gt;, and the entire history of RSA breaks is the field learning, over and over, that the third job is not optional. Hold that third clause; it is the trap the next section springs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If your code invokes a modular-exponentiation &quot;textbook RSA&quot; function directly on a message -- no OAEP, no PSS, no padding layer -- it is broken before it ships. Every serious library hides the bare permutation precisely so that no application accidentally uses it as encryption or a signature.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first widely deployed answer to those three jobs was PKCS#1 version 1.5, specified by RSA Laboratories in the early 1990s and republished as RFC 2313 in 1998 [@rfc2313]. For encryption, it wraps the message as &lt;code&gt;0x00 || 0x02 || PS || 0x00 || M&lt;/code&gt;, where &lt;code&gt;PS&lt;/code&gt; is at least eight nonzero random bytes -- that random padding supplies job one [@rfc8017]. For signatures it uses a different, fixed frame, &lt;code&gt;0x00 || 0x01 || 0xFF...0xFF || 0x00 || DigestInfo&lt;/code&gt;, whose rigid structure supplies job two [@rfc8017].&lt;/p&gt;
&lt;p&gt;On paper it fixed textbook RSA&apos;s two headline problems: the random bytes killed determinism, and the fixed structure gave the receiver something to check. For five years, it looked as if PKCS#1 v1.5 had turned the trapdoor into a cryptosystem. The structure was supposed to &lt;em&gt;add&lt;/em&gt; security. In 1998, Daniel Bleichenbacher showed that the structure was the vulnerability.&lt;/p&gt;
&lt;h2&gt;4. The Atom and Its Echoes: The Encryption Padding-Oracle Lineage&lt;/h2&gt;
&lt;p&gt;Before the detailed walk, here is the whole story on one timeline -- two intertwined tracks, the schemes on one side and the breaks on the other, with each break forcing the next response.&lt;/p&gt;

timeline
    title RSA schemes and their breaks, 1976 to 2026
    1977 : RSA trapdoor answers the Diffie-Hellman challenge
    1990 : Wiener breaks small private exponents
    1993 : PKCS#1 v1.5 padding deployed
    1997 : Bellcore CRT fault factors N from one signature
    1998 : Bleichenbacher padding oracle : OAEP standardized in response
    2001 : Manger reopens the oracle inside OAEP decoders
    2003 : Remote timing attacks proven practical : PSS standardized
    2016 : DROWN revives the oracle through SSLv2
    2017 : ROCA factors structured Infineon keys
    2018 : ROBOT signs with the facebook.com key : TLS 1.3 deletes RSA key exchange
    2023 : Marvin recovers keys by timing alone
    2024 : NIST IR 8547 sets the quantum retirement clock
    2025 : CFRG draft moves to deprecate v1.5 encryption
&lt;p&gt;Bleichenbacher&apos;s insight was not about RSA the mathematics at all. It was about a server&apos;s manners. A TLS server in the 1990s received an RSA-encrypted pre-master secret, decrypted it, and checked whether the result had valid PKCS#1 v1.5 padding -- did it start with the bytes &lt;code&gt;0x00 0x02&lt;/code&gt;, with a nonzero pad and a delimiter in the right place? If not, it returned an error. That error, however polite, answered a question the attacker was not supposed to be able to ask: &lt;em&gt;was this the encryption of a well-formed message?&lt;/em&gt; And because RSA is malleable, the attacker could turn that one bit of feedback into a full decryption.&lt;/p&gt;
&lt;p&gt;Here is the mechanism. The attacker holds a target ciphertext $c$ that encrypts an unknown $m$ -- say, a captured TLS session key. They pick a value $s$, compute $c \cdot s^e \bmod N$, and send it to the server. By the homomorphic property, the server is really decrypting $m \cdot s \bmod N$. If the server reports valid padding, the attacker learns that $m \cdot s \bmod N$ begins with &lt;code&gt;0x00 0x02&lt;/code&gt; -- which means it lies in the interval $[2B, 3B)$, where $B = 2^{8(k-2)}$ for a $k$-byte modulus.&lt;/p&gt;
&lt;p&gt;Each conforming $s$ is a linear inequality on the secret $m$. Collect enough of them, adaptively choosing each new $s$ to bisect the surviving range, and the interval of possible $m$ collapses to a single value. Bleichenbacher&apos;s 1998 paper showed this takes on the order of $2^{20}$ -- about a million -- queries for a 1,024-bit key, and the private key is never touched [@bleichenbacher98].&lt;/p&gt;

sequenceDiagram
    participant A as Attacker
    participant S as Decrypting server
    Note over A: Holds target ciphertext c, wants secret m
    A-&amp;gt;&amp;gt;S: Submit c times s to the e, for chosen s
    S-&amp;gt;&amp;gt;S: Decrypt and check PKCS#1 v1.5 padding
    alt Decrypted value starts with 00 02
        S--&amp;gt;&amp;gt;A: Conforming, no error or fast reply
        Note over A,S: Attacker learns m times s mod N is in 2B to 3B
    else Padding invalid
        S--&amp;gt;&amp;gt;A: Non-conforming, error, alert, or slow reply
    end
    Note over A: Narrow the possible range of m, choose next s
    A-&amp;gt;&amp;gt;S: Repeat, roughly a million queries in total
    Note over A: Range collapses to one value, plaintext m recovered
&lt;p&gt;A generation of engineers filed this under &quot;performance&quot; or &quot;obscure edge case.&quot; It is neither. It is a correctness and security failure of the &lt;em&gt;construction&lt;/em&gt;: the receiver&apos;s validity check, exposed through any observable side effect, is a decryption oracle for chosen ciphertexts. That is the &lt;a href=&quot;https://paragmali.com/blog/they-read-your-plaintext-without-breaking-your-cipher-a-fiel/&quot; rel=&quot;noopener&quot;&gt;symmetric-side padding-oracle attack&lt;/a&gt; of Part 6, transplanted to public-key land -- the same disease, a different organ.&lt;/p&gt;

Any observable behaviour -- an error message, a network reset, an injected fault, or a difference in response time -- that reveals whether a decrypted ciphertext had valid padding. Because the attacker chooses the ciphertexts, a padding-validity signal becomes a decryption oracle: enough yes-or-no answers reconstruct the plaintext without ever recovering the private key.
&lt;p&gt;The break violates the strongest standard security goal for encryption, IND-CCA2, in which the adversary may submit chosen ciphertexts to a decryption oracle and still must not learn anything about a challenge plaintext.&lt;/p&gt;

The security goal a padding oracle destroys: an adversary who can submit arbitrary ciphertexts for decryption still cannot distinguish which of two chosen plaintexts a challenge ciphertext encrypts. PKCS#1 v1.5 encryption fails this because its validity check leaks. Part 1 of this series develops the full definition.
&lt;p&gt;The scheme-level fix arrived almost immediately. In direct response to Bleichenbacher, RSA Laboratories standardized a new encryption padding, RSAES-OAEP, in PKCS#1 v2.0 (RFC 2437) later in 1998 [@rfc2437]. OAEP&apos;s design goal, spelled out in Section 6, is that any tampered ciphertext decodes to unstructured noise, so there is no &quot;conformant&quot; interval left to test -- the oracle has nothing to grade. It fixed the scheme. What it did not, and could not, fix by itself was the decoder.&lt;/p&gt;
&lt;p&gt;James Manger proved that in 2001. OAEP&apos;s decoding has two distinct early failure modes: the recovered integer can be too large to fit the byte block, or it can fit but fail the padding check. A decoder that distinguishes those two cases through different errors or different timing hands the attacker a &lt;em&gt;new&lt;/em&gt; oracle -- and Manger&apos;s variant is dramatically cheaper than Bleichenbacher&apos;s, needing on the order of $\log_2 N$ queries, roughly 2,048 for a 2,048-bit key, instead of $2^{20}$ [@manger01]. OAEP, the provably secure scheme, re-opened the identical class of attack through an imperfect implementation. The lesson the field wrote down, and then kept having to re-learn, was not &quot;use OAEP.&quot; It was &quot;use OAEP &lt;em&gt;and&lt;/em&gt; decode it in constant time.&quot;&lt;/p&gt;
&lt;p&gt;If Bleichenbacher&apos;s attack is the atom, the next quarter-century is three quieter echoes of it -- the same leaked bit, reached through channels that get progressively harder to see.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DROWN (2016): a cross-protocol channel.&lt;/strong&gt; By 2016, TLS servers had long since patched the obvious padding-error message. But many still supported SSLv2, an obsolete protocol from the 1990s, often on a different service such as a mail server -- and often with the &lt;em&gt;same&lt;/em&gt; RSA key. DROWN showed that an attacker could use the weak, deliberately export-crippled SSLv2 endpoint as the padding oracle, then use its answers to decrypt a modern TLS session that shared the key.The DROWN team measured that roughly 33 percent of all HTTPS servers were vulnerable at disclosure in March 2016, because key reuse across a TLS service and a forgotten SSLv2 service was rampant [@drownsite]. The math of the oracle was 1998&apos;s; only the delivery was new [@drown16].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ROBOT (2018): the same oracle, nineteen years on.&lt;/strong&gt; Hanno Bock, Juraj Somorovsky, and Craig Young revisited the original attack and found it alive across the modern internet. The padding-error message was gone, but the oracle now leaked through subtler tells: a TCP reset here, a connection timeout there, a duplicated alert message somewhere else -- any behaviour that differed between conforming and non-conforming padding. It affected almost a third of the top 100 domains, including Facebook and PayPal, and traced to products from F5, Citrix, Radware, Palo Alto Networks, IBM, and Cisco [@robot18].To make the point unmissable, the ROBOT authors used the recovered oracle to sign a message with the private key behind facebook.com&apos;s certificate -- a decryption oracle repurposed to forge a signature, without ever touching the factorization [@robotsite].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marvin (2023): pure timing, no error at all.&lt;/strong&gt; The quietest channel yet removes the error entirely. Hubert Kario&apos;s Marvin attack observes only the &lt;em&gt;time&lt;/em&gt; the decryption operation takes, since a conforming and a non-conforming padding often run through subtly different code paths. No alert, no reset, no message -- just a stopwatch. Marvin re-found exploitable leaks in implementations that had been declared immune after ROBOT, and it reaches well beyond TLS into S/MIME, JSON Web Tokens, and hardware tokens such as HSMs and smartcards [@marvin23]. The peer-reviewed write-up appeared at ESORICS 2023 under the fitting title &lt;em&gt;Everlasting ROBOT&lt;/em&gt; [@eprint2023]. Its recommendation was blunt: stop using PKCS#1 v1.5 encryption.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every member of this lineage leaks the same single bit -- was the padding valid? -- through a different channel. You cannot patch your way to safety one channel at a time, because the next channel is always quieter than the last. The only durable fixes are structural: remove the mode (as TLS 1.3 did) or forbid it (as FIPS did). If a standard forces RSA encryption, use OAEP with constant-time decoding, never v1.5.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Read the four together and the pattern is impossible to miss. The channel gets quieter each time -- an error string in 1998, a cross-protocol zombie in 2016, a TCP quirk in 2018, a bare microsecond in 2023 -- but the leaked bit never changes. This is why &quot;add more padding&quot; was never the answer and constant-time, uniform-failure decoding was. The catalog below is the evidence for this article&apos;s whole argument: every row is some party using the bare trapdoor as if it were a scheme, or letting a check confess.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Front&lt;/th&gt;
&lt;th&gt;Leaked channel&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;Textbook RSA [@rsa78]&lt;/td&gt;
&lt;td&gt;1978&lt;/td&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;None needed&lt;/td&gt;
&lt;td&gt;Deterministic, malleable bare permutation&lt;/td&gt;
&lt;td&gt;Never encrypt with the raw primitive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hastad / Coppersmith [@hastad88, @coppersmith97]&lt;/td&gt;
&lt;td&gt;1988 / 96&lt;/td&gt;
&lt;td&gt;Params&lt;/td&gt;
&lt;td&gt;Algebraic structure&lt;/td&gt;
&lt;td&gt;Small e, short or related messages&lt;/td&gt;
&lt;td&gt;Pad first, use e equals 65537&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bleichenbacher [@bleichenbacher98]&lt;/td&gt;
&lt;td&gt;1998&lt;/td&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;Padding-error message&lt;/td&gt;
&lt;td&gt;v1.5 validity check is a decryption oracle&lt;/td&gt;
&lt;td&gt;Uniform, unobservable failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manger [@manger01]&lt;/td&gt;
&lt;td&gt;2001&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Error-type or timing split&lt;/td&gt;
&lt;td&gt;Non-constant-time OAEP decode&lt;/td&gt;
&lt;td&gt;OAEP AND constant-time decoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DROWN [@drown16]&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;SSLv2 cross-protocol&lt;/td&gt;
&lt;td&gt;Same RSA key on a weak zombie protocol&lt;/td&gt;
&lt;td&gt;Never reuse keys, kill SSLv2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROBOT [@robot18]&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;TCP reset, timeout, alert&lt;/td&gt;
&lt;td&gt;v1.5 oracle still live behind subtle tells&lt;/td&gt;
&lt;td&gt;Remove v1.5 key exchange&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marvin [@marvin23]&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Decryption timing only&lt;/td&gt;
&lt;td&gt;Non-constant-time v1.5 depadding&lt;/td&gt;
&lt;td&gt;Retire v1.5 encryption entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e equals 3 forgery [@cve2006]&lt;/td&gt;
&lt;td&gt;2006&lt;/td&gt;
&lt;td&gt;Signature&lt;/td&gt;
&lt;td&gt;None, forged offline&lt;/td&gt;
&lt;td&gt;Lax verifier ignores trailing bytes&lt;/td&gt;
&lt;td&gt;Verify strictly, parse it all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BERserk [@cve2014]&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;Signature&lt;/td&gt;
&lt;td&gt;None, forged offline&lt;/td&gt;
&lt;td&gt;NSS mis-parses ASN.1 lengths&lt;/td&gt;
&lt;td&gt;Verify strictly, re-encode and compare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bellcore fault [@bdl97]&lt;/td&gt;
&lt;td&gt;1997&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;One faulty signature&lt;/td&gt;
&lt;td&gt;CRT fault reveals a prime by gcd&lt;/td&gt;
&lt;td&gt;Verify signature before release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kocher, Brumley-Boneh [@kocher96, @brumleyboneh03]&lt;/td&gt;
&lt;td&gt;1996 / 2003&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Exponentiation timing&lt;/td&gt;
&lt;td&gt;Secret-dependent modexp time&lt;/td&gt;
&lt;td&gt;Base blinding, constant time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wiener [@wiener90]&lt;/td&gt;
&lt;td&gt;1990&lt;/td&gt;
&lt;td&gt;Params&lt;/td&gt;
&lt;td&gt;Public key structure&lt;/td&gt;
&lt;td&gt;Private exponent d too small&lt;/td&gt;
&lt;td&gt;Never shrink d for speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mining Ps and Qs [@miningpq12]&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Params&lt;/td&gt;
&lt;td&gt;Shared prime factors&lt;/td&gt;
&lt;td&gt;Low boot-time entropy at keygen&lt;/td&gt;
&lt;td&gt;Seed the CSPRNG properly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROCA [@roca17]&lt;/td&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;Params&lt;/td&gt;
&lt;td&gt;Public modulus structure&lt;/td&gt;
&lt;td&gt;Infineon&apos;s structured primes&lt;/td&gt;
&lt;td&gt;Generate primes uniformly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Fourteen rows, one disease. But the encryption oracle is only one of four ways the bare trapdoor betrays its owner. The other three -- the lazy verifier, the glitched chip, and the badly chosen numbers -- are just as instructive, and one of them factors your modulus from a single fault.&lt;/p&gt;
&lt;h2&gt;5. The Rest of the Catalog: Signatures, Faults, and Bad Primes&lt;/h2&gt;
&lt;p&gt;If the encryption oracle is the trapdoor leaking through the decryptor&apos;s &lt;em&gt;answer&lt;/em&gt;, the remaining breaks are the trapdoor leaking through the verifier&apos;s &lt;em&gt;laziness&lt;/em&gt;, the hardware&apos;s &lt;em&gt;faults&lt;/em&gt;, and the &lt;em&gt;numbers&lt;/em&gt; you fed it. Three fronts, and the encryption-versus-signature split stays absolute across all of them.&lt;/p&gt;
&lt;h3&gt;Front A: the signature track, and the precision that governs it&lt;/h3&gt;
&lt;p&gt;Start with a fact that surprises people who lump all of v1.5 together: RFC 8017 records &lt;strong&gt;no known attack against the RSASSA-PKCS1-v1_5 &lt;em&gt;signature scheme itself&lt;/em&gt;&lt;/strong&gt; [@rfc8017]. Unlike v1.5 encryption, the signature padding has never been broken as a construction. It rests on a heuristic argument rather than a tight proof, which is why cryptographers reach for PSS in new designs, but it is legacy-not-broken. Where it &lt;em&gt;is&lt;/em&gt; fragile is at the verifier -- and that fragility has bitten twice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The $e = 3$ forgery (2006, CVE-2006-4339).&lt;/strong&gt; Daniel Bleichenbacher -- the same name, a different result eight years later -- described a forgery that needs no private key at all. A v1.5 signature block ends with the ASN.1 &lt;code&gt;DigestInfo&lt;/code&gt; structure, and a lazy verifier checks that the high-order bytes look right without confirming that the &lt;code&gt;DigestInfo&lt;/code&gt; sits flush against the end with no trailing data.&lt;/p&gt;
&lt;p&gt;With a small public exponent such as $e = 3$, an attacker can construct a number whose cube reproduces the correct prefix and hash in the leading bytes, then pack arbitrary garbage into the trailing bytes the verifier never inspects. Because cubing is cheap and the low bytes are free, the forged &quot;signature&quot; is just a carefully chosen cube root [@cve2006]. OpenSSL&apos;s advisory of 5 September 2006 traced it to exactly that omission: &quot;not checking for excess data&quot; after the exponentiation [@openssl2006].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BERserk (2014, CVE-2014-1568).&lt;/strong&gt; Eight years on, the same antipattern returned in a different guise. Mozilla&apos;s NSS library mis-parsed ASN.1 length fields during signature verification, letting an attacker smuggle forged content past the check and produce signatures that NSS -- and therefore Firefox and Chrome builds of the era -- accepted as valid, including for TLS certificates [@cve2014]. Once again, no scheme was broken; a verifier was.&lt;/p&gt;
&lt;p&gt;The recurring lesson is one line: &lt;strong&gt;verify strictly.&lt;/strong&gt; Parse the entire structure, reject any trailing data, re-encode the expected block and compare it byte-for-byte, and never take an $e = 3$ shortcut. The provable alternative, PSS, removes the temptation to hand-roll a lenient check by making the encoding randomized and its verification total; its mechanism is in the next section [@pss96]. These verifier bugs live where v1.5 signatures live -- inside 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;X.509 and PKI machinery&lt;/a&gt; that Part 4 of this series covers.&lt;/p&gt;

&quot;Still-deployed PKCS#1 v1.5&quot; hides two opposite truths, and conflating them is the single most common error in RSA writing. v1.5 *encryption* (RSAES-PKCS1-v1_5) is fundamentally dangerous padding: its validity check is a decryption oracle, and it should be retired. v1.5 *signatures* (RSASSA-PKCS1-v1_5) are dominant across Web PKI, FIPS-approved in FIPS 186-5, and unbroken as a scheme -- keep them where mandated and verify strictly [@rfc8017], [@fips1865]. One phrase must never carry both meanings. When you read &quot;RSA v1.5 is broken,&quot; ask which one, because the honest answer is &quot;the encryption, not the signatures.&quot;
&lt;h3&gt;Front B: the physical layer&lt;/h3&gt;
&lt;p&gt;Here the secret &lt;em&gt;is&lt;/em&gt; the factorization, so anything the hardware leaks about the private operation leaks the factors directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The CRT fault (Boneh, DeMillo, and Lipton, 1997).&lt;/strong&gt; Recall that fast RSA signing computes the result modulo $p$ and modulo $q$ separately, then recombines. Suppose a single bit flips during the modulo-$p$ half -- from a voltage glitch, a clock fault, cosmic radiation, or a deliberate injection. The faulty signature $s&apos;$ is now correct modulo $q$ but wrong modulo $p$. Then $s&apos;^e - m$ is divisible by $q$ but not by $p$, so a single $\gcd(s&apos;^e - m, N)$ hands you $q$ -- and the modulus is factored from &lt;em&gt;one&lt;/em&gt; faulty signature [@bdl97]. The demonstration below does exactly that with toy primes.&lt;/p&gt;
&lt;p&gt;{`
// Bellcore fault: a fault in one CRT half leaks a prime of N. Toy primes for clarity.
function modpow(base, exp, mod) {
  base = ((base % mod) + mod) % mod; let r = 1n;
  while (exp &amp;gt; 0n) { if (exp &amp;amp; 1n) r = (r * base) % mod; base = (base * base) % mod; exp &amp;gt;&amp;gt;= 1n; }
  return r;
}
function gcd(a, b) { while (b) { const t = a % b; a = b; b = t; } return a; }
const p = 61n, q = 53n, N = p * q, e = 17n, d = 2753n;
const m = 123n % N;                       // message representative to sign
const dp = d % (p - 1n), dq = d % (q - 1n);
const qInv = modpow(q, p - 2n, p);&lt;/p&gt;
&lt;p&gt;// Correct CRT signature (Garner recombination):
const sp = modpow(m, dp, p), sq = modpow(m, dq, q);
const s = (sq + q * (((qInv * (sp - sq)) % p + p) % p)) % N;
console.log(&apos;correct signature verifies?&apos;, modpow(s, e, N) === m);&lt;/p&gt;
&lt;p&gt;// A fault corrupts ONLY the mod-p half:
const spBad = (sp + 1n) % p;
const sBad = (sq + q * (((qInv * (spBad - sq)) % p + p) % p)) % N;
console.log(&apos;faulty  signature verifies?&apos;, modpow(sBad, e, N) === m);&lt;/p&gt;
&lt;p&gt;// One gcd factors the modulus:
const diff = ((modpow(sBad, e, N) - m) % N + N) % N;
console.log(&apos;gcd(sBad^e - m, N) =&apos;, gcd(diff, N).toString(), &apos;-&amp;gt; a secret prime of N (61 or 53)&apos;);
`}&lt;/p&gt;
&lt;p&gt;The fix is a single line of discipline: &lt;strong&gt;verify before release.&lt;/strong&gt; Recompute $s^e \bmod N$ and confirm it equals $m$ before the signature ever leaves the chip. A faulty signature never escapes, and the gcd trick has nothing to work with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Timing (Kocher, 1996; Brumley and Boneh, 2003).&lt;/strong&gt; Paul Kocher observed in 1996 that the time to compute $c^d \bmod N$ depends on the secret bits of $d$, because square-and-multiply does extra work on &lt;code&gt;1&lt;/code&gt; bits [@kocher96]. Folklore held that timing attacks needed local access. David Brumley and Dan Boneh demolished that in 2003 by recovering an OpenSSL server&apos;s private key &lt;em&gt;over a network&lt;/em&gt;, measuring only response times [@brumleyboneh03].&lt;/p&gt;
&lt;p&gt;The fix is &lt;strong&gt;base blinding&lt;/strong&gt;: multiply the input by $r^e$ for a fresh random $r$ before exponentiating, then divide the result by $r$ afterward, so the timing depends on a value the attacker cannot predict. OpenSSL turned blinding on by default in 2003. Marvin, from the previous section, is this front&apos;s 2023 revival -- the timing channel never truly closed.&lt;/p&gt;
&lt;h3&gt;Front C: the parameters&lt;/h3&gt;
&lt;p&gt;The trapdoor is only as strong as the numbers behind it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wiener (1990).&lt;/strong&gt; If you shrink the private exponent for speed so that $d &amp;lt; \tfrac{1}{3} N^{1/4}$, a continued-fraction expansion of $e/N$ recovers $d$ outright [@wiener90]. You cannot buy decryption speed by making the secret small.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coppersmith (1996 to 1997).&lt;/strong&gt; His lattice method sets the barrier $|x| &amp;lt; N^{1/e}$ that both &lt;em&gt;enables&lt;/em&gt; low-exponent attacks on short or stereotyped messages and &lt;em&gt;bounds&lt;/em&gt; them -- the same result that returns, weaponized, as ROCA [@coppersmith97].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mining Your Ps and Qs (2012).&lt;/strong&gt; Heninger and colleagues scanned the internet&apos;s public keys and computed pairwise gcds. Devices that generated keys at first boot, before they had gathered entropy, sometimes shared a prime -- and a shared prime means a free gcd factors both moduli. They computed private keys for about 0.50 percent of TLS hosts and 0.03 percent of SSH hosts this way [@miningpq12].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROCA (2017, CVE-2017-15361).&lt;/strong&gt; Infineon&apos;s key-generation library built primes with a special structure to speed things up. That structure let a Coppersmith attack factor the public modulus for a practical cost, and the affected chips were everywhere: TPMs, YubiKey 4 tokens, national electronic ID cards, and BitLocker deployments [@roca17], [@cve2017]. Coppersmith&apos;s 1996 barrier, patient for twenty years, collected its debt.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The root cause of the last two is not RSA math at all -- it is the &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;CSPRNG that generates the primes&lt;/a&gt;, the subject of Part 2 of this series. Feed RSA weak randomness and no scheme, no padding, and no proof can save it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Turn the diagnostic question on each front and it answers itself. The verifier confesses through a check it skipped. The chip confesses through a fault it did not catch. The clock confesses through a branch it did not equalize. The keygen confesses through primes it did not draw uniformly. In every case the attacker reads the private key off the receiver&apos;s &lt;em&gt;behaviour&lt;/em&gt;, not off the mathematics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Keep the empirical yardstick in view: the largest RSA modulus ever publicly factored is RSA-250, at 829 bits.RSA-250 was factored on 28 February 2020 using the Number Field Sieve, at a cost of roughly 2,700 core-years of computation [@rsa250]. That is the real, measured cost of breaking RSA by its front door -- which is exactly why nobody attacks that door when a padding check will open the side gate for a million cheap queries. Four fronts, one disease. The cure is not a cleverer patch on any single front. It is a change in what we mean when we say a scheme is secure.&lt;/p&gt;
&lt;h2&gt;6. Security Is a Property of the Scheme AND the Implementation&lt;/h2&gt;
&lt;p&gt;There are two famous ideas behind &quot;RSA done right,&quot; and a third, deeper one that the first two kept teaching the hard way.&lt;/p&gt;
&lt;h3&gt;Idea 1: OAEP, the all-or-nothing randomized transform&lt;/h3&gt;
&lt;p&gt;Optimal Asymmetric Encryption Padding, designed by Mihir Bellare and Phillip Rogaway in 1994, encodes the message before the RSA permutation so that the encoded block has no gradable structure for an attacker to probe [@oaep94]. Its construction is a two-round Feistel network with a hash-based mask-generation function, MGF1, as the round function -- the &lt;a href=&quot;https://paragmali.com/blog/the-fingerprint-two-files-shared-a-field-guide-to-cryptograp/&quot; rel=&quot;noopener&quot;&gt;hashing machinery&lt;/a&gt; that Part 10 of this series develops. Writing $\Vert$ for concatenation and $\oplus$ for XOR, and starting from a random $\mathrm{seed}$:&lt;/p&gt;
&lt;p&gt;$$\mathrm{DB} = \mathrm{lHash} \Vert \mathrm{PS} \Vert \texttt{0x01} \Vert M$$
$$\mathrm{maskedDB} = \mathrm{DB} \oplus \mathrm{MGF1}(\mathrm{seed}), \qquad \mathrm{maskedSeed} = \mathrm{seed} \oplus \mathrm{MGF1}(\mathrm{maskedDB})$$
$$\mathrm{EM} = \texttt{0x00} \Vert \mathrm{maskedSeed} \Vert \mathrm{maskedDB}$$&lt;/p&gt;
&lt;p&gt;Then the RSA permutation is applied to $\mathrm{EM}$ [@rfc8017]. The two Feistel rounds make every bit of the encoded block depend on every bit of the message &lt;em&gt;and&lt;/em&gt; the random seed. That is the property that kills the padding oracle: flip a single bit of an OAEP ciphertext and the decoded block is randomized wholesale, so a tampered ciphertext decodes to noise with overwhelming probability. There is no conformant interval to bisect, no structured remnant to grade.&lt;/p&gt;
&lt;p&gt;With the randomness supplying non-determinism and the all-or-nothing mixing supplying tamper-detection, OAEP is not only semantically secure but also non-malleable and secure against chosen-ciphertext attack -- IND-CCA2 in the random-oracle model [@oaep94abs].&lt;/p&gt;

Optimal Asymmetric Encryption Padding: a randomized, all-or-nothing transform applied to a message before the RSA permutation. It mixes the message with a random seed through two mask-generation passes -- a two-round Feistel network -- so that every bit of the encoded block depends on every bit of the input. Flipping any ciphertext bit randomizes the whole decoded block, which destroys malleability and yields chosen-ciphertext security in the random-oracle model.

OAEP&apos;s history includes a moment cryptography should be proud of. In 2001, Victor Shoup showed that the celebrated 1994 security proof did not go through for an arbitrary trapdoor permutation -- it quietly assumed more than the definition guarantees [@shoup01]. The scheme was not broken; the *argument* was. Later that year, Fujisaki, Okamoto, Pointcheval, and Stern repaired it, proving RSA-OAEP is chosen-ciphertext secure in the random-oracle model under the RSA assumption, by leaning on a property called partial-domain one-wayness -- though the resulting reduction is non-tight [@fops04]. A public &quot;our proof, not our scheme, was wrong,&quot; followed by a public fix, is exactly how a mature field is supposed to work.
&lt;h3&gt;Idea 2: PSS, the salted encoding with a tight proof&lt;/h3&gt;
&lt;p&gt;For signatures, the Probabilistic Signature Scheme, also from Bellare and Rogaway, plays the analogous role [@pss96]. To sign, it hashes the message to $\mathrm{mHash}$, draws a random $\mathrm{salt}$, and forms $M&apos; = \texttt{padding} \Vert \mathrm{mHash} \Vert \mathrm{salt}$; sets $H = \mathrm{Hash}(M&apos;)$; builds $\mathrm{DB} = \mathrm{PS} \Vert \texttt{0x01} \Vert \mathrm{salt}$; masks it as $\mathrm{maskedDB} = \mathrm{DB} \oplus \mathrm{MGF1}(H)$; and assembles $\mathrm{EM} = \mathrm{maskedDB} \Vert H \Vert \texttt{0xbc}$ before applying the RSA private operation [@rfc8017].&lt;/p&gt;
&lt;p&gt;The randomization and MGF mixing buy something v1.5 signatures never had: a &lt;em&gt;tight&lt;/em&gt; exact-security reduction. A forger against PSS implies an algorithm that inverts RSA at almost the same cost -- so breaking the signatures is essentially as hard as the RSA problem itself, not merely conjectured to be [@pss96]. That is strictly stronger than v1.5&apos;s heuristic argument. PSS entered the standard in PKCS#1 v2.1 (RFC 3447) in 2003 and remains in the current v2.2 [@rfc3447]. Yet the split holds: PSS is the provable &lt;em&gt;upgrade&lt;/em&gt;, but v1.5 signatures are not superseded in deployment -- they remain the Web PKI default.Beyond these two deployed schemes, the literature holds academic-only refinements -- SAEP/SAEP+, OAEP+, three-round OAEP, tight RSA-KEM variants, and message-recovery PSS-R -- which tighten proofs or trim assumptions but never displaced OAEP and PSS in practice. A pointer, not a section.&lt;/p&gt;

Probabilistic Signature Scheme: a randomized, salted RSA signature encoding. A fresh random salt and MGF1 mixing make every signature of the same message different, and the scheme comes with a tight security reduction -- a forger would yield an almost-equally-efficient algorithm for inverting RSA, the strongest guarantee any RSA scheme offers.
&lt;h3&gt;Idea 3: the one the first two kept teaching&lt;/h3&gt;
&lt;p&gt;Here is where the whole article turns. OAEP is provably secure, yet Manger re-opened the identical oracle inside a non-constant-time &lt;em&gt;decoder&lt;/em&gt;. PSS is provably secure, yet a fault or a timing leak in the &lt;em&gt;signer&lt;/em&gt; factors the key. The scheme was never the whole story.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Security is a property of the scheme AND its implementation, not of the math. A perfect scheme run by a decoder that leaks whether padding verified is exactly as broken as no scheme at all. The implementation disciplines below are part of the construction, not optional hygiene layered on top.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Three disciplines close the three channels the catalog exposed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Constant-time depadding with implicit rejection.&lt;/strong&gt; On any decryption failure, do not branch or return a distinguishable error; substitute a deterministic pseudo-random value and continue, so whether the padding verified stays unobservable. OpenSSL 3.2 ships this by default and aligned its behaviour with NSS so neither library can be an oracle for the other [@openssl32], [@gorsa].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CRT verify-before-release.&lt;/strong&gt; Recompute and check the signature before it leaves the device, defeating Bellcore faults [@bdl97].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base blinding.&lt;/strong&gt; Randomize the input before exponentiation so timing reveals nothing about the secret [@kocher96], [@brumleyboneh03].&lt;/li&gt;
&lt;/ul&gt;

A decryption strategy in which a padding failure does not produce a distinguishable outcome. Instead of branching or returning a specific error, the code substitutes a deterministic pseudo-random value derived from the private key and ciphertext and proceeds. Success and failure become indistinguishable through content, error type, or timing, so there is no oracle left to query.

flowchart TD
    M[Message or ciphertext arrives] --&amp;gt; SCH{&quot;Scheme layer&quot;}
    SCH --&amp;gt;|Encrypt| OAEP[RSAES-OAEP, randomized all-or-nothing]
    SCH --&amp;gt;|Sign| PSS[RSASSA-PSS, salted with tight proof]
    OAEP --&amp;gt; IMPL{&quot;Implementation layer&quot;}
    PSS --&amp;gt; IMPL
    IMPL --&amp;gt; CT[Constant-time depad, implicit rejection]
    IMPL --&amp;gt; VBR[CRT verify-before-release]
    IMPL --&amp;gt; BL[Base blinding]
    CT --&amp;gt; PAR{&quot;Parameter layer&quot;}
    VBR --&amp;gt; PAR
    BL --&amp;gt; PAR
    PAR --&amp;gt; P1[Modulus at least 2048 bits, e equals 65537, CSPRNG primes]
    P1 --&amp;gt; GATE{&quot;Does any behaviour reveal the padding verdict?&quot;}
    GATE --&amp;gt;|No| SAFE[RSA done right]
    GATE --&amp;gt;|Yes| BROKEN[Oracle reopens, the catalog repeats]
&lt;p&gt;The libraries now say this in plain language. Go&apos;s standard library deprecated its v1.5 decryption function outright, with a warning that doubles as the thesis of this article:&lt;/p&gt;

&quot;PKCS #1 v1.5 encryption is dangerous and should not be used ... whether this function returns an error or not discloses secret information.&quot; -- Go crypto/rsa package documentation [@gorsa]
&lt;p&gt;That is the whole discipline in one sentence: done right closes the padding-oracle class Part 6 traces across all of cryptography, and it does so at the implementation layer, not the math. So what does the whole stack look like in 2024 to 2026 -- and which parts are quietly being switched off?&lt;/p&gt;
&lt;h2&gt;7. State of the Art (2024 to 2026): Done Right Is a Stack, Not a Method&lt;/h2&gt;
&lt;p&gt;Most primitives have a single &quot;best&quot; option you can name. RSA does not. &quot;Done right&quot; in 2026 is a &lt;em&gt;conjunction&lt;/em&gt; of independent choices that must all hold at once. Remove any one and the 25-year catalog reopens at that layer. Here is the stack, layer by layer.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;What it adds&lt;/th&gt;
&lt;th&gt;Security status&lt;/th&gt;
&lt;th&gt;Still requires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;v1.5 encryption&lt;/td&gt;
&lt;td&gt;Encrypt&lt;/td&gt;
&lt;td&gt;Randomness only&lt;/td&gt;
&lt;td&gt;Broken as a target -- padding oracle&lt;/td&gt;
&lt;td&gt;Retire it; no safe deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSAES-OAEP&lt;/td&gt;
&lt;td&gt;Encrypt&lt;/td&gt;
&lt;td&gt;Randomness plus all-or-nothing mixing&lt;/td&gt;
&lt;td&gt;IND-CCA2 in the random-oracle model&lt;/td&gt;
&lt;td&gt;Constant-time, implicit-rejection decode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v1.5 signature&lt;/td&gt;
&lt;td&gt;Sign&lt;/td&gt;
&lt;td&gt;Fixed checkable structure&lt;/td&gt;
&lt;td&gt;Unbroken scheme, heuristic argument&lt;/td&gt;
&lt;td&gt;Strict verification, no e equals 3 shortcut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSASSA-PSS&lt;/td&gt;
&lt;td&gt;Sign&lt;/td&gt;
&lt;td&gt;Salt plus MGF, tight proof&lt;/td&gt;
&lt;td&gt;Provably as hard as the RSA problem&lt;/td&gt;
&lt;td&gt;CRT verify-before-release, blinding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Encryption scheme.&lt;/strong&gt; When a standard genuinely mandates RSA encryption of a small payload, the answer is RSAES-OAEP with SHA-256 and MGF1-SHA-256, decoded in constant time [@rfc8017]. But note the capacity ceiling: a 2,048-bit OAEP-SHA-256 ciphertext carries at most about 190 bytes of plaintext [@sp80056b]. &quot;Encrypt a file with RSA-OAEP&quot; is therefore not just risky but physically wrong -- RSA encryption is for keys, never bulk data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Signatures.&lt;/strong&gt; New designs use RSASSA-PSS, which TLS 1.3 makes mandatory for handshake signatures [@pss96], [@rfc8446]. Yet RSASSA-PKCS1-v1_5 signatures remain dominant and FIPS-approved across X.509 and TLS certificates [@fips1865]. This is not a contradiction: keep v1.5 signatures where they are mandated, and verify them strictly [@rfc8017].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation hardening.&lt;/strong&gt; This is the layer that actually stops the attacks. OpenSSL 3.2 enables constant-time depadding with implicit rejection by default [@openssl32], Go has deprecated its v1.5 decryption entry point [@gorsa], and the CFRG&apos;s 2025 implementation-guidance draft folds all of this into formal advice amending RFC 8017 [@cfrgdraft].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parameters.&lt;/strong&gt; A 2,048-bit modulus is the floor for 112-bit security; use 3,072 or 4,096 bits for long-term secrets; keep $e = 65537$; and draw primes from a properly seeded CSPRNG [@sp80057], [@fips1865].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment and policy.&lt;/strong&gt; The most durable fixes were structural, not cryptographic. TLS 1.3 &lt;em&gt;deleted&lt;/em&gt; RSA key exchange entirely, which killed the Bleichenbacher-on-TLS class at the protocol level rather than patching each oracle [@rfc8446]. FIPS policy disallows RSA v1.5 key transport after 31 December 2023 [@sp800131a], [@sp80056b]. And NIST IR 8547 puts a clock on RSA itself, deprecating 112-bit strength after 2030 and disallowing it after 2035 [@ir8547].&lt;/p&gt;

If you operate under FIPS 140-3, the encryption side of this is not advisory. NIST SP 800-131A Rev. 2 and SP 800-56B Rev. 2 together disallow RSAES-PKCS1-v1_5 key transport after 31 December 2023; approved RSA key establishment must use OAEP [@sp800131a], [@sp80056b]. The signature side is the opposite: RSASSA-PKCS1-v1_5 signatures remain FIPS-approved under FIPS 186-5 [@fips1865]. Same version number, opposite compliance verdicts -- which is exactly why the encryption-signature split is not pedantry.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Right scheme (OAEP to encrypt, PSS for new signatures), right implementation (constant-time depad with implicit rejection, CRT verify-before-release, base blinding), right parameters (2,048-bit floor, e equals 65537, CSPRNG primes), inside the right protocol (TLS 1.3, no RSA key exchange). Every deployed disaster in the catalog removed exactly one of these. None of it is new in 2026; the interesting movement is not toward a better RSA but away from RSA entirely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RSA done right is &lt;em&gt;stable&lt;/em&gt;. The genuinely current story is mostly about what to switch off and what is migrating away -- because for both of RSA&apos;s jobs, something smaller and faster is already taking over.&lt;/p&gt;
&lt;h2&gt;8. What RSA Is Losing To: ECDH, KEM-DEM, and the Post-Quantum Elephant&lt;/h2&gt;
&lt;p&gt;RSA is not being fixed into the future; it is being replaced out of it -- and the reasons have nothing to do with padding.&lt;/p&gt;
&lt;p&gt;The cleanest of those reasons is a design pattern that removes the attacker&apos;s target entirely. Never RSA-encrypt data. Instead, encapsulate a fresh random &lt;em&gt;symmetric&lt;/em&gt; key with the public key, and let an authenticated cipher carry the bulk. The public-key operation then transports only a uniform random key -- there is no attacker-chosen, structured plaintext for any oracle to grade. The entire Bleichenbacher-to-Marvin family loses its object, because the thing being decrypted is indistinguishable from random by construction [@rfc8446].&lt;/p&gt;

A two-part construction for hybrid encryption. A Key Encapsulation Mechanism (KEM) uses the public key to transport a freshly generated random symmetric key; a Data Encapsulation Mechanism (DEM) then encrypts the actual data with that key under an authenticated cipher. Because the public-key step only ever carries a uniform random key -- never a chosen, structured message -- there is no padding structure for an oracle to probe, so the padding-oracle class simply does not apply.

sequenceDiagram
    participant S as Sender
    participant R as Recipient
    Note over R: Publishes a public key
    S-&amp;gt;&amp;gt;S: Encapsulate, generate a random key K for the recipient
    S-&amp;gt;&amp;gt;S: Stretch K with a KDF, AEAD-encrypt the data
    S-&amp;gt;&amp;gt;R: Send the encapsulation and the AEAD ciphertext
    R-&amp;gt;&amp;gt;R: Decapsulate to recover K, run the same KDF
    R-&amp;gt;&amp;gt;R: AEAD-decrypt and verify the data
    Note over R,S: The public-key part carried only a random K, nothing to grade
&lt;p&gt;This is why elliptic-curve key agreement (ECDH, with Ed25519 and ECDSA for signatures) displaced RSA key transport: it does the same jobs with far smaller keys, faster operations, and forward secrecy -- a property RSA key transport never had, and the direct reason TLS 1.3 could &lt;em&gt;delete&lt;/em&gt; RSA key exchange rather than merely patch it [@rfc8446]. The &lt;a href=&quot;https://paragmali.com/blog/the-tag-verified-the-cipher-held-the-forgery-went-through-a-/&quot; rel=&quot;noopener&quot;&gt;KEM-DEM composition&lt;/a&gt; is developed in Part 11 of this series, the KDF step in Part 13, and the &lt;a href=&quot;https://paragmali.com/blog/the-aead-decision-matrix-seven-ciphers-three-edges-one-choic/&quot; rel=&quot;noopener&quot;&gt;AEAD that carries the payload&lt;/a&gt; in Part 7.&lt;/p&gt;
&lt;p&gt;Then there is the elephant. Peter Shor&apos;s algorithm factors integers in polynomial time on a large fault-tolerant quantum computer, which makes &lt;em&gt;all&lt;/em&gt; factoring-based cryptography -- every RSA key length, done right or not -- eventually breakable. NIST has already named the destination: ML-KEM (FIPS 203) for key encapsulation, and ML-DSA and SLH-DSA (FIPS 204 and 205) for signatures [@fips203], [@fips204], [@fips205].&lt;/p&gt;
&lt;p&gt;The migration is lopsided. Key encapsulation is moving fast -- hybrid X25519 plus ML-KEM-768 already carries a double-digit percentage of Cloudflare&apos;s TLS 1.3 traffic -- while post-quantum &lt;em&gt;signatures&lt;/em&gt; lag badly, because they run 10 to 200 times larger than an RSA signature and no public post-quantum certificate infrastructure existed before roughly 2026 [@cloudflare24], [@ir8547]. The lattice and hash-based internals belong to a later part of this series; here they matter only as RSA&apos;s replacement, not its repair.&lt;/p&gt;

Quantum risk is not only a future problem. An adversary can record RSA-encrypted traffic today and decrypt it once a capable quantum computer exists. For any secret that must stay confidential past roughly 2030, the exposure is *present tense* -- which is why NIST IR 8547 frames the transition as urgent rather than eventual, and why hybrid key exchange is being deployed now rather than when the machine arrives [@ir8547], [@cloudflare24].
&lt;p&gt;Laid side by side, the trade-offs explain why key transport migrated first and signatures are dragging. For moving a key:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key transport&lt;/th&gt;
&lt;th&gt;Forward secrecy&lt;/th&gt;
&lt;th&gt;Padding-oracle exposure&lt;/th&gt;
&lt;th&gt;Relative cost&lt;/th&gt;
&lt;th&gt;Quantum status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RSAES-OAEP [@rfc8017]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, if decode is not constant-time&lt;/td&gt;
&lt;td&gt;Large ciphertext, slow keygen&lt;/td&gt;
&lt;td&gt;Broken by Shor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECDH (X25519) [@rfc8446]&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None, no RSA decryption&lt;/td&gt;
&lt;td&gt;Small and fast&lt;/td&gt;
&lt;td&gt;Broken by Shor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-KEM-768 [@fips203]&lt;/td&gt;
&lt;td&gt;Yes (ephemeral)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Kilobyte-scale, fast&lt;/td&gt;
&lt;td&gt;Resistant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid X25519 + ML-KEM-768 [@cloudflare24]&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Sum of both, still practical&lt;/td&gt;
&lt;td&gt;Resistant if either holds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;And for signing:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signature&lt;/th&gt;
&lt;th&gt;Provable security&lt;/th&gt;
&lt;th&gt;Approx. signature size&lt;/th&gt;
&lt;th&gt;Main failure mode&lt;/th&gt;
&lt;th&gt;Quantum status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RSASSA-PSS [@pss96]&lt;/td&gt;
&lt;td&gt;Yes, tight in ROM&lt;/td&gt;
&lt;td&gt;256 bytes at 2048-bit&lt;/td&gt;
&lt;td&gt;Fault or timing in the signer&lt;/td&gt;
&lt;td&gt;Broken by Shor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSASSA-PKCS1-v1_5 [@rfc8017]&lt;/td&gt;
&lt;td&gt;No, heuristic only&lt;/td&gt;
&lt;td&gt;256 bytes at 2048-bit&lt;/td&gt;
&lt;td&gt;Lax-verifier forgery&lt;/td&gt;
&lt;td&gt;Broken by Shor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECDSA / Ed25519 [@rfc8446]&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;64 bytes&lt;/td&gt;
&lt;td&gt;Nonce reuse (ECDSA)&lt;/td&gt;
&lt;td&gt;Broken by Shor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-DSA-65 [@fips204]&lt;/td&gt;
&lt;td&gt;Yes, lattice&lt;/td&gt;
&lt;td&gt;About 3.3 kilobytes&lt;/td&gt;
&lt;td&gt;Implementation bugs&lt;/td&gt;
&lt;td&gt;Resistant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SLH-DSA [@fips205]&lt;/td&gt;
&lt;td&gt;Yes, hash-based&lt;/td&gt;
&lt;td&gt;Many kilobytes&lt;/td&gt;
&lt;td&gt;Large and slow&lt;/td&gt;
&lt;td&gt;Resistant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The signature column is the migration&apos;s hard problem, which is why post-quantum certificates lag key exchange by years [@ir8547]. If RSA is on the clock, it is worth asking where its security actually came from in the first place -- and why nobody has ever proved it was there.&lt;/p&gt;
&lt;h2&gt;9. Theoretical Limits: Where the Security Comes From, and Its Ceiling&lt;/h2&gt;
&lt;p&gt;Here is the uncomfortable truth a first course skips: there is no proof that RSA is secure.&lt;/p&gt;
&lt;p&gt;Start with the assumption itself. The RSA problem is to compute $e$-th roots modulo $N$ without the factorization. We know this is &lt;em&gt;no harder&lt;/em&gt; than factoring -- if you can factor $N$, you can compute $d$ and invert everything, so RSA $\le$ factoring. What nobody has shown is the reverse. It is an open question whether breaking RSA is &lt;em&gt;equivalent&lt;/em&gt; to factoring, and Boneh and Venkatesan gave evidence that a broad class of algebraic reductions from factoring to low-exponent RSA is unlikely to exist -- suggesting the two problems may not be equivalent at all [@bv98].&lt;/p&gt;
&lt;p&gt;Worse, factoring itself is not known to be hard in any proven sense: it sits in NP intersect co-NP, which is evidence it is probably &lt;em&gt;not&lt;/em&gt; NP-complete, and no one has proved a super-polynomial lower bound for it. RSA&apos;s security is heuristic and empirical -- it has survived decades of attack, and that is the entire argument [@boneh99].&lt;/p&gt;
&lt;p&gt;The empirical ceiling is concrete. The best classical algorithm, the General Number Field Sieve, runs in sub-exponential time, and the public record is RSA-250 at 829 bits, factored in February 2020 for roughly 2,700 core-years [@rsa250]. RSA-250 was one of the RSA Factoring Challenge moduli that RSA Laboratories first published in 1991 as public benchmarks for exactly this kind of progress [@rsanumbers]. That record sets the practical floors:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RSA modulus&lt;/th&gt;
&lt;th&gt;Symmetric-equivalent strength&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;1024-bit&lt;/td&gt;
&lt;td&gt;About 80 bits&lt;/td&gt;
&lt;td&gt;Broken within reach, disallowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2048-bit&lt;/td&gt;
&lt;td&gt;112 bits&lt;/td&gt;
&lt;td&gt;Current minimum, deprecated after 2030&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3072-bit&lt;/td&gt;
&lt;td&gt;128 bits&lt;/td&gt;
&lt;td&gt;Long-term use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4096-bit&lt;/td&gt;
&lt;td&gt;About 140 bits (interpolated)&lt;/td&gt;
&lt;td&gt;High assurance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7680-bit&lt;/td&gt;
&lt;td&gt;192 bits&lt;/td&gt;
&lt;td&gt;Next tabulated SP 800-57 step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15360-bit&lt;/td&gt;
&lt;td&gt;256 bits&lt;/td&gt;
&lt;td&gt;Shows how badly RSA scales&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Those equivalences come from NIST SP 800-57, except the 4,096-bit figure -- a common interpolation, since the standard steps directly from 3,072 bits (128) to 7,680 bits (192). The last row is the quiet punchline: matching a 256-bit symmetric key needs a 15,360-bit RSA modulus, because RSA strength grows only sub-exponentially in key length while the cost of using it grows with the cube [@sp80057]. RSA scales badly, and that alone pushes new systems toward elliptic curves.&lt;/p&gt;
&lt;p&gt;Then the cliff. On a large fault-tolerant quantum computer, Shor&apos;s algorithm factors in polynomial time -- not faster, but &lt;em&gt;categorically&lt;/em&gt; faster, collapsing the whole assumption. The only question is engineering, and the estimates are falling fast. In 2019, Gidney and Ekera estimated 20 million noisy qubits and 8 hours to factor a 2,048-bit modulus [@gidney19]; by 2025, Gidney had cut the qubit estimate to under a million [@gidney25].A 20-fold reduction in the resource estimate in six years, with no fundamental barrier in sight, is precisely the trend that drove NIST to put firm dates -- 2030 and 2035 -- on RSA&apos;s retirement in IR 8547. The deadline is set by the slope, not by any single machine.&lt;/p&gt;
&lt;p&gt;Three impossibility results frame the whole subject, each already seen in this article. First, determinism can never be IND-CPA -- that is structural, not fixable, which is why padding must randomize. Second, there is no known standard-model proof of IND-CCA2 for RSA-OAEP under the plain RSA assumption; the guarantee is random-oracle-model only, and even there non-tight [@shoup01], [@fops04]. Third, Coppersmith&apos;s barrier $|x| &amp;lt; N^{1/e}$ both enables low-exponent attacks and bounds them, a hard mathematical edge that no parameter choice moves [@coppersmith97].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; RSA&apos;s safety lives in an unproven gap -- breaking it is no harder than factoring, but maybe strictly easier, and factoring is not even proven hard -- and that gap sits on the near side of a quantum cliff with a falling date on it. &quot;Done right&quot; buys you security against every known classical attack. It does not buy you a proof, and it cannot buy you time past Shor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the ground under RSA is this uncertain, what exactly is still unsettled -- and which of those open problems can bite you today?&lt;/p&gt;
&lt;h2&gt;10. Open Problems: What Remains Genuinely Unsettled&lt;/h2&gt;
&lt;p&gt;Some of these are for theorists. Others are live in your dependency tree right now.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Is the RSA problem equivalent to factoring?&lt;/strong&gt; We have only one direction, RSA $\le$ factoring; the reverse is open, with evidence it may fail [@bv98]. It matters because RSA&apos;s whole security story rests on a hardness we have never actually pinned down.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A deployed, standard-model IND-CCA2 RSA scheme.&lt;/strong&gt; Constructions that avoid the random-oracle model exist on paper, but none is a shipping default; practice sidesteps the gap entirely by using KEM-DEM instead of RSA encryption [@fops04].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Constant-time RSA is a perpetually moving target.&lt;/strong&gt; Marvin re-found timing leaks in implementations previously believed immune, and only a handful -- such as BearSSL and BoringSSL -- passed its tests; leaks hide in general-purpose bignum code and even in error-logging paths, which is why the CFRG draft concludes the only safe path is to deprecate v1.5 encryption outright rather than keep hardening it [@marvin23], [@cfrgdraft].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The long tail of v1.5 encryption.&lt;/strong&gt; HSMs, PKCS#11 tokens, S/MIME, and JWE still use it in places that cannot simply be switched off -- exactly where Marvin keeps finding live oracles [@marvin23], [@gorsa].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generation-time entropy and structured primes.&lt;/strong&gt; Mining Your Ps and Qs and ROCA are operational failures with no clean universal fix: you cannot prove every device in the world seeded its CSPRNG correctly [@miningpq12], [@roca17].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;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;post-quantum migration timeline&lt;/a&gt;.&lt;/strong&gt; Key exchange is migrating; signatures are years behind, and harvest-now-decrypt-later keeps the pressure on [@cloudflare24], [@ir8547].&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Most of this list is someone else&apos;s research agenda. Two entries are not: the v1.5 &lt;em&gt;encryption&lt;/em&gt; long tail and weak key-generation entropy are the ones most likely to be sitting in your own stack right now, in an HSM integration or an embedded device you inherited. Audit those two first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Enough theory. Here is the whole argument compressed into rules you can apply on Monday.&lt;/p&gt;
&lt;h2&gt;11. The Practical Guide and the Misuse Catalog&lt;/h2&gt;
&lt;p&gt;Every rule below is one named break from the catalog, turned inside out. If you remember nothing else, remember the decision tree.&lt;/p&gt;

flowchart TD
    START{&quot;What do you need?&quot;} --&amp;gt;|Confidentiality| ENC{&quot;Can you avoid RSA encryption?&quot;}
    START --&amp;gt;|Authenticity| SIG{&quot;New design or legacy mandate?&quot;}
    ENC --&amp;gt;|Yes| KEM[Use ECDH or hybrid X25519 plus ML-KEM-768]
    ENC --&amp;gt;|No, a standard forces RSA| OAEP[RSAES-OAEP, SHA-256, constant-time decode, at least 2048-bit]
    SIG --&amp;gt;|New design| PSS[RSASSA-PSS with SHA-256]
    SIG --&amp;gt;|Legacy mandate| V15[v1.5 signature, verify strictly, no e equals 3 shortcut]
    KEM --&amp;gt; KEYS[Keys: e equals 65537, CSPRNG primes, verify-before-release, blinding]
    OAEP --&amp;gt; KEYS
    PSS --&amp;gt; KEYS
    V15 --&amp;gt; KEYS
    KEYS --&amp;gt; PQ{&quot;Secret must survive past 2030?&quot;}
    PQ --&amp;gt;|Yes| HY[Deploy hybrid post-quantum now]
    PQ --&amp;gt;|No| DONE[Ship it]
&lt;p&gt;Spelled out as decision rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encryption and key transport.&lt;/strong&gt; Prefer ECDH or a KEM, migrating to hybrid X25519 plus ML-KEM-768. If a standard forces RSA encryption, use RSAES-OAEP with SHA-256 and MGF1-SHA-256, a modulus of at least 2,048 bits, and a constant-time decoder with implicit rejection -- never v1.5 encryption [@rfc8017], [@sp80056b]. Never RSA-encrypt a payload; encapsulate a symmetric key and let an AEAD carry the data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signatures.&lt;/strong&gt; Use RSASSA-PSS for new designs [@pss96], [@fips1865]. Use v1.5 signatures only where mandated, and then verify strictly: full parse, re-encode and compare, reject trailing data, no $e = 3$ shortcut [@rfc8017].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keys and parameters.&lt;/strong&gt; A 2,048-bit floor, 3,072 or 4,096 bits for long-term secrets; $e = 65537$; independent CSPRNG-drawn primes per key; CRT with verify-before-release; base blinding [@sp80057], [@fips1865].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration.&lt;/strong&gt; Deploy hybrid post-quantum key agreement now, and inventory every RSA usage against the IR 8547 2030 and 2035 clock [@ir8547].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same rules as a lookup table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Key parameters&lt;/th&gt;
&lt;th&gt;Because (which break)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Key transport, preferred&lt;/td&gt;
&lt;td&gt;ECDH or hybrid X25519 + ML-KEM-768&lt;/td&gt;
&lt;td&gt;Ephemeral keys&lt;/td&gt;
&lt;td&gt;Forward secrecy, no oracle, quantum hedge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key transport, if RSA forced&lt;/td&gt;
&lt;td&gt;RSAES-OAEP, constant-time decode&lt;/td&gt;
&lt;td&gt;SHA-256, MGF1-SHA-256, at least 2048-bit&lt;/td&gt;
&lt;td&gt;Bleichenbacher, Manger, Marvin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature, new design&lt;/td&gt;
&lt;td&gt;RSASSA-PSS&lt;/td&gt;
&lt;td&gt;SHA-256, random salt&lt;/td&gt;
&lt;td&gt;v1.5 hand-wave, e equals 3 forgery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature, legacy mandate&lt;/td&gt;
&lt;td&gt;v1.5, verified strictly&lt;/td&gt;
&lt;td&gt;Full parse, reject trailing bytes&lt;/td&gt;
&lt;td&gt;e equals 3 forgery, BERserk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key generation&lt;/td&gt;
&lt;td&gt;e equals 65537, CSPRNG primes&lt;/td&gt;
&lt;td&gt;2048 floor, 3072+ long-term&lt;/td&gt;
&lt;td&gt;Wiener, ROCA, Mining Ps and Qs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private operation&lt;/td&gt;
&lt;td&gt;CRT verify-before-release, base blinding&lt;/td&gt;
&lt;td&gt;Recompute before output&lt;/td&gt;
&lt;td&gt;Bellcore fault, Kocher timing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-term secrets&lt;/td&gt;
&lt;td&gt;Hybrid post-quantum now&lt;/td&gt;
&lt;td&gt;Inventory to IR 8547 clock&lt;/td&gt;
&lt;td&gt;Shor, harvest-now-decrypt-later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Now the misuse catalog. Each antipattern maps to exactly one rule it violates -- the findings that recur in real code review:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;v1.5 encryption &quot;kept for compatibility.&quot;&lt;/strong&gt; The single most dangerous line in an RSA integration. Violates: retire v1.5 encryption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-constant-time OAEP or v1.5 decode.&lt;/strong&gt; A decoder that branches or times differently on padding failure. Violates: constant-time, implicit-rejection decoding (Manger, Marvin).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distinct decryption error messages, or branch timing.&lt;/strong&gt; Any observable difference between &quot;bad padding&quot; and &quot;other error.&quot; Violates: uniform, unobservable failure (Bleichenbacher).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Missing CRT verify-before-release.&lt;/strong&gt; Signing without recomputing the result first. Violates: verify before release (Bellcore fault).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unblinded exponentiation.&lt;/strong&gt; A modexp whose time depends on the secret. Violates: base blinding (Kocher, Brumley-Boneh).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An $e = 3$ shortcut in a verifier.&lt;/strong&gt; Checking the prefix without rejecting trailing data. Violates: verify strictly (e-equals-3 forgery, BERserk).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Textbook RSA copied from a tutorial.&lt;/strong&gt; The raw permutation on a message. Violates: never call the raw primitive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RSA-encrypting a large payload.&lt;/strong&gt; Treating RSA as a bulk cipher. Violates: encapsulate a key, never encrypt data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weak, shared, or reused-modulus keys.&lt;/strong&gt; Primes drawn from a cold CSPRNG or a structured library. Violates: seed the CSPRNG, draw primes uniformly (Mining Ps and Qs, ROCA).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OAEP hash or label mismatch, or MGF1 silently defaulting to SHA-1.&lt;/strong&gt; A quiet interoperability and downgrade trap. Violates: pin the hash and MGF explicitly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treating a &quot;small&quot; side channel as unexploitable.&lt;/strong&gt; Marvin demolished that folklore -- a few microseconds recovered keys [@marvin23]. Violates: assume every observable leaks.&lt;/li&gt;
&lt;/ul&gt;

Grep for the dangerous entry points and confirm every RSA decryption path uses OAEP with a constant-time decoder:&lt;ul&gt;
&lt;li&gt;Go: search for &lt;code&gt;DecryptPKCS1v15&lt;/code&gt; and &lt;code&gt;EncryptPKCS1v15&lt;/code&gt;; move to &lt;code&gt;DecryptOAEP&lt;/code&gt; and &lt;code&gt;EncryptOAEP&lt;/code&gt;. The v1.5 decryptor is deprecated for the reason quoted earlier [@gorsa].&lt;/li&gt;
&lt;li&gt;Java: flag &lt;code&gt;Cipher.getInstance(&quot;RSA/ECB/PKCS1Padding&quot;)&lt;/code&gt;; require &lt;code&gt;RSA/ECB/OAEPWithSHA-256AndMGF1Padding&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;OpenSSL CLI: &lt;code&gt;openssl pkeyutl -encrypt -pubin -inkey pub.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If a dependency genuinely still needs v1.5 decryption, confirm it runs a constant-time, implicit-rejection decoder -- OpenSSL 3.2 does so by default [@openssl32].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In practice, three violations dominate real audits: RSA v1.5 &lt;em&gt;encryption&lt;/em&gt; still enabled &quot;for a legacy client,&quot; a decryption path that is not verifiably constant-time, and RSA being used to encrypt bulk data instead of a symmetric key. Fix those three and you have closed most of the catalog [@marvin23], [@gorsa], [@openssl32].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Read the whole failure catalog again as this checklist, and one pattern remains.&lt;/p&gt;
&lt;h2&gt;12. Trapdoor, Not Cryptosystem&lt;/h2&gt;
&lt;p&gt;Return to where we began. The researchers who signed with facebook.com&apos;s key never factored its modulus, and neither did anyone in the twenty-five years of breaks between Bleichenbacher and Marvin. The factoring problem stood untouched the whole time. What fell, every single time, was something else: the decryptor&apos;s error handling, the signer&apos;s fault behaviour, the implementation&apos;s clock, or the generator&apos;s entropy. The attacker&apos;s real move is never to solve the hard math -- it is to turn the receiver&apos;s own reaction into the private-key operation.&lt;/p&gt;

The attacker does not factor the modulus. They turn the decryptor&apos;s reaction into the private-key operation -- and &quot;done right&quot; is the discipline of leaving that reaction with nothing to say.
&lt;p&gt;That is why &quot;RSA done right&quot; is a stack and not a setting. The right scheme, OAEP to encrypt and PSS for new signatures, does two of the three jobs. The right parameters -- a 2,048-bit floor, $e = 65537$, primes from a real CSPRNG -- keep the trapdoor strong. And the constant-time, fault-checked, blinded implementation does the third job, the one the field kept forgetting: it leaves no observable behaviour that answers the attacker&apos;s one question.&lt;/p&gt;
&lt;p&gt;Every deployed disaster in this article is a stack with exactly one layer missing. Textbook RSA is broken not because the math is weak but because it is &lt;em&gt;only&lt;/em&gt; the trapdoor, with none of the layers that make a trapdoor into a cryptosystem.&lt;/p&gt;
&lt;p&gt;The forward horizon makes the discipline sharper, not softer. RSA done right is stable against every known classical attack, and it still has an expiration date, because the one gap RSA can never close is the quantum one. The destination is not a better RSA. It is KEM-DEM composition and post-quantum algorithms -- constructions where the public-key operation carries only a uniform random key, and where Shor has no polynomial-time shortcut to wait for.&lt;/p&gt;
&lt;p&gt;So ask the diagnostic question one final time, now that it is answerable. When a ciphertext or signature it did not create arrives, what does the receiver reveal about it -- through its answer, its timing, or its faults? Done right, the answer is nothing at all.&lt;/p&gt;


No, but the word &quot;RSA&quot; hides three different answers. Textbook RSA is broken. PKCS#1 v1.5 *encryption* is dangerous and should be retired [@marvin23]. Done-right RSA -- OAEP or PSS, constant-time, at least 2,048 bits, public exponent 65537 -- is fine against every known classical attack, right up until a large quantum computer exists, which is why you should be planning migration in parallel [@ir8547].


No. Encapsulate a fresh random symmetric key with the public key and let an authenticated cipher encrypt the file. RSA-OAEP at 2,048 bits carries only about 190 bytes of plaintext anyway, so it was never meant for bulk data -- it moves keys, not files [@sp80056b].


No. OAEP fixes the *scheme*, not the decoder. James Manger showed that a non-constant-time OAEP decoder re-opens the very same padding oracle, and more cheaply than the original Bleichenbacher attack [@manger01]. The rule is &quot;OAEP AND constant-time decoding,&quot; never OAEP alone.


Not as a scheme -- there is no known attack against the RSASSA-PKCS1-v1_5 signature construction itself [@rfc8017]. What breaks is lax *verifiers*: the 2006 e-equals-3 forgery and the 2014 BERserk bug both forged signatures past sloppy verification, not by breaking the scheme [@cve2006], [@cve2014]. Verify strictly -- parse the entire structure and reject any trailing data.


Only with correct padding and a strict verifier, and it is not worth the risk. A small exponent has repeatedly enabled Hastad&apos;s broadcast attack, Coppersmith&apos;s low-exponent attacks, and signature forgeries [@hastad88], [@coppersmith97]. Use 65537: it is fast and dodges every low-exponent trap.


Yes for today -- a 2,048-bit modulus gives about 112 bits of security [@sp80057]. Use 3,072 bits or more for anything that must stay secret long-term, and start post-quantum planning: NIST deprecates 112-bit RSA after 2030 and disallows it after 2035 [@ir8547].


For most new work, yes. ECDH and Ed25519 give smaller, faster keys with the forward secrecy RSA key transport never had [@rfc8446], and hybrid X25519 plus ML-KEM-768 is already carrying real TLS traffic for the quantum transition [@cloudflare24]. RSA&apos;s destination is retirement, not repair.

&lt;p&gt;&amp;lt;StudyGuide slug=&quot;rsa-done-right-oaep-pss-bleichenbacher&quot; keyTerms={[
  { term: &quot;Trapdoor permutation&quot;, definition: &quot;A one-way function with a secret shortcut. RSA maps x to x-to-the-e modulo N, invertible only by whoever knows the factorization of N. It is a primitive, not a complete cryptosystem.&quot; },
  { term: &quot;Malleability&quot;, definition: &quot;RSA&apos;s multiplicative homomorphism: multiplying a ciphertext by a chosen factor predictably scales the plaintext, letting an attacker transform messages without decrypting. It is the lever behind the padding-oracle attacks.&quot; },
  { term: &quot;Padding oracle&quot;, definition: &quot;Any observable behaviour (an error, a timing difference, or a fault) that reveals whether a decrypted ciphertext had valid padding, turning a validity check into a decryption oracle.&quot; },
  { term: &quot;RSAES-OAEP&quot;, definition: &quot;A randomized, all-or-nothing encryption padding applied before the RSA permutation, giving chosen-ciphertext security in the random-oracle model, provided the decoder runs in constant time.&quot; },
  { term: &quot;RSASSA-PSS&quot;, definition: &quot;A randomized, salted RSA signature encoding with a tight security reduction to the RSA problem, the provable choice for new signature designs.&quot; },
  { term: &quot;Implicit rejection&quot;, definition: &quot;Returning a deterministic pseudo-random value on a padding failure instead of a distinguishable error, so success and failure are unobservable through content, error type, or timing.&quot; },
  { term: &quot;CRT in RSA&quot;, definition: &quot;Computing the private operation modulo p and modulo q separately for a roughly fourfold speedup, at the cost of a fault-attack surface unless the result is verified before release.&quot; },
  { term: &quot;KEM-DEM&quot;, definition: &quot;Transporting a random symmetric key with the public key, then encrypting the data with that key under an authenticated cipher, so no chosen structured plaintext exists for an oracle to grade.&quot; },
  { term: &quot;IND-CCA2&quot;, definition: &quot;The strongest standard security goal for encryption: even with access to a decryption oracle, an attacker cannot tell which plaintext a ciphertext hides. Padding oracles violate it.&quot; }
]} questions={[
  { q: &quot;Why is textbook RSA not a cryptosystem?&quot;, a: &quot;It is deterministic, malleable, and structurally leaky for small exponents. It needs padding that adds randomness, adds checkable redundancy, and leaks nothing about whether the check passed.&quot; },
  { q: &quot;What single bit did every attack from Bleichenbacher to Marvin leak?&quot;, a: &quot;Whether the padding was valid. Only the channel changed, from an error message to a cross-protocol zombie to a TCP quirk to pure timing.&quot; },
  { q: &quot;Why is OAEP necessary but not sufficient?&quot;, a: &quot;OAEP secures the scheme, but Manger showed that a non-constant-time decoder re-opens the same oracle. Security is a property of the scheme and its implementation together.&quot; },
  { q: &quot;What is the one split you must never blur?&quot;, a: &quot;v1.5 encryption is dangerous and should be retired, while v1.5 signatures are unbroken as a scheme and still dominant. Verify the signatures strictly.&quot; },
  { q: &quot;Why does done-right RSA still have an expiration date?&quot;, a: &quot;There is no proof RSA is secure, and Shor&apos;s algorithm factors in polynomial time on a quantum computer, so the destination is KEM-DEM plus post-quantum cryptography.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>rsa</category><category>oaep</category><category>rsa-pss</category><category>padding-oracle</category><category>bleichenbacher</category><category>post-quantum-crypto</category><category>cryptography</category><category>applied-cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>