CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-21895
7.50.02%

One is the Loneliest Number: Crashing Rust's RSA Crate

Alon Barad
Alon Barad
Software Engineer

Feb 24, 2026·6 min read·12 visits

PoC Available

Executive Summary (TL;DR)

The `rsa` crate (< 0.9.10) failed to validate that prime factors ($p$ and $q$) were strictly greater than 1. By submitting a crafted RSA key where a prime is set to `1`, an attacker can force the library to attempt modular arithmetic with a modulus of zero (or similar invalid operations), triggering a thread-killing panic. This affects downstream projects like `rPGP`, effectively allowing remote unauthenticated attackers to crash applications processing PGP keys.

A logic error in the validation of RSA private key components within the `rsa` Rust crate allows attackers to trigger a Denial of Service (DoS) by supplying the integer `1` as a prime factor. While Rust is famed for memory safety, it cannot save developers from basic arithmetic fallacies; specifically, allowing a prime factor of 1 results in division-by-zero panics deep within the cryptographic math engine.

The Hook: The Integrity of One

In the realm of cryptography, we obsess over large numbers. We want our primes to be massive, un-factorable giants that would take the heat death of the universe to reverse-engineer. But in our quest for bigness, we often forget to check the basement. What happens when the numbers are too small? Specifically, what happens when a prime factor is the multiplicative identity: the number 1?

The rsa crate is the de facto standard for pure-Rust RSA implementations. It is memory-safe, fast, and generally robust. However, even the safest languages allow you to shoot yourself in the foot if you ignore the laws of mathematics. RSA relies on the premise that $n = p \times q$, where $p$ and $q$ are distinct primes. If $p=1$, the math doesn't just get weak; it breaks fundamental assumptions of the algorithm.

This vulnerability isn't a complex buffer overflow or a heap grooming masterpiece. It's a failure to ask a simple question: "Is this number actually big enough to be a prime?" The answer, for versions prior to 0.9.10, was a resounding "meh, close enough."

The Flaw: Math is Hard, Bounds Checks are Harder

The vulnerability lies within src/key.rs, specifically in RsaPrivateKey::from_components. When constructing a private key from raw components (modulus, public exponent, private exponent, and primes), the library performs a sanity check to ensure the inputs are valid. Or at least, it tries to.

Here is the logic that was supposed to protect us:

// The offending code
for prime in &self.primes {
    if *prime < BigUint::one() { 
        // This only catches 0, not 1
        return Err(Error::InvalidPrime);
    }
    m *= prime;
}

Do you see it? The code checks if the prime is strictly less than one. In integer arithmetic, the only non-negative integer strictly less than 1 is 0. If an attacker passes 1, the check 1 < 1 evaluates to false, and the loop continues happily, accepting 1 as a valid prime factor.

Why does this matter? Because RSA private key operations—specifically the Chinese Remainder Theorem (CRT) optimizations—rely on calculating values modulo $(p-1)$. If $p=1$, then $(p-1)=0$. You don't need a PhD in math to know that modulo zero is a one-way ticket to Panic City.

The Code: The Smoking Gun

The fix, applied in commit 2926c91bef7cb14a7ccd42220a698cf4b1b692f7, is almost embarrassingly simple. It transforms a loose check into a strict boundary.

The Vulnerable Logic:

if *prime < BigUint::one() {
    return Err(Error::InvalidPrime);
}

The Fixed Logic:

if *prime <= BigUint::one() {
    return Err(Error::InvalidPrime);
}

By adding that single equals sign (=), the developers ensured that 1 is rejected. Additionally, the patch included an upgrade to the underlying num-bigint-dig library (from 0.8.4 to 0.8.6), which hardened the BigInt arithmetic against internal panics. This two-pronged approach fixed the logic error at the gate and reinforced the walls against the crash.

The Exploit: Panic at the Keyring

