Skip to content

Commit

Permalink
Implements Shamir and Feldman secret sharing.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jul 25, 2022
1 parent 5170e38 commit 50afb71
Show file tree
Hide file tree
Showing 4 changed files with 420 additions and 0 deletions.
73 changes: 73 additions & 0 deletions group/secretsharing/poly.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package secretsharing

import (
"errors"
"io"

"github.com/cloudflare/circl/group"
)

type polynomial struct {
deg uint64
coeff []group.Scalar
}

func randomPolynomial(rnd io.Reader, g group.Group, deg uint64) (p polynomial) {
p = polynomial{deg, make([]group.Scalar, deg+1)}

for i := 0; i <= int(deg); i++ {
p.coeff[i] = g.RandomScalar(rnd)
}
return
}

func (p polynomial) evaluate(x group.Scalar) group.Scalar {
px := p.coeff[p.deg].Copy()
for i := int(p.deg) - 1; i >= 0; i-- {
px.Mul(px, x)
px.Add(px, p.coeff[i])
}
return px
}

func LagrangeCoefficient(g group.Group, x []group.Scalar, index uint64) group.Scalar {
if index > uint64(len(x)) {
panic("invalid parameter")
}

num := g.NewScalar()
num.SetUint64(1)
den := g.NewScalar()
den.SetUint64(1)
tmp := g.NewScalar()

for j := range x {
if j != int(index) {
num.Mul(num, x[j])
den.Mul(den, tmp.Sub(x[j], x[index]))
}
}

return num.Mul(num, tmp.Inv(den))
}

func LagrangeInterpolate(g group.Group, x, px []group.Scalar) (group.Scalar, error) {
if len(x) != len(px) {
return nil, errors.New("lagrange: bad input length")
}

zero := g.NewScalar()
for i := range x {
if x[i].IsEqual(zero) {
return nil, errors.New("lagrange: tried to evaluate on zero")
}
}

pol0 := g.NewScalar()
delta := g.NewScalar()
for i := range x {
pol0.Add(pol0, delta.Mul(px[i], LagrangeCoefficient(g, x, uint64(i))))
}

return pol0, nil
}
60 changes: 60 additions & 0 deletions group/secretsharing/poly_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package secretsharing

import (
"testing"

"github.com/cloudflare/circl/group"
"github.com/cloudflare/circl/internal/test"
)

func TestPolyEval(t *testing.T) {
g := group.P256
p := polynomial{2, []group.Scalar{
g.NewScalar(),
g.NewScalar(),
g.NewScalar(),
}}
p.coeff[0].SetUint64(5)
p.coeff[1].SetUint64(5)
p.coeff[2].SetUint64(2)

x := g.NewScalar()
x.SetUint64(10)

got := p.evaluate(x)

want := g.NewScalar()
want.SetUint64(255)
if !got.IsEqual(want) {
test.ReportError(t, got, want)
}
}

func TestLagrange(t *testing.T) {
g := group.P256
p := polynomial{2, []group.Scalar{
g.NewScalar(),
g.NewScalar(),
g.NewScalar(),
}}
p.coeff[0].SetUint64(1234)
p.coeff[1].SetUint64(166)
p.coeff[2].SetUint64(94)

x := []group.Scalar{g.NewScalar(), g.NewScalar(), g.NewScalar()}
px := []group.Scalar{g.NewScalar(), g.NewScalar(), g.NewScalar()}
x[0].SetUint64(2)
px[0].SetUint64(1942)
x[1].SetUint64(4)
px[1].SetUint64(3402)
x[2].SetUint64(5)
px[2].SetUint64(4414)

got, err := LagrangeInterpolate(g, x, px)
test.CheckNoErr(t, err, "failed interpolation")
want := p.coeff[0]

if !got.IsEqual(want) {
test.ReportError(t, got, want)
}
}
138 changes: 138 additions & 0 deletions group/secretsharing/ss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Package secretsharing provides methods to split a secret in shares.
//
// Shamir's secret sharing: https://dl.acm.org/doi/10.1145/359168.359176
//
// Feldman's verifiable secret sharing: https://ieeexplore.ieee.org/document/4568297
package secretsharing

