ecm.py 10 KB

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