Files
tss/crypto/vss/feldman_vss.go
2025-08-02 15:20:14 -05:00

176 lines
4.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright © 2019 Binance
//
// This file is part of Binance. The full Binance copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.
// Feldman VSS, based on Paul Feldman, 1987., A practical scheme for non-interactive verifiable secret sharing.
// In Foundations of Computer Science, 1987., 28th Annual Symposium on. IEEE, 427–43
//
package vss
import (
"crypto/elliptic"
"errors"
"fmt"
"io"
"math/big"
"github.com/luxfi/tss/v2/common"
"github.com/luxfi/tss/v2/crypto"
)
type (
Share struct {
Threshold int
ID, // xi
Share *big.Int // Sigma i
}
Vs []*crypto.ECPoint // v0..vt
Shares []*Share
)
var (
ErrNumSharesBelowThreshold = fmt.Errorf("not enough shares to satisfy the threshold")
zero = big.NewInt(0)
one = big.NewInt(1)
)
// Check share ids of Shamir's Secret Sharing, return error if duplicate or 0 value found
func CheckIndexes(ec elliptic.Curve, indexes []*big.Int) ([]*big.Int, error) {
visited := make(map[string]struct{})
for _, v := range indexes {
vMod := new(big.Int).Mod(v, ec.Params().N)
if vMod.Cmp(zero) == 0 {
return nil, errors.New("party index should not be 0")
}
vModStr := vMod.String()
if _, ok := visited[vModStr]; ok {
return nil, fmt.Errorf("duplicate indexes %s", vModStr)
}
visited[vModStr] = struct{}{}
}
return indexes, nil
}
// Returns a new array of secret shares created by Shamir's Secret Sharing Algorithm,
// requiring a minimum number of shares to recreate, of length shares, from the input secret
func Create(ec elliptic.Curve, threshold int, secret *big.Int, indexes []*big.Int, rand io.Reader) (Vs, Shares, error) {
if secret == nil || indexes == nil {
return nil, nil, fmt.Errorf("vss secret or indexes == nil: %v %v", secret, indexes)
}
if threshold < 1 {
return nil, nil, errors.New("vss threshold < 1")
}
ids, err := CheckIndexes(ec, indexes)
if err != nil {
return nil, nil, err
}
num := len(indexes)
if num < threshold {
return nil, nil, ErrNumSharesBelowThreshold
}
poly := samplePolynomial(ec, threshold, secret, rand)
v := make(Vs, len(poly))
for i, ai := range poly {
v[i] = crypto.ScalarBaseMult(ec, ai)
}
shares := make(Shares, num)
for i := 0; i < num; i++ {
share := evaluatePolynomial(ec, threshold, poly, ids[i])
shares[i] = &Share{Threshold: threshold, ID: ids[i], Share: share}
}
return v, shares, nil
}
func (share *Share) Verify(ec elliptic.Curve, threshold int, vs Vs) bool {
if share.Threshold != threshold || vs == nil || len(vs) != threshold+1 {
return false
}
var err error
modQ := common.ModInt(ec.Params().N)
v, t := vs[0], one // YRO : we need to have our accumulator outside of the loop
for j := 1; j <= threshold; j++ {
// t = k_i^j
t = modQ.Mul(t, share.ID)
// v = v * v_j^t
vjt := vs[j].SetCurve(ec).ScalarMult(t)
v, err = v.SetCurve(ec).Add(vjt)
if err != nil {
return false
}
}
sigmaGi := crypto.ScalarBaseMult(ec, share.Share)
return sigmaGi.Equals(v)
}
func (shares Shares) ReConstruct(ec elliptic.Curve) (secret *big.Int, err error) {
if shares != nil && shares[0].Threshold > len(shares) {
return nil, ErrNumSharesBelowThreshold
}
modN := common.ModInt(ec.Params().N)
// x coords
xs := make([]*big.Int, 0)
for _, share := range shares {
xs = append(xs, share.ID)
}
secret = zero
for i, share := range shares {
times := one
for j := 0; j < len(xs); j++ {
if j == i {
continue
}
sub := modN.Sub(xs[j], share.ID)
subInv := modN.ModInverse(sub)
div := modN.Mul(xs[j], subInv)
times = modN.Mul(times, div)
}
fTimes := modN.Mul(share.Share, times)
secret = modN.Add(secret, fTimes)
}
return secret, nil
}
func samplePolynomial(ec elliptic.Curve, threshold int, secret *big.Int, rand io.Reader) []*big.Int {
q := ec.Params().N
v := make([]*big.Int, threshold+1)
v[0] = secret
for i := 1; i <= threshold; i++ {
ai := common.GetRandomPositiveInt(rand, q)
v[i] = ai
}
return v
}
// Evauluates a polynomial with coefficients such that:
// evaluatePolynomial([a, b, c, d], x):
//
// returns a + bx + cx^2 + dx^3
func evaluatePolynomial(ec elliptic.Curve, threshold int, v []*big.Int, id *big.Int) (result *big.Int) {
q := ec.Params().N
modQ := common.ModInt(q)
result = new(big.Int).Set(v[0])
X := big.NewInt(int64(1))
for i := 1; i <= threshold; i++ {
ai := v[i]
X = modQ.Mul(X, id)
aiXi := new(big.Int).Mul(ai, X)
result = modQ.Add(result, aiXi)
}
return
}