diff --git a/secretsharing/ss.go b/secretsharing/ss.go index 9573ddf0..d23da9d1 100644 --- a/secretsharing/ss.go +++ b/secretsharing/ss.go @@ -32,49 +32,28 @@ import ( type Share struct { // ID uniquely identifies a share in a secret sharing instance. ID group.Scalar - // Value stores the share generated from a secret sharing instance. + // Value stores the share generated by a secret sharing instance. Value group.Scalar } +// SecretSharing provides a (t,n) Shamir's secret sharing. It allows splitting +// a secret into n shares, such that the secret can be only recovered from +// any subset of t+1 shares. type SecretSharing struct { 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. +// NewShamirSecretSharing implements a (t,n) Shamir's secret sharing with +// threshold t. func NewShamirSecretSharing(t uint) SecretSharing { return SecretSharing{t} } -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] = g.RandomScalar(rnd) - } - return polynomial.New(c) -} - -// Shard splits the secret into n shares. +// Shard splits a 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 := g.NewScalar().SetUint64(uint64(i + 1)) - shares[i] = Share{ID: id, Value: poly.Evaluate(id)} - } - - return shares, nil + return NewSplitter(rnd, s.t, secret).multipleShard(n) } // Recover returns the secret provided more than t shares are given. Returns an -// error if the number of shares is not above the threshold or goes beyond the -// maximum number of shares. +// error if the number of shares is not above the threshold t. func (s SecretSharing) Recover(shares []Share) (group.Scalar, error) { if l := len(shares); l <= int(s.t) { return nil, errThreshold(s.t, uint(l)) @@ -95,13 +74,15 @@ func (s SecretSharing) Recover(shares []Share) (group.Scalar, error) { type SharesCommitment = []group.Element +// VerifiableSecretSharing provides a (t,n) Feldman's verifiable secret sharing. +// It allows splitting a secret into n shares, such 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. 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. It's -// verifiable as it allows checking whether a share is part of a secret committed -// during sharding. +// sharing with threshold t. 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 @@ -109,28 +90,23 @@ func NewFeldmanSecretSharing(t uint) (v VerifiableSecretSharing) { v.s.t = t; re // 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, n uint) ([]Share, SharesCommitment, error) { - if n <= v.s.t { - return nil, nil, errThreshold(v.s.t, n) + splitter := NewSplitter(rnd, v.s.t, secret) + shares, err := splitter.multipleShard(n) + if err != nil { + return nil, nil, err } g := secret.Group() - poly := v.s.polyFromSecret(rnd, secret) - 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) + shareComs := make(SharesCommitment, splitter.poly.Degree()+1) for i := range shareComs { - shareComs[i] = g.NewElement().MulGen(poly.Coefficient(uint(i))) + shareComs[i] = g.NewElement().MulGen(splitter.poly.Coefficient(uint(i))) } return shares, shareComs, nil } -// Verify returns true if a share was produced by sharding a secret. It uses -// the share commitments generated by the Shard function to verify this -// property. +// Verify returns true if a share was produced by sharding a secret. It uses the +// share commitments generated by the Shard function. func (v VerifiableSecretSharing) Verify(s Share, c SharesCommitment) bool { if len(c) != int(v.s.t+1) { return false @@ -148,12 +124,60 @@ func (v VerifiableSecretSharing) Verify(s Share, c SharesCommitment) bool { } // Recover returns the secret provided more than t shares are given. Returns an -// error if the number of shares is not above the threshold (t) or is larger -// than the maximum number of shares (n). +// error if the number of shares is not above the threshold t. func (v VerifiableSecretSharing) Recover(shares []Share) (group.Scalar, error) { return v.s.Recover(shares) } -var errThreshold = func(t, n uint) error { +type Splitter struct { + g group.Group + t uint + poly polynomial.Polynomial +} + +// NewSplitter returns a Splitter that can shard a secret with threshold t. +func NewSplitter(rnd io.Reader, t uint, secret group.Scalar) (sp Splitter) { + sp.g = secret.Group() + sp.t = t + + c := make([]group.Scalar, sp.t+1) + c[0] = secret.Copy() + for i := 1; i < len(c); i++ { + c[i] = sp.g.RandomScalar(rnd) + } + sp.poly = polynomial.New(c) + + return +} + +func (sp Splitter) multipleShard(n uint) ([]Share, error) { + if n <= sp.t { + return nil, errThreshold(sp.t, n) + } + + shares := make([]Share, n) + id := sp.g.NewScalar() + for i := range shares { + shares[i] = sp.ShardWithID(id.SetUint64(uint64(i + 1))) + } + + return shares, nil +} + +func (sp Splitter) Shard(rnd io.Reader) (s Share) { + return sp.ShardWithID(sp.g.RandomNonZeroScalar(rnd)) +} + +func (sp Splitter) ShardWithID(id group.Scalar) (s Share) { + if id.IsZero() { + panic("secretsharing: id cannot be zero") + } + + s.ID = id.Copy() + s.Value = sp.poly.Evaluate(s.ID) + return +} + +func errThreshold(t, n uint) error { return fmt.Errorf("secretsharing: number of shares (n=%v) must be above the threshold (t=%v)", n, t) }