dlog.sage 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. # Initialize an elliptic curve
  2. p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
  3. r = 115792089237316195423570985008687907852837564279074904382605163141518161494337
  4. Fp = GF(p) # Base Field
  5. Fr = GF(r) # Scalar Field
  6. A = 0
  7. B = 7
  8. E = EllipticCurve(GF(p), [A,B])
  9. assert(E.cardinality() == r)
  10. K.<x> = PolynomialRing(Fp, implementation="generic")
  11. L.<y> = PolynomialRing(K, implementation="generic")
  12. eqn = y^2 - x^3 - A * x - B
  13. # Returns line passing through points, works for all points and returns 1 for O + O = O
  14. def line(A, B):
  15. if A == 0 and B == 0:
  16. return 1
  17. else:
  18. [a, b, c] = Matrix([A, B, -(A+B)]).transpose().kernel().basis()[0]
  19. return a*x + b*y + c
  20. def dlog(D):
  21. # Derivative via partials
  22. Dx = D.differentiate(x)
  23. Dy = D.differentiate(y)
  24. Dz = Dx + Dy * ((3*x^2 + A) / (2*y))
  25. assert D != 0
  26. return Dz/D
  27. def dlog_alt(D):
  28. Dx = D.differentiate(x)
  29. Dy = D.differentiate(y)
  30. #Dz = Dx + Dy * ((3*x^2 + A) / (2*y))
  31. # Normally we calculate:
  32. # Dz/D
  33. # Due to a bug in sage, we will make the denominator D
  34. # solely an equation in x by taking its norm.
  35. # Denominator = V · V'
  36. V = 2*y * D
  37. # 2y Dz
  38. Dz_numer = (2*y*Dx + Dy * (3*x^2 + A) * V(y=-y)).mod(eqn)
  39. # Change denominator to the norm
  40. D_denom = (V * V(y=-y)).mod(eqn)
  41. return Dz_numer / D_denom
  42. P0 = E.random_element()
  43. P1 = E.random_element()
  44. P2 = E.random_element()
  45. Q = -int(Fr(5)^-1) * (P0 + 2*P1 + 3*P2)
  46. assert P0 + 2*P1 + 3*P2 + 5*Q == 0
  47. def div_add(div_f, div_g):
  48. div = div_f.copy()
  49. for P, n in div_g.items():
  50. if P in div:
  51. div[P] += n
  52. else:
  53. div[P] = n
  54. div = dict((P, n) for P, n in div.items() if n != 0)
  55. return div
  56. def div_invert(div):
  57. return dict((P, -n) for P, n in div.items())
  58. def div_sub(div_f, div_g):
  59. inv_div_g = div_invert(div_g)
  60. return div_add(div_f, inv_div_g)
  61. # 2[P₂] + [-2P₂] - 3[∞]
  62. f1 = line(P2, P2)
  63. D1 = {"P2": 2, "-2P2": 1, "∞": -3}
  64. # 2[P₁] + [-2P₁] - 3[∞]
  65. f2 = line(P1, P1)
  66. D2 = {"P1": 2, "-2P1": 1, "∞": -3}
  67. # [P₂] + [-P₂] - 2[∞]
  68. f3 = line(P2, -P2)
  69. D3 = {"P2": 1, "-P2": 1, "∞": -2}
  70. # [P₀] + [-P₀] - 2[∞]
  71. f4 = line(P0, -P0)
  72. D4 = {"P0": 1, "-P0": 1, "∞": -2}
  73. # (2[P₂] + [-2P₂] - 3[∞]
  74. # + 2[P₁] + [-2P₁] - 3[∞]
  75. # + [P₂] + [-P₂] - 2[∞]
  76. # + [P₀] + [-P₀] - 2[∞])
  77. # =
  78. # [P₀] + 2[P₁] + 3[P₂] + [-P₀] + [-2P₁] + [-2P₂] + [-P₂] - 10[∞]
  79. f5 = f1*f2*f3*f4
  80. D5 = div_add(div_add(D1, D2), div_add(D3, D4))
  81. assert D5 == {
  82. "P0": 1,
  83. "P1": 2,
  84. "P2": 3,
  85. "-P0": 1,
  86. "-2P1": 1,
  87. "-2P2": 1,
  88. "-P2": 1,
  89. "∞": -10
  90. }
  91. # [-2P₂] + [-2P₁] + [2(P₁ + P₂)] - 3[∞]
  92. f6 = line(-2*P2, -2*P1)
  93. D6 = {"-2P2": 1, "-2P1": 1, "2P1 + 2P2": 1, "∞": -3}
  94. # [-P₂] + [-P₀] + [P₀ + P₂] - 3[∞]
  95. f7 = line(-P2, -P0)
  96. D7 = {"-P2": 1, "-P0": 1, "P0 + P2": 1, "∞": -3}
  97. # ([P₀] + 2[P₁] + 3[P₂] + [-P₀] + [-2P₁] + [-2P₂] + [-P₂] - 10[∞]
  98. # - [-2P₂] - [-2P₁] - [2(P₁ + P₂)] + 3[∞]
  99. # - [-P₂] - [-P₀] - [P₀ + P₂] + 3[∞])
  100. # =
  101. # [P₀] + 2[P₁] + 3[P₂] - [2(P₁ + P₂)] - [P₀ + P₂] - 4[∞]
  102. f8 = f5/(f6*f7)
  103. D8 = div_sub(D5, div_add(D6, D7))
  104. assert D8 == {
  105. "P0": 1,
  106. "P1": 2,
  107. "P2": 3,
  108. "2P1 + 2P2": -1,
  109. "P0 + P2": -1,
  110. "∞": -4
  111. }
  112. # [P₀ + P₂] + [2(P₁ + P₂)] + [-(P₀ + 2P₁ + 3P₂)] - 3[∞]
  113. f9 = line(P0 + P2, 2*(P1 + P2))
  114. D9 = {"P0 + P2": 1, "2P1 + 2P2": 1, "5Q": 1, "∞": -3}
  115. # ([P₀] + 2[P₁] + 3[P₂] - [2(P₁ + P₂)] - [P₀ + P₂] - 4[∞]
  116. # + [P₀ + P₂] + [2(P₁ + P₂)] + [-(P₀ + 2P₁ + 3P₂)] - 3[∞])
  117. # =
  118. # [P₀] + 2[P₁] + 3[P₂] + [-(P₀ + 2P₁ + 3P₂)] - 7[∞]
  119. # = [P₀] + 2[P₁] + 3[P₂] + [5Q] - 7[∞]
  120. f10 = f8*f9
  121. D10 = div_add(D8, D9)
  122. assert D10 == {
  123. "P0": 1,
  124. "P1": 2,
  125. "P2": 3,
  126. "5Q": 1,
  127. "∞": -7
  128. }
  129. # Now construct 5[Q]
  130. # 2[Q] + [-2Q] - 3[∞]
  131. f11 = line(Q, Q)
  132. D11 = {"Q": 2, "-2Q": 1, "∞": -3}
  133. # [-2Q] + [2Q] - 2[∞]
  134. f12 = line(-2*Q, 2*Q)
  135. D12 = {"-2Q": 1, "2Q": 1, "∞": -2}
  136. # (2[Q] + [-2Q] - 3[∞]) - ([-2Q] + [2Q] - 2[∞])
  137. # ==
  138. # 2[Q] - [2Q] - [∞]
  139. f13 = f11/f12
  140. D13 = div_sub(D11, D12)
  141. assert D13 == {
  142. "Q": 2,
  143. "2Q": -1,
  144. "∞": -1
  145. }
  146. # multiply by 3
  147. # 6[Q] - 3[2Q] - 3[∞]
  148. f14 = f13*f13*f13
  149. D14 = div_add(div_add(D13, D13), D13)
  150. assert D14 == {
  151. "Q": 6,
  152. "2Q": -3,
  153. "∞": -3
  154. }
  155. # 2[2Q] + [-4Q] - 3[∞]
  156. f15 = line(2*Q, 2*Q)
  157. D15 = {"2Q": 2, "-4Q": 1, "∞": -3}
  158. # (6[Q] - 3[2Q] - 3[∞]) + (2[2Q] + [-4Q] - 3[∞])
  159. # ==
  160. # 6[Q] - [2Q] + [-4Q] - 6[∞]
  161. f16 = f14*f15
  162. D16 = div_add(D14, D15)
  163. assert D16 == {
  164. "Q": 6,
  165. "2Q": -1,
  166. "-4Q": 1,
  167. "∞": -6
  168. }
  169. # [2Q] + [-2Q] - 2[∞]
  170. f17 = line(2*Q, -2*Q)
  171. D17 = {"2Q": 1, "-2Q": 1, "∞": -2}
  172. # (6[Q] - [2Q] + [-4Q] - 6[∞]) + ([2Q] + [-2Q] - 2[∞])
  173. # ==
  174. # 6[Q] + [-2Q] + [-4Q] - 8[∞]
  175. f18 = f16*f17
  176. D18 = div_add(D16, D17)
  177. assert D18 == {
  178. "Q": 6,
  179. "-2Q": 1,
  180. "-4Q": 1,
  181. "∞": -8
  182. }
  183. # [-2Q] + [-4Q] + [6Q] - 3[∞]
  184. f19 = line(-2*Q, -4*Q)
  185. D19 = {"-2Q": 1, "-4Q": 1, "6Q": 1, "∞": -3}
  186. # (6[Q] + [-2Q] + [-4Q] - 8[∞]) - ([-2Q] + [-4Q] + [6Q] - 3[∞])
  187. # ==
  188. # 6[Q] - [6Q] - 5[∞]
  189. f20 = f18/f19
  190. D20 = div_sub(D18, D19)
  191. assert D20 == {
  192. "Q": 6,
  193. "6Q": -1,
  194. "∞": -5
  195. }
  196. # [6Q] + [-6Q] - 2[∞]
  197. f21 = line(6*Q, -6*Q)
  198. D21 = {"6Q": 1, "-6Q": 1, "∞": -2}
  199. # (6[Q] - [6Q] - 5[∞]) + ([6Q] + [-6Q] - 2[∞])
  200. # ==
  201. # 6[Q] + [-6Q] - 7[∞]
  202. f22 = f20*f21
  203. D22 = div_add(D20, D21)
  204. assert D22 == {"Q": 6, "-6Q": 1, "∞": -7}
  205. # [Q] + [-6Q] + [5Q] - 3[∞]
  206. f23 = line(Q, -6*Q)
  207. D23 = {"Q": 1, "-6Q": 1, "5Q": 1, "∞": -3}
  208. # (6[Q] + [-6Q] - 7[∞]) - ([Q] + [-6Q] + [5Q] - 3[∞])
  209. # ==
  210. # 5[Q] - [5Q] - 4[∞]
  211. f24 = f22/f23
  212. D24 = div_sub(D22, D23)
  213. assert D24 == {"Q": 5, "5Q": -1, "∞": -4}
  214. # Now combine the result
  215. f = f10*f24
  216. D = div_add(D10, D24)
  217. assert D == {
  218. "P0": 1,
  219. "P1": 2,
  220. "P2": 3,
  221. "Q": 5,
  222. "∞": -11
  223. }
  224. f_numer = f.numerator().mod(eqn)
  225. f_denom = f.denominator().mod(eqn)
  226. # ZeroDivisionError
  227. #DLog = dlog(f_numer)
  228. assert f(x=P0[0], y=P0[1]) == 0
  229. assert f(x=P1[0], y=P1[1]) == 0
  230. assert f(x=P2[0], y=P2[1]) == 0
  231. # Need to modify f because this is 0/0
  232. #assert f(x=Q[0], y=Q[1]) == 0
  233. Ps = [P0] + 2*[P1] + 3*[P2] + 5*[Q]
  234. D = construct_function(Ps)
  235. assert D(x=P0[0], y=P0[1]) == 0
  236. assert D(x=P1[0], y=P1[1]) == 0
  237. assert D(x=P2[0], y=P2[1]) == 0
  238. assert D(x=Q[0], y=Q[1]) == 0
  239. # This will fail due to a bug in sage:
  240. #DLog = dlog(D)
  241. # ZeroDivisionError
  242. DLog = dlog_alt(D)