44 min read

How RSA Breaks in Real Life: ROCA, Bleichenbacher's Ghosts, FREAK, and the Keys That Shared a Prime

No one has ever factored a strong, deployed RSA key -- yet ROCA, Bleichenbacher's oracle, DROWN, and FREAK broke real RSA anyway. The break was never the factoring.

Permalink

1. No One Has Ever Factored a Strong RSA Key. Your RSA Keys Broke Anyway.

The largest RSA key ever factored in public is a 250-digit, 829-bit challenge number, and cracking it in 2020 took the world's best number theorists about 2,700 core-years [1]. No strong, correctly generated RSA key protecting real traffic has ever been factored at all. Hold that fact still for a moment, because the next one refuses to sit beside it.

In the same era, Estonian national ID cards were forgeable [4], roughly a third of all HTTPS was decryptable [6], hundreds of thousands of embedded private keys were recoverable [3], and export-grade sessions were silently man-in-the-middled [8]. Every one of those was an RSA break. Not one of them was a factored strong modulus. So how does a factoring problem nobody can beat on a strong key keep producing forged signatures, decrypted sessions, and stolen private keys?

The resolution is the whole argument of this article, and it belongs before the evidence: none of these broke strong RSA. RSA is a trapdoor permutation, and its single security promise is narrow -- that factoring a strong n=pqn = p \cdot q is hard [10]. That promise silently depends on three things the mathematics does not control: where the primes came from, how the decrypting party validates padding, and which key strength a protocol will accept. It was those three deployment-owned layers, never the factoring problem, that gave way.

So carry one diagnostic question through everything that follows. When an RSA key, session, or signature falls, do not ask "did someone factor the modulus?" Ask instead: which layer the math depends on but does not control gave way -- the primes, the padding, or the negotiation? Every break in this article is a non-empty answer to that question, and you will learn to drop ROCA, DROWN, FREAK, and tomorrow's incident into it on sight.

To break RSA in the field, you never factor a strong modulus. You take the weak one the deployment already handed you, or the oracle it left open -- and the factoring problem on a strong key stands untouched.

The paradox even has a face. The researcher Nadia Heninger appears at both poles of this story: she recovered weak keys in the field, and she co-holds the public record for factoring a strong one [3, 1]. One person, both ends of the argument -- the weak keys that fall in an afternoon and the strong one that costs millennia of compute.

This is Part 3 of How It Breaks in Real Life, a series with one recurring thesis: the primitive's mathematics almost never caused the break; the deployment did. RSA is one of its cleanest cases. It has a companion piece, How RSA Would Break: Why Factoring Is the Slow Path and Coppersmith Is the Fast One, which handles the would-break-in-theory math: the Number Field Sieve, Coppersmith's lattices, and Shor's algorithm. This article is the did-break-in-the-field frame.

If no strong modulus was ever factored and the factoring problem never moved, then everything that broke was built around it. To see how "unfactored modulus" and "recovered key" can both be true at once, we have to go back to what RSA actually promises -- and, more importantly, what it silently assumes.

2. What RSA Actually Assumes

In 1977, Ron Rivest, Adi Shamir, and Leonard Adleman handed the world its first practical public-key cryptosystem: a public modulus n=pqn = p \cdot q, a public exponent ee, a private exponent dd, and a security argument resting on the hardness of factoring nn [10]. That assumption is exactly why the core problem has held for five decades. It is also exactly why, when RSA breaks in the field, the fault lies somewhere else. Being a trapdoor permutation made RSA a primitive. It did not make it a safe deployment.

Start with the single most important distinction in this article. RSA-the-primitive is a keyed permutation over the integers modulo nn, and its security promise is narrow and conditional. Here is the precise version, because the imprecise version is the source of half the confusion in the subject.

RSA trapdoor permutation

RSA fixes a modulus n=pqn = p \cdot q, a public exponent ee, and a private exponent dd; encryption is c=memodnc = m^e \bmod n and decryption is m=cdmodnm = c^d \bmod n. Its security rests on the assumption that inverting the permutation without dd is hard. Factoring nn is sufficient to invert it -- recover ϕ(n)\phi(n), then dd -- and remains the best known attack, so breaking RSA is at most as hard as factoring. Whether it is as hard as factoring, the RSA-problem-versus-factoring equivalence, is an open question [11, 10]. The math controls only that hardness, never where pp and qq come from.

Read that twice, because the direction matters. Factoring is sufficient to break RSA: if you can factor nn, you win. But nobody has proved the reverse, that breaking RSA requires factoring. The RSA problem might, in principle, be easier than factoring; we do not know. What we do know is that the best attack anyone has found on the primitive is still factoring, and factoring a strong modulus is where the wall stands.

Notice the quiet consequence: the field never even needed that unproven shortcut. Every deployed break in this article bypassed both directions entirely. No one factored a strong key, and no one found a clever inversion either. They walked around the math.

Because the guarantee is only about the difficulty of factoring, it says nothing about three other things:

  1. Where pp and qq come from. They must be large, independent, and drawn from real entropy.
  2. How the raw permutation is wrapped. Textbook RSA is deterministic and malleable, so it must be padded before it is used.
  3. Which key strength a protocol will accept. A 512-bit option nobody wants is still a 512-bit option somebody can force.
Textbook (raw) RSA and malleability

Unpadded RSA is deterministic: equal plaintexts always produce equal ciphertexts, so an eavesdropper can recognize repeats and test guesses. It is also malleable, because (m1e)(m2e)(m1m2)e(modn)(m_1^e)(m_2^e) \equiv (m_1 m_2)^e \pmod{n}: an attacker who multiplies a ciphertext by ses^e turns it into an encryption of msm \cdot s without knowing mm. Both properties make raw RSA unusable on its own; it must be wrapped in a padding scheme. That same malleability is the lever every padding-oracle attack pulls.

Those three silences are the structure of the whole article. Picture them as a map: the factoring-hardness assumption sits at the center, and three deployment-owned dependencies surround it. Each is an independent surface an attacker can reach without ever touching the factoring problem.

Ctrl + scroll to zoom
The three-layer dependency map: RSA's one guarantee at the center, and the three deployment-owned layers around it that the math depends on but cannot enforce.

Before we watch each dependency fail, three guardrails, because the argument is easy to overstate in exactly three ways.

The series this article belongs to lists a familiar set of culprits: the RNG, the nonce, the padding, the downgrade. RSA answers to most of them, but not the nonce. RSA signing has no secret per-signature nonce whose reuse leaks the key. The nonce-reuse break -- the reused kk behind the 2010 PlayStation 3 and 2013 Android SecureRandom key recoveries [12, 13, 14] -- is an (EC)DSA phenomenon, a different primitive. There is no "RSA nonce-reuse break"; when you see one described, it is really an ECDSA story, and it is told in its own article. Two of the three real culprits are RSA's, and both are downstream of the map above.

One narrow assumption, three silent dependencies, none of them the factoring problem. Before we watch each dependency break in the field, we need to see the contracts up close: what real entropy actually buys, why a padding check is a loaded gun, and why a 512-bit option nobody uses is still a live weapon.

