escrow.sage 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. # Player 1:
  2. #
  3. # $ sage multisig.sage genshare 2 3
  4. # A_0 = (17885028560402702239015261002188504514192120716688655005384557793085172279782, 8478670718018646753668209243232218399157692386519596227477013206445567901574)
  5. # A_1 = (24159996358010728217037819815568538754372914232633035350125672407860245980511, 25930880768349059332486494233731724414031492717763788767169325464072537905825)
  6. #
  7. # 18245370559787825905107196417449809168129753681991950558452633836050485263625*X + Y + 21540401009192565430578225487575742717824479971010380831145688671749585986737
  8. #
  9. # Player 1's R = (1, 18110273049677706376100070599318402040771879310880963369761162988986654645832)
  10. # Player 2's R = (2, 28812924799218929326885620434040569836005182110830660190988271901329532330304)
  11. # Player 3's R = (3, 10567554239431103421778424016590760667875428428838709632535638065279047066679)
  12. #
  13. # Player 2:
  14. #
  15. # $ sage multisig.sage genshare 2 3
  16. # A_0 = (802940932777049145807706375204159819724468245569326332695920733054960506640, 16355306126743352920399387200062438463930792131448355230670435463427127600465)
  17. # A_1 = (18141591414190824859071415735841428554530479628199222928098733408655160229999, 8126608045648459195646305845881115977993411928279311595258374101920586157117)
  18. #
  19. # 11632470451635415873896303993978816754215391793499851090581681689882331027728*X + Y + 14165208049524082319699866276606579004249018988481566200129536690961988099823
  20. #
  21. # Player 1's R = (1, 3150343808169550662296575981586581204898645699960230088968524367549043820546)
  22. # Player 2's R = (2, 20465895665863183644293018239779741414046310388402026378066585426060075740915)
  23. # Player 3's R = (3, 8833425214227767770396714245800924659830918594902175287484903736177744713187)
  24. #
  25. # Player 3:
  26. #
  27. # $ sage multisig.sage genshare 2 3
  28. # A_0 = (18050147058625614196833411623290233672504963362553094195996683497184802776885, 1558020177192412780876956779873498858051955811269538614817345479760770446460)
  29. # A_1 = (8859924332949089269591379272271847007387528856001855566494832087904814398546, 27961455601858187272958269886544614132704520355314277906405837204662338231452)
  30. #
  31. # 22603695278713704898809499991696306162603661219853447180662052608597912268649*X + Y + 26665809323852841663944379970510953158263212308412307792093486582513151778711
  32. #
  33. # Player 1's R = (1, 8626540016091551149031612542136694605859239435617539786603946305675661848834)
  34. # Player 2's R = (2, 14970867046706895106114858802612365406618634697705739985621636445471112528282)
  35. # Player 3's R = (3, 21315194077322239063198105063088036207378029959793940184639326585266563207730)
  36. # Each player has now generated the curve, shared its commits, and distributed shares.
  37. # Now we recover the public key using all A0s
  38. #
  39. # $ sage multisig.sage pubkey "(17885028560402702239015261002188504514192120716688655005384557793085172279782, 8478670718018646753668209243232218399157692386519596227477013206445567901574)" "(802940932777049145807706375204159819724468245569326332695920733054960506640, 16355306126743352920399387200062438463930792131448355230670435463427127600465)" "(18050147058625614196833411623290233672504963362553094195996683497184802776885, 1558020177192412780876956779873498858051955811269538614817345479760770446460)"
  40. #
  41. # (9803495978299341257553881350441085748898862974053305000078308077444698847765 : 7803011951094511021525891181798443393652882333120996671357964800654859381546 : 1)
  42. # To recover the shared secret, player's 1 and 2 will work together to
  43. # recreate all 3 curves.
  44. #
  45. # $ sage multisig.sage recover "(1, 18110273049677706376100070599318402040771879310880963369761162988986654645832)" "(2, 28812924799218929326885620434040569836005182110830660190988271901329532330304)"
  46. # 21540401009192565430578225487575742717824479971010380831145688671749585986737
  47. #
  48. # $ sage multisig.sage recover "(1, 3150343808169550662296575981586581204898645699960230088968524367549043820546)" "(2, 20465895665863183644293018239779741414046310388402026378066585426060075740915)"
  49. # 14165208049524082319699866276606579004249018988481566200129536690961988099823
  50. #
  51. # $ sage multisig.sage recover "(1, 8626540016091551149031612542136694605859239435617539786603946305675661848834)" "(2, 14970867046706895106114858802612365406618634697705739985621636445471112528282)"
  52. # 26665809323852841663944379970510953158263212308412307792093486582513151778711
  53. #
  54. # You can see each command returns the constant coefficient in each curve.
  55. # Finally we combine these to get the final curve and hence shared secret.
  56. #
  57. # $ sage multisig.sage combine 14165208049524082319699866276606579004249018988481566200129536690961988099823 21540401009192565430578225487575742717824479971010380831145688671749585986737 26665809323852841663944379970510953158263212308412307792093486582513151778711
  58. # Secret: 4475373763911391702436979230349320953610598304020960064009226448437999969077
  59. # Pubkey: (9803495978299341257553881350441085748898862974053305000078308077444698847765 : 7803011951094511021525891181798443393652882333120996671357964800654859381546 : 1)
  60. #
  61. # We can see the public key matches what we got in the 'pubkey' step.
  62. import argparse, base64, sys
  63. q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
  64. K = GF(q)
  65. P.<X, Y> = K[]
  66. p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
  67. Fp = GF(p)
  68. E = EllipticCurve(Fp, (0, 5))
  69. G = E(
  70. 23241645597038891398529199502196854108878665864265357905694087894995100434173,
  71. 14702009283686283423048268817274882285027504402886079870290245450065579125215
  72. )
  73. assert G.order() == q
  74. def genshare(args):
  75. t, n = args.t, args.n
  76. assert t <= n
  77. C = Y
  78. A = []
  79. for i in range(t):
  80. a_i = K.random_element()
  81. C += a_i * X^i
  82. A_i = a_i*G
  83. print(f"A_{i} = ({A_i[0]}, {A_i[1]})")
  84. A.append(A_i)
  85. print()
  86. print(C)
  87. print()
  88. R = []
  89. for j in range(1, n+1):
  90. x_j = j
  91. y_j = -C(X=x_j, Y=0)
  92. assert C(X=x_j, Y=y_j) == 0
  93. R_j = (x_j, y_j)
  94. print(f"Player {j}'s R = {R_j}")
  95. R.append(R_j)
  96. # Each player upon receiving their shares should perform this check
  97. def eval_C(x):
  98. P = E(0)
  99. for i, A_i in enumerate(A):
  100. P += x^i * A_i
  101. return P
  102. for R_j in R:
  103. x_j, y_j = R_j
  104. assert y_j*G + eval_C(x_j) == E(0)
  105. def pubkey(args):
  106. P = E(0)
  107. for A0str in args.A0:
  108. x, y = A0str.split(",")
  109. x = x.strip("(")
  110. y = y.strip(") ")
  111. x, y = K(x), K(y)
  112. A0 = E(x, y)
  113. P += A0
  114. print(P)
  115. def recover(args):
  116. R = []
  117. for Rjstr in args.Rj:
  118. x, y = Rjstr.split(",")
  119. x = x.strip("(")
  120. y = y.strip(") ")
  121. x, y = K(x), K(y)
  122. R_j = (x, y)
  123. R.append(R_j)
  124. # Create the Vandermonde matrix with a₀, …, aₜ₋₁ as indeterminates.
  125. V = []
  126. t = len(R)
  127. y = []
  128. for R_j in R:
  129. x_j, y_j = R_j
  130. y.append(y_j)
  131. V.append([x_j^i for i in range(t)])
  132. V = matrix(V)
  133. y = vector(y)
  134. a = V^-1 * -y
  135. print(a[0])
  136. def combine(args):
  137. a0 = K(0)
  138. for a in args.a0:
  139. a0 += a
  140. print(f"Secret: {a0}")
  141. print(f"Pubkey: {a0*G}")
  142. def main():
  143. parser = argparse.ArgumentParser(prog="multisig.sage")
  144. subparsers = parser.add_subparsers(required=True)
  145. parser_genshare = subparsers.add_parser("genshare", help="Generate a share")
  146. parser_genshare.add_argument("t", type=int, help="threshold for recovery")
  147. parser_genshare.add_argument("n", type=int, help="total players")
  148. parser_genshare.set_defaults(func=genshare)
  149. parser_pubkey = subparsers.add_parser("pubkey",
  150. help="Compute shared pubkey")
  151. parser_pubkey.add_argument("A0", nargs="+")
  152. parser_pubkey.set_defaults(func=pubkey)
  153. parser_recover = subparsers.add_parser("recover",
  154. help="Recover shared secret")
  155. parser_recover.add_argument("Rj", nargs="+")
  156. parser_recover.set_defaults(func=recover)
  157. parser_combine = subparsers.add_parser("combine",
  158. help="Combine shared secrets")
  159. parser_combine.add_argument("a0", type=int, nargs="+")
  160. parser_combine.set_defaults(func=combine)
  161. args = parser.parse_args()
  162. args.func(args)
  163. main()