kryptools 1.8.2__tar.gz → 1.9__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 (59) hide show
  1. {kryptools-1.8.2 → kryptools-1.9}/PKG-INFO +3 -2
  2. {kryptools-1.8.2 → kryptools-1.9}/README.md +2 -1
  3. {kryptools-1.8.2 → kryptools-1.9}/kryptools/Zmod.py +9 -2
  4. {kryptools-1.8.2 → kryptools-1.9}/kryptools/__init__.py +3 -3
  5. {kryptools-1.8.2 → kryptools-1.9}/kryptools/code.py +1 -1
  6. {kryptools-1.8.2 → kryptools-1.9}/kryptools/la.py +56 -31
  7. kryptools-1.9/kryptools/lat.py +631 -0
  8. kryptools-1.8.2/kryptools/lat.py → kryptools-1.9/kryptools/lat2.py +20 -147
  9. {kryptools-1.8.2 → kryptools-1.9}/kryptools/poly.py +201 -62
  10. {kryptools-1.8.2 → kryptools-1.9}/kryptools.egg-info/PKG-INFO +3 -2
  11. {kryptools-1.8.2 → kryptools-1.9}/kryptools.egg-info/SOURCES.txt +1 -0
  12. {kryptools-1.8.2 → kryptools-1.9}/pyproject.toml +1 -1
  13. kryptools-1.9/tests/test_lat.py +117 -0
  14. {kryptools-1.8.2 → kryptools-1.9}/tests/test_poly.py +4 -0
  15. kryptools-1.8.2/tests/test_lat.py +0 -49
  16. {kryptools-1.8.2 → kryptools-1.9}/LICENSE +0 -0
  17. {kryptools-1.8.2 → kryptools-1.9}/kryptools/GF2.py +0 -0
  18. {kryptools-1.8.2 → kryptools-1.9}/kryptools/aes.py +0 -0
  19. {kryptools-1.8.2 → kryptools-1.9}/kryptools/blockcipher.py +2 -2
  20. {kryptools-1.8.2 → kryptools-1.9}/kryptools/conway_polynomials.py +0 -0
  21. {kryptools-1.8.2 → kryptools-1.9}/kryptools/des.py +0 -0
  22. {kryptools-1.8.2 → kryptools-1.9}/kryptools/dlp.py +0 -0
  23. {kryptools-1.8.2 → kryptools-1.9}/kryptools/dlp_bsgs.py +0 -0
  24. {kryptools-1.8.2 → kryptools-1.9}/kryptools/dlp_ic.py +0 -0
  25. {kryptools-1.8.2 → kryptools-1.9}/kryptools/dlp_qs.py +0 -0
  26. {kryptools-1.8.2 → kryptools-1.9}/kryptools/dlp_rho.py +0 -0
  27. {kryptools-1.8.2 → kryptools-1.9}/kryptools/ec.py +0 -0
  28. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor.py +0 -0
  29. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor_dix.py +0 -0
  30. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor_ecm.py +0 -0
  31. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor_fmt.py +0 -0
  32. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor_pm1.py +0 -0
  33. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor_qs.py +0 -0
  34. {kryptools-1.8.2 → kryptools-1.9}/kryptools/factor_rho.py +0 -0
  35. {kryptools-1.8.2 → kryptools-1.9}/kryptools/intfuncs.py +0 -0
  36. {kryptools-1.8.2 → kryptools-1.9}/kryptools/keccak.py +0 -0
  37. {kryptools-1.8.2 → kryptools-1.9}/kryptools/nt.py +0 -0
  38. {kryptools-1.8.2 → kryptools-1.9}/kryptools/primes.py +0 -0
  39. {kryptools-1.8.2 → kryptools-1.9}/kryptools/sha1.py +0 -0
  40. {kryptools-1.8.2 → kryptools-1.9}/kryptools.egg-info/dependency_links.txt +0 -0
  41. {kryptools-1.8.2 → kryptools-1.9}/kryptools.egg-info/top_level.txt +0 -0
  42. {kryptools-1.8.2 → kryptools-1.9}/setup.cfg +0 -0
  43. {kryptools-1.8.2 → kryptools-1.9}/tests/test_GF2.py +0 -0
  44. {kryptools-1.8.2 → kryptools-1.9}/tests/test_Zmod.py +0 -0
  45. {kryptools-1.8.2 → kryptools-1.9}/tests/test_aes.py +0 -0
  46. {kryptools-1.8.2 → kryptools-1.9}/tests/test_code.py +0 -0
  47. {kryptools-1.8.2 → kryptools-1.9}/tests/test_des.py +0 -0
  48. {kryptools-1.8.2 → kryptools-1.9}/tests/test_dlog.py +0 -0
  49. {kryptools-1.8.2 → kryptools-1.9}/tests/test_ec.py +0 -0
  50. {kryptools-1.8.2 → kryptools-1.9}/tests/test_factor.py +0 -0
  51. {kryptools-1.8.2 → kryptools-1.9}/tests/test_factor_fmt.py +0 -0
  52. {kryptools-1.8.2 → kryptools-1.9}/tests/test_factor_pm1.py +0 -0
  53. {kryptools-1.8.2 → kryptools-1.9}/tests/test_factor_qs.py +0 -0
  54. {kryptools-1.8.2 → kryptools-1.9}/tests/test_intfuncs.py +0 -0
  55. {kryptools-1.8.2 → kryptools-1.9}/tests/test_keccak.py +0 -0
  56. {kryptools-1.8.2 → kryptools-1.9}/tests/test_la.py +0 -0
  57. {kryptools-1.8.2 → kryptools-1.9}/tests/test_nt.py +0 -0
  58. {kryptools-1.8.2 → kryptools-1.9}/tests/test_primes.py +0 -0
  59. {kryptools-1.8.2 → kryptools-1.9}/tests/test_sha1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.8.2
3
+ Version: 1.9
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
@@ -44,11 +44,12 @@ The tools contained are:
44
44
  * integer factorization (Fermat, Pollard p-1, Pollard rho, Lenstra's ECM, Dixon, basic quadratic sieve)
45
45
  * elliptic curves (Weierstrass form), group operations, order, discrete logarithms
46
46
  * linear algebra: Hermite normal form, Smith normal form, Gram-Schmidt
47
- * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz), SIS, ISIS, LWE
47
+ * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Hermite, Lenstra-Lenstra-Lovaz, BKZ), SIS, ISIS, LWE
48
48
  * linear codes: Hamming distance, left standard form, parity check matrix
49
49
 
50
50
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, kernel, solving linear systems, etc.)
51
51
  * BinaryMatrix: a class for Matrices with binary entries (for much faster row operations)
52
+ * Lattice: a class for lattices (SVP, CPV, LLL, BKZ, HKZ)
52
53
  * Poly: a class for polynomials (division, modulo, factoring and irreducibility test over finite fields, Lagrange interpolation)
53
54
  * Zmod: a class for the ring of integers modulo an integer
54
55
  * GF2: a class for Galois fields GF(2^n)
@@ -26,11 +26,12 @@ The tools contained are:
26
26
  * integer factorization (Fermat, Pollard p-1, Pollard rho, Lenstra's ECM, Dixon, basic quadratic sieve)
27
27
  * elliptic curves (Weierstrass form), group operations, order, discrete logarithms
28
28
  * linear algebra: Hermite normal form, Smith normal form, Gram-Schmidt
29
- * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz), SIS, ISIS, LWE
29
+ * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Hermite, Lenstra-Lenstra-Lovaz, BKZ), SIS, ISIS, LWE
30
30
  * linear codes: Hamming distance, left standard form, parity check matrix
31
31
 
32
32
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, kernel, solving linear systems, etc.)
33
33
  * BinaryMatrix: a class for Matrices with binary entries (for much faster row operations)
34
+ * Lattice: a class for lattices (SVP, CPV, LLL, BKZ, HKZ)
34
35
  * Poly: a class for polynomials (division, modulo, factoring and irreducibility test over finite fields, Lagrange interpolation)
35
36
  * Zmod: a class for the ring of integers modulo an integer
