kryptools 1.5__tar.gz → 1.5.2__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 (58) hide show
  1. {kryptools-1.5 → kryptools-1.5.2}/PKG-INFO +7 -1
  2. {kryptools-1.5 → kryptools-1.5.2}/README.md +7 -1
  3. {kryptools-1.5 → kryptools-1.5.2}/kryptools/GF2.py +14 -10
  4. {kryptools-1.5 → kryptools-1.5.2}/kryptools/Zmod.py +2 -1
  5. {kryptools-1.5 → kryptools-1.5.2}/kryptools/__init__.py +1 -1
  6. {kryptools-1.5 → kryptools-1.5.2}/kryptools/aes.py +4 -4
  7. {kryptools-1.5 → kryptools-1.5.2}/kryptools/blockcipher.py +5 -5
  8. {kryptools-1.5 → kryptools-1.5.2}/kryptools/code.py +25 -26
  9. {kryptools-1.5 → kryptools-1.5.2}/kryptools/des.py +1 -1
  10. {kryptools-1.5 → kryptools-1.5.2}/kryptools/dlp_ic.py +4 -4
  11. {kryptools-1.5 → kryptools-1.5.2}/kryptools/dlp_qs.py +4 -4
  12. {kryptools-1.5 → kryptools-1.5.2}/kryptools/dlp_rho.py +1 -1
  13. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor.py +27 -24
  14. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor_qs.py +1 -1
  15. {kryptools-1.5 → kryptools-1.5.2}/kryptools/keccak.py +27 -6
  16. {kryptools-1.5 → kryptools-1.5.2}/kryptools/la.py +5 -5
  17. {kryptools-1.5 → kryptools-1.5.2}/kryptools/nt.py +11 -13
  18. {kryptools-1.5 → kryptools-1.5.2}/kryptools/poly.py +89 -73
  19. {kryptools-1.5 → kryptools-1.5.2}/kryptools/sha1.py +1 -2
  20. {kryptools-1.5 → kryptools-1.5.2}/kryptools.egg-info/PKG-INFO +7 -1
  21. {kryptools-1.5 → kryptools-1.5.2}/pyproject.toml +1 -1
  22. {kryptools-1.5 → kryptools-1.5.2}/tests/test_GF2.py +18 -18
  23. {kryptools-1.5 → kryptools-1.5.2}/tests/test_Zmod.py +7 -8
  24. {kryptools-1.5 → kryptools-1.5.2}/tests/test_aes.py +2 -1
  25. {kryptools-1.5 → kryptools-1.5.2}/tests/test_code.py +3 -2
  26. {kryptools-1.5 → kryptools-1.5.2}/tests/test_des.py +2 -1
  27. {kryptools-1.5 → kryptools-1.5.2}/tests/test_dlog.py +3 -2
  28. {kryptools-1.5 → kryptools-1.5.2}/tests/test_ec.py +4 -3
  29. {kryptools-1.5 → kryptools-1.5.2}/tests/test_factor.py +3 -2
  30. {kryptools-1.5 → kryptools-1.5.2}/tests/test_factor_fmt.py +3 -2
  31. {kryptools-1.5 → kryptools-1.5.2}/tests/test_factor_pm1.py +2 -1
  32. {kryptools-1.5 → kryptools-1.5.2}/tests/test_factor_qs.py +2 -1
  33. {kryptools-1.5 → kryptools-1.5.2}/tests/test_intfuncs.py +7 -6
  34. {kryptools-1.5 → kryptools-1.5.2}/tests/test_keccak.py +19 -18
  35. {kryptools-1.5 → kryptools-1.5.2}/tests/test_la.py +4 -3
  36. {kryptools-1.5 → kryptools-1.5.2}/tests/test_lat.py +3 -2
  37. {kryptools-1.5 → kryptools-1.5.2}/tests/test_nt.py +8 -7
  38. kryptools-1.5.2/tests/test_poly.py +88 -0
  39. {kryptools-1.5 → kryptools-1.5.2}/tests/test_primes.py +5 -4
  40. {kryptools-1.5 → kryptools-1.5.2}/tests/test_sha1.py +2 -1
  41. kryptools-1.5/tests/test_poly.py +0 -63
  42. {kryptools-1.5 → kryptools-1.5.2}/LICENSE +0 -0
  43. {kryptools-1.5 → kryptools-1.5.2}/kryptools/conway_polynomials.py +0 -0
  44. {kryptools-1.5 → kryptools-1.5.2}/kryptools/dlp.py +0 -0
  45. {kryptools-1.5 → kryptools-1.5.2}/kryptools/dlp_bsgs.py +0 -0
  46. {kryptools-1.5 → kryptools-1.5.2}/kryptools/ec.py +0 -0
  47. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor_dix.py +0 -0
  48. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor_ecm.py +0 -0
  49. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor_fmt.py +0 -0
  50. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor_pm1.py +0 -0
  51. {kryptools-1.5 → kryptools-1.5.2}/kryptools/factor_rho.py +0 -0
  52. {kryptools-1.5 → kryptools-1.5.2}/kryptools/intfuncs.py +0 -0
  53. {kryptools-1.5 → kryptools-1.5.2}/kryptools/lat.py +0 -0
  54. {kryptools-1.5 → kryptools-1.5.2}/kryptools/primes.py +0 -0
  55. {kryptools-1.5 → kryptools-1.5.2}/kryptools.egg-info/SOURCES.txt +0 -0
  56. {kryptools-1.5 → kryptools-1.5.2}/kryptools.egg-info/dependency_links.txt +0 -0
  57. {kryptools-1.5 → kryptools-1.5.2}/kryptools.egg-info/top_level.txt +0 -0
  58. {kryptools-1.5 → kryptools-1.5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.5
3
+ Version: 1.5.2
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  License: MIT
@@ -16,6 +16,12 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Dynamic: license-file
18
18
 
19
+ [![Pypi](https://img.shields.io/pypi/v/kryptools)](https://pypi.org/project/kryptools)
20
+ [![PyVersion](https://img.shields.io/pypi/pyversions/kryptools)](https://www.python.org)
21
+ [![License](https://img.shields.io/pypi/l/kryptools)](https://github.com/teschlg/kryptools/blob/main/LICENSE)
22
+ [![Pylint](https://github.com/teschlg/kryptools/actions/workflows/pylint.yml/badge.svg)](https://github.com/teschlg/kryptools/actions/workflows/pylint.yml)
23
+ [![Pytest](https://github.com/teschlg/kryptools/actions/workflows/run_test.yml/badge.svg)](https://github.com/teschlg/kryptools/actions/workflows/run_test.yml)
24
+
19
25
  # kryptools
20
26
  Gerald Teschl <Gerald.Teschl@univie.ac.at>
21
27
 
@@ -1,3 +1,9 @@
1
+ [![Pypi](https://img.shields.io/pypi/v/kryptools)](https://pypi.org/project/kryptools)
2
+ [![PyVersion](https://img.shields.io/pypi/pyversions/kryptools)](https://www.python.org)
3
+ [![License](https://img.shields.io/pypi/l/kryptools)](https://github.com/teschlg/kryptools/blob/main/LICENSE)
4
+ [![Pylint](https://github.com/teschlg/kryptools/actions/workflows/pylint.yml/badge.svg)](https://github.com/teschlg/kryptools/actions/workflows/pylint.yml)
5
+ [![Pytest](https://github.com/teschlg/kryptools/actions/workflows/run_test.yml/badge.svg)](https://github.com/teschlg/kryptools/actions/workflows/run_test.yml)
6
+
1
7
  # kryptools
2
8
  Gerald Teschl <Gerald.Teschl@univie.ac.at>
3
9
 
@@ -39,4 +45,4 @@ The tools contained are:
39
45
  * Keccak: individual sponge operations, SHA3, SHAKE
40
46
 
41
47
  Documentation can be found in the jupyter notebook
42
- (mostly done, but might not contain everything: use the force, read the source).
48
+ (mostly done, but might not contain everything: use the force, read the source).
@@ -135,7 +135,7 @@ class GF2:
135
135
  class GF2nPoint:
136
136
  "Represents a point in the Galois field GF(2^n)."
137
137
 
138
- def __init__(self, x: int, field: "GF2n"):
138
+ def __init__(self, x: int, field: "GF2"):
139
139
  if isinstance(x, bytes | bytearray):
140
140
  x = int.from_bytes(x, byteorder=field.byteorder)
141
141
  self.x = int(x) % field.order
@@ -261,6 +261,8 @@ class GF2nPoint:
261
261
  return NotImplemented
262
262
  if not (self.x) and j < 0:
263
263
  raise ValueError("Division by zero.")
264
+ if self.field.order == 2:
265
+ return self
264
266
  if self.field.bitreversed:
265
267
  res = self.field(self.field.order >> 1)
266
268
  else:
@@ -276,25 +278,27 @@ class GF2nPoint:
276
278
  x *= x
277
279
  return res
278
280
 
279
- def __lshift__(self, i: int) -> "GF2nPoint":
281
+ def __lshift__(self, j: int) -> "GF2nPoint":
280
282
  "Cyclic rotation to the left."
281
- x = self.x << i
283
+ x = self.x << j
282
284
  x = (x % self.field.order) + (x // self.field.order)
283
285
  return self.field(x)
284
286
 
285
- def __rshift__(self, i: int) -> "GF2nPoint":
287
+ def __rshift__(self, j: int) -> "GF2nPoint":
286
288
  "Cyclic rotation to the right."
287
- x = self.x >> i
288
- x += (self.field.order >> i) * (self.x % 2**i)
289
+ x = self.x >> j
290
+ x += (self.field.order >> j) * (self.x % 2**j)
289
291
  return self.field(x)
290
292
 
291
293
  def sqrt(self) -> "GF2nPoint":
292
294
  "Compute the square root."
295
+ if self.field.order == 2:
296
+ return self
293
297
  return self**(self.field.order//2)
294
298
 
295
299
  def order(self) -> int:
296
300
  "Compute the order of the point in the group GF(2^n)^*."
297
- self.field._factors()
301
+ self.field._factors() # pylint: disable=W0212
298
302
  order = self.field.mult_order # our current guess
299
303
  one = self.field.one()
300
304
  for p, k in self.field.factors.items():
@@ -316,7 +320,7 @@ class GF2nPoint:
316
320
  if inv:
317
321
  return aes_sbox_inv[self.x]
318
322
  return aes_sbox[self.x]
319
- elif self.field.degree == 4 and self.field.modulus == 0b10011: # MiniAES
323
+ if self.field.degree == 4 and self.field.modulus == 0b10011: # MiniAES
320
324
  if inv:
321
325
  return miniaes_sbox_inv[self.x]
322
326
  return miniaes_sbox[self.x]
@@ -349,7 +353,7 @@ for i in range(256):
349
353
  aes_sbox = list(map(GF2_aes, aes_sbox))
350
354
  aes_sbox_inv = list(map(GF2_aes, aes_sbox_inv))
351
355
 
352
-
356
+
353
357
  # sbox for Mini-AES
354
358
  GF2_miniaes = GF2(4, 0b10011) # x^4 + x + 1 = Mini-AES
355
359
 
@@ -365,4 +369,4 @@ miniaes_sbox_inv = list(map(GF2_miniaes, miniaes_sbox_inv))
365
369
 
366
370
  #Ghash
367
371
 
368
- GF2_ghash = GF2(128, 0b11100001 << 120) # 1 + x + x^2 + x^7 (+ x^128) = Ghash
372
+ GF2_ghash = GF2(128, 0b11100001 << 120) # 1 + x + x^2 + x^7 (+ x^128) = Ghash
@@ -267,6 +267,7 @@ class ZmodPoint:
267
267
  return order
268
268
 
269
269
  def sqrt(self) -> "ZmodPoint":
270
+ "Compute a square root of the point."
270
271
  if not self.ring.n_factors:
271
272
  self.ring.n_factors = factorint(self.ring.n)
272
273
  a = int(self)
@@ -297,7 +298,7 @@ class ZmodPoint:
297
298
  raise ValueError("No quadratic residue!")
298
299
  m = 4 # start at 2^2
299
300
  x = ak % m # this is the root mod m
300
- for i in range(3, k + 1):
301
+ for _ in range(3, k + 1):
301
302
  # test all 4 poosible roots
302
303
  if (x**2 - ak) % (m * 2):
303
304
  x = m - x
@@ -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.5"
8
+ __version__ = "1.5.1"
9
9
  __email__ = "Gerald.Teschl@univie.ac.at"
10
10
 
11
11
  from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, is_carmichael_number, order, crt
@@ -144,18 +144,18 @@ class AESKeySchedule:
144
144
  #RotWord
145
145
  tmp = list(self.key[:,3])
146
146
  if self.showtmp:
147
- print("Start :" + str(tmp))
147
+ print("Start :" + str(tmp))
148
148
  tmp = rotate_left(tmp, 1)
149
149
  if self.showtmp:
150
- print("RotWord :" + str(tmp))
150
+ print("RotWord :" + str(tmp))
151
151
  #SubWord
152
152
  tmp = list(map(lambda x: x.sbox(), tmp))
153
153
  if self.showtmp:
154
- print("SubWord :" + str(tmp))
154
+ print("SubWord :" + str(tmp))
155
155
  #AddRound constant
156
156
  tmp[0] += GF2_aes(2)**(self.round - 1)
157
157
  if self.showtmp:
158
- print("AddConst :" + str(tmp))
158
+ print("AddConst :" + str(tmp))
159
159
  tmp = Matrix(tmp)
160
160
  self.key[:,0] += tmp
161
161
  self.key[:,1] += self.key[:,0]
@@ -50,9 +50,9 @@ class BlockCipher:
50
50
  "Encrypt using the given mode."
51
51
  if mode is None:
52
52
  mode = self.mode
53
- try:
53
+ if hasattr(self, "encrypt_" + mode.lower()):
54
54
  method = getattr(self, "encrypt_" + mode.lower())
55
- except:
55
+ else:
56
56
  raise ValueError("Unsupported encryption mode: ", mode)
57
57
  if key:
58
58
  self.set_key(key)
@@ -62,9 +62,9 @@ class BlockCipher:
62
62
  "Decrypt using the given mode."
63
63
  if mode is None:
64
64
  mode = self.mode
65
- try:
65
+ if hasattr(self, "decrypt_" + mode):
66
66
  method = getattr(self, "decrypt_" + mode)
67
- except:
67
+ else:
68
68
  raise ValueError("Unsupported encryption mode: ", mode)
69
69
  if key:
70
70
  self.set_key(key)
@@ -218,7 +218,7 @@ class BlockCipher:
218
218
  key_extra ^= R_b
219
219
 
220
220
  pad = len(text) % self.__class__.blocksize
221
- if pad or not len(text):
221
+ if pad or not text:
222
222
  # Compute key K2
223
223
  text += b'\x80' + b'\x00' * (self.__class__.blocksize - pad - 1)
224
224
  msb = key_extra >> (self.__class__.blocksize * 8 - 1)
@@ -85,7 +85,7 @@ class Goppa():
85
85
 
86
86
  def __init__(self, gf: GF2, g: Poly, alpha: list["GF2Point"]):
87
87
  if not all(map(lambda x: x in gf, alpha)):
88
- raise ValueError(f"{x}: alpha must be a list of elements from the Galois field!")
88
+ raise ValueError("All elements of alpha must be from the Galois field!")
89
89
  if not g.rabin_test():
90
90
  raise ValueError("The polynomial g must be irreducible!")
91
91
  self.gf = gf # underlying Galois field
@@ -166,16 +166,14 @@ class CyclicCode():
166
166
  if n < 2:
167
167
  raise ValueError(f"Code lenght {n} must be at least 2!")
168
168
  zero = 0 * g[0]
169
- try:
169
+ if hasattr(zero, "ring"):
170
170
  order = zero.ring.n # Zmod
171
- except:
172
- try:
173
- order = zero.field.order # GF2
174
- except:
175
- try:
176
- order = type(zero).order # galois
177
- except:
178
- raise ValueError("Unknown base filed!")
171
+ elif hasattr(zero, "field"):
172
+ order = zero.field.order # GF2
173
+ elif hasattr(type(zero), "order"):
174
+ order = type(zero).order # galois
175
+ else:
176
+ raise ValueError("Unknown base field!")
179
177
  self.order = order # order of the base field
180
178
  one = zero**0
181
179
  modulus = Poly([-one] + [zero] * (n-1) + [one])
@@ -189,7 +187,7 @@ class CyclicCode():
189
187
 
190
188
  def __repr__(self):
191
189
  return f"Cyclic [{self.n}, {self.k}] code over GF({self.order}) with generator polynomial g(x) = {self.g}."
192
-
190
+
193
191
  def encode(self, x: list|Poly, gf = None, systematic_form = False):
194
192
  "Encode a given list or polynomial."
195
193
  as_list = False
@@ -219,7 +217,7 @@ class CyclicCode():
219
217
  raise ValueError(f"Degree can be at most {self.n-1}.")
220
218
  x, r = y.divmod(self.g)
221
219
  if r:
222
- raise NotImplementedError(f"Word is no code word! Cannot decode.")
220
+ raise NotImplementedError("Word is no code word! Cannot decode.")
223
221
  if systematic_form:
224
222
  x.coeff = y.coeff[self.g.degree():]
225
223
  if as_list:
@@ -256,24 +254,24 @@ class ReedSolomonCode(CyclicCode):
256
254
  [0, 1, 0]
257
255
 
258
256
  """
259
- def __init__(self, k:int, gf, alpha = None):
257
+ def __init__(self, k:int, gf, alpha = None): # pylint: disable=W0231
260
258
  zero = gf(0)
261
259
  one = zero**0
262
- self. gf = gf
263
- try:
264
- n = gf.order()
265
- except:
266
- n = gf.order - 1
260
+ self.gf = gf
261
+ if hasattr(gf, "n"):
262
+ n = gf.n - 1 # Zmod
263
+ else:
264
+ n = gf.order - 1 # GF2|galois
267
265
  self.order = n + 1 # order of the base field
268
266
  self.n = n # lenght
269
267
  self.k = k # dimension
270
268
  self.d = n - k - 1 # minimal distance
271
269
  self.modulus = Poly([-one] + [zero] * (n-1) + [one])
272
270
  if alpha is None:
273
- try:
274
- alpha = gf.generator() # Zmod|GF2
275
- except:
271
+ if hasattr(gf, "primitive_element"):
276
272
  alpha = gf.primitive_element # galois
273
+ else:
274
+ alpha = gf.generator() # Zmod|GF2
277
275
  self.alpha = alpha
278
276
  self.g = prod([Poly([-alpha**j, one]) for j in range(1,n-k+1)], start = one) # generating polynomial
279
277
  self.h = Poly(self.modulus) // self.g # check polynomial
@@ -283,7 +281,7 @@ class ReedSolomonCode(CyclicCode):
283
281
  def __repr__(self):
284
282
  return f"Reed-Solomon-Code [{self.n}, {self.k}, {self.d}] over GF({self.n+1})."
285
283
 
286
- def encode(self, x: list|Poly):
284
+ def encode(self, x: list|Poly): # pylint: disable=W0221
287
285
  "Encode a given list or polynomial."
288
286
  as_list = False
289
287
  zero = 0 * self.g[0]
@@ -297,7 +295,7 @@ class ReedSolomonCode(CyclicCode):
297
295
  return y + [zero] * (self.n - len(y))
298
296
  return Poly(y)
299
297
 
300
- def decode(self, y: list|Poly):
298
+ def decode(self, y: list|Poly): # pylint: disable=W0221
301
299
  "Decode a given list or polynomial."
302
300
  as_list = False
303
301
  zero = 0 * self.g[0]
@@ -307,11 +305,12 @@ class ReedSolomonCode(CyclicCode):
307
305
  y = Poly(y, ring = self.gf)
308
306
  if y.degree() >= self.n:
309
307
  raise ValueError(f"Degree can be at most {self.n-1}.")
310
- x = Poly([ -y(self.alpha**(-j)) for j in range(self.n) ])
308
+ xx = [ -y(self.alpha**(-j)) for j in range(self.n) ]
309
+ x = Poly(xx)
311
310
  if x.degree() >= self.k: # No codewort
312
311
  t = (self.n - self.k) // 2
313
- A = Matrix([[ x[j - l % self.n] for l in range(1, t+1) ] for j in range(self.k+t, self.k+2*t) ])
314
- b = [ -x[self.k + t + l ] for l in range(t) ]
312
+ A = Matrix([[ xx[j - l % self.n] for l in range(1, t+1) ] for j in range(self.k+t, self.k+2*t) ])
313
+ b = [ -xx[self.k + t + l ] for l in range(t) ]
315
314
  lam = Poly([one] + list(A.solve(b)))
316
315
  x_coordinates = []
317
316
  y_coordinates = []
@@ -171,7 +171,7 @@ class FeistelCipher:
171
171
  def printtmp(title: str, a: list) -> None:
172
172
  print(f'{title}: {"".join(map(str,a))}')
173
173
  else:
174
- def printtmp(title: str, a: list) -> None:
174
+ def printtmp(title: str, a: list) -> None: # pylint: disable=W0613
175
175
  pass
176
176
  a = permute(a, self.__class__.E_box) # expansion box
177
177
  printtmp("EBox", a)
@@ -37,7 +37,7 @@ def dlog_ic(a: int, b: int, n: int, m: int, pollard: bool = True, verbose: int =
37
37
  break
38
38
  trys += 1
39
39
  else:
40
- raise Exception(f"Sorry, Index Calculus failed to find a relation after {trys} trys.")
40
+ raise RuntimeWarning(f"Sorry, Index Calculus failed to find a relation after {trys} trys.")
41
41
  if verbose > 2:
42
42
  if include_b:
43
43
  print(f"rel found: b*a^{x}=", b * pow(a, x, n) % n, relation)
@@ -101,7 +101,7 @@ def dlog_ic(a: int, b: int, n: int, m: int, pollard: bool = True, verbose: int =
101
101
  x = (m - relations[index][len_relations]) % m
102
102
  if pow(a, x, n) == b:
103
103
  return x
104
- raise Exception("Sorry, Index Calculus failed! Either the DLP is not solvable, or the order is not prime or incorrect.")
104
+ raise RuntimeWarning("Sorry, Index Calculus failed! Either the DLP is not solvable, or the order is not prime or incorrect.")
105
105
 
106
106
  #
107
107
  # Determine the parameters
@@ -111,7 +111,7 @@ def dlog_ic(a: int, b: int, n: int, m: int, pollard: bool = True, verbose: int =
111
111
  factorbase = tuple(p for p in sieve_eratosthenes(B) if gcd(p,n) == 1) # compute the factorbse
112
112
  factorbase_len = len(factorbase) # length of the factorbase
113
113
  if factorbase_len == 0:
114
- raise Exception("Sorry, Index Calculus could not find a factorbase!")
114
+ raise RuntimeWarning("Sorry, Index Calculus could not find a factorbase!")
115
115
  smallprimes_len, pollard_k = factorbase_len, None
116
116
  if pollard: # should we speed up trial division with Pollard p-1
117
117
  smallprimes_len, pollard_k = determine_trialdivison_bounds(B // 150, factorbase)
@@ -137,4 +137,4 @@ def dlog_ic(a: int, b: int, n: int, m: int, pollard: bool = True, verbose: int =
137
137
  if res:
138
138
  return res
139
139
 
140
- raise Exception("Sorry, Index Calculus could not find enough relations! ({n_relations} - {n_relations_redundant} = {n_relations - n_relations_redundant} out of {len_relations})")
140
+ raise RuntimeWarning("Sorry, Index Calculus could not find enough relations! ({n_relations} - {n_relations_redundant} = {n_relations - n_relations_redundant} out of {len_relations})")
@@ -97,7 +97,7 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
97
97
  break
98
98
  trys += 1
99
99
  else:
100
- raise Exception(f"Sorry, Quadratic Sieve failed to find a new relation after {trys} trys.")
100
+ raise RuntimeWarning(f"Sorry, Quadratic Sieve failed to find a new relation after {trys} trys.")
101
101
  if verbose > 2:
102
102
  if include_b:
103
103
  print(f"rel found: b*a^{x}=", b * pow(a, x, n) % n, relation)
@@ -160,7 +160,7 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
160
160
  x = (m - relations[index][len_relations]) % m
161
161
  if pow(a, x, n) == b:
162
162
  return x
163
- raise Exception("Sorry, Quadratic Sieve failed! Either the DLP is not solvable or the order is not prime or incorrect.")
163
+ raise RuntimeWarning("Sorry, Quadratic Sieve failed! Either the DLP is not solvable or the order is not prime or incorrect.")
164
164
 
165
165
  #
166
166
  # Determine the parameters
@@ -173,7 +173,7 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
173
173
  factorbase_log = [log(p) for p in factorbase] # we add these up to test if a number will probably factor
174
174
  factorbase_len = len(factorbase) # length of the factorbase
175
175
  if factorbase_len == 0:
176
- raise Exception("Sorry, Index Calculus could not find a factorbase!")
176
+ raise RuntimeWarning("Sorry, Index Calculus could not find a factorbase!")
177
177
  smallprimes_len, pollard_k = factorbase_len, None
178
178
  if pollard: # should we speed up trial division with Pollard p-1
179
179
  smallprimes_len, pollard_k = determine_trialdivison_bounds(B // 150, factorbase)
@@ -300,4 +300,4 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
300
300
  if res:
301
301
  return res
302
302
 
303
- 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}")
303
+ raise RuntimeWarning(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}")
@@ -59,4 +59,4 @@ def dlog_rho(a: int, b: int, n: int, m: int = None, brent=0) -> int:
59
59
  l += mm
60
60
  return l
61
61
  trys -= 1
62
- raise Exception("Sorry, pollard rho failed!")
62
+ raise RuntimeWarning("Sorry, pollard rho failed!")
@@ -55,21 +55,22 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
55
55
  prime_factors[-1] = 1
56
56
 
57
57
  def add_factors(m: int, mm: tuple) -> None:
58
+ "Delete m from remaining factors and add its factors mm to the appropriate dict."
58
59
  k = remaining_factors[m]
59
60
  del remaining_factors[m]
60
61
 
61
- for m in mm:
62
- if m in prime_factors:
63
- prime_factors[m] += k
64
- elif is_prime(m, trialdivision=False):
65
- prime_factors[m] = k
62
+ for m_i in mm:
63
+ if m_i in prime_factors:
64
+ prime_factors[m_i] += k
65
+ elif is_prime(m_i, trialdivision=False):
66
+ prime_factors[m_i] = k
66
67
  else:
67
- if m in remaining_factors:
68
- remaining_factors[m] += k
69
- elif m in new_factors:
70
- new_factors[m] += k
68
+ if m_i in remaining_factors:
69
+ remaining_factors[m_i] += k
70
+ elif m_i in new_factors:
71
+ new_factors[m_i] += k
71
72
  else:
72
- new_factors[m] = k
73
+ new_factors[m_i] = k
73
74
 
74
75
  if verbose:
75
76
  print(f"Factoring: {n} ({floor(log10(n)) + 1} digits)")
@@ -92,7 +93,7 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
92
93
  if res:
93
94
  n, k = res
94
95
  else:
95
- n, k = n, 1
96
+ k = 1
96
97
  if is_prime(n, trialdivision=False):
97
98
  prime_factors[n] = k
98
99
  return prime_factors
@@ -114,7 +115,7 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
114
115
  ]
115
116
  fermat_steps = 10
116
117
 
117
- for round, parameters in enumerate(ECM_PARAMETERS):
118
+ for nr, parameters in enumerate(ECM_PARAMETERS):
118
119
  fermat_steps += 10
119
120
  B1, B2, num_curves = parameters
120
121
  p_max = isqrt(max(remaining_factors))+1
@@ -126,7 +127,7 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
126
127
  D = isqrt(B2)
127
128
 
128
129
  if verbose:
129
- print(f"Round {round+1} (B1={B1})")
130
+ print(f"Round {nr+1} (B1={B1})")
130
131
 
131
132
  primes = sieve_eratosthenes(B1 - 1 + ((B2 - B1 + 1) // (2 * D) + 1) * 2 * D)
132
133
  pm1_parameters = tuple([10 * B1, B2] + list(_pm1_parameters(10 * B1, B2, primes=primes)))
@@ -135,14 +136,14 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
135
136
  methods = {_factor_fermat: "fmt", factor_pm1: "pm1", factor_ecm: "ecm"} #, factor_qs: "qs"}
136
137
  while remaining_factors:
137
138
  new_factors = {}
138
- for method in methods:
139
- factors = list(remaining_factors)
139
+ for method, method_name in methods.items():
140
+ factors = list(remaining_factors) # make a copy to avoid a runtime error if the dict changes
140
141
  for m in factors:
141
- if method == _factor_fermat:
142
+ if method == _factor_fermat: # pylint: disable=W0143
142
143
  tmp = _factor_fermat(m, maxsteps=fermat_steps, verbose=max(0, verbose - 1))
143
- elif method == factor_pm1:
144
+ elif method == factor_pm1: # pylint: disable=W0143
144
145
  tmp = factor_pm1(m, pm1_parameters=pm1_parameters, verbose=max(0, verbose - 1))
145
- elif method == factor_ecm:
146
+ elif method == factor_ecm: # pylint: disable=W0143
146
147
  tmp = factor_ecm(m, ecm_parameters=ecm_parameters, verbose=max(0, verbose - 1))
147
148
  else:
148
149
  tmp = method(m, verbose=max(0, verbose - 1))
@@ -160,16 +161,18 @@ def factorint(n: int, trial_bnd: int = 2500, verbose: int = 0) -> dict:
160
161
  tmp3.append(x)
161
162
  else:
162
163
  tmp3 = [tmp, tmp2]
163
- if verbose > 0: print(f"Factors found ({methods[method]}): ", tmp3)
164
+ if verbose > 0:
165
+ print(f"Factors found ({method_name}): ", tmp3)
164
166
  add_factors(m, tmp3)
165
167
  if not new_factors:
166
168
  break
167
- for m in new_factors:
169
+ for m, k in new_factors.items():
168
170
  if m in remaining_factors:
169
- remaining_factors[m] += new_factors[m]
171
+ remaining_factors[m] += k
170
172
  else:
171
- remaining_factors[m] = new_factors[m]
172
- if verbose > 1: print("Remaining: ", remaining_factors)
173
+ remaining_factors[m] = k
174
+ if verbose > 1:
175
+ print("Remaining: ", remaining_factors)
173
176
 
174
177
  if len(remaining_factors) == 0:
175
178
  return prime_factors
@@ -190,7 +193,7 @@ def divisors(n:int, proper:bool = False) -> int:
190
193
  if n <= 1:
191
194
  return divisor_list
192
195
  facctordict= factorint(n)
193
- primes = [p for p in facctordict]
196
+ primes = list(facctordict)
194
197
  nprimes = len(primes)
195
198
  multiplicites = [facctordict[p] for p in primes]
196
199
 
@@ -9,7 +9,7 @@ from .nt import legendre_symbol, sqrt_mod
9
9
 
10
10
  def bytexor(a: bytearray, b: bytes) -> bytes:
11
11
  """Xor the bytestring `b` to the bytearry `a`."""
12
- for i in range(len(a)):
12
+ for i in range(len(a)): # pylint: disable=C0200
13
13
  a[i] ^= b[i]
14
14
 
15
15
 
@@ -160,16 +160,21 @@ class SHA3():
160
160
 
161
161
  def __init__(self, n: int = 256):
162
162
  if n not in (224, 256, 384, 512):
163
- raise ValueError(f"n must be one of: 224, 256, 384, 512")
164
- self.keccak = Keccak(rate=1600-2*n)
163
+ raise ValueError("n must be one of: 224, 256, 384, 512")
164
+ self.keccak = Keccak(rate = 1600 - 2*n)
165
165
  self.keccak.pad = [b'\x86', b'\x06', b'\x80']
166
- self.hash_length = n // 8 # in bytes
166
+ self.digest_size = n // 8 # in bytes
167
+ self.block_size = (1600 - 2*n) // 8 # in bytes
168
+ self.name = f"SHA3-{n}"
167
169
 
168
170
  def __call__(self, msg: bytes | str) -> bytes:
169
171
  "Compute the hash of the message given or absorbed."
170
172
  self.keccak.reset()
171
173
  self.keccak.absorb(msg)
172
- return self.keccak.squeeze(self.hash_length)
174
+ return self.keccak.squeeze(self.digest_size)
175
+
176
+ def __repr__(self):
177
+ return self.name
173
178
 
174
179
  def update(self, msg: bytes | str) -> None:
175
180
  "Absorb the given message into the sponge."
@@ -179,10 +184,14 @@ class SHA3():
179
184
  "Compute the hash of the message given or absorbed."
180
185
  if msg is not None:
181
186
  self.keccak.absorb(msg)
182
- digest = self.keccak.squeeze(self.hash_length)
187
+ digest = self.keccak.squeeze(self.digest_size)
183
188
  self.keccak.reset()
184
189
  return digest
185
190
 
191
+ def hexdigest(self, msg: bytes | str | None = None) -> bytes:
192
+ "Compute the hash of the message given or absorbed. Return as hex value."
193
+ return self.digest(msg).hex()
194
+
186
195
 
187
196
  class SHAKE():
188
197
  """
@@ -197,11 +206,23 @@ class SHAKE():
197
206
 
198
207
  def __init__(self, msg: bytes | str, n: int = 128) -> None:
199
208
  if n not in (128, 256):
200
- raise ValueError(f"n must be one of: 128, 256")
209
+ raise ValueError("n must be one of: 128, 256")
201
210
  self.keccak = Keccak(rate=1600-2*n)
202
211
  self.keccak.pad = [b'\x9f', b'\x1f', b'\x80']
203
212
  self.keccak.absorb(msg)
213
+ self.name = f"SHAKE-{n}"
214
+
215
+ def __repr__(self):
216
+ return self.name
204
217
 
205
218
  def __call__(self, r: int) -> bytes:
206
219
  "Extract `r` bytes."
207
220
  return self.keccak.squeeze(r)
221
+
222
+ def digest(self, r: int) -> bytes:
223
+ "Extract `r` bytes."
224
+ return self(r)
225
+
226
+ def hexdigest(self, r: int) -> bytes:
227
+ "Extract `r` bytes as hex values."
228
+ return self(r).hex()
@@ -50,7 +50,7 @@ class Matrix:
50
50
  out[i] += self.__class__.print_post
51
51
  return '\n'.join(out)
52
52
 
53
- def _repr_mimebundle_(self, **kwargs):
53
+ def _repr_mimebundle_(self, **kwargs): # pylint: disable=W0613
54
54
  return {
55
55
  "text/plain": repr(self),
56
56
  "text/latex": "$\\displaystyle" + self.latex() + "$"
@@ -137,11 +137,11 @@ class Matrix:
137
137
  if len(cols) == self.cols:
138
138
  self.delete_rows(rows)
139
139
  return
140
- elif len(rows) == self.rows:
140
+ if len(rows) == self.rows:
141
141
  self.delete_columns(cols)
142
142
  return
143
143
  raise ValueError("Can only delete entire rows or entire columns!")
144
-
144
+
145
145
  def delete_rows(self, rows: int|list) -> None:
146
146
  "Deletes a row or a list of rows."
147
147
  if isinstance(rows, int):
@@ -153,7 +153,7 @@ class Matrix:
153
153
  for i in reversed(sorted(rows)):
154
154
  del self.matrix[i]
155
155
  self.rows -= len(rows)
156
-
156
+
157
157
  def delete_columns(self, cols: int|list) -> None:
158
158
  "Deletes a column or a list of columns."
159
159
  if isinstance(cols, int):
@@ -166,7 +166,7 @@ class Matrix:
166
166
  for j in reversed(sorted(cols)):
167
167
  del self.matrix[i][j]
168
168
  self.cols -= len(cols)
169
-
169
+
170
170
  def append_row(self, row: list|tuple, ring = None) -> None:
171
171
  "Append a row."
172
172
  if not isinstance(row, list|tuple) or len(row) != self.cols: