Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/shared/small_roots/jochemsz_may_integer.py
2589 views
1
import logging
2
from abc import ABCMeta
3
from abc import abstractmethod
4
from math import gcd
5
6
from sage.all import ZZ
7
8
from shared import small_roots
9
10
11
class Strategy(metaclass=ABCMeta):
12
@abstractmethod
13
def generate_S_M(self, f, m):
14
"""
15
Generates the S and M sets.
16
:param f: the polynomial
17
:param m: the amount of normal shifts to use
18
:return: a tuple containing the S and M sets
19
"""
20
pass
21
22
23
class BasicStrategy(Strategy):
24
def generate_S_M(self, f, m):
25
S = set((f ** (m - 1)).monomials())
26
M = set((f ** m).monomials())
27
return S, M
28
29
30
class ExtendedStrategy(Strategy):
31
def __init__(self, t):
32
self.t = t
33
34
def generate_S_M(self, f, m):
35
x = f.parent().gens()
36
assert len(x) == len(self.t)
37
38
S = set()
39
for monomial in (f ** (m - 1)).monomials():
40
for xi, ti in zip(x, self.t):
41
for j in range(ti + 1):
42
S.add(monomial * xi ** j)
43
44
M = set()
45
for monomial in S:
46
M.update((monomial * f).monomials())
47
48
return S, M
49
50
51
class Ernst1Strategy(Strategy):
52
def __init__(self, t):
53
self.t = t
54
55
def generate_S_M(self, f, m):
56
x1, x2, x3 = f.parent().gens()
57
58
S = set()
59
for i1 in range(m):
60
for i2 in range(m - i1):
61
for i3 in range(i2 + self.t + 1):
62
S.add(x1 ** i1 * x2 ** i2 * x3 ** i3)
63
64
M = set()
65
for i1 in range(m + 1):
66
for i2 in range(m - i1 + 1):
67
for i3 in range(i2 + self.t + 1):
68
M.add(x1 ** i1 * x2 ** i2 * x3 ** i3)
69
70
return S, M
71
72
73
class Ernst2Strategy(Strategy):
74
def __init__(self, t):
75
self.t = t
76
77
def generate_S_M(self, f, m):
78
x1, x2, x3 = f.parent().gens()
79
80
S = set()
81
for i1 in range(m):
82
for i2 in range(m - i1 + self.t):
83
for i3 in range(m - i1):
84
S.add(x1 ** i1 * x2 ** i2 * x3 ** i3)
85
86
M = set()
87
for i1 in range(m + 1):
88
for i2 in range(m - i1 + self.t + 1):
89
for i3 in range(m - i1 + 1):
90
M.add(x1 ** i1 * x2 ** i2 * x3 ** i3)
91
92
return S, M
93
94
95
def integer_multivariate(f, m, W, X, strategy, roots_method="resultants"):
96
"""
97
Computes small integer roots of a multivariate polynomial.
98
More information: Jochemsz E., May A., "A Strategy for Finding Roots of Multivariate Polynomials with New Applications in Attacking RSA Variants" (Section 2.2)
99
:param f: the polynomial
100
:param m: the parameter m
101
:param W: the parameter W
102
:param X: a list of approximate bounds on the roots for each variable
103
:param strategy: the strategy to use (Appendix B)
104
:param roots_method: the method to use to find roots (default: "resultants")
105
:return: a generator generating small roots (tuples) of the polynomial
106
"""
107
pr = f.parent()
108
x = pr.gens()
109
assert len(x) > 1
110
111
S, M = strategy.generate_S_M(f, m)
112
l = [0] * len(x)
113
for monomial in S:
114
for j, xj in enumerate(x):
115
l[j] = max(l[j], monomial.degree(xj))
116
117
a0 = int(f.constant_coefficient())
118
assert a0 != 0
119
while gcd(a0, W) != 1:
120
W += 1
121
122
R = W
123
for j, Xj in enumerate(X):
124
while gcd(a0, Xj) != 1:
125
Xj += 1
126
127
R *= Xj ** l[j]
128
X[j] = Xj
129
130
assert gcd(a0, R) == 1
131
f_ = (pow(a0, -1, R) * f % R).change_ring(ZZ)
132
133
logging.debug("Generating shifts...")
134
135
shifts = []
136
monomials = set()
137
for monomial in S:
138
g = monomial * f_
139
for xj, Xj, lj in zip(x, X, l):
140
g *= Xj ** (lj - monomial.degree(xj))
141
142
shifts.append(g)
143
monomials.add(monomial)
144
145
for monomial in M:
146
if monomial not in S:
147
shifts.append(monomial * R)
148
monomials.add(monomial)
149
150
L, monomials = small_roots.create_lattice(pr, shifts, X)
151
L = small_roots.reduce_lattice(L)
152
polynomials = small_roots.reconstruct_polynomials(L, f, R, monomials, X)
153
for roots in small_roots.find_roots(pr, [f] + polynomials, method=roots_method):
154
yield tuple(roots[xi] for xi in x)
155
156