36
37
  * GF2: a class for Galois fields GF(2^n)
@@ -63,8 +63,8 @@ class Zmod:
63
63
  def random(self, num: int = 0) -> "ZmodPoint":
64
64
  "Return a single random point or a list of random points."
65
65
  if num:
66
- return([self(randint(0,self.n-1)) for _ in range(num)])
67
- return self(randint(0,self.n-1))
66
+ return([self(randint(0, self.n-1)) for _ in range(num)])
67
+ return self(randint(0, self.n-1))
68
68
 
69
69
  def order(self) -> int:
70
70
  "Compute the order of the group Z_n^*."
@@ -91,6 +91,12 @@ class Zmod:
91
91
  self.group_order *= p**k
92
92
  return self.group_order
93
93
 
94
+ def factor_n(self) -> bool:
95
+ "Returns the factorization of n and stores it."
96
+ if not self.n_factors:
97
+ self.n_factors = factorint(self.n)
98
+ return self.n_factors
99
+
94
100
  def is_cyclic(self) -> bool:
95
101
  "Test if the group Z_n^* is cyclic."
96
102
  n = self.n
@@ -260,6 +266,7 @@ class ZmodPoint:
260
266
  return self.x - self.ring.n
261
267
 
262
268
  mods = sharp
269
+ lift_centered = sharp
263
270
 
264
271
  def order(self) -> int:
265
272
  "Compute the order of the point in the group Z_n^*."
@@ -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.8.2"
8
+ __version__ = "1.9"
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
@@ -15,8 +15,8 @@ from .factor import factorint, divisors
15
15
  from .dlp import dlog
16
16
  from .ec import EC_Weierstrass
17
17
  from .la import Matrix, zeros, eye, circulant, BinaryMatrix
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, q_ary_lattice
19
- from .lat import svp_lll, cvp_lll, sis_lll, isis_lll, svp_search, cvp_search, sis_search, isis_search
18
+ from .lat import Lattice, random_unimodular_matrix
19
+ from .lat2 import q_ary_lattice, gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round_cvp, babai_round_bnd, babai_plane_cvp, babai_plane_bnd, lll, svp, cvp, sis, isis, sis_search, isis_search
20
20
  from .poly import Poly, PolyBinMult, lagrange_interpolation
21
21
  from .Zmod import Zmod
22
22
  from .GF2 import GF2, GF2_aes, GF2_miniaes, GF2_ghash
@@ -414,7 +414,7 @@ class CyclicCode():
414
414
  >>> cc = CyclicCode(n, g)
415
415
 
416
416
  To encode a list of letters (the length must be equal to the dimension k of the code):
417
- >>> cc.encode([0,1,0])
417
+ >>> cc.encode([0, 1, 0])
418
418
  [0, 1, 1, 0]
419
419
 
420
420
  Decoding is only implemented for code words:
@@ -25,22 +25,26 @@ class Matrix:
25
25
  print_post = " ]"
26
26
  print_sep = ", "
27
27
 
28
- def __init__(self, matrix: list|tuple, ring = None):
28
+ def __init__(self, matrix: list, ring = None):
29
29
  if isinstance(matrix, BinaryMatrix):
30
30
  matrix = matrix.bitmatrix()
31
31
  ring = Zmod(2)
32
- elif not (isinstance(matrix, list|tuple) and matrix):
33
- raise ValueError("The given `matrix` must be a nonempty list.")
34
- elif not isinstance(matrix[0], list|tuple):
32
+ elif not isinstance(matrix, list):
33
+ try:
34
+ matrix = list(matrix)
35
+ except Exception as exc:
36
+ raise ValueError("The given `matrix` cannot be converted to a list.") from exc
37
+ if not isinstance(matrix[0], list):
35
38
  matrix = [[x] for x in matrix]
36
39
  self.matrix = matrix # the matrix as a list (rows) of lists (column entries)
37
40
  self.cols = len(matrix[0]) # number of columns
38
41
  self.rows = len(matrix) # number of rows
