diff --git a/secretsharing/ss.go b/secretsharing/ss.go index d89877a2a..9573ddf07 100644 --- a/secretsharing/ss.go +++ b/secretsharing/ss.go @@ -21,7 +21,6 @@ package secretsharing import ( - "errors" "fmt" "io" @@ -31,54 +30,46 @@ import ( // Share represents a share of a secret. type Share struct { - ID uint64 // ID uniquely identifies a share in a secret sharing instance. - Value group.Scalar // Value stores the share generated from a secret sharing instance. + // ID uniquely identifies a share in a secret sharing instance. + ID group.Scalar + // Value stores the share generated from a secret sharing instance. + Value group.Scalar } type SecretSharing struct { - g group.Group - t, n uint + t uint // t is the threshold. } // NewShamirSecretSharing implements a (t,n) Shamir's secret sharing. // A (t,n) secret sharing allows to split a secret into n shares, such that the -// secret can be only recovered from any subset of t+1 shares. Returns an error -// if 0 <= t < n does not hold. -func NewShamirSecretSharing(g group.Group, t, n uint) (SecretSharing, error) { - if t >= n { - return SecretSharing{}, errors.New("secretsharing: bad parameters") - } - return SecretSharing{g: g, t: t, n: n}, nil -} - -// Params returns the t and n parameters of the secret sharing. -func (s SecretSharing) Params() (t, n uint) { return s.t, s.n } +// secret can be only recovered from any subset of t+1 shares. +func NewShamirSecretSharing(t uint) SecretSharing { return SecretSharing{t} } -func (s SecretSharing) polyFromSecret(rnd io.Reader, secret group.Scalar) (p polynomial.Polynomial) { +func (s SecretSharing) polyFromSecret(rnd io.Reader, secret group.Scalar) polynomial.Polynomial { c := make([]group.Scalar, s.t+1) + g := secret.Group() + c[0] = secret.Copy() for i := 1; i < len(c); i++ { - c[i] = s.g.RandomScalar(rnd) + c[i] = g.RandomScalar(rnd) } - c[0] = secret.Copy() return polynomial.New(c) } -func (s SecretSharing) generateShares(poly polynomial.Polynomial) []Share { - shares := make([]Share, s.n) - x := s.g.NewScalar() +// Shard splits the secret into n shares. +func (s SecretSharing) Shard(rnd io.Reader, secret group.Scalar, n uint) ([]Share, error) { + if n <= s.t { + return nil, errThreshold(s.t, n) + } + + g := secret.Group() + poly := s.polyFromSecret(rnd, secret) + shares := make([]Share, n) for i := range shares { - id := i + 1 - x.SetUint64(uint64(id)) - shares[i].ID = uint64(id) - shares[i].Value = poly.Evaluate(x) + id := g.NewScalar().SetUint64(uint64(i + 1)) + shares[i] = Share{ID: id, Value: poly.Evaluate(id)} } - return shares -} - -// Shard splits the secret into n shares. -func (s SecretSharing) Shard(rnd io.Reader, secret group.Scalar) []Share { - return s.generateShares(s.polyFromSecret(rnd, secret)) + return shares, nil } // Recover returns the secret provided more than t shares are given. Returns an @@ -86,20 +77,18 @@ func (s SecretSharing) Shard(rnd io.Reader, secret group.Scalar) []Share { // maximum number of shares. func (s SecretSharing) Recover(shares []Share) (group.Scalar, error) { if l := len(shares); l <= int(s.t) { - return nil, fmt.Errorf("secretsharing: does not reach the threshold %v with %v shares", s.t, l) - } else if l > int(s.n) { - return nil, fmt.Errorf("secretsharing: %v shares above max number of shares %v", l, s.n) + return nil, errThreshold(s.t, uint(l)) } x := make([]group.Scalar, s.t+1) px := make([]group.Scalar, s.t+1) for i := range shares[:s.t+1] { - x[i] = s.g.NewScalar().SetUint64(shares[i].ID) + x[i] = shares[i].ID px[i] = shares[i].Value } l := polynomial.NewLagrangePolynomial(x, px) - zero := s.g.NewScalar() + zero := shares[0].ID.Group().NewScalar() return l.Evaluate(zero), nil } @@ -110,31 +99,33 @@ type VerifiableSecretSharing struct{ s SecretSharing } // NewFeldmanSecretSharing implements a (t,n) Feldman's verifiable secret // sharing. A (t,n) secret sharing allows to split a secret into n shares, such -// that the secret can be only recovered from any subset of t+1 shares. This -// method is verifiable because once the shares and the secret are committed -// during sharding, one can later verify whether the share was generated -// honestly. Returns an error if 0 < t <= n does not hold. -func NewFeldmanSecretSharing(g group.Group, t, n uint) (VerifiableSecretSharing, error) { - s, err := NewShamirSecretSharing(g, t, n) - return VerifiableSecretSharing{s}, err -} - -// Params returns the t and n parameters of the secret sharing. -func (v VerifiableSecretSharing) Params() (t, n uint) { return v.s.Params() } +// that the secret can be only recovered from any subset of t+1 shares. It's +// verifiable as it allows checking whether a share is part of a secret committed +// during sharding. +func NewFeldmanSecretSharing(t uint) (v VerifiableSecretSharing) { v.s.t = t; return } // Shard splits the secret into n shares, and also returns a commitment to both // the secret and the shares. The ShareCommitment must be sent to each party // so each party can verify its share is correct. Sharding a secret more // than once produces ShareCommitments with the same first entry. -func (v VerifiableSecretSharing) Shard(rnd io.Reader, secret group.Scalar) ([]Share, SharesCommitment) { +func (v VerifiableSecretSharing) Shard(rnd io.Reader, secret group.Scalar, n uint) ([]Share, SharesCommitment, error) { + if n <= v.s.t { + return nil, nil, errThreshold(v.s.t, n) + } + + g := secret.Group() poly := v.s.polyFromSecret(rnd, secret) - shares := v.s.generateShares(poly) + shares := make([]Share, n) + for i := range shares { + id := g.NewScalar().SetUint64(uint64(i + 1)) + shares[i] = Share{ID: id, Value: poly.Evaluate(id)} + } shareComs := make(SharesCommitment, poly.Degree()+1) for i := range shareComs { - shareComs[i] = v.s.g.NewElement().MulGen(poly.Coefficient(uint(i))) + shareComs[i] = g.NewElement().MulGen(poly.Coefficient(uint(i))) } - return shares, shareComs + return shares, shareComs, nil } // Verify returns true if a share was produced by sharding a secret. It uses @@ -145,15 +136,14 @@ func (v VerifiableSecretSharing) Verify(s Share, c SharesCommitment) bool { return false } + g := s.ID.Group() lc := len(c) - 1 - sum := v.s.g.NewElement().Set(c[lc]) - x := v.s.g.NewScalar() + sum := g.NewElement().Set(c[lc]) for i := lc - 1; i >= 0; i-- { - x.SetUint64(s.ID) - sum.Mul(sum, x) + sum.Mul(sum, s.ID) sum.Add(sum, c[i]) } - polI := v.s.g.NewElement().MulGen(s.Value) + polI := g.NewElement().MulGen(s.Value) return polI.IsEqual(sum) } @@ -163,3 +153,7 @@ func (v VerifiableSecretSharing) Verify(s Share, c SharesCommitment) bool { func (v VerifiableSecretSharing) Recover(shares []Share) (group.Scalar, error) { return v.s.Recover(shares) } + +var errThreshold = func(t, n uint) error { + return fmt.Errorf("secretsharing: number of shares (n=%v) must be above the threshold (t=%v)", n, t) +} diff --git a/secretsharing/ss_test.go b/secretsharing/ss_test.go index 1fd09c9c7..d512fa40c 100644 --- a/secretsharing/ss_test.go +++ b/secretsharing/ss_test.go @@ -14,11 +14,11 @@ func TestSecretSharing(tt *testing.T) { t := uint(2) n := uint(5) - s, err := secretsharing.NewShamirSecretSharing(g, t, n) - test.CheckNoErr(tt, err, "failed to create Shamir secret sharing") + s := secretsharing.NewShamirSecretSharing(t) want := g.RandomScalar(rand.Reader) - shares := s.Shard(rand.Reader, want) + shares, err := s.Shard(rand.Reader, want, n) + test.CheckNoErr(tt, err, "failed to shard a secret") test.CheckOk(len(shares) == int(n), "bad num shares", tt) tt.Run("subsetSize", func(ttt *testing.T) { @@ -43,11 +43,11 @@ func TestVerifiableSecretSharing(tt *testing.T) { t := uint(3) n := uint(5) - vs, err := secretsharing.NewFeldmanSecretSharing(g, t, n) - test.CheckNoErr(tt, err, "failed to create Feldman secret sharing") + vs := secretsharing.NewFeldmanSecretSharing(t) want := g.RandomScalar(rand.Reader) - shares, com := vs.Shard(rand.Reader, want) + shares, com, err := vs.Shard(rand.Reader, want, n) + test.CheckNoErr(tt, err, "failed to shard a secret") test.CheckOk(len(shares) == int(n), "bad num shares", tt) test.CheckOk(len(com) == int(t+1), "bad num commitments", tt) @@ -76,6 +76,7 @@ func TestVerifiableSecretSharing(tt *testing.T) { tt.Run("badShares", func(ttt *testing.T) { badShares := make([]secretsharing.Share, len(shares)) for i := range shares { + badShares[i].ID = shares[i].ID.Copy() badShares[i].Value = shares[i].Value.Copy() badShares[i].Value.SetUint64(9) } @@ -103,13 +104,13 @@ func BenchmarkSecretSharing(b *testing.B) { t := uint(3) n := uint(5) - s, _ := secretsharing.NewShamirSecretSharing(g, t, n) + s := secretsharing.NewShamirSecretSharing(t) want := g.RandomScalar(rand.Reader) - shares := s.Shard(rand.Reader, want) + shares, _ := s.Shard(rand.Reader, want, n) b.Run("Shard", func(b *testing.B) { for i := 0; i < b.N; i++ { - s.Shard(rand.Reader, want) + _, _ = s.Shard(rand.Reader, want, n) } }) @@ -125,13 +126,13 @@ func BenchmarkVerifiableSecretSharing(b *testing.B) { t := uint(3) n := uint(5) - vs, _ := secretsharing.NewFeldmanSecretSharing(g, t, n) + vs := secretsharing.NewFeldmanSecretSharing(t) want := g.RandomScalar(rand.Reader) - shares, com := vs.Shard(rand.Reader, want) + shares, com, _ := vs.Shard(rand.Reader, want, n) b.Run("Shard", func(b *testing.B) { for i := 0; i < b.N; i++ { - vs.Shard(rand.Reader, want) + _, _, _ = vs.Shard(rand.Reader, want, n) } })