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 }