42
+ if not self.cols or not self.cols:
43
+ raise ValueError("Both the number of rows and the number of columns must be nonzero.")
44
+ if any( len(row) != self.cols for row in self.matrix ):
45
+ raise ValueError("All matrix rows must have equal length!")
39
46
  self.nonpivotcols = None # set during rref
40
47
  self.pivotcols = None # set during rref
41
- for i in range(1, self.rows):
42
- if len(matrix[i]) != self.cols:
43
- raise ValueError("All matrix rows must have equal length!")
44
48
  if ring:
45
49
  self.map(ring)
46
50
 
@@ -281,10 +285,10 @@ class Matrix:
281
285
  return False
282
286
  if self.rows != other.rows or self.cols != other.cols:
283
287
  return False
284
- return all(self[i] == other[i] for i in range(self.rows * self.cols))
288
+ return all(self.matrix[i][j] == other.matrix[i][j] for i in range(self.rows) for j in range(self.cols))
285
289
 
286
290
  def __bool__(self):
287
- return any(bool(self[i]) for i in range(self.rows * self.cols))
291
+ return any( bool(x) for row in self.matrix for x in row)
288
292
 
289
293
  def map(self, func) -> None:
290
294
  "Apply a function to all elements in place."
@@ -293,9 +297,7 @@ class Matrix:
293
297
 
294
298
  def applyfunc(self, func) -> "Matrix":
295
299
  "Apply a function to all elements."
296
- tmp = self[:, :]
297
- tmp.map(func)
298
- return tmp
300
+ return self.__class__([ list(map(func, row)) for row in self.matrix ])
299
301
 
300
302
  def _guess_zero(self):
301
303
  "Guess zero and one in the ring of the coefficients."
@@ -303,6 +305,22 @@ class Matrix:
303
305
  one = zero**0
304
306
  return zero, one
305
307
 
308
+ def is_integer(self, convert: bool = False):
309
+ "Test if all coefficients are integers and convert integer valued fractions if requested."
310
+ if all( isinstance(x, int) or (isinstance(x, Fraction) and x.denominator == 1) for row in self.matrix for x in row):
311
+ if convert:
312
+ self.map(int)
313
+ return True
314
+ return False
315
+
316
+ def is_rational(self, convert: bool = False):
317
+ "Test if all coefficients are rational and convert to fractions if requested."
318
+ if all( isinstance(x, (Fraction, int)) for row in self.matrix for x in row):
319
+ if convert:
320
+ self.map(Fraction)
321
+ return True
322
+ return False
323
+
306
324
  def norm2(self) -> float:
307
325
  "Squared Frobenius/Euclidean norm."
308
326
  return sum(sum(abs(x)**2 for x in row) for row in self.matrix)
@@ -318,7 +336,7 @@ class Matrix:
318
336
  tmp = sum(sum(abs(x)**p for x in row) for row in self.matrix)
319
337
  return tmp**(1/p)
320
338
 
321
- def dot(self, other) -> int:
339
+ def dot(self, other) -> Number:
322
340
  "Dot product of two vectors."
323
341
  if self.rows == 1 and other.rows == 1 and self.cols == other.cols:
324
342
  return sum(x * y for x, y in zip(self.matrix[0], other.matrix[0]))
@@ -382,9 +400,12 @@ class Matrix:
382
400
  if hasattr(self.matrix[0][0], "ring") and not self.matrix[0][0].ring.is_field():
383
401
  # the matrix is over a ring (not a field)
384
402
  return self._rref_ring(start = start, drop_zero_rows = drop_zero_rows)
385
- one = self._guess_zero()[1]
386
403
  n, m = self.cols, self.rows
387
- R = [ self.matrix[i][:] for i in range(m) ]
404
+ if self.is_rational():
405
+ R = [ list(map(Fraction, row)) for row in self.matrix ]
406
+ else:
407
+ R = [ row[:] for row in self.matrix ]
408
+ one = self._guess_zero()[1]
388
409
  pivotcols = []
389
410
  nonpivotcols = []
390
411
  i = 0
@@ -428,15 +449,15 @@ class Matrix:
428
449
  def _rref_ring(self, start: int = 0, drop_zero_rows: bool = False) -> "Matrix":
