Skip to content

Commit bbaa64a

Browse files
committed
Major refactoring for simplicity.
1 parent 9e2b5b0 commit bbaa64a

File tree

3 files changed

+153
-163
lines changed

3 files changed

+153
-163
lines changed

secretsharing/example_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package secretsharing_test
2+
3+
import (
4+
"crypto/rand"
5+
"fmt"
6+
7+
"github.com/cloudflare/circl/group"
8+
"github.com/cloudflare/circl/secretsharing"
9+
)
10+
11+
func ExampleSecretSharing() {
12+
g := group.P256
13+
t := uint(2)
14+
n := uint(5)
15+
16+
secret := g.RandomScalar(rand.Reader)
17+
ss := secretsharing.New(rand.Reader, t, secret)
18+
shares := ss.Share(n)
19+
20+
got, err := secretsharing.Recover(t, shares[:t])
21+
fmt.Printf("Recover secret: %v\nError: %v\n", secret.IsEqual(got), err)
22+
23+
got, err = secretsharing.Recover(t, shares[:t+1])
24+
fmt.Printf("Recover secret: %v\nError: %v\n", secret.IsEqual(got), err)
25+
// Output:
26+
// Recover secret: false
27+
// Error: secretsharing: number of shares (n=2) must be above the threshold (t=2)
28+
// Recover secret: true
29+
// Error: <nil>
30+
}
31+
32+
func ExampleVerify() {
33+
g := group.P256
34+
t := uint(2)
35+
n := uint(5)
36+
37+
secret := g.RandomScalar(rand.Reader)
38+
ss := secretsharing.New(rand.Reader, t, secret)
39+
shares := ss.Share(n)
40+
coms := ss.CommitSecret()
41+
42+
for i := range shares {
43+
ok := secretsharing.Verify(t, shares[i], coms)
44+
fmt.Printf("Share %v is valid: %v\n", i, ok)
45+
}
46+
47+
got, err := secretsharing.Recover(t, shares)
48+
fmt.Printf("Recover secret: %v\nError: %v\n", secret.IsEqual(got), err)
49+
// Output:
50+
// Share 0 is valid: true
51+
// Share 1 is valid: true
52+
// Share 2 is valid: true
53+
// Share 3 is valid: true
54+
// Share 4 is valid: true
55+
// Recover secret: true
56+
// Error: <nil>
57+
}

secretsharing/ss.go

+64-97
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@
22
//
33
// Let n be the number of parties, and t the number of corrupted parties such
44
// 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 t+1 shares.
5+
// shares, such that the secret can be recovered from any subset of at least t+1
6+
// different shares.
67
//
7-
// The NewShamirSecretSharing function creates a Shamir secret sharing [1],
8-
// which relies on Lagrange polynomial interpolation.
8+
// A Shamir secret sharing [1] relies on Lagrange polynomial interpolation.
9+
// A Feldman secret sharing [2] extends Shamir's by commiting the secret, which
10+
// allows to verify that a share is part of the committed secret.
911
//
10-
// The NewFeldmanSecretSharing function creates a Feldman secret sharing [2],
11-
// which extends Shamir's by allowing to verify that a share is part of a
12-
// committed secret.
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.
1315
//
1416
// In this implementation, secret sharing is defined over the scalar field of
1517
// a prime order group.
1618
//
1719
// References
1820
//
19-
// [1] https://dl.acm.org/doi/10.1145/359168.359176
20-
// [2] https://ieeexplore.ieee.org/document/4568297
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/
2123
package secretsharing
2224

