RSA with Non-Coprime Exponent (e Shares a Factor with φ(N))
- •
What it is
- •
Standard RSA decryption requires , which only exists when . If instead , no single decryption exponent recovers the message uniquely — but decryption isn't hopeless: factor out of to get . If (common once the "bad" factor is stripped out), a partial inverse exists, and raising the ciphertext to recovers the true message up to an ambiguity of exactly possible values: . Enumerating all roots of unity and multiplying each against gives every candidate plaintext; the real one is picked out by checking which decodes to readable text (or a known prefix).
- •
- •
When to apply
- •
An RSA challenge deliberately generates (or leaks a modulus) such that — a tell is being composite and unusual, or the source explicitly warning the implementation is "broken."
- •
- •
Math
- •
With and : ; satisfies . When is prime, the -th roots of unity mod form a cyclic subgroup, generated by for random — provided has order exactly , not a proper divisor of it. Checking for every prime factor of before using it avoids silently enumerating too few candidates.
- •
- •
Worked example
- •
Toy 48-bit prime with (so exactly) and : recovering a partial inverse mod , then enumerating all 8 roots of unity (checking each candidate root has order exactly 8, not 2 or 4) correctly reproduced the true message among the 8 candidates every trial. A naive check of just "" occasionally selected an order-2 or order-4 root instead, producing only 2 or 4 distinct candidates and silently missing the true message — an easy trap to fall into.
- •
- •
Python
- •
from math import gcd d0 = gcd(e, n - 1) # n prime, phi(n) = n-1 k = (n - 1) // d0 a = pow(e, -1, k) m0 = pow(c, a, n) def order_d0_root(d0): while True: h = random.randrange(2, n - 2) r = pow(h, (n - 1) // d0, n) if all(pow(r, d0 // q, n) != 1 for q in prime_factors(d0)): return r r = order_d0_root(d0) candidates = {(m0 * pow(r, i, n)) % n for i in range(d0)}
- •
- •
SageMath
- •
Not particularly relevant — Python's
pow(x, -1, m)already handles the modular inverse cleanly. (A more general Sage-friendly variant — usinginverse_modand brute-collecting roots of unity — handles the case where e isn't a clean prime power, useful if d0 has multiple distinct prime factors.)
- •
- •
Related
- •
Cards
- •
What condition on e and φ(N) breaks the uniqueness of standard RSA decryption?
- •
gcd(e, φ(N)) > 1 — no single modular inverse of e exists mod φ(N).
- •
- •
How many candidate plaintexts does this ambiguity produce, and how do you find the real one?
- •
Exactly gcd(e, φ(N)) candidates (the d0-th roots of unity mod N); the real one is identified by checking which decodes to sensible text.
- •
- •