Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/attacks/rsa/boneh_durfee.py
2589 views
1
import logging
2
import os
3
import sys
4
5
from sage.all import RR
6
from sage.all import ZZ
7
8
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))
9
if sys.path[1] != path:
10
sys.path.insert(1, path)
11
12
from attacks.factorization import known_phi
13
from shared.small_roots import herrmann_may
14
15
16
def attack(N, e, factor_bit_length, partial_p=None, delta=0.25, m=1, t=None):
17
"""
18
Recovers the prime factors if the private exponent is too small.
19
This implementation exploits knowledge of least significant bits of prime factors, if available.
20
More information: Boneh D., Durfee G., "Cryptanalysis of RSA with Private Key d Less than N^0.292"
21
:param N: the modulus
22
:param e: the public exponent
23
:param factor_bit_length: the bit length of the prime factors
24
:param partial_p: the partial prime factor p (PartialInteger) (default: None)
25
:param delta: a predicted bound on the private exponent (d < N^delta) (default: 0.25)
26
:param m: the m value to use for the small roots method (default: 1)
27
:param t: the t value to use for the small roots method (default: automatically computed using m)
28
:return: a tuple containing the prime factors, or None if the factors were not found
29
"""
30
# Use additional information about factors to speed up Boneh-Durfee.
31
p_lsb, p_lsb_bit_length = (0, 0) if partial_p is None else partial_p.get_known_lsb()
32
q_lsb = (pow(p_lsb, -1, 2 ** p_lsb_bit_length) * N) % (2 ** p_lsb_bit_length)
33
A = ((N >> p_lsb_bit_length) + pow(2, -p_lsb_bit_length, e) * (p_lsb * q_lsb - p_lsb - q_lsb + 1))
34
35
x, y = ZZ["x", "y"].gens()
36
f = x * (A + y) + pow(2, -p_lsb_bit_length, e)
37
X = int(RR(e) ** delta)
38
Y = int(2 ** (factor_bit_length - p_lsb_bit_length + 1))
39
t = int((1 - 2 * delta) * m) if t is None else t
40
logging.info(f"Trying {m = }, {t = }...")
41
for x0, y0 in herrmann_may.modular_bivariate(f, e, m, t, X, Y):
42
z = int(f(x0, y0))
43
if z % e == 0:
44
k = pow(x0, -1, e)
45
s = (N + 1 + k) % e
46
phi = N - s + 1
47
factors = known_phi.factorize(N, phi)
48
if factors:
49
return factors
50
51
return None
52
53
54
def attack_multi_prime(N, e, factor_bit_length, factors, delta=0.25, m=1, t=None):
55
"""
56
Recovers the prime factors if the private exponent is too small.
57
This method works for a modulus consisting of any number of primes.
58
:param N: the modulus
59
:param e: the public exponent
60
:param factor_bit_length: the bit length of the prime factors
61
:param factors: the number of prime factors in the modulus
62
:param delta: a predicted bound on the private exponent (d < n^delta) (default: 0.25)
63
:param m: the m value to use for the small roots method (default: 1)
64
:param t: the t value to use for the small roots method (default: automatically computed using m)
65
:return: a tuple containing the prime factors, or None if the factors were not found
66
"""
67
x, y = ZZ["x", "y"].gens()
68
A = N + 1
69
f = x * (A + y) + 1
70
X = int(RR(e) ** delta)
71
Y = int(2 ** ((factors - 1) * factor_bit_length + 1))
72
t = int((1 - 2 * delta) * m) if t is None else t
73
logging.info(f"Trying {m = }, {t = }...")
74
for x0, y0 in herrmann_may.modular_bivariate(f, e, m, t, X, Y):
75
z = int(f(x0, y0))
76
if z % e == 0:
77
k = pow(x0, -1, e)
78
s = (N + 1 + k) % e
79
phi = N - s + 1
80
factors = known_phi.factorize_multi_prime(N, phi)
81
if factors:
82
return factors
83
84
return None
85
86