You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

weierstrass_prover.sage 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. # Prover implementation for Weierstrass curves of the form
  2. # y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws
  3. # operating on affine and Jacobian coordinates, including the point at infinity
  4. # represented by a 4th variable in coordinates.
  5. load("group_prover.sage")
  6. class affinepoint:
  7. def __init__(self, x, y, infinity=0):
  8. self.x = x
  9. self.y = y
  10. self.infinity = infinity
  11. def __str__(self):
  12. return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity)
  13. class jacobianpoint:
  14. def __init__(self, x, y, z, infinity=0):
  15. self.X = x
  16. self.Y = y
  17. self.Z = z
  18. self.Infinity = infinity
  19. def __str__(self):
  20. return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity)
  21. def point_at_infinity():
  22. return jacobianpoint(1, 1, 1, 1)
  23. def negate(p):
  24. if p.__class__ == affinepoint:
  25. return affinepoint(p.x, -p.y)
  26. if p.__class__ == jacobianpoint:
  27. return jacobianpoint(p.X, -p.Y, p.Z)
  28. assert(False)
  29. def on_weierstrass_curve(A, B, p):
  30. """Return a set of zero-expressions for an affine point to be on the curve"""
  31. return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'})
  32. def tangential_to_weierstrass_curve(A, B, p12, p3):
  33. """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)"""
  34. return constraints(zero={
  35. (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve'
  36. })
  37. def colinear(p1, p2, p3):
  38. """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear"""
  39. return constraints(zero={
  40. (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1',
  41. (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2',
  42. (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3'
  43. })
  44. def good_affine_point(p):
  45. return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'})
  46. def good_jacobian_point(p):
  47. return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'})
  48. def good_point(p):
  49. return constraints(nonzero={p.Z^6 : 'nonzero_X'})
  50. def finite(p, *affine_fns):
  51. con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'})
  52. if p.Z != 0:
  53. return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con)
  54. else:
  55. return con
  56. def infinite(p):
  57. return constraints(nonzero={p.Infinity : 'infinite_point'})
  58. def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC):
  59. """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions"""
  60. assumeLaw = (good_affine_point(pa) +
  61. good_affine_point(pb) +
  62. good_jacobian_point(pA) +
  63. good_jacobian_point(pB) +
  64. on_weierstrass_curve(A, B, pa) +
  65. on_weierstrass_curve(A, B, pb) +
  66. finite(pA) +
  67. finite(pB) +
  68. constraints(nonzero={pa.x - pb.x : 'different_x'}))
  69. require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
  70. colinear(pa, pb, negate(pc))))
  71. return (assumeLaw, require)
  72. def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC):
  73. """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions"""
  74. assumeLaw = (good_affine_point(pa) +
  75. good_affine_point(pb) +
  76. good_jacobian_point(pA) +
  77. good_jacobian_point(pB) +
  78. on_weierstrass_curve(A, B, pa) +
  79. on_weierstrass_curve(A, B, pb) +
  80. finite(pA) +
  81. finite(pB) +
  82. constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'}))
  83. require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
  84. tangential_to_weierstrass_curve(A, B, pa, negate(pc))))
  85. return (assumeLaw, require)
  86. def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC):
  87. assumeLaw = (good_affine_point(pa) +
  88. good_affine_point(pb) +
  89. good_jacobian_point(pA) +
  90. good_jacobian_point(pB) +
  91. on_weierstrass_curve(A, B, pa) +
  92. on_weierstrass_curve(A, B, pb) +
  93. finite(pA) +
  94. finite(pB) +
  95. constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'}))
  96. require = infinite(pC)
  97. return (assumeLaw, require)
  98. def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC):
  99. assumeLaw = (good_affine_point(pa) +
  100. good_affine_point(pb) +
  101. good_jacobian_point(pA) +
  102. good_jacobian_point(pB) +
  103. on_weierstrass_curve(A, B, pb) +
  104. infinite(pA) +
  105. finite(pB))
  106. require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'}))
  107. return (assumeLaw, require)
  108. def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC):
  109. assumeLaw = (good_affine_point(pa) +
  110. good_affine_point(pb) +
  111. good_jacobian_point(pA) +
  112. good_jacobian_point(pB) +
  113. on_weierstrass_curve(A, B, pa) +
  114. infinite(pB) +
  115. finite(pA))
  116. require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'}))
  117. return (assumeLaw, require)
  118. def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC):
  119. assumeLaw = (good_affine_point(pa) +
  120. good_affine_point(pb) +
  121. good_jacobian_point(pA) +
  122. good_jacobian_point(pB) +
  123. infinite(pA) +
  124. infinite(pB))
  125. require = infinite(pC)
  126. return (assumeLaw, require)
  127. laws_jacobian_weierstrass = {
  128. 'add': law_jacobian_weierstrass_add,
  129. 'double': law_jacobian_weierstrass_double,
  130. 'add_opposite': law_jacobian_weierstrass_add_opposites,
  131. 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a,
  132. 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b,
  133. 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab
  134. }
  135. def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
  136. """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
  137. F = Integers(p)
  138. print("Formula %s on Z%i:" % (name, p))
  139. points = []
  140. for x in range(0, p):
  141. for y in range(0, p):
  142. point = affinepoint(F(x), F(y))
  143. r, e = concrete_verify(on_weierstrass_curve(A, B, point))
  144. if r:
  145. points.append(point)
  146. for za in range(1, p):
  147. for zb in range(1, p):
  148. for pa in points:
  149. for pb in points:
  150. for ia in range(2):
  151. for ib in range(2):
  152. pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
  153. pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
  154. for branch in range(0, branches):
  155. assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
  156. pC.X = F(pC.X)
  157. pC.Y = F(pC.Y)
  158. pC.Z = F(pC.Z)
  159. pC.Infinity = F(pC.Infinity)
  160. r, e = concrete_verify(assumeAssert + assumeBranch)
  161. if r:
  162. match = False
  163. for key in laws_jacobian_weierstrass:
  164. assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC)
  165. r, e = concrete_verify(assumeLaw)
  166. if r:
  167. if match:
  168. print(" multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity))
  169. else:
  170. match = True
  171. r, e = concrete_verify(require)
  172. if not r:
  173. print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
  174. print()
  175. def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
  176. assumeLaw, require = f(A, B, pa, pb, pA, pB, pC)
  177. return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require)
  178. def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
  179. """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically"""
  180. R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
  181. lift = lambda x: fastfrac(R,x)
  182. ax = lift(ax)
  183. ay = lift(ay)
  184. Az = lift(Az)
  185. bx = lift(bx)
  186. by = lift(by)
  187. Bz = lift(Bz)
  188. Ai = lift(Ai)
  189. Bi = lift(Bi)
  190. pa = affinepoint(ax, ay, Ai)
  191. pb = affinepoint(bx, by, Bi)
  192. pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai)
  193. pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi)
  194. res = {}
  195. for key in laws_jacobian_weierstrass:
  196. res[key] = []
  197. print("Formula " + name + ":")
  198. count = 0
  199. for branch in range(branches):
  200. assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
  201. pC.X = lift(pC.X)
  202. pC.Y = lift(pC.Y)
  203. pC.Z = lift(pC.Z)
  204. pC.Infinity = lift(pC.Infinity)
  205. for key in laws_jacobian_weierstrass:
  206. res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
  207. for key in res:
  208. print(" %s:" % key)
  209. val = res[key]
  210. for x in val:
  211. if x[0] is not None:
  212. print(" branch %i: %s" % (x[1], x[0]))
  213. print()