From a974901e3c841a7fe4b00644977c628aeb732c72 Mon Sep 17 00:00:00 2001 From: Daniel Bourdrez <3641580+bytemare@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:51:25 +0200 Subject: [PATCH] Add commitment tu public key share (#39) * Add commitment to public key share Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- examples_test.go | 9 +++------ sharing.go | 51 +++++++++++++++++++++++++++++++++++++----------- tests/ss_test.go | 36 +++++++++++++++++++--------------- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/examples_test.go b/examples_test.go index 9db6eaa..37b3db4 100644 --- a/examples_test.go +++ b/examples_test.go @@ -65,22 +65,19 @@ func ExampleVerify() { secret := g.NewScalar().Random() // Shard the secret into shares - shares, polynomial, err := secretsharing.ShardReturnPolynomial(g, secret, threshold, shareholders) + shares, err := secretsharing.ShardAndCommit(g, secret, threshold, shareholders) if err != nil { panic(err) } - // Commit to polynomial. - commitment := secretsharing.Commit(g, polynomial) - // 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()) + publicShare := keyshare.Public() // Verify that the key share's public key is consistent with the commitment. - if !secretsharing.Verify(g, keyshare.Identifier(), publicKey, commitment) { + if !secretsharing.Verify(g, publicShare.ID, publicShare.PublicKey, publicShare.Commitment) { panic("invalid public key for shareholder") } } diff --git a/sharing.go b/sharing.go index e1d9705..38013fa 100644 --- a/sharing.go +++ b/sharing.go @@ -38,6 +38,9 @@ type PublicKeyShare struct { // The PublicKey of Secret belonging to the participant. PublicKey *group.Element + // The Commitment to the polynomial the key was created with. + Commitment []*group.Element + // ID of the participant. ID uint64 } @@ -66,8 +69,22 @@ func (s KeyShare) Public() *PublicKeyShare { return s.PublicKeyShare } +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), + Commitment: nil, + ID: id, + }, + } +} + // 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. +// To use Verifiable Secret Sharing, use ShardAndCommit. func Shard( g group.Group, secret *group.Scalar, @@ -83,21 +100,33 @@ func Shard( return shares, err } -func makeKeyShare(g group.Group, id uint64, p Polynomial) *KeyShare { - ids := g.NewScalar().SetUInt64(id) - yi := p.Evaluate(ids) +// ShardAndCommit does the same as Shard but populates the returned key shares with the Commitment to the polynomial. +func ShardAndCommit(g group.Group, + secret *group.Scalar, + threshold, total uint, + polynomial ...*group.Scalar, +) ([]*KeyShare, error) { + shares, p, err := ShardReturnPolynomial(g, secret, threshold, total, polynomial...) + if err != nil { + return nil, err + } - return &KeyShare{ - Secret: yi, - PublicKeyShare: &PublicKeyShare{ - PublicKey: g.Base().Multiply(yi), - ID: id, - }, + commitment := Commit(g, p) + + for _, share := range shares { + share.Commitment = commitment + } + + for _, pi := range p { + pi.Zero() // zero-out the polynomial, just to be sure. } + + return shares, nil } // ShardReturnPolynomial splits the secret into total shares, recoverable by a subset of threshold shares, and returns -// the constructed secret polynomial. To use Verifiable Secret Sharing, call Commit with the returned polynomial. +// the constructed secret polynomial without committing to it. Use the Commit function if you want to commit to the +// returned polynomial. func ShardReturnPolynomial( g group.Group, secret *group.Scalar, diff --git a/tests/ss_test.go b/tests/ss_test.go index fc9707d..9680a4e 100644 --- a/tests/ss_test.go +++ b/tests/ss_test.go @@ -151,13 +151,11 @@ func TestCommitment(t *testing.T) { t.Run(g.String(), func(tt *testing.T) { secret := g.NewScalar().Random() - shares, polynomial, err := secretsharing.ShardReturnPolynomial(g, secret, threshold, total) + shares, err := secretsharing.ShardAndCommit(g, secret, threshold, total) if err != nil { t.Fatal(err) } - commitment := secretsharing.Commit(g, polynomial) - for i, keyshare := range shares { pk := g.Base().Multiply(keyshare.Secret) @@ -166,7 +164,7 @@ func TestCommitment(t *testing.T) { t.Fatal("expected equality") } - if !secretsharing.Verify(g, keyshare.ID, pk, commitment) { + if !secretsharing.Verify(g, pubkey.ID, pk, pubkey.Commitment) { t.Fatalf("invalid public key for shareholder %d", i) } } @@ -206,13 +204,11 @@ func TestVerify_BadShares(t *testing.T) { t.Run(g.String(), func(tt *testing.T) { secret := g.NewScalar().Random() - shares, polynomial, err := secretsharing.ShardReturnPolynomial(g, secret, threshold, total) + shares, err := secretsharing.ShardAndCommit(g, secret, threshold, total) if err != nil { t.Fatal(err) } - commitments := secretsharing.Commit(g, polynomial) - // Alter the shares for _, share := range shares { share.Secret.Random() @@ -221,7 +217,7 @@ func TestVerify_BadShares(t *testing.T) { // Verify for _, share := range shares { pk := g.Base().Multiply(share.Secret) - if secretsharing.Verify(g, share.ID, pk, commitments) { + if secretsharing.Verify(g, share.ID, pk, share.Commitment) { t.Fatalf("verification succeeded but shouldn't") } } @@ -288,6 +284,14 @@ func TestShard_LowShares(t *testing.T) { if _, err := secretsharing.Shard(g, secret, threshold, total); err == nil || err.Error() != expected { t.Fatalf("expected error %q, got %q", expected, err) } + + if _, err := secretsharing.ShardAndCommit(g, secret, threshold, total); err == nil || err.Error() != expected { + t.Fatalf("expected error %q, got %q", expected, err) + } + + if _, _, err := secretsharing.ShardReturnPolynomial(g, secret, threshold, total); err == nil || err.Error() != expected { + t.Fatalf("expected error %q, got %q", expected, err) + } }) } } @@ -501,32 +505,32 @@ func TestCombine_BadIdentifiers_Duplicates(t *testing.T) { } func TestPubKeyForCommitment(t *testing.T) { - threshold := uint(3) // threshold is the minimum amount of necessary shares to recombine the secret - shareholders := uint(7) // the total amount of key share-holders + threshold := uint(3) // threshold is the minimum amount of necessary shares to recombine the secret + total := uint(7) // the total amount of key share-holders for _, g := range groups { // This is the global secret to be shared secret := g.NewScalar().Random() // Shard the secret into shares - shares, polynomial, err := secretsharing.ShardReturnPolynomial(g, secret, threshold, shareholders) + shares, err := secretsharing.ShardAndCommit(g, secret, threshold, total) if err != nil { - panic(err) + t.Fatal(err) } - commitment := secretsharing.Commit(g, polynomial) + publicShare := shares[0].Public() // No expected error - pk, err := secretsharing.PubKeyForCommitment(g, shares[0].ID, commitment) + pk, err := secretsharing.PubKeyForCommitment(g, publicShare.ID, publicShare.Commitment) if err != nil { t.Fatal(err) } - if pk.Equal(shares[0].PublicKey) != 1 { + if pk.Equal(publicShare.PublicKey) != 1 { t.Fatalf("unexpected public key:\n\twant: %v\n\tgot : %v\n", shares[0].PublicKey.Hex(), pk.Hex()) } - if !secretsharing.Verify(g, shares[0].ID, shares[0].PublicKey, commitment) { + if !secretsharing.Verify(g, publicShare.ID, publicShare.PublicKey, publicShare.Commitment) { t.Fatal("unexpected public key") } }