github.com/cloudflare/circl@v1.5.0/secretsharing/ss.go (about)

     1  // Package secretsharing provides methods to split secrets into shares.
     2  //
     3  // Let n be the number of parties, and t the number of corrupted parties such
     4  // that 0 <= t < n. A (t,n) secret sharing allows to split a secret into n
     5  // shares, such that the secret can be recovered from any subset of at least t+1
     6  // different shares.
     7  //
     8  // A Shamir secret sharing [1] relies on Lagrange polynomial interpolation.
     9  // A Feldman secret sharing [2] extends Shamir's by committing the secret,
    10  // which allows to verify that a share is part of the committed secret.
    11  //
    12  // New returns a SecretSharing compatible with Shamir secret sharing.
    13  // The SecretSharing can be verifiable (compatible with Feldman secret sharing)
    14  // using the CommitSecret and Verify functions.
    15  //
    16  // In this implementation, secret sharing is defined over the scalar field of
    17  // a prime order group.
    18  //
    19  // References
    20  //
    21  //	[1] Shamir, How to share a secret. https://dl.acm.org/doi/10.1145/359168.359176/
    22  //	[2] Feldman, A practical scheme for non-interactive verifiable secret sharing. https://ieeexplore.ieee.org/document/4568297/
    23  package secretsharing
    24  
    25  import (
    26  	"fmt"
    27  	"io"
    28  
    29  	"github.com/cloudflare/circl/group"
    30  	"github.com/cloudflare/circl/math/polynomial"
    31  )
    32  
    33  // Share represents a share of a secret.
    34  type Share struct {
    35  	// ID uniquely identifies a share in a secret sharing instance. ID is never zero.
    36  	ID group.Scalar
    37  	// Value stores the share generated by a secret sharing instance.
    38  	Value group.Scalar
    39  }
    40  
    41  // SecretCommitment is the set of commitments generated by splitting a secret.
    42  type SecretCommitment = []group.Element
    43  
    44  // SecretSharing provides a (t,n) Shamir's secret sharing. It allows splitting
    45  // a secret into n shares, such that the secret can be only recovered from
    46  // any subset of t+1 shares.
    47  type SecretSharing struct {
    48  	g    group.Group
    49  	t    uint
    50  	poly polynomial.Polynomial
    51  }
    52  
    53  // New returns a SecretSharing providing a (t,n) Shamir's secret sharing.
    54  // It allows splitting a secret into n shares, such that the secret is
    55  // only recovered from any subset of at least t+1 shares.
    56  func New(rnd io.Reader, t uint, secret group.Scalar) SecretSharing {
    57  	c := make([]group.Scalar, t+1)
    58  	c[0] = secret.Copy()
    59  	g := secret.Group()
    60  	for i := 1; i < len(c); i++ {
    61  		c[i] = g.RandomScalar(rnd)
    62  	}
    63  
    64  	return SecretSharing{g: g, t: t, poly: polynomial.New(c)}
    65  }
    66  
    67  // Share creates n shares with an ID monotonically increasing from 1 to n.
    68  func (ss SecretSharing) Share(n uint) []Share {
    69  	shares := make([]Share, n)
    70  	id := ss.g.NewScalar()
    71  	for i := range shares {
    72  		shares[i] = ss.ShareWithID(id.SetUint64(uint64(i + 1)))
    73  	}
    74  
    75  	return shares
    76  }
    77  
    78  // ShareWithID creates one share of the secret using the ID as identifier.
    79  // Notice that shares with the same ID are considered equal.
    80  // Panics, if the ID is zero.
    81  func (ss SecretSharing) ShareWithID(id group.Scalar) Share {
    82  	if id.IsZero() {
    83  		panic("secretsharing: id cannot be zero")
    84  	}
    85  
    86  	return Share{
    87  		ID:    id.Copy(),
    88  		Value: ss.poly.Evaluate(id),
    89  	}
    90  }
    91  
    92  // CommitSecret creates a commitment to the secret for further verifying shares.
    93  func (ss SecretSharing) CommitSecret() SecretCommitment {
    94  	c := make(SecretCommitment, ss.poly.Degree()+1)
    95  	for i := range c {
    96  		c[i] = ss.g.NewElement().MulGen(ss.poly.Coefficient(uint(i)))
    97  	}
    98  	return c
    99  }
   100  
   101  // Verify returns true if the share s was produced by sharing a secret with
   102  // threshold t and commitment of the secret c.
   103  func Verify(t uint, s Share, c SecretCommitment) bool {
   104  	if len(c) != int(t+1) {
   105  		return false
   106  	}
   107  	if s.ID.IsZero() {
   108  		return false
   109  	}
   110  
   111  	g := s.ID.Group()
   112  	lc := len(c) - 1
   113  	sum := g.NewElement().Set(c[lc])
   114  	for i := lc - 1; i >= 0; i-- {
   115  		sum.Mul(sum, s.ID)
   116  		sum.Add(sum, c[i])
   117  	}
   118  	polI := g.NewElement().MulGen(s.Value)
   119  	return polI.IsEqual(sum)
   120  }
   121  
   122  // Recover returns a secret provided more than t different shares are given.
   123  // Returns an error if the number of shares is not above the threshold t.
   124  // Panics if some shares are duplicated, i.e., shares must have different IDs.
   125  func Recover(t uint, shares []Share) (secret group.Scalar, err error) {
   126  	if l := len(shares); l <= int(t) {
   127  		return nil, errThreshold(t, uint(l))
   128  	}
   129  
   130  	x := make([]group.Scalar, t+1)
   131  	px := make([]group.Scalar, t+1)
   132  	for i := range shares[:t+1] {
   133  		x[i] = shares[i].ID
   134  		px[i] = shares[i].Value
   135  	}
   136  
   137  	l := polynomial.NewLagrangePolynomial(x, px)
   138  	zero := shares[0].ID.Group().NewScalar()
   139  
   140  	return l.Evaluate(zero), nil
   141  }
   142  
   143  func errThreshold(t, n uint) error {
   144  	return fmt.Errorf("secretsharing: number of shares (n=%v) must be above the threshold (t=%v)", n, t)
   145  }