Skip to content

Commit 50afb71

Browse files
committed
Implements Shamir and Feldman secret sharing.
1 parent 5170e38 commit 50afb71

File tree

4 files changed

+420
-0
lines changed

4 files changed

+420
-0
lines changed

group/secretsharing/poly.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package secretsharing
2+
3+
import (
4+
"errors"
5+
"io"
6+
7+
"github.com/cloudflare/circl/group"
8+
)
9+
10+
type polynomial struct {
11+
deg uint64
12+
coeff []group.Scalar
13+
}
14+
15+
func randomPolynomial(rnd io.Reader, g group.Group, deg uint64) (p polynomial) {
16+
p = polynomial{deg, make([]group.Scalar, deg+1)}
17+
18+
for i := 0; i <= int(deg); i++ {
19+
p.coeff[i] = g.RandomScalar(rnd)
20+
}
21+
return
22+
}
23+
24+
func (p polynomial) evaluate(x group.Scalar) group.Scalar {
25+
px := p.coeff[p.deg].Copy()
26+
for i := int(p.deg) - 1; i >= 0; i-- {
27+
px.Mul(px, x)
28+
px.Add(px, p.coeff[i])
29+
}
30+
return px
31+
}
32+
33+
func LagrangeCoefficient(g group.Group, x []group.Scalar, index uint64) group.Scalar {
34+
if index > uint64(len(x)) {
35+
panic("invalid parameter")
36+
}
37+
38+
num := g.NewScalar()
39+
num.SetUint64(1)
40+
den := g.NewScalar()
41+
den.SetUint64(1)
42+
tmp := g.NewScalar()
43+
44+
for j := range x {
45+
if j != int(index) {
46+
num.Mul(num, x[j])
47+
den.Mul(den, tmp.Sub(x[j], x[index]))
48+
}
49+
}
50+
51+
return num.Mul(num, tmp.Inv(den))
52+
}
53+
54+
func LagrangeInterpolate(g group.Group, x, px []group.Scalar) (group.Scalar, error) {
55+
if len(x) != len(px) {
56+
return nil, errors.New("lagrange: bad input length")
57+
}
58+
59+
zero := g.NewScalar()
60+
for i := range x {
61+
if x[i].IsEqual(zero) {
62+
return nil, errors.New("lagrange: tried to evaluate on zero")
63+
}
64+
}
65+
66+
pol0 := g.NewScalar()
67+
delta := g.NewScalar()
68+
for i := range x {
69+
pol0.Add(pol0, delta.Mul(px[i], LagrangeCoefficient(g, x, uint64(i))))
70+
}
71+
72+
return pol0, nil
73+
}

group/secretsharing/poly_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package secretsharing
2+
3+
import (
4+
"testing"
5+
6+
"github.com/cloudflare/circl/group"
7+
"github.com/cloudflare/circl/internal/test"
8+
)
9+
10+
func TestPolyEval(t *testing.T) {
11+
g := group.P256
12+
p := polynomial{2, []group.Scalar{
13+
g.NewScalar(),
14+
g.NewScalar(),
15+
g.NewScalar(),
16+
}}
17+
p.coeff[0].SetUint64(5)
18+
p.coeff[1].SetUint64(5)
19+
p.coeff[2].SetUint64(2)
20+
21+
x := g.NewScalar()
22+
x.SetUint64(10)
23+
24+
got := p.evaluate(x)
25+
26+
want := g.NewScalar()
27+
want.SetUint64(255)
28+
if !got.IsEqual(want) {
29+
test.ReportError(t, got, want)
30+
}
31+
}
32+
33+
func TestLagrange(t *testing.T) {
34+
g := group.P256
35+
p := polynomial{2, []group.Scalar{
36+
g.NewScalar(),
37+
g.NewScalar(),
38+
g.NewScalar(),
39+
}}
40+
p.coeff[0].SetUint64(1234)
41+
p.coeff[1].SetUint64(166)
42+
p.coeff[2].SetUint64(94)
43+
44+
x := []group.Scalar{g.NewScalar(), g.NewScalar(), g.NewScalar()}
45+
px := []group.Scalar{g.NewScalar(), g.NewScalar(), g.NewScalar()}
46+
x[0].SetUint64(2)
47+
px[0].SetUint64(1942)
48+
x[1].SetUint64(4)
49+
px[1].SetUint64(3402)
50+
x[2].SetUint64(5)
51+
px[2].SetUint64(4414)
52+
53+
got, err := LagrangeInterpolate(g, x, px)
54+
test.CheckNoErr(t, err, "failed interpolation")
55+
want := p.coeff[0]
56+
57+
if !got.IsEqual(want) {
58+
test.ReportError(t, got, want)
59+
}
60+
}