429
450
  "Compute the reduced echelon form if the base ring is Z_n and no field."
430
451
  ring = self.matrix[0][0].ring
431
- M = self.applyfunc(int)
452
+ R = self.applyfunc(int)
432
453
  done = start
433
- while done < M.cols:
434
- M = M.hrnf(start = start, drop_zero_rows = drop_zero_rows)
435
- done = M.cols
436
- for i, j in enumerate(M.pivotcols):
454
+ while done < R.cols:
455
+ R = R.hrnf(start = start, drop_zero_rows = drop_zero_rows)
456
+ done = R.cols
457
+ for i, j in enumerate(R.pivotcols):
437
458
  if j < start:
438
459
  continue
439
- pivot = M.matrix[i][j]
460
+ pivot = R.matrix[i][j]
440
461
  if pivot >= ring.n:
441
462
  done = min(j, done)
442
463
  pivot %= ring.n
@@ -448,17 +469,17 @@ class Matrix:
448
469
  if pivot > 1: # we can make the pivot smaller
449
470
  done = min(j, done)
450
471
  tmp = pow(pivot, -1, ring.n)
451
- for k in range(j, M.cols):
452
- M.matrix[i][k] *= tmp
453
- M.map(lambda x: x % ring.n)
454
- M.map(ring)
455
- return M
472
+ for k in range(j, R.cols):
473
+ R.matrix[i][k] *= tmp
474
+ R.map(lambda x: x % ring.n)
475
+ R.map(ring)
476
+ return R
456
477
 
457
478
 
458
479
  def hrnf(self, start = 0, drop_zero_rows: bool = True) -> "Matrix":
459
480
  "Compute the Hermite row normal form."
460
481
  n, m = self.cols, self.rows
461
- if not isinstance(self.matrix[0][0], int):
482
+ if not self.is_integer(convert = True):
462
483
  raise ValueError("Hermite normal form requires integer entries!")
463
484
  H = self[:,:]
464
485
  H.pivotcols = []
@@ -506,7 +527,7 @@ class Matrix:
506
527
  def hnf(self, drop_zero_columns: bool = True) -> "Matrix":
507
528
  "Compute the Hermite normal form."
508
529
  n, m = self.cols, self.rows
509
- if not isinstance(self.matrix[0][0], int):
530
+ if not self.is_integer(convert = True):
510
531
  raise ValueError("Hermite normal form requires integer entries!")
511
532
  H = self[:,:]
512
533
  j = n - 1
@@ -542,7 +563,7 @@ class Matrix:
542
563
 
543
564
  def snf(self, drop_zero_rows: bool = False, drop_zero_columns: bool = False, include_S: bool = True, include_T: bool = True) -> "Matrix":
544
565
  "Computes the Smith normal form D of a matrix A with integer coefficients together with invertible matrices S and T such that D = S * A * T."
545
- if not isinstance(self.matrix[0][0], int):
566
+ if not self.is_integer(convert = True):
546
567
  raise ValueError("Smith normal form requires integer entries!")
547
568
  m, n = self.rows, self.cols
548
569
  A = self[:,:]
@@ -685,11 +706,15 @@ class Matrix:
685
706
  if hasattr(self.matrix[0][0], "ring") and not self.matrix[0][0].ring.is_field():
686
707
  # the matrix is over a ring (not a field)
687
708
  ring = self.matrix[0][0].ring
688
- R = [ list(map(lambda x: Fraction(int(x)), self.matrix[i])) for i in range(m) ]
709
+ R = [ list(map(lambda x: Fraction(int(x)), row)) for row in self.matrix ]
689
710
  zero, one = ring(0), 1
711
+ elif self.is_integer():
712
+ ring = None
713
+ R = [ list(map(Fraction, row)) for row in self.matrix ]
714
+ zero, one = 0, 1
690
715
  else:
691
716
  ring = None
692
- R = [ self.matrix[i][:] for i in range(m) ]
717
+ R = [ row[:] for row in self.matrix ]
693
718
  zero, one = self._guess_zero()
694
719
  D = one
695
720
  i = 0