kryptools 0.9.8__tar.gz → 0.9.10__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.9.8 → kryptools-0.9.10}/PKG-INFO +3 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/Zmod.py +27 -4
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/__init__.py +3 -2
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/ec.py +5 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor.py +10 -5
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_ecm.py +0 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_fmt.py +5 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_pm1.py +1 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_rho.py +0 -1
- kryptools-0.9.10/kryptools/intfuncs.py +106 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/la.py +3 -3
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/nt.py +24 -3
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/primes.py +7 -41
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/PKG-INFO +3 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/SOURCES.txt +14 -1
- {kryptools-0.9.8 → kryptools-0.9.10}/pyproject.toml +3 -1
- kryptools-0.9.10/tests/test_Zmod.py +66 -0
- kryptools-0.9.10/tests/test_dlog.py +18 -0
- kryptools-0.9.10/tests/test_ec.py +27 -0
- kryptools-0.9.10/tests/test_factor.py +18 -0
- kryptools-0.9.10/tests/test_factor_fmt.py +12 -0
- kryptools-0.9.10/tests/test_factor_qs.py +9 -0
- kryptools-0.9.10/tests/test_intfuncs.py +78 -0
- kryptools-0.9.10/tests/test_la.py +20 -0
- kryptools-0.9.10/tests/test_lat.py +15 -0
- kryptools-0.9.10/tests/test_nt.py +96 -0
- kryptools-0.9.10/tests/test_poly.py +15 -0
- kryptools-0.9.10/tests/test_primes.py +104 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/LICENSE +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/README.md +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_ic.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_qs.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_rho.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_dix.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_qs.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/lat.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/poly.py +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-0.9.8 → kryptools-0.9.10}/setup.cfg +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.10
|
|
4
4
|
Summary: Implemenation of same basic algorithms used in cryptography.
|
|
5
5
|
Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
|
|
6
|
+
License: MIT
|
|
6
7
|
Project-URL: Homepage, https://github.com/teschlg/kryptools
|
|
8
|
+
Project-URL: Source, https://github.com/teschlg/kryptools
|
|
7
9
|
Project-URL: Issues, https://github.com/teschlg/kryptools/issues
|
|
8
10
|
Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
|
|
9
11
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -4,7 +4,7 @@ Ring of intergers modulo `n`.
|
|
|
4
4
|
|
|
5
5
|
from math import gcd
|
|
6
6
|
from .factor import factorint
|
|
7
|
-
|
|
7
|
+
from .intfuncs import prime_power
|
|
8
8
|
|
|
9
9
|
class Zmod:
|
|
10
10
|
"""
|
|
@@ -25,12 +25,18 @@ class Zmod:
|
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
def __init__(self, n: int, short: bool = True):
|
|
28
|
+
if not isinstance(n, int) or n < 1:
|
|
29
|
+
raise ValueError(f"{n} is not a positive integer.")
|
|
28
30
|
self.n = n
|
|
29
31
|
self.short = short
|
|
30
32
|
self.group_order = 0
|
|
31
33
|
self.factors = {} # factoring of the group order
|
|
32
34
|
|
|
33
|
-
def __call__(self, x: int):
|
|
35
|
+
def __call__(self, x: int|list|tuple):
|
|
36
|
+
if isinstance(x, list):
|
|
37
|
+
return [ZmodPoint(xx, self) for xx in x]
|
|
38
|
+
elif isinstance(x, tuple):
|
|
39
|
+
return (ZmodPoint(xx, self) for xx in x)
|
|
34
40
|
return ZmodPoint(x, self)
|
|
35
41
|
|
|
36
42
|
def __eq__(self, other):
|
|
@@ -62,8 +68,25 @@ class Zmod:
|
|
|
62
68
|
self.group_order *= p**k
|
|
63
69
|
return self.group_order
|
|
64
70
|
|
|
65
|
-
def
|
|
71
|
+
def is_cyclic(self) -> bool:
|
|
72
|
+
"""Test if the group Z_n^* is cyclic."""
|
|
73
|
+
n = self.n
|
|
74
|
+
if n < 8:
|
|
75
|
+
return True
|
|
76
|
+
if not n % 2:
|
|
77
|
+
n //= 2
|
|
78
|
+
if not n % 2:
|
|
79
|
+
return False
|
|
80
|
+
if prime_power(n):
|
|
81
|
+
return True
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def generator(self, all = False) -> int|None:
|
|
66
85
|
"""Return a generator of the group Z_n^*."""
|
|
86
|
+
if not self.is_cyclic():
|
|
87
|
+
if all:
|
|
88
|
+
return []
|
|
89
|
+
return None
|
|
67
90
|
for a in range(2, self.n):
|
|
68
91
|
if gcd(a, self.n) > 1:
|
|
69
92
|
continue
|
|
@@ -76,7 +99,7 @@ class Zmod:
|
|
|
76
99
|
|
|
77
100
|
def star(self) -> int:
|
|
78
101
|
"""Return a list of all elements of the group Z_n^*."""
|
|
79
|
-
elements = [ 1 ]
|
|
102
|
+
elements = [ self(1) ]
|
|
80
103
|
for a in range(2, self.n):
|
|
81
104
|
if gcd(a, self.n) > 1:
|
|
82
105
|
continue
|
|
@@ -5,11 +5,12 @@ Implemenation of same basic algorithms used in cryptography.
|
|
|
5
5
|
__author__ = "Gerald Teschl"
|
|
6
6
|
__copyright__ = "Copyright 2024, Gerald Teschl"
|
|
7
7
|
__license__ = "MIT License"
|
|
8
|
-
__version__ = "0.9.
|
|
8
|
+
__version__ = "0.9.10"
|
|
9
9
|
__email__ = "Gerald.Teschl@univie.ac.at"
|
|
10
10
|
|
|
11
|
-
from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, order, crt
|
|
11
|
+
from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, is_carmichael_number, order, crt
|
|
12
12
|
from .primes import sieve_eratosthenes, prime_pi, is_prime, next_prime, previous_prime, random_prime, random_strongprime, is_safeprime, random_safeprime, is_blumprime, random_blumprime, miller_rabin_test, lucas_test
|
|
13
|
+
from .intfuncs import iroot, ilog, perfect_square, perfect_power, prime_power
|
|
13
14
|
from .factor import factorint, divisors
|
|
14
15
|
from .dlp import dlog
|
|
15
16
|
from .ec import EC_Weierstrass
|
|
@@ -71,6 +71,10 @@ class EC_Weierstrass():
|
|
|
71
71
|
return False
|
|
72
72
|
|
|
73
73
|
def __contains__(self, P: "ECPoint") -> bool:
|
|
74
|
+
if not isinstance(P, ECPoint) or P.curve != self:
|
|
75
|
+
return False
|
|
76
|
+
if P.x is None and P.y is None:
|
|
77
|
+
return True
|
|
74
78
|
return P.y**2 == P.x**3 + self.a * P.x + self.b
|
|
75
79
|
|
|
76
80
|
def info(self):
|
|
@@ -268,7 +272,7 @@ class ECPoint:
|
|
|
268
272
|
else:
|
|
269
273
|
y = (curve.p - y1) % curve.p
|
|
270
274
|
self.y = curve.gf(y)
|
|
271
|
-
if not
|
|
275
|
+
if self not in curve:
|
|
272
276
|
raise ValueError("Point not on curve!")
|
|
273
277
|
|
|
274
278
|
def __repr__(self):
|
|
@@ -116,7 +116,7 @@ def factorint(n: int, verbose: int = 0, trial_bnd: int = 2500) -> dict:
|
|
|
116
116
|
B2 += B2 & 1
|
|
117
117
|
num_curves *= 2
|
|
118
118
|
D = isqrt(B2)
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
if verbose:
|
|
121
121
|
print(f"Round {round+1} (B1={B1})")
|
|
122
122
|
|
|
@@ -172,16 +172,21 @@ def factorint(n: int, verbose: int = 0, trial_bnd: int = 2500) -> dict:
|
|
|
172
172
|
|
|
173
173
|
def divisors(n:int, proper:bool = False) -> int:
|
|
174
174
|
"""Returns an unsorted list of all divisors of an integer `n`."""
|
|
175
|
+
if not isinstance(n, int):
|
|
176
|
+
raise ValueError("Number must be an integer!")
|
|
177
|
+
n = abs(n)
|
|
178
|
+
if proper:
|
|
179
|
+
divisors = []
|
|
180
|
+
else:
|
|
181
|
+
divisors = [1]
|
|
182
|
+
if n <= 1:
|
|
183
|
+
return divisors
|
|
175
184
|
facctordict= factorint(n)
|
|
176
185
|
primes = [p for p in facctordict]
|
|
177
186
|
nprimes = len(primes)
|
|
178
187
|
multiplicites = [facctordict[p] for p in primes]
|
|
179
188
|
|
|
180
189
|
exponents = [0] * nprimes
|
|
181
|
-
if proper:
|
|
182
|
-
divisors = []
|
|
183
|
-
else:
|
|
184
|
-
divisors = [1]
|
|
185
190
|
i = 0
|
|
186
191
|
while True:
|
|
187
192
|
exponents[i] += 1
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Integer factorization: Fermat's method
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from math import isqrt,
|
|
5
|
+
from math import isqrt, floor, log10
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def factor_fermat(n: int, maxsteps: int|None = None, verbose: int = 0) -> list:
|
|
9
9
|
"""Find factors of n using the method of Fermat."""
|
|
10
|
+
if not isinstance(n, int) or n < 1:
|
|
11
|
+
raise ValueError("Number to be factored must be a positive integer!")
|
|
10
12
|
if verbose:
|
|
11
13
|
print(f"Factoring (Fermat): {n} ({floor(log10(n)) + 1} digits)")
|
|
12
14
|
factors = []
|
|
@@ -19,6 +21,8 @@ def factor_fermat(n: int, maxsteps: int|None = None, verbose: int = 0) -> list:
|
|
|
19
21
|
while n % p == 0:
|
|
20
22
|
factors.append(p)
|
|
21
23
|
n //= p
|
|
24
|
+
if n == 1:
|
|
25
|
+
return factors
|
|
22
26
|
start = isqrt(n - 1) + 1
|
|
23
27
|
step, mod = parameters[n % 24]
|
|
24
28
|
start += (mod - start) % step
|
|
@@ -59,7 +59,7 @@ def factor_pm1(n: int, B1: int|None = None, B2: int|None = None, x: int = 2, pm1
|
|
|
59
59
|
y = pow(x, stage_two_deltas[0], n)
|
|
60
60
|
D = (y - 1) % n
|
|
61
61
|
for k, d in enumerate(stage_two_deltas[1:]):
|
|
62
|
-
if not
|
|
62
|
+
if d not in saved:
|
|
63
63
|
saved[d] = pow(x, d, n)
|
|
64
64
|
y = y * saved[d] % n
|
|
65
65
|
D = D * (y - 1) % n
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integer Functions:
|
|
3
|
+
iroot(k, n) integer k'th root of n
|
|
4
|
+
ilog(b, n) integer log of n with respect ot base b
|
|
5
|
+
perfect_square(n) test if a number is a perfect square
|
|
6
|
+
perfect_power(n) test if a number is a perfect power
|
|
7
|
+
prime_power(n) test if a number is a prime power
|
|
8
|
+
"""
|
|
9
|
+
from math import isqrt
|
|
10
|
+
from .primes import sieve_eratosthenes, is_prime
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def iroot(k: int, n: int) -> int:
|
|
14
|
+
"""For a given positive integers `n` and `k`, finds the largest integer `r` such that `r**k <= n`."""
|
|
15
|
+
if not isinstance(k, int) or k < 1:
|
|
16
|
+
raise ValueError(f"k={k} must be a positive integer.")
|
|
17
|
+
if not isinstance(n, int) or n < 0:
|
|
18
|
+
raise ValueError(f"n={n} must be a nonegative integer.")
|
|
19
|
+
if n <= 1 or k == 1:
|
|
20
|
+
return n
|
|
21
|
+
if k == 2:
|
|
22
|
+
return isqrt(n)
|
|
23
|
+
rr = n
|
|
24
|
+
kk = k
|
|
25
|
+
while kk > 1:
|
|
26
|
+
kk //= 2
|
|
27
|
+
rr = isqrt(rr)
|
|
28
|
+
r = rr + 1
|
|
29
|
+
k1 = k-1
|
|
30
|
+
while rr < r:
|
|
31
|
+
r = rr
|
|
32
|
+
rr = (k1 * rr + n // rr ** k1) // k # Newton iteration
|
|
33
|
+
return r
|
|
34
|
+
|
|
35
|
+
def ilog(b: int, n:int) -> int:
|
|
36
|
+
"""For a given positive integer `n`, finds the largest integer `l` such that `b**l <= n`."""
|
|
37
|
+
# https://stackoverflow.com/questions/39190815/how-to-make-perfect-power-algorithm-more-efficient/39191163#39191163
|
|
38
|
+
if not isinstance(b, int) or b < 2:
|
|
39
|
+
raise ValueError(f"base b={b} must a positive integer larger then one.")
|
|
40
|
+
if not isinstance(n, int) or n <= 0:
|
|
41
|
+
raise ValueError(f"n={n} must be a positive integer.")
|
|
42
|
+
if n == 1:
|
|
43
|
+
return 0
|
|
44
|
+
if b == 2:
|
|
45
|
+
return n.bit_length() - 1
|
|
46
|
+
lo, blo, hi, bhi = 0, 1, 1, b
|
|
47
|
+
while bhi < n:
|
|
48
|
+
lo, blo, hi, bhi = hi, bhi, hi + hi, bhi * bhi
|
|
49
|
+
while 1 < (hi - lo):
|
|
50
|
+
mid = (lo + hi) // 2
|
|
51
|
+
bmid = blo * pow(b, mid - lo)
|
|
52
|
+
if n < bmid:
|
|
53
|
+
hi, bhi = mid, bmid
|
|
54
|
+
elif bmid < n:
|
|
55
|
+
lo, blo = mid, bmid
|
|
56
|
+
else: return mid
|
|
57
|
+
if bhi == n: return hi
|
|
58
|
+
return lo
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def perfect_square(n: int) -> int|None:
|
|
62
|
+
"""Returns the root if `n` is a perfect square and None else."""
|
|
63
|
+
if not isinstance(n, int) or n < 2:
|
|
64
|
+
return None
|
|
65
|
+
s = isqrt(n)
|
|
66
|
+
if s*s == n:
|
|
67
|
+
return s
|
|
68
|
+
|
|
69
|
+
def perfect_power(n: int) -> tuple|None:
|
|
70
|
+
"""Returns integers `(m, p)` with `m**p == n` if `n` is a perfect power and None else."""
|
|
71
|
+
if not isinstance(n, int) or abs(n) < 2:
|
|
72
|
+
return None
|
|
73
|
+
sign = 1
|
|
74
|
+
if n < 0:
|
|
75
|
+
sign = -1
|
|
76
|
+
n = abs(n)
|
|
77
|
+
for p in sieve_eratosthenes(n.bit_length() - 1):
|
|
78
|
+
m = iroot(p, n)
|
|
79
|
+
if pow(m, p) == n:
|
|
80
|
+
if sign == 1:
|
|
81
|
+
return m, p
|
|
82
|
+
if p > 2:
|
|
83
|
+
return -m, p
|
|
84
|
+
|
|
85
|
+
def prime_power(n: int) -> tuple|None:
|
|
86
|
+
"""Returns integers `(p, k)` with `p**k == n` if `n` is a prime power and None else."""
|
|
87
|
+
if not isinstance(n, int) or n < 2:
|
|
88
|
+
return None
|
|
89
|
+
if is_prime(n):
|
|
90
|
+
return (n, 1)
|
|
91
|
+
r = n
|
|
92
|
+
k = 1
|
|
93
|
+
while r.bit_length() > 20:
|
|
94
|
+
k += 1
|
|
95
|
+
r = iroot(k, n)
|
|
96
|
+
if r**k == n and is_prime(r):
|
|
97
|
+
return (r, k)
|
|
98
|
+
for p in sieve_eratosthenes(r - 1):
|
|
99
|
+
k = 0
|
|
100
|
+
while n % p == 0:
|
|
101
|
+
k += 1
|
|
102
|
+
n //= p
|
|
103
|
+
if k >= 1:
|
|
104
|
+
if n == 1:
|
|
105
|
+
return (p, k)
|
|
106
|
+
return None
|
|
@@ -69,7 +69,7 @@ class Matrix:
|
|
|
69
69
|
if isinstance(item, int):
|
|
70
70
|
i, j = divmod(item, self.cols)
|
|
71
71
|
return self.matrix[i][j]
|
|
72
|
-
return
|
|
72
|
+
return [self.matrix[k // self.cols][k % self.cols] for k in range(self.cols * self.rows)[item]]
|
|
73
73
|
|
|
74
74
|
def __setitem__(self, item, value):
|
|
75
75
|
if isinstance(item, tuple):
|
|
@@ -187,12 +187,12 @@ class Matrix:
|
|
|
187
187
|
def __mul__(self, other) -> "Matrix":
|
|
188
188
|
if isinstance(other, Matrix):
|
|
189
189
|
return self.multiply(other)
|
|
190
|
-
if isinstance(other, Number) or type(other)
|
|
190
|
+
if isinstance(other, Number) or type(other) is type(self.matrix[0][0]):
|
|
191
191
|
return self.__class__([ [item * other for item in row] for row in self.matrix ])
|
|
192
192
|
return NotImplemented
|
|
193
193
|
|
|
194
194
|
def __rmul__(self, other) -> "Matrix":
|
|
195
|
-
if isinstance(other, Number) or type(other)
|
|
195
|
+
if isinstance(other, Number) or type(other) is type(self.matrix[0][0]):
|
|
196
196
|
return self.__class__([ [item * other for item in row] for row in self.matrix ])
|
|
197
197
|
return NotImplemented
|
|
198
198
|
|
|
@@ -9,6 +9,7 @@ Number theory tools:
|
|
|
9
9
|
euler_phi(n) Euler phi function of n
|
|
10
10
|
carmichael_lambda(n) Carmichael lambda function of n
|
|
11
11
|
moebius_mu(n) Moebius function of n
|
|
12
|
+
is_carmichael_number(n) tests if a number is a Carmichael number
|
|
12
13
|
order(a, n) oder of a in the multiplicative group Z_n^*
|
|
13
14
|
crt([a1, a2, ...],[m1, m2, ...]) Chinese Remainder Theorem
|
|
14
15
|
"""
|
|
@@ -73,6 +74,8 @@ def convergents(cont_frac: list[int]) -> float:
|
|
|
73
74
|
|
|
74
75
|
def legendre_symbol(a: int, p: int) -> int:
|
|
75
76
|
"""Compute the Legendre symbol of `a` with respect to the prime `p`."""
|
|
77
|
+
if not isinstance(p, int) or p < 2:
|
|
78
|
+
raise ValueError(f"{p} must be a prime.")
|
|
76
79
|
a = a % p
|
|
77
80
|
if a == 0:
|
|
78
81
|
return 0
|
|
@@ -81,7 +84,9 @@ def legendre_symbol(a: int, p: int) -> int:
|
|
|
81
84
|
return -1
|
|
82
85
|
|
|
83
86
|
def jacobi_symbol(a: int, n: int) -> int:
|
|
84
|
-
"""Compute the Jacobi symbol of `a` with respect to the integer `n`."""
|
|
87
|
+
"""Compute the Jacobi symbol of `a` with respect to the odd integer `n`."""
|
|
88
|
+
if not isinstance(n, int) or n < 1 or not n % 2:
|
|
89
|
+
raise ValueError(f"{n} must be a positive odd integer.")
|
|
85
90
|
# Crandall/Pomerance Algorithm 2.3.5
|
|
86
91
|
a = a % n
|
|
87
92
|
t = 1
|
|
@@ -143,6 +148,8 @@ def carmichael_lambda(n: int) -> int:
|
|
|
143
148
|
"""Carmichael's lambda function of `n`."""
|
|
144
149
|
if not isinstance(n, int) or n < 1:
|
|
145
150
|
raise ValueError(f"{n} is not a positive integer")
|
|
151
|
+
if n == 1:
|
|
152
|
+
return 1
|
|
146
153
|
k = factorint(n)
|
|
147
154
|
lam_all = [] # values corresponding to the prime factors
|
|
148
155
|
for p in k:
|
|
@@ -168,6 +175,15 @@ def moebius_mu(n: int) -> int:
|
|
|
168
175
|
return -1
|
|
169
176
|
return 1
|
|
170
177
|
|
|
178
|
+
def is_carmichael_number(n: int) -> bool:
|
|
179
|
+
"""Tests if a number is a Carmichael number using Korselt's criterion."""
|
|
180
|
+
if n < 561:
|
|
181
|
+
return False
|
|
182
|
+
factors = factorint(n)
|
|
183
|
+
if len(factors) == 1: # prime
|
|
184
|
+
return False
|
|
185
|
+
return max(factors.values()) == 1 and not any((n-1) % (p-1) for p in factors)
|
|
186
|
+
|
|
171
187
|
# Order in Z_p^*
|
|
172
188
|
|
|
173
189
|
def order(a: int, n: int, factor=False) -> int:
|
|
@@ -215,7 +231,12 @@ def crt(a: list[int], m: list[int], coprime = True) -> int:
|
|
|
215
231
|
if l != len(a):
|
|
216
232
|
raise ValueError("The lists of numbers and modules must have equal length.")
|
|
217
233
|
|
|
218
|
-
if not coprime:
|
|
234
|
+
if not coprime:
|
|
235
|
+
if not all( (isinstance(mi, int) and mi > 1) for mi in m):
|
|
236
|
+
raise ValueError("Moduli must be positive integers larger than one.")
|
|
237
|
+
if not all( isinstance(ai, int) for ai in a):
|
|
238
|
+
raise ValueError("Residues must be integers.")
|
|
239
|
+
# make a copy
|
|
219
240
|
m = [ mi for mi in m ]
|
|
220
241
|
a = [ ai for ai in a ]
|
|
221
242
|
for i in range(l):
|
|
@@ -224,7 +245,7 @@ def crt(a: list[int], m: list[int], coprime = True) -> int:
|
|
|
224
245
|
if g == 1: # moduli are coprime, nothing to be done
|
|
225
246
|
continue
|
|
226
247
|
if (a[i] - a[j]) % g:
|
|
227
|
-
|
|
248
|
+
raise ValueError("Congruences not solvable!")
|
|
228
249
|
for p, k in factorint(g).items():
|
|
229
250
|
p = p**k # remove this factor from one of the equations
|
|
230
251
|
if gcd(m[i] // p, p) == 1:
|
|
@@ -13,9 +13,6 @@ Tools for prime numbers:
|
|
|
13
13
|
random_blumprime(l) find a pseudorandom Blum prime with bit length at least l
|
|
14
14
|
miller_rabin_test(n, b) Miller-Rabin primality test with base b
|
|
15
15
|
lucas_test(n) strong Lucas test
|
|
16
|
-
perfect_square(n) test if a number is a perfect square
|
|
17
|
-
perfect_power(n) test if a number is a perfect power
|
|
18
|
-
iroot(k, n) integer k'th root of n
|
|
19
16
|
"""
|
|
20
17
|
from math import isqrt, gcd, floor, ceil
|
|
21
18
|
from random import randint
|
|
@@ -72,7 +69,7 @@ def miller_rabin_test(n: int, bases: list[int] | int) -> bool:
|
|
|
72
69
|
if a == 0:
|
|
73
70
|
continue
|
|
74
71
|
b = pow(a, m, n)
|
|
75
|
-
if b
|
|
72
|
+
if b in (1, n - 1):
|
|
76
73
|
continue
|
|
77
74
|
for _ in range(1, k):
|
|
78
75
|
b = pow(b, 2, n)
|
|
@@ -110,40 +107,6 @@ def _lucas_sequence(n, D, k):
|
|
|
110
107
|
Qk %= n
|
|
111
108
|
return U % n, V % n, Qk
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
def perfect_square(n: int) -> int|None:
|
|
115
|
-
"""Returns the root if `n` is a perfect square and None else."""
|
|
116
|
-
s = isqrt(n)
|
|
117
|
-
if s*s == n:
|
|
118
|
-
return s
|
|
119
|
-
|
|
120
|
-
def iroot(k: int, n: int) -> int:
|
|
121
|
-
"""Compute the integer k'th root of n."""
|
|
122
|
-
if not isinstance(k, int) or n < 1:
|
|
123
|
-
raise ValueError(f"{k} is not a positive integer")
|
|
124
|
-
if k == 1:
|
|
125
|
-
return n
|
|
126
|
-
if k == 2:
|
|
127
|
-
return isqrt(n)
|
|
128
|
-
r = isqrt(n)
|
|
129
|
-
while r**k >= n:
|
|
130
|
-
rr = r
|
|
131
|
-
r = isqrt(r)
|
|
132
|
-
r =rr + 1
|
|
133
|
-
k1 = k-1
|
|
134
|
-
while rr < r:
|
|
135
|
-
r = rr
|
|
136
|
-
rr = (k1 * rr + n // rr ** k1) // k # Newton iteration
|
|
137
|
-
return r
|
|
138
|
-
|
|
139
|
-
def perfect_power(n: int) -> tuple|None:
|
|
140
|
-
"""Returns integers `(m, p)` with `m**p == n` if `n` is a perfect power and None else."""
|
|
141
|
-
for p in sieve_eratosthenes(n.bit_length() - 1):
|
|
142
|
-
m = iroot(p, n)
|
|
143
|
-
if pow(m, p) == n:
|
|
144
|
-
return (m, p)
|
|
145
|
-
|
|
146
|
-
|
|
147
110
|
# https://en.wikipedia.org/wiki/Lucas_pseudoprime#Implementing_a_Lucas_probable_prime_test
|
|
148
111
|
|
|
149
112
|
def lucas_test(n: int) -> bool:
|
|
@@ -152,9 +115,10 @@ def lucas_test(n: int) -> bool:
|
|
|
152
115
|
return False
|
|
153
116
|
if n % 2 == 0:
|
|
154
117
|
return n == 2
|
|
155
|
-
|
|
118
|
+
s = isqrt(n)
|
|
119
|
+
if s * s == n: # the search for D will not succeed in this case
|
|
156
120
|
return False
|
|
157
|
-
|
|
121
|
+
|
|
158
122
|
# write n = k 2^s - 1
|
|
159
123
|
k = (n + 1) // 2
|
|
160
124
|
s = 1
|
|
@@ -163,7 +127,7 @@ def lucas_test(n: int) -> bool:
|
|
|
163
127
|
s += 1
|
|
164
128
|
|
|
165
129
|
# Selfridge method for choosing D
|
|
166
|
-
|
|
130
|
+
|
|
167
131
|
D = 5
|
|
168
132
|
while True:
|
|
169
133
|
g = gcd(abs(D), n)
|
|
@@ -189,6 +153,8 @@ def lucas_test(n: int) -> bool:
|
|
|
189
153
|
|
|
190
154
|
def is_prime(n: int, trialdivision: bool = True) -> bool:
|
|
191
155
|
"""Test if an integer `n` if probable prime."""
|
|
156
|
+
if not isinstance(n, int) or n < 2:
|
|
157
|
+
return False
|
|
192
158
|
# Test small primes
|
|
193
159
|
if trialdivision:
|
|
194
160
|
for p in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.10
|
|
4
4
|
Summary: Implemenation of same basic algorithms used in cryptography.
|
|
5
5
|
Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
|
|
6
|
+
License: MIT
|
|
6
7
|
Project-URL: Homepage, https://github.com/teschlg/kryptools
|
|
8
|
+
Project-URL: Source, https://github.com/teschlg/kryptools
|
|
7
9
|
Project-URL: Issues, https://github.com/teschlg/kryptools/issues
|
|
8
10
|
Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
|
|
9
11
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -16,6 +16,7 @@ kryptools/factor_fmt.py
|
|
|
16
16
|
kryptools/factor_pm1.py
|
|
17
17
|
kryptools/factor_qs.py
|
|
18
18
|
kryptools/factor_rho.py
|
|
19
|
+
kryptools/intfuncs.py
|
|
19
20
|
kryptools/la.py
|
|
20
21
|
kryptools/lat.py
|
|
21
22
|
kryptools/nt.py
|
|
@@ -24,4 +25,16 @@ kryptools/primes.py
|
|
|
24
25
|
kryptools.egg-info/PKG-INFO
|
|
25
26
|
kryptools.egg-info/SOURCES.txt
|
|
26
27
|
kryptools.egg-info/dependency_links.txt
|
|
27
|
-
kryptools.egg-info/top_level.txt
|
|
28
|
+
kryptools.egg-info/top_level.txt
|
|
29
|
+
tests/test_Zmod.py
|
|
30
|
+
tests/test_dlog.py
|
|
31
|
+
tests/test_ec.py
|
|
32
|
+
tests/test_factor.py
|
|
33
|
+
tests/test_factor_fmt.py
|
|
34
|
+
tests/test_factor_qs.py
|
|
35
|
+
tests/test_intfuncs.py
|
|
36
|
+
tests/test_la.py
|
|
37
|
+
tests/test_lat.py
|
|
38
|
+
tests/test_nt.py
|
|
39
|
+
tests/test_poly.py
|
|
40
|
+
tests/test_primes.py
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kryptools"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.10"
|
|
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
|
+
license = { text = "MIT" }
|
|
9
10
|
requires-python = ">=3.10"
|
|
10
11
|
classifiers = [
|
|
11
12
|
"Programming Language :: Python :: 3",
|
|
@@ -15,5 +16,6 @@ classifiers = [
|
|
|
15
16
|
|
|
16
17
|
[project.urls]
|
|
17
18
|
Homepage = "https://github.com/teschlg/kryptools"
|
|
19
|
+
Source = "https://github.com/teschlg/kryptools"
|
|
18
20
|
Issues = "https://github.com/teschlg/kryptools/issues"
|
|
19
21
|
Docs = "https://github.com/teschlg/kryptools/tree/main/doc"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from math import gcd
|
|
3
|
+
from kryptools import Zmod, euler_phi
|
|
4
|
+
|
|
5
|
+
def test_Zmod_ops():
|
|
6
|
+
for n in range(2, 10):
|
|
7
|
+
Z_n = Zmod(n)
|
|
8
|
+
for a in range(n):
|
|
9
|
+
aa = Z_n(a)
|
|
10
|
+
bool(aa) == bool(a)
|
|
11
|
+
for i in range(4):
|
|
12
|
+
assert aa ** i == Z_n(pow(a, i, n))
|
|
13
|
+
if i == 0 or gcd(a, n) == 1:
|
|
14
|
+
assert aa ** -i == Z_n(pow(a, -i, n))
|
|
15
|
+
else:
|
|
16
|
+
with pytest.raises(ValueError):
|
|
17
|
+
aa ** -i
|
|
18
|
+
if gcd(a, n) == 1:
|
|
19
|
+
assert aa ** aa.order() == Z_n(1)
|
|
20
|
+
else:
|
|
21
|
+
with pytest.raises(ValueError):
|
|
22
|
+
aa.order()
|
|
23
|
+
for b in range(n):
|
|
24
|
+
bb = Z_n(b)
|
|
25
|
+
assert aa + bb == Z_n(a + b)
|
|
26
|
+
assert aa - bb == Z_n(a - b)
|
|
27
|
+
assert aa * bb == Z_n(a * b)
|
|
28
|
+
if gcd(b, n) == 1:
|
|
29
|
+
assert aa / bb == Z_n( a * pow(b, -1, n) )
|
|
30
|
+
|
|
31
|
+
def test_Zmod_methods():
|
|
32
|
+
Z_5 = Zmod(5)
|
|
33
|
+
assert Z_5(3).order() == 4
|
|
34
|
+
assert Z_5(1).is_generator() == False
|
|
35
|
+
assert Z_5(3).is_generator() == True
|
|
36
|
+
assert Z_5.generator(all = True) == Z_5([2, 3])
|
|
37
|
+
assert Z_5.star() == Z_5([1, 2, 3, 4])
|
|
38
|
+
assert [Z_5(i).sharp() for i in range(Z_5.n)] == [0, 1, 2, -2, -1]
|
|
39
|
+
assert [abs(Z_5(i)) for i in range(Z_5.n)] == [0, 1, 2, 2, 1]
|
|
40
|
+
assert str(Z_5(3)) == "3"
|
|
41
|
+
Z_5.short = False
|
|
42
|
+
assert str(Z_5(6)) == "1 (mod 5)"
|
|
43
|
+
|
|
44
|
+
Z_6 = Zmod(6)
|
|
45
|
+
assert Z_6(5).order() == 2
|
|
46
|
+
with pytest.raises(ValueError):
|
|
47
|
+
Z_6(3).order()
|
|
48
|
+
assert Z_6(1).is_generator() == False
|
|
49
|
+
assert Z_6(5).is_generator() == True
|
|
50
|
+
assert Z_6.generator(all = True) == Z_6([5])
|
|
51
|
+
assert Z_6.star() == Z_6([1, 5])
|
|
52
|
+
assert str(Z_6(3)) == "3"
|
|
53
|
+
|
|
54
|
+
def test_Zmod_order():
|
|
55
|
+
for n in range(2,10):
|
|
56
|
+
Z_n = Zmod(n)
|
|
57
|
+
assert Z_n.order() == euler_phi(n)
|
|
58
|
+
|
|
59
|
+
OEIS_A033948 = [ 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 17, 18, 19, 22, 23, 25, 26, 27,
|
|
60
|
+
29, 31, 34, 37, 38, 41, 43, 46, 47, 49, 50, 53, 54, 58, 59, 61, 62, 67, 71, 73, 74,
|
|
61
|
+
79, 81, 82, 83, 86, 89, 94, 97, 98, 101, 103, 106, 107, 109, 113, 118, 121, 122, 125,
|
|
62
|
+
127, 131, 134, 137, 139 ]
|
|
63
|
+
|
|
64
|
+
def test_Zmod_cyclic():
|
|
65
|
+
for n in range(2, OEIS_A033948[-1]+1):
|
|
66
|
+
assert Zmod(n).is_cyclic() == (n in OEIS_A033948)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from random import randint, seed
|
|
3
|
+
from kryptools import dlog
|
|
4
|
+
|
|
5
|
+
seed(0)
|
|
6
|
+
|
|
7
|
+
with pytest.raises(ValueError):
|
|
8
|
+
dlog(1, 3, 10)
|
|
9
|
+
|
|
10
|
+
def test_dlogt():
|
|
11
|
+
for data in (
|
|
12
|
+
[557639, 278819, 2],
|
|
13
|
+
[24570203447, 12285101723, 2],
|
|
14
|
+
[28031135240181527, 14015567620090763, 2] ):
|
|
15
|
+
p, m, a = data # m is the order of a in Z_p
|
|
16
|
+
x = randint(2, m - 1)
|
|
17
|
+
b = pow(a, x, p)
|
|
18
|
+
assert dlog(a, b, p) == x
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from random import randint, seed
|
|
3
|
+
from kryptools import EC_Weierstrass
|
|
4
|
+
seed(0)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_EC():
|
|
8
|
+
ec = EC_Weierstrass(239, 3, 1)
|
|
9
|
+
O = ec(None, None) # point at infinity
|
|
10
|
+
assert O in ec
|
|
11
|
+
P = ec.random()
|
|
12
|
+
assert P in ec
|
|
13
|
+
Q = ec.random()
|
|
14
|
+
assert Q in ec
|
|
15
|
+
assert P + O == P
|
|
16
|
+
assert O + P == P
|
|
17
|
+
assert P - P == O
|
|
18
|
+
assert P + P == 2 * P
|
|
19
|
+
R = O
|
|
20
|
+
for i in range(5):
|
|
21
|
+
assert i * P == R
|
|
22
|
+
R += P
|
|
23
|
+
assert P + Q == Q + P
|
|
24
|
+
assert Q.order() * Q == O
|
|
25
|
+
k = randint(1, ec.order())
|
|
26
|
+
R = k * Q
|
|
27
|
+
assert R.dlog(Q) == k
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from kryptools import factorint, is_prime
|
|
3
|
+
|
|
4
|
+
def myprod(factors: dict) -> int:
|
|
5
|
+
n = 1
|
|
6
|
+
for p in factors:
|
|
7
|
+
assert is_prime(p)
|
|
8
|
+
n *= p**factors[p]
|
|
9
|
+
return n
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_factorint():
|
|
13
|
+
assert factorint(0) == {0: 1}
|
|
14
|
+
assert factorint(1) == {}
|
|
15
|
+
for n in ( 1489576198567193874913874619387459183543154617315437135656,
|
|
16
|
+
2**128 - 1,
|
|
17
|
+
4521089809**7 ):
|
|
18
|
+
assert n == myprod(factorint(n))
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from math import prod
|
|
3
|
+
from kryptools import is_prime
|
|
4
|
+
from kryptools.factor_fmt import factor_fermat
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_fermat():
|
|
8
|
+
for n in range(1,1000):
|
|
9
|
+
factors = factor_fermat(n)
|
|
10
|
+
assert n == prod(factors)
|
|
11
|
+
if len(factors) == 1:
|
|
12
|
+
assert is_prime(n)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from random import randint, seed
|
|
3
|
+
from math import gcd
|
|
4
|
+
from kryptools import sieve_eratosthenes, factorint
|
|
5
|
+
from kryptools import iroot, ilog, perfect_square, perfect_power, prime_power
|
|
6
|
+
seed(0)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_iroot():
|
|
10
|
+
assert iroot(3, 0) == 0
|
|
11
|
+
assert iroot(3, 1) == 1
|
|
12
|
+
assert iroot(1, 3) == 3
|
|
13
|
+
for _ in range(100):
|
|
14
|
+
n = randint(0, 10**10)
|
|
15
|
+
for k in range(2, 5):
|
|
16
|
+
r = iroot(k, n)
|
|
17
|
+
assert r ** k <= n and (r+1) ** k > n
|
|
18
|
+
|
|
19
|
+
def test_ilog():
|
|
20
|
+
for n in range(1, 10000):
|
|
21
|
+
for b in range(2,5):
|
|
22
|
+
l = ilog(b, n)
|
|
23
|
+
assert b**l <= n and b**(l+1) > n
|
|
24
|
+
for _ in range(1000):
|
|
25
|
+
n = randint(1, 10**10)
|
|
26
|
+
for b in range(2,5):
|
|
27
|
+
l = ilog(b, n)
|
|
28
|
+
assert b**l <= n and b**(l+1) > n
|
|
29
|
+
|
|
30
|
+
def test_perfect_square():
|
|
31
|
+
for n in (-1, 0, 1):
|
|
32
|
+
assert perfect_square(n) == None
|
|
33
|
+
for n in range(2, 1000):
|
|
34
|
+
factors = factorint(n)
|
|
35
|
+
res = perfect_square(n)
|
|
36
|
+
if all( not (k % 2) for k in factors.values()):
|
|
37
|
+
assert isinstance(res, int)
|
|
38
|
+
assert res**2 == n
|
|
39
|
+
else:
|
|
40
|
+
assert res == None
|
|
41
|
+
|
|
42
|
+
def test_perfect_power():
|
|
43
|
+
for n in (-1, 0, 1):
|
|
44
|
+
assert perfect_power(n) == None
|
|
45
|
+
for n in range(2, 1000):
|
|
46
|
+
factors = factorint(n)
|
|
47
|
+
res = perfect_power(n)
|
|
48
|
+
g = gcd(*factors.values())
|
|
49
|
+
if g > 1:
|
|
50
|
+
assert isinstance(res, tuple)
|
|
51
|
+
assert res[0]**res[1] == n
|
|
52
|
+
n = -n
|
|
53
|
+
res = perfect_power(n)
|
|
54
|
+
while not g % 2:
|
|
55
|
+
g //= 2
|
|
56
|
+
if g > 1:
|
|
57
|
+
assert isinstance(res, tuple)
|
|
58
|
+
assert res[0]**res[1] == n
|
|
59
|
+
else:
|
|
60
|
+
assert res == None
|
|
61
|
+
else:
|
|
62
|
+
assert res == None
|
|
63
|
+
assert perfect_power(-n) == None
|
|
64
|
+
|
|
65
|
+
def test_prime_power():
|
|
66
|
+
for n in (-1, 0, 1):
|
|
67
|
+
assert prime_power(n) == None
|
|
68
|
+
for p in sieve_eratosthenes(30):
|
|
69
|
+
for k in range(1,6):
|
|
70
|
+
assert prime_power(p**k) == (p, k)
|
|
71
|
+
for n in range(2, 1000):
|
|
72
|
+
factors = factorint(n)
|
|
73
|
+
res = prime_power(n)
|
|
74
|
+
if len(factors) > 1:
|
|
75
|
+
assert res is None
|
|
76
|
+
else:
|
|
77
|
+
p, k = res
|
|
78
|
+
assert n == p**k
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from fractions import Fraction
|
|
3
|
+
from kryptools import Matrix, Zmod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_Matrix():
|
|
7
|
+
M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 12]])
|
|
8
|
+
assert len(M) == 3 * 3
|
|
9
|
+
assert M[0] == 1
|
|
10
|
+
assert M[:] == [1, 2, 3, 4, 5, 6, 7, 8, 12]
|
|
11
|
+
assert 2 * M - M == M
|
|
12
|
+
M.map(Fraction)
|
|
13
|
+
Mi = M.inv()
|
|
14
|
+
assert M * Mi == M.eye()
|
|
15
|
+
|
|
16
|
+
Z_11 = Zmod(11)
|
|
17
|
+
assert 2 * M - M == M
|
|
18
|
+
M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 12]], ring=Z_11)
|
|
19
|
+
Mi = M.inv()
|
|
20
|
+
assert M * Mi == M.eye()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from fractions import Fraction
|
|
3
|
+
from kryptools import Matrix, gram_schmidt, random_unimodular_matrix
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_gram_schmidt():
|
|
7
|
+
V = Matrix([[5, 8], [0, 1]], ring=Fraction)
|
|
8
|
+
Vs, M = gram_schmidt(V)
|
|
9
|
+
assert V == Vs * M
|
|
10
|
+
|
|
11
|
+
def test_random_unimodular_matrix():
|
|
12
|
+
for i in range(2, 5):
|
|
13
|
+
U = random_unimodular_matrix(i)
|
|
14
|
+
U.map(Fraction)
|
|
15
|
+
assert U.det() in (-1, 1)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from random import randint, seed
|
|
3
|
+
from math import prod, lcm
|
|
4
|
+
from fractions import Fraction
|
|
5
|
+
from kryptools import sieve_eratosthenes, is_prime
|
|
6
|
+
from kryptools import egcd, crt, cf, convergents, legendre_symbol, jacobi_symbol, carmichael_lambda, euler_phi, moebius_mu, is_carmichael_number
|
|
7
|
+
seed(0)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_egcd():
|
|
11
|
+
for a in range(100):
|
|
12
|
+
for b in range(100):
|
|
13
|
+
g, x, y = egcd(a, b)
|
|
14
|
+
assert x * a + y * b == g
|
|
15
|
+
|
|
16
|
+
def test_crt():
|
|
17
|
+
maxsize = 1000
|
|
18
|
+
numtests = 100
|
|
19
|
+
primes = sieve_eratosthenes(11)
|
|
20
|
+
n = prod(primes)
|
|
21
|
+
for _ in range(numtests):
|
|
22
|
+
x = randint(0, n-1)
|
|
23
|
+
assert x == crt([x % p for p in primes], primes)
|
|
24
|
+
for _ in range(numtests):
|
|
25
|
+
m = []
|
|
26
|
+
for _ in range(10):
|
|
27
|
+
m.append(randint(2, maxsize))
|
|
28
|
+
M = lcm(*m)
|
|
29
|
+
x = randint(0, M-1)
|
|
30
|
+
|
|
31
|
+
def test_cf():
|
|
32
|
+
for x in (Fraction(21,11), Fraction(11,21), Fraction(1,3)):
|
|
33
|
+
assert convergents(cf(x))[-1] == x
|
|
34
|
+
|
|
35
|
+
jacobi_data = (
|
|
36
|
+
(1, 1), (0, 1, -1, 0), (0, 1, -1, -1, 1, 0), (0, 1, 1, -1, 1, -1, -1, 0),
|
|
37
|
+
(0, 1, 1, 0, 1, 1, 0, 1, 1, 0), (0, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 0),
|
|
38
|
+
(0, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 0),
|
|
39
|
+
(0, 1, 1, 0, 1, 0, 0, -1, 1, 0, 0, -1, 0, -1, -1, 0),
|
|
40
|
+
(0, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 0) )
|
|
41
|
+
|
|
42
|
+
def test_jacobi_symbol():
|
|
43
|
+
for i in range(len(jacobi_data)):
|
|
44
|
+
n = 2 * i + 1
|
|
45
|
+
if is_prime(n):
|
|
46
|
+
for x in range(n+1):
|
|
47
|
+
assert jacobi_symbol(x, n) == jacobi_data[i][x]
|
|
48
|
+
assert jacobi_symbol(x, n) == legendre_symbol(x, n)
|
|
49
|
+
else:
|
|
50
|
+
for x in range(n+1):
|
|
51
|
+
assert jacobi_symbol(x, n) == jacobi_data[i][x]
|
|
52
|
+
|
|
53
|
+
#https://oeis.org/A000010
|
|
54
|
+
OEIS_A000010 = [
|
|
55
|
+
1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10, 22, 8, 20,
|
|
56
|
+
12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22,
|
|
57
|
+
46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20,
|
|
58
|
+
66, 32, 44 ]
|
|
59
|
+
|
|
60
|
+
def test_euler_phi():
|
|
61
|
+
for n in range(1, len(OEIS_A000010)+1):
|
|
62
|
+
assert euler_phi(n) == OEIS_A000010[n - 1]
|
|
63
|
+
|
|
64
|
+
#https://oeis.org/A002322
|
|
65
|
+
OEIS_A002322 = [
|
|
66
|
+
1, 1, 2, 2, 4, 2, 6, 2, 6, 4, 10, 2, 12, 6, 4, 4, 16, 6, 18, 4, 6, 10, 22, 2, 20,
|
|
67
|
+
12, 18, 6, 28, 4, 30, 8, 10, 16, 12, 6, 36, 18, 12, 4, 40, 6, 42, 10, 12, 22, 46,
|
|
68
|
+
4, 42, 20, 16, 12, 52, 18, 20, 6, 18, 28, 58, 4, 60, 30, 6, 16, 12, 10, 66, 16,
|
|
69
|
+
22, 12, 70, 6, 72, 36, 20, 18, 30, 12, 78, 4, 54 ]
|
|
70
|
+
|
|
71
|
+
def test_carmichael_lambda():
|
|
72
|
+
for n in range(1, len(OEIS_A002322)+1):
|
|
73
|
+
assert carmichael_lambda(n) == OEIS_A002322[n - 1]
|
|
74
|
+
|
|
75
|
+
#https://oeis.org/A008683
|
|
76
|
+
OEIS_A008683 = [
|
|
77
|
+
1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1, 0,
|
|
78
|
+
0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, -1, -1, 0, 0, 1, -1, 0, 0,
|
|
79
|
+
0, 1, 0, -1, 0, 1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 1, -1, -1, 0, 1, -1, -1, 0, -1, 1,
|
|
80
|
+
0, 0, 1, -1 ]
|
|
81
|
+
|
|
82
|
+
def test_moebius_mu():
|
|
83
|
+
for n in range(1, len(OEIS_A008683)+1):
|
|
84
|
+
assert moebius_mu(n) == OEIS_A008683[n - 1]
|
|
85
|
+
|
|
86
|
+
OEIS_A002997 = [ 561, 1105, 1729, 2465, 2821, 6601, 8911, 10585, 15841, 29341, 41041,
|
|
87
|
+
46657, 52633, 62745, 63973, 75361, 101101, 115921, 126217, 162401, 172081, 188461,
|
|
88
|
+
252601, 278545, 294409, 314821, 334153, 340561, 399001, 410041, 449065, 488881,
|
|
89
|
+
512461, 530881, 552721 ]
|
|
90
|
+
|
|
91
|
+
def test_is_carmichael_number():
|
|
92
|
+
for n in range(OEIS_A002997[4]+2):
|
|
93
|
+
assert is_carmichael_number(n) == (n in OEIS_A002997)
|
|
94
|
+
for n in OEIS_A002997:
|
|
95
|
+
assert is_carmichael_number(n) == True
|
|
96
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from kryptools import Poly, Zmod
|
|
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
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from math import isqrt
|
|
3
|
+
from random import randint, seed
|
|
4
|
+
from kryptools import sieve_eratosthenes, prime_pi, next_prime, previous_prime, is_prime, is_safeprime, miller_rabin_test, lucas_test
|
|
5
|
+
seed(0)
|
|
6
|
+
|
|
7
|
+
#https://oeis.org/A000040
|
|
8
|
+
OEIS_A000010 = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
|
|
9
|
+
71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
|
|
10
|
+
163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
|
|
11
|
+
257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353,
|
|
12
|
+
359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457,
|
|
13
|
+
461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
|
|
14
|
+
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673,
|
|
15
|
+
677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
|
|
16
|
+
809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
|
|
17
|
+
919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021,
|
|
18
|
+
1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109,
|
|
19
|
+
1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223 ]
|
|
20
|
+
|
|
21
|
+
def sieve(max: int) -> list:
|
|
22
|
+
is_prime = [True] * (max + 1) # to begin with, all numbers are potentially prime
|
|
23
|
+
is_prime[0 :: 2] = [False] * (max // 2 + 1)
|
|
24
|
+
is_prime[1] = False
|
|
25
|
+
is_prime[2] = True
|
|
26
|
+
|
|
27
|
+
# sieve out the primes starting at 3 in steps of 2 (ignoring even numbers)
|
|
28
|
+
for p in range(3, isqrt(max - 1) + 2, 2):
|
|
29
|
+
if is_prime[p]: # sieve out all multiples; note that numbers p*q with q<p were already sieved out previously
|
|
30
|
+
pp = p * p
|
|
31
|
+
p2 = p + p
|
|
32
|
+
is_prime[pp :: p2] = [False] * ((max - pp) // p2 + 1)
|
|
33
|
+
return is_prime
|
|
34
|
+
|
|
35
|
+
prime_test = sieve(OEIS_A000010[-1])
|
|
36
|
+
assert [ p for p in range(OEIS_A000010[-1]+1) if prime_test[p] ] == OEIS_A000010
|
|
37
|
+
|
|
38
|
+
def test_sieve_eratosthenes():
|
|
39
|
+
for i, p in enumerate(sieve_eratosthenes(OEIS_A000010[-1])):
|
|
40
|
+
assert p == OEIS_A000010[i]
|
|
41
|
+
if i > 0:
|
|
42
|
+
assert previous_prime(p-1) == OEIS_A000010[i-1]
|
|
43
|
+
if i < len(OEIS_A000010) - 1:
|
|
44
|
+
assert next_prime(p+1) == OEIS_A000010[i+1]
|
|
45
|
+
|
|
46
|
+
#https://oeis.org/A000040
|
|
47
|
+
OEIS_A000720 = [ 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,
|
|
48
|
+
9, 9, 9, 9, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 14, 14,
|
|
49
|
+
15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 18, 18, 18, 18, 19,
|
|
50
|
+
19, 19, 19, 20, 20, 21, 21, 21, 21, 21, 21 ]
|
|
51
|
+
|
|
52
|
+
def test_prime_pi():
|
|
53
|
+
for n in range(1, len(OEIS_A000720)+1):
|
|
54
|
+
assert prime_pi(n) == OEIS_A000720[n - 1]
|
|
55
|
+
|
|
56
|
+
max_sieve = 25326001
|
|
57
|
+
prime_test = sieve(max_sieve)
|
|
58
|
+
|
|
59
|
+
def test_is_prime():
|
|
60
|
+
for n in range(max_sieve + 1):
|
|
61
|
+
assert is_prime(n) == prime_test[n]
|
|
62
|
+
|
|
63
|
+
OEIS_A005385 = [ 5, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347, 359, 383, 467,
|
|
64
|
+
479, 503, 563, 587, 719, 839, 863, 887, 983, 1019, 1187, 1283, 1307, 1319, 1367, 1439,
|
|
65
|
+
1487, 1523, 1619, 1823, 1907, 2027, 2039, 2063, 2099, 2207, 2447, 2459, 2579, 2819,
|
|
66
|
+
2879, 2903, 2963 ]
|
|
67
|
+
|
|
68
|
+
def test_is_safeprime():
|
|
69
|
+
for n in range(OEIS_A000720[-1]+2):
|
|
70
|
+
assert is_safeprime(n) == (n in OEIS_A005385)
|
|
71
|
+
for n in OEIS_A005385:
|
|
72
|
+
assert is_safeprime(n) == True
|
|
73
|
+
|
|
74
|
+
OEIS_A014233 = [ 2047, 1373653, 25326001, 3215031751, 2152302898747, 3474749660383,
|
|
75
|
+
341550071728321, 341550071728321, 3825123056546413051, 3825123056546413051,
|
|
76
|
+
3825123056546413051, 318665857834031151167461, 3317044064679887385961981 ]
|
|
77
|
+
|
|
78
|
+
if OEIS_A014233[2] > max_sieve:
|
|
79
|
+
prime_test = sieve(OEIS_A014233[2])
|
|
80
|
+
|
|
81
|
+
def test_miller_rabin():
|
|
82
|
+
for i, n in enumerate(OEIS_A014233[0:3]):
|
|
83
|
+
bases = OEIS_A000010[:i+1]
|
|
84
|
+
assert miller_rabin_test(n, bases) == True
|
|
85
|
+
if i < 1:
|
|
86
|
+
for m in range(3, n, 2):
|
|
87
|
+
assert miller_rabin_test(m, bases) == prime_test[m]
|
|
88
|
+
else:
|
|
89
|
+
for _ in range(500 * i):
|
|
90
|
+
r = randint(2, n)
|
|
91
|
+
assert miller_rabin_test(m, bases) == prime_test[m]
|
|
92
|
+
|
|
93
|
+
OEIS_A217255 = [ 5459, 5777, 10877, 16109, 18971, 22499, 24569, 25199, 40309, 58519,
|
|
94
|
+
75077, 97439, 100127, 113573, 115639, 130139, 155819, 158399, 161027, 162133, 176399,
|
|
95
|
+
176471, 189419, 192509, 197801, 224369, 230691, 231703, 243629, 253259, 268349,
|
|
96
|
+
288919, 313499, 324899 ]
|
|
97
|
+
|
|
98
|
+
if OEIS_A217255[30] > max_sieve:
|
|
99
|
+
prime_test = sieve(OEIS_A217255[30])
|
|
100
|
+
|
|
101
|
+
def test_lucas():
|
|
102
|
+
for m in range(2, OEIS_A217255[30]+1):
|
|
103
|
+
if not (lucas_test(m) == prime_test[m]):
|
|
104
|
+
assert m in OEIS_A217255
|
|
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
|