3. Three Contracts the Math Cannot Enforce

Every field break in this article is the violation of one specific promise -- a promise the RSA math assumes but has no way to enforce. There are exactly three, one per layer of the map, and once you can see the contract, the break becomes obvious. Here they are, side by side.

LayerThe contract it imposesCost of violating itWho owns it
Key generationPrimes are unpredictable and never sharedKeys become enumerable, colliding, or structurally factorableThe RNG, the key-generation library, the boot-time entropy source
Padding validationNever reveal whether a decryption was well-paddedThe server becomes a decryption or signing oracleThe TLS and PKCS#1 implementation
NegotiationNever accept a crippled key or a dead protocolA strong peer is forced down to a breakable oneThe protocol state machine and its configuration

The key-generation contract: the primes must be unpredictable and never shared. Two "random" primes are only as unpredictable as the generator that produced them. Starve that generator of entropy, cripple it with a bug, or bias its structure, and the primes come out predictable, colliding, or specially formed -- while every later step still looks textbook-correct.

Entropy and CSPRNG seeding

A cryptographically secure pseudorandom number generator (CSPRNG) stretches a small secret seed into an unbounded stream of unpredictable bits, and its output is only as unpredictable as that seed. If the seed is starved of real entropy at boot, crippled by a bug that removes entropy, or drawn from a generator that constrains its outputs, the primes it produces become predictable, colliding, or specially formed, even though the arithmetic around them is flawless.

Shared-factor (batch-GCD) recovery

If two moduli n1n_1 and n2n_2 accidentally share one prime, then gcd(n1,n2)=p\gcd(n_1, n_2) = p recovers that prime with a single Euclidean gcd, and one division gives the other factor of each. A batch-GCD computes every pairwise gcd across millions of keys at once with a product-and-remainder tree, in quasi-linear time. It is simultaneously an attack (recover private keys at Internet scale) and a defense (scan your own fleet for collisions).

The hidden assumption behind this contract is quiet but total: enough real entropy reached key generation, and no two keys ever drew the same prime.

The padding contract: never reveal whether padding was valid. Textbook RSA is deterministic and malleable, so a real system must wrap the message before exponentiating. For twenty-five years the near-universal wrapper has been PKCS#1 v1.5.

PKCS#1 v1.5 padding

Before exponentiation the message is wrapped as 00 02 [at least 8 random non-zero bytes] 00 [message] [17]. The leading 00 02 marks an encryption block, the random bytes make equal plaintexts encrypt differently, and the 00 separator marks where the message begins. The attack in this article is on this v1.5 encryption format, whose validity check becomes the oracle. PKCS#1 v1.5 signatures are a separate construction and are not what these attacks break.

Here is the conceptual pivot the whole subject turns on. In 1998, Daniel Bleichenbacher noticed that the padding check itself is a leak [5]. A server that decrypts a ciphertext and then reveals -- by error, timeout, or timing -- whether the result began with the required 00 02 bytes has answered a yes/no question about the plaintext. One bit does not sound like much. But raw RSA is malleable, so the attacker can ask the question about a plaintext of their choosing.

Padding oracle (adaptive chosen-ciphertext)

A decrypting party that reveals -- by error message, connection reset, timeout, or response timing -- whether a submitted ciphertext decrypted to validly padded plaintext leaks one bit per query. Feed it enough adaptively chosen ciphertexts and those one-bit answers pin the plaintext down, without ever recovering the private key. This is the single most load-bearing idea in the article.

Because raw RSA is malleable, an attacker can multiply a ciphertext cc by ses^e to form c=csemodnc' = c \cdot s^e \bmod n, which decrypts to msmodnm \cdot s \bmod n. A server that reveals whether each such cc' was validly padded answers one yes/no question per query about where mm lies. About 10610^6 adaptive queries narrow mm to a single value: a session decrypted, with no private key recovered and nothing factored. This one mechanic drives Bleichenbacher, DROWN, and ROBOT.

You can feel the mechanic in miniature. The toy below uses an idealized one-bit oracle -- it reports only whether the decryption falls in the bottom half of the range -- and binary-searches the plaintext out of the server without ever touching the key. The oracle recovers no private key and factors nothing; it turns the server into a one-session decryption (or one-message signing) oracle, and the modulus is untouched afterward. Keep two things distinct: the attack is on PKCS#1 v1.5 encryption, and v1.5 signatures are a separate scheme it does not break.

JavaScript A one-bit decryption oracle recovers the plaintext (toy modulus)
function modpow(b, e, n){ b %= n; let r = 1n; while (e > 0n){ if (e & 1n) r = (r*b)%n; b = (b*b)%n; e >>= 1n; } return r; }
const p = 61n, q = 53n, n = p*q;          // toy modulus n = 3233
const e = 17n, d = 2753n;                 // public e, private d
const m = 65n;                            // the secret plaintext
const c = modpow(m, e, n);                // the ciphertext the attacker holds

// The ONLY leak: does the decryption land in the bottom half of [0, n)?
// An idealized magnitude / MSB oracle -- one bit per query.
const oracle = (ct) => modpow(ct, d, n) < n / 2n;

const enc2 = modpow(2n, e, n);            // multiplier that doubles the plaintext (malleability)
const K = 14n, SCALE = 1n << K;
let loS = 0n, hiS = n * SCALE, ct = c;
for (let i = 0n; i < K; i++){
const mid = (loS + hiS) / 2n;
if (oracle(ct)) hiS = mid; else loS = mid;   // read one bit, halve the interval
ct = (ct * enc2) % n;                          // next query sees 2*m, 4*m, ... mod n
}
console.log('recovered plaintext ->', (hiS / SCALE).toString());   // -> 65

// The real PKCS#1 v1.5 oracle asks a different one-bit question: do the top bytes
// equal 00 02? That narrows m onto a union of intervals 2B <= m < 3B rather than a
// < n/2 half -- same principle, different bit. This < n/2 magnitude test is the same
// family of magnitude / most-significant-byte leak Manger's 2001 attack exploits
// against OAEP -- but his oracle tests x < B = 2^(8(k-1)) (whether the leading octet
// is zero), not x < n/2.

Press Run to execute.

The negotiation contract: never accept a deliberately crippled key or a dead protocol. The math cannot see which key strength a handshake settled on, nor whether the protocol carrying it was retired a decade ago.

Export-grade cryptography (RSA_EXPORT and SSLv2)

1990s US export regulations capped exportable cryptography at deliberately weak strengths, including 512-bit RSA_EXPORT key exchange, and SSLv2 was the era's now-obsolete protocol [8, 18]. Both were retired in principle long ago, yet both survived in shipping code and server configurations long enough to become live attack surfaces.

The assumption here is that only a party that wants export-grade crypto, or wants SSLv2, will ever get it.

