scrape.sage 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # Scrape PVSS
  2. # https://eprint.iacr.org/2017/216.pdf
  3. from random import sample
  4. from hashlib import sha256
  5. t = 3 # Threshold
  6. n = 10 # Participants
  7. assert t <= n
  8. # Pallas
  9. p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
  10. q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
  11. Fp = GF(p)
  12. Fq = GF(q)
  13. Ep = EllipticCurve(Fp, (0, 5))
  14. Ep.set_order(q)
  15. # ValueCommitR Generator: g
  16. vcr_x = 0x07f444550fa409bb4f66235bea8d2048406ed745ee90802f0ec3c668883c5a91
  17. vcr_y = 0x24136777af26628c21562cc9e46fb7c2279229f1f39281460e2f46c8a772d9ca
  18. g = Ep([vcr_x, vcr_y])
  19. # NullifierK Generator: h
  20. nfk_x = 0x25e7aa169ca8198d2e375571faf4c9cf5e7eb192ccb5db9bd36f6aa7e447ca75
  21. nfk_y = 0x155c1f851b1a3384880473442008ff755fe0a49ec1c1b4332db8dce21ae001cc
  22. h = Ep([nfk_x, nfk_y])
  23. # ==============
  24. # Initialization
  25. # ==============
  26. # Every party P_i publishes a public key pk_i and witholds the corresponding
  27. # secret key sk_i.
  28. sk = []
  29. pk = []
  30. for i in range(n):
  31. sk_i = Fq.random_element()
  32. pk_i = h * sk_i
  33. sk.append(sk_i)
  34. pk.append(pk_i)
  35. # ============
  36. # Distribution
  37. # ============
  38. # The dealer selects a random secret s:
  39. s = Fq.random_element()
  40. # Pick a polynomial for sharing the secret
  41. alpha = [s]
  42. for i in range(t-1):
  43. alpha.append(Fq.random_element())
  44. R.<ω> = PolynomialRing(Fq)
  45. poly = R(alpha)
  46. assert poly.degree() == t-1
  47. assert poly.coefficients()[0] == s
  48. # Encrypt the shares
  49. shares = []
  50. for i in range(1, n+1):
  51. shares.append(poly(i))
  52. enc_shares = []
  53. for (i, share) in enumerate(shares):
  54. enc_shares.append(pk[i] * share)
  55. # Commit to shares
  56. v = []
  57. for share in shares:
  58. v.append(g * share)
  59. # Create DLEQ proofs:
  60. # (Remember DLEQ(g,x,h,y) where g and h are generators, x=g*α, y=h*α)
  61. # We calculate DLEQ(g, v_i, pk_i, enc_shares_i):
  62. # x_i = g * share_i = v_i
  63. # y_i = pk_i * share_i = enc_shares_i
  64. w = Fq.random_element()
  65. # Fiat-Shamir:
  66. e = sha256()
  67. e.update(str(g*w).encode())
  68. for i in range(n):
  69. e.update(str(v[i]).encode())
  70. e.update(str(enc_shares[i]).encode())
  71. e.update(str(pk[i] * w).encode())
  72. e_prover = Fq(int(e.hexdigest(), 16))
  73. a1 = g * w
  74. a2 = []
  75. for i in range(n):
  76. a2.append(pk[i] * w)
  77. z = []
  78. for i in range(n):
  79. z_i = w - shares[i] * e_prover
  80. z.append(z_i)
  81. # ============
  82. # Verification
  83. # ============
  84. # Check the DLEQ proof:
  85. e = sha256()
  86. e.update(str(a1).encode())
  87. for i in range(n):
  88. e.update(str(v[i]).encode())
  89. e.update(str(enc_shares[i]).encode())
  90. e.update(str(a2[i]).encode())
  91. e_verifier = Fq(int(e.hexdigest(), 16))
  92. assert e_prover == e_verifier
  93. for i in range(n):
  94. assert a1 == g*z[i] + v[i]*e_verifier
  95. assert a2[i] == pk[i]*z[i] + enc_shares[i]*e_verifier
  96. # Reed Solomon check:
  97. # We can sample a polynomial with random coefficients of degree n-t-1
  98. RS.<σ> = PolynomialRing(Fq)
  99. σ_coeff = [Fq.random_element() for _ in range(n-t)]
  100. v_poly = RS(σ_coeff)
  101. assert v_poly.degree() == n-t-1
  102. # Then perform the following:
  103. v_p = Ep(0)
  104. for i in range(n):
  105. c_perp = v_poly(Fq(i))
  106. for j in range(n):
  107. if i != j:
  108. c_perp *= (Fq(i) - Fq(j)).inverse()
  109. v_p += v[i] * c_perp
  110. assert v_p == Ep(0)
  111. # At this point we accept the proof and shares as valid.
  112. # ==============
  113. # Reconstruction
  114. # ==============
  115. # Parties decrypt their shares, ~s_i = ^s_i * 1/sk_i = h * s_i
  116. dec_shares = []
  117. for i in range(n):
  118. share = enc_shares[i] * sk[i].inverse()
  119. dec_shares.append(share)
  120. # See pvss.sage for DLEQ reconstruction proofs.
  121. # The proof is: DLEQ(h, pk_i, dec_shares_i, enc_shares_i), showing that
  122. # the decrypted share dec_share_i corresponds to enc_shares_i.
  123. def lambda_func(i, t, indices):
  124. lambda_i = Fq(1)
  125. for j in indices:
  126. if j != i:
  127. lambda_i *= Fq(j+1) / (Fq(i+1) - Fq(j+1))
  128. return lambda_i
  129. # Pooling the shares. Sample a set of t shares and reconstruct secret.
  130. sample_indices = sorted(sample(range(n), t))
  131. sampled_shares = [dec_shares[i] for i in sample_indices]
  132. pooled = Ep(0)
  133. for idx, share in zip(sample_indices, sampled_shares):
  134. pooled += share * lambda_func(idx, t, sample_indices)
  135. assert h*s == h*poly(0) == pooled