kryptools 1.3.4__tar.gz → 1.3.5__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.3.4 → kryptools-1.3.5}/PKG-INFO +1 -1
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/Zmod.py +57 -3
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/__init__.py +1 -1
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor.py +9 -2
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/nt.py +7 -1
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools.egg-info/PKG-INFO +1 -1
- {kryptools-1.3.4 → kryptools-1.3.5}/pyproject.toml +1 -1
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_Zmod.py +20 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/LICENSE +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/README.md +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/GF2.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/aes.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/blockcipher.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/code.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/conway_polynomials.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/des.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/dlp.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/dlp_ic.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/dlp_qs.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/dlp_rho.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/ec.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor_dix.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor_ecm.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor_fmt.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor_pm1.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor_qs.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/factor_rho.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/intfuncs.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/keccak.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/la.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/lat.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/poly.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/primes.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools/sha1.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools.egg-info/SOURCES.txt +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/setup.cfg +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_GF2.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_aes.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_code.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_des.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_dlog.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_ec.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_factor.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_factor_fmt.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_factor_pm1.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_factor_qs.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_intfuncs.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_keccak.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_la.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_lat.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_nt.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_poly.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_primes.py +0 -0
- {kryptools-1.3.4 → kryptools-1.3.5}/tests/test_sha1.py +0 -0
|
@@ -5,6 +5,7 @@ Ring of intergers modulo `n`.
|
|
|
5
5
|
from math import gcd
|
|
6
6
|
from .factor import factorint
|
|
7
7
|
from .primes import is_prime
|
|
8
|
+
from .nt import sqrt_mod, crt
|
|
8
9
|
from .intfuncs import prime_power
|
|
9
10
|
|
|
10
11
|
|
|
@@ -30,12 +31,13 @@ class Zmod:
|
|
|
30
31
|
if not isinstance(n, int) or n < 1:
|
|
31
32
|
raise ValueError(f"{n} is not a positive integer.")
|
|
32
33
|
self.n = n
|
|
34
|
+
self.n_factors = {} # factoring of n
|
|
33
35
|
self.short = short
|
|
34
36
|
self.group_order = 0
|
|
35
|
-
self.factors = {} # factoring of the group order
|
|
37
|
+
self.factors = {} # factoring of the multiplicative group order
|
|
36
38
|
|
|
37
39
|
def __repr__(self):
|
|
38
|
-
return f"Z_{self.n}
|
|
40
|
+
return f"Z_{self.n}"
|
|
39
41
|
|
|
40
42
|
def __call__(self, x: int | list | tuple):
|
|
41
43
|
if isinstance(x, list|tuple):
|
|
@@ -59,7 +61,9 @@ class Zmod:
|
|
|
59
61
|
if self.group_order:
|
|
60
62
|
return self.group_order
|
|
61
63
|
# We compute euler_phi(n) and its factorization in one pass
|
|
62
|
-
|
|
64
|
+
if not self.n_factors:
|
|
65
|
+
self.n_factors = factorint(self.n)
|
|
66
|
+
for p, k in self.n_factors.items(): # first factorize n
|
|
63
67
|
# factor p-1 and add the factors
|
|
64
68
|
for pm, km in factorint(p - 1).items():
|
|
65
69
|
if pm in self.factors:
|
|
@@ -243,6 +247,56 @@ class ZmodPoint:
|
|
|
243
247
|
break
|
|
244
248
|
return order
|
|
245
249
|
|
|
250
|
+
def sqrt(self) -> "ZmodPoint":
|
|
251
|
+
if not self.ring.n_factors:
|
|
252
|
+
self.ring.n_factors = factorint(self.ring.n)
|
|
253
|
+
a = int(self)
|
|
254
|
+
if a in (0, 1):
|
|
255
|
+
return self.ring(a)
|
|
256
|
+
roots = []
|
|
257
|
+
powers = []
|
|
258
|
+
for p, k in self.ring.n_factors.items():
|
|
259
|
+
q = p**(k-1)
|
|
260
|
+
pk = q * p
|
|
261
|
+
ak = a % pk
|
|
262
|
+
powers.append(pk)
|
|
263
|
+
if ak in (0, 1):
|
|
264
|
+
roots.append(ak)
|
|
265
|
+
continue
|
|
266
|
+
# write ak as p^j * invertible
|
|
267
|
+
j = 0
|
|
268
|
+
while ak % p == 0:
|
|
269
|
+
j += 1
|
|
270
|
+
ak //= p
|
|
271
|
+
if j % 2:
|
|
272
|
+
raise ValueError("No quadratic residue!")
|
|
273
|
+
if ak == 1:
|
|
274
|
+
roots.append(p**(j//2))
|
|
275
|
+
continue
|
|
276
|
+
if p == 2:
|
|
277
|
+
if ak % 8 != 1:
|
|
278
|
+
raise ValueError("No quadratic residue!")
|
|
279
|
+
m = 4 # start at 2^2
|
|
280
|
+
x = ak % m # this is the root mod m
|
|
281
|
+
for i in range(3, k + 1):
|
|
282
|
+
# test all 4 poosible roots
|
|
283
|
+
if (x**2 - ak) % (m * 2):
|
|
284
|
+
x = m - x
|
|
285
|
+
if (x**2 - ak) % (m*2):
|
|
286
|
+
x = (x * (m//2-1)) % m
|
|
287
|
+
if (x**2 - ak) % (m * 2):
|
|
288
|
+
x = m - x
|
|
289
|
+
m *= 2
|
|
290
|
+
else:
|
|
291
|
+
# odd prime: Cipolla's formula
|
|
292
|
+
x = sqrt_mod(ak, p)
|
|
293
|
+
if x is None:
|
|
294
|
+
raise ValueError("No quadratic residue!")
|
|
295
|
+
if k > 1:
|
|
296
|
+
x = pow(x, q, pk) * pow(ak,((p-2)*q+1)//2, pk) % pk
|
|
297
|
+
roots.append((p**(j//2) * x) % pk)
|
|
298
|
+
return self.ring(crt(roots, powers))
|
|
299
|
+
|
|
246
300
|
def is_generator(self):
|
|
247
301
|
"Test if the point is a generator of the group Z_n^*."
|
|
248
302
|
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.3.
|
|
8
|
+
__version__ = "1.3.5"
|
|
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
|
|
@@ -6,6 +6,7 @@ Factorization of integers:
|
|
|
6
6
|
|
|
7
7
|
from math import isqrt, gcd, prod, floor, log10
|
|
8
8
|
from .primes import sieve_eratosthenes, is_prime
|
|
9
|
+
from .intfuncs import perfect_power
|
|
9
10
|
from .factor_pm1 import _pm1_parameters, factor_pm1
|
|
10
11
|
from .factor_ecm import _ecm_parameters, factor_ecm
|
|
11
12
|
#from .factor_qs import factor_qs
|
|
@@ -86,10 +87,16 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
|
|
|
86
87
|
return prime_factors
|
|
87
88
|
if verbose:
|
|
88
89
|
print("Trial division found:", list(prime_factors))
|
|
90
|
+
# check if the remainder is a perfect power
|
|
91
|
+
res = perfect_power(n)
|
|
92
|
+
if res:
|
|
93
|
+
n, k = res
|
|
94
|
+
else:
|
|
95
|
+
n, k = n, 1
|
|
89
96
|
if is_prime(n, trialdivision=False):
|
|
90
|
-
prime_factors[n] =
|
|
97
|
+
prime_factors[n] = k
|
|
91
98
|
return prime_factors
|
|
92
|
-
remaining_factors = {n:
|
|
99
|
+
remaining_factors = {n: k}
|
|
93
100
|
|
|
94
101
|
# https://gitlab.inria.fr/zimmerma/ecm/
|
|
95
102
|
ECM_PARAMETERS = [
|
|
@@ -111,7 +111,7 @@ def jacobi_symbol(a: int, n: int) -> int:
|
|
|
111
111
|
return 0
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def sqrt_mod(a: int, p: int) -> int:
|
|
114
|
+
def sqrt_mod(a: int, p: int) -> int|None:
|
|
115
115
|
"""Compute a square root of `a` modulo `p` unsing Cipolla's algorithm."""
|
|
116
116
|
a %= p
|
|
117
117
|
if a in (0, 1):
|
|
@@ -120,6 +120,12 @@ def sqrt_mod(a: int, p: int) -> int:
|
|
|
120
120
|
return None
|
|
121
121
|
if p % 4 == 3: # easy case
|
|
122
122
|
return pow(a, (p + 1) // 4, p)
|
|
123
|
+
if p % 8 == 5:
|
|
124
|
+
x = pow(a, (p + 3)//8, p)
|
|
125
|
+
c = pow(x, 2, p)
|
|
126
|
+
if c % p == a:
|
|
127
|
+
return x
|
|
128
|
+
return (x * pow(2, (p - 1)//4, p)) % p
|
|
123
129
|
# Cipolla's algorithm
|
|
124
130
|
for c in range(1, p): # find a field extension
|
|
125
131
|
tmp = (c * c - a) % p
|
|
@@ -87,6 +87,26 @@ def test_ZmodP_order():
|
|
|
87
87
|
xx *=x
|
|
88
88
|
assert o == x.order()
|
|
89
89
|
|
|
90
|
+
def test_ZmodP_sqrt():
|
|
91
|
+
for n in range(1,100):
|
|
92
|
+
print(n)
|
|
93
|
+
ring = Zmod(n)
|
|
94
|
+
one = ring(1)
|
|
95
|
+
roots = {} # compute a dict of all roots
|
|
96
|
+
for x in ring:
|
|
97
|
+
y = x**2
|
|
98
|
+
if y in roots:
|
|
99
|
+
roots[y].append(x)
|
|
100
|
+
else:
|
|
101
|
+
roots[y] = [ x ]
|
|
102
|
+
for x in ring:
|
|
103
|
+
if x not in roots:
|
|
104
|
+
with pytest.raises(ValueError):
|
|
105
|
+
x.sqrt()
|
|
106
|
+
else:
|
|
107
|
+
y = x.sqrt()
|
|
108
|
+
assert y**2 == x
|
|
109
|
+
|
|
90
110
|
def test_Zmod_field():
|
|
91
111
|
for n in range(1, 100):
|
|
92
112
|
assert Zmod(n).is_field() == is_prime(n)
|
|
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
|