GGH-crypto 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- GGH_crypto-1.0.4/GGH_crypto/GGH/GGH.py +294 -0
- GGH_crypto-1.0.4/GGH_crypto/GGH/__init__.py +3 -0
- GGH_crypto-1.0.4/GGH_crypto/GGH_HNF/GGH_HNF.py +291 -0
- GGH_crypto-1.0.4/GGH_crypto/GGH_HNF/__init__.py +3 -0
- GGH_crypto-1.0.4/GGH_crypto/Utils/Utils.py +513 -0
- GGH_crypto-1.0.4/GGH_crypto/Utils/__init__.py +3 -0
- GGH_crypto-1.0.4/GGH_crypto/__init__.py +5 -0
- GGH_crypto-1.0.4/GGH_crypto.egg-info/PKG-INFO +38 -0
- GGH_crypto-1.0.4/GGH_crypto.egg-info/SOURCES.txt +16 -0
- GGH_crypto-1.0.4/GGH_crypto.egg-info/dependency_links.txt +1 -0
- GGH_crypto-1.0.4/GGH_crypto.egg-info/requires.txt +4 -0
- GGH_crypto-1.0.4/GGH_crypto.egg-info/top_level.txt +1 -0
- GGH_crypto-1.0.4/LICENSE.txt +17 -0
- GGH_crypto-1.0.4/PKG-INFO +38 -0
- GGH_crypto-1.0.4/README.md +18 -0
- GGH_crypto-1.0.4/setup.cfg +7 -0
- GGH_crypto-1.0.4/setup.py +35 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import sympy as sp
|
|
3
|
+
import math
|
|
4
|
+
from flint import fmpz_mat, fmpz, fmpq, fmpq_mat
|
|
5
|
+
from fractions import Fraction
|
|
6
|
+
import time
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from ..Utils.Utils import Utils
|
|
14
|
+
|
|
15
|
+
class GGHCryptosystem:
|
|
16
|
+
"""
|
|
17
|
+
Implementation of the GGH (Goldreich-Goldwasser-Halevi) cryptographic system.
|
|
18
|
+
|
|
19
|
+
This class provides methods for generating keys, encrypting, and decrypting messages
|
|
20
|
+
using the GGH lattice-based cryptographic system.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
dimension (int): The dimension of the lattice.
|
|
24
|
+
integer_sigma (bool): If True, uses an integer value for sigma.
|
|
25
|
+
sigma (int or float): The sigma parameter for error generation.
|
|
26
|
+
message (fmpq_mat): The message to be encrypted.
|
|
27
|
+
ciphertext (fmpq_mat): The encrypted text.
|
|
28
|
+
error (fmpz_mat or fmpq_mat): The error vector.
|
|
29
|
+
private_basis (fmpz_mat): The private basis of the lattice.
|
|
30
|
+
public_basis (fmpz_mat): The public basis of the lattice.
|
|
31
|
+
unimodular (fmpz_mat): The unimodular matrix for public key generation.
|
|
32
|
+
debug (bool): If True, prints debug information.
|
|
33
|
+
private_key (fmpz_mat): The private key.
|
|
34
|
+
public_key (tuple): The public key (public basis, sigma).
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
dimension (int): The dimension of the lattice.
|
|
38
|
+
private_basis (fmpz_mat, optional): A predefined private basis.
|
|
39
|
+
public_basis (fmpz_mat, optional): A predefined public basis.
|
|
40
|
+
unimodular (fmpz_mat, optional): A predefined unimodular matrix.
|
|
41
|
+
message (fmpq_mat, optional): A predefined message.
|
|
42
|
+
error (fmpz_mat or fmpq_mat, optional): A predefined error vector.
|
|
43
|
+
sigma (int or float, optional): A predefined value for sigma.
|
|
44
|
+
integer_sigma (bool, optional): Whether to use an integer value for sigma. Default is True.
|
|
45
|
+
debug (bool, optional): Whether to enable debug prints. Default is False.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
ValueError: If the dimensions of the provided bases or vectors do not match.
|
|
49
|
+
"""
|
|
50
|
+
def __init__(self, dimension, private_basis=None, public_basis=None, unimodular=None, message=None,
|
|
51
|
+
error=None, sigma = None, integer_sigma=True, debug=False):
|
|
52
|
+
self.dimension = dimension
|
|
53
|
+
self.integer_sigma = integer_sigma
|
|
54
|
+
self.sigma = sigma
|
|
55
|
+
|
|
56
|
+
self.message = message
|
|
57
|
+
self.ciphertext = None
|
|
58
|
+
self.error = error
|
|
59
|
+
|
|
60
|
+
self.private_basis = private_basis
|
|
61
|
+
self.public_basis = public_basis
|
|
62
|
+
self.unimodular = unimodular
|
|
63
|
+
self.debug = debug
|
|
64
|
+
|
|
65
|
+
self.private_key = None
|
|
66
|
+
self.public_key = None
|
|
67
|
+
|
|
68
|
+
# Check dimensions of bases if provided
|
|
69
|
+
if private_basis is not None:
|
|
70
|
+
if private_basis.nrows() != dimension or private_basis.ncols() != dimension:
|
|
71
|
+
raise ValueError(f"[GGH] Private basis must be a {dimension}x{dimension} matrix, but got {private_basis.nrows()}x{private_basis.ncols()}")
|
|
72
|
+
|
|
73
|
+
if public_basis is not None:
|
|
74
|
+
if public_basis.nrows() != dimension or public_basis.ncols() != dimension:
|
|
75
|
+
raise ValueError(f"[GGH] Public basis must be a {dimension}x{dimension} matrix, but got {public_basis.nrows()}x{public_basis.ncols()}")
|
|
76
|
+
|
|
77
|
+
if unimodular is not None:
|
|
78
|
+
if unimodular.nrows() != dimension or unimodular.ncols() != dimension:
|
|
79
|
+
raise ValueError(f"[GGH] Unimodular matrix must be a {dimension}x{dimension} matrix, but got {unimodular.nrows()}x{unimodular.ncols()}")
|
|
80
|
+
|
|
81
|
+
# Check dimensions of vectors if provided
|
|
82
|
+
if message is not None:
|
|
83
|
+
if message.ncols() != dimension:
|
|
84
|
+
raise ValueError(f"[GGH] Message vector must have length {dimension}, but got length {message.ncols()}")
|
|
85
|
+
|
|
86
|
+
if error is not None:
|
|
87
|
+
if error.ncols() != dimension:
|
|
88
|
+
raise ValueError(f"[GGH] Error vector must have length {dimension}, but got length {error.ncols()}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if private_basis is not None:
|
|
92
|
+
self.generate_keys_from_R_or_B()
|
|
93
|
+
else:
|
|
94
|
+
self.generate_keys()
|
|
95
|
+
|
|
96
|
+
def random_unimodular(self, dim, mix):
|
|
97
|
+
"""
|
|
98
|
+
Generates a random unimodular matrix.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
dim (int): The dimension of the matrix.
|
|
102
|
+
mix (int): The number of mixing operations to perform.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
fmpz_mat: A random unimodular matrix.
|
|
106
|
+
"""
|
|
107
|
+
T = sp.eye(dim)
|
|
108
|
+
x = sp.zeros(1, dim)
|
|
109
|
+
choices = [-1, 0, 1]
|
|
110
|
+
weights = [1, 5, 1]
|
|
111
|
+
|
|
112
|
+
for _ in range(mix):
|
|
113
|
+
rows = list(range(dim))
|
|
114
|
+
random.shuffle(rows)
|
|
115
|
+
for i in rows:
|
|
116
|
+
x[0, i] = 1
|
|
117
|
+
for k in range(dim):
|
|
118
|
+
if k != i:
|
|
119
|
+
x[0, k] = random.choices(choices, weights=weights)[0]
|
|
120
|
+
|
|
121
|
+
# Perform matrix multiplication more efficiently
|
|
122
|
+
new_row = x * T
|
|
123
|
+
for j in range(dim):
|
|
124
|
+
T[i, j] = new_row[0, j]
|
|
125
|
+
|
|
126
|
+
# Reset x for the next iteration
|
|
127
|
+
x[0, i] = 0
|
|
128
|
+
|
|
129
|
+
return Utils.npsp_to_fmpz_mat(T)
|
|
130
|
+
|
|
131
|
+
def generate_sigma(self, R):
|
|
132
|
+
"""
|
|
133
|
+
Generates the sigma value based on the L1 norm of the inverse of R.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
R (fmpz_mat): The R matrix to calculate the inverse from.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
int or float: The calculated sigma value.
|
|
140
|
+
"""
|
|
141
|
+
rho = Utils.vector_l1_norm(R.inv())
|
|
142
|
+
|
|
143
|
+
sigma_max = 1 / (2 * rho)
|
|
144
|
+
|
|
145
|
+
if self.integer_sigma:
|
|
146
|
+
return int(math.floor(sigma_max)) if math.floor(sigma_max) < 3 else 3 #standard sigma is 3
|
|
147
|
+
else:
|
|
148
|
+
return sigma_max
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def generate_error(self):
|
|
152
|
+
"""
|
|
153
|
+
Generates a random error vector based on sigma.
|
|
154
|
+
"""
|
|
155
|
+
sigma = self.public_key[1]
|
|
156
|
+
|
|
157
|
+
random_elements = [random.choice([-sigma, sigma]) for _ in range(self.dimension)]
|
|
158
|
+
|
|
159
|
+
if isinstance(sigma, int):
|
|
160
|
+
self.error = fmpz_mat([random_elements])
|
|
161
|
+
else:
|
|
162
|
+
random_elements = [fmpq(Fraction(item).numerator, Fraction(item).denominator) for item in random_elements]
|
|
163
|
+
self.error = fmpq_mat([random_elements])
|
|
164
|
+
|
|
165
|
+
def generate_random_message(self):
|
|
166
|
+
"""
|
|
167
|
+
Generates a random message.
|
|
168
|
+
"""
|
|
169
|
+
random_elements = [random.randint(-128, 127) for _ in range(self.dimension)]
|
|
170
|
+
self.message = fmpq_mat([random_elements])
|
|
171
|
+
|
|
172
|
+
def generate_keys_from_R_or_B(self):
|
|
173
|
+
"""
|
|
174
|
+
Generates keys from a provided private or public basis.
|
|
175
|
+
"""
|
|
176
|
+
if self.debug:
|
|
177
|
+
logger.info("[GGH] Private basis given as input, inverting it..")
|
|
178
|
+
|
|
179
|
+
R_inv = self.private_basis.inv()
|
|
180
|
+
|
|
181
|
+
if not self.sigma:
|
|
182
|
+
self.sigma = self.generate_sigma(R_inv)
|
|
183
|
+
|
|
184
|
+
if self.public_basis is None:
|
|
185
|
+
if self.debug:
|
|
186
|
+
logger.info("[GGH] Generating public key...")
|
|
187
|
+
|
|
188
|
+
if not self.unimodular:
|
|
189
|
+
self.unimodular = self.random_unimodular(self.dimension, 2)
|
|
190
|
+
self.public_basis = self.unimodular * self.private_basis
|
|
191
|
+
else:
|
|
192
|
+
if self.debug:
|
|
193
|
+
logger.info("[GGH] Using the provided public basis as the public key")
|
|
194
|
+
|
|
195
|
+
self.public_key = (self.public_basis, self.sigma)
|
|
196
|
+
self.private_key = self.private_basis
|
|
197
|
+
|
|
198
|
+
def generate_keys(self):
|
|
199
|
+
"""
|
|
200
|
+
Generates the private and public keys of the cryptographic system.
|
|
201
|
+
"""
|
|
202
|
+
tries = 0
|
|
203
|
+
l = 4
|
|
204
|
+
k = fmpz(l * math.ceil(math.sqrt(self.dimension) + 1))
|
|
205
|
+
if self.debug:
|
|
206
|
+
logger.info("[GGH] Generating private basis...")
|
|
207
|
+
time_start = time.time()
|
|
208
|
+
while True:
|
|
209
|
+
R = fmpz_mat([[random.randint(-l, l-1) for _ in range(self.dimension)] for _ in range(self.dimension)])
|
|
210
|
+
I = Utils.npsp_to_fmpz_mat(sp.eye(self.dimension))
|
|
211
|
+
KI = k * I
|
|
212
|
+
R += KI
|
|
213
|
+
|
|
214
|
+
tries += 1
|
|
215
|
+
|
|
216
|
+
if R.det() != 0:
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
if self.debug:
|
|
220
|
+
priv_time = time.time() - time_start
|
|
221
|
+
logger.info(f"[GGH] Time taken: {priv_time} with {tries} tries")
|
|
222
|
+
|
|
223
|
+
self.private_basis = R
|
|
224
|
+
|
|
225
|
+
if not self.sigma:
|
|
226
|
+
if self.debug:
|
|
227
|
+
logger.info("[GGH] Generating sigma...")
|
|
228
|
+
time_start = time.time()
|
|
229
|
+
self.sigma = self.generate_sigma(R)
|
|
230
|
+
if self.debug:
|
|
231
|
+
sigma_time = time.time() - time_start
|
|
232
|
+
logger.info(f"Generated sigma is {self.sigma}, time taken: {sigma_time}")
|
|
233
|
+
|
|
234
|
+
if not self.unimodular:
|
|
235
|
+
if self.debug:
|
|
236
|
+
logger.info(f"Generating unimodular matrix...")
|
|
237
|
+
time_start = time.time()
|
|
238
|
+
self.unimodular = self.random_unimodular(self.dimension, 2)
|
|
239
|
+
if self.debug:
|
|
240
|
+
unim_time = time.time() - time_start
|
|
241
|
+
logger.info(f"[GGH] Time taken: {unim_time}")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if self.debug:
|
|
245
|
+
logger.info(f"[GGH] Generating public basis...")
|
|
246
|
+
time_start = time.time()
|
|
247
|
+
self.public_basis = self.unimodular * self.private_basis
|
|
248
|
+
if self.debug:
|
|
249
|
+
pub_time = time.time() - time_start
|
|
250
|
+
logger.info(f"[GGH] Time taken: {pub_time}")
|
|
251
|
+
|
|
252
|
+
self.public_key = (self.public_basis, self.sigma)
|
|
253
|
+
self.private_key = R
|
|
254
|
+
|
|
255
|
+
def encrypt(self):
|
|
256
|
+
"""
|
|
257
|
+
Encrypts the message using the public key and an error vector.
|
|
258
|
+
"""
|
|
259
|
+
if self.debug:
|
|
260
|
+
logger.info(f"[GGH] Encrypting...")
|
|
261
|
+
time_start = time.time()
|
|
262
|
+
if self.error is None:
|
|
263
|
+
self.generate_error()
|
|
264
|
+
|
|
265
|
+
if self.message is None:
|
|
266
|
+
self.generate_random_message()
|
|
267
|
+
|
|
268
|
+
B = self.public_key[0]
|
|
269
|
+
|
|
270
|
+
self.ciphertext = self.message * B + self.error
|
|
271
|
+
if self.debug:
|
|
272
|
+
enc_time = time.time() - time_start
|
|
273
|
+
logger.info(f"[GGH] Time taken: {enc_time}")
|
|
274
|
+
|
|
275
|
+
def decrypt(self):
|
|
276
|
+
"""
|
|
277
|
+
Decrypts the ciphertext using the private key.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
fmpq_mat: The decrypted message.
|
|
281
|
+
"""
|
|
282
|
+
if self.debug:
|
|
283
|
+
logger.info(f"[GGH] Decrypting...")
|
|
284
|
+
time_start = time.time()
|
|
285
|
+
|
|
286
|
+
CVP = Utils.babai_rounding(self.private_basis, self.ciphertext)
|
|
287
|
+
|
|
288
|
+
result = CVP * self.public_basis.inv()
|
|
289
|
+
|
|
290
|
+
if self.debug:
|
|
291
|
+
dec_time = time.time() - time_start
|
|
292
|
+
logger.info(f"[GGH] Time taken: {dec_time}")
|
|
293
|
+
|
|
294
|
+
return result
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import sympy as sp
|
|
2
|
+
import random
|
|
3
|
+
import math
|
|
4
|
+
from flint import fmpz_mat, fmpq_mat, fmpq, fmpz
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
import time
|
|
7
|
+
import numpy as np
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
from ..Utils.Utils import Utils
|
|
14
|
+
|
|
15
|
+
class GGHHNFCryptosystem:
|
|
16
|
+
"""
|
|
17
|
+
Implementation of the GGH (Goldreich-Goldwasser-Halevi) cryptographic system with Hermite Normal Form (HNF) modifications.
|
|
18
|
+
|
|
19
|
+
This class provides methods for generating keys, encrypting, and decrypting messages
|
|
20
|
+
using the GGH lattice-based cryptographic system with HNF optimizations.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
dimension (int): The dimension of the lattice.
|
|
24
|
+
lattice_point (fmpz_mat): The lattice point to be encrypted.
|
|
25
|
+
ciphertext (fmpq_mat): The encrypted text.
|
|
26
|
+
error (fmpz_mat): The error vector.
|
|
27
|
+
alpha (float): The alpha parameter for error generation.
|
|
28
|
+
private_basis (fmpz_mat): The private basis of the lattice.
|
|
29
|
+
public_basis (fmpz_mat): The public basis of the lattice.
|
|
30
|
+
R_rho (Decimal): The rho parameter of the private basis.
|
|
31
|
+
GGH_private (bool): If True, uses GGH's matrix transformation technique for private basis generation.
|
|
32
|
+
debug (bool): If True, prints debug information.
|
|
33
|
+
private_key (tuple): The private key (R_inv, R).
|
|
34
|
+
public_key (tuple): The public key (H, R_rho).
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
dimension (int): The dimension of the lattice.
|
|
38
|
+
private_basis (fmpz_mat, optional): A predefined private basis.
|
|
39
|
+
public_basis (fmpz_mat, optional): A predefined public basis.
|
|
40
|
+
lattice_point (fmpz_mat, optional): A predefined lattice point.
|
|
41
|
+
error (fmpz_mat, optional): A predefined error vector.
|
|
42
|
+
alpha (float, optional): The alpha parameter for error generation. Default is 0.75.
|
|
43
|
+
GGH_private (bool, optional): Whether to use GGH's matrix transformation technique. Default is False.
|
|
44
|
+
debug (bool, optional): Whether to enable debug prints. Default is False.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
ValueError: If the dimensions of the provided bases or vectors do not match.
|
|
48
|
+
"""
|
|
49
|
+
def __init__(self, dimension, private_basis=None, public_basis=None, lattice_point=None, error=None, alpha=0.75, GGH_private=False, debug=False):
|
|
50
|
+
self.dimension = dimension
|
|
51
|
+
|
|
52
|
+
self.lattice_point = lattice_point
|
|
53
|
+
self.ciphertext = None
|
|
54
|
+
self.error = error
|
|
55
|
+
self.alpha = alpha
|
|
56
|
+
|
|
57
|
+
self.private_basis = private_basis
|
|
58
|
+
self.public_basis = public_basis
|
|
59
|
+
|
|
60
|
+
self.R_rho = None
|
|
61
|
+
self.GGH_private = GGH_private
|
|
62
|
+
self.debug = debug
|
|
63
|
+
|
|
64
|
+
self.private_key = None
|
|
65
|
+
self.public_key = None
|
|
66
|
+
|
|
67
|
+
if private_basis is not None:
|
|
68
|
+
if private_basis.nrows() != dimension or private_basis.ncols() != dimension:
|
|
69
|
+
raise ValueError(f"[GGH-HNF] Private basis must be a {dimension}x{dimension} matrix, but got {private_basis.nrows()}x{private_basis.ncols()}")
|
|
70
|
+
|
|
71
|
+
if public_basis is not None:
|
|
72
|
+
if public_basis.nrows() != dimension or public_basis.ncols() != dimension:
|
|
73
|
+
raise ValueError(f"[GGH-HNF] Public basis must be a {dimension}x{dimension} matrix, but got {public_basis.nrows()}x{public_basis.ncols()}")
|
|
74
|
+
|
|
75
|
+
if lattice_point is not None:
|
|
76
|
+
if lattice_point.ncols() != dimension:
|
|
77
|
+
raise ValueError(f"[GGH-HNF] Lattice point vector must have length {dimension}, but got length {lattice_point.ncols()}")
|
|
78
|
+
|
|
79
|
+
if error is not None:
|
|
80
|
+
if error.ncols() != dimension:
|
|
81
|
+
raise ValueError(f"[GGH-HNF] Error vector must have length {dimension}, but got length {error.ncols()}")
|
|
82
|
+
|
|
83
|
+
if self.private_basis is not None:
|
|
84
|
+
self.generate_keys_from_R_or_B()
|
|
85
|
+
else:
|
|
86
|
+
self.generate_keys()
|
|
87
|
+
|
|
88
|
+
if self.error is None:
|
|
89
|
+
self.generate_random_error()
|
|
90
|
+
else:
|
|
91
|
+
if self.debug:
|
|
92
|
+
logger.info(f"[GGH-HNF] Length of error vector is: {Utils.vector_l2_norm(self.error)}")
|
|
93
|
+
|
|
94
|
+
def generate_keys_from_R_or_B(self):
|
|
95
|
+
"""
|
|
96
|
+
Generates keys from a provided private or public basis.
|
|
97
|
+
"""
|
|
98
|
+
if self.debug:
|
|
99
|
+
logger.info("[GGH-HNF] Private basis given as input, inverting it..")
|
|
100
|
+
|
|
101
|
+
R = self.private_basis
|
|
102
|
+
R_inv = R.inv()
|
|
103
|
+
|
|
104
|
+
if self.debug:
|
|
105
|
+
logger.info("[GGH-HNF] Calculating rho...")
|
|
106
|
+
self.R_rho = self.calculate_rho(self.private_basis)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if self.public_basis is None:
|
|
110
|
+
if self.debug:
|
|
111
|
+
logger.info("[GGH-HNF] Generating public basis using HNF of the good basis...")
|
|
112
|
+
H = R.hnf()
|
|
113
|
+
self.public_basis = H
|
|
114
|
+
else:
|
|
115
|
+
if self.debug:
|
|
116
|
+
logger.info("[GGH-HNF] Using the provided public basis as the public key")
|
|
117
|
+
H = self.public_basis
|
|
118
|
+
|
|
119
|
+
self.public_key = (H, self.R_rho)
|
|
120
|
+
self.private_key = (R_inv, R)
|
|
121
|
+
|
|
122
|
+
def min_norm_row(self, matrix):
|
|
123
|
+
"""
|
|
124
|
+
Finds the minimum norm row in the given matrix.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
matrix (fmpz_mat or fmpq_mat): The input matrix.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
float: The minimum norm among all rows.
|
|
131
|
+
"""
|
|
132
|
+
norms = []
|
|
133
|
+
for j in range(matrix.nrows()):
|
|
134
|
+
row = [matrix[j, i] for i in range(matrix.ncols())]
|
|
135
|
+
norms.append(Utils.vector_l2_norm(row))
|
|
136
|
+
min_norm = min(norms)
|
|
137
|
+
return norms[norms.index(min_norm)]
|
|
138
|
+
|
|
139
|
+
def calculate_rho(self, basis):
|
|
140
|
+
"""
|
|
141
|
+
Calculates the rho parameter for the given basis.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
basis (fmpz_mat): The input basis.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Decimal: The calculated rho value.
|
|
148
|
+
"""
|
|
149
|
+
basis_orthogonalized = Utils.gram_schmidt(np.array(basis.tolist()).astype(int))
|
|
150
|
+
basis_orthogonalized = Utils.npsp_to_fmpq_mat(basis_orthogonalized)
|
|
151
|
+
min_norm = self.min_norm_row(basis_orthogonalized)
|
|
152
|
+
rho = Decimal(0.5) * min_norm
|
|
153
|
+
|
|
154
|
+
if self.debug:
|
|
155
|
+
logger.info(f'[GGH-HNF] Rho is: {rho:f}')
|
|
156
|
+
return rho
|
|
157
|
+
|
|
158
|
+
def generate_random_error(self):
|
|
159
|
+
"""
|
|
160
|
+
Generates a random error vector based on the alpha and R_rho parameters.
|
|
161
|
+
"""
|
|
162
|
+
n = self.dimension
|
|
163
|
+
max_norm = Decimal(self.alpha) * self.R_rho
|
|
164
|
+
|
|
165
|
+
while True:
|
|
166
|
+
error = fmpz_mat([[random.randint(-n, n) for _ in range(self.dimension)]])
|
|
167
|
+
|
|
168
|
+
error_norm = Utils.vector_l2_norm(error)
|
|
169
|
+
|
|
170
|
+
if error_norm < max_norm:
|
|
171
|
+
break
|
|
172
|
+
n -= 1
|
|
173
|
+
|
|
174
|
+
self.error = error
|
|
175
|
+
|
|
176
|
+
if self.debug:
|
|
177
|
+
logger.info(f"[GGH-HNF] Length of error vector is: {error_norm}")
|
|
178
|
+
|
|
179
|
+
def reduce_mod_B(self):
|
|
180
|
+
"""
|
|
181
|
+
Generates the private and public keys of the cryptographic system.
|
|
182
|
+
"""
|
|
183
|
+
r = fmpq_mat(self.error)
|
|
184
|
+
H = self.public_basis
|
|
185
|
+
x = fmpz_mat(r.nrows(), r.ncols())
|
|
186
|
+
|
|
187
|
+
# Iterate over each coordinate starting from the last one
|
|
188
|
+
for i in reversed(range(r.ncols())):
|
|
189
|
+
sum_j = sum(H[j, i] * x[0, j] for j in range(i + 1, self.dimension))
|
|
190
|
+
# Compute x[i] using the formula
|
|
191
|
+
x[0, i] = math.floor((r[0, i] - sum_j) / H[i, i])
|
|
192
|
+
|
|
193
|
+
return x
|
|
194
|
+
|
|
195
|
+
def generate_keys(self):
|
|
196
|
+
"""
|
|
197
|
+
Encrypts the lattice point using the public key and an error vector.
|
|
198
|
+
"""
|
|
199
|
+
n = self.dimension
|
|
200
|
+
tries = 0
|
|
201
|
+
if self.GGH_private:
|
|
202
|
+
if self.debug:
|
|
203
|
+
logger.info("[GGH-HNF] Generating private basis using GGH's matrix transformations technique...")
|
|
204
|
+
time_start = time.time()
|
|
205
|
+
l = n
|
|
206
|
+
k = fmpz(l * math.ceil(math.sqrt(self.dimension) + 1))
|
|
207
|
+
|
|
208
|
+
while True:
|
|
209
|
+
R = fmpz_mat([[random.randint(-l, l-1) for _ in range(self.dimension)] for _ in range(self.dimension)])
|
|
210
|
+
I = Utils.npsp_to_fmpz_mat(sp.eye(self.dimension))
|
|
211
|
+
KI = k * I
|
|
212
|
+
R += KI
|
|
213
|
+
|
|
214
|
+
tries += 1
|
|
215
|
+
|
|
216
|
+
if R.det() != 0:
|
|
217
|
+
break
|
|
218
|
+
else:
|
|
219
|
+
if self.debug:
|
|
220
|
+
logger.info("[GGH-HNF] Generating private basis using Micciancio's random matrices tecnique...")
|
|
221
|
+
time_start = time.time()
|
|
222
|
+
while True:
|
|
223
|
+
R = fmpz_mat([[random.randint(-n, n - 1) for _ in range(n)] for _ in range(n)])
|
|
224
|
+
R = R.lll()
|
|
225
|
+
|
|
226
|
+
tries += 1
|
|
227
|
+
|
|
228
|
+
if R.det() != 0:
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
if self.debug:
|
|
233
|
+
priv_time = time.time() - time_start
|
|
234
|
+
logger.info(f"[GGH-HNF] Time taken: {priv_time} with {tries} tries")
|
|
235
|
+
self.private_basis = R
|
|
236
|
+
|
|
237
|
+
if self.debug:
|
|
238
|
+
time_start = time.time()
|
|
239
|
+
logger.info("[GGH-HNF] Calculating rho...")
|
|
240
|
+
self.R_rho = self.calculate_rho(self.private_basis)
|
|
241
|
+
if self.debug:
|
|
242
|
+
rho_time = time.time() - time_start
|
|
243
|
+
logger.info(f"[GGH-HNF] Time taken: {rho_time}")
|
|
244
|
+
|
|
245
|
+
if self.debug:
|
|
246
|
+
logger.info("[GGH-HNF] Generating public basis...")
|
|
247
|
+
time_start = time.time()
|
|
248
|
+
self.public_basis = H = R.hnf()
|
|
249
|
+
if self.debug:
|
|
250
|
+
pub_time = time.time() - time_start
|
|
251
|
+
logger.info(f"[GGH-HNF] Time taken: {pub_time}")
|
|
252
|
+
|
|
253
|
+
self.public_key = (H, self.R_rho)
|
|
254
|
+
self.private_key = R
|
|
255
|
+
|
|
256
|
+
def encrypt(self):
|
|
257
|
+
if self.debug:
|
|
258
|
+
logger.info(f"[GGH-HNF] Encrypting...")
|
|
259
|
+
time_start = time.time()
|
|
260
|
+
if self.lattice_point is None:
|
|
261
|
+
x = self.reduce_mod_B()
|
|
262
|
+
|
|
263
|
+
H = self.public_basis
|
|
264
|
+
r = self.error
|
|
265
|
+
|
|
266
|
+
self.ciphertext = r - x * H
|
|
267
|
+
if self.debug:
|
|
268
|
+
enc_time = time.time() - time_start
|
|
269
|
+
logger.info(f"[GGH-HNF] Time taken: {enc_time}")
|
|
270
|
+
|
|
271
|
+
def decrypt(self):
|
|
272
|
+
"""
|
|
273
|
+
Decrypts the ciphertext using the private key.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
fmpq_mat: The decrypted lattice point.
|
|
277
|
+
"""
|
|
278
|
+
if self.debug:
|
|
279
|
+
logger.info(f"[GGH-HNF] Decrypting...")
|
|
280
|
+
time_start = time.time()
|
|
281
|
+
|
|
282
|
+
CVP = Utils.babai_rounding(self.private_basis, self.ciphertext)
|
|
283
|
+
|
|
284
|
+
if self.debug:
|
|
285
|
+
dec_time = time.time() - time_start
|
|
286
|
+
logger.info(f"[GGH-HNF] Time taken: {dec_time}")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
return fmpq_mat(self.ciphertext) - CVP
|
|
290
|
+
|
|
291
|
+
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
from flint import fmpz_mat, fmpq_mat, fmpq, fmpz
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import numpy as np
|
|
4
|
+
from decimal import Decimal, getcontext
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import ast
|
|
8
|
+
from fractions import Fraction
|
|
9
|
+
import re
|
|
10
|
+
import time
|
|
11
|
+
import numpy as np
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class Utils:
|
|
18
|
+
"""
|
|
19
|
+
A utility class providing various helper methods for lattice-based cryptography operations.
|
|
20
|
+
|
|
21
|
+
This class includes methods for linear algebra operations, lattice visualizations,
|
|
22
|
+
mathematical computations, and file I/O operations specific to lattice-based cryptography.
|
|
23
|
+
|
|
24
|
+
Class Methods:
|
|
25
|
+
gram_schmidt(basis): Performs Gram-Schmidt orthogonalization on a given basis.
|
|
26
|
+
visualize_lattice(basis_1, basis_1_cvp, point, basis_2=None, basis_2_cvp=None, title="Lattice Plot", limit=5):
|
|
27
|
+
Visualizes a 2D lattice with given bases and points.
|
|
28
|
+
embedding(basis, ciphertext, visualize=False, GGH=False, BKZ=False, block=20, pruned=False, precision=100, bkzautoabort=True, bkzmaxloops=None, nolll=False):
|
|
29
|
+
Performs lattice embedding for closest vector problem (CVP) solving.
|
|
30
|
+
npsp_to_fmpq_mat(basis): Converts a numpy array or sympy matrix to an fmpq_mat.
|
|
31
|
+
npsp_to_fmpz_mat(basis): Converts a numpy array or sympy matrix to an fmpz_mat.
|
|
32
|
+
vector_l1_norm(row): Calculates the L1 norm of a vector.
|
|
33
|
+
vector_l2_norm(row): Calculates the L2 norm of a vector.
|
|
34
|
+
get_hadamard_ratio(basis=None, precision=10): Calculates the Hadamard ratio of a basis.
|
|
35
|
+
write_matrix_to_file(matrix, filename): Writes a matrix to a file.
|
|
36
|
+
load_matrix_from_file(filename, matrix_type='fmpq'): Loads a matrix from a file.
|
|
37
|
+
BKZ_reduction(matrix, block=20, pruned=False, precision=90, bkzautoabort=True, bkzmaxloops=None, nolll=False):
|
|
38
|
+
Performs BKZ (Block Korkine-Zolotarev) lattice reduction.
|
|
39
|
+
babai_rounding(basis, point, visualize=False): Performs Babai's rounding algorithm for CVP.
|
|
40
|
+
|
|
41
|
+
Note:
|
|
42
|
+
This class assumes the availability of certain libraries like flint, matplotlib, and numpy.
|
|
43
|
+
It also interacts with system commands, particularly for BKZ reduction using fplll.
|
|
44
|
+
"""
|
|
45
|
+
def gram_schmidt(basis):
|
|
46
|
+
"""
|
|
47
|
+
Performs Gram-Schmidt orthogonalization on a given basis.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
basis (numpy.ndarray): The input basis.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
numpy.ndarray: The orthogonalized basis.
|
|
54
|
+
"""
|
|
55
|
+
ortho_basis = basis[0:1,:].copy()
|
|
56
|
+
for i in range(1, basis.shape[0]):
|
|
57
|
+
proj = np.diag((basis[i,:].dot(ortho_basis.T)/np.linalg.norm(ortho_basis,axis=1)**2).flat).dot(ortho_basis)
|
|
58
|
+
ortho_basis = np.vstack((ortho_basis, basis[i,:] - proj.sum(0)))
|
|
59
|
+
return ortho_basis
|
|
60
|
+
|
|
61
|
+
def visualize_lattice(basis_1, basis_1_cvp, point, basis_2=None, basis_2_cvp=None, title="Lattice Plot", limit=5):
|
|
62
|
+
"""
|
|
63
|
+
Visualizes a 2D lattice with given bases and points.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
basis_1 (fmpz_mat): The first basis.
|
|
67
|
+
basis_1_cvp (fmpz_mat): The closest vector point for basis_1.
|
|
68
|
+
point (fmpz_mat): The target point.
|
|
69
|
+
basis_2 (fmpz_mat, optional): The second basis.
|
|
70
|
+
basis_2_cvp (fmpz_mat, optional): The closest vector point for basis_2.
|
|
71
|
+
title (str, optional): The title of the plot.
|
|
72
|
+
limit (int, optional): The limit for the lattice points to plot.
|
|
73
|
+
"""
|
|
74
|
+
basis_1_np = np.array(basis_1.tolist()).astype(int)
|
|
75
|
+
|
|
76
|
+
plt.rcParams.update({'font.size': 14})
|
|
77
|
+
|
|
78
|
+
# Check if basis_2 is provided
|
|
79
|
+
if basis_2 is not None:
|
|
80
|
+
basis_2_np = np.array(basis_2.tolist()).astype(int)
|
|
81
|
+
|
|
82
|
+
point_np = np.array(point.tolist()).astype(int)
|
|
83
|
+
|
|
84
|
+
# Create a meshgrid of integer coordinates
|
|
85
|
+
x = np.arange(-limit, limit + 1)
|
|
86
|
+
y = np.arange(-limit, limit + 1)
|
|
87
|
+
coords = np.array(np.meshgrid(x, y)).T.reshape(-1, 2)
|
|
88
|
+
|
|
89
|
+
# Multiply the meshgrid by the basis matrix to get lattice points
|
|
90
|
+
lattice_points = np.dot(coords, basis_1_np)
|
|
91
|
+
|
|
92
|
+
fig, ax = plt.subplots(figsize=(10, 10))
|
|
93
|
+
|
|
94
|
+
ax.plot(lattice_points[:, 0], lattice_points[:, 1], 'o', color='black')
|
|
95
|
+
ax.grid(True, linestyle='--', alpha=0.7)
|
|
96
|
+
ax.set_xlabel('X-axis')
|
|
97
|
+
ax.set_ylabel('Y-axis')
|
|
98
|
+
ax.set_title(title)
|
|
99
|
+
|
|
100
|
+
# Add Cartesian axes
|
|
101
|
+
ax.axhline(y=0, color='k', linewidth=0.5)
|
|
102
|
+
ax.axvline(x=0, color='k', linewidth=0.5)
|
|
103
|
+
|
|
104
|
+
label_w = "w"
|
|
105
|
+
label_w_2 = "w_2"
|
|
106
|
+
label_t = "t"
|
|
107
|
+
|
|
108
|
+
ax.scatter(point_np[0][0], point_np[0][1])
|
|
109
|
+
|
|
110
|
+
if basis_2_cvp is not None:
|
|
111
|
+
ax.scatter(int(basis_2_cvp[0, 0]), int(basis_2_cvp[0, 1]), color='red', s=70)
|
|
112
|
+
|
|
113
|
+
if basis_1_cvp is not None:
|
|
114
|
+
ax.scatter(int(basis_1_cvp[0, 0]), int(basis_1_cvp[0, 1]), color='blueviolet', s=70)
|
|
115
|
+
|
|
116
|
+
ax.annotate(label_t,
|
|
117
|
+
(point_np[0][0], point_np[0][1]),
|
|
118
|
+
textcoords="offset points",
|
|
119
|
+
xytext=(10, -5),
|
|
120
|
+
ha='center')
|
|
121
|
+
|
|
122
|
+
if basis_1_cvp is not None:
|
|
123
|
+
ax.annotate(label_w,
|
|
124
|
+
(int(basis_1_cvp[0, 0]), int(basis_1_cvp[0, 1])),
|
|
125
|
+
textcoords="offset points",
|
|
126
|
+
xytext=(0, 10),
|
|
127
|
+
ha='center')
|
|
128
|
+
|
|
129
|
+
if basis_2_cvp is not None:
|
|
130
|
+
ax.annotate(label_w_2,
|
|
131
|
+
(int(basis_2_cvp[0, 0]), int(basis_2_cvp[0, 1])),
|
|
132
|
+
textcoords="offset points",
|
|
133
|
+
xytext=(0, -12),
|
|
134
|
+
ha='center')
|
|
135
|
+
|
|
136
|
+
ax.arrow(0, 0, basis_1_np[0][0], basis_1_np[0][1], lw=2, head_width=0.1, head_length=0.1, fc='blueviolet', ec='blueviolet')
|
|
137
|
+
ax.arrow(0, 0, basis_1_np[1][0], basis_1_np[1][1], lw=2, head_width=0.1, head_length=0.1, fc='blueviolet', ec='blueviolet')
|
|
138
|
+
|
|
139
|
+
if basis_2 is not None:
|
|
140
|
+
ax.arrow(0, 0, basis_2_np[0][0], basis_2_np[0][1], lw=2, head_width=0.1, head_length=0.1, fc='red', ec='red')
|
|
141
|
+
ax.arrow(0, 0, basis_2_np[1][0], basis_2_np[1][1], lw=2, head_width=0.1, head_length=0.1, fc='red', ec='red')
|
|
142
|
+
|
|
143
|
+
# Set equal aspect ratio
|
|
144
|
+
ax.set_aspect('equal', adjustable='box')
|
|
145
|
+
|
|
146
|
+
# Calculate the limits based on lattice points and basis vectors
|
|
147
|
+
all_points = [lattice_points]
|
|
148
|
+
|
|
149
|
+
# Ensure all arrays are 2D before appending
|
|
150
|
+
all_points.append(basis_1_np.reshape(-1, 2))
|
|
151
|
+
all_points.append(point_np.reshape(-1, 2))
|
|
152
|
+
|
|
153
|
+
if basis_2 is not None:
|
|
154
|
+
all_points.append(basis_2_np.reshape(-1, 2))
|
|
155
|
+
if basis_1_cvp is not None:
|
|
156
|
+
all_points.append(np.array(basis_1_cvp.tolist()).astype(int).reshape(-1, 2))
|
|
157
|
+
if basis_2_cvp is not None:
|
|
158
|
+
all_points.append(np.array(basis_2_cvp.tolist()).astype(int).reshape(-1, 2))
|
|
159
|
+
|
|
160
|
+
all_points = np.vstack(all_points)
|
|
161
|
+
|
|
162
|
+
x_min, y_min = np.min(all_points, axis=0)
|
|
163
|
+
x_max, y_max = np.max(all_points, axis=0)
|
|
164
|
+
|
|
165
|
+
# Add some padding
|
|
166
|
+
padding = 0.5
|
|
167
|
+
ax.set_xlim(x_min - padding, x_max + padding)
|
|
168
|
+
ax.set_ylim(y_min - padding, y_max + padding)
|
|
169
|
+
|
|
170
|
+
plt.tight_layout()
|
|
171
|
+
plt.show()
|
|
172
|
+
|
|
173
|
+
def embedding(basis, ciphertext, visualize=False, GGH=False, BKZ=False, block=20,
|
|
174
|
+
pruned=False, precision=100, bkzautoabort=True, bkzmaxloops=None, nolll=False):
|
|
175
|
+
"""
|
|
176
|
+
Performs lattice embedding for closest vector problem (CVP) solving.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
basis (fmpz_mat): The lattice basis.
|
|
180
|
+
ciphertext (fmpz_mat): The ciphertext vector.
|
|
181
|
+
visualize (bool, optional): Whether to visualize the result.
|
|
182
|
+
GGH (bool, optional): Whether to use GGH-specific constraints.
|
|
183
|
+
BKZ (bool, optional): Whether to use BKZ reduction.
|
|
184
|
+
block (int, optional): The block size for BKZ reduction.
|
|
185
|
+
pruned (bool, optional): Whether to use pruning in BKZ.
|
|
186
|
+
precision (int, optional): The precision for BKZ calculations.
|
|
187
|
+
bkzautoabort (bool, optional): Whether to use auto-abort in BKZ.
|
|
188
|
+
bkzmaxloops (int, optional): The maximum number of loops for BKZ.
|
|
189
|
+
nolll (bool, optional): Whether to skip LLL in BKZ.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
fmpz_mat: The closest vector to the ciphertext in the lattice.
|
|
193
|
+
"""
|
|
194
|
+
if basis.ncols() != ciphertext.ncols():
|
|
195
|
+
raise ValueError(f"[Utils] Point is a {1}x{ciphertext.ncols()} matrix, but basis is a {basis.nrows()}x{basis.ncols()} one")
|
|
196
|
+
|
|
197
|
+
n = basis.nrows()
|
|
198
|
+
|
|
199
|
+
# Create the initial matrix with R and a column of zeros
|
|
200
|
+
matrix_emb = fmpz_mat([[int(basis[i,j]) if j < n else 0 for j in range(n+1)] for i in range(n)])
|
|
201
|
+
|
|
202
|
+
# Add t as the last row, with 1 as the last element
|
|
203
|
+
last_row = [int(ciphertext[0,i]) for i in range(n)] + [1]
|
|
204
|
+
matrix_emb = fmpz_mat(matrix_emb.tolist() + [last_row])
|
|
205
|
+
|
|
206
|
+
if BKZ:
|
|
207
|
+
matrix_emb = Utils.BKZ_reduction(matrix_emb, block=block, pruned=pruned, precision=precision,
|
|
208
|
+
bkzautoabort=bkzautoabort, bkzmaxloops=bkzmaxloops, nolll=nolll)
|
|
209
|
+
else:
|
|
210
|
+
matrix_emb = matrix_emb.lll()
|
|
211
|
+
|
|
212
|
+
min_norm = float('inf')
|
|
213
|
+
shortest_vector = None
|
|
214
|
+
|
|
215
|
+
for i in range(n + 1):
|
|
216
|
+
i_th_row = fmpz_mat([[matrix_emb[i, j] for j in range(n + 1)]])
|
|
217
|
+
|
|
218
|
+
if GGH:
|
|
219
|
+
if matrix_emb[i, n] == 1: # Se l'ultimo elemento è 1
|
|
220
|
+
norm = Utils.vector_l2_norm(i_th_row)
|
|
221
|
+
if norm < min_norm:
|
|
222
|
+
min_norm = norm
|
|
223
|
+
# Prendi i primi n valori della riga più corta
|
|
224
|
+
shortest_vector = fmpz_mat([[matrix_emb[i, j] for j in range(n)]])
|
|
225
|
+
else:
|
|
226
|
+
# Se GGH non è vero, considera comunque la riga per trovare il vettore più corto
|
|
227
|
+
norm = Utils.vector_l2_norm(i_th_row)
|
|
228
|
+
if norm < min_norm:
|
|
229
|
+
min_norm = norm
|
|
230
|
+
# Prendi i primi n valori della riga più corta
|
|
231
|
+
shortest_vector = fmpz_mat([[matrix_emb[i, j] for j in range(n)]])
|
|
232
|
+
|
|
233
|
+
# Se GGH è vero e nessun vettore con 1 come ultimo elemento è stato trovato,
|
|
234
|
+
# ritorna comunque il vettore più corto trovato
|
|
235
|
+
if GGH and shortest_vector is None:
|
|
236
|
+
for i in range(n + 1):
|
|
237
|
+
i_th_row = fmpz_mat([[matrix_emb[i, j] for j in range(n + 1)]])
|
|
238
|
+
norm = Utils.vector_l2_norm(i_th_row)
|
|
239
|
+
if norm < min_norm:
|
|
240
|
+
min_norm = norm
|
|
241
|
+
# Prendi i primi n valori della riga più corta
|
|
242
|
+
shortest_vector = fmpz_mat([[matrix_emb[i, j] for j in range(n)]])
|
|
243
|
+
|
|
244
|
+
closest_vector = ciphertext - shortest_vector
|
|
245
|
+
|
|
246
|
+
if visualize:
|
|
247
|
+
if basis.nrows() != 2:
|
|
248
|
+
raise ValueError(f"[Utils] Can't visualize. Basis must be a {2}x{2} matrix, but got a {basis.nrows()}x{basis.ncols()} one")
|
|
249
|
+
Utils.visualize_lattice(basis, closest_vector, ciphertext, title="Embedding method")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
return closest_vector
|
|
253
|
+
|
|
254
|
+
def npsp_to_fmpq_mat(basis):
|
|
255
|
+
"""
|
|
256
|
+
Converts a numpy array or sympy matrix to an fmpq_mat.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
basis (numpy.ndarray or sympy.Matrix): The input matrix.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
fmpq_mat: The converted matrix.
|
|
263
|
+
"""
|
|
264
|
+
fractions = [[Fraction(item) for item in row] for row in basis.tolist()]
|
|
265
|
+
return fmpq_mat([[fmpq(f.numerator, f.denominator) for f in row] for row in fractions])
|
|
266
|
+
|
|
267
|
+
def npsp_to_fmpz_mat(basis):
|
|
268
|
+
"""
|
|
269
|
+
Converts a numpy array or sympy matrix to an fmpz_mat.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
basis (numpy.ndarray or sympy.Matrix): The input matrix.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
fmpz_mat: The converted matrix.
|
|
276
|
+
"""
|
|
277
|
+
return fmpz_mat([[int(item) for item in sublist] for sublist in basis.tolist()])
|
|
278
|
+
|
|
279
|
+
def vector_l1_norm(row):
|
|
280
|
+
"""
|
|
281
|
+
Calculates the L1 norm of a vector.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
row (fmpz_mat or fmpq_mat): The input vector.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Decimal: The L1 norm of the vector.
|
|
288
|
+
"""
|
|
289
|
+
if isinstance(row, fmpz_mat):
|
|
290
|
+
row = fmpq_mat(row)
|
|
291
|
+
getcontext().prec = 50
|
|
292
|
+
return max(sum(abs(Decimal(int(x.numer())) / Decimal(int(x.denom()))) for x in row) for row in row.tolist())
|
|
293
|
+
|
|
294
|
+
def vector_l2_norm(row):
|
|
295
|
+
"""
|
|
296
|
+
Calculates the L2 norm of a vector.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
row (fmpz_mat or fmpq_mat): The input vector.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Decimal: The L2 norm of the vector.
|
|
303
|
+
"""
|
|
304
|
+
if isinstance(row, fmpz_mat):
|
|
305
|
+
row = fmpq_mat(row)
|
|
306
|
+
getcontext().prec = 50
|
|
307
|
+
return Decimal(sum((Decimal(int(x.numer())) / Decimal(int(x.denom()))) ** 2 for x in row)).sqrt()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def get_hadamard_ratio(basis=None, precision=10):
|
|
311
|
+
"""
|
|
312
|
+
Calculates the Hadamard ratio of a basis.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
basis (fmpz_mat or fmpq_mat, optional): The input basis.
|
|
316
|
+
precision (int, optional): The precision for calculations.
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
tuple: (Decimal ratio, str formatted ratio)
|
|
320
|
+
"""
|
|
321
|
+
norms = []
|
|
322
|
+
dimension = basis.nrows()
|
|
323
|
+
|
|
324
|
+
# Set a high precision for Decimal calculations
|
|
325
|
+
getcontext().prec = precision
|
|
326
|
+
|
|
327
|
+
for i in range(basis.nrows()):
|
|
328
|
+
row = fmpz_mat([[basis[i, j] for j in range(basis.ncols())]])
|
|
329
|
+
norm = Utils.vector_l2_norm(row)
|
|
330
|
+
norms.append(Decimal(str(norm)))
|
|
331
|
+
|
|
332
|
+
# Use log sum instead of direct multiplication
|
|
333
|
+
log_denominator = sum(norm.ln() for norm in norms)
|
|
334
|
+
log_numerator = abs(Decimal(basis.det().str())).ln()
|
|
335
|
+
|
|
336
|
+
# Calculate the ratio using logs
|
|
337
|
+
log_result = (log_numerator - log_denominator) / Decimal(dimension)
|
|
338
|
+
result = log_result.exp()
|
|
339
|
+
|
|
340
|
+
return result, f"{result:.{precision}f}"
|
|
341
|
+
|
|
342
|
+
def write_matrix_to_file(matrix, filename):
|
|
343
|
+
"""
|
|
344
|
+
Writes a matrix to a file.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
matrix (fmpz_mat or fmpq_mat): The matrix to write.
|
|
348
|
+
filename (str): The name of the file to write to.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
str: The full path of the written file.
|
|
352
|
+
"""
|
|
353
|
+
filename = os.path.join(os.getcwd(), filename)
|
|
354
|
+
|
|
355
|
+
rows = matrix.nrows()
|
|
356
|
+
cols = matrix.ncols()
|
|
357
|
+
|
|
358
|
+
# Open the file for writing
|
|
359
|
+
with open(filename, "w") as file:
|
|
360
|
+
file.write("[")
|
|
361
|
+
# Iterate over each row of the matrix
|
|
362
|
+
for i in range(rows):
|
|
363
|
+
# Iterate over each column of the matrix
|
|
364
|
+
file.write("[")
|
|
365
|
+
for j in range(cols):
|
|
366
|
+
# Write the element to the file
|
|
367
|
+
file.write(matrix[i, j].str() + " ")
|
|
368
|
+
# Write a newline character after each row
|
|
369
|
+
file.write("]\n")
|
|
370
|
+
file.write("]")
|
|
371
|
+
|
|
372
|
+
return filename
|
|
373
|
+
|
|
374
|
+
def load_matrix_from_file(filename, matrix_type='fmpq'):
|
|
375
|
+
"""
|
|
376
|
+
Loads a matrix from a file.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
filename (str): The name of the file to read from.
|
|
380
|
+
matrix_type (str, optional): The type of matrix to load ('fmpz' or 'fmpq').
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
fmpz_mat or fmpq_mat: The loaded matrix.
|
|
384
|
+
|
|
385
|
+
Raises:
|
|
386
|
+
ValueError: If an invalid matrix_type is provided.
|
|
387
|
+
"""
|
|
388
|
+
with open(os.path.join(os.getcwd(), filename), 'r') as file:
|
|
389
|
+
content = file.read().replace(']', '],').replace(' ]', ']').replace(' ', ', ')[:-4] + ']'
|
|
390
|
+
|
|
391
|
+
if matrix_type == 'fmpz':
|
|
392
|
+
return fmpz_mat(ast.literal_eval(content))
|
|
393
|
+
elif matrix_type == 'fmpq':
|
|
394
|
+
data = [[Fraction(*map(int, elem.split('/') if '/' in elem else (elem, 1)))
|
|
395
|
+
for elem in re.findall(r'[-]?\d+(?:/\d+)?', row)]
|
|
396
|
+
for row in re.findall(r'\[(.*?)\]', content)]
|
|
397
|
+
|
|
398
|
+
matrix = fmpq_mat(len(data), len(data[0]) if data else 0)
|
|
399
|
+
|
|
400
|
+
for i, row in enumerate(data):
|
|
401
|
+
for j, frac in enumerate(row):
|
|
402
|
+
matrix[i, j] = fmpq(frac.numerator, frac.denominator)
|
|
403
|
+
return matrix
|
|
404
|
+
else:
|
|
405
|
+
raise ValueError("Invalid matrix_type. Use 'fmpz' or 'fmpq'.")
|
|
406
|
+
|
|
407
|
+
def BKZ_reduction(matrix, block=20, pruned=False, precision=90, bkzautoabort=True, bkzmaxloops=None, nolll=False):
|
|
408
|
+
"""
|
|
409
|
+
Performs BKZ (Block Korkine-Zolotarev) lattice reduction.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
matrix (fmpz_mat): The input matrix to reduce.
|
|
413
|
+
block (int, optional): The block size for BKZ.
|
|
414
|
+
pruned (bool, optional): Whether to use pruning.
|
|
415
|
+
precision (int, optional): The precision for calculations.
|
|
416
|
+
bkzautoabort (bool, optional): Whether to use auto-abort.
|
|
417
|
+
bkzmaxloops (int, optional): The maximum number of loops.
|
|
418
|
+
nolll (bool, optional): Whether to skip LLL.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
tuple: (fmpz_mat reduced matrix, str error message or None)
|
|
422
|
+
"""
|
|
423
|
+
input_path = Utils.write_matrix_to_file(matrix, f'input.txt')
|
|
424
|
+
output_path = Utils.write_matrix_to_file(matrix, f'output.txt')
|
|
425
|
+
|
|
426
|
+
if os.name == 'nt':
|
|
427
|
+
command = f"wsl fplll input.txt -a bkz -b {block} -p {precision} -m wrapper -f mpfr"
|
|
428
|
+
if pruned:
|
|
429
|
+
command += " -s default.json"
|
|
430
|
+
if bkzautoabort:
|
|
431
|
+
command += " -bkzautoabort"
|
|
432
|
+
if bkzmaxloops != None:
|
|
433
|
+
command += f" -bkzmaxloops {bkzmaxloops}"
|
|
434
|
+
if nolll:
|
|
435
|
+
command += " -nolll"
|
|
436
|
+
command += f" > output.txt"
|
|
437
|
+
else:
|
|
438
|
+
command = f"fplll input.txt -a bkz -b {block} -p {precision} -m wrapper -f mpfr"
|
|
439
|
+
if pruned:
|
|
440
|
+
command += " -s default.json"
|
|
441
|
+
if bkzautoabort:
|
|
442
|
+
command += " -bkzautoabort"
|
|
443
|
+
if bkzmaxloops != None:
|
|
444
|
+
command += f" -bkzmaxloops {bkzmaxloops}"
|
|
445
|
+
if nolll:
|
|
446
|
+
command += " -nolll"
|
|
447
|
+
command += f" > output.txt"
|
|
448
|
+
try:
|
|
449
|
+
# Run the command and capture its output
|
|
450
|
+
logger.info(f"Reduction started with the following parameters:\n"
|
|
451
|
+
f" block: {block}\n"
|
|
452
|
+
f" pruned: {pruned}\n"
|
|
453
|
+
f" precision: {precision}\n"
|
|
454
|
+
f" bkzautoabort: {bkzautoabort}\n"
|
|
455
|
+
f" bkzmaxloops: {bkzmaxloops}\n"
|
|
456
|
+
f" nolll: {nolll}")
|
|
457
|
+
logger.info("Final command:\n"
|
|
458
|
+
f"{command}")
|
|
459
|
+
|
|
460
|
+
time_now = time.time()
|
|
461
|
+
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=os.getcwd())
|
|
462
|
+
output, error = process.communicate()
|
|
463
|
+
output = output.decode('utf-8')
|
|
464
|
+
error = error.decode('utf-8')
|
|
465
|
+
|
|
466
|
+
if error:
|
|
467
|
+
if str(error).strip() != "Failure: loops limit exceeded in BKZ":
|
|
468
|
+
logger.error("Error during reduction:", error)
|
|
469
|
+
else:
|
|
470
|
+
error = None
|
|
471
|
+
|
|
472
|
+
logger.info(f"Reduction completed, time taken: {time.time() - time_now}")
|
|
473
|
+
|
|
474
|
+
# Load the reduced matrix
|
|
475
|
+
reduced_matrix = Utils.load_matrix_from_file(f"output.txt", "fmpz")
|
|
476
|
+
|
|
477
|
+
os.remove(output_path)
|
|
478
|
+
os.remove(input_path)
|
|
479
|
+
|
|
480
|
+
return reduced_matrix, error
|
|
481
|
+
|
|
482
|
+
except Exception as e:
|
|
483
|
+
return None, str(e)
|
|
484
|
+
|
|
485
|
+
def babai_rounding(basis, point, visualize=False):
|
|
486
|
+
"""
|
|
487
|
+
Performs Babai's rounding algorithm for CVP.
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
basis (fmpz_mat): The lattice basis.
|
|
491
|
+
point (fmpz_mat): The target point.
|
|
492
|
+
visualize (bool, optional): Whether to visualize the result.
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
fmpz_mat: The closest vector to the point in the lattice.
|
|
496
|
+
|
|
497
|
+
Raises:
|
|
498
|
+
ValueError: If visualization is requested for a non-2D lattice.
|
|
499
|
+
"""
|
|
500
|
+
x = point * basis.inv()
|
|
501
|
+
|
|
502
|
+
for i in range(x.nrows()):
|
|
503
|
+
for j in range(x.ncols()):
|
|
504
|
+
x[i,j] = round(x[i,j])
|
|
505
|
+
|
|
506
|
+
closest_vector = x * basis
|
|
507
|
+
|
|
508
|
+
if visualize:
|
|
509
|
+
if basis.nrows() != 2:
|
|
510
|
+
raise ValueError(f"[Utils] Can't visualize. Basis must be a {2}x{2} matrix, but got a {basis.nrows()}x{basis.ncols()} one")
|
|
511
|
+
Utils.visualize_lattice(basis, closest_vector, point, title="Babai rounding technique")
|
|
512
|
+
|
|
513
|
+
return closest_vector
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: GGH-crypto
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: GGH_crypto is a Python package for lattice-based cryptography, focusing on GGH and GGH-HNF implementations.
|
|
5
|
+
Home-page: https://github.com/TheGaBr0/GGH
|
|
6
|
+
Download-URL: https://github.com/TheGaBr0/GGH/archive/refs/tags/v1.0.4.tar.gz
|
|
7
|
+
Author: Gabriele Bottani
|
|
8
|
+
Author-email: gbotani19@gmail.com
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: GGH,GGH-HNF,GGH_CRYPTO,Lattice,LLL,BKZ,Lattice-based-cryptography
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE.txt
|
|
20
|
+
|
|
21
|
+
# GGH-crypto
|
|
22
|
+
GGH-crypto is a Python package implementing the Goldreich-Goldwasser-Halevi (GGH) public key cryptosystem and its optimization, GGH-HNF by Micciancio. This package is designed for educational and research purposes, offering insights into lattice-based cryptography.
|
|
23
|
+
This project was developed as part of a 3-year degree program at the Università degli Studi di Milano (University of Milan). It explores the resilience of lattice-based cryptography against quantum threats and introduces an hybrid variant.
|
|
24
|
+
|
|
25
|
+
# Features
|
|
26
|
+
|
|
27
|
+
- Implementation of the original GGH cryptosystem (1997)
|
|
28
|
+
- Implementation of the GGH-HNF optimization (2002)
|
|
29
|
+
- Utility functions for lattice-based cryptography
|
|
30
|
+
- Algorithms for solving the Closest Vector Problem (CVP)
|
|
31
|
+
- Lattice reduction algorithms
|
|
32
|
+
|
|
33
|
+
# Usage and details
|
|
34
|
+
For detailed installation, usage, examples and documentation, please visit the [GitHub repository](https://github.com/TheGaBr0/GGH).
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Note
|
|
38
|
+
Both the original GGH cryptosystem and its GGH-HNF optimization have known security vulnerabilities. This implementation is not intended for production use.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
README.md
|
|
3
|
+
setup.cfg
|
|
4
|
+
setup.py
|
|
5
|
+
GGH_crypto/__init__.py
|
|
6
|
+
GGH_crypto.egg-info/PKG-INFO
|
|
7
|
+
GGH_crypto.egg-info/SOURCES.txt
|
|
8
|
+
GGH_crypto.egg-info/dependency_links.txt
|
|
9
|
+
GGH_crypto.egg-info/requires.txt
|
|
10
|
+
GGH_crypto.egg-info/top_level.txt
|
|
11
|
+
GGH_crypto/GGH/GGH.py
|
|
12
|
+
GGH_crypto/GGH/__init__.py
|
|
13
|
+
GGH_crypto/GGH_HNF/GGH_HNF.py
|
|
14
|
+
GGH_crypto/GGH_HNF/__init__.py
|
|
15
|
+
GGH_crypto/Utils/Utils.py
|
|
16
|
+
GGH_crypto/Utils/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
GGH_crypto
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
Copyright (c) 2018 YOUR NAME
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
The above copyright notice and this permission notice shall be included in all
|
|
10
|
+
copies or substantial portions of the Software.
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
12
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
13
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
14
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
15
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
16
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
17
|
+
SOFTWARE.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: GGH_crypto
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: GGH_crypto is a Python package for lattice-based cryptography, focusing on GGH and GGH-HNF implementations.
|
|
5
|
+
Home-page: https://github.com/TheGaBr0/GGH
|
|
6
|
+
Download-URL: https://github.com/TheGaBr0/GGH/archive/refs/tags/v1.0.4.tar.gz
|
|
7
|
+
Author: Gabriele Bottani
|
|
8
|
+
Author-email: gbotani19@gmail.com
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: GGH,GGH-HNF,GGH_CRYPTO,Lattice,LLL,BKZ,Lattice-based-cryptography
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE.txt
|
|
20
|
+
|
|
21
|
+
# GGH-crypto
|
|
22
|
+
GGH-crypto is a Python package implementing the Goldreich-Goldwasser-Halevi (GGH) public key cryptosystem and its optimization, GGH-HNF by Micciancio. This package is designed for educational and research purposes, offering insights into lattice-based cryptography.
|
|
23
|
+
This project was developed as part of a 3-year degree program at the Università degli Studi di Milano (University of Milan). It explores the resilience of lattice-based cryptography against quantum threats and introduces an hybrid variant.
|
|
24
|
+
|
|
25
|
+
# Features
|
|
26
|
+
|
|
27
|
+
- Implementation of the original GGH cryptosystem (1997)
|
|
28
|
+
- Implementation of the GGH-HNF optimization (2002)
|
|
29
|
+
- Utility functions for lattice-based cryptography
|
|
30
|
+
- Algorithms for solving the Closest Vector Problem (CVP)
|
|
31
|
+
- Lattice reduction algorithms
|
|
32
|
+
|
|
33
|
+
# Usage and details
|
|
34
|
+
For detailed installation, usage, examples and documentation, please visit the [GitHub repository](https://github.com/TheGaBr0/GGH).
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Note
|
|
38
|
+
Both the original GGH cryptosystem and its GGH-HNF optimization have known security vulnerabilities. This implementation is not intended for production use.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# GGH-crypto
|
|
2
|
+
GGH-crypto is a Python package implementing the Goldreich-Goldwasser-Halevi (GGH) public key cryptosystem and its optimization, GGH-HNF by Micciancio. This package is designed for educational and research purposes, offering insights into lattice-based cryptography.
|
|
3
|
+
This project was developed as part of a 3-year degree program at the Università degli Studi di Milano (University of Milan). It explores the resilience of lattice-based cryptography against quantum threats and introduces an hybrid variant.
|
|
4
|
+
|
|
5
|
+
# Features
|
|
6
|
+
|
|
7
|
+
- Implementation of the original GGH cryptosystem (1997)
|
|
8
|
+
- Implementation of the GGH-HNF optimization (2002)
|
|
9
|
+
- Utility functions for lattice-based cryptography
|
|
10
|
+
- Algorithms for solving the Closest Vector Problem (CVP)
|
|
11
|
+
- Lattice reduction algorithms
|
|
12
|
+
|
|
13
|
+
# Usage and details
|
|
14
|
+
For detailed installation, usage, examples and documentation, please visit the [GitHub repository](https://github.com/TheGaBr0/GGH).
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Note
|
|
18
|
+
Both the original GGH cryptosystem and its GGH-HNF optimization have known security vulnerabilities. This implementation is not intended for production use.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
this_directory = Path(__file__).parent
|
|
5
|
+
long_description = (this_directory / "README.md").read_text()
|
|
6
|
+
|
|
7
|
+
setup(
|
|
8
|
+
name = 'GGH_crypto',
|
|
9
|
+
packages = find_packages(),
|
|
10
|
+
version = '1.0.4',
|
|
11
|
+
license='MIT',
|
|
12
|
+
description = 'GGH_crypto is a Python package for lattice-based cryptography, focusing on GGH and GGH-HNF implementations.',
|
|
13
|
+
long_description=long_description,
|
|
14
|
+
long_description_content_type='text/markdown',
|
|
15
|
+
author = 'Gabriele Bottani',
|
|
16
|
+
author_email = 'gbotani19@gmail.com',
|
|
17
|
+
url = 'https://github.com/TheGaBr0/GGH',
|
|
18
|
+
download_url = 'https://github.com/TheGaBr0/GGH/archive/refs/tags/v1.0.4.tar.gz',
|
|
19
|
+
keywords = ['GGH', 'GGH-HNF', 'GGH_CRYPTO', 'Lattice', 'LLL', 'BKZ', 'Lattice-based-cryptography'],
|
|
20
|
+
install_requires=[
|
|
21
|
+
'matplotlib',
|
|
22
|
+
'numpy',
|
|
23
|
+
'python_flint',
|
|
24
|
+
'sympy',
|
|
25
|
+
],
|
|
26
|
+
classifiers=[
|
|
27
|
+
'Development Status :: 5 - Production/Stable',
|
|
28
|
+
'Intended Audience :: Developers',
|
|
29
|
+
'Topic :: Software Development :: Build Tools',
|
|
30
|
+
'License :: OSI Approved :: MIT License',
|
|
31
|
+
'Programming Language :: Python :: 3.10',
|
|
32
|
+
'Programming Language :: Python :: 3.11',
|
|
33
|
+
'Programming Language :: Python :: 3.12'
|
|
34
|
+
],
|
|
35
|
+
)
|