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.
- {kryptools-1.8 → kryptools-1.8.1}/PKG-INFO +1 -1
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/Zmod.py +9 -3
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/__init__.py +1 -1
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/la.py +61 -29
- {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/PKG-INFO +1 -1
- {kryptools-1.8 → kryptools-1.8.1}/pyproject.toml +1 -1
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_la.py +30 -1
- {kryptools-1.8 → kryptools-1.8.1}/LICENSE +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/README.md +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/GF2.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/aes.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/blockcipher.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/code.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/conway_polynomials.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/des.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_ic.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_qs.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/dlp_rho.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/ec.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_dix.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_ecm.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_fmt.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_pm1.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_qs.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/factor_rho.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/intfuncs.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/keccak.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/lat.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/nt.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/poly.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/primes.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools/sha1.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/SOURCES.txt +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/setup.cfg +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_GF2.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_Zmod.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_aes.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_code.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_des.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_dlog.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_ec.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor_fmt.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor_pm1.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_factor_qs.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_intfuncs.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_keccak.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_lat.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_nt.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_poly.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_primes.py +0 -0
- {kryptools-1.8 → kryptools-1.8.1}/tests/test_sha1.py +0 -0
|
@@ -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
|
-
|
|
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 =
|
|
385
|
-
while
|
|
386
|
-
M = M.
|
|
387
|
-
done =
|
|
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 =
|
|
393
|
+
done = min(j, done)
|
|
392
394
|
pivot %= ring.n
|
|
393
|
-
if pivot
|
|
394
|
-
|
|
395
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
560
|
-
A
|
|
561
|
-
A[:, self.cols] = b
|
|
565
|
+
A = self[:,:]
|
|
566
|
+
A.append_column(b)
|
|
562
567
|
A = A.rref(drop_zero_rows = True)
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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
|
-
|
|
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,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)]
|
|
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
|
|
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
|