Three contracts, each reasonable, each unenforced by the math. The factoring problem cannot tell whether you fed it a shared prime, a leaky padding check, or a forced 512-bit key. Which raises the only question that matters: on real smartcards and real servers, do these contracts actually hold? They do not. Here is where, when, and how each one broke.

4. Three Deployment Failures, Three Layers, One Pattern

The comfortable belief is "we use RSA-2048, so our keys, sessions, and signatures are safe." What follows are seven independent refutations of that inference: seven deployed field breaks, grouped into three pillars, one per layer the math depends on but does not control. Each pillar runs in chronological order, but read it as a failure catalog, not a lineage of improving designs. They are three simultaneous layers every real deployment must secure at once, and the chronology is simply the attacker's frontier moving from one unmet obligation to the next as each was patched.

IncidentYearPillarMechanismModulusRecovers key?Factors it?The fix (changed the deployment)
Debian OpenSSL RNG2008Key generationSeed collapsed to the process ID; keys enumerableWeakYesNoReal entropy; regenerate every key
Mining Your Ps and Qs2012Key generationShared prime; one gcd factors bothWeakYesYes (weak)Entropy at boot; batch-GCD scanning
ROCA2017Key generationStructured primes; Coppersmith latticeWeakYesYes (weak)Fix the library; ROCA detector; regenerate
Bleichenbacher1998Paddingv1.5 padding-validity oracleStrongNoNoUniform errors; OAEP; drop RSA transport
DROWN2016PaddingSame oracle via a shared-key SSLv2 endpointStrongNoNoDisable SSLv2; stop cross-protocol key reuse
ROBOT2017PaddingThe 1998 oracle still answering across nine vendorsStrongNoNoConstant-time uniform errors; retire RSA encryption
FREAK2015NegotiationDowngrade to 512-bit RSA_EXPORT, then factorWeakYesYes (weak)Remove export ciphers; downgrade protection; TLS 1.3
RSA-250 (ceiling)2020ChallengeGNFS on an 829-bit challenge numberStrongn/aYes (challenge)Not a field break; the public ceiling, never deployed

Read the two rightmost data columns as the argument in a grid: every row that ends in a factored modulus is a weak modulus, and every row with a strong modulus recovers no key and factors nothing. The one strong modulus ever factored, RSA-250, is a challenge number that never protected anything.

Ctrl + scroll to zoom
The attacker's frontier from 1998 to 2020: not a lineage but a moving front, hopping to whichever layer was still unmet as each was patched.

Pillar 1: The primes were weak before the math began

All three key-generation breaks share one shape: generation produced a weak modulus, and then -- only then -- arithmetic finished it off. A strong modulus is on none of these paths.

Ctrl + scroll to zoom
How a weak key is born, then finished. Three independent generation faults converge on a weak modulus; the math merely completes a job the deployment set up.

Debian OpenSSL, defect 2006, disclosed 2008. A well-meaning Debian patch, written to silence a Valgrind warning about uninitialized memory, removed most of the entropy feeding OpenSSL's PRNG. The change shipped in openssl 0.9.8c-1, and for nearly two years the effective seed collapsed to essentially the process ID, about 32,767 possibilities [2, 19]. Keys stopped being unpredictable and became enumerable: an attacker could precompute the whole set. No factoring, no oracle, just a keyspace small enough to list. The defect was uploaded as openssl 0.9.8c-1 on 17 September 2006 and disclosed on 13 May 2008 by Luciano Bello [20]. Field measurement came from Yilek and colleagues at IMC 2009, who watched the fix propagate: 751 vulnerable certificates observed, and a meaningful fraction still vulnerable roughly six months later [21].

Mining Your Ps and Qs, 2012. Nadia Heninger, Zakir Durumeric, Eric Wustrow, and J. Alex Halderman scanned the entire IPv4 Internet's TLS and SSH hosts and found something worse than predictability. They found collision. Headless and embedded devices, generating keys at first boot with almost no entropy, sometimes produced moduli that shared a single prime with an unrelated device. A shared prime is fatal, because gcd(n1,n2)=p\gcd(n_1, n_2) = p factors both moduli in the time of one gcd.

They measured that 5.57% of TLS hosts shared keys, and they remotely recovered the RSA private keys of 0.50% of all TLS hosts through common factors, notifying 54 manufacturers [3]. An independent study the same year, titled "Ron was wrong, Whit is right," found the same collisions in a different dataset and ruled out a single-vendor fluke [22].

You can watch both keys fall from one gcd:

JavaScript Two independent RSA moduli fall to a single shared-prime gcd
const gcd = (a, b) => { while (b) { [a, b] = [b, a % b]; } return a; };
// Two RSA moduli from two different devices. Neither looks weak on its own.
// (Shrunk stand-ins; real moduli are hundreds of digits and the gcd costs the same.)
const n1 = 63900000000000000000000000000000000000000000002625100000000000000000000000000000000000000000026931n;
const n2 = 74700000000000000000000000000000000000000000003196300000000000000000000000000000000000000000034189n;
const p = gcd(n1, n2);              // one Euclidean gcd -- the entire attack
const q1 = n1 / p, q2 = n2 / p;
console.log('shared prime p =', p.toString());
console.log('device 1 recovered:', p * q1 === n1);
console.log('device 2 recovered:', p * q2 === n2);
console.log('Both private keys recovered from one gcd. Key size was irrelevant.');

Press Run to execute.

ROCA, 2017. The first two breaks needed bad randomness. ROCA needed none. Matus Nemec, Marek Sys, Petr Svenda, Dusan Klinec, and Vashek Matyas showed that Infineon's RSALib built its primes in a speed-optimizing but constrained form, p=kM+(65537amodM)p = k \cdot M + (65537^a \bmod M), detectable from the public key alone and factorable by a Coppersmith-style lattice far faster than general factoring [23, 4, 24]. The randomness was fine; the structure was the flaw, so every key the chip ever generated was affected. The first author is Matus Nemec (not "Miroslav"), and the fifth is Vashek, or Vaclav, Matyas (not "Vladimir"). The affected products were the YubiKey 4 (not the YubiKey 5), Estonian national eID cards, and Infineon-based TPMs. Because the attack keys off structure rather than entropy, it is RNG-independent [4].

Coppersmith's method (small roots)

For a monic polynomial of degree dd modulo NN, Coppersmith's method finds every integer root x0x_0 with x0N1/d\lvert x_0 \rvert \le N^{1/d} in polynomial time, using lattice reduction [25]. When a prime is built with a special constrained structure, part of it becomes such a small root, and the method factors nn far faster than general-purpose factoring. It is the dormant 1997 engine ROCA revived; the lattice details belong to the companion How RSA Would Break.

The cost, from the authors themselves: a 1024-bit key fell in under three CPU-months, about $76 on AWS; a 2048-bit key in under 100 CPU-years, about $40,000; and roughly 760,000 confirmed-vulnerable keys were in the field [4]. Notice what "2048-bit" bought here: nothing. The number that mattered was not the key size but the structure of the primes.

Pillar 2: The padding check was an oracle