2325
import (
@@ -30,131 +32,96 @@ import (
3032

3133
// Share represents a share of a secret.
3234
type Share struct {
33-
// ID uniquely identifies a share in a secret sharing instance.
35+
// ID uniquely identifies a share in a secret sharing instance. ID is never zero.
3436
ID group.Scalar
3537
// Value stores the share generated by a secret sharing instance.
3638
Value group.Scalar
3739
}
3840

41+
// SecretCommitment is the set of commitments generated by splitting a secret.
42+
type SecretCommitment = []group.Element
43+
3944
// SecretSharing provides a (t,n) Shamir's secret sharing. It allows splitting
4045
// a secret into n shares, such that the secret can be only recovered from
4146
// any subset of t+1 shares.
4247
type SecretSharing struct {
43-
t uint // t is the threshold.
44-
}
45-
46-
// NewShamirSecretSharing implements a (t,n) Shamir's secret sharing with
47-
// threshold t.
48-
func NewShamirSecretSharing(t uint) SecretSharing { return SecretSharing{t} }
49-
50-
// Shard splits a secret into n shares.
51-
func (s SecretSharing) Shard(rnd io.Reader, secret group.Scalar, n uint) ([]Share, error) {
52-
return NewSplitter(rnd, s.t, secret).multipleShard(n)
53-
}
54-
55-
type SharesCommitment = []group.Element
56-
57-
// VerifiableSecretSharing provides a (t,n) Feldman's verifiable secret sharing.
58-
// It allows splitting a secret into n shares, such that the secret can be only
59-
// recovered from any subset of t+1 shares.
60-
// It's verifiable as it allows checking whether a share is part of a secret
61-
// committed during sharding.
62-
type VerifiableSecretSharing struct{ s SecretSharing }
63-
64-
// NewFeldmanSecretSharing implements a (t,n) Feldman's verifiable secret
65-
// sharing with threshold t.
66-
func NewFeldmanSecretSharing(t uint) (v VerifiableSecretSharing) { v.s.t = t; return }
67-
68-
// Shard splits the secret into n shares, and also returns a commitment to both
69-
// the secret and the shares. The ShareCommitment must be sent to each party
70-
// so each party can verify its share is correct. Sharding a secret more
71-
// than once produces ShareCommitments with the same first entry.
72-
func (v VerifiableSecretSharing) Shard(rnd io.Reader, secret group.Scalar, n uint) ([]Share, SharesCommitment, error) {
73-
splitter := NewSplitter(rnd, v.s.t, secret)
74-
shares, err := splitter.multipleShard(n)
75-
if err != nil {
76-
return nil, nil, err
77-
}
78-
79-
g := secret.Group()
80-
shareComs := make(SharesCommitment, splitter.poly.Degree()+1)
81-
for i := range shareComs {
82-
shareComs[i] = g.NewElement().MulGen(splitter.poly.Coefficient(uint(i)))
83-
}
84-
85-
return shares, shareComs, nil
86-
}
87-
88-
// Verify returns true if a share was produced by sharding a secret. It uses the
89-
// share commitments generated by the Shard function.
90-
func (v VerifiableSecretSharing) Verify(s Share, c SharesCommitment) bool {
91-
if len(c) != int(v.s.t+1) {
92-
return false
93-
}
94-
95-
g := s.ID.Group()
96-
lc := len(c) - 1
97-
sum := g.NewElement().Set(c[lc])
98-
for i := lc - 1; i >= 0; i-- {
99-
sum.Mul(sum, s.ID)
100-
sum.Add(sum, c[i])
101-
}
102-
polI := g.NewElement().MulGen(s.Value)
103-
return polI.IsEqual(sum)
104-
}
105-
106-
type Splitter struct {
10748
g group.Group
10849
t uint
10950
poly polynomial.Polynomial
11051
}
11152

112-
// NewSplitter returns a Splitter that can shard a secret with threshold t.
113-
func NewSplitter(rnd io.Reader, t uint, secret group.Scalar) (sp Splitter) {
114-
sp.g = secret.Group()
115-
sp.t = t
116-
117-
c := make([]group.Scalar, sp.t+1)
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)
11858
c[0] = secret.Copy()
59+
g := secret.Group()
11960
for i := 1; i < len(c); i++ {
120-
c[i] = sp.g.RandomScalar(rnd)
61+
c[i] = g.RandomScalar(rnd)
12162
}
122-
sp.poly = polynomial.New(c)
12363

124-
return
64+
return SecretSharing{g: g, t: t, poly: polynomial.New(c)}
12565
}
12666

127-
func (sp Splitter) Shard(rnd io.Reader) (s Share) {
128-
return sp.ShardWithID(sp.g.RandomNonZeroScalar(rnd))
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
12976
}
13077

131-
func (sp Splitter) ShardWithID(id group.Scalar) (s Share) {
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 {
13282
if id.IsZero() {
13383
panic("secretsharing: id cannot be zero")
13484
}
13585

136-
s.ID = id.Copy()
137-
s.Value = sp.poly.Evaluate(s.ID)
138-
return
86+
return Share{
87+
ID: id.Copy(),
88+
Value: ss.poly.Evaluate(id),
89+
}
13990
}
14091

141-
func (sp Splitter) multipleShard(n uint) ([]Share, error) {
142-
if n <= sp.t {
143-
return nil, errThreshold(sp.t, n)
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)))
14497
}
98+
return c
99+
}
145100

146-
shares := make([]Share, n)
147-
id := sp.g.NewScalar()
148-
for i := range shares {
149-
shares[i] = sp.ShardWithID(id.SetUint64(uint64(i + 1)))
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
150109
}
151110

152-
return shares, nil
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)
153120
}
154121

155122
// Recover returns a secret provided more than t different shares are given.
156123
// Returns an error if the number of shares is not above the threshold t.
157-
// Panics if some shares are duplicated.
124+
// Panics if some shares are duplicated, i.e., shares must have different IDs.
158125
func Recover(t uint, shares []Share) (secret group.Scalar, err error) {
159126
if l := len(shares); l <= int(t) {
160127
return nil, errThreshold(t, uint(l))

0 commit comments

Comments
 (0)