pvss.sage 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # Publicly Verifiable Secret Sharing
  2. # https://www.win.tue.nl/~berry/papers/crypto99.pdf
  3. # This scheme depends on an honest dealer.
  4. from random import sample
  5. from hashlib import sha256
  6. from itertools import chain
  7. t = 3 # Threshold
  8. n = 5 # Participants
  9. assert t <= n
  10. # Pallas
  11. p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
  12. q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
  13. Fp = GF(p)
  14. Fq = GF(q)
  15. Ep = EllipticCurve(Fp, (0, 5))
  16. Ep.set_order(q)
  17. # ValueCommitR Generator: g
  18. vcr_x = 0x07f444550fa409bb4f66235bea8d2048406ed745ee90802f0ec3c668883c5a91
  19. vcr_y = 0x24136777af26628c21562cc9e46fb7c2279229f1f39281460e2f46c8a772d9ca
  20. g = Ep([vcr_x, vcr_y])
  21. # NullifierK Generator: G
  22. nfk_x = 0x25e7aa169ca8198d2e375571faf4c9cf5e7eb192ccb5db9bd36f6aa7e447ca75
  23. nfk_y = 0x155c1f851b1a3384880473442008ff755fe0a49ec1c1b4332db8dce21ae001cc
  24. G = Ep([nfk_x, nfk_y])
  25. # ==============
  26. # Initialization
  27. # ==============
  28. # The participants create their keypairs and register their public keys
  29. # y_i = G^{x_i}
  30. x = []
  31. y = []
  32. for i in range(n):
  33. x_i = Fq.random_element()
  34. x.append(x_i)
  35. y.append(G * x_i)
  36. # ============
  37. # Distribution
  38. # ============
  39. # The dealer selects a secret s:
  40. s = Fq.random_element()
  41. # The dealer picks a random polynomial p of degree at most t-1 with
  42. # coefficients in Fq and sets s=alpha_0
  43. alpha = [s]
  44. for i in range(t-1):
  45. alpha.append(Fq.random_element())
  46. R.<ω> = PolynomialRing(Fq)
  47. p = R(alpha)
  48. assert p.degree() == t-1
  49. assert p.coefficients()[0] == s
  50. # The dealer keeps this polynomial secret but publishes the related
  51. # commitments C_j = g ^ {a_j} , for 0 ≤ j < t
  52. C = []
  53. for j in range(t):
  54. C.append(g * alpha[j])
  55. # The dealer also publishes the encrypted shares Y_i = y_i ^ p(i),
  56. # for 1 ≤ i ≤ n , using the public keys of the participants:
  57. Y = []
  58. for i in range(1, n+1):
  59. Y.append(y[i-1] * p(i))
  60. # Let X_i = prod_{j=0}^{t-1} C_j * i^j. The verifier computes this from the
  61. # published commitments C_j.
  62. X = []
  63. for i in range(1, n+1):
  64. X_i = Ep(0)
  65. for j in range(t):
  66. X_i += C[j] * (i^j)
  67. X.append(X_i)
  68. # The dealer shows that the encrypted shares are consistent by
  69. # producing a proof of knowledge of the unique p(i), 1 ≤ i ≤ n,
  70. # satisfying: X_i = g^p(i) , Y_i = y_i^p(i)
  71. for i in range(1, n+1):
  72. assert X[i-1] == g * p(i)
  73. assert Y[i-1] == y[i-1] * p(i)
  74. # For a non-interactive proof, we can use the Fiat-Shamir technique.
  75. # (See DLEQ in the paper)
  76. # The prover calculates a1_i and a2_i:
  77. w_i = []
  78. p_a1 = []
  79. p_a2 = []
  80. for i in range(n):
  81. w = Fq.random_element()
  82. w_i.append(w)
  83. a1_i = g * w
  84. a2_i = y[i] * w
  85. p_a1.append(a1_i)
  86. p_a2.append(a2_i)
  87. # And then hashes the necessary values in order to produce c:
  88. assert len(X) == len(Y) == len(p_a1) == len(p_a2)
  89. c_hasher = sha256()
  90. for point in chain(X, Y, p_a1, p_a2):
  91. x_coord, y_coord = point.xy()
  92. c_hasher.update(str(x_coord).encode())
  93. c_hasher.update(str(y_coord).encode())
  94. # Prover publishes c
  95. c = Fq(int(c_hasher.hexdigest(), 16))
  96. # And finally, prover calculates and publishes r_i responses:
  97. r = []
  98. for i in range(1, n+1):
  99. r_i = w_i[i-1] - p(i) * c
  100. r.append(r_i)
  101. # The verifier calculates a1_i and a2_i:
  102. # a1_i = g^r_i * X_i^c
  103. # a2_i = y_i^r_i * Y_i^c
  104. v_a1 = []
  105. v_a2 = []
  106. for i in range(n):
  107. a1_i = (g * r[i]) + (X[i] * c)
  108. a2_i = (y[i] * r[i]) + (Y[i] * c)
  109. v_a1.append(a1_i)
  110. v_a2.append(a2_i)
  111. # And then hashes the necessary values in order to produce c:
  112. v_hasher = sha256()
  113. for point in chain(X, Y, v_a1, v_a2):
  114. x_coord, y_coord = point.xy()
  115. v_hasher.update(str(x_coord).encode())
  116. v_hasher.update(str(y_coord).encode())
  117. v_c = Fq(int(v_hasher.hexdigest(), 16))
  118. # And checks that the hash matches the published c
  119. assert v_c == c
  120. # ==============
  121. # Reconstruction
  122. # ==============
  123. # Using its private key x_i, each participant finds the share S_i = G^p(i)
  124. # from Y_i by computing S_i = Y_i^(1/x_i). They publish S_i plus a proof
  125. # that the value S_i is a correct decryption of Y_i. To this end it
  126. # suffices to prove knowledge of an alpha such that y_i = G^alpha and
  127. # Y_i = S_i^alpha, which is accomplished by the non-interactive version
  128. # of the protocol DLEQ(G, y_i, S_i, Y_i).
  129. S = []
  130. for i in range(n):
  131. S_i = Y[i] * x[i].inverse_of_unit()
  132. assert S_i == G * p(i+1)
  133. S.append(S_i)
  134. # DLEQ proofs for reconstruction
  135. dleq_proofs = []
  136. for i in range(n):
  137. w = Fq.random_element()
  138. a1 = G * w
  139. a2 = S[i] * w
  140. dleq_hasher = sha256()
  141. for point in [G, y[i], S[i], Y[i], a1, a2]:
  142. x_coord, y_coord = point.xy()
  143. dleq_hasher.update(str(x_coord).encode())
  144. dleq_hasher.update(str(y_coord).encode())
  145. c = Fq(int(dleq_hasher.hexdigest(), 16))
  146. r = w - x[i] * c
  147. dleq_proofs.append((c, r))
  148. # DLEQ verifications for reconstruction
  149. for i in range(n):
  150. c, r = dleq_proofs[i]
  151. a1 = G * r + y[i] * c
  152. a2 = S[i] * r + Y[i] * c
  153. dleq_hasher = sha256()
  154. for point in [G, y[i], S[i], Y[i], a1, a2]:
  155. x_coord, y_coord = point.xy()
  156. dleq_hasher.update(str(x_coord).encode())
  157. dleq_hasher.update(str(y_coord).encode())
  158. v_c = Fq(int(dleq_hasher.hexdigest(), 16))
  159. assert v_c == c
  160. # Pooling the shares. Sample a set of t shares and reconstruct secret.
  161. sample_indices = sorted(sample(range(n), t))
  162. sampled_shares = [S[i] for i in sample_indices]
  163. pooled = Ep(0)
  164. def lambda_func(i, t, indices):
  165. lambda_i = Fq(1)
  166. for j in indices:
  167. if j != i:
  168. lambda_i *= Fq(j+1) / (Fq(i+1) - Fq(j+1))
  169. return lambda_i
  170. for idx, share in zip(sample_indices, sampled_shares):
  171. pooled += share * lambda_func(idx, t, sample_indices)
  172. # Assert reconstructed secret
  173. assert G*s == G*p(0) == pooled