Now the padding pivot from Section 3, shown surviving twenty-five years of patches. Three incidents, one bug, reappearing at a new layer each time an inner defense hardened.

Ctrl + scroll to zoom
The Bleichenbacher padding oracle. Each reply is one bit about the plaintext; malleability lets the attacker choose which bit. The private key is never touched.

Bleichenbacher, 1998. The origin. A PKCS#1 v1.5 encryption endpoint that reveals whether a decryption was well-padded becomes an adaptive chosen-ciphertext decryption oracle, and about 10610^6 queries decrypt a captured session. The private key is never recovered and nothing is factored; the attacker simply borrows the server's decryption ability for one message [5].

DROWN, 2016. Nimrod Aviram and fourteen coauthors, Heninger and Halderman among them, showed the 1998 oracle had a door nobody had closed: a forgotten SSLv2 endpoint. If a modern TLS server shared its RSA key with an old SSLv2 server -- common, because operators reused certificates -- an attacker could run a cross-protocol Bleichenbacher attack through the SSLv2 side, whose export ciphers and an OpenSSL bug made a "special" variant cheap.

At disclosure in March 2016, 33% of all HTTPS servers, 25% of the top million, and 22% of browser-trusted sites were vulnerable; it fell to about 1.2% by 2019 [6]. DROWN is not ROBOT. DROWN (2016) reached modern TLS cross-protocol, through a shared-key SSLv2 endpoint. ROBOT (disclosed December 2017, published at USENIX Security 2018) found the same oracle still answering directly on modern TLS stacks. Different years, different vector, the same 1998 bug [6, 7].

ROBOT, 2017. Hanno Bock, Juraj Somorovsky, and Craig Young gave the bug its name, Return Of Bleichenbacher's Oracle Threat, and showed the nineteen-year-old oracle still live across nine vendors, including F5, Citrix, Radware, Palo Alto Networks, IBM, and Cisco, with vulnerable subdomains on 27 of the top 100 domains, Facebook and PayPal among them [26, 27, 7]. To prove the point without crossing a line, the team used the oracle to sign a message with Facebook's private key, and still never recovered that key.

ROBOT "allows performing RSA decryption and signing operations with the private key of a TLS server" -- and yet recovers no private key at all. Its own recommendation is the entire fix thesis in one line: disable RSA encryption cipher suites entirely.

All three padding incidents carry the sharpest version of the thesis: the attacker uses the private key without ever holding it, and the modulus is untouched at the end. The remedy was never a bigger key. It was uniform, constant-time error handling and, ultimately, retiring RSA encryption in favor of OAEP or forward-secret key exchange, exactly as ROBOT advised.

Pillar 3: The negotiation forced a crippled key

FREAK, 2015. Benjamin Beurdouche and seven coauthors found a TLS state-machine flaw with a nasty consequence. A man-in-the-middle could force a handshake down to 512-bit RSA_EXPORT even when neither the client nor the server wanted export-grade crypto. And 512-bit RSA has been factorable since RSA-155 fell in 1999, so the attacker factors the downgraded modulus in hours for about $100 on cloud compute, then impersonates or decrypts the session. At disclosure, 36.7% of browser-trusted HTTPS servers still accepted export RSA [18, 28, 8, 29, 15].

Ctrl + scroll to zoom
FREAK: a man-in-the-middle forces the handshake down to a 512-bit export key neither party asked for, then factors it. The downgrade, not the math, created the weak modulus.

The factoring step is feasible here only because a downgrade plus 1990s export policy forced a deliberately weak key. The math finished a job the deployment set up, and it could not have touched a 2048-bit key the same way. The academic paper, "A Messy State of the Union," has eight authors; never attribute FREAK to a single name [18]. The freakattack.com credit to Karthikeyan Bhargavan and the miTLS team, with tracking by the University of Michigan, is a separate and separately-true fact [8]. The fix was, again, a deployment change, not a cryptographic one: remove export ciphers, disable SSLv2, add downgrade protection, and ultimately TLS 1.3 dropping RSA key exchange entirely.

Three layers, seven field breaks, one shape. Every time, a contract the RSA math depends on but does not control was violated, real keys or sessions or signatures fell, and the factoring problem on a strong modulus did not move. Seen one at a time, each looks like a smartcard bug, a TLS bug, a legacy-protocol bug. Seen together, they are a single pattern, and the pattern is the whole point.

5. Every Fix Changed How RSA Is Generated, Used, or Negotiated -- Never RSA

Stop treating the seven incidents as separate. Line them up and one realization collapses the subject: every field break attacked a layer the math depends on but does not control -- entropy, then the padding check, then negotiated strength -- and every fix changed how RSA is generated, used, or negotiated, never the factoring problem.

The breakthrough here is not a eureka discovery. It is an engineering discipline, visible only because the same shape repeats across a twenty-year drumbeat from 1998 to 2020. Map each fix to its layer and watch none of them touch the primitive:

  • Key generation was answered with validated high-entropy generation plus post-hoc structure and shared-factor testing: FIPS 186-5, batch-GCD scanners, and the ROCA detector [30, 3, 4].
  • Padding was answered with OAEP, uniform constant-time error handling, and, most durably, structurally retiring RSA encryption [31, 7].
  • Negotiation was answered by removing export and SSLv2, adding downgrade protection, and TLS 1.3 deleting static RSA key exchange outright [9].

It is worth dwelling on the padding fix that failed, because its failure is the most instructive event in the whole story. The canonical countermeasure was not hand-waving. TLS 1.2 specified it precisely: on any decryption or padding failure, the server does not signal an error at all. It substitutes a randomly generated premaster secret and proceeds as if nothing were wrong, so the handshake fails uniformly and only later, at the Finished-message MAC, with no observable difference between valid and invalid padding [32]. The RFC even cites Bleichenbacher and the Klima-Pokorny-Rosa version oracle by name.

It removed exactly one channel, the explicit error message. And the same one bit resurfaced through three more: a forgotten SSLv2 sibling (DROWN), implementation quirks like TCP resets and alert timing (ROBOT), and decryption timing (Marvin) [6, 7, 33].

That is the deep lesson. A scheme whose validity is observable cannot be patched uniformly safe; it can only be structurally replaced. The field kept relearning Bleichenbacher's single lesson at a new layer each time an inner defense hardened.

Every field break attacked a layer the math depends on but does not control, and every remedy -- validated entropy and structure tests, OAEP and PSS with uniform errors, dead export and SSLv2, TLS 1.3 -- hardened a deployment obligation. The factoring problem is the fixed point around which everything else evolved. Soundness requires the weakest of three deployment layers to hold, and hardening any one of them is an exercise in usage, not cryptanalysis.

If every fix is "generate, use, or negotiate RSA correctly," then the state of the art is simply the catalog of what "correctly" means at each layer in 2026 -- and where, even now, correct-by-the-book still is not enough.

6. What Correct RSA Deployment Looks Like in 2026

The modern answer is unglamorous, and that is the point: be sound at all three layers at once, because validated key generation does nothing for a leaky padding check, and a perfect padding scheme does nothing for a forced 512-bit downgrade.

