kryptools 0.1__py3-none-any.whl
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/Zmod.py +115 -0
- kryptools/__init__.py +13 -0
- kryptools/dlp.py +76 -0
- kryptools/dlp_bsgs.py +29 -0
- kryptools/dlp_ic.py +136 -0
- kryptools/dlp_qs.py +297 -0
- kryptools/dlp_rho.py +62 -0
- kryptools/ec.py +383 -0
- kryptools/factor.py +138 -0
- kryptools/factor_ecm.py +140 -0
- kryptools/factor_pm1.py +58 -0
- kryptools/factor_qs.py +174 -0
- kryptools/la.py +223 -0
- kryptools/lat.py +168 -0
- kryptools/nt.py +271 -0
- kryptools/poly.py +232 -0
- kryptools/primes.py +123 -0
- kryptools-0.1.dist-info/LICENSE +21 -0
- kryptools-0.1.dist-info/METADATA +35 -0
- kryptools-0.1.dist-info/RECORD +22 -0
- kryptools-0.1.dist-info/WHEEL +5 -0
- kryptools-0.1.dist-info/top_level.txt +1 -0
kryptools/dlp_rho.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Discrete log solvers: Pollard rho
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from math import gcd, isqrt
|
|
6
|
+
from random import randint
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def dlog_rho(a: int, b: int, n: int, m: int = None, brent=0) -> int:
|
|
10
|
+
"""Compute the discrete log_a(b) in Z_p of an element a of order m using Pollard's rho algorithm."""
|
|
11
|
+
a %= n
|
|
12
|
+
b %= n
|
|
13
|
+
if not m:
|
|
14
|
+
m = n - 1
|
|
15
|
+
trys = 10
|
|
16
|
+
max_iter = 10 * isqrt(m) # stop iterating and try a different start value
|
|
17
|
+
|
|
18
|
+
def f(x: int, alpha: int, beta: int) -> (int, int, int):
|
|
19
|
+
r = x % 3
|
|
20
|
+
if r == 0:
|
|
21
|
+
return x * a % n, (alpha + 1) % m, beta
|
|
22
|
+
if r == 1:
|
|
23
|
+
return pow(x, 2, n), (2 * alpha) % m, (2 * beta) % m
|
|
24
|
+
return (x * b) % n, alpha, (beta + 1) % m
|
|
25
|
+
|
|
26
|
+
while trys > 0:
|
|
27
|
+
j0 = randint(1, m - 1)
|
|
28
|
+
x, alpha_x, beta_x = (b * pow(a, j0, n)) % n, j0, 1 # x_0
|
|
29
|
+
if brent == 0: # Floyd's cycle detection algorithm
|
|
30
|
+
i = 1
|
|
31
|
+
x, alpha_x, beta_x = f(x, alpha_x, beta_x) # x_1=f(x_0)
|
|
32
|
+
y, alpha_y, beta_y = f(x, alpha_x, beta_x) # y_1=f(f(x_0))
|
|
33
|
+
while x != y and i < max_iter:
|
|
34
|
+
i += 1
|
|
35
|
+
x, alpha_x, beta_x = f(x, alpha_x, beta_x) # f(x_j)
|
|
36
|
+
y, alpha_y, beta_y = f(y, alpha_y, beta_y)
|
|
37
|
+
y, alpha_y, beta_y = f(y, alpha_y, beta_y) # f(f(y_j))
|
|
38
|
+
else: # Brent's cycle detection algorithm
|
|
39
|
+
i = 1 # search for a cycle length k < 2^i
|
|
40
|
+
k = 1 # cycle length
|
|
41
|
+
y, alpha_y, beta_y = f(x, alpha_x, beta_x) # f(x_0)
|
|
42
|
+
while x != y and i < max_iter:
|
|
43
|
+
if i == k: # start a new power of two
|
|
44
|
+
x, alpha_x, beta_x = y, alpha_y, beta_y
|
|
45
|
+
i *= 2
|
|
46
|
+
k = 0
|
|
47
|
+
y, alpha_y, beta_y = f(y, alpha_y, beta_y)
|
|
48
|
+
k += 1
|
|
49
|
+
# we found a collission (or hit max_iter)
|
|
50
|
+
if x == y and (beta_y - beta_x) % m != 0:
|
|
51
|
+
d = gcd(beta_y - beta_x, m)
|
|
52
|
+
mm = m // d
|
|
53
|
+
if (alpha_x - alpha_y) % d != 0:
|
|
54
|
+
return None # no solution
|
|
55
|
+
l = (((alpha_x - alpha_y) // d) * pow((beta_y - beta_x) // d, -1, mm)) % mm
|
|
56
|
+
while pow(a, l, n) != b:
|
|
57
|
+
if l >= m:
|
|
58
|
+
return None
|
|
59
|
+
l += mm
|
|
60
|
+
return l
|
|
61
|
+
trys -= 1
|
|
62
|
+
raise Exception("Sorry, pollard rho failed!")
|
kryptools/ec.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Elliptic curves
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from math import isqrt, floor, sqrt
|
|
6
|
+
from random import randint
|
|
7
|
+
from .factor import factorint
|
|
8
|
+
from .nt import legendre_symbol, sqrt_mod, crt
|
|
9
|
+
from .Zmod import Zmod
|
|
10
|
+
|
|
11
|
+
class EC_Weierstrass():
|
|
12
|
+
"""
|
|
13
|
+
Elliptic curve in Weierstrass normal form y^2 = x^3 + ax + b mod p.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
|
|
17
|
+
To define an elliptic curve use
|
|
18
|
+
>>> ec = EC_Weierstrass(239, 3, 1)
|
|
19
|
+
|
|
20
|
+
To declare a point on the elliptic curve use
|
|
21
|
+
>>> P = ec(43, 222)
|
|
22
|
+
>>> Q = ec(148, 218))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
The usual arithmetic operations are supported.
|
|
26
|
+
>>> P + Q
|
|
27
|
+
(195, 41)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, p: int, a: int, b: int, order: int = None):
|
|
31
|
+
if p < 3:
|
|
32
|
+
raise ValueError(f"{p} must be a prime larger than 2.")
|
|
33
|
+
if (4 * pow(a, 3, p) + 27 * pow(b, 2, p) ) % p == 0:
|
|
34
|
+
raise ValueError(f"Curve is singular!")
|
|
35
|
+
self.p = p
|
|
36
|
+
self.gf = Zmod(p, short = True)
|
|
37
|
+
self.a = self.gf(a % p)
|
|
38
|
+
self.b = self.gf(b % p)
|
|
39
|
+
self.group_order = order
|
|
40
|
+
self.group_order_factors = None
|
|
41
|
+
self.short = False # display points in short format
|
|
42
|
+
self.hex = False # display points as hex values in compressed format
|
|
43
|
+
|
|
44
|
+
def __call__(self, x: int | str | None = None, y: int | None = None, short: bool = False):
|
|
45
|
+
if isinstance(x, str):
|
|
46
|
+
x = x.replace(" ", "")
|
|
47
|
+
type = x[:2]
|
|
48
|
+
x = x[2:]
|
|
49
|
+
if type == '02' or type == '03':
|
|
50
|
+
short = 1
|
|
51
|
+
x = int(x, 16)
|
|
52
|
+
if type == '02':
|
|
53
|
+
y = 0
|
|
54
|
+
else:
|
|
55
|
+
y = 1
|
|
56
|
+
elif type == '04':
|
|
57
|
+
l = len(x)//2
|
|
58
|
+
y = int(x[l:], 16)
|
|
59
|
+
x = int(x[:l], 16)
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError('Unsupported hex type.')
|
|
62
|
+
return ECPoint(x, y, self, short)
|
|
63
|
+
|
|
64
|
+
def __eq__(self, other):
|
|
65
|
+
if isinstance(other, self.__class__):
|
|
66
|
+
return self.p == other.p and self.a == other.a and self.b == other.b
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
def __contains__(self, P: "ECPoint") -> bool:
|
|
70
|
+
return P.y**2 == P.x**3 + self.a * P.x + self.b
|
|
71
|
+
|
|
72
|
+
def info(self):
|
|
73
|
+
format = "Weierstrass curve y^2 = x^3"
|
|
74
|
+
if int(self.a):
|
|
75
|
+
format += f" + {self.a} x"
|
|
76
|
+
if int(self.b):
|
|
77
|
+
format += f" + {self.b}"
|
|
78
|
+
format += f" over Z_{self.p}."
|
|
79
|
+
print(format)
|
|
80
|
+
|
|
81
|
+
def add(self, x1, y1, x2, y2):
|
|
82
|
+
if x1 is None:
|
|
83
|
+
return x2, y2
|
|
84
|
+
if x2 is None:
|
|
85
|
+
return x1, y1
|
|
86
|
+
if x1 == x2:
|
|
87
|
+
if y1 == y2:
|
|
88
|
+
return self.dbl(x1, y1)
|
|
89
|
+
return None, None
|
|
90
|
+
s = (y2 - y1) / (x2 - x1)
|
|
91
|
+
x3 = s**2 - x1 - x2
|
|
92
|
+
y3 = s * (x1 - x3) - y1
|
|
93
|
+
return x3, y3
|
|
94
|
+
|
|
95
|
+
def dbl(self, x, y):
|
|
96
|
+
if x is None or not y:
|
|
97
|
+
return None, None
|
|
98
|
+
s = (3 * x**2 + self.a) / (2 * y)
|
|
99
|
+
x3 = s**2 - x - x
|
|
100
|
+
y3 = s * (x - x3) - y
|
|
101
|
+
return x3, y3
|
|
102
|
+
|
|
103
|
+
def mult(self, j: int, x, y): # Addition-subtraction ladder
|
|
104
|
+
if j == 0:
|
|
105
|
+
return None, None
|
|
106
|
+
if j < 0:
|
|
107
|
+
y = -y
|
|
108
|
+
j *= -1
|
|
109
|
+
# xx, yy = None, None
|
|
110
|
+
# while j > 0:
|
|
111
|
+
# # If j is odd, add x
|
|
112
|
+
# if j & 1:
|
|
113
|
+
# xx, yy = self.add(xx, yy, x, y)
|
|
114
|
+
# # Now double
|
|
115
|
+
# j >>= 1 # j= j//2
|
|
116
|
+
# x, y = self.dbl(x, y)
|
|
117
|
+
xx, yy = x, y
|
|
118
|
+
j3 = 3 * j
|
|
119
|
+
form = "0" + str(j3.bit_length()) + "b"
|
|
120
|
+
for a, b in zip(format(j3, form)[1:-1], format(j, form)[1:-1]):
|
|
121
|
+
xx, yy = self.dbl(xx, yy)
|
|
122
|
+
if a == "1" and b == "0":
|
|
123
|
+
xx, yy = self.add(xx, yy, x, y)
|
|
124
|
+
if a == "0" and b == "1":
|
|
125
|
+
xx, yy = self.add(xx, yy, x, -y)
|
|
126
|
+
return xx, yy
|
|
127
|
+
|
|
128
|
+
def random(self):
|
|
129
|
+
j = -1
|
|
130
|
+
while j == -1:
|
|
131
|
+
x = self.gf(randint(0, self.p - 1))
|
|
132
|
+
y2 = int(x**3 + self.a * x + self.b)
|
|
133
|
+
j = legendre_symbol(y2, self.p)
|
|
134
|
+
return ECPoint(x, randint(0, 1), self, short = True)
|
|
135
|
+
|
|
136
|
+
def order(self, order: int = None) -> int:
|
|
137
|
+
if order:
|
|
138
|
+
self.group_order = order
|
|
139
|
+
elif not self.group_order:
|
|
140
|
+
if self.p < 230:
|
|
141
|
+
self.group_order = self.order_naive()
|
|
142
|
+
else:
|
|
143
|
+
self.group_order = self.order_shanks_mestre()
|
|
144
|
+
return self.group_order
|
|
145
|
+
|
|
146
|
+
def factor_order(self) -> dict:
|
|
147
|
+
if self.group_order_factors:
|
|
148
|
+
return self.group_order_factors
|
|
149
|
+
if not self.group_order:
|
|
150
|
+
self.order()
|
|
151
|
+
self.group_order_factors = factorint(self.group_order)
|
|
152
|
+
return self.group_order_factors
|
|
153
|
+
|
|
154
|
+
def order_naive(self) -> int:
|
|
155
|
+
a1 = (int(self.a) + 1) % self.p
|
|
156
|
+
y = int(self.b)
|
|
157
|
+
order = self.p + 1 + legendre_symbol(y, self.p)
|
|
158
|
+
for x in range(self.p - 1):
|
|
159
|
+
y += (3 * x * (x + 1) + a1) % self.p
|
|
160
|
+
order += legendre_symbol(y, self.p)
|
|
161
|
+
return order
|
|
162
|
+
|
|
163
|
+
def order_shanks_mestre(self) -> int:
|
|
164
|
+
if self.p < 230:
|
|
165
|
+
return self.order_naive()
|
|
166
|
+
j = 1
|
|
167
|
+
while j != -1:
|
|
168
|
+
g = randint(1, self.p - 1)
|
|
169
|
+
j = legendre_symbol(g, self.p)
|
|
170
|
+
W = floor(sqrt(2 * sqrt(self.p)))
|
|
171
|
+
a, b = int(self.a), int(self.b)
|
|
172
|
+
while True:
|
|
173
|
+
sigma = 0
|
|
174
|
+
while sigma == 0:
|
|
175
|
+
x = randint(0, self.p - 1)
|
|
176
|
+
sigma = legendre_symbol(x**3 + a * x + b, self.p)
|
|
177
|
+
if sigma == 1:
|
|
178
|
+
ec = EC_Weierstrass(self.p, a, b)
|
|
179
|
+
else:
|
|
180
|
+
ec = EC_Weierstrass(
|
|
181
|
+
self.p, pow(g, 2, self.p) * a, pow(g, 3, self.p) * b
|
|
182
|
+
)
|
|
183
|
+
x = g * x % self.p
|
|
184
|
+
x = ec.gf(x)
|
|
185
|
+
y2 = int(x**3 + ec.a * x + ec.b)
|
|
186
|
+
y = ec.gf(sqrt_mod(y2, self.p))
|
|
187
|
+
|
|
188
|
+
A = {}
|
|
189
|
+
xx, yy = ec.mult(ec.p + 1, x, y)
|
|
190
|
+
for j in range(W):
|
|
191
|
+
if xx is None:
|
|
192
|
+
A[None] = j
|
|
193
|
+
else:
|
|
194
|
+
A[int(xx)] = j
|
|
195
|
+
xx, yy = ec.add(xx, yy, x, y)
|
|
196
|
+
B = []
|
|
197
|
+
xg, yg = ec.mult(W, x, y)
|
|
198
|
+
xx, yy = None, None
|
|
199
|
+
for j in range(W + 1):
|
|
200
|
+
if xx is None:
|
|
201
|
+
if xx in A:
|
|
202
|
+
B.append([A[None], j])
|
|
203
|
+
elif int(xx) in A:
|
|
204
|
+
B.append([A[int(xx)], j])
|
|
205
|
+
xx, yy = ec.add(xx, yy, xg, yg)
|
|
206
|
+
if len(B) == 1:
|
|
207
|
+
beta, gamma = B[0]
|
|
208
|
+
break
|
|
209
|
+
t = beta + gamma * W
|
|
210
|
+
if ec.mult(ec.p + 1 + t, x, y)[0] is not None:
|
|
211
|
+
t = beta - gamma * W
|
|
212
|
+
assert ec.mult(ec.p + 1 + t, x, y)[0] is None
|
|
213
|
+
|
|
214
|
+
return self.p + 1 + sigma * t
|
|
215
|
+
|
|
216
|
+
class ECPoint:
|
|
217
|
+
"Point on an elliptic curve"
|
|
218
|
+
def __init__(self, x: int, y: int, curve: EC_Weierstrass, short:bool = False):
|
|
219
|
+
self.curve = curve
|
|
220
|
+
if x is None:
|
|
221
|
+
self.x = None
|
|
222
|
+
self.y = None
|
|
223
|
+
else:
|
|
224
|
+
self.x = curve.gf(x)
|
|
225
|
+
if short:
|
|
226
|
+
y2 = int(self.x**3 + curve.a * self.x + curve.b)
|
|
227
|
+
j = legendre_symbol(y2, curve.p)
|
|
228
|
+
if j == -1:
|
|
229
|
+
raise ValueError("Point not on curve!")
|
|
230
|
+
y1 = sqrt_mod(y2, curve.p)
|
|
231
|
+
if y % 2 == 0:
|
|
232
|
+
y = y1
|
|
233
|
+
else:
|
|
234
|
+
y = (curve.p - y1) % curve.p
|
|
235
|
+
self.y = curve.gf(y)
|
|
236
|
+
if not self in curve:
|
|
237
|
+
raise ValueError("Point not on curve!")
|
|
238
|
+
|
|
239
|
+
def __repr__(self):
|
|
240
|
+
if self.x is None:
|
|
241
|
+
return "O"
|
|
242
|
+
if self.curve.hex is True:
|
|
243
|
+
if self.curve.short is True:
|
|
244
|
+
pre = '0x02'
|
|
245
|
+
if int(self.y) % 2 == 1:
|
|
246
|
+
pre = '0x03'
|
|
247
|
+
return pre+f'{int(self.x):x}'
|
|
248
|
+
pre = '0x04'
|
|
249
|
+
return pre+f'{int(self.x):x}{int(self.y):x}'
|
|
250
|
+
if self.curve.short is True:
|
|
251
|
+
return f"({int(self.x)}, {int(self.y) % 2})"
|
|
252
|
+
return f"({int(self.x)}, {int(self.y)})"
|
|
253
|
+
|
|
254
|
+
def __eq__(self, other):
|
|
255
|
+
if not isinstance(other, self.__class__):
|
|
256
|
+
return False
|
|
257
|
+
if not self.curve == other.curve:
|
|
258
|
+
return False
|
|
259
|
+
if self.x is None:
|
|
260
|
+
return other.x is None
|
|
261
|
+
if other.x is None:
|
|
262
|
+
return self.x is None
|
|
263
|
+
return self.x == other.x and self.y == other.y
|
|
264
|
+
|
|
265
|
+
def __bool__(self):
|
|
266
|
+
return self.x is not None
|
|
267
|
+
|
|
268
|
+
def __hash__(self):
|
|
269
|
+
if self.x is None:
|
|
270
|
+
return hash(None)
|
|
271
|
+
return hash((int(self.x), int(self.y)))
|
|
272
|
+
|
|
273
|
+
def __add__(self, other: "ECPoint") -> "ECPoint":
|
|
274
|
+
if not isinstance(other, self.__class__) or self.curve != other.curve:
|
|
275
|
+
raise ValueError(f"Cannot add {self} and {other}.")
|
|
276
|
+
x, y = self.curve.add(self.x, self.y, other.x, other.y)
|
|
277
|
+
return ECPoint(x, y, self.curve)
|
|
278
|
+
|
|
279
|
+
def __sub__(self, other: "ECPoint") -> "ECPoint":
|
|
280
|
+
return self + other.__neg__()
|
|
281
|
+
|
|
282
|
+
def __rmul__(self, scalar: int) -> "ECPoint":
|
|
283
|
+
x, y = self.curve.mult(scalar, self.x, self.y)
|
|
284
|
+
return ECPoint(x, y, self.curve)
|
|
285
|
+
|
|
286
|
+
def __neg__(self) -> "ECPoint":
|
|
287
|
+
if self.x is None or not self.y:
|
|
288
|
+
return self
|
|
289
|
+
return ECPoint(self.x, -self.y, self.curve)
|
|
290
|
+
|
|
291
|
+
def order(self) -> int:
|
|
292
|
+
"""Compute the order of an element."""
|
|
293
|
+
self.curve.factor_order() # Make sure the factorization is available
|
|
294
|
+
order = self.curve.group_order
|
|
295
|
+
for p, k in self.curve.group_order_factors.items():
|
|
296
|
+
for _ in range(k):
|
|
297
|
+
order_try = order // p
|
|
298
|
+
if self.curve.mult(order_try, self.x, self.y)[0] is None:
|
|
299
|
+
order = order_try
|
|
300
|
+
else:
|
|
301
|
+
break
|
|
302
|
+
return order
|
|
303
|
+
|
|
304
|
+
def dlog(Q, P: "ECPoint") -> int:
|
|
305
|
+
"""Compute the discrete log_P(Q) in EC."""
|
|
306
|
+
m = P.order()
|
|
307
|
+
mf = factorint(m)
|
|
308
|
+
assert m * Q == P.curve(None, None), "DLP not solvable."
|
|
309
|
+
# We first use Pohlig-Hellman to split m into powers of prime factors
|
|
310
|
+
mm = []
|
|
311
|
+
ll = []
|
|
312
|
+
for pj, kj in mf.items():
|
|
313
|
+
Pj = (m // pj**kj) * P
|
|
314
|
+
Qj = (m // pj**kj) * Q
|
|
315
|
+
l = Qj.dlog_ph(Pj, pj, kj)
|
|
316
|
+
if l is None:
|
|
317
|
+
return None
|
|
318
|
+
mm += [pj**kj]
|
|
319
|
+
ll += [l]
|
|
320
|
+
return crt(ll, mm)
|
|
321
|
+
|
|
322
|
+
def dlog_ph(Q, P: "ECPoint", q: int, k: int) -> int:
|
|
323
|
+
"""Compute the discrete log_P(Q) in EC if P has order q^k using Pohlig-Hellman reduction."""
|
|
324
|
+
if k == 1 or q**k < 10000:
|
|
325
|
+
return Q.dlog_switch(P, q**k)
|
|
326
|
+
Pj = q**(k - 1) * P
|
|
327
|
+
P1 = Pj
|
|
328
|
+
Qj = q**(k - 1) * Q
|
|
329
|
+
xj = Qj.dlog_switch(P1, q)
|
|
330
|
+
for j in range(2, k + 1):
|
|
331
|
+
Pj = q**(k - j) * P
|
|
332
|
+
Qj = q**(k - j) * Q - xj * Pj
|
|
333
|
+
yj = Qj.dlog_switch(P1, q)
|
|
334
|
+
xj = xj + q ** (j - 1) * yj % q**j
|
|
335
|
+
return xj
|
|
336
|
+
|
|
337
|
+
def dlog_switch(Q, P: "ECPoint", m: int) -> int:
|
|
338
|
+
"""Compute the discrete log_P(Q) in EC if P has order m choosing an appropriate method."""
|
|
339
|
+
if m < 100:
|
|
340
|
+
return Q.dlog_naive(P, m)
|
|
341
|
+
return Q.dlog_bsgs(P, m)
|
|
342
|
+
|
|
343
|
+
def dlog_naive(Q, P: "ECPoint", m: int) -> int:
|
|
344
|
+
"""Compute the discrete log_P(Q) in EC using an exhaustive search."""
|
|
345
|
+
if not Q.curve == P.curve and not isinstance(Q, P.__class__):
|
|
346
|
+
raise ValueError(f"Points must be on the same curve!")
|
|
347
|
+
j = 0
|
|
348
|
+
xx, yy = None, None
|
|
349
|
+
while xx != Q.x:
|
|
350
|
+
j += 1
|
|
351
|
+
xx, yy = P.curve.add(xx, yy, P.x, P.y)
|
|
352
|
+
if xx is None:
|
|
353
|
+
raise ValueError("DLP not solvabel!")
|
|
354
|
+
if yy == Q.y:
|
|
355
|
+
return j
|
|
356
|
+
return m - j
|
|
357
|
+
|
|
358
|
+
def dlog_bsgs(Q, P: "ECPoint", m: int) -> int:
|
|
359
|
+
"""Compute the discrete log_P(Q) in EC if P has order m using Shanks' baby-step-giant-step algorithm."""
|
|
360
|
+
if not Q.curve == P.curve and not isinstance(P, Q.__class__):
|
|
361
|
+
raise ValueError(f"Points must be on the same curve!")
|
|
362
|
+
mm = 1 + isqrt(m - 1)
|
|
363
|
+
m2 = mm//2 + mm % 1 # we use the group symmetry to halve the number of steps
|
|
364
|
+
# initialize baby_steps table
|
|
365
|
+
baby_steps = {}
|
|
366
|
+
baby_step = P
|
|
367
|
+
for j in range(1,m2+1):
|
|
368
|
+
baby_steps[int(baby_step.x)] = j, int(baby_step.y)
|
|
369
|
+
baby_step += P
|
|
370
|
+
|
|
371
|
+
# now take the giant steps
|
|
372
|
+
giant_stride = -mm * P
|
|
373
|
+
giant_step = Q
|
|
374
|
+
for l in range(mm+1):
|
|
375
|
+
if giant_step.x == None:
|
|
376
|
+
return l * mm
|
|
377
|
+
if int(giant_step.x) in baby_steps:
|
|
378
|
+
j = baby_steps[int(giant_step.x)][0]
|
|
379
|
+
if int(giant_step.y) != baby_steps[int(giant_step.x)][1]:
|
|
380
|
+
j *= -1
|
|
381
|
+
return (l * mm + j) % m
|
|
382
|
+
giant_step += giant_stride
|
|
383
|
+
raise ValueError("DLP not solvabel!")
|
kryptools/factor.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Factorization of integers:
|
|
3
|
+
factorint(n) factorize the integer n into prime factors
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from math import isqrt, gcd
|
|
7
|
+
from .primes import sieve_eratosthenes, isprime
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Factoring
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _factor_fermat(n: int, steps: int = 10) -> list:
|
|
15
|
+
a = isqrt(n - 1) + 1
|
|
16
|
+
step =2
|
|
17
|
+
if n % 3 == 2: # if n % 3 = 2, then a must be a multiple of 3
|
|
18
|
+
a += 2 - ((a - 1) % 3)
|
|
19
|
+
step = 3
|
|
20
|
+
elif (n % 4 == 1) ^ (a & 1): # if n % 4 = 1,3 then a must be odd, even, respectively
|
|
21
|
+
a += 1
|
|
22
|
+
for _ in range(steps):
|
|
23
|
+
#if a > (n + 9) // 6:
|
|
24
|
+
# return
|
|
25
|
+
b = isqrt(a * a - n)
|
|
26
|
+
if b * b == a * a - n:
|
|
27
|
+
return a - b
|
|
28
|
+
a += step
|
|
29
|
+
|
|
30
|
+
from .factor_pm1 import _pm1_parameters, factor_pm1
|
|
31
|
+
from .factor_ecm import _ecm_parameters, factor_ecm
|
|
32
|
+
#from .factor_qs import factor_qs
|
|
33
|
+
|
|
34
|
+
def factorint(n: int, verbose: int = 0) -> list:
|
|
35
|
+
"Factor a number."
|
|
36
|
+
prime_factors = {}
|
|
37
|
+
|
|
38
|
+
def add_factors(m: int, mm: tuple) -> None:
|
|
39
|
+
k = remaining_factors[m]
|
|
40
|
+
del remaining_factors[m]
|
|
41
|
+
|
|
42
|
+
for m in mm:
|
|
43
|
+
if m in prime_factors:
|
|
44
|
+
prime_factors[m] += k
|
|
45
|
+
elif isprime(m):
|
|
46
|
+
prime_factors[m] = k
|
|
47
|
+
else:
|
|
48
|
+
if m in remaining_factors:
|
|
49
|
+
remaining_factors[m] += k
|
|
50
|
+
elif m in new_factors:
|
|
51
|
+
new_factors[m] += k
|
|
52
|
+
else:
|
|
53
|
+
new_factors[m] = k
|
|
54
|
+
|
|
55
|
+
# trial division
|
|
56
|
+
B = 2500
|
|
57
|
+
factorbase = sieve_eratosthenes(B)
|
|
58
|
+
for p in factorbase:
|
|
59
|
+
k = 0
|
|
60
|
+
while n % p == 0:
|
|
61
|
+
k += 1
|
|
62
|
+
n //= p
|
|
63
|
+
if k:
|
|
64
|
+
prime_factors[p] = k
|
|
65
|
+
if n == 1:
|
|
66
|
+
return prime_factors
|
|
67
|
+
if verbose:
|
|
68
|
+
print("Trial division found:", list(prime_factors))
|
|
69
|
+
if isprime(n):
|
|
70
|
+
prime_factors[n] = 1
|
|
71
|
+
return prime_factors
|
|
72
|
+
remaining_factors = { n: 1 }
|
|
73
|
+
|
|
74
|
+
# https://gitlab.inria.fr/zimmerma/ecm/
|
|
75
|
+
ECM_PARAMETERS = [
|
|
76
|
+
[ 11000, 1900000, 74],
|
|
77
|
+
[ 50000, 13000000, 221],
|
|
78
|
+
[ 250000, 130000000, 453],
|
|
79
|
+
[ 1000000, 1000000000, 984],
|
|
80
|
+
[ 3000000, 5700000000, 2541],
|
|
81
|
+
[ 11000000, 35000000000, 4949],
|
|
82
|
+
[ 43000000, 240000000000, 8266],
|
|
83
|
+
[110000000, 780000000000, 20158],
|
|
84
|
+
[260000000, 3200000000000, 47173],
|
|
85
|
+
[850000000, 16000000000000, 77666]
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
for parameters in ECM_PARAMETERS:
|
|
89
|
+
B1, B2, num_curves = parameters
|
|
90
|
+
num_curves *= 2
|
|
91
|
+
D = isqrt(B2)
|
|
92
|
+
primes = sieve_eratosthenes(B1 - 1 + ((B2 - B1 + 1) // (2 * D) + 1) * 2 * D)
|
|
93
|
+
pm1_parameters = _pm1_parameters(10 * B1, B2, primes = primes)
|
|
94
|
+
ecm_parameters = tuple([num_curves] + list(_ecm_parameters(B1, B2, D, primes = primes)))
|
|
95
|
+
|
|
96
|
+
methods = {_factor_fermat: "fm", factor_pm1: "pm1", factor_ecm: "ecm"} #, factor_qs: "qs"}
|
|
97
|
+
while remaining_factors:
|
|
98
|
+
new_factors = {}
|
|
99
|
+
for method in [ _factor_fermat, factor_pm1, factor_ecm ]: # , factor_qs ]:
|
|
100
|
+
factors = list(remaining_factors)
|
|
101
|
+
for m in factors:
|
|
102
|
+
if verbose > 1: print("Factoring: ",m, "method", methods[method])
|
|
103
|
+
if method == factor_pm1:
|
|
104
|
+
tmp = factor_pm1(m, pm1_parameters = pm1_parameters)
|
|
105
|
+
elif method == factor_ecm:
|
|
106
|
+
tmp = factor_ecm(m, ecm_parameters = ecm_parameters)
|
|
107
|
+
else:
|
|
108
|
+
tmp = method(m)
|
|
109
|
+
if tmp:
|
|
110
|
+
tmp2 = m // tmp
|
|
111
|
+
g = gcd(tmp, tmp2)
|
|
112
|
+
if g > 1:
|
|
113
|
+
tmp3 = [ g, g ]
|
|
114
|
+
for x in (tmp, tmp2):
|
|
115
|
+
x //= g
|
|
116
|
+
while x % g == 0:
|
|
117
|
+
tmp3.append(g)
|
|
118
|
+
x //= g
|
|
119
|
+
if x > 1:
|
|
120
|
+
tmp3.append(x)
|
|
121
|
+
else:
|
|
122
|
+
tmp3 = [tmp, tmp2]
|
|
123
|
+
if verbose > 0: print("Factors found (", methods[method] ,"): ", tmp3)
|
|
124
|
+
add_factors(m, tmp3)
|
|
125
|
+
if not new_factors:
|
|
126
|
+
break
|
|
127
|
+
for m in new_factors:
|
|
128
|
+
if m in remaining_factors:
|
|
129
|
+
remaining_factors[m] += new_factors[m]
|
|
130
|
+
else:
|
|
131
|
+
remaining_factors[m] = new_factors[m]
|
|
132
|
+
if verbose > 1: print("Remaining: ", remaining_factors)
|
|
133
|
+
|
|
134
|
+
if len(remaining_factors) == 0:
|
|
135
|
+
return prime_factors
|
|
136
|
+
|
|
137
|
+
print("Incomplete factorization!")
|
|
138
|
+
return prime_factors, remaining_factors, new_factors
|