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.
@@ -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,3 @@
1
+ from .GGH import GGHCryptosystem
2
+
3
+ __all__ = ['GGHCryptosystem']
@@ -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,3 @@
1
+ from .GGH_HNF import GGHHNFCryptosystem
2
+
3
+ __all__ = ['GGHHNFCryptosystem']
@@ -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,3 @@
1
+ from .Utils import Utils
2
+
3
+ __all__ = ['Utils']
@@ -0,0 +1,5 @@
1
+ from .GGH.GGH import GGHCryptosystem
2
+ from .GGH_HNF.GGH_HNF import GGHHNFCryptosystem
3
+ from .Utils.Utils import Utils
4
+
5
+ __all__ = ['GGHCryptosystem', 'GGHHNFCryptosystem', 'Utils']
@@ -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,4 @@
1
+ matplotlib
2
+ numpy
3
+ python_flint
4
+ sympy
@@ -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,7 @@
1
+ [metadata]
2
+ description-file = README.md
3
+
4
+ [egg_info]
5
+ tag_build =
6
+ tag_date = 0
7
+
@@ -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
+ )