Key generation. Use validated, high-entropy, structure-checked generation per FIPS 186-5, published February 2023 [30]. Guarantee real entropy before the first key is generated, use 2048-bit or larger moduli, and run continuous fleet hygiene: batch-GCD across your own keys and the ROCA detector against certified black boxes [3, 4]. Generation and measurement are complementary -- measurement catches what generation missed.

Padding and encryption. Use RSA-OAEP for encryption and RSA-PSS for signatures, both from RFC 8017, with constant-time, uniform-error decryption; better still, avoid RSA key transport altogether [31].

RSA-OAEP and RSA-PSS

OAEP (Optimal Asymmetric Encryption Padding) is a plaintext-aware encryption padding, and PSS is the analogous randomized signature scheme; both are specified in RFC 8017 [31]. OAEP removes the Bleichenbacher oracle as a class, but only when decryption returns one constant-time, generic error for every failure. OAEP decoding has two internally distinct failure cases -- an integer-out-of-range or wrong leading octet, versus an octet-format or integrity failure -- and if an implementation lets them be distinguishable by code, alert, or timing, OAEP itself becomes a padding oracle: Manger's 2001 attack then recovers the plaintext in about log2n\log_2 n queries, far fewer than Bleichenbacher's 10610^6, because the leaked bit is cleaner [34]. PSS is not what the padding oracles break; construction depth belongs to RSA Done Right.

The one-line takeaway is worth memorizing: OAEP done wrong is Bleichenbacher wearing different padding. Even the fix is a deployment obligation. Marvin (2023, Hubert Kario at Red Hat) is a modern timing revival of the v1.5 oracle across many libraries. Its existence and authorship are verified, but keep it at mention weight pending venue and peer-review confirmation, rather than leaning on it as a load-bearing result [33, 35].

Negotiation. Use TLS 1.3, which removes static RSA key exchange entirely: all key exchange is forward-secret (EC)DHE, and RSA survives only as a signature algorithm, with the rationale spelled out in the RFC's Appendix E.8, "Attacks on Static RSA" [9]. SSLv2, export ciphers, and RSA_EXPORT are dead. Forward secrecy is the structural win here: with no RSA key transport on the wire, there is no v1.5 decryption for an oracle to be about. The deployed stacks already reflect this -- mainstream TLS libraries now default to TLS 1.3, and some, like rustls, never implemented the TLS_RSA_* transport suites at all [36].

"Validate generation, use OAEP and PSS, drop RSA key transport" is the whole answer for greenfield code. But engineers inherit constraints -- a device that must self-generate keys, a peer that only speaks v1.5, a fleet already in the field -- so the real question is not "what is best" but "what are my options at each layer, ranked, and exactly when does each apply?"

7. Competing Approaches: How the Field Closes Each Gap

For each of the three loci, the honest picture is competing options with real trade-offs, not a single winner.

PillarOption AOption BOption CMain trade-off
Key generationHardware RNG plus entropy at bootDerandomized key generation from a strong seedPost-hoc structure and shared-factor testingPrevention versus detection; they coexist
PaddingOAEP (safe only with one constant-time generic error)Hardened uniform-error v1.5 (RFC 5246 random premaster)Abandon RSA encryption for (EC)DHECompatibility versus a clean break
DowngradeTLS 1.3 downgrade protection and protocol retirementConfig hardening (TLS_FALLBACK_SCSV, disable export and SSLv2)--A new protocol versus patching the old one

Key generation. Three approaches coexist. A hardware RNG with guaranteed entropy at boot prevents the Debian and shared-prime failures at the source. Derandomized key generation -- deriving the primes from a single strong seed -- removes the runtime-entropy dependency entirely, at the cost of trusting that seed. And post-hoc structure and shared-factor testing (batch-GCD scanners, the ROCA detector) catches what generation missed [3, 4, 30]. Measurement is the backstop for generation: ROCA and Mining Your Ps and Qs were both discovered by scanning the field, not by auditing source.

Padding. Three options, chosen by how much legacy you must carry. Fix the scheme with OAEP -- but recall Manger, that it is only misuse-safe with one constant-time generic error [34]. Or keep v1.5 for compatibility and harden the implementation toward a uniform error, the RFC 5246 random-premaster substitution -- a twenty-five-year losing battle across error text, protocol siblings, implementation quirks, and timing [32]. Or abandon RSA encryption for forward-secret (EC)DHE. Different deployments sit under different compatibility constraints, so all three survive in the field [31, 7].

Downgrade. Two options. TLS 1.3, with built-in downgrade protection and outright protocol and algorithm retirement, is the durable answer. For stacks that cannot move yet, configuration hardening -- TLS_FALLBACK_SCSV, disabling export ciphers and SSLv2 -- is the interim one [9, 6].

Notice the convergence. The durable answer to both Pillar 2 and Pillar 3 is the same move: do not do RSA key transport. The clean surviving split is that RSA-for-signatures (PSS) stays first-class while RSA-for-encryption is retired.

Every one of these options changes how RSA is generated, used, or negotiated, or drops RSA key transport entirely, and each buys its safety with a specific cost -- a scan, a migration, a compatibility break, a forward-secret handshake. Which means the honest way to close the technical arc is to ask what is provably true on both sides: how little the attacker needs, and how much the defender can actually guarantee.

8. What Is Provably True on Both Sides

The thesis has two sides, and each has its own frontier. The surprise is the asymmetry: the math side is a slow, honest, well-mapped concession, and all the operationally relevant limits live in the deployment.

The math side, the honest concession. Factoring is real but slow. The Number Field Sieve runs in heuristic sub-exponential time LN[1/3,(64/9)1/3]L_N[1/3, (64/9)^{1/3}], with (64/9)1/31.923(64/9)^{1/3} \approx 1.923 [37], and the public record has crept forward for two decades.

ChallengeBitsYearEffortMethodDeployed strong key factored?
RSA-1555121999feasible in hours todayNFSNever
RSA-7687682009~1500 core-years sievingNFSNever
RSA-2407952019~900 core-yearsNFSNever
RSA-2508292020~2700 core-yearsGNFS (CADO-NFS)Never
RSA-20482048--astronomically out of reach classically--Never

Every row is a challenge number from the old RSA Factoring Challenge, a public target list RSA Laboratories once published [38], and none was ever a deployed key [15, 16, 39, 37, 1]. 512-bit is hours, which is exactly why FREAK works; 2048-bit is out of classical reach; and Shor's algorithm collapses all of it on a quantum computer -- a story the companion How RSA Would Break owns. Read the rightmost column top to bottom: it says Never at every row, because no strong, correctly generated, deployed modulus has ever been factored in the field. RSA-250's roughly 2700 core-years split into about 2450 for sieving and 250 for the matrix step, run on Intel Xeon Gold 6130 cores with CADO-NFS. It is a 250-digit, 829-bit challenge number, and it never protected a deployed system [1].

