pvss.sage 5.6 KB

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