kryptools 1.2__tar.gz → 1.3.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.2 → kryptools-1.3.1}/PKG-INFO +3 -1
- {kryptools-1.2 → kryptools-1.3.1}/README.md +2 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/GF2.py +3 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/Zmod.py +5 -1
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/__init__.py +2 -1
- kryptools-1.3.1/kryptools/code.py +140 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/la.py +2 -2
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/lat.py +2 -1
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/poly.py +29 -7
- {kryptools-1.2 → kryptools-1.3.1}/kryptools.egg-info/PKG-INFO +3 -1
- {kryptools-1.2 → kryptools-1.3.1}/kryptools.egg-info/SOURCES.txt +2 -0
- {kryptools-1.2 → kryptools-1.3.1}/pyproject.toml +1 -1
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_GF2.py +5 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_Zmod.py +9 -0
- kryptools-1.3.1/tests/test_code.py +41 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_la.py +7 -0
- kryptools-1.3.1/tests/test_poly.py +36 -0
- kryptools-1.2/tests/test_poly.py +0 -20
- {kryptools-1.2 → kryptools-1.3.1}/LICENSE +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/aes.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/blockcipher.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/conway_polynomials.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/des.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/dlp.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/dlp_ic.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/dlp_qs.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/dlp_rho.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/ec.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor_dix.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor_ecm.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor_fmt.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor_pm1.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor_qs.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/factor_rho.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/intfuncs.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/keccak.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/nt.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/primes.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools/sha1.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/setup.cfg +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_aes.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_des.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_dlog.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_ec.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_factor.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_factor_fmt.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_factor_pm1.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_factor_qs.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_intfuncs.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_keccak.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_lat.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_nt.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_primes.py +0 -0
- {kryptools-1.2 → kryptools-1.3.1}/tests/test_sha1.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.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
|
|
@@ -39,11 +39,13 @@ The tools contained are:
|
|
|
39
39
|
* elliptic curves (Weierstrass form), group operations, order, discrete logarithms
|
|
40
40
|
* linear algebra: Hermite normal form, Gram-Schmidt
|
|
41
41
|
* lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
|
|
42
|
+
* linear codes: Hamming distance, left standard form, parity check matrix
|
|
42
43
|
|
|
43
44
|
* Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
|
|
44
45
|
* Poly: a class for polynomials (division, modulo)
|
|
45
46
|
* Zmod: a class for the ring of integers modulo an integer
|
|
46
47
|
* GF2: a class for Galois fields GF(2^n)
|
|
48
|
+
* Goppa: a class for encoding and decoding Goppa codes
|
|
47
49
|
|
|
48
50
|
* BlockCipher: a class implementing the usual modes of operation (ECB, CBC, GCM, etc.)
|
|
49
51
|
* AESCipher: individual AES operations
|
|
@@ -21,11 +21,13 @@ The tools contained are:
|
|
|
21
21
|
* elliptic curves (Weierstrass form), group operations, order, discrete logarithms
|
|
22
22
|
* linear algebra: Hermite normal form, Gram-Schmidt
|
|
23
23
|
* lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
|
|
24
|
+
* linear codes: Hamming distance, left standard form, parity check matrix
|
|
24
25
|
|
|
25
26
|
* Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
|
|
26
27
|
* Poly: a class for polynomials (division, modulo)
|
|
27
28
|
* Zmod: a class for the ring of integers modulo an integer
|
|
28
29
|
* GF2: a class for Galois fields GF(2^n)
|
|
30
|
+
* Goppa: a class for encoding and decoding Goppa codes
|
|
29
31
|
|
|
30
32
|
* BlockCipher: a class implementing the usual modes of operation (ECB, CBC, GCM, etc.)
|
|
31
33
|
* AESCipher: individual AES operations
|
|
@@ -17,7 +17,7 @@ class Zmod:
|
|
|
17
17
|
To define a finite Galois field modulo the prime 5 use
|
|
18
18
|
>>> Z_5=Zmod(5)
|
|
19
19
|
|
|
20
|
-
To declare 3 as an element of our Galois
|
|
20
|
+
To declare 3 as an element of our Galois field use
|
|
21
21
|
>>> Z_5(3)
|
|
22
22
|
3
|
|
23
23
|
|
|
@@ -210,6 +210,10 @@ class ZmodPoint:
|
|
|
210
210
|
def __abs__(self) -> int:
|
|
211
211
|
return abs(self.sharp())
|
|
212
212
|
|
|
213
|
+
def bits(self) -> list:
|
|
214
|
+
"Convert to a list of bits."
|
|
215
|
+
return [int(d) for d in str(format(self.x, "0" + str(int.bit_length(self.ring.n - 1)) + "b"))]
|
|
216
|
+
|
|
213
217
|
def sharp(self):
|
|
214
218
|
"Returns a symmetric (w.r.t. 0) representative."
|
|
215
219
|
tmp = (self.ring.n - 1) // 2
|
|
@@ -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
|
+
__version__ = "1.3.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
|
|
@@ -19,6 +19,7 @@ from .lat import gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round
|
|
|
19
19
|
from .poly import Poly
|
|
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
23
|
from .blockcipher import BlockCipher, bytexor
|
|
23
24
|
from .aes import AESKeySchedule, AESCipher, AESBlockCipher
|
|
24
25
|
from .des import list2int, int2list, permute, invert_permutation, rotate_left, SDESKeySchedule, SDESCipher, DESKeySchedule, DESCipher, DESBlockCipher
|
|
@@ -0,0 +1,140 @@
|
|
|
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)
|
|
@@ -191,7 +191,7 @@ class Matrix:
|
|
|
191
191
|
return False
|
|
192
192
|
if self.rows != other.rows or self.cols != other.cols:
|
|
193
193
|
return False
|
|
194
|
-
return
|
|
194
|
+
return all(self[i] == other[i] for i in range(self.rows * self.cols))
|
|
195
195
|
|
|
196
196
|
def __bool__(self):
|
|
197
197
|
return any(bool(self[i]) for i in range(self.rows * self.cols))
|
|
@@ -374,7 +374,7 @@ class Matrix:
|
|
|
374
374
|
A[:, 0:self.cols] = self
|
|
375
375
|
A[:, self.cols] = b
|
|
376
376
|
A = A.rref()
|
|
377
|
-
solution = self.zeros(self.
|
|
377
|
+
solution = self.zeros(self.cols, 1)
|
|
378
378
|
for i in range(A.rows-1, -1, -1):
|
|
379
379
|
if not any(A.matrix[i][:-1]):
|
|
380
380
|
if A.matrix[i][-1]:
|
|
@@ -109,7 +109,8 @@ def babai_plane_bnd(U: Matrix, p=2) -> float:
|
|
|
109
109
|
|
|
110
110
|
def lagrange_lr(V: Matrix) -> Matrix:
|
|
111
111
|
"Lagrange lattice reduction."
|
|
112
|
-
|
|
112
|
+
if (V.rows, V.cols) != (2, 2):
|
|
113
|
+
raise ValueError("Lagrange lattice reduction requires dimension two.")
|
|
113
114
|
v1, v2 = V[:, 0], V[:, 1]
|
|
114
115
|
if norm2(v1) > norm2(v2):
|
|
115
116
|
v1, v2 = v2, v1
|
|
@@ -4,7 +4,7 @@ Polynomials
|
|
|
4
4
|
|
|
5
5
|
from numbers import Number
|
|
6
6
|
from .primes import is_prime
|
|
7
|
-
from .factor import factorint
|
|
7
|
+
#from .factor import factorint
|
|
8
8
|
|
|
9
9
|
class Poly:
|
|
10
10
|
"""
|
|
@@ -231,6 +231,16 @@ class Poly:
|
|
|
231
231
|
tmp *= tmp
|
|
232
232
|
return res
|
|
233
233
|
|
|
234
|
+
def bits(self) -> list[int]:
|
|
235
|
+
"List of bits of all coefficients."
|
|
236
|
+
ring = self.coeff[0].__class__
|
|
237
|
+
if not (hasattr(ring, 'bits') and callable(ring.bits)):
|
|
238
|
+
raise NotImplementedError("Coefficients cannot be converted to bits.")
|
|
239
|
+
out = []
|
|
240
|
+
for c in self.coeff:
|
|
241
|
+
out += c.bits()
|
|
242
|
+
return out
|
|
243
|
+
|
|
234
244
|
def max(self) -> int:
|
|
235
245
|
"Maximum of the absolute value of all coefficients."
|
|
236
246
|
return max(abs(c) for c in self.coeff)
|
|
@@ -377,11 +387,23 @@ class Poly:
|
|
|
377
387
|
isfield = True
|
|
378
388
|
if not isfield:
|
|
379
389
|
raise ValueError("The polynomial does not seem to be over a finite field.")
|
|
380
|
-
n = self.degree()
|
|
381
|
-
x = self.__class__([0, 1], ring=ring, modulus = self.coeff) # x
|
|
382
|
-
for p in factorint(n).keys():
|
|
383
|
-
|
|
390
|
+
#n = self.degree()
|
|
391
|
+
#x = self.__class__([0, 1], ring=ring, modulus = self.coeff) # x
|
|
392
|
+
#for p in factorint(n).keys():
|
|
393
|
+
# g = self.gcd(x**(q**(n//p)) - x) # gcd with x^{q/n} - x
|
|
394
|
+
# if g.degree():
|
|
395
|
+
# return False
|
|
396
|
+
#g = x**(q**n) - x
|
|
397
|
+
#return not(g)
|
|
398
|
+
x = Poly([0, 1], ring = ring) # x
|
|
399
|
+
xq = Poly(q * [0] + [1], ring = ring, modulus = self.coeff) # x^q modulo self to keep the polynomials small
|
|
400
|
+
for k in range(1, self.degree() // 2 + 1):
|
|
401
|
+
if k == 1:
|
|
402
|
+
b = xq
|
|
403
|
+
else:
|
|
404
|
+
b **= q # we compute x^{q^k} mod a recursively
|
|
405
|
+
g = self.gcd(b - x) # gcd with x^{q^k} - x
|
|
384
406
|
if g.degree():
|
|
385
407
|
return False
|
|
386
|
-
|
|
387
|
-
|
|
408
|
+
return True
|
|
409
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.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
|
|
@@ -39,11 +39,13 @@ The tools contained are:
|
|
|
39
39
|
* elliptic curves (Weierstrass form), group operations, order, discrete logarithms
|
|
40
40
|
* linear algebra: Hermite normal form, Gram-Schmidt
|
|
41
41
|
* lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
|
|
42
|
+
* linear codes: Hamming distance, left standard form, parity check matrix
|
|
42
43
|
|
|
43
44
|
* Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
|
|
44
45
|
* Poly: a class for polynomials (division, modulo)
|
|
45
46
|
* Zmod: a class for the ring of integers modulo an integer
|
|
46
47
|
* GF2: a class for Galois fields GF(2^n)
|
|
48
|
+
* Goppa: a class for encoding and decoding Goppa codes
|
|
47
49
|
|
|
48
50
|
* BlockCipher: a class implementing the usual modes of operation (ECB, CBC, GCM, etc.)
|
|
49
51
|
* AESCipher: individual AES operations
|
|
@@ -6,6 +6,7 @@ kryptools/Zmod.py
|
|
|
6
6
|
kryptools/__init__.py
|
|
7
7
|
kryptools/aes.py
|
|
8
8
|
kryptools/blockcipher.py
|
|
9
|
+
kryptools/code.py
|
|
9
10
|
kryptools/conway_polynomials.py
|
|
10
11
|
kryptools/des.py
|
|
11
12
|
kryptools/dlp.py
|
|
@@ -36,6 +37,7 @@ kryptools.egg-info/top_level.txt
|
|
|
36
37
|
tests/test_GF2.py
|
|
37
38
|
tests/test_Zmod.py
|
|
38
39
|
tests/test_aes.py
|
|
40
|
+
tests/test_code.py
|
|
39
41
|
tests/test_des.py
|
|
40
42
|
tests/test_dlog.py
|
|
41
43
|
tests/test_ec.py
|
|
@@ -8,6 +8,11 @@ num_tests = 50
|
|
|
8
8
|
def test_GF2_ops():
|
|
9
9
|
for n, modulus in [(4, None), (8, None), (8, 0b100011011), (12, None), (128, 0b11100001 << 120)]:
|
|
10
10
|
gf = GF2(n, modulus=modulus)
|
|
11
|
+
assert gf(1)
|
|
12
|
+
assert not gf(0)
|
|
13
|
+
assert gf(1) == gf(1)
|
|
14
|
+
assert not gf(1) == gf(0)
|
|
15
|
+
assert not gf(0) == 0
|
|
11
16
|
if gf.power < 17:
|
|
12
17
|
assert len(list(gf)) == gf.order
|
|
13
18
|
assert len(list(gf.star())) == gf.order - 1
|
|
@@ -42,6 +42,11 @@ def test_Zmod_methods():
|
|
|
42
42
|
assert list(Z_5.star()) == Z_5([1, 2, 3, 4])
|
|
43
43
|
assert [Z_5(i).sharp() for i in range(Z_5.n)] == [0, 1, 2, -2, -1]
|
|
44
44
|
assert [abs(Z_5(i)) for i in range(Z_5.n)] == [0, 1, 2, 2, 1]
|
|
45
|
+
assert not Z_5(0)
|
|
46
|
+
assert Z_5(1)
|
|
47
|
+
assert Z_5(2) == Z_5(2)
|
|
48
|
+
assert Z_5(2) != Z_5(3)
|
|
49
|
+
assert Z_5(2) != 2
|
|
45
50
|
assert str(Z_5(3)) == "3"
|
|
46
51
|
Z_5.short = False
|
|
47
52
|
assert str(Z_5(6)) == "1 (mod 5)"
|
|
@@ -57,6 +62,10 @@ def test_Zmod_methods():
|
|
|
57
62
|
assert Z_6(5).is_generator() is True
|
|
58
63
|
assert list(Z_6.generators()) == Z_6([5])
|
|
59
64
|
assert list(Z_6.star()) == Z_6([1, 5])
|
|
65
|
+
assert Z_6(1)
|
|
66
|
+
assert Z_6(2) == Z_6(2)
|
|
67
|
+
assert Z_6(2) != Z_6(3)
|
|
68
|
+
assert Z_5(2) != Z_6(2)
|
|
60
69
|
assert str(Z_6(3)) == "3"
|
|
61
70
|
|
|
62
71
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from random import randint, seed
|
|
3
|
+
from kryptools import Zmod, GF2, Poly, Matrix
|
|
4
|
+
from kryptools import gen2pchk, Goppa
|
|
5
|
+
|
|
6
|
+
seed(0)
|
|
7
|
+
Z_2 = Zmod(2)
|
|
8
|
+
|
|
9
|
+
def Hamming2(h: int) -> Matrix:
|
|
10
|
+
"Parity check matrix for the binary Hamming code."
|
|
11
|
+
out = []
|
|
12
|
+
for i in range(1,2**h):
|
|
13
|
+
out.append([int(d) for d in str(format(i, "0" + str(h) + "b"))])
|
|
14
|
+
return Matrix(out, ring = Z_2).transpose()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_gen2pchk():
|
|
18
|
+
for h in range(2,6):
|
|
19
|
+
H = Hamming2(h)
|
|
20
|
+
G = gen2pchk(H)
|
|
21
|
+
assert not H * G.transpose()
|
|
22
|
+
|
|
23
|
+
num_tests = 10
|
|
24
|
+
|
|
25
|
+
def test_Goppa():
|
|
26
|
+
for gf, t in [ [GF2(4), 3], [GF2(6), 4]]:
|
|
27
|
+
found = False
|
|
28
|
+
while not found:
|
|
29
|
+
c = [randint(0,gf.order-1) for _ in range(t)] + [1]
|
|
30
|
+
g = Poly(c, ring = gf)
|
|
31
|
+
found = g.rabin_test()
|
|
32
|
+
alpha = list(gf)
|
|
33
|
+
|
|
34
|
+
goppa = Goppa(gf, g, alpha)
|
|
35
|
+
for _ in range(num_tests):
|
|
36
|
+
x = [ randint(0,1) for _ in range(goppa.G.rows) ]
|
|
37
|
+
y = goppa.encode(x)
|
|
38
|
+
for _ in range(t):
|
|
39
|
+
i = randint(0, goppa.G.cols-1)
|
|
40
|
+
y[i] = 1- y[i]
|
|
41
|
+
assert goppa.decode(y) == x
|
|
@@ -5,6 +5,13 @@ from kryptools import Matrix, Zmod
|
|
|
5
5
|
|
|
6
6
|
def test_Matrix():
|
|
7
7
|
M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 12]])
|
|
8
|
+
Z = M.zeros()
|
|
9
|
+
assert M
|
|
10
|
+
assert M == M
|
|
11
|
+
assert not Z
|
|
12
|
+
Z[0] = 1
|
|
13
|
+
assert Z
|
|
14
|
+
assert Z != Z.zeros()
|
|
8
15
|
assert len(M) == 3 * 3
|
|
9
16
|
assert M[0] == 1
|
|
10
17
|
assert M[:] == [1, 2, 3, 4, 5, 6, 7, 8, 12]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from kryptools import Poly, Zmod, GF2
|
|
3
|
+
from kryptools import divisors, moebius_mu
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_Poly():
|
|
7
|
+
p = Poly([2, 0, 2])
|
|
8
|
+
q = Poly([1, 2, 3, 4])
|
|
9
|
+
d, m = q.divmod(p)
|
|
10
|
+
assert d * p + m == q
|
|
11
|
+
|
|
12
|
+
Z_5 = Zmod(5)
|
|
13
|
+
p.map(Z_5)
|
|
14
|
+
q.map(Z_5)
|
|
15
|
+
d, m = q.divmod(p)
|
|
16
|
+
assert d * p + m == q
|
|
17
|
+
|
|
18
|
+
gf = GF2(8, modulus=0b100011011)
|
|
19
|
+
p = Poly([2, 1, 1, 3], modulus=[1, 0, 0, 0, 1], ring=gf)
|
|
20
|
+
q = p.inv()
|
|
21
|
+
assert p * q == Poly([1], modulus=[1, 0, 0, 0, 1], ring=gf)
|
|
22
|
+
|
|
23
|
+
def test_rabin():
|
|
24
|
+
for gf,t in [ [Zmod(7), 4], [GF2(4), 3]]:
|
|
25
|
+
order = len(list(gf))
|
|
26
|
+
count = 0
|
|
27
|
+
for i in range(order**t):
|
|
28
|
+
c = []
|
|
29
|
+
while i:
|
|
30
|
+
i, m = divmod(i, order)
|
|
31
|
+
c.append(m)
|
|
32
|
+
c += [0] * (t- len(c)) + [1]
|
|
33
|
+
a = Poly(c, ring=gf)
|
|
34
|
+
if a.rabin_test():
|
|
35
|
+
count += 1
|
|
36
|
+
assert count == sum([moebius_mu(d) * order ** (t // d) for d in divisors(t)]) // t
|
kryptools-1.2/tests/test_poly.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from kryptools import Poly, Zmod, GF2
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def test_Poly():
|
|
6
|
-
p = Poly([2, 0, 2])
|
|
7
|
-
q = Poly([1, 2, 3, 4])
|
|
8
|
-
d, m = q.divmod(p)
|
|
9
|
-
assert d * p + m == q
|
|
10
|
-
|
|
11
|
-
Z_5 = Zmod(5)
|
|
12
|
-
p.map(Z_5)
|
|
13
|
-
q.map(Z_5)
|
|
14
|
-
d, m = q.divmod(p)
|
|
15
|
-
assert d * p + m == q
|
|
16
|
-
|
|
17
|
-
gf = GF2(8, modulus=0b100011011)
|
|
18
|
-
p = Poly([2, 1, 1, 3], modulus=[1, 0, 0, 0, 1], ring=gf)
|
|
19
|
-
q = p.inv()
|
|
20
|
-
assert p * q == Poly([1], modulus=[1, 0, 0, 0, 1], ring=gf)
|
|
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
|