The Heninger through-line closes here. The researcher who recovered weak keys by the thousand in Pillar 1 co-holds this record -- the slow, honest way a strong key falls [1]. That is the entire weak-versus-strong distinction compressed into one career. And note the theory is not even settled in the defender's favor: there is no proof that factoring requires super-polynomial time, and the RSA-problem-versus-factoring equivalence is itself open [11]. Two-plus decades of open cryptanalysis is an empirical floor, not a theorem -- and it never once mattered in the field.

The deployment side, where the real limits live. Here the limits are provable-in-practice, and none is about a strong modulus's strength.

AttackAttacked layerWorst-case workNeeds a weak modulus?Recovers the private key?
Padding oracle (Bleichenbacher family)PaddingAbout 10610^6 queries, polynomialNoNo
Batch-GCD shared primesKey generationOne gcd, quasi-linear over the fleetYesYes
ROCA and CoppersmithKey generationLattice reduction, polynomialYesYes
FREAK downgrade then factorNegotiationHours to factor 512-bitYes (forced)Yes
GNFS on a strong modulusThe math itselfSub-exponential, ~2700 core-years at 829-bit--Only the challenge key

Three boundaries, stated outright. First, a padding oracle needs no factoring: its query count is bounded below only by the roughly one-bit-per-conforming-reply information rate, so decryption is many queries but always polynomial, and you cannot make an observable-validity scheme require exponentially many queries [5, 34]. Second, a shared prime is a single gcd away and a structured prime a Coppersmith lattice away -- quasi-linear or polynomial, regardless of key size [3, 4]. Third, certification is not structural testing -- the ROCA lesson from Pillar 1, where FIPS 140-2 and CC EAL5+ chips shipped structurally factorable keys for years [4].

The math side is a slow, mapped concession -- an 829-bit public ceiling, and no strong deployed key ever factored. The deployment side holds the real limits: a padding oracle needs no factoring, a shared prime is one gcd, a structured prime one lattice, a downgrade one forced 512-bit key -- all independent of key size. "Strong-modulus-secure" and "system-secure" are different claims, and the gap between them is inherent, not accidental.

If the math side is a slow, mapped concession and the deployment side has hard, polynomial-cost limits, the practical frontier is obvious: it is wherever those deployment limits are still being hit in the wild. That is not a solved problem. It is an active one.

9. Where RSA Still Breaks in the Field

The math side is a slow, honest concession. The deployment side is not closed. Here are the places the same three-pillar pattern is still live, each an operational frontier, each consistent with the thesis that the weak link is a layer the math depends on, never the factoring problem.

Entropy on embedded, first-boot, and cloned systems (Pillar 1). The exact Debian and shared-prime gap, revived by cloud VM cloning and container images that reproduce identical state at key-generation time [40]. The best partial answer is FIPS 186-5 generation plus fleet batch-GCD, but coverage of cloned and containerized keys is incomplete [3, 30].

The PKCS#1 v1.5 long tail and timing oracles (Pillar 2). v1.5 is still specified and everywhere. Marvin (2023) showed the timing axis is still open across many libraries, tied to the working impossibility result that an observable-validity scheme cannot be patched uniformly safe, only structurally replaced [33, 35, 7].

Protocol and algorithm-retirement lag (Pillar 3). How does a dead algorithm -- SSLv2, export RSA, v1.5 -- stay live for a decade or more? DROWN fell from 33% in 2016 to about 1.2% by 2019, and FREAK from 36.7% toward the low single digits: large but incomplete progress [6, 8]. This is a socio-technical problem, not a cryptanalytic one.

The next ROCA-class library (Pillar 1). Detecting a structurally biased generator inside certified closed hardware, before it ships millions of keys. The ROCA detector is signature-based, so a genuinely new structure would evade it [4].

Post-quantum signature migration ahead of Shor. This is the one future break that is the math. Key exchange is already forward-secret, but signatures need proactive migration; the mechanics belong to the post-quantum and would-break siblings.

One boundary is worth fencing so it is never miscounted as a field incident. Wiener's small-dd attack recovers a too-small private exponent d<13n1/4d < \tfrac{1}{3} n^{1/4} from the public key by continued fractions (1990) [41], and Hastad's low-exponent broadcast recovers the same unpadded low-ee message sent to ee recipients via CRT and an integer ee-th root (1988) [42]. Both are real, but they are parameter-choice, theoretical breaks -- the canonical argument for randomized padding -- not documented field incidents. They belong to How RSA Would Break. Parameter-choice attacks on deliberately bad exponents are a different genre from the deployment failures cataloged here.

Every open problem here is the same sentence in new clothes: a contract the RSA math depends on is hard to keep in the real world. Which means the practical guide writes itself. It is the thesis made operational, each rule routed to the incident it prevents.

10. What to Do on Monday

Everything above collapses into a short decision procedure and a shorter list of nevers, each rule tied to the incident it prevents.

Ctrl + scroll to zoom
The 2026 deployment decision tree: secure all three layers in order, because any one of them failing is sufficient for a break.

Key generation. Guarantee real entropy before the first key is generated; follow FIPS 186-5; never generate keys at unseeded first boot; use a 2048-bit minimum with validated generation; and run batch-GCD and the ROCA detector across your keys and CT logs, regenerating or revoking anything flagged. This prevents Debian enumeration, shared primes, and ROCA [30, 3, 4].

Padding and encryption. Never use raw PKCS#1 v1.5. If you must do RSA encryption, use OAEP with constant-time, uniform-error handling -- one generic error, per Manger; better still, prefer (EC)DHE forward secrecy and drop RSA key transport, which TLS 1.3 does for you. This prevents Bleichenbacher, DROWN, ROBOT, and Marvin [31, 34, 9].

Signatures. Use RSA-PSS, which is not what the oracles break, and plan the post-quantum signature migration ahead of Shor [31].

Negotiation. Disable SSLv2, export ciphers, and RSA_EXPORT; enable downgrade protection; prefer TLS 1.3 over 1.2. This prevents FREAK and DROWN's SSLv2 channel [9, 6].

Do this......and you reproduceThe fix
Generate keys at unseeded first bootDebian enumeration, shared primesReal entropy before keygen; batch-GCD
Trust a certified black box as structurally safeROCAROCA detector; regenerate
Reveal padding validity by error, reset, timeout, or timingBleichenbacher, DROWN, ROBOT, MarvinUniform constant-time errors; OAEP; drop RSA transport
Reuse one RSA key across protocolsDROWN, via SSLv2Separate keys; disable SSLv2
Leave export ciphers or SSLv2 liveFREAK, DROWNRemove export and SSLv2; downgrade protection
Assume "2048-bit means safe"Every non-factoring breakSecure all three layers, not the key size

The batch-GCD check is not just an attacker's tool; it is your fleet audit. Run it against your own moduli:

