Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/shared/small_roots/jochemsz_may_modular.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_M(self, f, l, m):
14
"""
15
Generates the M dict.
16
:param f: the polynomial
17
:param l: the leading monomial
18
:param m: the amount of normal shifts to use
19
:return: the M dict
20
"""
21
pass
22
23
24
class BasicStrategy(Strategy):
25
def generate_M(self, f, l, m):
26
M = {}
27
fm_monomials = (f ** m).monomials()
28
for k in range(m + 1):
29
M[k] = set()
30
fmk_monomials = (f ** (m - k)).monomials()
31
for monomial in fm_monomials:
32
if monomial // (l ** k) in fmk_monomials:
33
M[k].add(monomial)
34
35
M[m + 1] = []
36
return M
37
38
39
class ExtendedStrategy(Strategy):
40
def __init__(self, t):
41
self.t = t
42
43
def generate_M(self, f, l, m):
44
x = f.parent().gens()
45
assert len(x) == len(self.t)
46
47
M = {}
48
fm_monomials = (f ** m).monomials()
49
for k in range(m + 1):
50
M[k] = set()
51
fmk_monomials = (f ** (m - k)).monomials()
52
for monomial in fm_monomials:
53
if monomial // (l ** k) in fmk_monomials:
54
for xi, ti in zip(x, self.t):
55
for j in range(ti + 1):
56
M[k].add(monomial * xi ** j)
57
58
M[m + 1] = []
59
return M
60
61
62
class BonehDurfeeStrategy(Strategy):
63
def __init__(self, t):
64
self.t = t
65
66
def generate_M(self, f, l, m):
67
x1, x2 = f.parent().gens()
68
69
M = {}
70
for k in range(m + 1):
71
M[k] = set()
72
for i1 in range(k, m + 1):
73
for i2 in range(k, i1 + self.t + 1):
74
M[k].add(x1 ** i1 * x2 ** i2)
75
76
M[m + 1] = []
77
return M
78
79
80
class BlomerMayStrategy(Strategy):
81
def __init__(self, t):
82
self.t = t
83
84
def generate_M(self, f, l, m):
85
x1, x2, x3 = f.parent().gens()
86
87
M = {}
88
for k in range(m + 1):
89
M[k] = set()
90
for i1 in range(k, m + 1):
91
for i2 in range(m - i1 + 1):
92
for i3 in range(i2 + self.t - 1):
93
M[k].add(x1 ** i1 * x2 ** i2 * x3 ** i3)
94
95
M[m + 1] = []
96
return M
97
98
99
def modular_multivariate(f, N, m, X, strategy, roots_method="groebner"):
100
"""
101
Computes small integer roots of a multivariate polynomial.
102
More information: Jochemsz E., May A., "A Strategy for Finding Roots of Multivariate Polynomials with New Applications in Attacking RSA Variants" (Section 2.1)
103
:param f: the polynomial
104
:param N: the modulus
105
:param m: the parameter m
106
:param X: a list of approximate bounds on the roots for each variable
107
:param strategy: the strategy to use (Appendix A)
108
:param roots_method: the method to use to find roots (default: "groebner")
109
:return: a generator generating small roots (tuples) of the polynomial
110
"""
111
f = f.change_ring(ZZ)
112
pr = f.parent()
113
x = pr.gens()
114
assert len(x) > 1
115
116
# Sage lm method depends on the term ordering
117
l = 1
118
for monomial in f.monomials():
119
if monomial % l == 0:
120
l = monomial
121
122
al = int(f.coefficient(l))
123
assert gcd(al, N) == 1
124
f_ = (pow(al, -1, N) * f % N).change_ring(ZZ)
125
126
logging.debug("Generating shifts...")
127
128
M = strategy.generate_M(f, l, m)
129
shifts = []
130
monomials = set()
131
for k in range(m + 1):
132
for monomial in M[k]:
133
if monomial not in M[k + 1]:
134
shifts.append(monomial // (l ** k) * f_ ** k * N ** (m - k))
135
monomials.add(monomial)
136
137
L, monomials = small_roots.create_lattice(pr, shifts, X)
138
L = small_roots.reduce_lattice(L)
139
polynomials = small_roots.reconstruct_polynomials(L, f, N ** m, monomials, X)
140
for roots in small_roots.find_roots(pr, polynomials, method=roots_method):
141
yield tuple(roots[xi] for xi in x)
142
143