-
Notifications
You must be signed in to change notification settings - Fork 125
/
extended_hnp.py
116 lines (98 loc) · 3.44 KB
/
extended_hnp.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import os
import sys
from sage.all import QQ
from sage.all import ZZ
from sage.all import block_matrix
from sage.all import identity_matrix
from sage.all import matrix
from sage.all import vector
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))
if sys.path[1] != path:
sys.path.insert(1, path)
from shared.lattice import closest_vectors
def attack(x_, N, pi, nu, a, p, u, b, delta=None):
"""
Solves the extended hidden number problem (definition 6 in the source paper).
More information: Hlavac M., Rosa T., "Extended Hidden Number Problem and Its Cryptanalytic Applications" (Section 4)
:param x_: the known bits of x
:param N: the modulus
:param pi: the pi values
:param nu: the nu values
:param a: the alpha values
:param p: the rho values
:param u: the mu values
:param b: the beta values
:param delta: the delta value (default: automatically computed)
:return: a generator generating possible values of x
"""
assert len(pi) == len(nu), "pi and v lists should be of equal length."
assert len(a) == len(p) == len(u) == len(b), "a, p, u, and b lists should be of equal length."
m = len(pi)
d = len(a)
l = []
for i in range(d):
assert len(p[i]) == len(u[i]), "p[i] and u[i] lists should be of equal length."
l.append(len(p[i]))
L = sum(l)
D = d + m + L
KD = QQ(2 ** (D / 4) * (m + L) ** (1 / 2) + 1) / 2
delta = QQ(1 / (2 * KD)) if delta is None else QQ(delta)
assert 0 < KD * delta < 1
Id = identity_matrix(ZZ, d)
P = matrix(ZZ, L, d)
row = 0
for i in range(d):
for j in range(l[i]):
P[row, i] = p[i][j]
row += 1
A = matrix(ZZ, m, d)
for i in range(d):
for j in range(m):
A[j, i] = a[i] * 2 ** pi[j]
X = matrix(QQ, m, m)
for j in range(m):
X[j, j] = delta / (2 ** nu[j])
K = matrix(QQ, L, L)
pos = 0
for i in range(d):
for j in range(l[i]):
K[pos, pos] = delta / (2 ** u[i][j])
pos += 1
B = block_matrix(QQ, [
[N * Id, matrix(QQ, d, m), matrix(QQ, d, L)],
[A, X, matrix(QQ, m, L)],
[P, matrix(QQ, L, m), K]
])
v = vector(QQ, [delta / 2] * D)
for i in range(d):
v[i] = (b[i] - a[i] * x_) % N
for W in closest_vectors(B, v, algorithm="babai"):
z = x_
for j in range(m):
z += 2 ** pi[j] * int((W[d + j] * 2 ** nu[j]) / delta)
z %= N
yield z
def dsa_known_bits(N, h, r, s, x, k):
"""
Recovers the (EC)DSA private key if any nonce bits are known.
:param N: the modulus
:param h: a list containing the hashed messages
:param r: a list containing the r values
:param s: a list containing the s values
:param x: the partial private key (PartialInteger, can be fully unknown)
:param k: a list containing the partial nonces (PartialIntegers)
:return: a generator generating possible private keys
"""
assert len(h) == len(r) == len(s) == len(k), "h, r, s, and k lists should be of equal length."
x_, pi, nu = x.get_known_and_unknowns()
a = []
p = []
u = []
b = []
for hi, ri, si, ki in zip(h, r, s, k):
a.append(ri)
ki_, li, ui = ki.get_known_and_unknowns()
p.append([(-si * 2 ** lij) % N for lij in li])
u.append(ui)
b.append((si * ki_ - hi) % N)
yield from attack(x_, N, pi, nu, a, p, u, b)