Spaces:
Sleeping
Sleeping
File size: 11,702 Bytes
6a86ad5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
from math import log
from sympy.core.random import _randint
from sympy.external.gmpy import gcd, invert, sqrt
from sympy.utilities.misc import as_int
from .generate import sieve, primerange
from .primetest import isprime
#----------------------------------------------------------------------------#
# #
# Lenstra's Elliptic Curve Factorization #
# #
#----------------------------------------------------------------------------#
class Point:
"""Montgomery form of Points in an elliptic curve.
In this form, the addition and doubling of points
does not need any y-coordinate information thus
decreasing the number of operations.
Using Montgomery form we try to perform point addition
and doubling in least amount of multiplications.
The elliptic curve used here is of the form
(E : b*y**2*z = x**3 + a*x**2*z + x*z**2).
The a_24 parameter is equal to (a + 2)/4.
References
==========
.. [1] Kris Gaj, Soonhak Kwon, Patrick Baier, Paul Kohlbrenner, Hoang Le, Mohammed Khaleeluddin, Ramakrishna Bachimanchi,
Implementing the Elliptic Curve Method of Factoring in Reconfigurable Hardware,
Cryptographic Hardware and Embedded Systems - CHES 2006 (2006), pp. 119-133,
https://doi.org/10.1007/11894063_10
https://www.hyperelliptic.org/tanja/SHARCS/talks06/Gaj.pdf
"""
def __init__(self, x_cord, z_cord, a_24, mod):
"""
Initial parameters for the Point class.
Parameters
==========
x_cord : X coordinate of the Point
z_cord : Z coordinate of the Point
a_24 : Parameter of the elliptic curve in Montgomery form
mod : modulus
"""
self.x_cord = x_cord
self.z_cord = z_cord
self.a_24 = a_24
self.mod = mod
def __eq__(self, other):
"""Two points are equal if X/Z of both points are equal
"""
if self.a_24 != other.a_24 or self.mod != other.mod:
return False
return self.x_cord * other.z_cord % self.mod ==\
other.x_cord * self.z_cord % self.mod
def add(self, Q, diff):
"""
Add two points self and Q where diff = self - Q. Moreover the assumption
is self.x_cord*Q.x_cord*(self.x_cord - Q.x_cord) != 0. This algorithm
requires 6 multiplications. Here the difference between the points
is already known and using this algorithm speeds up the addition
by reducing the number of multiplication required. Also in the
mont_ladder algorithm is constructed in a way so that the difference
between intermediate points is always equal to the initial point.
So, we always know what the difference between the point is.
Parameters
==========
Q : point on the curve in Montgomery form
diff : self - Q
Examples
========
>>> from sympy.ntheory.ecm import Point
>>> p1 = Point(11, 16, 7, 29)
>>> p2 = Point(13, 10, 7, 29)
>>> p3 = p2.add(p1, p1)
>>> p3.x_cord
23
>>> p3.z_cord
17
"""
u = (self.x_cord - self.z_cord)*(Q.x_cord + Q.z_cord)
v = (self.x_cord + self.z_cord)*(Q.x_cord - Q.z_cord)
add, subt = u + v, u - v
x_cord = diff.z_cord * add * add % self.mod
z_cord = diff.x_cord * subt * subt % self.mod
return Point(x_cord, z_cord, self.a_24, self.mod)
def double(self):
"""
Doubles a point in an elliptic curve in Montgomery form.
This algorithm requires 5 multiplications.
Examples
========
>>> from sympy.ntheory.ecm import Point
>>> p1 = Point(11, 16, 7, 29)
>>> p2 = p1.double()
>>> p2.x_cord
13
>>> p2.z_cord
10
"""
u = pow(self.x_cord + self.z_cord, 2, self.mod)
v = pow(self.x_cord - self.z_cord, 2, self.mod)
diff = u - v
x_cord = u*v % self.mod
z_cord = diff*(v + self.a_24*diff) % self.mod
return Point(x_cord, z_cord, self.a_24, self.mod)
def mont_ladder(self, k):
"""
Scalar multiplication of a point in Montgomery form
using Montgomery Ladder Algorithm.
A total of 11 multiplications are required in each step of this
algorithm.
Parameters
==========
k : The positive integer multiplier
Examples
========
>>> from sympy.ntheory.ecm import Point
>>> p1 = Point(11, 16, 7, 29)
>>> p3 = p1.mont_ladder(3)
>>> p3.x_cord
23
>>> p3.z_cord
17
"""
Q = self
R = self.double()
for i in bin(k)[3:]:
if i == '1':
Q = R.add(Q, self)
R = R.double()
else:
R = Q.add(R, self)
Q = Q.double()
return Q
def _ecm_one_factor(n, B1=10000, B2=100000, max_curve=200, seed=None):
"""Returns one factor of n using
Lenstra's 2 Stage Elliptic curve Factorization
with Suyama's Parameterization. Here Montgomery
arithmetic is used for fast computation of addition
and doubling of points in elliptic curve.
Explanation
===========
This ECM method considers elliptic curves in Montgomery
form (E : b*y**2*z = x**3 + a*x**2*z + x*z**2) and involves
elliptic curve operations (mod N), where the elements in
Z are reduced (mod N). Since N is not a prime, E over FF(N)
is not really an elliptic curve but we can still do point additions
and doubling as if FF(N) was a field.
Stage 1 : The basic algorithm involves taking a random point (P) on an
elliptic curve in FF(N). The compute k*P using Montgomery ladder algorithm.
Let q be an unknown factor of N. Then the order of the curve E, |E(FF(q))|,
might be a smooth number that divides k. Then we have k = l * |E(FF(q))|
for some l. For any point belonging to the curve E, |E(FF(q))|*P = O,
hence k*P = l*|E(FF(q))|*P. Thus kP.z_cord = 0 (mod q), and the unknownn
factor of N (q) can be recovered by taking gcd(kP.z_cord, N).
Stage 2 : This is a continuation of Stage 1 if k*P != O. The idea utilize
the fact that even if kP != 0, the value of k might miss just one large
prime divisor of |E(FF(q))|. In this case we only need to compute the
scalar multiplication by p to get p*k*P = O. Here a second bound B2
restrict the size of possible values of p.
Parameters
==========
n : Number to be Factored
B1 : Stage 1 Bound. Must be an even number.
B2 : Stage 2 Bound. Must be an even number.
max_curve : Maximum number of curves generated
Returns
=======
integer | None : ``n`` (if it is prime) else a non-trivial divisor of ``n``. ``None`` if not found
References
==========
.. [1] Carl Pomerance, Richard Crandall, Prime Numbers: A Computational Perspective,
2nd Edition (2005), page 344, ISBN:978-0387252827
"""
randint = _randint(seed)
if isprime(n):
return n
# When calculating T, if (B1 - 2*D) is negative, it cannot be calculated.
D = min(sqrt(B2), B1 // 2 - 1)
sieve.extend(D)
beta = [0] * D
S = [0] * D
k = 1
for p in primerange(2, B1 + 1):
k *= pow(p, int(log(B1, p)))
# Pre-calculate the prime numbers to be used in stage 2.
# Using the fact that the x-coordinates of point P and its
# inverse -P coincide, the number of primes to be checked
# in stage 2 can be reduced.
deltas_list = []
for r in range(B1 + 2*D, B2 + 2*D, 4*D):
deltas = set()
deltas.update((abs(q - r) - 1) // 2 for q in primerange(r - 2*D, r + 2*D))
# d in deltas iff r+(2d+1) and/or r-(2d+1) is prime
deltas_list.append(list(deltas))
for _ in range(max_curve):
#Suyama's Parametrization
sigma = randint(6, n - 1)
u = (sigma**2 - 5) % n
v = (4*sigma) % n
u_3 = pow(u, 3, n)
try:
# We use the elliptic curve y**2 = x**3 + a*x**2 + x
# where a = pow(v - u, 3, n)*(3*u + v)*invert(4*u_3*v, n) - 2
# However, we do not declare a because it is more convenient
# to use a24 = (a + 2)*invert(4, n) in the calculation.
a24 = pow(v - u, 3, n)*(3*u + v)*invert(16*u_3*v, n) % n
except ZeroDivisionError:
#If the invert(16*u_3*v, n) doesn't exist (i.e., g != 1)
g = gcd(2*u_3*v, n)
#If g = n, try another curve
if g == n:
continue
return g
Q = Point(u_3, pow(v, 3, n), a24, n)
Q = Q.mont_ladder(k)
g = gcd(Q.z_cord, n)
#Stage 1 factor
if g != 1 and g != n:
return g
#Stage 1 failure. Q.z = 0, Try another curve
elif g == n:
continue
#Stage 2 - Improved Standard Continuation
S[0] = Q
Q2 = Q.double()
S[1] = Q2.add(Q, Q)
beta[0] = (S[0].x_cord*S[0].z_cord) % n
beta[1] = (S[1].x_cord*S[1].z_cord) % n
for d in range(2, D):
S[d] = S[d - 1].add(Q2, S[d - 2])
beta[d] = (S[d].x_cord*S[d].z_cord) % n
# i.e., S[i] = Q.mont_ladder(2*i + 1)
g = 1
W = Q.mont_ladder(4*D)
T = Q.mont_ladder(B1 - 2*D)
R = Q.mont_ladder(B1 + 2*D)
for deltas in deltas_list:
# R = Q.mont_ladder(r) where r in range(B1 + 2*D, B2 + 2*D, 4*D)
alpha = (R.x_cord*R.z_cord) % n
for delta in deltas:
# We want to calculate
# f = R.x_cord * S[delta].z_cord - S[delta].x_cord * R.z_cord
f = (R.x_cord - S[delta].x_cord)*\
(R.z_cord + S[delta].z_cord) - alpha + beta[delta]
g = (g*f) % n
T, R = R, R.add(W, T)
g = gcd(n, g)
#Stage 2 Factor found
if g != 1 and g != n:
return g
def ecm(n, B1=10000, B2=100000, max_curve=200, seed=1234):
"""Performs factorization using Lenstra's Elliptic curve method.
This function repeatedly calls ``_ecm_one_factor`` to compute the factors
of n. First all the small factors are taken out using trial division.
Then ``_ecm_one_factor`` is used to compute one factor at a time.
Parameters
==========
n : Number to be Factored
B1 : Stage 1 Bound. Must be an even number.
B2 : Stage 2 Bound. Must be an even number.
max_curve : Maximum number of curves generated
seed : Initialize pseudorandom generator
Examples
========
>>> from sympy.ntheory import ecm
>>> ecm(25645121643901801)
{5394769, 4753701529}
>>> ecm(9804659461513846513)
{4641991, 2112166839943}
"""
n = as_int(n)
if B1 % 2 != 0 or B2 % 2 != 0:
raise ValueError("both bounds must be even")
_factors = set()
for prime in sieve.primerange(1, 100000):
if n % prime == 0:
_factors.add(prime)
while(n % prime == 0):
n //= prime
while(n > 1):
factor = _ecm_one_factor(n, B1, B2, max_curve, seed)
if factor is None:
raise ValueError("Increase the bounds")
_factors.add(factor)
n //= factor
factors = set()
for factor in _factors:
if isprime(factor):
factors.add(factor)
continue
factors |= ecm(factor)
return factors
|