Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/shared/small_roots/ernst.py
2589 views
1
import logging
2
3
from sage.all import RR
4
from sage.all import ZZ
5
from sage.all import gcd
6
7
from shared import small_roots
8
9
10
def integer_trivariate_1(f, m, t, W, X, Y, Z, check_bounds=True, roots_method="groebner"):
11
"""
12
Computes small integer roots of a trivariate polynomial.
13
More information: Ernst M. et al., "Partial Key Exposure Attacks on RSA Up to Full Size Exponents" (Section 4.1.1)
14
:param f: the polynomial
15
:param m: the parameter m
16
:param t: the parameter t
17
:param W: the parameter W
18
:param X: an approximate bound on the x roots
19
:param Y: an approximate bound on the y roots
20
:param Z: an approximate bound on the z roots
21
:param check_bounds: perform bounds check (default: True)
22
:param roots_method: the method to use to find roots (default: "groebner")
23
:return: a generator generating small roots (tuples of x, y, and z roots) of the polynomial
24
"""
25
pr = f.parent()
26
x, y, z = pr.gens()
27
28
tau = t / m
29
if check_bounds and RR(X) ** (1 + 3 * tau) * RR(Y) ** (2 + 3 * tau) * RR(Z) ** (1 + 3 * tau + 3 * tau ** 2) > RR(W) ** (1 + 3 * tau):
30
logging.debug(f"Bound check failed for {m = }, {t = }")
31
return
32
33
R = int(f.constant_coefficient())
34
assert R != 0
35
while gcd(R, X) != 1:
36
X += 1
37
while gcd(R, Y) != 1:
38
Y += 1
39
while gcd(R, Z) != 1:
40
Z += 1
41
while gcd(R, W) != 1:
42
W += 1
43
44
n = (X * Y) ** m * Z ** (m + t) * W
45
assert gcd(R, n) == 1
46
f_ = (pow(R, -1, n) * f % n).change_ring(ZZ)
47
48
logging.debug("Generating shifts...")
49
50
shifts = []
51
for i in range(m + 1):
52
for j in range(m - i + 1):
53
for k in range(j + 1):
54
g = x ** i * y ** j * z ** k * f_ * X ** (m - i) * Y ** (m - j) * Z ** (m + t - k)
55
shifts.append(g)
56
57
for k in range(j + 1, j + t + 1):
58
h = x ** i * y ** j * z ** k * f_ * X ** (m - i) * Y ** (m - j) * Z ** (m + t - k)
59
shifts.append(h)
60
61
for i in range(m + 2):
62
j = m + 1 - i
63
for k in range(j + 1):
64
g_ = n * x ** i * y ** j * z ** k
65
shifts.append(g_)
66
67
for k in range(j + 1, j + t + 1):
68
h_ = n * x ** i * y ** j * z ** k
69
shifts.append(h_)
70
71
L, monomials = small_roots.create_lattice(pr, shifts, [X, Y, Z])
72
L = small_roots.reduce_lattice(L)
73
polynomials = small_roots.reconstruct_polynomials(L, f, n, monomials, [X, Y, Z])
74
for roots in small_roots.find_roots(pr, [f] + polynomials, method=roots_method):
75
yield roots[x], roots[y], roots[z]
76
77
78
def integer_trivariate_2(f, m, t, W, X, Y, Z, check_bounds=True, roots_method="groebner"):
79
"""
80
Computes small integer roots of a trivariate polynomial.
81
More information: Ernst M. et al., "Partial Key Exposure Attacks on RSA Up to Full Size Exponents" (Section 4.1.2)
82
:param f: the polynomial
83
:param m: the parameter m
84
:param t: the parameter t
85
:param W: the parameter W
86
:param X: an approximate bound on the x roots
87
:param Y: an approximate bound on the y roots
88
:param Z: an approximate bound on the z roots
89
:param check_bounds: perform bounds check (default: True)
90
:param roots_method: the method to use to find roots (default: "groebner")
91
:return: a generator generating small roots (tuples of x, y, and z roots) of the polynomial
92
"""
93
pr = f.parent()
94
x, y, z = pr.gens()
95
96
tau = t / m
97
if check_bounds and RR(X) ** (2 + 3 * tau) * RR(Y) ** (3 + 6 * tau + 3 * tau ** 2) * RR(Z) ** (3 + 3 * tau) > RR(W) ** (2 + 3 * tau):
98
logging.debug(f"Bound check failed for {m = }, {t = }")
99
return
100
101
R = int(f.constant_coefficient())
102
assert R != 0
103
while gcd(R, X) != 1:
104
X += 1
105
while gcd(R, Y) != 1:
106
Y += 1
107
while gcd(R, Z) != 1:
108
Z += 1
109
while gcd(R, W) != 1:
110
W += 1
111
112
n = X ** m * Y ** (m + t) * Z ** m * W
113
assert gcd(R, n) == 1
114
f_ = (pow(R, -1, n) * f % n).change_ring(ZZ)
115
116
logging.debug("Generating shifts...")
117
118
shifts = []
119
for i in range(m + 1):
120
for j in range(m - i + 1):
121
for k in range(m - i + 1):
122
g = x ** i * y ** j * z ** k * f_ * X ** (m - i) * Y ** (m + t - j) * Z ** (m - k)
123
shifts.append(g)
124
125
for j in range(m - i + 1, m - i + t + 1):
126
for k in range(m - i + 1):
127
h = x ** i * y ** j * z ** k * f_ * X ** (m - i) * Y ** (m + t - j) * Z ** (m - k)
128
shifts.append(h)
129
130
for i in range(m + 2):
131
for j in range(m + t + 2 - i):
132
k = m + 1 - i
133
g_ = n * x ** i * y ** j * z ** k
134
shifts.append(g_)
135
136
for i in range(m + 1):
137
j = m + t + 1 - i
138
for k in range(m - i + 1):
139
h_ = n * x ** i * y ** j * z ** k
140
shifts.append(h_)
141
142
L, monomials = small_roots.create_lattice(pr, shifts, [X, Y, Z])
143
L = small_roots.reduce_lattice(L)
144
polynomials = small_roots.reconstruct_polynomials(L, f, n, monomials, [X, Y, Z])
145
for roots in small_roots.find_roots(pr, [f] + polynomials, method=roots_method):
146
yield roots[x], roots[y], roots[z]
147
148