group/secretsharing/ss.go

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Package secretsharing provides methods to split a secret in shares.
2+
//
3+
// Shamir's secret sharing: https://dl.acm.org/doi/10.1145/359168.359176
4+
//
5+
// Feldman's verifiable secret sharing: https://ieeexplore.ieee.org/document/4568297
6+
package secretsharing
7+
8+
import (
9+
"errors"
10+
"fmt"
11+
"io"
12+
13+
"github.com/cloudflare/circl/group"
14+
)
15+
16+
type SecretShare struct {
17+
ID uint64
18+
Share group.Scalar
19+
}
20+
21+
// SecretSharing implements Shamir's secret sharing.
22+
type SecretSharing interface {
23+
Shard(rnd io.Reader, secret group.Scalar) []SecretShare
24+
Recover(shares []SecretShare) (secret group.Scalar, err error)
25+
}
26+
27+
type secretSharing struct {
28+
G group.Group
29+
T, N uint64
30+
}
31+
32+
// New returns a struct implementing SecretSharing interface.
33+
func New(g group.Group, t, n uint64) (secretSharing, error) {
34+
if !(0 < t && t <= n) || g == nil {
35+
return secretSharing{}, errors.New("secretsharing: bad parameters")
36+
}
37+
ss := secretSharing{G: g, T: t, N: n}
38+
var _ SecretSharing = ss // checking at compile-time
39+
return ss, nil
40+
}
41+
42+
func (s secretSharing) polyFromSecret(rnd io.Reader, secret group.Scalar) (p polynomial) {
43+
p = randomPolynomial(rnd, s.G, s.T)
44+
p.coeff[0] = secret.Copy()
45+
return
46+
}
47+
48+
func (s secretSharing) generateShares(poly polynomial) []SecretShare {
49+
shares := make([]SecretShare, s.N)
50+
x := s.G.NewScalar()
51+
for i := range shares {
52+
id := uint64(i + 1)
53+
x.SetUint64(id)
54+
shares[i].ID = id
55+
shares[i].Share = poly.evaluate(x)
56+
}
57+
58+
return shares
59+
}
60+
61+
func (s secretSharing) Shard(rnd io.Reader, secret group.Scalar) []SecretShare {
62+
return s.generateShares(s.polyFromSecret(rnd, secret))
63+
}
64+
65+
func (s secretSharing) Recover(shares []SecretShare) (group.Scalar, error) {
66+
if l := len(shares); l <= int(s.T) {
67+
return nil, fmt.Errorf("secretsharing: do not met threshold %v with %v shares", s.T, l)
68+
} else if l > int(s.N) {
69+
return nil, fmt.Errorf("secretsharing: %v shares above max number of shares %v", l, s.N)
70+
}
71+
72+
x := make([]group.Scalar, len(shares))
73+
px := make([]group.Scalar, len(shares))
74+
for i := range shares {
75+
x[i] = s.G.NewScalar()
76+
x[i].SetUint64(shares[i].ID)
77+
px[i] = shares[i].Share
78+
}
79+
80+
return LagrangeInterpolate(s.G, x, px)
81+
}
82+
83+
type SharesCommitment = []group.Element
84+
85+
type verifiableSecretSharing struct {
86+
s secretSharing
87+
}
88+
89+
// SecretSharing implements Feldman's secret sharing.
90+
type VerifiableSecretSharing interface {
91+
Shard(rnd io.Reader, secret group.Scalar) ([]SecretShare, SharesCommitment)
92+
Recover(shares []SecretShare) (secret group.Scalar, err error)
93+
Verify(share SecretShare, coms SharesCommitment) bool
94+
}
95+
96+
// New returns a struct implementing VerifiableSecretSharing interface.
97+
func NewVerifiable(g group.Group, t, n uint64) (verifiableSecretSharing, error) {
98+
s, err := New(g, t, n)
99+
vs := verifiableSecretSharing{s}
100+
var _ verifiableSecretSharing = vs // checking at compile-time
101+
return vs, err
102+
}
103+
104+
func (v verifiableSecretSharing) Shard(rnd io.Reader, secret group.Scalar) ([]SecretShare, SharesCommitment) {
105+
poly := v.s.polyFromSecret(rnd, secret)
106+
shares := v.s.generateShares(poly)
107+
108+
vecComm := make(SharesCommitment, v.s.T+1)
109+
for i, ki := range poly.coeff {
110+
vecComm[i] = v.s.G.NewElement()
111+
vecComm[i].MulGen(ki)
112+
}
113+
114+
return shares, vecComm
115+
}
116+
117+
func (v verifiableSecretSharing) Verify(s SecretShare, c SharesCommitment) bool {
118+
if len(c) != int(v.s.T+1) {
119+
return false
120+
}
121+
122+
polI := v.s.G.NewElement().MulGen(s.Share)
123+
124+
lc := len(c) - 1
125+
sum := c[lc].Copy()
126+
x := v.s.G.NewScalar()
127+
for i := lc - 1; i >= 0; i-- {
128+
x.SetUint64(s.ID)
129+
sum.Mul(sum, x)
130+
sum.Add(sum, c[i])
131+
}
132+
133+
return polI.IsEqual(sum)
134+
}
135+
136+
func (v verifiableSecretSharing) Recover(shares []SecretShare) (group.Scalar, error) {
137+
return v.s.Recover(shares)
138+
}

0 commit comments

Comments
 (0)