JavaScript A paste-in fleet audit: flag any two keys that share a factor
const gcd = (a, b) => { while (b) { [a, b] = [b, a % b]; } return a; };
// Public moduli scraped from your own devices or CT logs.
const fleet = {
'host-a': 63900000000000000000000000000000000000000000002625100000000000000000000000000000000000000000026931n,
'host-b': 74700000000000000000000000000000000000000000003196300000000000000000000000000000000000000000034189n,
'host-c': 35510000000000000000000000000000000000000000001968800000000000000000000000000000000000000000010257n,
'host-d': 18130000000000000000000000000000000000000000001447800000000000000000000000000000000000000000026649n,
};
const names = Object.keys(fleet);
let flagged = 0;
for (let i = 0; i < names.length; i++) {
for (let j = i + 1; j < names.length; j++) {
  const g = gcd(fleet[names[i]], fleet[names[j]]);
  if (g > 1n) {
    flagged++;
    console.log('WEAK PAIR:', names[i], '&', names[j], '-> shared factor', g.toString());
  }
}
}
console.log(flagged ? ('Regenerate and revoke ' + flagged + ' compromised pair(s).') : 'No shared factors found.');

Press Run to execute.

Turning that into a real audit

To feed real keys in, extract each modulus first with a one-liner like openssl x509 -in cert.pem -noout -modulus, collect them into the list above, and run it. Any pair with a gcd greater than 1 is a compromised pair: regenerate and revoke both, then find out why two devices drew the same prime.

The checklist is short because the lesson is one sentence. Before restating it, let us clear the handful of confident, wrong sentences that keep these bugs alive in design meetings.

11. Misconceptions, Precisely Corrected

The paradox survives mostly because a few plausible, wrong statements keep getting repeated. Here they are, corrected, each answer tied back to a catalog entry and the three-pillar thesis.

Frequently asked questions

Has anyone ever factored real RSA?

Only weak keys: 512-bit export keys (FREAK), shared-prime keys, and ROCA-structured keys. The public challenge ceiling is the 829-bit RSA-250, which took about 2,700 core-years in 2020. No strong, correctly generated, deployed modulus has ever been factored [1, 8].

Does Bleichenbacher or ROBOT recover the private key?

No. It is a decryption or signing oracle: the attacker decrypts one session or forges one signature, but the key is never extracted and the modulus is never factored [7, 5].

Is v1.5 signing broken the way v1.5 encryption is?

No -- it is a different scheme. The padding oracle is on v1.5 encryption. PSS signatures, and even v1.5 signatures, are not what falls. Keep the two constructions distinct [31].

Is there an RSA nonce-reuse break like ECDSA's?

No. RSA signing has no secret per-signature nonce. The per-signature kk-reuse break behind the PlayStation 3 and Android incidents is an (EC)DSA phenomenon, told in its own article [12, 14].

Is a bigger key size the fix?

Not for padding oracles, shared or structured primes, or downgrade -- those are deployment defects, independent of key size. Size only matters against brute factoring, which is exactly why 512-bit export was fatal and a strong key is not [3, 4].

We are FIPS or Common Criteria certified. Are we safe?

Not necessarily. ROCA's keys were CC EAL5+ certified; certification tested the RNG, not the public-key structure, and roughly 760,000 factorable keys shipped anyway [4].

Is RSA broken?

The primitive, no. But stop using RSA key transport and raw PKCS#1 v1.5, prefer forward-secret (EC)DHE and OAEP or PSS, and plan the post-quantum signature migration ahead of Shor [9, 31].

Every correction points at the same root: the factoring problem was never the weak link. Time to say the sentence the whole article was built to earn.

To Break RSA in the Field, You Never Factor a Strong Modulus

Return to the opening paradox, now resolved. The public factoring ceiling is an 829-bit challenge number that cost about 2,700 core-years, and no strong deployed key has ever fallen to it [1]. And yet national ID cards were forgeable, a third of HTTPS was decryptable, embedded keys were recoverable, and sessions were man-in-the-middled -- because every one of those breaks happened at a layer the factoring problem knows nothing about.

The primes were weak before the math began: Debian, shared primes, ROCA. The padding check was an oracle: Bleichenbacher, DROWN, ROBOT. The negotiation forced a crippled key: FREAK. Three layers, seven deployed field breaks, and the factoring problem on a strong modulus untouched in all of them. RSA-250, the eighth row of the catalog, is the ceiling -- a strong-but-undeployed challenge number, not a field break.

Every fix changed how RSA is generated, used, or negotiated -- validated entropy and structure tests, OAEP and PSS and uniform errors, dead export and SSLv2 and TLS 1.3 -- never RSA. And where three incidents did end in a factorization, the deployment had already produced a weak modulus, so the math merely finished a job it had been handed.

When your RSA keys fall, don't ask whether someone factored the modulus. Ask which layer the math depends on but does not control gave way: the primes, the padding, or the negotiation.

One last time, keep the hedge honest: almost never, not never. Factoring is real and fell RSA-512, RSA-768, and RSA-250 as challenge numbers, and Shor's algorithm is coming for strong keys on a quantum computer -- but that is the story How RSA Would Break tells, and the constructive OAEP and PSS depth belongs to RSA Done Right.

The factoring problem was never the weak link in the field, and no bigger key would have saved a single one of these deployments. That is why this is Part 3 of a series about how things break in real life, not a chapter on the Number Field Sieve.

Study guide

Key terms

RSA trapdoor permutation
n = p times q with public (n, e) and private d; factoring is sufficient to break RSA and remains the best known attack, while whether inverting RSA is as hard as factoring is an open question.
Textbook (raw) RSA and malleability
Unpadded RSA is deterministic and malleable, so multiplying a ciphertext by s to the e multiplies the plaintext by s; it must be padded before use.
Entropy and CSPRNG seeding
A generator's output is only as unpredictable as its seed; a starved, crippled, or biased seed yields predictable, enumerable, or colliding primes.
Shared-factor (batch-GCD) recovery
If two moduli share a prime, one gcd factors both; a batch-GCD computes every pairwise gcd across millions of keys at once.
PKCS#1 v1.5 padding
The 00 02 encryption format wrapped around a message; its validity check is what becomes Bleichenbacher's oracle.
Padding oracle
A decryptor that reveals whether padding was valid leaks one bit per query; enough adaptive queries decrypt a message without the private key.
Coppersmith's method
Finds small roots of a polynomial modulo N in polynomial time; when primes are structured it factors n far faster than general factoring, the engine behind ROCA.
Export-grade RSA
Deliberately weak 512-bit RSA_EXPORT key exchange and the obsolete SSLv2 protocol, retired in principle but live long enough to power FREAK and DROWN.
RSA-OAEP and RSA-PSS
The modern encryption and signature paddings; OAEP removes the Bleichenbacher oracle only when decryption returns one constant-time generic error, per Manger.

