kryptools 1.3.5__tar.gz → 1.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.
Files changed (58) hide show
  1. {kryptools-1.3.5 → kryptools-1.4}/PKG-INFO +4 -2
  2. {kryptools-1.3.5 → kryptools-1.4}/README.md +3 -1
  3. {kryptools-1.3.5 → kryptools-1.4}/kryptools/GF2.py +2 -2
  4. {kryptools-1.3.5 → kryptools-1.4}/kryptools/Zmod.py +2 -2
  5. {kryptools-1.3.5 → kryptools-1.4}/kryptools/__init__.py +3 -3
  6. kryptools-1.4/kryptools/code.py +326 -0
  7. {kryptools-1.3.5 → kryptools-1.4}/kryptools/dlp_qs.py +1 -1
  8. {kryptools-1.3.5 → kryptools-1.4}/kryptools/poly.py +20 -0
  9. {kryptools-1.3.5 → kryptools-1.4}/kryptools.egg-info/PKG-INFO +4 -2
  10. {kryptools-1.3.5 → kryptools-1.4}/pyproject.toml +1 -1
  11. {kryptools-1.3.5 → kryptools-1.4}/tests/test_code.py +22 -1
  12. {kryptools-1.3.5 → kryptools-1.4}/tests/test_poly.py +15 -2
  13. kryptools-1.3.5/kryptools/code.py +0 -140
  14. {kryptools-1.3.5 → kryptools-1.4}/LICENSE +0 -0
  15. {kryptools-1.3.5 → kryptools-1.4}/kryptools/aes.py +0 -0
  16. {kryptools-1.3.5 → kryptools-1.4}/kryptools/blockcipher.py +0 -0
  17. {kryptools-1.3.5 → kryptools-1.4}/kryptools/conway_polynomials.py +0 -0
  18. {kryptools-1.3.5 → kryptools-1.4}/kryptools/des.py +0 -0
  19. {kryptools-1.3.5 → kryptools-1.4}/kryptools/dlp.py +0 -0
  20. {kryptools-1.3.5 → kryptools-1.4}/kryptools/dlp_bsgs.py +0 -0
  21. {kryptools-1.3.5 → kryptools-1.4}/kryptools/dlp_ic.py +0 -0
  22. {kryptools-1.3.5 → kryptools-1.4}/kryptools/dlp_rho.py +0 -0
  23. {kryptools-1.3.5 → kryptools-1.4}/kryptools/ec.py +0 -0
  24. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor.py +0 -0
  25. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor_dix.py +0 -0
  26. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor_ecm.py +0 -0
  27. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor_fmt.py +0 -0
  28. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor_pm1.py +0 -0
  29. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor_qs.py +0 -0
  30. {kryptools-1.3.5 → kryptools-1.4}/kryptools/factor_rho.py +0 -0
  31. {kryptools-1.3.5 → kryptools-1.4}/kryptools/intfuncs.py +0 -0
  32. {kryptools-1.3.5 → kryptools-1.4}/kryptools/keccak.py +0 -0
  33. {kryptools-1.3.5 → kryptools-1.4}/kryptools/la.py +0 -0
  34. {kryptools-1.3.5 → kryptools-1.4}/kryptools/lat.py +0 -0
  35. {kryptools-1.3.5 → kryptools-1.4}/kryptools/nt.py +0 -0
  36. {kryptools-1.3.5 → kryptools-1.4}/kryptools/primes.py +0 -0
  37. {kryptools-1.3.5 → kryptools-1.4}/kryptools/sha1.py +0 -0
  38. {kryptools-1.3.5 → kryptools-1.4}/kryptools.egg-info/SOURCES.txt +0 -0
  39. {kryptools-1.3.5 → kryptools-1.4}/kryptools.egg-info/dependency_links.txt +0 -0
  40. {kryptools-1.3.5 → kryptools-1.4}/kryptools.egg-info/top_level.txt +0 -0
  41. {kryptools-1.3.5 → kryptools-1.4}/setup.cfg +0 -0
  42. {kryptools-1.3.5 → kryptools-1.4}/tests/test_GF2.py +0 -0
  43. {kryptools-1.3.5 → kryptools-1.4}/tests/test_Zmod.py +0 -0
  44. {kryptools-1.3.5 → kryptools-1.4}/tests/test_aes.py +0 -0
  45. {kryptools-1.3.5 → kryptools-1.4}/tests/test_des.py +0 -0
  46. {kryptools-1.3.5 → kryptools-1.4}/tests/test_dlog.py +0 -0
  47. {kryptools-1.3.5 → kryptools-1.4}/tests/test_ec.py +0 -0
  48. {kryptools-1.3.5 → kryptools-1.4}/tests/test_factor.py +0 -0
  49. {kryptools-1.3.5 → kryptools-1.4}/tests/test_factor_fmt.py +0 -0
  50. {kryptools-1.3.5 → kryptools-1.4}/tests/test_factor_pm1.py +0 -0
  51. {kryptools-1.3.5 → kryptools-1.4}/tests/test_factor_qs.py +0 -0
  52. {kryptools-1.3.5 → kryptools-1.4}/tests/test_intfuncs.py +0 -0
  53. {kryptools-1.3.5 → kryptools-1.4}/tests/test_keccak.py +0 -0
  54. {kryptools-1.3.5 → kryptools-1.4}/tests/test_la.py +0 -0
  55. {kryptools-1.3.5 → kryptools-1.4}/tests/test_lat.py +0 -0
  56. {kryptools-1.3.5 → kryptools-1.4}/tests/test_nt.py +0 -0
  57. {kryptools-1.3.5 → kryptools-1.4}/tests/test_primes.py +0 -0
  58. {kryptools-1.3.5 → kryptools-1.4}/tests/test_sha1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.3.5
