kryptools 0.4__tar.gz → 0.6__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-0.4 → kryptools-0.6}/PKG-INFO +2 -2
- {kryptools-0.4 → kryptools-0.6}/kryptools/Zmod.py +29 -11
- {kryptools-0.4 → kryptools-0.6}/kryptools/__init__.py +1 -1
- {kryptools-0.4 → kryptools-0.6}/kryptools/dlp.py +5 -4
- {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_qs.py +3 -3
- {kryptools-0.4 → kryptools-0.6}/kryptools/ec.py +1 -1
- {kryptools-0.4 → kryptools-0.6}/kryptools/factor_ecm.py +1 -1
- {kryptools-0.4 → kryptools-0.6}/kryptools/la.py +26 -6
- {kryptools-0.4 → kryptools-0.6}/kryptools/lat.py +13 -4
- {kryptools-0.4 → kryptools-0.6}/kryptools/nt.py +3 -2
- {kryptools-0.4 → kryptools-0.6}/kryptools/poly.py +51 -26
- {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/PKG-INFO +2 -2
- {kryptools-0.4 → kryptools-0.6}/pyproject.toml +2 -2
- {kryptools-0.4 → kryptools-0.6}/LICENSE +0 -0
- {kryptools-0.4 → kryptools-0.6}/README.md +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_ic.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_rho.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/factor.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/factor_dix.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/factor_fmt.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/factor_pm1.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/factor_qs.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools/primes.py +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/SOURCES.txt +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-0.4 → kryptools-0.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6
|
|
4
4
|
Summary: Implemenation of same basic algorithms used in cryptography.
|
|
5
5
|
Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
|
|
6
6
|
Project-URL: Homepage, https://github.com/teschlg/kryptools
|
|
@@ -9,7 +9,7 @@ Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
|
|
@@ -90,48 +90,66 @@ class ZmodPoint:
|
|
|
90
90
|
return hash(self.x)
|
|
91
91
|
|
|
92
92
|
def __add__(self, other: "ZmodPoint") -> "ZmodPoint":
|
|
93
|
-
if isinstance(other, self.__class__)
|
|
93
|
+
if isinstance(other, self.__class__):
|
|
94
|
+
if self.ring != other.ring:
|
|
95
|
+
raise NotImplementedError("Cannot add elements from different rings.")
|
|
94
96
|
return self.__class__(self.x + other.x, self.ring)
|
|
95
97
|
if isinstance(other, int):
|
|
96
98
|
return self.__class__(self.x + other, self.ring)
|
|
97
99
|
return NotImplemented
|
|
98
100
|
|
|
99
101
|
def __radd__(self, scalar: int) -> "ZmodPoint":
|
|
100
|
-
|
|
102
|
+
if isinstance(scalar, int):
|
|
103
|
+
return self.__class__(scalar + self.x, self.ring)
|
|
104
|
+
return NotImplemented
|
|
101
105
|
|
|
102
106
|
def __neg__(self) -> "ZmodPoint":
|
|
103
107
|
return self.__class__(-self.x, self.ring)
|
|
104
108
|
|
|
105
109
|
def __sub__(self, other: "ZmodPoint") -> "ZmodPoint":
|
|
106
|
-
if isinstance(other, self.__class__)
|
|
110
|
+
if isinstance(other, self.__class__):
|
|
111
|
+
if self.ring != other.ring:
|
|
112
|
+
raise NotImplementedError("Cannot subtract elements from different rings.")
|
|
107
113
|
return self.__class__(self.x - other.x, self.ring)
|
|
108
114
|
if isinstance(other, int):
|
|
109
115
|
return self.__class__(self.x - other, self.ring)
|
|
110
116
|
return NotImplemented
|
|
111
117
|
|
|
112
118
|
def __rsub__(self, scalar: int) -> "ZmodPoint":
|
|
113
|
-
|
|
119
|
+
if isinstance(scalar, int):
|
|
120
|
+
return self.__class__(scalar - self.x, self.ring)
|
|
121
|
+
return NotImplemented
|
|
114
122
|
|
|
115
123
|
def __mul__(self, other: "ZmodPoint") -> "ZmodPoint":
|
|
116
|
-
if isinstance(other, self.__class__)
|
|
124
|
+
if isinstance(other, self.__class__):
|
|
125
|
+
if self.ring != other.ring:
|
|
126
|
+
raise NotImplementedError("Cannot multiply elements from different rings.")
|
|
117
127
|
return self.__class__(self.x * other.x, self.ring)
|
|
118
128
|
if isinstance(other, int):
|
|
119
129
|
return self.__class__(self.x * other, self.ring)
|
|
120
130
|
return NotImplemented
|
|
121
131
|
|
|
122
132
|
def __rmul__(self, scalar: int) -> "ZmodPoint":
|
|
123
|
-
|
|
133
|
+
if isinstance(scalar, int):
|
|
134
|
+
return self.__class__(scalar * self.x, self.ring)
|
|
135
|
+
return NotImplemented
|
|
124
136
|
|
|
125
137
|
def __truediv__(self, other: "ZmodPoint") -> "ZmodPoint":
|
|
126
|
-
if
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
if isinstance(other, self.__class__):
|
|
139
|
+
if self.ring != other.ring:
|
|
140
|
+
raise NotImplementedError("Cannot divide elements from different rings.")
|
|
141
|
+
return self.__class__(self.x * pow(other.x, -1, self.ring.n), self.ring)
|
|
142
|
+
return NotImplemented
|
|
129
143
|
|
|
130
144
|
def __rtruediv__(self, scalar: int) -> "ZmodPoint":
|
|
131
|
-
|
|
145
|
+
if isinstance(scalar, int):
|
|
146
|
+
return self.__class__(scalar * pow(self.x, -1, self.ring.n), self.ring)
|
|
147
|
+
return NotImplemented
|
|
132
148
|
|
|
133
149
|
def __pow__(self, scalar: int) -> "ZmodPoint":
|
|
134
|
-
|
|
150
|
+
if isinstance(scalar, int):
|
|
151
|
+
return self.__class__(pow(self.x, scalar, self.ring.n), self.ring)
|
|
152
|
+
return NotImplemented
|
|
135
153
|
|
|
136
154
|
def sharp(self):
|
|
137
155
|
"Returns a symmetric (w.r.t. 0) representative."
|
|
@@ -8,6 +8,6 @@ from .factor import factorint
|
|
|
8
8
|
from .dlp import dlog
|
|
9
9
|
from .ec import EC_Weierstrass
|
|
10
10
|
from .la import Matrix, zeros, eye
|
|
11
|
-
from .lat import gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round_cvp, babai_plane_cvp, lagrange_lr, lll, random_unimodular_matrix
|
|
11
|
+
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
|
|
12
12
|
from .poly import Poly
|
|
13
13
|
from .Zmod import Zmod
|
|
@@ -11,7 +11,7 @@ from .dlp_qs import dlog_qs
|
|
|
11
11
|
from .factor import factorint
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def dlog_naive(a: int, b: int, n: int, m: int = None) -> int:
|
|
14
|
+
def dlog_naive(a: int, b: int, n: int, m: int = None) -> int|None:
|
|
15
15
|
"""Compute the discrete log_a(b) in Z_n of an element a of order m by exhaustive search."""
|
|
16
16
|
a %= n
|
|
17
17
|
b %= n
|
|
@@ -43,14 +43,14 @@ def _dlog_ph(a: int, b: int, n: int, q: int, k: int) -> int:
|
|
|
43
43
|
for j in range(2, k + 1):
|
|
44
44
|
aj = pow(a, q ** (k - j), n)
|
|
45
45
|
bj = pow(b, q ** (k - j), n)
|
|
46
|
-
yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q)
|
|
46
|
+
yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q) # pylint: disable=E1130
|
|
47
47
|
if yj is None:
|
|
48
48
|
return None
|
|
49
49
|
xj = xj + q ** (j - 1) * yj % q**j
|
|
50
50
|
return xj
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def dlog(a: int, b: int, n: int, m: int = None) -> int:
|
|
53
|
+
def dlog(a: int, b: int, n: int, m: int|None = None) -> int:
|
|
54
54
|
"""Compute the discrete log_a(b) in Z_n of an element a of order m using Pohlig-Hellman reduction."""
|
|
55
55
|
a %= n
|
|
56
56
|
b %= n
|
|
@@ -60,7 +60,8 @@ def dlog(a: int, b: int, n: int, m: int = None) -> int:
|
|
|
60
60
|
mf = factorint(m)
|
|
61
61
|
else:
|
|
62
62
|
m, mf = order(a, n, True)
|
|
63
|
-
|
|
63
|
+
if pow(b, m, n) != 1:
|
|
64
|
+
raise ValueError("DLP not solvable.")
|
|
64
65
|
# We first use Pohlig-Hellman to split m into powers of prime factors
|
|
65
66
|
mm = []
|
|
66
67
|
ll = []
|
|
@@ -227,7 +227,7 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
|
|
|
227
227
|
if r == 0:
|
|
228
228
|
roots = [ -(inv2 * aa) % p ] # one root
|
|
229
229
|
else:
|
|
230
|
-
roots = [ (inv2 * (r - aa)) % p, (inv2 * (-r - aa)) % p ] # two roots
|
|
230
|
+
roots = [ (inv2 * (r - aa)) % p, (inv2 * (-r - aa)) % p ] # two roots pylint: disable=E1130
|
|
231
231
|
for r in roots:
|
|
232
232
|
x = r # start value for x
|
|
233
233
|
while x < max_j:
|
|
@@ -255,10 +255,10 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
|
|
|
255
255
|
relation = find_relation(include_b)
|
|
256
256
|
res = process_relation(relation)
|
|
257
257
|
if res:
|
|
258
|
-
return
|
|
258
|
+
return res
|
|
259
259
|
|
|
260
260
|
#
|
|
261
|
-
# find the B-smooth numbers and add them to the system
|
|
261
|
+
# find the B-smooth numbers and add them to the system
|
|
262
262
|
#
|
|
263
263
|
|
|
264
264
|
for s in range(sieve_bound):
|
|
@@ -320,7 +320,7 @@ class ECPoint:
|
|
|
320
320
|
def __neg__(self) -> "ECPoint":
|
|
321
321
|
if self.x is None or not self.y:
|
|
322
322
|
return self
|
|
323
|
-
return ECPoint(self.x, -self.y, self.curve)
|
|
323
|
+
return ECPoint(self.x, -self.y, self.curve) # pylint: disable=E1130
|
|
324
324
|
|
|
325
325
|
def order(self) -> int:
|
|
326
326
|
"""Compute the order of an element."""
|
|
@@ -84,7 +84,7 @@ def _ecm_parameters(B1: int, B2: int = None, D: int = None, primes: tuple = None
|
|
|
84
84
|
return D, stage_one, stage_two_deltas
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple = None):
|
|
87
|
+
def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple|None = None):
|
|
88
88
|
"Factors a number n using Lentsta's ECM method."
|
|
89
89
|
|
|
90
90
|
if ecm_parameters:
|
|
@@ -3,6 +3,8 @@ Linear algebra
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from math import inf, sqrt, prod
|
|
6
|
+
from numbers import Number
|
|
7
|
+
from fractions import Fraction
|
|
6
8
|
|
|
7
9
|
class Matrix:
|
|
8
10
|
"""
|
|
@@ -146,15 +148,21 @@ class Matrix:
|
|
|
146
148
|
for j in range(other.cols):
|
|
147
149
|
for k in range(other.rows):
|
|
148
150
|
result.matrix[i][j] += self.matrix[i][k] * other.matrix[k][j]
|
|
151
|
+
if self.rows == 1 and other.cols == 1:
|
|
152
|
+
return result.matrix[0][0]
|
|
149
153
|
return result
|
|
150
154
|
|
|
151
155
|
def __add__(self, other) -> "Matrix":
|
|
152
|
-
if isinstance(other, Matrix)
|
|
156
|
+
if isinstance(other, Matrix):
|
|
157
|
+
if other.cols != self.cols or other.rows != self.rows:
|
|
158
|
+
raise NotImplementedError("Matrix dimensions do not match!")
|
|
153
159
|
return Matrix([ [ x1 + y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
|
|
154
160
|
return NotImplemented
|
|
155
161
|
|
|
156
162
|
def __sub__(self, other) -> "Matrix":
|
|
157
|
-
if isinstance(other, Matrix)
|
|
163
|
+
if isinstance(other, Matrix):
|
|
164
|
+
if other.cols != self.cols or other.rows != self.rows:
|
|
165
|
+
raise NotImplementedError("Matrix dimensions do not match!")
|
|
158
166
|
return Matrix([ [ x1 - y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
|
|
159
167
|
return NotImplemented
|
|
160
168
|
|
|
@@ -164,12 +172,14 @@ class Matrix:
|
|
|
164
172
|
def __mul__(self, other) -> "Matrix":
|
|
165
173
|
if isinstance(other, Matrix):
|
|
166
174
|
return self.multiply(other)
|
|
167
|
-
|
|
175
|
+
if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
|
|
176
|
+
return Matrix([ [item * other for item in row] for row in self.matrix ])
|
|
177
|
+
return NotImplemented
|
|
168
178
|
|
|
169
179
|
def __rmul__(self, other) -> "Matrix":
|
|
170
|
-
if isinstance(other,
|
|
171
|
-
return self.
|
|
172
|
-
return
|
|
180
|
+
if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
|
|
181
|
+
return Matrix([ [item * other for item in row] for row in self.matrix ])
|
|
182
|
+
return NotImplemented
|
|
173
183
|
|
|
174
184
|
def rref(self) -> "Matrix":
|
|
175
185
|
"Compute the reduced echelon form of a matrix M."
|
|
@@ -238,6 +248,16 @@ class Matrix:
|
|
|
238
248
|
raise ValueError("Matrix is not invertible!")
|
|
239
249
|
return MM[:,n:]
|
|
240
250
|
|
|
251
|
+
def is_unimodular(self) -> bool:
|
|
252
|
+
"Test if the matrix is unimodular."
|
|
253
|
+
if self.rows != self.rows:
|
|
254
|
+
return False
|
|
255
|
+
def is_integer(i):
|
|
256
|
+
if isinstance(i, int) or (isinstance(i, Fraction) and i.denominator == 1):
|
|
257
|
+
return True
|
|
258
|
+
return False
|
|
259
|
+
return all([is_integer(i) for i in self]) and self.det()**2 == 1
|
|
260
|
+
|
|
241
261
|
def zeros(self, m: int = None, n: int = None):
|
|
242
262
|
"Returns a zero matrix of the same dimension"
|
|
243
263
|
if not m and not n:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Lattice tools
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from math import prod
|
|
5
|
+
from math import prod, floor
|
|
6
6
|
from fractions import Fraction
|
|
7
7
|
from random import choice, sample
|
|
8
8
|
from .la import Matrix, zeros
|
|
@@ -73,19 +73,28 @@ def hadamard_ratio(M: Matrix) -> float:
|
|
|
73
73
|
return (gram_det(M) / prod([M[:, i].norm() for i in range(m)])) ** (1 / m)
|
|
74
74
|
|
|
75
75
|
def babai_round_cvp(x: Matrix, U: Matrix) -> Matrix:
|
|
76
|
-
"Babai's rounding algorithm for solving the CVP."
|
|
76
|
+
"Babai's rounding algorithm for approximately solving the CVP."
|
|
77
77
|
s = U.inv() * x
|
|
78
78
|
k = s.applyfunc(round)
|
|
79
79
|
return U * k
|
|
80
80
|
|
|
81
|
+
def babai_round_bnd(U: Matrix) -> float:
|
|
82
|
+
"Bound for Babai's rounding algorithm for solving the CVP."
|
|
83
|
+
return floor(1 / (2 * max([ U.inv()[i,:].norm(1) for i in range(U.rows)])))
|
|
84
|
+
|
|
81
85
|
def babai_plane_cvp(x: Matrix, U: Matrix) -> Matrix:
|
|
82
|
-
"Babai's closest plane algorithm for solving the CVP."
|
|
86
|
+
"Babai's closest plane algorithm for approximately solving the CVP."
|
|
83
87
|
Us = gram_schmidt(U)[0]
|
|
84
88
|
y = x
|
|
85
89
|
for k in range(U.cols - 1, -1, -1):
|
|
86
90
|
y = y - round(y.dot(Us[:, k]) / norm2(Us[:, k])) * U[:, k]
|
|
87
91
|
return (x - y).applyfunc(round)
|
|
88
92
|
|
|
93
|
+
def babai_plane_bnd(U: Matrix, p = 2) -> float:
|
|
94
|
+
"Bound for Babai's closest plane algorithm for solving the CVP."
|
|
95
|
+
Us = gram_schmidt(U)[0]
|
|
96
|
+
return float(0.5 * min([Us[:, i].norm(p) for i in range(Us.rows)]))
|
|
97
|
+
|
|
89
98
|
def lagrange_lr(V: Matrix) -> Matrix:
|
|
90
99
|
"Lagrange lattice reduction."
|
|
91
100
|
assert (V.rows, V.cols) == (2, 2)
|
|
@@ -99,7 +108,7 @@ def lagrange_lr(V: Matrix) -> Matrix:
|
|
|
99
108
|
return Matrix([list(v1), list(v3)]).transpose()
|
|
100
109
|
|
|
101
110
|
def lll(V: Matrix, delta: float = 0.75, sort: bool = True) -> Matrix:
|
|
102
|
-
"
|
|
111
|
+
"LLL algorithm for lattice reduction."
|
|
103
112
|
|
|
104
113
|
assert 0 < delta <= 1, f"LLL reqires 0 < delta={delta} <= 1"
|
|
105
114
|
j = 1
|
|
@@ -104,6 +104,7 @@ def crt(a: list[int], m: list[int]) -> int:
|
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
def fraction_repr(self):
|
|
107
|
+
"Representation of a fraction."
|
|
107
108
|
if self.denominator == 1:
|
|
108
109
|
return str(self.numerator)
|
|
109
110
|
return str(self.numerator) + "/" + str(self.denominator)
|
|
@@ -228,7 +229,7 @@ def order(a: int, n: int, factor=False) -> int:
|
|
|
228
229
|
"""Compute the order of `a` in the group Z_n^*."""
|
|
229
230
|
a %= n
|
|
230
231
|
assert a != 0 and gcd(a, n) == 1, f"{a} and {n} are not coprime!"
|
|
231
|
-
factors =
|
|
232
|
+
factors = {} # We compute euler_phi(n) and its factorization in one pass
|
|
232
233
|
for p, k in factorint(n).items(): # first factorize n
|
|
233
234
|
for pm, km in factorint(p - 1).items(): # factor p-1 and add the factors
|
|
234
235
|
if pm in factors:
|
|
@@ -255,7 +256,7 @@ def order(a: int, n: int, factor=False) -> int:
|
|
|
255
256
|
else:
|
|
256
257
|
break
|
|
257
258
|
if factor and i < k:
|
|
258
|
-
factors_order[p] = k - i
|
|
259
|
+
factors_order[p] = k - i # pylint: disable=E0606
|
|
259
260
|
if factor:
|
|
260
261
|
return order_a, factors_order
|
|
261
262
|
return order_a
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Polynomials
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from numbers import Number
|
|
6
|
+
|
|
5
7
|
class Poly:
|
|
6
8
|
"""
|
|
7
9
|
Represents a polynomial as a list of coefficients.
|
|
@@ -92,14 +94,16 @@ class Poly:
|
|
|
92
94
|
"Apply a given function to all coefficients."
|
|
93
95
|
self.coeff = list(map(func, self.coeff))
|
|
94
96
|
|
|
97
|
+
def _check_type(self, other):
|
|
98
|
+
return isinstance(other, int) or (isinstance(other, Number) and isinstance(self.coeff[0], Number)) or type(other) == type(self.coeff[0])
|
|
99
|
+
|
|
95
100
|
def __add__(self, other: "Poly") -> "Poly":
|
|
96
101
|
if not isinstance(other, self.__class__):
|
|
97
|
-
|
|
102
|
+
if self._check_type(other):
|
|
98
103
|
tmp = self.coeff[:]
|
|
99
104
|
tmp[0] += other
|
|
100
105
|
return self.__class__(tmp, modulus=self.modulus)
|
|
101
|
-
|
|
102
|
-
return NotImplemented
|
|
106
|
+
return NotImplemented
|
|
103
107
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
104
108
|
if ls < lo:
|
|
105
109
|
scoeff = self.coeff + (lo - ls) * [0]
|
|
@@ -116,12 +120,10 @@ class Poly:
|
|
|
116
120
|
|
|
117
121
|
def __radd__(self, other: "Poly") -> "Poly":
|
|
118
122
|
if not isinstance(other, self.__class__):
|
|
119
|
-
|
|
123
|
+
if self._check_type(other):
|
|
120
124
|
tmp = self.coeff[:]
|
|
121
125
|
tmp[0] += other
|
|
122
126
|
return self.__class__(tmp, modulus=self.modulus)
|
|
123
|
-
except:
|
|
124
|
-
pass
|
|
125
127
|
return NotImplemented
|
|
126
128
|
|
|
127
129
|
def __neg__(self) -> "Poly":
|
|
@@ -129,12 +131,11 @@ class Poly:
|
|
|
129
131
|
|
|
130
132
|
def __sub__(self, other: "Poly") -> "Poly":
|
|
131
133
|
if not isinstance(other, self.__class__):
|
|
132
|
-
|
|
134
|
+
if self._check_type(other):
|
|
133
135
|
tmp = self.coeff[:]
|
|
134
136
|
tmp[0] -= other
|
|
135
137
|
return self.__class__(tmp, modulus=self.modulus)
|
|
136
|
-
|
|
137
|
-
return NotImplemented
|
|
138
|
+
return NotImplemented
|
|
138
139
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
139
140
|
if ls < lo:
|
|
140
141
|
scoeff = self.coeff + (lo - ls) * [0]
|
|
@@ -147,24 +148,21 @@ class Poly:
|
|
|
147
148
|
modulus = self.modulus
|
|
148
149
|
if not modulus and other.modulus:
|
|
149
150
|
modulus = other.modulus
|
|
150
|
-
return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus=modulus)
|
|
151
|
+
return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus = modulus)
|
|
151
152
|
|
|
152
153
|
def __rsub__(self, other: "Poly") -> "Poly":
|
|
153
154
|
if not isinstance(other, self.__class__):
|
|
154
|
-
|
|
155
|
+
if self._check_type(other):
|
|
155
156
|
tmp = self.coeff[:]
|
|
156
157
|
tmp[0] -= other
|
|
157
158
|
return self.__class__(tmp, modulus=self.modulus)
|
|
158
|
-
except:
|
|
159
|
-
pass
|
|
160
159
|
return NotImplemented
|
|
161
160
|
|
|
162
161
|
def __mul__(self, other: "Poly") -> "Poly":
|
|
163
162
|
if not isinstance(other, self.__class__):
|
|
164
|
-
|
|
165
|
-
return Poly([other * s for s in self.coeff])
|
|
166
|
-
|
|
167
|
-
return NotImplemented
|
|
163
|
+
if self._check_type(other):
|
|
164
|
+
return Poly([other * s for s in self.coeff], modulus = self.modulus)
|
|
165
|
+
return NotImplemented
|
|
168
166
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
169
167
|
coeff = [0] * (ls + lo - 1)
|
|
170
168
|
for k in range(ls + lo - 1):
|
|
@@ -177,23 +175,50 @@ class Poly:
|
|
|
177
175
|
modulus = self.modulus
|
|
178
176
|
if not modulus and other.modulus:
|
|
179
177
|
modulus = other.modulus
|
|
180
|
-
return self.__class__(coeff, modulus=modulus)
|
|
178
|
+
return self.__class__(coeff, modulus = modulus)
|
|
179
|
+
|
|
180
|
+
def __rmul__(self, other) -> "Poly":
|
|
181
|
+
if self._check_type(other):
|
|
182
|
+
return self.__class__([other * s for s in self.coeff], modulus=self.modulus)
|
|
183
|
+
return NotImplemented
|
|
181
184
|
|
|
182
|
-
def
|
|
183
|
-
|
|
185
|
+
def __truediv__(self, other) -> "Poly":
|
|
186
|
+
if self._check_type(other):
|
|
187
|
+
return self.__class__([s / other for s in self.coeff], modulus=self.modulus)
|
|
188
|
+
if isinstance(other, self.__class__):
|
|
189
|
+
if not other.modulus:
|
|
190
|
+
raise NotImplementedError("Cannot invert polynomials without modulus.")
|
|
191
|
+
return self * other.inv()
|
|
192
|
+
return NotImplemented
|
|
193
|
+
|
|
194
|
+
def __rtruediv__(self, other) -> "Poly":
|
|
195
|
+
if not self.modulus:
|
|
196
|
+
raise NotImplementedError("Cannot invert polynomials without modulus.")
|
|
197
|
+
return other * self.inv()
|
|
184
198
|
|
|
185
199
|
def __pow__(self, i: int) -> "Poly":
|
|
186
|
-
|
|
200
|
+
if not isinstance(i, int):
|
|
201
|
+
return NotImplemented
|
|
202
|
+
zero = 0 * self.coeff[0]
|
|
203
|
+
one = zero + 1
|
|
204
|
+
res = self.__class__([one], modulus=self.modulus)
|
|
187
205
|
if i < 0:
|
|
188
206
|
if not self.modulus:
|
|
189
|
-
raise NotImplementedError("Cannot divide.")
|
|
207
|
+
raise NotImplementedError("Cannot divide polynomials without modulus.")
|
|
190
208
|
tmp = self.inv()
|
|
209
|
+
i *= -1
|
|
191
210
|
else:
|
|
192
211
|
tmp = self
|
|
193
212
|
for _ in range(i):
|
|
194
213
|
res *= tmp
|
|
195
214
|
return res
|
|
196
215
|
|
|
216
|
+
def __floordiv__(self, other: "Poly") -> "Poly":
|
|
217
|
+
return self.divmod(other)[0]
|
|
218
|
+
|
|
219
|
+
def __mod__(self, other: "Poly") -> "Poly":
|
|
220
|
+
return self.divmod(other)[1]
|
|
221
|
+
|
|
197
222
|
def divmod(self, other: "Poly") -> ("Poly", "Poly"):
|
|
198
223
|
"Polynom division with remainder."
|
|
199
224
|
if isinstance(other, list):
|
|
@@ -261,10 +286,10 @@ class Poly:
|
|
|
261
286
|
raise NotImplementedError(f"Cannot invert {self} modulo {other}.")
|
|
262
287
|
if not other:
|
|
263
288
|
raise NotImplementedError(f"{other} must be nonzero.")
|
|
289
|
+
zero = 0 * self.coeff[0]
|
|
290
|
+
one = zero +1
|
|
264
291
|
r0, r1 = other, self
|
|
265
|
-
y0, y1 = self.__class__([
|
|
266
|
-
[1], modulus=self.modulus
|
|
267
|
-
)
|
|
292
|
+
y0, y1 = self.__class__([zero], modulus=self.modulus), self.__class__([one], modulus=self.modulus)
|
|
268
293
|
while r1:
|
|
269
294
|
q, r = r0.divmod(r1)
|
|
270
295
|
r0, r1 = r1, r
|
|
@@ -272,6 +297,6 @@ class Poly:
|
|
|
272
297
|
if r0.degree() != 0:
|
|
273
298
|
raise ValueError(f"{self} is not invertible mod {other}.")
|
|
274
299
|
tmp = 1 / r0[0]
|
|
275
|
-
for i in range(y0
|
|
300
|
+
for i in range(len(y0)):
|
|
276
301
|
y0.coeff[i] *= tmp
|
|
277
302
|
return y0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6
|
|
4
4
|
Summary: Implemenation of same basic algorithms used in cryptography.
|
|
5
5
|
Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
|
|
6
6
|
Project-URL: Homepage, https://github.com/teschlg/kryptools
|
|
@@ -9,7 +9,7 @@ Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kryptools"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
|
|
6
6
|
]
|
|
7
7
|
description = "Implemenation of same basic algorithms used in cryptography."
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Programming Language :: Python :: 3",
|
|
12
12
|
"License :: OSI Approved :: MIT License",
|
|
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
|