forked from jvdsn/crypto-attacks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
howgrave_graham.py
61 lines (49 loc) · 1.79 KB
/
howgrave_graham.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import logging
from sage.all import Matrix
from sage.all import ZZ
def modular_univariate(f, N, m, t, X, early_return=True):
"""
Computes small modular roots of a univariate polynomial.
More information: May A., "New RSA Vulnerabilities Using Lattice Reduction Methods (Section 3.2)"
:param f: the polynomial
:param N: the modulus
:param m: the amount of g shifts to use
:param t: the amount of h shifts to use
:param X: an approximate bound on the roots
:param early_return: try to return as early as possible (default: true)
:return: a generator generating small roots of the polynomial
"""
f = f.monic().change_ring(ZZ)
x = f.parent().gen()
d = f.degree()
B = Matrix(ZZ, d * m + t)
row = 0
logging.debug("Generating g shifts...")
for i in range(m):
for j in range(d):
g = x ** j * N ** (m - i) * f ** i
for col in range(row + 1):
B[row, col] = g(x * X)[col]
row += 1
logging.debug("Generating h shifts...")
for i in range(t):
h = x ** i * f ** m
h = h(x * X)
for col in range(row + 1):
B[row, col] = h[col]
row += 1
logging.debug("Executing the LLL algorithm...")
B = B.LLL()
logging.debug("Reconstructing polynomials...")
for row in range(B.nrows()):
new_polynomial = 0
for col in range(B.ncols()):
new_polynomial += B[row, col] * x ** col
if new_polynomial.is_constant():
continue
new_polynomial = new_polynomial(x / X).change_ring(ZZ)
for x0, _ in new_polynomial.roots():
yield int(x0)
if early_return:
# Assuming that the first "good" polynomial in the lattice doesn't provide roots, we return.
return