Wiener's attack is an attack on RSA that uses continued fractions to find the private exponent
Wiener's attack is based on the following theorem:
Let
In order to better understand Wiener's Attack, it may be useful to take note of certain properties of RSA:
We may start by noting that the congruence
Dividing the former equation by
It is possible to quickly factor
Wiener's attack works by expanding
- Since
$$\phi(n)$$ is even, and$$e$$ and$$d$$ are both by definition coprime to$$\phi(n)$$ , we know that$$d$$ is odd. - Given the above equations and the values of
$$e$$ ,$$n$$ ,$$d$$ , and$$k$$ , we can solve for$$\phi(n)$$ with the equation$$\phi(n) = \frac{ed-1}{k}$$ , thus we know that$$ed-1$$ has to be divisible by$$k$$ . - If our
$$\phi(n)$$ is correct, the polynomial$$x^2 - (n - \phi(n) + 1)x + n$$ will have roots$$p$$ and$$q$$ , which we can verify by checking if$$pq = n$$ .
Suppose we have the public key
- Convert the fraction
$$\frac{e}{n}$$ into a continued fraction$$[a_0;a_1,a_2, \ldots , a_{k-2},a_{k-1}, a_k]$$ - Iterate over each convergent in the continued fraction:
$$\frac{a_{0}}{1},a_{0} + \frac{1}{a_{1}},a_{0} + \frac{1}{a_{1} + \frac{1}{a_{2}}}, \ \ldots, a_{0} + \frac{1}{a_{1} + \frac{\ddots} {a_{k-2} + \frac{1}{a_{k-1}}}},$$ - Check if the convergent is
$$\frac{k}{d}$$ by doing the following:- Set the numerator to be
$$k$$ and denominator to be$$d$$ - Check if
$$d$$ is odd, if not, move on to the next convergent - Check if
$$ed \equiv 1 \mod k$$ , if not, move on to the next convergent - Set
$$\phi(n) = \frac{ed-1}{k}$$ and find the roots of the polynomial$$x^2 - (n - \phi(n) + 1)x + n$$ - If the roots of the polynomial are integers, then we've found
$$d$$ . (If not, move on to the next convergent)
- Set the numerator to be
- If all convergents have been tried, and none of them work, then the given RSA parameters are not vulnerable to Wiener's attack.
Here's a sage implementation to play around with:
from Crypto.Util.number import long_to_bytes
def wiener(e, n):
# Convert e/n into a continued fraction
cf = continued_fraction(e/n)
convergents = cf.convergents()
for kd in convergents:
k = kd.numerator()
d = kd.denominator()
# Check if k and d meet the requirements
if k == 0 or d%2 == 0 or e*d % k != 1:
continue
phi = (e*d - 1)/k
# Create the polynomial
x = PolynomialRing(RationalField(), 'x').gen()
f = x^2 - (n-phi+1)*x + n
roots = f.roots()
# Check if polynomial as two roots
if len(roots) != 2:
continue
# Check if roots of the polynomial are p and q
p,q = int(roots[0][0]), int(roots[1][0])
if p*q == n:
return d
return None
# Test to see if our attack works
if __name__ == '__main__':
n = 6727075990400738687345725133831068548505159909089226909308151105405617384093373931141833301653602476784414065504536979164089581789354173719785815972324079
e = 4805054278857670490961232238450763248932257077920876363791536503861155274352289134505009741863918247921515546177391127175463544741368225721957798416107743
c = 5928120944877154092488159606792758283490469364444892167942345801713373962617628757053412232636219967675256510422984948872954949616521392542703915478027634
d = wiener(e,n)
assert not d is None, "Wiener's attack failed :("
print(long_to_bytes(int(pow(c,d,n))).decode())
//TODO: Proof of Wiener's theorem
The Python module owiener
simplifies the scripting process of Wiener's attack:
{% embed url="https://github.com/orisano/owiener" caption="owiener" %}
Here is a Wiener's attack template:
#!/usr/bin/env python3
import owiener
from Crypto.Util.number import long_to_bytes
#--------Data--------#
N = <N>
e = <e>
c = <c>
#--------Wiener's attack--------#
d = owiener.attack(e, N)
if d:
m = pow(c, d, N)
flag = long_to_bytes(m).decode()
print(flag)
else:
print("Wiener's Attack failed.")