| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- # Publicly Verifiable Secret Sharing
- # https://www.win.tue.nl/~berry/papers/crypto99.pdf
- # This scheme depends on an honest dealer.
- t = 3 # Threshold
- n = 5 # Participants
- # Pallas
- p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
- q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
- Fp = GF(p)
- Fq = GF(q)
- Ep = EllipticCurve(Fp, (0, 5))
- Ep.set_order(q * 0x01)
- Eq = EllipticCurve(Fq, (0, 5))
- Eq.set_order(p * 0x01)
- # ValueCommitR Generator: g
- vcr_x = 0x07f444550fa409bb4f66235bea8d2048406ed745ee90802f0ec3c668883c5a91
- vcr_y = 0x24136777af26628c21562cc9e46fb7c2279229f1f39281460e2f46c8a772d9ca
- g = Ep([vcr_x, vcr_y])
- # NullifierK Generator: G
- nfk_x = 0x25e7aa169ca8198d2e375571faf4c9cf5e7eb192ccb5db9bd36f6aa7e447ca75
- nfk_y = 0x155c1f851b1a3384880473442008ff755fe0a49ec1c1b4332db8dce21ae001cc
- G = Ep([nfk_x, nfk_y])
- # ==============
- # Initialization
- # ==============
- # The participants create their keypairs and register their public keys
- # y_i = G^{x_i}
- x = []
- y = []
- for i in range(n):
- x_i = Fq.random_element()
- #x_i = Fq(i+2)
- x.append(x_i)
- y.append(G * x_i)
- # ============
- # Distribution
- # ============
- # The dealer selects a secret s:
- s = Fq.random_element()
- #s = Fq(42)
- # The dealer picks a random polynomial p of degree at most t-1 with
- # coefficients in Fp and sets s=alpha_0
- alpha = []
- for i in range(t):
- alpha.append(Fq.random_element())
- #alpha.append(Fq(i+2))
- alpha[0] = s
- R.<ω> = PolynomialRing(Fq)
- p = R(alpha)
- assert p.degree() == t-1
- assert p.coefficients()[0] == s
- # The dealer keeps this polynomial secret but publishes the related
- # commitments C_j = g ^ {a_j} , for 0 ≤ j < t
- C = []
- for j in range(t):
- C.append(g * alpha[j])
- # The dealer also publishes the encrypted shares Y_i = y_i ^ p(i),
- # for 1 ≤ i ≤ n , using the public keys of the participants
- Y = []
- for i in range(1, n+1):
- Y.append(y[i-1] * p(i))
- # Finally, let X_i = prod_{j=0}^{t-1} C_j * i^j. The verifier computes
- # this from the published commitments.
- X = []
- for i in range(1, n+1):
- X_i = Ep(0)
- for j in range(t):
- X_i += C[j] * (i^j)
- X.append(X_i)
- # The dealer shows that the encrypted shares are consistent by
- # producing a proof of knowledge of the unique p(i), 1 ≤ i ≤ n,
- # satisfying: X_i = g^p(i) , Y_i = y_i^p(i)
- for i in range(1, n+1):
- assert X[i-1] == g * p(i)
- assert Y[i-1] == y[i-1] * p(i)
- # For a non-interactive proof, we can use the Fiat-Shamir technique.
- # TODO: See DLEQ in the paper.
- # ==============
- # Reconstruction
- # ==============
- # Using its private key x_i, each participant finds the share S_i = G^p(i)
- # from Y_i by computing S_i = Y_i^(1/x_i). They publish S_i plus a proof
- # that the value S_i is a correct decryption of Y_i. To this end it
- # suffices to prove knowledge of an alpha such that y_i = G^alpha and
- # Y_i = S_i^alpha, which is accomplished by the non-interactive version
- # of the protocol DLEQ(G, y_i, S_i, Y_i).
- S = []
- for i in range(n):
- S_i = Y[i] * (1 / x[i])
- assert S_i == G * p(i+1)
- S.append(S_i)
- # Pooling the shares. Sample a set of t shares and reconstruct secret.
- # FIXME: This is for 1,...,t, we should be able to take random ones.
- shares = S[:t]
- pooled = Ep(0)
- def lambda_func(i, t):
- lambda_i = Fq(1)
- for j in range(1, t+1):
- if j != i:
- lambda_i *= Fq(j) * (Fq(j-i))**(-1)
- return lambda_i
- for i in range(t):
- pooled += shares[i] * lambda_func(i+1, t)
- # Assert reconstructed secret
- assert G*s == G*p(0) == pooled
|