import (
"errors"
"fmt"
"io"

"github.com/cloudflare/circl/group"
)

type SecretShare struct {
ID uint64
Share group.Scalar
}

// SecretSharing implements Shamir's secret sharing.
type SecretSharing interface {
Shard(rnd io.Reader, secret group.Scalar) []SecretShare
Recover(shares []SecretShare) (secret group.Scalar, err error)
}

type secretSharing struct {
G group.Group
T, N uint64
}

// New returns a struct implementing SecretSharing interface.
func New(g group.Group, t, n uint64) (secretSharing, error) {
if !(0 < t && t <= n) || g == nil {
return secretSharing{}, errors.New("secretsharing: bad parameters")
}
ss := secretSharing{G: g, T: t, N: n}
var _ SecretSharing = ss // checking at compile-time
return ss, nil
}

func (s secretSharing) polyFromSecret(rnd io.Reader, secret group.Scalar) (p polynomial) {
p = randomPolynomial(rnd, s.G, s.T)
p.coeff[0] = secret.Copy()
return
}

func (s secretSharing) generateShares(poly polynomial) []SecretShare {
shares := make([]SecretShare, s.N)
x := s.G.NewScalar()
for i := range shares {
id := uint64(i + 1)
x.SetUint64(id)
shares[i].ID = id
shares[i].Share = poly.evaluate(x)
}

return shares
}

func (s secretSharing) Shard(rnd io.Reader, secret group.Scalar) []SecretShare {
return s.generateShares(s.polyFromSecret(rnd, secret))
}

func (s secretSharing) Recover(shares []SecretShare) (group.Scalar, error) {
if l := len(shares); l <= int(s.T) {
return nil, fmt.Errorf("secretsharing: do not met threshold %v with %v shares", s.T, l)
} else if l > int(s.N) {
return nil, fmt.Errorf("secretsharing: %v shares above max number of shares %v", l, s.N)
}

x := make([]group.Scalar, len(shares))
px := make([]group.Scalar, len(shares))
for i := range shares {
x[i] = s.G.NewScalar()
x[i].SetUint64(shares[i].ID)
px[i] = shares[i].Share
}

return LagrangeInterpolate(s.G, x, px)
}

type SharesCommitment = []group.Element

type verifiableSecretSharing struct {
s secretSharing
}

// SecretSharing implements Feldman's secret sharing.
type VerifiableSecretSharing interface {
Shard(rnd io.Reader, secret group.Scalar) ([]SecretShare, SharesCommitment)
Recover(shares []SecretShare) (secret group.Scalar, err error)
Verify(share SecretShare, coms SharesCommitment) bool
}

// New returns a struct implementing VerifiableSecretSharing interface.
func NewVerifiable(g group.Group, t, n uint64) (verifiableSecretSharing, error) {
s, err := New(g, t, n)
vs := verifiableSecretSharing{s}
var _ verifiableSecretSharing = vs // checking at compile-time
return vs, err
}

func (v verifiableSecretSharing) Shard(rnd io.Reader, secret group.Scalar) ([]SecretShare, SharesCommitment) {
poly := v.s.polyFromSecret(rnd, secret)
shares := v.s.generateShares(poly)

vecComm := make(SharesCommitment, v.s.T+1)
for i, ki := range poly.coeff {
vecComm[i] = v.s.G.NewElement()
vecComm[i].MulGen(ki)
}

return shares, vecComm
}

func (v verifiableSecretSharing) Verify(s SecretShare, c SharesCommitment) bool {
if len(c) != int(v.s.T+1) {
return false
}

polI := v.s.G.NewElement().MulGen(s.Share)

lc := len(c) - 1
sum := c[lc].Copy()
x := v.s.G.NewScalar()
for i := lc - 1; i >= 0; i-- {
x.SetUint64(s.ID)
sum.Mul(sum, x)
sum.Add(sum, c[i])
}

return polI.IsEqual(sum)
}

func (v verifiableSecretSharing) Recover(shares []SecretShare) (group.Scalar, error) {
return v.s.Recover(shares)
}
Loading

0 comments on commit 50afb71

Please sign in to comment.