kryptools 1.8__tar.gz → 1.8.1__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 (57) hide show
  1. {kryptools-1.8 → kryptools-1.8.1}/PKG-INFO +1 -1
  2. {kryptools-1.8 → kryptools-1.8.1}/kryptools/Zmod.py +9 -3
  3. {kryptools-1.8 → kryptools-1.8.1}/kryptools/__init__.py +1 -1
  4. {kryptools-1.8 → kryptools-1.8.1}/kryptools/la.py +61 -29
  5. {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/PKG-INFO +1 -1
  6. {kryptools-1.8 → kryptools-1.8.1}/pyproject.toml +1 -1
  7. {kryptools-1.8 → kryptools-1.8.1}/tests/test_la.py +30 -1
  8. {kryptools-1.8 → kryptools-1.8.1}/LICENSE +0 -0
  9. {kryptools-1.8 → kryptools-1.8.1}/README.md +0 -0
  10. {kryptools-1.8 → kryptools-1.8.1}/kryptools/GF2.py +0 -0
  11. {kryptools-1.8 → kryptools-1.8.1}/kryptools/aes.py +0 -0
  12. {kryptools-1.8 → kryptools-1.8.1}/kryptools/blockcipher.py +0 -0
  13. {kryptools-1.8 → kryptools-1.8.1}/kryptools/code.py +0 -0
  14. {kryptools-1.8 → kryptools-1.8.1}/kryptools/conway_polynomials.py +0 -0
  15. {kryptools-1.8 → kryptools-1.8.1}/kryptools/des.py +0 -0
  16. {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp.py +0 -0
  17. {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_bsgs.py +0 -0
  18. {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_ic.py +0 -0
  19. {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_qs.py +0 -0
  20. {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_rho.py +0 -0
  21. {kryptools-1.8 → kryptools-1.8.1}/kryptools/ec.py +0 -0
  22. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor.py +0 -0
  23. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_dix.py +0 -0
  24. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_ecm.py +0 -0
  25. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_fmt.py +0 -0
  26. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_pm1.py +0 -0
  27. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_qs.py +0 -0
  28. {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_rho.py +0 -0
  29. {kryptools-1.8 → kryptools-1.8.1}/kryptools/intfuncs.py +0 -0
  30. {kryptools-1.8 → kryptools-1.8.1}/kryptools/keccak.py +0 -0
  31. {kryptools-1.8 → kryptools-1.8.1}/kryptools/lat.py +0 -0
  32. {kryptools-1.8 → kryptools-1.8.1}/kryptools/nt.py +0 -0
  33. {kryptools-1.8 → kryptools-1.8.1}/kryptools/poly.py +0 -0
  34. {kryptools-1.8 → kryptools-1.8.1}/kryptools/primes.py +0 -0
  35. {kryptools-1.8 → kryptools-1.8.1}/kryptools/sha1.py +0 -0
  36. {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/SOURCES.txt +0 -0
  37. {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/dependency_links.txt +0 -0
  38. {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/top_level.txt +0 -0
  39. {kryptools-1.8 → kryptools-1.8.1}/setup.cfg +0 -0
  40. {kryptools-1.8 → kryptools-1.8.1}/tests/test_GF2.py +0 -0
  41. {kryptools-1.8 → kryptools-1.8.1}/tests/test_Zmod.py +0 -0
  42. {kryptools-1.8 → kryptools-1.8.1}/tests/test_aes.py +0 -0
  43. {kryptools-1.8 → kryptools-1.8.1}/tests/test_code.py +0 -0
  44. {kryptools-1.8 → kryptools-1.8.1}/tests/test_des.py +0 -0
  45. {kryptools-1.8 → kryptools-1.8.1}/tests/test_dlog.py +0 -0
  46. {kryptools-1.8 → kryptools-1.8.1}/tests/test_ec.py +0 -0
  47. {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor.py +0 -0
  48. {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor_fmt.py +0 -0
  49. {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor_pm1.py +0 -0
  50. {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor_qs.py +0 -0
  51. {kryptools-1.8 → kryptools-1.8.1}/tests/test_intfuncs.py +0 -0
  52. {kryptools-1.8 → kryptools-1.8.1}/tests/test_keccak.py +0 -0
  53. {kryptools-1.8 → kryptools-1.8.1}/tests/test_lat.py +0 -0
  54. {kryptools-1.8 → kryptools-1.8.1}/tests/test_nt.py +0 -0
  55. {kryptools-1.8 → kryptools-1.8.1}/tests/test_poly.py +0 -0
  56. {kryptools-1.8 → kryptools-1.8.1}/tests/test_primes.py +0 -0
  57. {kryptools-1.8 → kryptools-1.8.1}/tests/test_sha1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.8
3
+ Version: 1.8.1
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
@@ -328,18 +328,24 @@ class ZmodPoint:
328
328
  roots.append((p**(j//2) * x) % pk)
329
329
  return self.ring(crt(roots, powers))
330
330
 
331
- def solve(self, b):
331
+ def solve(self, b, all_solutions: bool = False) -> "ZmodPoint":
332
332
  "Find a solution `x` of the linear equation `self * x == b` in Z_n."
333
333
  b = int(b)
334
334
  if not self:
335
335
  if b:
336
336
  return None
337
+ if all_solutions:
338
+ return list(self.ring)
337
339
  return self.ring(0)
338
340
  g = gcd(self.x, self.ring.n)
339
341
  if b % g:
340
342
  return None
341
- return self.ring(pow(self.x // g, -1, self.ring.n) * (b // g))
343
+ m = self.ring.n // g
344
+ sol = self.ring(pow(self.x // g, -1, m) * (b // g) % m)
345
+ if all_solutions:
346
+ return [ sol + j * m for j in range(g) ]
347
+ return sol
342
348
 
343
- def is_generator(self):
349
+ def is_generator(self) -> bool:
344
350
  "Test if the point is a generator of the group Z_n^*."
345
351
  return self.ring.order() == self.order()
@@ -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"
8
+ __version__ = "1.8.1"
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
@@ -381,26 +381,32 @@ class Matrix:
381
381
  "Compute the reduced echelon form if the base ring is Z_n and no field."
382
382
  ring = self.matrix[0][0].ring
383
383
  M = self.applyfunc(int)
384
- done = False
385
- while not done:
386
- M = M.hermite_rnf(start = start, drop_zero_rows = drop_zero_rows)
387
- done = True
384
+ done = start
385
+ while done < M.cols:
386
+ M = M.hrnf(start = start, drop_zero_rows = drop_zero_rows)
387
+ done = M.cols
388
388
  for i, j in enumerate(M.pivotcols):
389
+ if j < start:
390
+ continue
389
391
  pivot = M.matrix[i][j]
390
392
  if pivot >= ring.n:
391
- done = False
393
+ done = min(j, done)
392
394
  pivot %= ring.n
393
- if pivot!=1 and gcd(pivot, ring.n) == 1: # we can make the pivot one
394
- done = False
395
- M.matrix[i][j] = 1
395
+ if pivot: # find the invertible part
396
+ g = gcd(pivot, ring.n)
397
+ while g > 1:
398
+ pivot //= g
399
+ g = gcd(pivot, ring.n)
400
+ if pivot > 1: # we can make the pivot smaller
401
+ done = min(j, done)
396
402
  tmp = pow(pivot, -1, ring.n)
397
- for k in range(j + 1, M.cols):
403
+ for k in range(j, M.cols):
398
404
  M.matrix[i][k] *= tmp
399
405
  M.map(lambda x: x % ring.n)
400
406
  M.map(ring)
401
407
  return M
402
408
 
403
- def hermite_rnf(self, start = 0, drop_zero_rows: bool = False) -> "Matrix":
409
+ def hrnf(self, start = 0, drop_zero_rows: bool = False) -> "Matrix":
404
410
  "Compute the Hermite row normal form."
405
411
  n, m = self.cols, self.rows
406
412
  if not isinstance(self.matrix[0][0], int):
@@ -553,32 +559,58 @@ class Matrix:
553
559
  def solve(self, b: list|tuple) -> "Matrix":
554
560
  "Solve the linear system with given inhomogenous vector."
555
561
  if isinstance(b, list|tuple):
556
- b = Matrix(b)
562
+ b = self.__class__(b)
557
563
  if self.rows != b.rows or b.cols != 1:
558
564
  raise ValueError("Matrix dimensions do not match.")
559
- A = self.zeros(self.rows, self.cols + 1) # extended coefficient matrix
560
- A[:, 0:self.cols] = self
561
- A[:, self.cols] = b
565
+ A = self[:,:]
566
+ A.append_column(b)
562
567
  A = A.rref(drop_zero_rows = True)
563
- solution = self.zeros(self.cols, 1)
564
- if not any(A.matrix[-1][:-1]):
565
- if A.matrix[-1][-1]:
566
- return None # Not solvable
568
+ if not any(A.matrix[-1][:-1]) and A.matrix[-1][-1]:
569
+ return None # Not solvable
567
570
  if hasattr(self.matrix[0][0], "ring") and not self.matrix[0][0].ring.is_field():
568
571
  # the matrix is over a ring (not a field)
569
572
  ring = self.matrix[0][0].ring
570
- else:
571
- ring = None
572
- for i in range(A.rows-1, -1, -1):
573
- j = A.pivotcols[i]
574
- b = A.matrix[i][-1]
575
- if ring:
576
- for k in range(j + 1, self.cols):
577
- b -= A.matrix[i][k] * solution[k]
578
- b = A.matrix[i][j].solve(b)
579
- if b is None:
573
+ # compute column normal form (TODO implement full Smith normal form)
574
+ b = A[:,-1]
575
+
576
+ A = A[:,:-1].transpose()
577
+ A.map(int)
578
+ A.append_column(A.eye(A.rows))
579
+ A = A.hrnf().transpose()
580
+ R = A[b.rows:,:b.rows]
581
+ A = A[:b.rows,:b.rows]
582
+ A.map(ring)
583
+
584
+ solution_nr = [ 0 ] * A.rows
585
+ solutions_left = True
586
+ next_solution = False # start with the first solution
587
+ while solutions_left:
588
+ solution = solution = [ None ] * A.rows
589
+ solutions_left = False
590
+ for i in range(A.rows):
591
+ bi = b[i]
592
+ for k in range(i):
593
+ bi -= A.matrix[i][k] * solution[k]
594
+ c = A.matrix[i][i].solve(bi, all_solutions = True)
595
+ if c is None:
596
+ solution = None
597
+ break
598
+ if next_solution and solution_nr[i] < len(c) - 1:
599
+ solution_nr[i] += 1
600
+ next_solution = False
601
+ if solution_nr[i] < len(c) -1:
602
+ solutions_left = True
603
+ solution[i] = c[solution_nr[i]]
604
+ if next_solution: # no more solutions left
580
605
  return None
581
- solution[j] = b
606
+ if solution is not None:
607
+ return R * Matrix(solution)
608
+ next_solution = True
609
+ return None
610
+ # the matrix is over a field
611
+ solution = self.zeros(self.cols, 1)
612
+ for i, j in enumerate(A.pivotcols):
613
+ solution[j] = A.matrix[i][-1]
582
614
  return solution
583
615
 
584
616
  def is_unimodular(self) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.8
3
+ Version: 1.8.1
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "1.8"
3
+ version = "1.8.1"
4
4
  authors = [
5
5
  { name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
6
6
  ]
@@ -1,5 +1,6 @@
1
1
  # pragma pylint: disable=C0114,C0116
2
2
  import pytest # pylint: disable=W0611
3
+ from math import gcd # pylint: disable=C0411
3
4
  from random import randint, seed # pylint: disable=C0411
4
5
  from fractions import Fraction # pylint: disable=C0411
5
6
  from kryptools import Matrix, Zmod, GF2, eye, circulant, BinaryMatrix
@@ -36,11 +37,39 @@ def test_Matrix():
36
37
  Mi = M.inv()
37
38
  assert M * Mi == M.eye()
38
39
 
40
+ def test_Matrix2():
41
+ num_tests = 100
42
+ seed(0)
43
+ for ring in Zmod(7), Zmod(8), Zmod(10):
44
+ for m,n in ((3, 5), (5, 3), (3, 3)):
45
+ for _ in range(num_tests):
46
+ A = Matrix([[ring.random() for _ in range(m)] for _ in range(n)])
47
+ b = Matrix([ring.random() for _ in range(n)])
48
+ AA = A.applyfunc(lambda x: Fraction(int(x)))
49
+ if n == m:
50
+ d = A.det()
51
+ assert d == ring(AA.det())
52
+ if gcd(d.x, ring.n) == 1:
53
+ assert A.inv() * A == A.eye()
54
+ else:
55
+ with pytest.raises(ValueError):
56
+ A.inv()
57
+ x = A.solve(b)
58
+ if x is not None:
59
+ assert A * x == b
60
+ else:
61
+ if n == m:
62
+ assert gcd(d.x, ring.n) != 1
63
+ xx = Matrix([ring.random() for _ in range(m)])
64
+ b = A * xx
65
+ x = A.solve(b)
66
+ assert A * x == b
67
+
39
68
  def test_kernel():
40
69
  for gf in Zmod(2), Zmod(7), GF2(1), GF2(8):
41
70
  for m,n in ((3, 5), (5, 3), (3, 3)):
42
71
  for _ in range(25):
43
- A = Matrix([[gf.random() for _ in range(m)] for _ in range(n)], ring = gf)
72
+ A = Matrix([[gf.random() for _ in range(m)] for _ in range(n)])
44
73
  K = A.kernel()
45
74
  assert not A * K
46
75
  assert K.rows == A.cols
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
File without changes
File without changes
File without changes
File without changes
File without changes