Skip to content

Commit

Permalink
Add splitter of secrets.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Oct 19, 2022
1 parent a480114 commit b0a42cb
Showing 1 changed file with 74 additions and 50 deletions.
124 changes: 74 additions & 50 deletions secretsharing/ss.go
Expand Up @@ -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))
Expand All @@ -95,42 +74,39 @@ 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
// 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, 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
Expand All @@ -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)
}

0 comments on commit b0a42cb

Please sign in to comment.