ecm.py 12 KB

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