3
+ Version: 1.4
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  License: MIT
@@ -42,10 +42,12 @@ The tools contained are:
42
42
  * linear codes: Hamming distance, left standard form, parity check matrix
43
43
 
44
44
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
45
- * Poly: a class for polynomials (division, modulo)
45
+ * Poly: a class for polynomials (division, modulo, irreducibility test, Lagrange interpolation)
46
46
  * Zmod: a class for the ring of integers modulo an integer
47
47
  * GF2: a class for Galois fields GF(2^n)
48
48
  * Goppa: a class for encoding and decoding Goppa codes
49
+ * CyclicCode: a class for encoding and decoding cyclic codes
50
+ * ReedSolomonCode: a class for encoding and decoding Reed-Solomon codes
49
51
 
50
52
  * BlockCipher: a class implementing the usual modes of operation (ECB, CBC, GCM, etc.)
51
53
  * AESCipher: individual AES operations
@@ -24,10 +24,12 @@ The tools contained are:
24
24
  * linear codes: Hamming distance, left standard form, parity check matrix
25
25
 
26
26
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
27
- * Poly: a class for polynomials (division, modulo)
27
+ * Poly: a class for polynomials (division, modulo, irreducibility test, Lagrange interpolation)
28
28
  * Zmod: a class for the ring of integers modulo an integer
29
29
  * GF2: a class for Galois fields GF(2^n)
30
30
  * Goppa: a class for encoding and decoding Goppa codes
31
+ * CyclicCode: a class for encoding and decoding cyclic codes
32
+ * ReedSolomonCode: a class for encoding and decoding Reed-Solomon codes
31
33
 
32
34
  * BlockCipher: a class implementing the usual modes of operation (ECB, CBC, GCM, etc.)
33
35
  * AESCipher: individual AES operations
@@ -57,8 +57,8 @@ class GF2:
57
57
  def __repr__(self):
58
58
  return f"GF(2^{self.power})"
59
59
 
60
- def __call__(self, x: int | list | tuple):
61
- if isinstance(x, list|tuple):
60
+ def __call__(self, x: int | list | tuple | range | map):
61
+ if isinstance(x, list|tuple|range|map):
62
62
  return [GF2nPoint(xx, self) for xx in x]
63
63
  return GF2nPoint(x, self)
64
64
 
@@ -39,8 +39,8 @@ class Zmod:
39
39
  def __repr__(self):
40
40
  return f"Z_{self.n}"
41
41
 
42
- def __call__(self, x: int | list | tuple):
43
- if isinstance(x, list|tuple):
42
+ def __call__(self, x: int | list | tuple | range | map):
43
+ if isinstance(x, list|tuple|range|map):
44
44
  return [ZmodPoint(xx, self) for xx in x]
45
45
  return ZmodPoint(x, self)
46
46
 
@@ -5,7 +5,7 @@ Implemenation of same basic algorithms used in cryptography.
5
5
  __author__ = "Gerald Teschl"
6
6
  __copyright__ = "Copyright 2024-2025, Gerald Teschl"
7
7
  __license__ = "MIT License"
8
- __version__ = "1.3.5"
8
+ __version__ = "1.4"
9
9
  __email__ = "Gerald.Teschl@univie.ac.at"
10
10
 
11
11
  from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, is_carmichael_number, order, crt
@@ -16,10 +16,10 @@ from .dlp import dlog
16
16
  from .ec import EC_Weierstrass
