-
Notifications
You must be signed in to change notification settings - Fork 0
/
protocol.py
244 lines (200 loc) · 7.79 KB
/
protocol.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
from typing import List
from copy import deepcopy
from transformations import (
Transformation,
AffineTransformation,
QuadraticComposition,
)
from my_maths import (
Polynomial,
get_tuple_index,
Vector,
substract,
eval_polynomial,
)
import specs
class SecretKey:
"""
dimensions represents [a_1, .. a_m] (see the article)
"""
def __init__(self, dimensions: List[int]) -> None:
self.dimensions = dimensions
self.transformations: List[Transformation] = []
self.transformations.append(
AffineTransformation(dimensions[0], dimensions[1])
)
for index in range(1, len(dimensions) - 1):
self.transformations.append(
QuadraticComposition(dimensions[index])
)
self.transformations.append(
AffineTransformation(dimensions[index], dimensions[index + 1])
)
def get_dimensions(self) -> List[int]:
return self.dimensions
def get_n_input(self) -> int:
return self.dimensions[0]
def get_n_output(self) -> int:
return self.dimensions[-1]
def get_n_transformations(self) -> int:
return len(self.transformations)
def get_n_params(self) -> int:
n_params = 0
for transformation in self.transformations:
n_params += transformation.get_n_params()
return n_params
def get_params(self) -> List[int]:
params = []
for transformation in self.transformations:
params += transformation.get_params()
return params
def set_params(self, params: List[int]) -> None:
param_index = 0
for transformation in self.transformations:
n_params_for_transformation = transformation.get_n_params()
transformation_params = params[
param_index: param_index + n_params_for_transformation
]
transformation.set_params(transformation_params)
param_index += n_params_for_transformation
def __str__(self) -> str:
s = "SECRET KEY:"
s += "\n-> " + "p = " + str(specs.p)
s += "\n-> " + "dimensions: = " + str(self.dimensions)
for index in range(self.get_n_transformations()):
s += (
"\n\ntransformation "
+ str(index)
+ ":\n\n"
+ str(self.transformations[index])
)
s += "\n\n"
return s
class PublicKey:
"""A public key consists in a list of 'n_output' (multivariate) Polynomials.
They all have 'n_input' variables.
"""
def __init__(self, n_input: int, n_output: int) -> None:
self.n_output = n_output
self.n_input = n_input
self.polynomials = [Polynomial(n_input) for _ in range(n_output)]
def get_n_input(self) -> int:
return self.n_input
def get_n_output(self) -> int:
return self.n_output
def set_polynomial(
self, index: int, multivariate_polynomial: Polynomial
) -> None:
assert 0 <= index < self.n_output
self.polynomials[index] = multivariate_polynomial
def get_polynomial(self, output_index: int) -> Polynomial:
assert 0 <= output_index < self.n_output
return self.polynomials[output_index]
def __str__(self) -> str:
s = "PUBLIC KEY:"
s += (
"\n\n-> "
+ "p = "
+ str(specs.p)
+ " (these multivariate polynomials must be considered modulo p)"
)
s += "\n-> " + str(self.n_output) + " lines (ie output variables)"
s += "\n-> " + str(self.n_input) + " input variables"
for line_index in range(self.n_output):
polynomial = self.polynomials[line_index]
s += (
"\n\nline number "
+ str(line_index + 1)
+ " : "
+ str(polynomial)
)
s += "\n\n"
return s
def generate_random_secret_key(dimensions: List[int]) -> SecretKey:
secret_key = SecretKey(dimensions)
for transformation in secret_key.transformations:
transformation.set_random_coefs()
return secret_key
def get_symbol_coef(symbol_index: int, n_total_symbols: int):
pol = Polynomial(n_total_symbols)
pol.set_coef(get_tuple_index(symbol_index, n_total_symbols), 1)
return pol
def generate_public_key(secret_key: SecretKey, verbose: bool) -> PublicKey:
if verbose:
print("generating public key:")
public_key = PublicKey(secret_key.get_n_input(), secret_key.get_n_output())
n_input = secret_key.get_n_input()
vector = Vector(n_input)
for var_index in range(0, n_input):
pol_var = Polynomial(n_input)
pol_var.set_coef(get_tuple_index(var_index, n_input), 1)
vector[var_index] = pol_var
for transformation in secret_key.transformations:
vector = transformation.eval(vector)
for index in range(len(vector)):
polynomial = vector[index]
public_key.set_polynomial(index, polynomial)
return public_key
def encrypt(message: Vector, public_key: PublicKey) -> Vector:
"""message : a Vector of integers"""
assert len(message) == public_key.get_n_input()
encrypted_message = Vector(public_key.get_n_output())
for line_index in range(public_key.get_n_output()):
encrypted_message[line_index] = eval_polynomial(
public_key.get_polynomial(line_index), message
)
return encrypted_message
def decrypt(encrypted_message: Vector, secret_key: SecretKey):
"""Returns a set containing all the possible decrypted messages
(tuple of int) correspnding to encrypted_message (tuple of int).
"""
assert len(encrypted_message) == secret_key.get_n_output()
solutions = [encrypted_message]
for transformation in secret_key.transformations[::-1]:
copy_solutions = deepcopy(solutions)
solutions = []
for sol in copy_solutions:
solutions += transformation.inverse(sol)
return solutions
def generate_symbolic_secret_key(dimensions: List[int]) -> SecretKey:
secret_key = SecretKey(dimensions)
n_params = secret_key.get_n_params()
symbol_index = 0
for transformation in secret_key.transformations:
transformation_n_params = transformation.get_n_params()
transformation_params = [None] * transformation_n_params
for param_index in range(0, transformation_n_params):
transformation_params[param_index] = get_symbol_coef(
symbol_index, n_params
)
symbol_index += 1
transformation.set_params(transformation_params)
return secret_key
def generate_system_needed_to_break_secret_key_security(
dimensions,
) -> List[Polynomial]:
"""Recovering the public key from the secret key is equivalent to
solving a system of multivariate polynomials. Returns a example of such
a system
"""
int_secret_key = generate_random_secret_key(dimensions)
int_public_key = generate_public_key(int_secret_key, verbose=False)
symbolic_secret_key = generate_symbolic_secret_key(dimensions)
symbolic_public_key = generate_public_key(
symbolic_secret_key, verbose=False
)
print(symbolic_public_key)
system_needed_to_break_secret_key_security = []
for polynomial_index in range(dimensions[-1]):
for coef_index in symbolic_public_key.get_polynomial(
polynomial_index
).coefs:
int_value = int_public_key.get_polynomial(
polynomial_index
).get_coef(coef_index)
polynomial_value = symbolic_public_key.get_polynomial(
polynomial_index
).get_coef(coef_index)
line = substract(polynomial_value, int_value)
system_needed_to_break_secret_key_security.append(line)
return system_needed_to_break_secret_key_security