kryptools 0.8__tar.gz → 0.9.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.
Files changed (28) hide show
  1. {kryptools-0.8 → kryptools-0.9.1}/PKG-INFO +8 -5
  2. {kryptools-0.8 → kryptools-0.9.1}/README.md +7 -4
  3. {kryptools-0.8 → kryptools-0.9.1}/kryptools/__init__.py +2 -2
  4. {kryptools-0.8 → kryptools-0.9.1}/kryptools/dlp.py +2 -6
  5. {kryptools-0.8 → kryptools-0.9.1}/kryptools/dlp_bsgs.py +1 -1
  6. {kryptools-0.8 → kryptools-0.9.1}/kryptools/dlp_qs.py +1 -1
  7. {kryptools-0.8 → kryptools-0.9.1}/kryptools/dlp_rho.py +1 -1
  8. {kryptools-0.8 → kryptools-0.9.1}/kryptools/ec.py +4 -3
  9. {kryptools-0.8 → kryptools-0.9.1}/kryptools/factor.py +12 -5
  10. {kryptools-0.8 → kryptools-0.9.1}/kryptools/la.py +10 -7
  11. {kryptools-0.8 → kryptools-0.9.1}/kryptools/lat.py +3 -1
  12. {kryptools-0.8 → kryptools-0.9.1}/kryptools/nt.py +43 -79
  13. {kryptools-0.8 → kryptools-0.9.1}/kryptools/primes.py +29 -17
  14. {kryptools-0.8 → kryptools-0.9.1}/kryptools.egg-info/PKG-INFO +8 -5
  15. {kryptools-0.8 → kryptools-0.9.1}/pyproject.toml +1 -1
  16. {kryptools-0.8 → kryptools-0.9.1}/LICENSE +0 -0
  17. {kryptools-0.8 → kryptools-0.9.1}/kryptools/Zmod.py +0 -0
  18. {kryptools-0.8 → kryptools-0.9.1}/kryptools/dlp_ic.py +0 -0
  19. {kryptools-0.8 → kryptools-0.9.1}/kryptools/factor_dix.py +0 -0
  20. {kryptools-0.8 → kryptools-0.9.1}/kryptools/factor_ecm.py +0 -0
  21. {kryptools-0.8 → kryptools-0.9.1}/kryptools/factor_fmt.py +0 -0
  22. {kryptools-0.8 → kryptools-0.9.1}/kryptools/factor_pm1.py +0 -0
  23. {kryptools-0.8 → kryptools-0.9.1}/kryptools/factor_qs.py +0 -0
  24. {kryptools-0.8 → kryptools-0.9.1}/kryptools/poly.py +0 -0
  25. {kryptools-0.8 → kryptools-0.9.1}/kryptools.egg-info/SOURCES.txt +0 -0
  26. {kryptools-0.8 → kryptools-0.9.1}/kryptools.egg-info/dependency_links.txt +0 -0
  27. {kryptools-0.8 → kryptools-0.9.1}/kryptools.egg-info/top_level.txt +0 -0
  28. {kryptools-0.8 → kryptools-0.9.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.8
3
+ Version: 0.9.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
  Project-URL: Homepage, https://github.com/teschlg/kryptools
@@ -22,18 +22,21 @@ you are welcome to read (and modify) the source. Any suggestions on how to make
22
22
  code more readable or make it better are welcome. However, my main goal is to keep
23
23
  it simple and readability will be preferred over small speed improvements.
24
24
 
25
+ It does not require any external libraries.
26
+
25
27
  The tools contained are:
26
28
 
27
- * number theory: sqrt modulo primes, crt, continued fractions, etc.
28
- * primes: Sieve of Erathostenes, primality tests
29
+ * number theory: sqrt modulo primes, Jacobi/Legendre symbol, Chinese Remainder Theorem, continued fractions, etc.
30
+ * primes: Sieve of Erathostenes, primality tests, generation of random safe/strong primes
29
31
  * solvers for discrete logarithms (naive, Pollard rho, Shanks baby step/giant step, index calculus, quadratic sieve)
30
32
  * integer factorization (Fermat, Pollard p-1, Lenstra's ECM, Dixon, basic quadratic sieve)
33
+ * elliptic curves (Weierstrass form), group operations, order, discrete logarithms
31
34
  * linear algebra: Hermite normal form, Gram-Schmidt
32
- * lattices: Babai rounding/nearest plane, lattice reduction
35
+ * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
33
36
 
34
37
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
35
38
  * Poly: a class for polynomials (division, modulo)
36
39
  * Zmod: a class for the ring of integers modulo an integer
37
40
 
38
41
  Documentation can be found in the jupyter notebook
39
- (currently incomplete: use the force, read the source).
42
+ (mostly done, but might not contain everything: use the force, read the source).
@@ -7,18 +7,21 @@ you are welcome to read (and modify) the source. Any suggestions on how to make
7
7
  code more readable or make it better are welcome. However, my main goal is to keep
8
8
  it simple and readability will be preferred over small speed improvements.
9
9
 
10
+ It does not require any external libraries.
11
+
10
12
  The tools contained are:
11
13
 
12
- * number theory: sqrt modulo primes, crt, continued fractions, etc.
13
- * primes: Sieve of Erathostenes, primality tests
14
+ * number theory: sqrt modulo primes, Jacobi/Legendre symbol, Chinese Remainder Theorem, continued fractions, etc.
15
+ * primes: Sieve of Erathostenes, primality tests, generation of random safe/strong primes
14
16
  * solvers for discrete logarithms (naive, Pollard rho, Shanks baby step/giant step, index calculus, quadratic sieve)
15
17
  * integer factorization (Fermat, Pollard p-1, Lenstra's ECM, Dixon, basic quadratic sieve)
18
+ * elliptic curves (Weierstrass form), group operations, order, discrete logarithms
16
19
  * linear algebra: Hermite normal form, Gram-Schmidt
17
- * lattices: Babai rounding/nearest plane, lattice reduction
20
+ * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
18
21
 
19
22
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
20
23
  * Poly: a class for polynomials (division, modulo)
21
24
  * Zmod: a class for the ring of integers modulo an integer
22
25
 
23
26
  Documentation can be found in the jupyter notebook
24
- (currently incomplete: use the force, read the source).
27
+ (mostly done, but might not contain everything: use the force, read the source).
@@ -2,8 +2,8 @@
2
2
  Implemenation of same basic algorithms used in cryptography.
3
3
  """
4
4
 
5
- from .nt import egcd, crt, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, order, carmichael_lambda
6
- from .primes import sieve_eratosthenes, is_prime, next_prime, random_prime, random_strongprime, is_safeprime, random_safeprime
5
+ from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, order, crt
6
+ from .primes import sieve_eratosthenes, is_prime, next_prime, previous_prime, random_prime, random_strongprime, is_safeprime, random_safeprime
7
7
  from .factor import factorint
8
8
  from .dlp import dlog
9
9
  from .ec import EC_Weierstrass
@@ -22,7 +22,7 @@ def dlog_naive(a: int, b: int, n: int, m: int = None) -> int|None:
22
22
  aa = (aa * a) % n
23
23
  if aa == b:
24
24
  return i + 1
25
- return None # no solution
25
+ raise ValueError("DLP not solvable.")
26
26
 
27
27
  def _dlog_switch(a: int, b: int, n: int, m: int) -> int:
28
28
  """Compute the discrete log_a(b) in Z_n of an element a of order m choosing an appropriate method."""
@@ -44,14 +44,12 @@ def _dlog_ph(a: int, b: int, n: int, q: int, k: int) -> int:
44
44
  aj = pow(a, q ** (k - j), n)
45
45
  bj = pow(b, q ** (k - j), n)
46
46
  yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q) # pylint: disable=E1130
47
- if yj is None:
48
- return None
49
47
  xj = xj + q ** (j - 1) * yj % q**j
50
48
  return xj
51
49
 
52
50
 
53
51
  def dlog(a: int, b: int, n: int, m: int|None = None) -> int:
54
- """Compute the discrete log_a(b) in Z_n of an element a of order m using Pohlig-Hellman reduction."""
52
+ """Compute the discrete log_a(b) in Z_n of an element `a` of order `m` using Pohlig-Hellman reduction."""
55
53
  a %= n
56
54
  b %= n
57
55
  assert gcd(a, n) == 1, "a and n must be coprime."
@@ -69,8 +67,6 @@ def dlog(a: int, b: int, n: int, m: int|None = None) -> int:
69
67
  aj = pow(a, m // pj**kj, n)
70
68
  bj = pow(b, m // pj**kj, n)
71
69
  l = _dlog_ph(aj, bj, n, pj, kj)
72
- if l is None:
73
- return None
74
70
  mm += [pj**kj]
75
71
  ll += [l]
76
72
  return crt(ll, mm)
@@ -26,4 +26,4 @@ def dlog_bsgs(a: int, b: int, n: int, m: int = None) -> int:
26
26
  if giant_step in baby_steps:
27
27
  return l * mm + baby_steps[giant_step]
28
28
  giant_step = giant_step * giant_stride % n
29
- return None # no solution
29
+ raise ValueError("DLP not solvable.")
@@ -292,6 +292,6 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
292
292
  relation[x + j] = 1
293
293
  res = process_relation(relation)
294
294
  if res:
295
- return(res)
295
+ return res
296
296
 
297
297
  raise Exception(f"Sorry, Quadratic sieve could not find enough relations! ({n_relations} - {n_relations_redundant} = {n_relations - n_relations_redundant} out of {len_relations}). Try to increase sieve_factor={sieve_factor}")
@@ -55,7 +55,7 @@ def dlog_rho(a: int, b: int, n: int, m: int = None, brent=0) -> int:
55
55
  l = (((alpha_x - alpha_y) // d) * pow((beta_y - beta_x) // d, -1, mm)) % mm
56
56
  while pow(a, l, n) != b:
57
57
  if l >= m:
58
- return None
58
+ raise ValueError("DLP not solvable.")
59
59
  l += mm
60
60
  return l
61
61
  trys -= 1
@@ -1,5 +1,6 @@
1
1
  """
2
- Elliptic curves
2
+ Elliptic curves:
3
+ EC_Weierstrass() constructor for a Weierstrass curve y^2 = x^3 + ax + b mod p
3
4
  """
4
5
 
5
6
  from math import isqrt, floor, sqrt
@@ -11,11 +12,11 @@ from .poly import Poly
11
12
 
12
13
  class EC_Weierstrass():
13
14
  """
14
- Elliptic curve in Weierstrass normal form y^2 = x^3 + ax + b mod p.
15
+ Elliptic curve in Weierstrass normal form `y^2 = x^3 + ax + b mod p`.
15
16
 
16
17
  Example:
17
18
 
18
- To define an elliptic curve use
19
+ To define an elliptic curve y^2 = x^3 + 3x + 1 mod 239 use
19
20
  >>> ec = EC_Weierstrass(239, 3, 1)
20
21
 
21
22
  To declare a point on the elliptic curve use
@@ -28,9 +28,16 @@ def _factor_fermat(n: int, steps: int = 10) -> list:
28
28
  if b * b == a * a - n:
29
29
  return a - b
30
30
 
31
- def factorint(n: int, verbose: int = 0) -> list:
32
- "Factor a number."
31
+ def factorint(n: int, verbose: int = 0) -> dict:
32
+ "Factor a the ineger `n`."
33
33
  prime_factors = {}
34
+ if not isinstance(n, int):
35
+ raise ValueError("Number to be factored must be an integer!")
36
+ if n == 0:
37
+ return prime_factors
38
+ if n < 0:
39
+ n *= -1
40
+ prime_factors[-1] = 1
34
41
 
35
42
  def add_factors(m: int, mm: tuple) -> None:
36
43
  k = remaining_factors[m]
@@ -96,7 +103,8 @@ def factorint(n: int, verbose: int = 0) -> list:
96
103
  for method in [ _factor_fermat, factor_pm1, factor_ecm ]: # , factor_qs ]:
97
104
  factors = list(remaining_factors)
98
105
  for m in factors:
99
- if verbose > 1: print("Factoring: ",m, "method", methods[method])
106
+ if verbose > 1:
107
+ print("Factoring: ",m, "method", methods[method])
100
108
  if method == factor_pm1:
101
109
  tmp = factor_pm1(m, pm1_parameters = pm1_parameters)
102
110
  elif method == factor_ecm:
@@ -131,5 +139,4 @@ def factorint(n: int, verbose: int = 0) -> list:
131
139
  if len(remaining_factors) == 0:
132
140
  return prime_factors
133
141
 
134
- print("Incomplete factorization!")
135
- return prime_factors, remaining_factors, new_factors
142
+ raise ValueError("Incomplete factorization:", prime_factors, remaining_factors, new_factors)
@@ -169,6 +169,9 @@ class Matrix:
169
169
  def __neg__(self) -> "Matrix":
170
170
  return -1 * self
171
171
 
172
+ def __pos__(self) -> "Matrix":
173
+ return self
174
+
172
175
  def __mul__(self, other) -> "Matrix":
173
176
  if isinstance(other, Matrix):
174
177
  return self.multiply(other)
@@ -208,7 +211,7 @@ class Matrix:
208
211
 
209
212
  def det(self) -> int:
210
213
  "Compute the determinant of a matrix M."
211
- if self.rows != self.rows:
214
+ if self.rows != self.cols:
212
215
  raise ValueError("Matrix must be square!")
213
216
  n, m = self.cols, self.rows
214
217
  R = self[:, :]
@@ -236,7 +239,7 @@ class Matrix:
236
239
 
237
240
  def inv(self) -> "Matrix":
238
241
  "Compute the inverse of a square matrix M."
239
- if self.rows != self.rows:
242
+ if self.rows != self.cols:
240
243
  raise ValueError("Matrix must be square!")
241
244
  n = self.cols
242
245
  MM = Matrix([[0 for _ in range(2*n)] for _ in range(n)])
@@ -250,7 +253,7 @@ class Matrix:
250
253
 
251
254
  def is_unimodular(self) -> bool:
252
255
  "Test if the matrix is unimodular."
253
- if self.rows != self.rows:
256
+ if self.rows != self.cols:
254
257
  return False
255
258
  def is_integer(i):
256
259
  if isinstance(i, int) or (isinstance(i, Fraction) and i.denominator == 1):
@@ -259,7 +262,7 @@ class Matrix:
259
262
  return all([is_integer(i) for i in self]) and self.det()**2 == 1
260
263
 
261
264
  def zeros(self, m: int = None, n: int = None):
262
- "Returns a zero matrix of the same dimension"
265
+ "Returns a zero matrix of the same dimension."
263
266
  if not m and not n:
264
267
  n, m = self.cols, self.rows
265
268
  elif not n:
@@ -271,7 +274,7 @@ class Matrix:
271
274
  return Matrix([[ zero for j in range(n) ] for i in range(m) ])
272
275
 
273
276
  def eye(self, m: int = None, n: int = None):
274
- "Returns an identity matrix of the same dimension"
277
+ "Returns an identity matrix of the same dimension."
275
278
  def delta(i, j):
276
279
  if i == j:
277
280
  return one
@@ -289,13 +292,13 @@ class Matrix:
289
292
 
290
293
 
291
294
  def zeros(m: int, n: int = None, zero = 0) -> "Matrix":
292
- "Returns a zero matrix of the given dimension"
295
+ "Returns a zero matrix of the given dimension."
293
296
  if not n:
294
297
  n = m
295
298
  return Matrix([[ zero for j in range(n) ] for i in range(m) ])
296
299
 
297
300
  def eye(m:int, n: int = None) -> "Matrix":
298
- "Returns an identity matrix of the given dimension"
301
+ "Returns an identity matrix of the given dimension."
299
302
  def delta(i, j):
300
303
  if i == j:
301
304
  return 1
@@ -42,6 +42,8 @@ def hermite_nf(M: Matrix) -> Matrix:
42
42
  H[:, jj] -= tmp * H[:, j]
43
43
  #print(H)
44
44
  j -= 1
45
+ if j < 0:
46
+ break
45
47
  while H.cols> 1 and all(not H[i, 0] for i in range(m)): # remove zero columns
46
48
  H = H[:,1:]
47
49
  return H
@@ -173,7 +175,7 @@ def lll(V: Matrix, delta: float = 0.75, sort: bool = True) -> Matrix:
173
175
  return U
174
176
 
175
177
  def random_unimodular_matrix(n: int, iterations: int = 50, max_val: int = None) -> Matrix:
176
- "Create a random unimodular matrix of dimension n."
178
+ "Create a pseudorandom unimodular matrix of dimension n."
177
179
  W = zeros(n)
178
180
  for i in range(n):
179
181
  for j in range(i, n):
@@ -1,13 +1,15 @@
1
1
  """
2
2
  Number theory tools:
3
3
  egcd(a,b) extended Euclidean agorithm
4
- crt([a1, a2, ...],[m1, m2, ...]) Chinese Remainder Theorem
5
4
  cf(Fraction(m,n)) continued fraction expansions
6
5
  convergents() convergents of a continued fraction
7
6
  legendre_symbol(a, p) compute the Legendre symbol of a with respect to the prime p
8
7
  jacobi_symbol(a, n) compute the Jacobi symbol of a with respect to n
9
8
  sqrt_mod(n, p) square root of n modulo a prime p
9
+ euler_phi(n) Euler phi function of n
10
+ carmichael_lambda(n) Carmichael lambda function of n
10
11
  order(a, n) oder of a in the multiplicative group Z_n^*
12
+ crt([a1, a2, ...],[m1, m2, ...]) Chinese Remainder Theorem
11
13
  """
12
14
  from math import gcd, lcm, prod
13
15
  from fractions import Fraction
@@ -26,83 +28,8 @@ def egcd(a: int, b: int) -> (int, int, int):
26
28
  return r0, x0, y0
27
29
 
28
30
 
29
- # Chinese remainder theorem
30
-
31
- def crt(a: list[int], m: list[int]) -> int:
32
- """Solve given linear congruences x % m[j] == a[j] using the Chinese Remainder Theorem."""
33
- l = len(a)
34
- assert len(m) == l, "The lists of numbers and modules must have equal length."
35
- M = prod(m)
36
- Mi = [M // m[i] for i in range(l)]
37
- MNi = [Mi[i] * pow(Mi[i], -1, m[i]) % M for i in range(l)]
38
- return sum([a[i] * MNi[i] % M for i in range(l)]) % M
39
-
40
31
  # Continued fractions
41
32
 
42
- # class Fraction:
43
- # "Rationl number"
44
- #
45
- # def __init__(self, numerator: int, denominator: int = 1):
46
- # assert denominator, "Denominator must be nozero."
47
- # tmp = gcd(denominator, numerator)
48
- # if tmp != 1:
49
- # denominator //= tmp
50
- # numerator //= tmp
51
- # self.numerator = numerator
52
- # self.denominator = denominator
53
- #
54
- # def __repr__(self):
55
- # if self.denominator == 1:
56
- # return str(self.numerator)
57
- # else:
58
- # return str(self.numerator) + "/" + str(self.denominator)
59
- #
60
- # def __eq__(self, other):
61
- # if not isinstance(other, self.__class__):
62
- # return False
63
- # return self.denominator == other.denominator and self.numerator == other.numerator
64
- #
65
- # def __bool__(self):
66
- # return self.numerator != 0
67
- #
68
- # def __add__(self, other: "Fraction") -> "Fraction":
69
- # if isinstance(other, self.__class__):
70
- # return Rational(self.numerator * other.denominator + other.numerator * self.denominator, self.denominator * other.denominator)
71
- # if isinstance(other, int):
72
- # return Rational(self.numerator + other * self.denominator, self.denominator)
73
- # raise ValueError(f"Cannot add {self} and {other}.")
74
- #
75
- # def __neg__(self) -> "Fraction":
76
- # return Rational(- self.numerator, self.denominator)
77
- #
78
- # def __sub__(self, other: "Fraction") -> "Fraction":
79
- # if isinstance(other, self.__class__):
80
- # return Rational(self.numerator * other.denominator - other.numerator * self.denominator, self.denominator * other.denominator)
81
- # if isinstance(other, int):
82
- # return Rational(self.numerator - other * self.denominator, self.denominator)
83
- # raise ValueError(f"Cannot subtract {self} and {other}.")
84
- #
85
- # def __mul__(self, other: "Fraction") -> "Fraction":
86
- # if isinstance(other, self.__class__):
87
- # return Rational(self.numerator * other.numerator, self.denominator * other.denominator)
88
- # if isinstance(other, int):
89
- # return Rational(self.numerator * other, self.denominator)
90
- # raise ValueError(f"Cannot multiply {self} and {other}.")
91
- #
92
- # def __rmul__(self, other: int) -> "Fraction":
93
- # return Rational(self.denominator * other, self.numerator)
94
- #
95
- # def __truediv__(self, other: "Fraction") -> "Fraction":
96
- # if isinstance(other, self.__class__):
97
- # return Rational(self.numerator * other.denominator, self.denominator * other.numerator)
98
- # if isinstance(other, int):
99
- # return Rational(self.numerator, self.denominator * other)
100
- # raise ValueError(f"Cannot divide {self} and {other}.")
101
- #
102
- # def __pow__(self, scalar: int) -> "Fraction":
103
- # return Rational(self.numerator**scalar, self.denominator**scalar)
104
-
105
-
106
33
  def fraction_repr(self):
107
34
  "Representation of a fraction."
108
35
  if self.denominator == 1:
@@ -113,7 +40,7 @@ Fraction.__repr__ = fraction_repr
113
40
 
114
41
 
115
42
  def cf(x: Fraction) -> list[int]:
116
- "Compute the continued fraction expansion of the rational number m/n."
43
+ "Compute the continued fraction expansion of the rational fraction `x`."
117
44
  res = []
118
45
  m = x.numerator
119
46
  n = x.denominator
@@ -144,7 +71,7 @@ def convergents(cont_frac: list[int]) -> float:
144
71
  # Legendre symbol
145
72
 
146
73
  def legendre_symbol(a: int, p: int) -> int:
147
- """Compute the Legendre symbol of a with respect to the prime p."""
74
+ """Compute the Legendre symbol of `a` with respect to the prime `p`."""
148
75
  a = a % p
149
76
  if a == 0:
150
77
  return 0
@@ -153,7 +80,7 @@ def legendre_symbol(a: int, p: int) -> int:
153
80
  return -1
154
81
 
155
82
  def jacobi_symbol(a: int, n: int) -> int:
156
- """Compute the Jacobi symbol of a with respect to the integer n."""
83
+ """Compute the Jacobi symbol of `a` with respect to the integer `n`."""
157
84
  # Crandall/Pomerance Algorithm 2.3.5
158
85
  a = a % n
159
86
  t = 1
@@ -260,3 +187,40 @@ def order(a: int, n: int, factor=False) -> int:
260
187
  if factor:
261
188
  return order_a, factors_order
262
189
  return order_a
190
+
191
+ # Chinese remainder theorem
192
+
193
+ def crt(a: list[int], m: list[int], coprime = True) -> int:
194
+ """Solve given linear congruences `x % m[j] == a[j]` using the Chinese Remainder Theorem."""
195
+ l = len(m)
196
+ if l != len(a):
197
+ raise ValueError("The lists of numbers and modules must have equal length.")
198
+
199
+ if not coprime: # make a copy
200
+ m = [ mi for mi in m ]
201
+ a = [ ai for ai in a ]
202
+ for i in range(l):
203
+ for j in range(i + 1,l):
204
+ g = gcd(m[i], m[j])
205
+ if g == 1: # moduli are coprime, nothing to be done
206
+ continue
207
+ if (a[i] - a[j]) % g:
208
+ raise ValueError(f"Congruences not solvable!")
209
+ for p, k in factorint(g).items():
210
+ p = p**k # remove this factor from one of the equations
211
+ if gcd(m[i] // p, p) == 1:
212
+ m[i] //= p
213
+ a[i] %= m[i]
214
+ else:
215
+ m[j] //= p
216
+ a[j] %= m[j]
217
+ for i in reversed(range(l)):
218
+ if m[i] == 1:
219
+ del m[i]
220
+ del a[i]
221
+ l =len(m)
222
+
223
+ M = prod(m)
224
+ Mi = [M // m[i] for i in range(l)]
225
+ MNi = [Mi[i] * pow(Mi[i], -1, m[i]) % M for i in range(l)]
226
+ return sum([a[i] * MNi[i] % M for i in range(l)]) % M
@@ -3,10 +3,11 @@ Tools for prime numbers:
3
3
  sieve_eratosthenes(B) a tuple of all primes up to including B
4
4
  is_prime(n) test if n is probably prime
5
5
  next_prime(n) find the next prime larger or equal n
6
- random_prime(l) find a random prime with bit length at least l
7
- random_strongprime(l) find a random strong prime with bit length at least l
6
+ previous_prime(n) find the previous prime smaller or equal n
7
+ random_prime(l) find a pseudorandom prime with bit length at least l
8
+ random_strongprime(l) find a pseudorandom strong prime with bit length at least l
8
9
  is_safeprime(n) test if n is a safe prime
9
- random_safeprime(n) find a random safe prime with bit length at least l and ord(2)=(p-1)/2
10
+ random_safeprime(n) find a pseudorandom safe prime with bit length at least l and ord(2)=(p-1)/2
10
11
  miller_rabin_test(n, b) Miller-Rabin primality test with base b
11
12
  """
12
13
  from math import isqrt, gcd
@@ -15,16 +16,16 @@ from .nt import jacobi_symbol
15
16
 
16
17
  # Erathostenes
17
18
 
18
- def sieve_eratosthenes(B: int) -> list:
19
- """ "Returns a list of all primes up to (including) max."""
20
- B1 = (isqrt(B) -1)//2
19
+ def sieve_eratosthenes(B: int) -> tuple:
20
+ """Returns a tuple of all primes up to (including) `B`."""
21
+ B1 = (isqrt(B) - 1)//2
21
22
  B = (B - 1)//2
22
- isprime = [True] * (B + 1) # to begin with, all numbers are potentially prime
23
- # sieve out the primes p=2*q+1 starting at 3 in steps of 2 (ignoring even numbers)
23
+ isprime = [True] * (B + 1) # to begin with, all odd numbers are potentially prime
24
+ # sieve out the primes p=2*q+1 starting at q = 1
24
25
  for q in range(1, B1 + 1):
25
- if isprime[q]: # sieve out all multiples; numbers p*q with q<p were already sieved out previously
26
- qq = (q << 1) * (q + 1)
27
- p = (q << 1) | 1
26
+ if isprime[q]: # sieve out all multiples; numbers p*r with r<p were already sieved out previously
27
+ qq = 2 * q * (q + 1)
28
+ p = 2 * q + 1
28
29
  isprime[qq :: p] = [False] * ((B - qq) // p + 1)
29
30
 
30
31
  return tuple([2] + [2 * q + 1 for q in range(1, B + 1) if isprime[q]])
@@ -33,7 +34,7 @@ def sieve_eratosthenes(B: int) -> list:
33
34
 
34
35
 
35
36
  def miller_rabin_test(n: int, bases: list[int] | int) -> bool:
36
- """Run a Miller-Rabin test with given bases on n."""
37
+ """Run a Miller-Rabin test with given bases on `n`."""
37
38
  if n < 2:
38
39
  return False
39
40
  if n % 2 == 0:
@@ -58,7 +59,7 @@ def miller_rabin_test(n: int, bases: list[int] | int) -> bool:
58
59
  return False
59
60
 
60
61
  def is_prime(n: int) -> bool:
61
- """Test if an integer n if probable prime."""
62
+ """Test if an integer `n` if probable prime."""
62
63
  if n < 18446744073709551616: # https://miller-rabin.appspot.com
63
64
  return miller_rabin_test(n, [2, 325, 9375, 28178, 450775, 9780504, 1795265022])
64
65
  if n < 3317044064679887385961981:
@@ -66,18 +67,29 @@ def is_prime(n: int) -> bool:
66
67
  return miller_rabin_test(n, [2]) and _is_strong_lucas_prp(n) # Baillie–PSW primality test
67
68
 
68
69
  def next_prime(n: int) -> int:
69
- """Find the next prime larger or equal n."""
70
+ """Find the next prime larger or equal `n`."""
71
+ if n < 3:
72
+ return 2
70
73
  n |= 1 # make sure n is odd
71
74
  while not is_prime(n):
72
75
  n += 2
73
76
  return n
74
77
 
78
+ def previous_prime(n: int) -> int:
79
+ """Find the previous prime smaller or equal `n`."""
80
+ if n < 3:
81
+ return 2
82
+ n += (n & 1) - 1 # make sure n is odd
83
+ while not is_prime(n):
84
+ n -= 2
85
+ return n
86
+
75
87
  def random_prime(l: int) -> int:
76
- """Find a random prime with bit length at least l."""
88
+ """Find a pseudorandom prime with bit length at least `l`."""
77
89
  return next_prime(randint(2 ** (l - 1), 2**l - 1))
78
90
 
79
91
  def random_strongprime(l: int) -> int:
80
- """Find a random strong prime with bit length at least l using Gordon's algorithm."""
92
+ """Find a pseudorandom strong prime with bit length at least `l` using Gordon's algorithm."""
81
93
  t = random_prime(l)
82
94
  s = random_prime(l)
83
95
  u = 2 * t
@@ -96,7 +108,7 @@ def is_safeprime(p: int) -> bool:
96
108
  return is_prime(p) and is_prime((p - 1) // 2)
97
109
 
98
110
  def random_safeprime(l: int) -> int:
99
- """Find a random safe prime with bit length at least l and ord(2)=(p-1)/2."""
111
+ """Find a pseudorandom safe prime with bit length at least `l` and `ord(2)=(p-1)/2`."""
100
112
  p = randint(2 ** (l - 1), 2**l - 1)
101
113
  p = p - (p % 24) + 23
102
114
  while not is_safeprime(p):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.8
3
+ Version: 0.9.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
  Project-URL: Homepage, https://github.com/teschlg/kryptools
@@ -22,18 +22,21 @@ you are welcome to read (and modify) the source. Any suggestions on how to make
22
22
  code more readable or make it better are welcome. However, my main goal is to keep
23
23
  it simple and readability will be preferred over small speed improvements.
24
24
 
25
+ It does not require any external libraries.
26
+
25
27
  The tools contained are:
26
28
 
27
- * number theory: sqrt modulo primes, crt, continued fractions, etc.
28
- * primes: Sieve of Erathostenes, primality tests
29
+ * number theory: sqrt modulo primes, Jacobi/Legendre symbol, Chinese Remainder Theorem, continued fractions, etc.
30
+ * primes: Sieve of Erathostenes, primality tests, generation of random safe/strong primes
29
31
  * solvers for discrete logarithms (naive, Pollard rho, Shanks baby step/giant step, index calculus, quadratic sieve)
30
32
  * integer factorization (Fermat, Pollard p-1, Lenstra's ECM, Dixon, basic quadratic sieve)
33
+ * elliptic curves (Weierstrass form), group operations, order, discrete logarithms
31
34
  * linear algebra: Hermite normal form, Gram-Schmidt
32
- * lattices: Babai rounding/nearest plane, lattice reduction
35
+ * lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
33
36
 
34
37
  * Matrix: a class for Matrices (inverse, det, reduced echelon form, etc.)
35
38
  * Poly: a class for polynomials (division, modulo)
36
39
  * Zmod: a class for the ring of integers modulo an integer
37
40
 
38
41
  Documentation can be found in the jupyter notebook
39
- (currently incomplete: use the force, read the source).
42
+ (mostly done, but might not contain everything: use the force, read the source).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "0.8"
3
+ version = "0.9.1"
4
4
  authors = [
5
5
  { name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
6
6
  ]
File without changes
File without changes
File without changes
File without changes
File without changes