ecm.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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] http://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 * mod_inverse(self.z_cord, self.mod) % self.mod ==\
  46. other.x_cord * mod_inverse(other.z_cord, self.mod) % 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 algorihtm 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, v = self.x_cord + self.z_cord, self.x_cord - self.z_cord
  93. u, v = u*u, v*v
  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. from sympy.functions.elementary.miscellaneous import sqrt
  163. from sympy.polys.polytools import gcd
  164. n = as_int(n)
  165. if B1 % 2 != 0 or B2 % 2 != 0:
  166. raise ValueError("The Bounds should be an even integer")
  167. sieve.extend(B2)
  168. if isprime(n):
  169. return n
  170. curve = 0
  171. D = int(sqrt(B2))
  172. beta = [0]*(D + 1)
  173. S = [0]*(D + 1)
  174. k = 1
  175. for p in sieve.primerange(1, B1 + 1):
  176. k *= pow(p, integer_log(B1, p)[0])
  177. while(curve <= max_curve):
  178. curve += 1
  179. #Suyama's Paramatrization
  180. sigma = rgen.randint(6, n - 1)
  181. u = (sigma*sigma - 5) % n
  182. v = (4*sigma) % n
  183. diff = v - u
  184. u_3 = pow(u, 3, n)
  185. try:
  186. C = (pow(diff, 3, n)*(3*u + v)*mod_inverse(4*u_3*v, n) - 2) % n
  187. except ValueError:
  188. #If the mod_inverse(4*u_3*v, n) doesn't exist
  189. return gcd(4*u_3*v, n)
  190. a24 = (C + 2)*mod_inverse(4, n) % n
  191. Q = Point(u_3, pow(v, 3, n), a24, n)
  192. Q = Q.mont_ladder(k)
  193. g = gcd(Q.z_cord, n)
  194. #Stage 1 factor
  195. if g != 1 and g != n:
  196. return g
  197. #Stage 1 failure. Q.z = 0, Try another curve
  198. elif g == n:
  199. continue
  200. #Stage 2 - Improved Standard Continuation
  201. S[1] = Q.double()
  202. S[2] = S[1].double()
  203. beta[1] = (S[1].x_cord*S[1].z_cord) % n
  204. beta[2] = (S[2].x_cord*S[2].z_cord) % n
  205. for d in range(3, D + 1):
  206. S[d] = S[d - 1].add(S[1], S[d - 2])
  207. beta[d] = (S[d].x_cord*S[d].z_cord) % n
  208. g = 1
  209. B = B1 - 1
  210. T = Q.mont_ladder(B - 2*D)
  211. R = Q.mont_ladder(B)
  212. for r in range(B, B2, 2*D):
  213. alpha = (R.x_cord*R.z_cord) % n
  214. for q in sieve.primerange(r + 2, r + 2*D + 1):
  215. delta = (q - r) // 2
  216. f = (R.x_cord - S[d].x_cord)*(R.z_cord + S[d].z_cord) -\
  217. alpha + beta[delta]
  218. g = (g*f) % n
  219. #Swap
  220. T, R = R, R.add(S[D], T)
  221. g = gcd(n, g)
  222. #Stage 2 Factor found
  223. if g != 1 and g != n:
  224. return g
  225. #ECM failed, Increase the bounds
  226. raise ValueError("Increase the bounds")
  227. def ecm(n, B1=10000, B2=100000, max_curve=200, seed=1234):
  228. """Performs factorization using Lenstra's Elliptic curve method.
  229. This function repeatedly calls `ecm_one_factor` to compute the factors
  230. of n. First all the small factors are taken out using trial division.
  231. Then `ecm_one_factor` is used to compute one factor at a time.
  232. Parameters
  233. ==========
  234. n : Number to be Factored
  235. B1 : Stage 1 Bound
  236. B2 : Stage 2 Bound
  237. max_curve : Maximum number of curves generated
  238. seed : Initialize pseudorandom generator
  239. Examples
  240. ========
  241. >>> from sympy.ntheory import ecm
  242. >>> ecm(25645121643901801)
  243. {5394769, 4753701529}
  244. >>> ecm(9804659461513846513)
  245. {4641991, 2112166839943}
  246. """
  247. _factors = set()
  248. for prime in sieve.primerange(1, 100000):
  249. if n % prime == 0:
  250. _factors.add(prime)
  251. while(n % prime == 0):
  252. n //= prime
  253. rgen.seed(seed)
  254. while(n > 1):
  255. try:
  256. factor = _ecm_one_factor(n, B1, B2, max_curve)
  257. except ValueError:
  258. raise ValueError("Increase the bounds")
  259. _factors.add(factor)
  260. n //= factor
  261. factors = set()
  262. for factor in _factors:
  263. if isprime(factor):
  264. factors.add(factor)
  265. continue
  266. factors |= ecm(factor)
  267. return factors