72 lines
1.8 KiB
Python
72 lines
1.8 KiB
Python
import math
|
|
|
|
def continued_fraction(numerator, denominator):
|
|
"""Generate the continued fraction expansion of numerator/denominator."""
|
|
cf = []
|
|
while denominator:
|
|
a = numerator // denominator
|
|
cf.append(a)
|
|
numerator, denominator = denominator, numerator - a * denominator
|
|
return cf
|
|
|
|
|
|
def convergents_from_cf(cf):
|
|
"""Generate convergents (k, d) from a continued fraction sequence cf."""
|
|
n0, d0 = cf[0], 1
|
|
yield (n0, 1)
|
|
if len(cf) == 1:
|
|
return
|
|
n1 = cf[1] * cf[0] + 1
|
|
d1 = cf[1]
|
|
yield (n1, d1)
|
|
for i in range(2, len(cf)):
|
|
ni = cf[i] * n1 + n0
|
|
di = cf[i] * d1 + d0
|
|
yield (ni, di)
|
|
n0, d0, n1, d1 = n1, d1, ni, di
|
|
|
|
|
|
def is_perfect_square(x):
|
|
"""Check whether x is a perfect square."""
|
|
if x < 0:
|
|
return False
|
|
s = math.isqrt(x)
|
|
return s * s == x
|
|
|
|
|
|
def wiener_attack(e, n):
|
|
"""
|
|
Attempt to recover the RSA private exponent d using Wiener's attack.
|
|
|
|
Args:
|
|
e: Public exponent
|
|
n: Modulus
|
|
|
|
Returns:
|
|
If successful, returns the private exponent d; otherwise returns None.
|
|
"""
|
|
cf = continued_fraction(e, n)
|
|
for k, d in convergents_from_cf(cf):
|
|
if k == 0:
|
|
continue
|
|
|
|
# Check whether (e*d - 1) is divisible by k to derive phi
|
|
if (e * d - 1) % k != 0:
|
|
continue
|
|
phi = (e * d - 1) // k
|
|
|
|
# Discriminant of x^2 - (n - phi + 1)x + n = 0
|
|
s = n - phi + 1
|
|
discr = s * s - 4 * n
|
|
|
|
if discr >= 0 and is_perfect_square(discr):
|
|
t = math.isqrt(discr)
|
|
|
|
# Recover p and q
|
|
p = (s + t) // 2
|
|
q = (s - t) // 2
|
|
if p * q == n:
|
|
return d
|
|
|
|
return None
|