Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/attacks/rsa/wiener_attack_lattice.py
2589 views
1
import logging
2
import os
3
import sys
4
from itertools import combinations
5
from math import isqrt
6
from math import prod
7
8
from sage.all import RR
9
from sage.all import ZZ
10
from sage.all import matrix
11
12
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))
13
if sys.path[1] != path:
14
sys.path.insert(1, path)
15
16
from attacks.factorization import known_phi
17
from shared.lattice import shortest_vectors
18
from shared.small_roots import aono
19
from shared.small_roots import reduce_lattice
20
21
22
def attack(N, e):
23
"""
24
Recovers the prime factors of a modulus and the private exponent if the private exponent is too small.
25
More information: Nguyen P. Q., "Public-Key Cryptanalysis"
26
:param N: the modulus
27
:param e: the public exponent
28
:return: a tuple containing the prime factors and the private exponent, or None if the private exponent was not found
29
"""
30
s = isqrt(N)
31
L = matrix(ZZ, [[e, s], [N, 0]])
32
33
for v in shortest_vectors(L):
34
d = v[1] // s
35
k = abs(v[0] - e * d) // N
36
d = abs(d)
37
if pow(pow(2, e, N), d, N) != 2:
38
continue
39
40
phi = (e * d - 1) // k
41
factors = known_phi.factorize(N, phi)
42
if factors:
43
return *factors, int(d)
44
45
return None
46
47
48
# Construct R_{u, v} for a specific monomial.
49
def _construct_relation(N, monomial, x):
50
vars = monomial.variables()
51
l = [x[i] for i in range(x.index(vars[-1]) + 1)]
52
R = 1
53
u = 0
54
v = 0
55
i = len(vars)
56
for var in vars:
57
if var != x[0] and var < l[0] and len(l) >= 2 * i:
58
# Guo equation
59
R *= l[0] - var
60
l.pop(0)
61
v += 1
62
else:
63
# Wiener equation
64
R *= var - N
65
u += 1
66
67
l.remove(var)
68
i -= 1
69
70
return R, u, v
71
72
73
def attack_multiple_exponents_1(N, e, alpha):
74
"""
75
Recovers the prime factors of a modulus given multiple public exponents with small corresponding private exponents.
76
More information: Howgrave-Graham N., Seifert J., "Extending Wiener’s Attack in the Presence of Many Decrypting Exponents"
77
:param N: the modulus
78
:param e: the public exponent
79
:param alpha: the bound on the private exponents (i.e. d < N^alpha)
80
:return: a tuple containing the prime factors, or None if the prime factors were not found
81
"""
82
n = len(e)
83
pr = ZZ[",".join(f"x{i}" for i in range(n))]
84
x = pr.gens()
85
86
monomials = [1]
87
for i, xi in enumerate(x):
88
monomials.append(xi)
89
for j in range(i):
90
for comb in combinations(x[:i], j + 1):
91
monomials.append(prod(comb) * xi)
92
93
L = matrix(ZZ, len(monomials))
94
exp_a = [n]
95
exp_b = [0]
96
for col, monomial in enumerate(monomials):
97
if col == 0:
98
L[0, 0] = 1
99
continue
100
101
R, u, v = _construct_relation(N, monomial, x)
102
for row, monomial in enumerate(monomials):
103
if row == 0:
104
L[0, col] = R.constant_coefficient()
105
else:
106
L[row, col] = R.monomial_coefficient(monomial) * monomial(*e)
107
108
exp_a.append(n - v)
109
exp_b.append(u / 2)
110
111
max_a = max(exp_a)
112
max_b = max(exp_b)
113
D = matrix(ZZ, len(monomials))
114
for i, (a, b) in enumerate(zip(exp_a, exp_b)):
115
D[i, i] = int(RR(N) ** ((max_a - a) * alpha + (max_b - b)))
116
117
L = L * D
118
L_ = reduce_lattice(L)
119
b = L.solve_left(L_[0])
120
phi = round(b[1] / b[0] * e[0])
121
return known_phi.factorize(N, phi)
122
123
124
def attack_multiple_exponents_2(N, e, d_bit_length, m=1):
125
"""
126
Recovers the prime factors of a modulus given multiple public exponents with small corresponding private exponents.
127
More information: Aono Y., "Minkowski sum based lattice construction for multivariate simultaneous Coppersmith’s technique and applications to RSA" (Section 4)
128
:param N: the modulus
129
:param e: the public exponent
130
:param d_bit_length: the bit length of the private exponents
131
:param m: the m value to use for the small roots method (default: 1)
132
:return: a tuple containing the prime factors, or None if the prime factors were not found
133
"""
134
l = len(e)
135
assert len(set(e)) == l, "All public exponents must be distinct"
136
assert l >= 1, "At least one public exponent is required."
137
138
pr = ZZ[",".join(f"x{i}" for i in range(l)) + ",y"]
139
gens = pr.gens()
140
x = gens[:-1]
141
y = gens[-1]
142
F = [-1 + x[k] * (y + N) for k in range(l)]
143
X = [2 ** d_bit_length for _ in range(l)]
144
Y = 3 * isqrt(N)
145
logging.info(f"Trying {m = }...")
146
for roots in aono.integer_multivariate(F, e, m, X + [Y]):
147
phi = roots[y] + N
148
factors = known_phi.factorize(N, phi)
149
if factors:
150
return factors
151
152
return None
153
154