From 54fa224dbbf1b1c0f8d54a3f10a81adb321ce1e4 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Wed, 19 Sep 2018 15:00:11 +0800 Subject: core:DKG and TSIG protocol (#115) --- core/crypto.go | 99 ++++++++++++ core/crypto_test.go | 53 +++++++ core/dkg-tsig-protocol.go | 341 +++++++++++++++++++++++++++++++++++++++++ core/dkg-tsig-protocol_test.go | 191 +++++++++++++++++++++++ core/types/dkg.go | 19 ++- 5 files changed, 698 insertions(+), 5 deletions(-) create mode 100644 core/dkg-tsig-protocol.go create mode 100644 core/dkg-tsig-protocol_test.go (limited to 'core') diff --git a/core/crypto.go b/core/crypto.go index 402fd2e..8fcb118 100644 --- a/core/crypto.go +++ b/core/crypto.go @@ -143,3 +143,102 @@ func hashPosition(position types.Position) common.Hash { binaryHeight, ) } + +func hashDKGPrivateShare(prvShare *types.DKGPrivateShare) common.Hash { + binaryRound := make([]byte, 8) + binary.LittleEndian.PutUint64(binaryRound, prvShare.Round) + + return crypto.Keccak256Hash( + prvShare.ProposerID.Hash[:], + binaryRound, + prvShare.PrivateShare.Bytes(), + ) +} + +func verifyDKGPrivateShareSignature( + prvShare *types.DKGPrivateShare, sigToPub SigToPubFn) (bool, error) { + hash := hashDKGPrivateShare(prvShare) + pubKey, err := sigToPub(hash, prvShare.Signature) + if err != nil { + return false, err + } + if prvShare.ProposerID != types.NewValidatorID(pubKey) { + return false, nil + } + return true, nil +} + +func hashDKGMasterPublicKey(mpk *types.DKGMasterPublicKey) common.Hash { + binaryRound := make([]byte, 8) + binary.LittleEndian.PutUint64(binaryRound, mpk.Round) + + return crypto.Keccak256Hash( + mpk.ProposerID.Hash[:], + mpk.DKGID.GetLittleEndian(), + mpk.PublicKeyShares.MasterKeyBytes(), + binaryRound, + ) +} + +func verifyDKGMasterPublicKeySignature( + mpk *types.DKGMasterPublicKey, sigToPub SigToPubFn) (bool, error) { + hash := hashDKGMasterPublicKey(mpk) + pubKey, err := sigToPub(hash, mpk.Signature) + if err != nil { + return false, err + } + if mpk.ProposerID != types.NewValidatorID(pubKey) { + return false, nil + } + return true, nil +} + +func hashDKGComplaint(complaint *types.DKGComplaint) common.Hash { + binaryRound := make([]byte, 8) + binary.LittleEndian.PutUint64(binaryRound, complaint.Round) + + hashPrvShare := hashDKGPrivateShare(&complaint.PrivateShare) + + return crypto.Keccak256Hash( + complaint.ProposerID.Hash[:], + binaryRound, + hashPrvShare[:], + ) +} + +func verifyDKGComplaintSignature( + complaint *types.DKGComplaint, sigToPub SigToPubFn) (bool, error) { + hash := hashDKGComplaint(complaint) + pubKey, err := sigToPub(hash, complaint.Signature) + if err != nil { + return false, err + } + if complaint.ProposerID != types.NewValidatorID(pubKey) { + return false, nil + } + return true, nil +} + +func hashDKGPartialSignature(psig *types.DKGPartialSignature) common.Hash { + binaryRound := make([]byte, 8) + binary.LittleEndian.PutUint64(binaryRound, psig.Round) + + return crypto.Keccak256Hash( + psig.ProposerID.Hash[:], + binaryRound, + psig.PartialSignature[:], + ) +} + +func verifyDKGPartialSignatureSignature( + psig *types.DKGPartialSignature, sigToPub SigToPubFn) (bool, error) { + hash := hashDKGPartialSignature(psig) + pubKey, err := sigToPub(hash, psig.Signature) + if err != nil { + return false, err + } + if psig.ProposerID != types.NewValidatorID(pubKey) { + return false, nil + } + return true, nil +} diff --git a/core/crypto_test.go b/core/crypto_test.go index b96b0bd..7f3c3f3 100644 --- a/core/crypto_test.go +++ b/core/crypto_test.go @@ -24,6 +24,7 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/types" "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg" "github.com/dexon-foundation/dexon-consensus-core/crypto/eth" "github.com/stretchr/testify/suite" ) @@ -210,6 +211,58 @@ func (s *CryptoTestSuite) TestCRSSignature() { s.False(verifyCRSSignature(block, crs, eth.SigToPub)) } +func (s *CryptoTestSuite) TestDKGSignature() { + prv, err := eth.NewPrivateKey() + s.Require().Nil(err) + vID := types.NewValidatorID(prv.PublicKey()) + prvShare := &types.DKGPrivateShare{ + ProposerID: vID, + Round: 5, + PrivateShare: *dkg.NewPrivateKey(), + } + prvShare.Signature, err = prv.Sign(hashDKGPrivateShare(prvShare)) + s.Require().Nil(err) + s.True(verifyDKGPrivateShareSignature(prvShare, eth.SigToPub)) + prvShare.Round++ + s.False(verifyDKGPrivateShareSignature(prvShare, eth.SigToPub)) + + id := dkg.NewID([]byte{13}) + _, pkShare := dkg.NewPrivateKeyShares(1) + mpk := &types.DKGMasterPublicKey{ + ProposerID: vID, + Round: 5, + DKGID: id, + PublicKeyShares: *pkShare, + } + mpk.Signature, err = prv.Sign(hashDKGMasterPublicKey(mpk)) + s.Require().Nil(err) + s.True(verifyDKGMasterPublicKeySignature(mpk, eth.SigToPub)) + mpk.Round++ + s.False(verifyDKGMasterPublicKeySignature(mpk, eth.SigToPub)) + + complaint := &types.DKGComplaint{ + ProposerID: vID, + Round: 5, + PrivateShare: *prvShare, + } + complaint.Signature, err = prv.Sign(hashDKGComplaint(complaint)) + s.Require().Nil(err) + s.True(verifyDKGComplaintSignature(complaint, eth.SigToPub)) + complaint.Round++ + s.False(verifyDKGComplaintSignature(complaint, eth.SigToPub)) + + sig := &types.DKGPartialSignature{ + ProposerID: vID, + Round: 5, + PartialSignature: dkg.PartialSignature{}, + } + sig.Signature, err = prv.Sign(hashDKGPartialSignature(sig)) + s.Require().Nil(err) + s.True(verifyDKGPartialSignatureSignature(sig, eth.SigToPub)) + sig.Round++ + s.False(verifyDKGPartialSignatureSignature(sig, eth.SigToPub)) +} + func TestCrypto(t *testing.T) { suite.Run(t, new(CryptoTestSuite)) } diff --git a/core/dkg-tsig-protocol.go b/core/dkg-tsig-protocol.go new file mode 100644 index 0000000..c549ecb --- /dev/null +++ b/core/dkg-tsig-protocol.go @@ -0,0 +1,341 @@ +// Copyright 2018 The dexon-consensus-core Authors +// This file is part of the dexon-consensus-core library. +// +// The dexon-consensus-core library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus-core library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus-core library. If not, see +// . + +package core + +import ( + "fmt" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/types" + "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg" +) + +// Errors for dkg module. +var ( + ErrNotDKGParticipant = fmt.Errorf( + "not a DKG participant") + ErrNotQualifyDKGParticipant = fmt.Errorf( + "not a qualified DKG participant") + ErrIDShareNotFound = fmt.Errorf( + "private share not found for specific ID") + ErrNotReachThreshold = fmt.Errorf( + "threshold not reach") + ErrIncorrectPrivateShareSignature = fmt.Errorf( + "incorrect private share signature") + ErrIncorrectPartialSignatureSignature = fmt.Errorf( + "incorrect partialSignature signature") + ErrIncorrectPartialSignature = fmt.Errorf( + "incorrect partialSignature") + ErrNotEnoughtPartialSignatures = fmt.Errorf( + "not enough of partial signatures") +) + +type dkgComplaintReceiver interface { + // ProposeDKGComplaint proposes a DKGComplaint. + ProposeDKGComplaint(complaint *types.DKGComplaint) + + // ProposeDKGMasterPublicKey propose a DKGMasterPublicKey. + ProposeDKGMasterPublicKey(mpk *types.DKGMasterPublicKey) + + // ProposeDKGPrivateShare propose a DKGPrivateShare. + ProposeDKGPrivateShare(to types.ValidatorID, prv *types.DKGPrivateShare) +} + +type dkgProtocol struct { + ID types.ValidatorID + recv dkgComplaintReceiver + round uint64 + threshold int + sigToPub SigToPubFn + idMap map[types.ValidatorID]dkg.ID + mpkMap map[types.ValidatorID]*dkg.PublicKeyShares + masterPrivateShare *dkg.PrivateKeyShares + prvShares *dkg.PrivateKeyShares +} + +type dkgShareSecret struct { + privateKey *dkg.PrivateKey +} + +type dkgGroupPublicKey struct { + round uint64 + qualifyIDs dkg.IDs + idMap map[types.ValidatorID]dkg.ID + publicKeys map[types.ValidatorID]*dkg.PublicKey + groupPublicKey *dkg.PublicKey + threshold int + sigToPub SigToPubFn +} + +type tsigProtocol struct { + groupPublicKey *dkgGroupPublicKey + sigs map[dkg.ID]dkg.PartialSignature + threshold int +} + +func newDKGID(ID types.ValidatorID) dkg.ID { + return dkg.NewID(ID.Hash[:]) +} + +func newDKGProtocol( + ID types.ValidatorID, + recv dkgComplaintReceiver, + round uint64, + threshold int, + sigToPub SigToPubFn) *dkgProtocol { + + prvShare, pubShare := dkg.NewPrivateKeyShares(threshold) + + recv.ProposeDKGMasterPublicKey(&types.DKGMasterPublicKey{ + ProposerID: ID, + Round: round, + DKGID: newDKGID(ID), + PublicKeyShares: *pubShare, + }) + + return &dkgProtocol{ + ID: ID, + recv: recv, + round: round, + threshold: threshold, + sigToPub: sigToPub, + idMap: make(map[types.ValidatorID]dkg.ID), + mpkMap: make(map[types.ValidatorID]*dkg.PublicKeyShares), + masterPrivateShare: prvShare, + prvShares: dkg.NewEmptyPrivateKeyShares(), + } +} + +func (d *dkgProtocol) processMasterPublicKeys( + mpks []*types.DKGMasterPublicKey) error { + d.idMap = make(map[types.ValidatorID]dkg.ID, len(mpks)) + d.mpkMap = make(map[types.ValidatorID]*dkg.PublicKeyShares, len(mpks)) + ids := make(dkg.IDs, len(mpks)) + for i := range mpks { + vID := mpks[i].ProposerID + d.idMap[vID] = mpks[i].DKGID + d.mpkMap[vID] = &mpks[i].PublicKeyShares + ids[i] = mpks[i].DKGID + } + d.masterPrivateShare.SetParticipants(ids) + for _, mpk := range mpks { + share, ok := d.masterPrivateShare.Share(mpk.DKGID) + if !ok { + return ErrIDShareNotFound + } + d.recv.ProposeDKGPrivateShare(mpk.ProposerID, &types.DKGPrivateShare{ + ProposerID: d.ID, + Round: d.round, + PrivateShare: *share, + }) + } + return nil +} + +func (d *dkgProtocol) sanityCheck(prvShare *types.DKGPrivateShare) error { + if _, exist := d.idMap[prvShare.ProposerID]; !exist { + return ErrNotDKGParticipant + } + ok, err := verifyDKGPrivateShareSignature(prvShare, d.sigToPub) + if err != nil { + return err + } + if !ok { + return ErrIncorrectPrivateShareSignature + } + return nil +} + +func (d *dkgProtocol) processPrivateShare( + prvShare *types.DKGPrivateShare) error { + if d.round != prvShare.Round { + return nil + } + self, exist := d.idMap[d.ID] + // This validator is not a DKG participant, ignore the private share. + if !exist { + return nil + } + if err := d.sanityCheck(prvShare); err != nil { + return err + } + mpk := d.mpkMap[prvShare.ProposerID] + ok, err := mpk.VerifyPrvShare(self, &prvShare.PrivateShare) + if err != nil { + return err + } + if !ok { + complaint := &types.DKGComplaint{ + ProposerID: d.ID, + Round: d.round, + PrivateShare: *prvShare, + } + d.recv.ProposeDKGComplaint(complaint) + } else { + sender := d.idMap[prvShare.ProposerID] + if err := d.prvShares.AddShare(sender, &prvShare.PrivateShare); err != nil { + return err + } + } + return nil +} + +func (d *dkgProtocol) recoverShareSecret(qualifyIDs dkg.IDs) ( + *dkgShareSecret, error) { + if len(qualifyIDs) <= d.threshold { + return nil, ErrNotReachThreshold + } + prvKey, err := d.prvShares.RecoverPrivateKey(qualifyIDs) + if err != nil { + return nil, err + } + return &dkgShareSecret{ + privateKey: prvKey, + }, nil +} + +func (ss *dkgShareSecret) sign(hash common.Hash) dkg.PartialSignature { + // DKG sign will always success. + sig, _ := ss.privateKey.Sign(hash) + return dkg.PartialSignature(sig) +} + +func newDKGGroupPublicKey( + round uint64, + mpks []*types.DKGMasterPublicKey, complaints []*types.DKGComplaint, + threshold int, sigToPub SigToPubFn) ( + *dkgGroupPublicKey, error) { + // Calculate qualify members. + complaintsByID := map[types.ValidatorID]int{} + for _, complaint := range complaints { + complaintsByID[complaint.PrivateShare.ProposerID]++ + } + disqualifyIDs := map[types.ValidatorID]struct{}{} + for vID, num := range complaintsByID { + if num > threshold { + disqualifyIDs[vID] = struct{}{} + } + } + qualifyIDs := make(dkg.IDs, 0, len(mpks)-len(disqualifyIDs)) + mpkMap := make(map[dkg.ID]*types.DKGMasterPublicKey, cap(qualifyIDs)) + idMap := make(map[types.ValidatorID]dkg.ID) + for _, mpk := range mpks { + if _, exist := disqualifyIDs[mpk.ProposerID]; exist { + continue + } + mpkMap[mpk.DKGID] = mpk + idMap[mpk.ProposerID] = mpk.DKGID + qualifyIDs = append(qualifyIDs, mpk.DKGID) + } + // Recover qualify members' public key. + pubKeys := make(map[types.ValidatorID]*dkg.PublicKey, len(qualifyIDs)) + for _, recvID := range qualifyIDs { + pubShares := dkg.NewEmptyPublicKeyShares() + for _, id := range qualifyIDs { + pubShare, err := mpkMap[id].PublicKeyShares.Share(recvID) + if err != nil { + return nil, err + } + if err := pubShares.AddShare(id, pubShare); err != nil { + return nil, err + } + } + pubKey, err := pubShares.RecoverPublicKey(qualifyIDs) + if err != nil { + return nil, err + } + pubKeys[mpkMap[recvID].ProposerID] = pubKey + } + // Recover Group Public Key. + pubShares := make([]*dkg.PublicKeyShares, 0, len(qualifyIDs)) + for _, id := range qualifyIDs { + pubShares = append(pubShares, &mpkMap[id].PublicKeyShares) + } + groupPK := dkg.RecoverGroupPublicKey(pubShares) + return &dkgGroupPublicKey{ + round: round, + qualifyIDs: qualifyIDs, + idMap: idMap, + publicKeys: pubKeys, + threshold: threshold, + groupPublicKey: groupPK, + sigToPub: sigToPub, + }, nil +} + +func (gpk *dkgGroupPublicKey) verifySignature( + hash common.Hash, sig crypto.Signature) bool { + return gpk.groupPublicKey.VerifySignature(hash, sig) +} + +func newTSigProtocol(gpk *dkgGroupPublicKey) *tsigProtocol { + return &tsigProtocol{ + groupPublicKey: gpk, + sigs: make(map[dkg.ID]dkg.PartialSignature, gpk.threshold+1), + } +} + +func (tsig *tsigProtocol) sanityCheck(psig *types.DKGPartialSignature) error { + _, exist := tsig.groupPublicKey.publicKeys[psig.ProposerID] + if !exist { + return ErrNotQualifyDKGParticipant + } + ok, err := verifyDKGPartialSignatureSignature( + psig, tsig.groupPublicKey.sigToPub) + if err != nil { + return err + } + if !ok { + return ErrIncorrectPartialSignatureSignature + } + return nil +} + +func (tsig *tsigProtocol) processPartialSignature( + hash common.Hash, psig *types.DKGPartialSignature) error { + if psig.Round != tsig.groupPublicKey.round { + return nil + } + id, exist := tsig.groupPublicKey.idMap[psig.ProposerID] + if !exist { + return ErrNotQualifyDKGParticipant + } + if err := tsig.sanityCheck(psig); err != nil { + return err + } + pubKey := tsig.groupPublicKey.publicKeys[psig.ProposerID] + if !pubKey.VerifySignature(hash, crypto.Signature(psig.PartialSignature)) { + return ErrIncorrectPartialSignature + } + tsig.sigs[id] = psig.PartialSignature + return nil +} + +func (tsig *tsigProtocol) signature() (crypto.Signature, error) { + if len(tsig.sigs) <= tsig.groupPublicKey.threshold { + return nil, ErrNotEnoughtPartialSignatures + } + ids := make(dkg.IDs, 0, len(tsig.sigs)) + psigs := make([]dkg.PartialSignature, 0, len(tsig.sigs)) + for id, psig := range tsig.sigs { + ids = append(ids, id) + psigs = append(psigs, psig) + } + return dkg.RecoverSignature(psigs, ids) +} diff --git a/core/dkg-tsig-protocol_test.go b/core/dkg-tsig-protocol_test.go new file mode 100644 index 0000000..f1c0017 --- /dev/null +++ b/core/dkg-tsig-protocol_test.go @@ -0,0 +1,191 @@ +// Copyright 2018 The dexon-consensus-core Authors +// This file is part of the dexon-consensus-core library. +// +// The dexon-consensus-core library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus-core library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus-core library. If not, see +// . + +package core + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/dexon-foundation/dexon-consensus-core/core/test" + "github.com/dexon-foundation/dexon-consensus-core/core/types" + "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg" + "github.com/dexon-foundation/dexon-consensus-core/crypto/eth" +) + +type DKGTSIGProtocolTestSuite struct { + suite.Suite + + vIDs types.ValidatorIDs + dkgIDs map[types.ValidatorID]dkg.ID + prvKeys map[types.ValidatorID]crypto.PrivateKey +} + +type testDKGReceiver struct { + s *DKGTSIGProtocolTestSuite + + prvKey crypto.PrivateKey + complaints map[types.ValidatorID]*types.DKGComplaint + mpk *types.DKGMasterPublicKey + prvShare map[types.ValidatorID]*types.DKGPrivateShare +} + +func newTestDKGReceiver( + s *DKGTSIGProtocolTestSuite, prvKey crypto.PrivateKey) *testDKGReceiver { + return &testDKGReceiver{ + s: s, + prvKey: prvKey, + complaints: make(map[types.ValidatorID]*types.DKGComplaint), + prvShare: make(map[types.ValidatorID]*types.DKGPrivateShare), + } +} + +func (r *testDKGReceiver) ProposeDKGComplaint(complaint *types.DKGComplaint) { + var err error + complaint.Signature, err = r.prvKey.Sign(hashDKGComplaint(complaint)) + r.s.Require().NoError(err) + r.complaints[complaint.ProposerID] = complaint +} + +func (r *testDKGReceiver) ProposeDKGMasterPublicKey( + mpk *types.DKGMasterPublicKey) { + var err error + mpk.Signature, err = r.prvKey.Sign(hashDKGMasterPublicKey(mpk)) + r.s.Require().NoError(err) + r.mpk = mpk +} +func (r *testDKGReceiver) ProposeDKGPrivateShare( + to types.ValidatorID, prv *types.DKGPrivateShare) { + var err error + prv.Signature, err = r.prvKey.Sign(hashDKGPrivateShare(prv)) + r.s.Require().NoError(err) + r.prvShare[to] = prv +} + +func (s *DKGTSIGProtocolTestSuite) setupDKGParticipants(n int) { + s.vIDs = make(types.ValidatorIDs, 0, n) + s.prvKeys = make(map[types.ValidatorID]crypto.PrivateKey, n) + s.dkgIDs = make(map[types.ValidatorID]dkg.ID) + ids := make(dkg.IDs, 0, n) + for i := 0; i < n; i++ { + prvKey, err := eth.NewPrivateKey() + s.Require().NoError(err) + vID := types.NewValidatorID(prvKey.PublicKey()) + s.vIDs = append(s.vIDs, vID) + s.prvKeys[vID] = prvKey + id := dkg.NewID(vID.Hash[:]) + ids = append(ids, id) + s.dkgIDs[vID] = id + } +} + +// TestDKGTSIGProtocol will test the entire DKG+TISG protocol including +// exchanging private shares, recovering share secret, creating partial sign and +// recovering threshold signature. +// All participants are good people in this test. +func (s *DKGTSIGProtocolTestSuite) TestDKGTSIGProtocol() { + k := 3 + n := 10 + round := uint64(1) + gov, err := test.NewGovernance(5, 100) + s.Require().NoError(err) + s.setupDKGParticipants(n) + + receivers := make(map[types.ValidatorID]*testDKGReceiver, n) + protocols := make(map[types.ValidatorID]*dkgProtocol, n) + for _, vID := range s.vIDs { + receivers[vID] = newTestDKGReceiver(s, s.prvKeys[vID]) + protocols[vID] = newDKGProtocol( + vID, + receivers[vID], + round, + k, + eth.SigToPub, + ) + s.Require().NotNil(receivers[vID].mpk) + } + + for _, receiver := range receivers { + gov.AddDKGMasterPublicKey(receiver.mpk) + } + + for _, protocol := range protocols { + s.Require().NoError( + protocol.processMasterPublicKeys(gov.DKGMasterPublicKeys(round))) + } + + for _, receiver := range receivers { + s.Require().Len(receiver.prvShare, n) + for vID, prvShare := range receiver.prvShare { + s.Require().NoError(protocols[vID].processPrivateShare(prvShare)) + } + } + + for _, recv := range receivers { + s.Require().Len(recv.complaints, 0) + } + + // DKG is fininished. + gpk, err := newDKGGroupPublicKey(round, + gov.DKGMasterPublicKeys(round), gov.DKGComplaints(round), + k, eth.SigToPub, + ) + s.Require().NoError(err) + s.Require().Len(gpk.qualifyIDs, n) + qualifyIDs := make(map[dkg.ID]struct{}, len(gpk.qualifyIDs)) + for _, id := range gpk.qualifyIDs { + qualifyIDs[id] = struct{}{} + } + + shareSecrets := make( + map[types.ValidatorID]*dkgShareSecret, len(qualifyIDs)) + + for vID, protocol := range protocols { + _, exist := qualifyIDs[s.dkgIDs[vID]] + s.Require().True(exist) + var err error + shareSecrets[vID], err = protocol.recoverShareSecret(gpk.qualifyIDs) + s.Require().NoError(err) + } + + tsig := newTSigProtocol(gpk) + msgHash := crypto.Keccak256Hash([]byte("🏖🍹")) + for vID, shareSecret := range shareSecrets { + psig := &types.DKGPartialSignature{ + ProposerID: vID, + Round: round, + PartialSignature: shareSecret.sign(msgHash), + } + var err error + psig.Signature, err = s.prvKeys[vID].Sign(hashDKGPartialSignature(psig)) + s.Require().NoError(err) + s.Require().NoError(tsig.processPartialSignature(msgHash, psig)) + if len(tsig.sigs) > k { + break + } + } + + sig, err := tsig.signature() + s.Require().NoError(err) + s.True(gpk.verifySignature(msgHash, sig)) +} + +func TestDKGTSIGProtocol(t *testing.T) { + suite.Run(t, new(DKGTSIGProtocolTestSuite)) +} diff --git a/core/types/dkg.go b/core/types/dkg.go index 7fee404..7fb686c 100644 --- a/core/types/dkg.go +++ b/core/types/dkg.go @@ -24,17 +24,18 @@ import ( // DKGPrivateShare describe a secret share in DKG protocol. type DKGPrivateShare struct { - ProposerID ValidatorID `json:"proposer_id"` - Round uint64 `json:"round"` - PrivateKeyShare dkg.PrivateKey `json:"private_key_share"` - Signature crypto.Signature `json:"signature"` + ProposerID ValidatorID `json:"proposer_id"` + Round uint64 `json:"round"` + PrivateShare dkg.PrivateKey `json:"private_share"` + Signature crypto.Signature `json:"signature"` } // DKGMasterPublicKey decrtibe a master public key in DKG protocol. type DKGMasterPublicKey struct { ProposerID ValidatorID `json:"proposer_id"` Round uint64 `json:"round"` - PublicKeyShares dkg.PublicKeyShares `json:"private_key_share"` + DKGID dkg.ID `json:"dkg_id"` + PublicKeyShares dkg.PublicKeyShares `json:"public_key_shares"` Signature crypto.Signature `json:"signature"` } @@ -45,3 +46,11 @@ type DKGComplaint struct { PrivateShare DKGPrivateShare `json:"private_share"` Signature crypto.Signature `json:"signature"` } + +// DKGPartialSignature describe a partial signature in DKG protocol. +type DKGPartialSignature struct { + ProposerID ValidatorID `json:"proposerID"` + Round uint64 `json:"round"` + PartialSignature dkg.PartialSignature `json:"partial_signature"` + Signature crypto.Signature `json:"signature"` +} -- cgit v1.2.3