17
17
  from .la import Matrix, zeros, eye
18
18
  from .lat import gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round_cvp, babai_round_bnd, babai_plane_cvp, babai_plane_bnd, lagrange_lr, lll, random_unimodular_matrix
19
- from .poly import Poly
19
+ from .poly import Poly, lagrange_interpolation
20
20
  from .Zmod import Zmod
21
21
  from .GF2 import GF2, GF2_aes, GF2_miniaes, GF2_ghash
22
- from .code import left_standard_form, gen2pchk, hamming_dist, Goppa
22
+ from .code import left_standard_form, gen2pchk, hamming_dist, Goppa, CyclicCode, ReedSolomonCode
23
23
  from .blockcipher import BlockCipher, bytexor
24
24
  from .aes import AESKeySchedule, AESCipher, AESBlockCipher
25
25
  from .des import list2int, int2list, permute, invert_permutation, rotate_left, SDESKeySchedule, SDESCipher, DESKeySchedule, DESCipher, DESBlockCipher
@@ -0,0 +1,326 @@
1
+ """
2
+ Linear codes
3
+ """
4
+
5
+ from math import prod
6
+ from .Zmod import Zmod
7
+ from .GF2 import GF2
8
+ from .poly import Poly, lagrange_interpolation
9
+ from .la import Matrix, eye, zeros
10
+
11
+
12
+ def poly2bits(x: Poly) -> list:
13
+ "Convert a polynomial in GF(2^n)[x] to a list of binary digits."
14
+ out = []
15
+ for c in x.coeff:
16
+ out += c.bits()
17
+ return out
18
+
19
+ def left_standard_form(M: Matrix) -> (Matrix, Matrix):
20
+ "Compute the left standard form of a generator matrix. Return the standard form and the pertmutation matrix."
21
+ # reduced row echelon form
22
+ M = M.rref()
23
+ # purge zero rows
24
+ for i in range(M.rows-1,-1,-1):
25
+ if not M[i,:]:
26
+ M.delete_rows(i)
27
+ # permute columns to get the identity on the left
28
+ last = min(M.rows, M.cols)
29
+ P = eye(M.cols)
30
+ for i in range(last):
31
+ if not M[i,i]: # the diagonal entry vanishes
32
+ # find the index of the pivot and permute
33
+ j = i + 1
34
+ while j <= M.cols and not M[i, j]:
35
+ j += 1
36
+ P[:,i], P[:,j] = P[:,j], P[:,i]
37
+ M[:,i], M[:,j] = M[:,j], M[:,i]
38
+ return M, P
39
+
40
+ def gen2pchk(G: Matrix) -> Matrix:
41
+ "Compute the parity check matrix from a given generator matrix (and vice versa)."
42
+ G, P = left_standard_form(G)
43
+ d = G.cols - G.rows
44
+ if d <= 0:
45
+ raise ValueError("A generator matrix must have more columns than rows!")
46
+ zero = 0 * G[0]
47
+ one = zero**0
48
+ # we start with a zero matrix
49
+ H = zeros(d, G.cols, zero = zero)
50
+ # create the identity on the right
51
+ for i in range(d):
52
+ H[i,G.rows+i] = one
53
+ # add minus the transpose of the right part of G as the left part
54
+ tmp = - G[:,-d:].transpose()
55
+ for i in range(G.cols - d):
56
+ H[:, i] = tmp[:, i]
57
+ return H * P.transpose()
58
+
59
+ def hamming_dist(a: iter, b: iter):
60
+ "Hamming distance of two iterables."
61
+ if len(a) != len(b):
62
+ raise ValueError("Both iteralbles must have equal length.")
63
+ return sum(aa != bb for aa, bb in zip(a, b))
64
+
65
+ class Goppa():
66
+ """
67
+ Binary irreducible Goppa code.
68
+
69
+ Example:
70
+
71
+ >>> gf = GF2(4) # base field GF(2^4)
72
+ >>> g = Poly([2, 0, 0, 1], ring = gf) # irreducible Polynomial for the code
73
+ >>> alpha = list(gf) # list of points from the base field (must not contain zeros of g)
74
+ >>> goppa = Goppa(gf, g, alpha)
75
+
76
+ To encode a list of bits (the length must be equal to the dimension k of the code)
77
+ >>> goppa.encode([1, 0, 1, 1])
78
+ [0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1]
79
+
80
+ Decoding still works if there are at most deg(g) errors:
81
+ >>> goppa.decode([0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0])
82
+ [1, 0, 1, 1]
83
+
84
+ """
85
+
86
+ def __init__(self, gf: GF2, g: Poly, alpha: list["GF2Point"]):
87
+ if not all(map(lambda x: x in gf, alpha)):
88
+ raise ValueError(f"{x}: alpha must be a list of elements from the Galois field!")
89
+ if not g.rabin_test():
90
+ raise ValueError("The polynomial g must be irreducible!")
91
+ self.gf = gf # underlying Galois field
92
+ self.g = g # polynomial
93
+ self.alpha = alpha # list of elements from gf
94
+ # Compute the control matrix
95
+ self.Z_2 = Zmod(2)
96
+
97
+ tmp =[]
98
+ for x in alpha:
99
+ tmp.append(Poly([-x, 1], ring = gf, modulus = g).inv().bits())
100
+ self.H = Matrix(tmp, ring=self.Z_2).transpose()
101
+ # Compute the generator matrix
102
+ self.G = gen2pchk(self.H)
103
+
104
+ def __repr__(self):
105
+ return f"Binary irreducible Goppa code over GF(2^{self.gf.power}) with polynomial g(x) = {self.g}."
106
+
107
+ def encode(self, x: list[int]):
108
+ "Encode a given list of bits."
109
+ x = Matrix(x, ring = self.Z_2)
110
+ y = self.G.transpose() * x
111
+ y.map(int)
112
+ return list(y)
113
+
114
+ def decode(self, y: list[int]):
115
+ "Decode a given list of bits."
116
+ y = Matrix(y, ring = self.Z_2)
117
+ if self.H * y: # no code word
118
+ s = self.gf(0) # syndrom polynomial
119
+ for i, x in enumerate(self.alpha):
120
+ s += int(y[i]) * Poly([-x, 1], ring = self.gf, modulus = self.g).inv()
121
+ v = (s.inv() - Poly([0, 1], ring = self.gf, modulus = self.g))**(self.gf.order ** self.g.degree() // 2)
122
+ # determine error locator polynomial sigma(x) = a(x^2) + b(x^2) * x
123
+ # find a and b by running EEA until b has the desired degree
124
+ v.modulus = None
125
+ r0, r1 = self.g, v
126
+ y0, y1 = Poly([self.gf(0)]), Poly([self.gf(1)])
127
+ while 2 * r1.degree() > self.g.degree():
128
+ q, r = r0.divmod(r1)
129
+ r0, r1 = r1, r
130
+ y0, y1 = y1, y0 - q * y1
131
+ a, b = r1, y1
132
+ sigma = a**2 + b**2 * Poly([0,1], ring = self.gf)
133
+ # correct the error
134
+ for x in self.alpha:
135
+ if not sigma(x):
136
+ y[int(x)] += 1
137
+ if self.H * y:
138
+ raise ValueError("Decoding failed!")
139
+ x = self.G.transpose().solve(y)
140
+ x.map(int)
141
+ return list(x)
142
+
143
+ class CyclicCode():
144
+ """
145
+ Cyclic code.
146
+
147
+ Example:
148
+
149
+ >>> n = 4 # length of the code
150
+ >>> g = Poly([1, 1], ring = Zmod(2)) # generating Polynomial for the code
151
+ >>> cc = CyclicCode(n, g)
152
+
153
+ To encode a list of letters (the length must be equal to the dimension k of the code):
154
+ >>> cc.encode([0,1,0], ring = Zmod(2))
155
+ [0, 1, 1, 0]
156
+
157
+ Decoding is only implemnted for code words:
158
+ >>> cc.decode([0, 1, 1, 0], ring = Zmod(2))
159
+ [0, 1, 0]
160
+
161
+ """
162
+
163
+ def __init__(self, n:int, g: Poly):
164
+ if n < 2:
165
+ raise ValueError(f"Code lenght {n} must be at least 2!")
166
+ zero = 0 * g[0]
167
+ try:
168
+ order = zero.ring.n # Zmod
169
+ except:
170
+ try:
171
+ order = zero.field.order # GF2
172
+ except:
173
+ try:
174
+ order = type(zero).order # galois
175
+ except:
176
+ raise ValueError("Unknown base filed!")
177
+ self.order = order # order of the base field
178
+ one = zero**0
179
+ modulus = Poly([-one] + [zero] * (n-1) + [one])
180
+ self.modulus = modulus
181
+ if modulus % g:
182
+ raise ValueError(f"The polynomial g must be a factor of x^{n} - 1!")
183
+ self.n = n # lenght
184
+ self.k = n - g.degree() # dimension
185
+ self.g = g # generating polynomial
186
+ self.h = Poly(modulus) // g
187
+
188
+ def __repr__(self):
189
+ return f"Cyclic [{self.n}, {self.k}] code over GF({self.order}) with generator polynomial g(x) = {self.g}."
190
+
191
+ def encode(self, x: list|Poly, gf = None, systematic_form = False):
192
+ "Encode a given list or polynomial."
193
+ as_list = False
194
+ zero = 0 * self.g[0]
195
+ if isinstance(x, list | tuple):
196
+ as_list = True
197
+ x = Poly(x, ring = gf)
198
+ if x.degree() >= self.k:
199
+ raise ValueError(f"Degree can be at most {self.k-1}.")
200
+ if systematic_form:
201
+ x.coeff = [zero] * self.g.degree() + x.coeff
202
+ y = x - (x % self.g)
203
+ else:
204
+ y = self.g * x
205
+ if as_list:
206
+ return y.coeff + [zero] * (self.n - len(y.coeff))
207
+ return y
208
+
209
+ def decode(self, y: list|Poly, gf = None, systematic_form = False):
210
+ "Decode a given list or polynomial."
211
+ as_list = False
212
+ zero = 0 * self.g[0]
213
+ if isinstance(y, list | tuple):
214
+ as_list = True
215
+ y = Poly(y, ring = gf)
216
+ if y.degree() >= self.n:
217
+ raise ValueError(f"Degree can be at most {self.n-1}.")
218
+ x, r = y.divmod(self.g)
219
+ if r:
220
+ raise NotImplementedError(f"Word is no code word! Cannot decode.")
221
+ if systematic_form:
222
+ x.coeff = y.coeff[self.g.degree():]
223
+ if as_list:
224
+ return x.coeff + [zero] * (self.k - len(x.coeff))
225
+ return x
226
+
227
+ def generator_matrix(self):
228
+ "Return the generator matrix of the code."
229
+ zero = 0 * self.g[0]
230
+ return Matrix([[zero] * j + self.g.coeff + [zero] * (self.k - j - 1 ) for j in range(self.k)])
231
+
232
+ def check_matrix(self):
233
+ "Return the parity check matrix of the code."
234
+ zero = 0 * self.g[0]
235
+ tmp = list(reversed(self.h.coeff))
236
+ return Matrix([[zero] * j + tmp + [zero] * (self.n - self.k - j - 1 ) for j in range(self.n-self.k)])
237
+
238
+ class ReedSolomonCode(CyclicCode):
239
+ """
240
+ Reed-Solomon code.
241
+
242
+ Example:
243
+
244
+ >>> gf = Zmod(13) # Galois field
245
+ >>> k = 3 # dimension of the code
246
+ >>> rsc = ReedSolomonCode(k, gf)
247
+
248
+ To encode a list of letters (the length must be equal to the dimension k of the code; conversion to the Galois field is done automatically):
249
+ >>> cc.encode([1, 2, 3])
250
+ [0, 1, 1, 0]
251
+
252
+ Decoding is only implemnted for code words:
253
+ >>> cc.decode([0, 1, 1, 0], ring = Zmod(2))
254
+ [0, 1, 0]
255
+
256
+ """
257
+ def __init__(self, k:int, gf, alpha = None):
258
+ zero = gf(0)
259
+ one = zero**0
260
+ self. gf = gf
261
+ try:
262
+ n = gf.order()
263
+ except:
264
+ n = gf.order - 1
265
+ self.order = n + 1 # order of the base field
266
+ self.n = n # lenght
267
+ self.k = k # dimension
268
+ self.d = n - k - 1 # minimal distance
269
+ self.modulus = Poly([-one] + [zero] * (n-1) + [one])
270
+ if alpha is None:
271
+ try:
272
+ alpha = gf.generator() # Zmod|GF2
273
+ except:
274
+ alpha = gf.primitive_element # galois
275
+ self.alpha = alpha
276
+ self.g = prod([Poly([-alpha**j, one]) for j in range(1,n-k+1)], start = one) # generating polynomial
277
+ self.h = Poly(self.modulus) // self.g # check polynomial
278
+ assert k == n - self.g.degree()
279
+ assert self.g * self.h == self.modulus
280
+
281
+ def __repr__(self):
282
+ return f"Reed-Solomon-Code [{self.n}, {self.k}, {self.d}] over GF({self.n+1})."
283
+
284
+ def encode(self, x: list|Poly):
285
+ "Encode a given list or polynomial."
286
+ as_list = False
287
+ zero = 0 * self.g[0]
288
+ if isinstance(x, list | tuple):
289
+ as_list = True
290
+ x = Poly(x, ring = self.gf)
291
+ if x.degree() >= self.k:
292
+ raise ValueError(f"Degree can be at most {self.k-1}.")
293
+ y = [ x(self.alpha**j) for j in range(self.n) ]
294
+ if as_list:
295
+ return y + [zero] * (self.n - len(y))
296
+ return Poly(y)
297
+
298
+ def decode(self, y: list|Poly):
299
+ "Decode a given list or polynomial."
300
+ as_list = False
301
+ zero = 0 * self.g[0]
302
+ one = zero**0
303
+ if isinstance(y, list | tuple):
304
+ as_list = True
305
+ y = Poly(y, ring = self.gf)
306
+ if y.degree() >= self.n:
307
+ raise ValueError(f"Degree can be at most {self.n-1}.")
308
+ x = Poly([ -y(self.alpha**(-j)) for j in range(self.n) ])
309
+ if x.degree() >= self.k: # No codewort
310
+ t = (self.n - self.k) // 2
311
+ A = Matrix([[ x[j - l % self.n] for l in range(1, t+1) ] for j in range(self.k+t, self.k+2*t) ])
312
+ b = [ -x[self.k + t + l ] for l in range(t) ]
313
+ lam = Poly([one] + list(A.solve(b)))
314
+ x_coordinates = []
315
+ y_coordinates = []
316
+ for j in range(self.n):
317
+ if len(x_coordinates) == self.k:
318
+ break
319
+ alphaj = self.alpha**j
320
+ if lam(alphaj):
321
+ x_coordinates.append(alphaj)
322
+ y_coordinates.append(y[j])
323
+ x = lagrange_interpolation(x_coordinates, y_coordinates)
324
+ if as_list:
325
+ return x.coeff + [zero] * (self.k - len(x.coeff))
326
+ return x
@@ -97,7 +97,7 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
97
97
  break
98
98
  trys += 1
99
99
  else:
100
- raise Exception(f"Sorry, Quadratic Sieve failed failed to find a new relation after {trys} trys.")
100
+ raise Exception(f"Sorry, Quadratic Sieve failed to find a new relation after {trys} trys.")
101
101
  if verbose > 2:
102
102
  if include_b:
103
103
  print(f"rel found: b*a^{x}=", b * pow(a, x, n) % n, relation)
@@ -2,6 +2,7 @@
2
2
  Polynomials
3
3
  """
4
4
 
5
+ from math import prod
5
6
  from numbers import Number
6
7
  from .primes import is_prime
7
8
  #from .factor import factorint
@@ -407,3 +408,22 @@ class Poly:
407
408
  return False
408
409
  return True
409
410
 
411
+
412
+ def lagrange_interpolation(x_coordinates:list, y_coordinates:list) -> "Poly":
413
+ "Compute the Lagrange interpolation polynomial from a given list of x and y values."
414
+ l = len(x_coordinates)
415
+ if l == 0 or l != len(y_coordinates):
416
+ raise ValueError('List of x and y values must be nonempty of equal length.')
417
+ if l != len(set(x_coordinates)):
418
+ raise ValueError('List of x values must not contain duplicate values.')
419
+ # Guess zero and one
420
+ zero = 0 * x_coordinates[0]
421
+ one = zero**0
422
+
423
+ poly = Poly([zero])
424
+ L_all = prod([Poly([-x, one]) for x in x_coordinates], start = one)
425
+
426
+ for x, y in zip(x_coordinates, y_coordinates):
427
+ L = L_all // Poly([-x, one])
428
+ poly += (y / prod([x -xx for xx in x_coordinates if xx != x], start = one)) * L
429
+ return poly
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.3.5
3
+ Version: 1.4
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  License: MIT
@@ -42,10 +42,12 @@ The tools contained are:
42
42
  * linear codes: Hamming distance, left standard form, parity check matrix
43
43
 
44
44
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
45
- * Poly: a class for polynomials (division, modulo)
45
+ * Poly: a class for polynomials (division, modulo, irreducibility test, Lagrange interpolation)
46
46
  * Zmod: a class for the ring of integers modulo an integer
47
47
  * GF2: a class for Galois fields GF(2^n)
48
48
  * Goppa: a class for encoding and decoding Goppa codes
49
+ * CyclicCode: a class for encoding and decoding cyclic codes
50
+ * ReedSolomonCode: a class for encoding and decoding Reed-Solomon codes
49
51
 
50
52
  * BlockCipher: a class implementing the usual modes of operation (ECB, CBC, GCM, etc.)
51
53
  * AESCipher: individual AES operations
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "1.3.5"
3
+ version = "1.4"
4
4
  authors = [
5
5
  { name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
6
6
  ]
@@ -1,7 +1,7 @@
1
1
  import pytest
2
2
  from random import randint, seed
3
3
  from kryptools import Zmod, GF2, Poly, Matrix
4
- from kryptools import gen2pchk, Goppa
4
+ from kryptools import gen2pchk, Goppa, CyclicCode, ReedSolomonCode
5
5
 
6
6
  seed(0)
7
7
  Z_2 = Zmod(2)
@@ -39,3 +39,24 @@ def test_Goppa():
39
39
  i = randint(0, goppa.G.cols-1)
40
40
  y[i] = 1- y[i]
41
41
  assert goppa.decode(y) == x
42
+
43
+ def test_Cyclic():
44
+ for gf, n, g in [ [Zmod(2), 4, [1, 1]], [Zmod(3), 4, [-1, 1]], [Zmod(11), 5, [4, 6, 1]] ]:
45
+ g = Poly(g, ring = gf)
46
+ cc = CyclicCode(n, g)
47
+ for _ in range(num_tests):
48
+ x = [ gf(randint(0, cc.order-1)) for _ in range(cc.k) ]
49
+ y = cc.encode(x)
50
+ assert cc.decode(y) == x
51
+
52
+ def test_ReedSolomon():
53
+ for gf, k in [ [Zmod(13), 3], [GF2(6), 4]]:
54
+ rsc = ReedSolomonCode(k, gf)
55
+ t = (rsc.n - rsc.k) // 2
56
+ for _ in range(num_tests):
57
+ x = [ gf(randint(0, rsc.n)) for _ in range(rsc.k) ]
58
+ y = rsc.encode(x)
59
+ for _ in range(t):
60
+ i = randint(0, rsc.n-1)
61
+ y[i] += gf(1)
62
+ assert rsc.decode(y) == x
@@ -1,5 +1,6 @@
1
1
  import pytest
2
- from kryptools import Poly, Zmod, GF2
2
+ from random import sample, choices
3
+ from kryptools import Poly, Zmod, GF2, lagrange_interpolation
3
4
  from kryptools import divisors, moebius_mu
4
5
 
5
6
 
@@ -33,4 +34,16 @@ def test_rabin():
33
34
  a = Poly(c, ring=gf)
34
35
  if a.rabin_test():
35
36
  count += 1
36
- assert count == sum([moebius_mu(d) * order ** (t // d) for d in divisors(t)]) // t
37
+ assert count == sum([moebius_mu(d) * order ** (t // d) for d in divisors(t)]) // t
38
+
39
+ def test_lagrange():
40
+ for gf in [ Zmod(2), Zmod(3), Zmod(5), Zmod(7), Zmod(23), GF2(4), GF2(6)]:
41
+ all = list(gf)
42
+ l = len(all)//2
43
+ for _ in range(10):
44
+ x_coordinates = sample(all, l)
45
+ y_coordinates = choices(all, k = l)
46
+ p = lagrange_interpolation(x_coordinates, y_coordinates)
47
+ assert p.degree() <= len(x_coordinates) - 1
48
+ for x, y in zip(x_coordinates, y_coordinates):
49
+ assert p(x) == y
@@ -1,140 +0,0 @@
1
- """
2
- Linear codes
3
- """
4
-
5
- from .Zmod import Zmod
6
- from .GF2 import GF2
7
- from .poly import Poly
8
- from .la import Matrix, eye, zeros
9
-
10
-
11
- def poly2bits(x: Poly) -> list:
12
- "Convert a polynomial in GF(2^n)[x] to a list of binary digits."
13
- out = []
14
- for c in x.coeff:
15
- out += c.bits()
16
- return out
17
-
18
- def left_standard_form(M: Matrix) -> (Matrix, Matrix):
19
- "Compute the left standard form of a generator matrix. Return the standard form and the pertmutation matrix."
20
- # reduced row echelon form
21
- M = M.rref()
22
- # purge zero rows
23
- for i in range(M.rows-1,-1,-1):
24
- if not M[i,:]:
25
- M.delete_rows(i)
26
- # permute columns to get the identity on the left
27
- last = min(M.rows, M.cols)
28
- P = eye(M.cols)
29
- for i in range(last):
30
- if not M[i,i]: # the diagonal entry vanishes
31
- # find the index of the pivot and permute
32
- j = i + 1
33
- while j <= M.cols and not M[i, j]:
34
- j += 1
35
- P[:,i], P[:,j] = P[:,j], P[:,i]
36
- M[:,i], M[:,j] = M[:,j], M[:,i]
37
- return M, P
38
-
39
- def gen2pchk(G: Matrix) -> Matrix:
40
- "Compute the parity check matrix from a given generator matrix (and vice versa)."
41
- G, P = left_standard_form(G)
42
- d = G.cols - G.rows
43
- if d <= 0:
44
- raise ValueError("A generator matrix must have more columns than rows!")
45
- zero = 0 * G[0]
46
- one = zero**0
47
- # we start with a zero matrix
48
- H = zeros(d, G.cols, zero = zero)
49
- # create the identity on the right
50
- for i in range(d):
51
- H[i,G.rows+i] = one
52
- # add minus the transpose of the right part of G as the left part
53
- tmp = - G[:,-d:].transpose()
54
- for i in range(G.cols - d):
55
- H[:, i] = tmp[:, i]
56
- return H * P.transpose()
57
-
58
- def hamming_dist(a: iter, b: iter):
59
- "Hamming distance of two iterables."
60
- if len(a) != len(b):
61
- raise ValueError("Both iteralbles must have equal length.")
62
- return sum(aa != bb for aa, bb in zip(a, b))
63
-
64
- class Goppa():
65
- """
66
- Binary irreducible Goppa code.
67
-
68
- Example:
69
-
70
- >>> gf = GF2(4) # base field GF(2^n)
71
- >>> g = Poly([2, 0, 0, 1], ring = gf) # irreducible Polynomial for the code
72
- >>> alpha = list(gf) # list of points from the base field (must not contain zeros of g)
73
- >>> goppa = Goppa(gf, g, alpha)
74
-
75
- To encode a list of bits (the length must be equal to the dimension k of the code)
76
- >>> goppa.encode([1, 0, 1, 1])
77
- [0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1]
78
-
79
- Decoding still works if there are at most deg(g) errors:
80
- >>> goppa.decode([0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0])
81
- [1, 0, 1, 1]
82
-
83
- """
84
-
85
- def __init__(self, gf: GF2, g: Poly, alpha: list["GF2Point"]):
86
- if not all(map(lambda x: x in gf, alpha)):
87
- raise ValueError(f"{x}: alpha must be a list of elements from the Galois field!")
88
- if not g.rabin_test():
89
- raise ValueError(f"The polynomial g must be irreducible!")
90
- self.gf = gf # underlying Galois field
91
- self.g = g # polynomial
92
- self.alpha = alpha # list of elements from gf
93
- # Compute the control matrix
94
- self.Z_2 = Zmod(2)
95
-
96
- tmp =[]
97
- for x in alpha:
98
- tmp.append(Poly([-x, 1], ring = gf, modulus = g).inv().bits())
99
- self.H = Matrix(tmp, ring=self.Z_2).transpose()
100
- # Compute the generator matrix
101
- self.G = gen2pchk(self.H)
102
-
103
- def __repr__(self):
104
- return f"Binary irreducible Goppa code over GF(2^{self.gf.power}) with polynomial g(x) = {self.g}."
105
-
106
- def encode(self, x: list[int]):
107
- "Encode a given list of bits."
108
- x = Matrix(x, ring = self.Z_2)
109
- y = self.G.transpose() * x
110
- y.map(int)
111
- return list(y)
112
-
113
- def decode(self, y: list[int]):
114
- "Decode a given list of bits."
115
- y = Matrix(y, ring = self.Z_2)
116
- if self.H * y: # no code word
117
- s = self.gf(0) # syndrom polynomial
118
- for i, x in enumerate(self.alpha):
119
- s += int(y[i]) * Poly([-x, 1], ring = self.gf, modulus = self.g).inv()
120
- v = (s.inv() - Poly([0, 1], ring = self.gf, modulus = self.g))**(self.gf.order ** self.g.degree() // 2)
121
- # determine error locator polynomial sigma(x) = a(x^2) + b(x^2) * x
122
- # find a and b by running EEA until b has the desired degree
123
- v.modulus = None
124
- r0, r1 = self.g, v
125
- y0, y1 = Poly([self.gf(0)]), Poly([self.gf(1)])
126
- while 2 * r1.degree() > self.g.degree():
127
- q, r = r0.divmod(r1)
128
- r0, r1 = r1, r
129
- y0, y1 = y1, y0 - q * y1
130
- a, b = r1, y1
131
- sigma = a**2 + b**2 * Poly([0,1], ring = self.gf)
132
- # correct the error
133
- for x in self.alpha:
134
- if not sigma(x):
135
- y[int(x)] += 1
136
- if self.H * y:
137
- raise ValueError("Decoding failed!")
138
- x = self.G.transpose().solve(y)
139
- x.map(int)
140
- return list(x)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes