diff options
-rw-r--r-- | core/dkg-tsig-protocol_test.go | 194 | ||||
-rw-r--r-- | crypto/dkg/dkg.go | 3 | ||||
-rw-r--r-- | crypto/dkg/dkg_test.go | 13 |
3 files changed, 210 insertions, 0 deletions
diff --git a/core/dkg-tsig-protocol_test.go b/core/dkg-tsig-protocol_test.go index 1ea65b2..7da8866 100644 --- a/core/dkg-tsig-protocol_test.go +++ b/core/dkg-tsig-protocol_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/dexon-foundation/dexon-consensus-core/common" "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" @@ -238,6 +239,199 @@ func (s *DKGTSIGProtocolTestSuite) TestNackComplaint() { } } +// TestComplaint tests if the received private share is not valid, a complaint +// should be proposed. +func (s *DKGTSIGProtocolTestSuite) TestComplaint() { + k := 3 + n := 10 + round := uint64(1) + gov, err := test.NewGovernance(5, 100) + s.Require().NoError(err) + + receivers, protocols := s.newProtocols(k, n, round) + + byzantineID := s.vIDs[0] + targetID := s.vIDs[1] + receiver := receivers[targetID] + protocol := protocols[targetID] + + for _, receiver := range receivers { + gov.AddDKGMasterPublicKey(receiver.mpk) + } + + for _, protocol := range protocols { + s.Require().NoError( + protocol.processMasterPublicKeys(gov.DKGMasterPublicKeys(round))) + } + + // These messages are not valid. + err = protocol.processPrivateShare(&types.DKGPrivateShare{ + ProposerID: types.ValidatorID{Hash: common.NewRandomHash()}, + Round: round, + }) + s.Error(ErrNotDKGParticipant, err) + err = protocol.processPrivateShare(&types.DKGPrivateShare{ + ProposerID: byzantineID, + Round: round, + }) + s.Error(ErrIncorrectPrivateShareSignature, err) + + // Byzantine node is sending incorrect private share. + err = protocol.processPrivateShare(receivers[byzantineID].prvShare[byzantineID]) + s.NoError(err) + s.Require().Len(receiver.complaints, 1) + complaint, exist := receiver.complaints[byzantineID] + s.True(exist) + s.Equal(byzantineID, complaint.PrivateShare.ProposerID) +} + +// TestQualifyIDs tests if there is a id with t+1 complaints, it should not be +// in the qualifyIDs. +func (s *DKGTSIGProtocolTestSuite) TestQualifyIDs() { + k := 3 + n := 10 + round := uint64(1) + gov, err := test.NewGovernance(5, 100) + s.Require().NoError(err) + + receivers, _ := s.newProtocols(k, n, round) + + byzantineID := s.vIDs[0] + + for _, receiver := range receivers { + gov.AddDKGMasterPublicKey(receiver.mpk) + } + + complaints := make([]*types.DKGComplaint, k+1) + for i := range complaints { + vID := s.vIDs[i] + complaints[i] = &types.DKGComplaint{ + ProposerID: vID, + Round: round, + PrivateShare: types.DKGPrivateShare{ + ProposerID: byzantineID, + Round: round, + }, + } + } + + gpk, err := newDKGGroupPublicKey(round, + gov.DKGMasterPublicKeys(round), complaints, + k, eth.SigToPub, + ) + s.Require().NoError(err) + s.Require().Len(gpk.qualifyIDs, n-1) + for _, id := range gpk.qualifyIDs { + s.NotEqual(id, byzantineID) + } + + gpk2, err := newDKGGroupPublicKey(round, + gov.DKGMasterPublicKeys(round), complaints[:k], + k, eth.SigToPub, + ) + s.Require().NoError(err) + s.Require().Len(gpk2.qualifyIDs, n) +} + +// TestPartialSignature tests if tsigProtocol can handle incorrect partial +// signature and report error. +func (s *DKGTSIGProtocolTestSuite) TestPartialSignature() { + k := 3 + n := 10 + round := uint64(1) + gov, err := test.NewGovernance(5, 100) + s.Require().NoError(err) + + receivers, protocols := s.newProtocols(k, n, round) + + byzantineID := s.vIDs[0] + + for _, receiver := range receivers { + gov.AddDKGMasterPublicKey(receiver.mpk) + } + + for _, protocol := range protocols { + s.Require().NoError( + protocol.processMasterPublicKeys(gov.DKGMasterPublicKeys(round))) + } + + for senderID, receiver := range receivers { + s.Require().Len(receiver.prvShare, n) + if senderID == byzantineID { + continue + } + for vID, prvShare := range receiver.prvShare { + s.Require().NoError(protocols[vID].processPrivateShare(prvShare)) + } + } + + for _, protocol := range protocols { + protocol.proposeNackComplaints() + } + + for _, recv := range receivers { + s.Require().Len(recv.complaints, 1) + complaint, exist := recv.complaints[byzantineID] + s.Require().True(exist) + gov.AddDKGComplaint(complaint) + } + + // 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-1) + 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]] + if vID == byzantineID { + exist = !exist + } + 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("🏖🍹")) + byzantineID2 := s.vIDs[1] + for vID, shareSecret := range shareSecrets { + psig := &types.DKGPartialSignature{ + ProposerID: vID, + Round: round, + PartialSignature: shareSecret.sign(msgHash), + } + if vID == byzantineID2 { + psig.PartialSignature[0]++ + } + var err error + psig.Signature, err = s.prvKeys[vID].Sign(hashDKGPartialSignature(psig)) + s.Require().NoError(err) + err = tsig.processPartialSignature(msgHash, psig) + if vID == byzantineID { + s.Require().Error(ErrNotQualifyDKGParticipant, err) + } else if vID == byzantineID2 { + s.Require().Error(ErrIncorrectPartialSignature, err) + } else { + s.Require().NoError(err) + } + } + + 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/crypto/dkg/dkg.go b/crypto/dkg/dkg.go index 4e21d45..df01078 100644 --- a/crypto/dkg/dkg.go +++ b/crypto/dkg/dkg.go @@ -304,6 +304,9 @@ func (prv *PrivateKey) Bytes() []byte { // VerifySignature checks that the given public key created signature over hash. func (pub PublicKey) VerifySignature( hash common.Hash, signature crypto.Signature) bool { + if len(signature) == 0 { + return false + } var sig bls.Sign if err := sig.Deserialize(signature[:]); err != nil { fmt.Println(err) diff --git a/crypto/dkg/dkg_test.go b/crypto/dkg/dkg_test.go index 8e8ce79..badb0ce 100644 --- a/crypto/dkg/dkg_test.go +++ b/crypto/dkg/dkg_test.go @@ -277,6 +277,19 @@ func (s *DKGTestSuite) TestDKGProtocol() { s.True(groupPK.VerifySignature(hash, recoverSig2)) } +func (s *DKGTestSuite) TestSignature() { + prvKey := NewPrivateKey() + pubKey := prvKey.PublicKey() + hash := crypto.Keccak256Hash([]byte("🛫")) + sig, err := prvKey.Sign(hash) + s.Require().NoError(err) + s.True(pubKey.VerifySignature(hash, sig)) + sig[0]++ + s.False(pubKey.VerifySignature(hash, sig)) + sig = crypto.Signature{} + s.False(pubKey.VerifySignature(hash, sig)) +} + func TestDKG(t *testing.T) { suite.Run(t, new(DKGTestSuite)) } |