Skip to content

Commit

Permalink
Optimize public key recovery and some renaming (#38)
Browse files Browse the repository at this point in the history
* Optimize public key recovery and some renaming

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>
  • Loading branch information
bytemare authored Jul 10, 2024
1 parent 60e53bc commit ba34b98
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 97 deletions.
26 changes: 13 additions & 13 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import (
secretsharing "github.com/bytemare/secret-sharing"
)

// ExampleShardAndCombine shows how to split a private key into shares and how to recombine it from a
// subset of shares.
func ExampleShardAndCombine() {
// ExampleShard shows how to split a private key into shares and how to recombine it from a
// subset of shares. For an example of Verifiable Secret Sharing, see ExampleVerify.
func ExampleShard() {
// These are the configuration parameters
g := group.Ristretto255Sha512
threshold := uint(3) // threshold is the minimum amount of necessary shares to recombine the secret
Expand All @@ -33,13 +33,13 @@ func ExampleShardAndCombine() {
panic(err)
}

// Assemble a subset of shares to recover the secret. We must use [threshold+1] or more shares.
subset := []secretsharing.KeyShare{
// Assemble a subset of shares to recover the secret. We must use threshold or more shares.
subset := []secretsharing.Share{
shares[5], shares[0], shares[3],
}

// Combine the subset of shares.
recovered, err := secretsharing.Combine(g, subset)
recovered, err := secretsharing.CombineShares(g, subset)
if err != nil {
panic(err)
}
Expand All @@ -53,9 +53,9 @@ func ExampleShardAndCombine() {
// Output: Key split into shares and recombined with a subset of shares!
}

// ExampleShardAndVerify shows how to split a private key into shares and how one can verify a secret, should the dealer
// be potentially malicious.
func ExampleShardAndVerify() {
// ExampleShardAndVerify shows how to split a private key into shares, commit to the underlying polynomial, and verify
// the generated public keys given the initial commitment.
func ExampleVerify() {
// These are the configuration parameters
g := group.Ristretto255Sha512
threshold := uint(3) // threshold is minimum amount of necessary shares to recombine the secret
Expand All @@ -70,16 +70,16 @@ func ExampleShardAndVerify() {
panic(err)
}

// Commit to be computed by the dealer.
// Commit to polynomial.
commitment := secretsharing.Commit(g, polynomial)

// You can verify any public key using the commitment. This can be run by a single party or any other with access to
// the party's public key.
// You can verify any public key using the commitment. This can be run by a single participant or any other
// participant access to the participant's public key.
for _, keyshare := range shares {
// Let's derive the public key. Other parties won't have access to the private key, naturally.
publicKey := g.Base().Multiply(keyshare.SecretKey())

// Verify that the keys hare's public key is consistent with the commitment.
// Verify that the key share's public key is consistent with the commitment.
if !secretsharing.Verify(g, keyshare.Identifier(), publicKey, commitment) {
panic("invalid public key for shareholder")
}
Expand Down
4 changes: 2 additions & 2 deletions polynomial.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ func (p Polynomial) DeriveInterpolatingValue(g group.Group, id *group.Scalar) (*

// PolynomialInterpolateConstant recovers the constant term of the interpolating polynomial defined by the set of
// key shares.
func PolynomialInterpolateConstant(g group.Group, shares []KeyShare) (*group.Scalar, error) {
xCoords := NewPolynomialFromListFunc(g, shares, func(share KeyShare) *group.Scalar {
func PolynomialInterpolateConstant(g group.Group, shares []Share) (*group.Scalar, error) {
xCoords := NewPolynomialFromListFunc(g, shares, func(share Share) *group.Scalar {
return g.NewScalar().SetUInt64(share.Identifier())
})

Expand Down
83 changes: 63 additions & 20 deletions sharing.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,59 +23,87 @@ var (
errPolySecretNotSet = errors.New("provided polynomial's first coefficient not set to the secret")
)

// The KeyShare interface enables to use functions in this package with compatible key shares.
type KeyShare interface {
// The Share interface enables to use functions in this package with compatible key shares.
type Share interface {
// Identifier returns the identity for this share.
Identifier() uint64

// SecretKey returns the participant's secret share.
SecretKey() *group.Scalar
}

// Share identifies the sharded key share for a given participant.
type Share struct {
// Secret is the participant's secret share.
Secret *group.Scalar
// PublicKeyShare specifies the public key of a participant identified with ID. This can be useful to keep a registry of
// participants.
type PublicKeyShare struct {
// The PublicKey of Secret belonging to the participant.
PublicKey *group.Element

// ID uniquely identifies a key share within secret sharing instance.
// ID of the participant.
ID uint64
}

// KeyShare holds the secret and public key share for a given participant.
type KeyShare struct {
// The Secret of a participant (or secret share).
Secret *group.Scalar

// PublicKeyShare is the public part of the participant's key share.
*PublicKeyShare
}

// Identifier returns the identity for this share.
func (s *Share) Identifier() uint64 {
func (s KeyShare) Identifier() uint64 {
return s.ID
}

// SecretKey returns the participant's secret share.
func (s *Share) SecretKey() *group.Scalar {
func (s KeyShare) SecretKey() *group.Scalar {
return s.Secret
}

// Shard splits the secret into total shares, recoverable by a subset of threshold shares. This is the function you
// should probably use.
// Public returns the public key share and identifier corresponding to the secret key share.
func (s KeyShare) Public() *PublicKeyShare {
return s.PublicKeyShare
}

// Shard splits the secret into total shares, recoverable by a subset of threshold shares.
// To use Verifiable Secret Sharing, use ShardReturnPolynomial and commit to the polynomial with Commit.
func Shard(
g group.Group,
secret *group.Scalar,
threshold, total uint,
polynomial ...*group.Scalar,
) ([]*Share, error) {
) ([]*KeyShare, error) {
shares, p, err := ShardReturnPolynomial(g, secret, threshold, total, polynomial...)

for _, pi := range p {
pi.Zero() // zero-out the polynomial, juste to be sure
pi.Zero() // zero-out the polynomial, just to be sure.
}

return shares, err
}

func makeKeyShare(g group.Group, id uint64, p Polynomial) *KeyShare {
ids := g.NewScalar().SetUInt64(id)
yi := p.Evaluate(ids)

return &KeyShare{
Secret: yi,
PublicKeyShare: &PublicKeyShare{
PublicKey: g.Base().Multiply(yi),
ID: id,
},
}
}

// ShardReturnPolynomial splits the secret into total shares, recoverable by a subset of threshold shares, and returns
// the constructed polynomial. Unless you know what you are doing, you probably want to use Shard() instead.
// the constructed secret polynomial. To use Verifiable Secret Sharing, call Commit with the returned polynomial.
func ShardReturnPolynomial(
g group.Group,
secret *group.Scalar,
threshold, total uint,
polynomial ...*group.Scalar,
) ([]*Share, Polynomial, error) {
) ([]*KeyShare, Polynomial, error) {
if total < threshold {
return nil, nil, errTooFewShares
}
Expand All @@ -92,19 +120,34 @@ func ShardReturnPolynomial(
p[0] = secret.Copy()

// Evaluate the polynomial for each point x=1,...,n
secretKeyShares := make([]*Share, total)
secretKeyShares := make([]*KeyShare, total)

for i := uint64(1); i <= uint64(total); i++ {
id := g.NewScalar().SetUInt64(i)
yi := p.Evaluate(id)
secretKeyShares[i-1] = &Share{ID: i, Secret: yi}
secretKeyShares[i-1] = makeKeyShare(g, i, p)
}

return secretKeyShares, p, nil
}

// KeyShares is a set of KeyShares.
type KeyShares []*KeyShare

// Combine recovers the constant secret by combining the key shares.
func Combine(g group.Group, shares []KeyShare) (*group.Scalar, error) {
func (k KeyShares) Combine(g group.Group) (*group.Scalar, error) {
if len(k) == 0 {
return nil, errNoShares
}

s := make([]Share, len(k))
for i, ks := range k {
s[i] = ks
}

return CombineShares(g, s)
}

// CombineShares recovers the constant secret by combining the key shares using the Share interface.
func CombineShares(g group.Group, shares []Share) (*group.Scalar, error) {
if len(shares) == 0 {
return nil, errNoShares
}
Expand Down
Loading

0 comments on commit ba34b98

Please sign in to comment.