References

  1. F. Boudot, P. Gaudry, A. Guillevic, N. Heninger, E. Thome, & P. Zimmermann (2020). Factorization of RSA-250. https://caramba.loria.fr/rsa250.txt
  2. NIST NVD (2008). CVE-2008-0166. https://nvd.nist.gov/vuln/detail/CVE-2008-0166
  3. N. Heninger, Z. Durumeric, E. Wustrow, & J. A. Halderman (2012). Mining Your Ps and Qs: Detection of Widespread Weak Keys in Network Devices. https://factorable.net/
  4. M. Nemec, M. Sys, P. Svenda, D. Klinec, & V. Matyas (2017). The Return of Coppersmith's Attack (CRoCS project page). https://crocs.fi.muni.cz/public/papers/rsa_ccs17
  5. D. Bleichenbacher (1998). Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1. https://archiv.infsec.ethz.ch/education/fs08/secsem/Bleichenbacher98.pdf
  6. N. Aviram & et al. (2016). DROWN: Breaking TLS Using SSLv2. https://drownattack.com/
  7. H. Bock, J. Somorovsky, & C. Young (2017). The ROBOT Attack. https://robotattack.org/
  8. K. Bhargavan, miTLS, & University of Michigan (2015). FREAK: Factoring RSA Export Keys. https://freakattack.com/
  9. E. Rescorla (2018). The Transport Layer Security (TLS) Protocol Version 1.3 (RFC 8446). https://datatracker.ietf.org/doc/html/rfc8446
  10. R. Rivest, A. Shamir, & L. Adleman (1978). A Method for Obtaining Digital Signatures and Public-Key Cryptosystems. https://doi.org/10.1145/359340.359342
  11. D. Boneh (1999). Twenty Years of Attacks on the RSA Cryptosystem. https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf
  12. bushing, marcan, & sven (2010). Console Hacking 2010: PS3 Epic Fail (27C3). https://media.ccc.de/v/27c3-4087-en-console_hacking_2010
  13. A. Klyubin (2013). Some SecureRandom Thoughts. https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
  14. Bitcoin Project (2013). Android Security Vulnerability. https://bitcoin.org/en/alert/2013-08-11-android
  15. S. Cavallar & et al. (2000). Factorization of a 512-bit RSA Modulus. https://www.iacr.org/archive/eurocrypt2000/1807/18070001-new.pdf
  16. T. Kleinjung & et al. (2010). Factorization of RSA-768 (EPFL announcement). https://documents.epfl.ch/users/l/le/lenstra/public/papers/rsa768.txt
  17. B. Kaliski (1998). PKCS #1: RSA Encryption Version 1.5 (RFC 2313). https://www.rfc-editor.org/info/rfc2313
  18. B. Beurdouche, K. Bhargavan, A. Delignat-Lavaud, C. Fournet, M. Kohlweiss, A. Pironti, P.-Y. Strub, & J. K. Zinzindohoue (2015). A Messy State of the Union: Taming the Composite State Machines of TLS. https://doi.org/10.1109/SP.2015.39
  19. Debian Project (2008). DSA-1571-1 openssl -- predictable random number generator. https://www.debian.org/security/2008/dsa-1571
  20. F. Weimer (2008). DSA-1571-1 openssl (full advisory text). https://lists.debian.org/debian-security-announce/2008/msg00152.html
  21. S. Yilek, E. Rescorla, H. Shacham, B. Enright, & S. Savage (2009). When Private Keys are Public: Results from the 2008 Debian OpenSSL Vulnerability. https://hovav.net/ucsd/papers/yrses09.html
  22. A. K. Lenstra, J. P. Hughes, M. Augier, J. W. Bos, T. Kleinjung, & C. Wachter (2012). Ron was wrong, Whit is right. https://eprint.iacr.org/2012/064
  23. M. Nemec, M. Sys, P. Svenda, D. Klinec, & V. Matyas (2017). The Return of Coppersmith's Attack: Practical Factorization of Widely Used RSA Moduli. https://dl.acm.org/doi/10.1145/3133956.3133969
  24. MITRE (2017). CVE-2017-15361 (ROCA / Infineon RSALib). https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15361
  25. D. Coppersmith (1997). Small Solutions to Polynomial Equations and Low Exponent RSA Vulnerabilities. https://doi.org/10.1007/s001459900028
  26. H. Bock, J. Somorovsky, & C. Young (2018). Return Of Bleichenbacher's Oracle Threat (ROBOT). https://www.usenix.org/conference/usenixsecurity18/presentation/bock
  27. H. Bock, J. Somorovsky, & C. Young (2017). Return Of Bleichenbacher's Oracle Threat (ROBOT) -- ePrint version. https://eprint.iacr.org/2017/1189
  28. B. Beurdouche & et al. (2015). A Messy State of the Union: Taming the Composite State Machines of TLS (open-access PDF). https://www.ieee-security.org/TC/SP2015/papers-archived/6949a535.pdf
  29. MITRE (2015). CVE-2015-0204 (FREAK / RSA_EXPORT downgrade). https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-0204
  30. NIST (2023). FIPS 186-5: Digital Signature Standard (DSS). https://csrc.nist.gov/pubs/fips/186-5/final
  31. K. Moriarty, B. Kaliski, J. Jonsson, & A. Rusch (2016). PKCS #1: RSA Cryptography Specifications Version 2.2 (RFC 8017). https://www.rfc-editor.org/info/rfc8017
  32. T. Dierks & E. Rescorla (2008). The Transport Layer Security (TLS) Protocol Version 1.2 (RFC 5246). https://www.rfc-editor.org/info/rfc5246
  33. H. Kario (2023). Everlasting ROBOT: the Marvin Attack. https://people.redhat.com/~hkario/marvin/marvin-attack-paper.pdf
  34. J. Manger (2001). A Chosen Ciphertext Attack on RSA OAEP as Standardized in PKCS #1 v2.0. https://www.iacr.org/archive/crypto2001/21390229.pdf
  35. IACR (2023). The Marvin Attack (IACR news item). https://iacr.org/news/item/21604
  36. rustls Project (2025). rustls manual: protocol features and non-features. https://docs.rs/rustls/latest/rustls/manual/_04_features/index.html
  37. F. Boudot, P. Gaudry, A. Guillevic, N. Heninger, E. Thome, & P. Zimmermann (2019). 795-bit Factoring and Discrete Logarithms (RSA-240 / DLP-240). https://caramba.loria.fr/dlp240-rsa240.txt
  38. Wikipedia contributors (2024). RSA Factoring Challenge. https://en.wikipedia.org/wiki/RSA_Factoring_Challenge
  39. T. Kleinjung & et al. (2010). Factorization of a 768-bit RSA Modulus. https://eprint.iacr.org/2010/006
  40. T. Ristenpart & S. Yilek (2010). When Good Randomness Goes Bad: Virtual Machine Reset Vulnerabilities and Hedging Deployed Cryptography. https://www.ndss-symposium.org/ndss2010/when-good-randomness-goes-bad-virtual-machine-reset-vulnerabilities-and-hedging-deployed/
  41. M. J. Wiener (1990). Cryptanalysis of Short RSA Secret Exponents. https://doi.org/10.1109/18.54902
  42. J. Hastad (1988). Solving Simultaneous Modular Equations of Low Degree. https://doi.org/10.1137/0217019