Skip to content

Commit

Permalink
Major refactoring for simplicity.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Nov 24, 2022
1 parent 9e2b5b0 commit bbaa64a
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 163 deletions.
57 changes: 57 additions & 0 deletions secretsharing/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package secretsharing_test

import (
"crypto/rand"
"fmt"

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

func ExampleSecretSharing() {
g := group.P256
t := uint(2)
n := uint(5)

secret := g.RandomScalar(rand.Reader)
ss := secretsharing.New(rand.Reader, t, secret)
shares := ss.Share(n)

got, err := secretsharing.Recover(t, shares[:t])
fmt.Printf("Recover secret: %v\nError: %v\n", secret.IsEqual(got), err)

got, err = secretsharing.Recover(t, shares[:t+1])
fmt.Printf("Recover secret: %v\nError: %v\n", secret.IsEqual(got), err)
// Output:
// Recover secret: false
// Error: secretsharing: number of shares (n=2) must be above the threshold (t=2)
// Recover secret: true
// Error: <nil>
}

func ExampleVerify() {
g := group.P256
t := uint(2)
n := uint(5)

secret := g.RandomScalar(rand.Reader)
ss := secretsharing.New(rand.Reader, t, secret)
shares := ss.Share(n)
coms := ss.CommitSecret()

for i := range shares {
ok := secretsharing.Verify(t, shares[i], coms)
fmt.Printf("Share %v is valid: %v\n", i, ok)
}

got, err := secretsharing.Recover(t, shares)
fmt.Printf("Recover secret: %v\nError: %v\n", secret.IsEqual(got), err)
// Output:
// Share 0 is valid: true
// Share 1 is valid: true
// Share 2 is valid: true
// Share 3 is valid: true
// Share 4 is valid: true
// Recover secret: true
// Error: <nil>
}
161 changes: 64 additions & 97 deletions secretsharing/ss.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@
//
// Let n be the number of parties, and t the number of corrupted parties such
// that 0 <= t < n. A (t,n) secret sharing allows to split a secret into n
// shares, such that the secret can be recovered from any subset of t+1 shares.
// shares, such that the secret can be recovered from any subset of at least t+1
// different shares.
//
// The NewShamirSecretSharing function creates a Shamir secret sharing [1],
// which relies on Lagrange polynomial interpolation.
// A Shamir secret sharing [1] relies on Lagrange polynomial interpolation.
// A Feldman secret sharing [2] extends Shamir's by commiting the secret, which
// allows to verify that a share is part of the committed secret.
//
// The NewFeldmanSecretSharing function creates a Feldman secret sharing [2],
// which extends Shamir's by allowing to verify that a share is part of a
// committed secret.
// New returns a SecretSharing compatible with Shamir secret sharing.
// The SecretSharing can be verifiable (compatible with Feldman secret sharing)
// using the CommitSecret and Verify functions.
//
// In this implementation, secret sharing is defined over the scalar field of
// a prime order group.
//
// References
//
// [1] https://dl.acm.org/doi/10.1145/359168.359176
// [2] https://ieeexplore.ieee.org/document/4568297
// [1] Shamir, How to share a secret. https://dl.acm.org/doi/10.1145/359168.359176/
// [2] Feldman, A practical scheme for non-interactive verifiable secret sharing. https://ieeexplore.ieee.org/document/4568297/
package secretsharing

import (
Expand All @@ -30,131 +32,96 @@ import (

// Share represents a share of a secret.
type Share struct {
// ID uniquely identifies a share in a secret sharing instance.
// ID uniquely identifies a share in a secret sharing instance. ID is never zero.
ID group.Scalar
// Value stores the share generated by a secret sharing instance.
Value group.Scalar
}

// SecretCommitment is the set of commitments generated by splitting a secret.
type SecretCommitment = []group.Element

// SecretSharing provides a (t,n) Shamir's secret sharing. It allows splitting
// a secret into n shares, such that the secret can be only recovered from
// any subset of t+1 shares.
type SecretSharing struct {
t uint // t is the threshold.
}

// NewShamirSecretSharing implements a (t,n) Shamir's secret sharing with
// threshold t.
func NewShamirSecretSharing(t uint) SecretSharing { return SecretSharing{t} }

// Shard splits a secret into n shares.
func (s SecretSharing) Shard(rnd io.Reader, secret group.Scalar, n uint) ([]Share, error) {
return NewSplitter(rnd, s.t, secret).multipleShard(n)
}

type SharesCommitment = []group.Element

// VerifiableSecretSharing provides a (t,n) Feldman's verifiable secret sharing.
// It allows splitting a secret into n shares, such that the secret can be only
// recovered from any subset of t+1 shares.
// It's verifiable as it allows checking whether a share is part of a secret
// committed during sharding.
type VerifiableSecretSharing struct{ s SecretSharing }

// NewFeldmanSecretSharing implements a (t,n) Feldman's verifiable secret
// sharing with threshold t.
func NewFeldmanSecretSharing(t uint) (v VerifiableSecretSharing) { v.s.t = t; return }

// Shard splits the secret into n shares, and also returns a commitment to both
// the secret and the shares. The ShareCommitment must be sent to each party
// so each party can verify its share is correct. Sharding a secret more
// than once produces ShareCommitments with the same first entry.
func (v VerifiableSecretSharing) Shard(rnd io.Reader, secret group.Scalar, n uint) ([]Share, SharesCommitment, error) {
splitter := NewSplitter(rnd, v.s.t, secret)
shares, err := splitter.multipleShard(n)
if err != nil {
return nil, nil, err
}

g := secret.Group()
shareComs := make(SharesCommitment, splitter.poly.Degree()+1)
for i := range shareComs {
shareComs[i] = g.NewElement().MulGen(splitter.poly.Coefficient(uint(i)))
}

return shares, shareComs, nil
}

// Verify returns true if a share was produced by sharding a secret. It uses the
// share commitments generated by the Shard function.
func (v VerifiableSecretSharing) Verify(s Share, c SharesCommitment) bool {
if len(c) != int(v.s.t+1) {
return false
}

g := s.ID.Group()
lc := len(c) - 1
sum := g.NewElement().Set(c[lc])
for i := lc - 1; i >= 0; i-- {
sum.Mul(sum, s.ID)
sum.Add(sum, c[i])
}
polI := g.NewElement().MulGen(s.Value)
return polI.IsEqual(sum)
}

type Splitter struct {
g group.Group
t uint
poly polynomial.Polynomial
}

// NewSplitter returns a Splitter that can shard a secret with threshold t.
func NewSplitter(rnd io.Reader, t uint, secret group.Scalar) (sp Splitter) {
sp.g = secret.Group()
sp.t = t

c := make([]group.Scalar, sp.t+1)
// New returns a SecretSharing providing a (t,n) Shamir's secret sharing.
// It allows splitting a secret into n shares, such that the secret is
// only recovered from any subset of at least t+1 shares.
func New(rnd io.Reader, t uint, secret group.Scalar) SecretSharing {
c := make([]group.Scalar, t+1)
c[0] = secret.Copy()
g := secret.Group()
for i := 1; i < len(c); i++ {
c[i] = sp.g.RandomScalar(rnd)
c[i] = g.RandomScalar(rnd)
}
sp.poly = polynomial.New(c)

return
return SecretSharing{g: g, t: t, poly: polynomial.New(c)}
}

func (sp Splitter) Shard(rnd io.Reader) (s Share) {
return sp.ShardWithID(sp.g.RandomNonZeroScalar(rnd))
// Share creates n shares with an ID monotonically increasing from 1 to n.
func (ss SecretSharing) Share(n uint) []Share {
shares := make([]Share, n)
id := ss.g.NewScalar()
for i := range shares {
shares[i] = ss.ShareWithID(id.SetUint64(uint64(i + 1)))
}

return shares
}

func (sp Splitter) ShardWithID(id group.Scalar) (s Share) {
// ShareWithID creates one share of the secret using the ID as identifier.
// Notice that shares with the same ID are considered equal.
// Panics, if the ID is zero.
func (ss SecretSharing) ShareWithID(id group.Scalar) Share {
if id.IsZero() {
panic("secretsharing: id cannot be zero")
}

s.ID = id.Copy()
s.Value = sp.poly.Evaluate(s.ID)
return
return Share{
ID: id.Copy(),
Value: ss.poly.Evaluate(id),
}
}

func (sp Splitter) multipleShard(n uint) ([]Share, error) {
if n <= sp.t {
return nil, errThreshold(sp.t, n)
// CommitSecret creates a commitment to the secret for further verifying shares.
func (ss SecretSharing) CommitSecret() SecretCommitment {
c := make(SecretCommitment, ss.poly.Degree()+1)
for i := range c {
c[i] = ss.g.NewElement().MulGen(ss.poly.Coefficient(uint(i)))
}
return c
}

shares := make([]Share, n)
id := sp.g.NewScalar()
for i := range shares {
shares[i] = sp.ShardWithID(id.SetUint64(uint64(i + 1)))
// Verify returns true if the share s was produced by sharing a secret with
// threshold t and commitment of the secret c.
func Verify(t uint, s Share, c SecretCommitment) bool {
if len(c) != int(t+1) {
return false
}
if s.ID.IsZero() {
return false
}

return shares, nil
g := s.ID.Group()
lc := len(c) - 1
sum := g.NewElement().Set(c[lc])
for i := lc - 1; i >= 0; i-- {
sum.Mul(sum, s.ID)
sum.Add(sum, c[i])
}
polI := g.NewElement().MulGen(s.Value)
return polI.IsEqual(sum)
}

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

0 comments on commit bbaa64a

Please sign in to comment.