To exploit this, we don't need a debugger or a shellcode encoder. We just need a text editor or a simple script to modify a serialized key. The most prominent target for this exploit is the PGP ecosystem, specifically applications using the pgp crate (which depends on rsa).

The Attack Chain:

  1. Craft the Packet: The attacker generates a standard OpenPGP Secret Key packet. These packets contain the RSA private key components.
  2. The Modification: The attacker locates the bytes corresponding to prime1 (p) or prime2 (q) and overwrites them to represent the integer 1.
  3. Delivery: The attacker uploads this "poisoned" key to a keyserver, sends it via email to an automated gateway, or simply serves it to a client application.
  4. The Crash: When the victim application parses the packet, it calls RsaPrivateKey::from_components. The validation passes (because $1 \not< 1$). The application then attempts to use the key. As soon as it attempts to calculate CRT parameters (like $dP = d \pmod{p-1}$), the BigUint library attempts a division by zero.

In Rust, a division by zero in the main thread (or an uncaught thread) results in a panic!. The process terminates immediately. For a web server or a key processing daemon, this is a total service outage.

The Impact: Why Should We Panic?

You might look at the CVSS score of 2.7 (assigned by some CNAs) and think, "Low severity? I'll patch it next month." Do not be deceived. That score assumes a generic context where the attacker has no path to the vulnerability. In the real world, cryptographic libraries parse untrusted data by design.

If you are running an OpenPGP keyserver, an email encryption gateway, or an identity provider that accepts user-uploaded certificates, this is a Critical vulnerability. A single malicious packet can take down your entire ingestion pipeline. Since Rust panics unwind the stack, unless you have extremely robust panic catching at the worker level (which many async runtimes do, but not all logic paths guarantee), your service goes dark.

It is a trivial, asymmetric attack: low effort for the attacker, high availability impact for the defender. It turns your robust Rust application into a fragile house of cards.

The Fix: Restoring Sanity

The remediation is straightforward, thanks to Rust's package management. You need to ensure your dependency tree is using a safe version of the rsa crate.

Immediate Action: Run the following in your project root:

cargo update -p rsa

Verify that Cargo.lock shows rsa version 0.9.10 or higher.

For Downstream Users: If you use rPGP or other high-level wrappers, check their advisories. Most have already bumped their dependency versions. If you are pinning dependencies (bad practice, but it happens), unpin them to allow the patch to flow through.

> [!NOTE] > If you cannot upgrade immediately, you must implement manual validation of all RSA components before passing them to the rsa crate. Ensure p > 1 and q > 1.

Official Patches

RustCryptoCommit fixing the prime validation logic

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P
EPSS Probability
0.02%
Top 95% most exploited

Affected Systems

Rust `rsa` crate < 0.9.10rPGP (Rust OpenPGP implementation)RustCrypto ecosystem downstream consumers

Affected Versions Detail

Product
Affected Versions
Fixed Version
rsa
RustCrypto
< 0.9.100.9.10
pgp
rPGP
Affected via dependencyDependent on rsa update
AttributeDetail
CWE IDCWE-703
Attack VectorNetwork (Remote)
CVSS Base7.5 (High - Downstream)
ImpactDenial of Service (Panic)
Exploit StatusPoC Available
Root CauseIncorrect Comparison Operator

MITRE ATT&CK Mapping

T1499.004Endpoint Denial of Service: Application Exhaustion
Impact
T1552.004Unsecured Credentials: Private Keys
Credential Access
CWE-703
Improper Check or Handling of Exceptional Conditions

Improper Check or Handling of Exceptional Conditions

Known Exploits & Detection

Research ReportPanic at the Keyring: Exploiting RSA crate validation logic

Vulnerability Timeline

Patch committed to RSA repository
2026-01-06
CVE-2026-21895 Published
2026-01-08
Downstream rPGP advisory published
2026-02-13
Public PoC details released
2026-02-14

References & Sources

  • [1]GHSA Advisory for RSA Crate
  • [2]GHSA Advisory for rPGP

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.