diff options
author | Mission Liao <mission.liao@dexon.org> | 2018-09-26 16:55:15 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-26 16:55:15 +0800 |
commit | 663817d3e0d5a3c28cb0c5e378a533e242af5fdf (patch) | |
tree | 8d1952cc04a5735ce7cd060445667160bb21fc60 /core | |
parent | e8468d7206dbee2a8dfb34bfccc29d0d7273a777 (diff) | |
download | tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.tar tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.tar.gz tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.tar.bz2 tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.tar.lz tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.tar.xz tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.tar.zst tangerine-consensus-663817d3e0d5a3c28cb0c5e378a533e242af5fdf.zip |
core: move crypto to core/crypto (#140)
- Move key-holder to authenticator
Make core.keyHolder public as core.Authenticator, it
is not required to make this part an interface.
- Make private when there is no need to go public.
- Fix data race
Diffstat (limited to 'core')
37 files changed, 1373 insertions, 108 deletions
diff --git a/core/agreement-state_test.go b/core/agreement-state_test.go index 0142724..2aa50ac 100644 --- a/core/agreement-state_test.go +++ b/core/agreement-state_test.go @@ -24,9 +24,9 @@ import ( "github.com/stretchr/testify/suite" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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/eth" ) type AgreementStateTestSuite struct { diff --git a/core/agreement.go b/core/agreement.go index ffc4ba8..1b1cbde 100644 --- a/core/agreement.go +++ b/core/agreement.go @@ -24,8 +24,8 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // Errors for agreement module. diff --git a/core/agreement_test.go b/core/agreement_test.go index 681d7b6..4f8a3ac 100644 --- a/core/agreement_test.go +++ b/core/agreement_test.go @@ -21,9 +21,9 @@ import ( "testing" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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/eth" "github.com/stretchr/testify/suite" ) diff --git a/core/key-holder.go b/core/authenticator.go index 355c823..480d6cc 100644 --- a/core/key-holder.go +++ b/core/authenticator.go @@ -19,55 +19,57 @@ package core import ( "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) -type keyHolder struct { +// Authenticator verify data owner. +type Authenticator struct { prvKey crypto.PrivateKey pubKey crypto.PublicKey sigToPub SigToPubFn } -func newKeyHolder(prvKey crypto.PrivateKey, sigToPub SigToPubFn) *keyHolder { - return &keyHolder{ +// NewAuthenticator constructs an Authenticator instance. +func NewAuthenticator(prvKey crypto.PrivateKey, sigToPub SigToPubFn) *Authenticator { + return &Authenticator{ prvKey: prvKey, pubKey: prvKey.PublicKey(), sigToPub: sigToPub, } } -// SignBlock implements core.Signer. -func (h *keyHolder) SignBlock(b *types.Block) (err error) { - b.ProposerID = types.NewNodeID(h.pubKey) +// SignBlock signs a types.Block. +func (au *Authenticator) SignBlock(b *types.Block) (err error) { + b.ProposerID = types.NewNodeID(au.pubKey) if b.Hash, err = hashBlock(b); err != nil { return } - if b.Signature, err = h.prvKey.Sign(b.Hash); err != nil { + if b.Signature, err = au.prvKey.Sign(b.Hash); err != nil { return } return } -// SignVote implements core.Signer. -func (h *keyHolder) SignVote(v *types.Vote) (err error) { - v.ProposerID = types.NewNodeID(h.pubKey) - v.Signature, err = h.prvKey.Sign(hashVote(v)) +// SignVote signs a types.Vote. +func (au *Authenticator) SignVote(v *types.Vote) (err error) { + v.ProposerID = types.NewNodeID(au.pubKey) + v.Signature, err = au.prvKey.Sign(hashVote(v)) return } -// SignCRS implements core.Signer -func (h *keyHolder) SignCRS(b *types.Block, crs common.Hash) (err error) { - if b.ProposerID != types.NewNodeID(h.pubKey) { +// SignCRS signs CRS signature of types.Block. +func (au *Authenticator) SignCRS(b *types.Block, crs common.Hash) (err error) { + if b.ProposerID != types.NewNodeID(au.pubKey) { err = ErrInvalidProposerID return } - b.CRSSignature, err = h.prvKey.Sign(hashCRS(b, crs)) + b.CRSSignature, err = au.prvKey.Sign(hashCRS(b, crs)) return } -// VerifyBlock implements core.CryptoVerifier. -func (h *keyHolder) VerifyBlock(b *types.Block) (err error) { +// VerifyBlock verifies the signature of types.Block. +func (au *Authenticator) VerifyBlock(b *types.Block) (err error) { hash, err := hashBlock(b) if err != nil { return @@ -76,7 +78,7 @@ func (h *keyHolder) VerifyBlock(b *types.Block) (err error) { err = ErrIncorrectHash return } - pubKey, err := h.sigToPub(b.Hash, b.Signature) + pubKey, err := au.sigToPub(b.Hash, b.Signature) if err != nil { return } @@ -87,12 +89,12 @@ func (h *keyHolder) VerifyBlock(b *types.Block) (err error) { return } -// VerifyVote implements core.CryptoVerifier. -func (h *keyHolder) VerifyVote(v *types.Vote) (bool, error) { - return verifyVoteSignature(v, h.sigToPub) +// VerifyVote verifies the signature of types.Vote. +func (au *Authenticator) VerifyVote(v *types.Vote) (bool, error) { + return verifyVoteSignature(v, au.sigToPub) } -// VerifyWitness implements core.CryptoVerifier. -func (h *keyHolder) VerifyCRS(b *types.Block, crs common.Hash) (bool, error) { - return verifyCRSSignature(b, crs, h.sigToPub) +// VerifyCRS verifies the CRS signature of types.Block. +func (au *Authenticator) VerifyCRS(b *types.Block, crs common.Hash) (bool, error) { + return verifyCRSSignature(b, crs, au.sigToPub) } diff --git a/core/key-holder_test.go b/core/authenticator_test.go index cb5fda7..40b5e0c 100644 --- a/core/key-holder_test.go +++ b/core/authenticator_test.go @@ -22,23 +22,23 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto/eth" "github.com/stretchr/testify/suite" ) -type KeyHolderTestSuite struct { +type AuthenticatorTestSuite struct { suite.Suite } -func (s *KeyHolderTestSuite) setupKeyHolder() *keyHolder { +func (s *AuthenticatorTestSuite) setupAuthenticator() *Authenticator { k, err := eth.NewPrivateKey() s.NoError(err) - return newKeyHolder(k, eth.SigToPub) + return NewAuthenticator(k, eth.SigToPub) } -func (s *KeyHolderTestSuite) TestBlock() { - k := s.setupKeyHolder() +func (s *AuthenticatorTestSuite) TestBlock() { + k := s.setupAuthenticator() b := &types.Block{ ParentHash: common.NewRandomHash(), Position: types.Position{ @@ -52,8 +52,8 @@ func (s *KeyHolderTestSuite) TestBlock() { s.NoError(k.VerifyBlock(b)) } -func (s *KeyHolderTestSuite) TestVote() { - k := s.setupKeyHolder() +func (s *AuthenticatorTestSuite) TestVote() { + k := s.setupAuthenticator() v := &types.Vote{ ProposerID: types.NodeID{Hash: common.NewRandomHash()}, Type: types.VoteConfirm, @@ -70,8 +70,8 @@ func (s *KeyHolderTestSuite) TestVote() { s.NoError(err) } -func (s *KeyHolderTestSuite) TestCRS() { - k := s.setupKeyHolder() +func (s *AuthenticatorTestSuite) TestCRS() { + k := s.setupAuthenticator() b := &types.Block{ ParentHash: common.NewRandomHash(), Position: types.Position{ @@ -91,6 +91,6 @@ func (s *KeyHolderTestSuite) TestCRS() { s.NoError(err) } -func TestKeyHolder(t *testing.T) { - suite.Run(t, new(KeyHolderTestSuite)) +func TestAuthenticator(t *testing.T) { + suite.Run(t, new(AuthenticatorTestSuite)) } diff --git a/core/compaction-chain.go b/core/compaction-chain.go index 4405bbc..4bb3ffb 100644 --- a/core/compaction-chain.go +++ b/core/compaction-chain.go @@ -26,8 +26,8 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // Errors for compaction chain. diff --git a/core/compaction-chain_test.go b/core/compaction-chain_test.go index 3603fb6..ed02b78 100644 --- a/core/compaction-chain_test.go +++ b/core/compaction-chain_test.go @@ -23,8 +23,8 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto/eth" "github.com/stretchr/testify/suite" ) diff --git a/core/configuration-chain.go b/core/configuration-chain.go index a6c0f39..46f02cc 100644 --- a/core/configuration-chain.go +++ b/core/configuration-chain.go @@ -23,8 +23,8 @@ import ( "sync" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // Errors for configuration chain.. diff --git a/core/configuration-chain_test.go b/core/configuration-chain_test.go index ae82e42..fc3f48e 100644 --- a/core/configuration-chain_test.go +++ b/core/configuration-chain_test.go @@ -25,11 +25,11 @@ import ( "github.com/stretchr/testify/suite" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/dkg" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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 ConfigurationChainTestSuite struct { @@ -113,7 +113,12 @@ func (r *testCCReceiver) ProposeDKGAntiNackComplaint( prv.Signature, err = prvKey.Sign(hashDKGPrivateShare(prv)) r.s.Require().NoError(err) for _, cc := range r.nodes { - err = cc.processPrivateShare(prv) + // Use Marshal/Unmarshal to do deep copy. + data, err := json.Marshal(prv) + r.s.Require().NoError(err) + prvCopy := &types.DKGPrivateShare{} + r.s.Require().NoError(json.Unmarshal(data, prvCopy)) + err = cc.processPrivateShare(prvCopy) r.s.Require().NoError(err) } }() diff --git a/core/consensus.go b/core/consensus.go index c7eea32..b8c8f77 100644 --- a/core/consensus.go +++ b/core/consensus.go @@ -27,8 +27,8 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // SigToPubFn is a function to recover public key from signature. diff --git a/core/consensus_test.go b/core/consensus_test.go index a58d3e8..799698e 100644 --- a/core/consensus_test.go +++ b/core/consensus_test.go @@ -24,9 +24,9 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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/eth" "github.com/stretchr/testify/suite" ) diff --git a/core/crypto.go b/core/crypto.go index 26e44f9..2a1e3c5 100644 --- a/core/crypto.go +++ b/core/crypto.go @@ -21,8 +21,8 @@ import ( "encoding/binary" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) func hashWitness(block *types.Block) (common.Hash, error) { diff --git a/core/crypto/dkg/constant.go b/core/crypto/dkg/constant.go new file mode 100644 index 0000000..119613b --- /dev/null +++ b/core/crypto/dkg/constant.go @@ -0,0 +1,26 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package dkg + +import ( + "github.com/Spiderpowa/bls/ffi/go/bls" +) + +const ( + curve = bls.CurveFp382_2 +) diff --git a/core/crypto/dkg/dkg.go b/core/crypto/dkg/dkg.go new file mode 100644 index 0000000..fe05e96 --- /dev/null +++ b/core/crypto/dkg/dkg.go @@ -0,0 +1,381 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package dkg + +import ( + "encoding/json" + "fmt" + + "github.com/Spiderpowa/bls/ffi/go/bls" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" +) + +var ( + // ErrDuplicatedShare is reported when adding an private key share of same id. + ErrDuplicatedShare = fmt.Errorf("invalid share") + // ErrNoIDToRecover is reported when no id is provided for recovering private + // key. + ErrNoIDToRecover = fmt.Errorf("no id to recover private key") + // ErrShareNotFound is reported when the private key share of id is not found + // when recovering private key. + ErrShareNotFound = fmt.Errorf("share not found") +) + +var publicKeyLength int + +func init() { + bls.Init(curve) + + pubKey := &bls.PublicKey{} + publicKeyLength = len(pubKey.Serialize()) +} + +// PrivateKey represents a private key structure implments +// Crypto.PrivateKey interface. +type PrivateKey struct { + privateKey bls.SecretKey + publicKey PublicKey +} + +// MarshalJSON implements json.Marshaller. +func (prv *PrivateKey) MarshalJSON() ([]byte, error) { + return json.Marshal(&prv.privateKey) +} + +// UnmarshalJSON implements json.Unmarshaller. +func (prv *PrivateKey) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &prv.privateKey) +} + +// ID is the id for DKG protocol. +type ID = bls.ID + +// IDs is an array of ID. +type IDs []ID + +// PublicKey represents a public key structure implements +// Crypto.PublicKey interface. +type PublicKey struct { + publicKey bls.PublicKey +} + +// PrivateKeyShares represents a private key shares for DKG protocol. +type PrivateKeyShares struct { + shares []PrivateKey + shareIndex map[ID]int + masterPrivateKey []bls.SecretKey +} + +// PublicKeyShares represents a public key shares for DKG protocol. +type PublicKeyShares struct { + shares []PublicKey + shareIndex map[ID]int + masterPublicKey []bls.PublicKey +} + +// MarshalJSON implements json.Marshaller. +func (pubs *PublicKeyShares) MarshalJSON() ([]byte, error) { + type Alias PublicKeyShares + data := &struct { + MasterPublicKeys []*bls.PublicKey `json:"master_public_keys"` + }{ + make([]*bls.PublicKey, len(pubs.masterPublicKey)), + } + for i := range pubs.masterPublicKey { + data.MasterPublicKeys[i] = &pubs.masterPublicKey[i] + } + return json.Marshal(data) +} + +// UnmarshalJSON implements json.Unmarshaller. +func (pubs *PublicKeyShares) UnmarshalJSON(data []byte) error { + type Alias PublicKeyShares + aux := &struct { + MasterPublicKeys []*bls.PublicKey `json:"master_public_keys"` + }{} + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + mpk := make([]bls.PublicKey, len(aux.MasterPublicKeys)) + for i, pk := range aux.MasterPublicKeys { + mpk[i] = *pk + } + pubs.masterPublicKey = mpk + return nil +} + +// NewID creates a ew ID structure. +func NewID(id []byte) ID { + var blsID bls.ID + blsID.SetLittleEndian(id) + return blsID +} + +// NewPrivateKey creates a new PrivateKey structure. +func NewPrivateKey() *PrivateKey { + var key bls.SecretKey + key.SetByCSPRNG() + return &PrivateKey{ + privateKey: key, + publicKey: *newPublicKey(&key), + } +} + +// NewPrivateKeyShares creates a DKG private key shares of threshold t. +func NewPrivateKeyShares(t int) (*PrivateKeyShares, *PublicKeyShares) { + var prv bls.SecretKey + prv.SetByCSPRNG() + msk := prv.GetMasterSecretKey(t) + mpk := bls.GetMasterPublicKey(msk) + return &PrivateKeyShares{ + masterPrivateKey: msk, + shareIndex: make(map[ID]int), + }, &PublicKeyShares{ + shareIndex: make(map[ID]int), + masterPublicKey: mpk, + } +} + +// NewEmptyPrivateKeyShares creates an empty private key shares. +func NewEmptyPrivateKeyShares() *PrivateKeyShares { + return &PrivateKeyShares{ + shareIndex: make(map[ID]int), + } +} + +// SetParticipants sets the DKG participants. +func (prvs *PrivateKeyShares) SetParticipants(IDs IDs) { + prvs.shares = make([]PrivateKey, len(IDs)) + prvs.shareIndex = make(map[ID]int, len(IDs)) + for idx, ID := range IDs { + prvs.shares[idx].privateKey.Set(prvs.masterPrivateKey, &ID) + prvs.shareIndex[ID] = idx + } +} + +// AddShare adds a share. +func (prvs *PrivateKeyShares) AddShare(ID ID, share *PrivateKey) error { + if idx, exist := prvs.shareIndex[ID]; exist { + if !share.privateKey.IsEqual(&prvs.shares[idx].privateKey) { + return ErrDuplicatedShare + } + return nil + } + prvs.shareIndex[ID] = len(prvs.shares) + prvs.shares = append(prvs.shares, *share) + return nil +} + +// RecoverPrivateKey recovers private key from the shares. +func (prvs *PrivateKeyShares) RecoverPrivateKey(qualifyIDs IDs) ( + *PrivateKey, error) { + var prv PrivateKey + if len(qualifyIDs) == 0 { + return nil, ErrNoIDToRecover + } + for i, ID := range qualifyIDs { + idx, exist := prvs.shareIndex[ID] + if !exist { + return nil, ErrShareNotFound + } + if i == 0 { + prv.privateKey = prvs.shares[idx].privateKey + continue + } + prv.privateKey.Add(&prvs.shares[idx].privateKey) + } + return &prv, nil +} + +// RecoverPublicKey recovers public key from the shares. +func (prvs *PrivateKeyShares) RecoverPublicKey(qualifyIDs IDs) ( + *PublicKey, error) { + var pub PublicKey + if len(qualifyIDs) == 0 { + return nil, ErrNoIDToRecover + } + for i, ID := range qualifyIDs { + idx, exist := prvs.shareIndex[ID] + if !exist { + return nil, ErrShareNotFound + } + if i == 0 { + pub.publicKey = *prvs.shares[idx].privateKey.GetPublicKey() + continue + } + pub.publicKey.Add(prvs.shares[idx].privateKey.GetPublicKey()) + } + return &pub, nil +} + +// Share returns the share for the ID. +func (prvs *PrivateKeyShares) Share(ID ID) (*PrivateKey, bool) { + idx, exist := prvs.shareIndex[ID] + if !exist { + return nil, false + } + return &prvs.shares[idx], true +} + +// NewEmptyPublicKeyShares creates an empty public key shares. +func NewEmptyPublicKeyShares() *PublicKeyShares { + return &PublicKeyShares{ + shareIndex: make(map[ID]int), + } +} + +// Share returns the share for the ID. +func (pubs *PublicKeyShares) Share(ID ID) (*PublicKey, error) { + idx, exist := pubs.shareIndex[ID] + if exist { + return &pubs.shares[idx], nil + } + var pk PublicKey + if err := pk.publicKey.Set(pubs.masterPublicKey, &ID); err != nil { + return nil, err + } + pubs.AddShare(ID, &pk) + return &pk, nil +} + +// AddShare adds a share. +func (pubs *PublicKeyShares) AddShare(ID ID, share *PublicKey) error { + if idx, exist := pubs.shareIndex[ID]; exist { + if !share.publicKey.IsEqual(&pubs.shares[idx].publicKey) { + return ErrDuplicatedShare + } + return nil + } + pubs.shareIndex[ID] = len(pubs.shares) + pubs.shares = append(pubs.shares, *share) + return nil +} + +// VerifyPrvShare verifies if the private key shares is valid. +func (pubs *PublicKeyShares) VerifyPrvShare(ID ID, share *PrivateKey) ( + bool, error) { + var pk bls.PublicKey + if err := pk.Set(pubs.masterPublicKey, &ID); err != nil { + return false, err + } + return pk.IsEqual(share.privateKey.GetPublicKey()), nil +} + +// VerifyPubShare verifies if the public key shares is valid. +func (pubs *PublicKeyShares) VerifyPubShare(ID ID, share *PublicKey) ( + bool, error) { + var pk bls.PublicKey + if err := pk.Set(pubs.masterPublicKey, &ID); err != nil { + return false, err + } + return pk.IsEqual(&share.publicKey), nil +} + +// RecoverPublicKey recovers private key from the shares. +func (pubs *PublicKeyShares) RecoverPublicKey(qualifyIDs IDs) ( + *PublicKey, error) { + var pub PublicKey + if len(qualifyIDs) == 0 { + return nil, ErrNoIDToRecover + } + for i, ID := range qualifyIDs { + idx, exist := pubs.shareIndex[ID] + if !exist { + return nil, ErrShareNotFound + } + if i == 0 { + pub.publicKey = pubs.shares[idx].publicKey + continue + } + pub.publicKey.Add(&pubs.shares[idx].publicKey) + } + return &pub, nil +} + +// MasterKeyBytes returns []byte representation of master public key. +func (pubs *PublicKeyShares) MasterKeyBytes() []byte { + bytes := make([]byte, 0, len(pubs.masterPublicKey)*publicKeyLength) + for _, pk := range pubs.masterPublicKey { + bytes = append(bytes, pk.Serialize()...) + } + return bytes +} + +// newPublicKey creates a new PublicKey structure. +func newPublicKey(prvKey *bls.SecretKey) *PublicKey { + return &PublicKey{ + publicKey: *prvKey.GetPublicKey(), + } +} + +// PublicKey returns the public key associate this private key. +func (prv *PrivateKey) PublicKey() crypto.PublicKey { + return prv.publicKey +} + +// Sign calculates a signature. +func (prv *PrivateKey) Sign(hash common.Hash) (crypto.Signature, error) { + msg := string(hash[:]) + sign := prv.privateKey.Sign(msg) + return crypto.Signature(sign.Serialize()), nil +} + +// Bytes returns []byte representation of private key. +func (prv *PrivateKey) Bytes() []byte { + return prv.privateKey.GetLittleEndian() +} + +// SetBytes sets the private key data to []byte. +func (prv *PrivateKey) SetBytes(bytes []byte) error { + var key bls.SecretKey + if err := key.SetLittleEndian(bytes); err != nil { + return err + } + prv.privateKey = key + prv.publicKey = *newPublicKey(&prv.privateKey) + return nil +} + +// String returns string representation of privat key. +func (prv *PrivateKey) String() string { + return prv.privateKey.GetHexString() +} + +// 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) + return false + } + msg := string(hash[:]) + return sig.Verify(&pub.publicKey, msg) +} + +// Bytes returns []byte representation of public key. +func (pub PublicKey) Bytes() []byte { + var bytes []byte + pub.publicKey.Deserialize(bytes) + return bytes +} diff --git a/core/crypto/dkg/dkg_test.go b/core/crypto/dkg/dkg_test.go new file mode 100644 index 0000000..9488dcc --- /dev/null +++ b/core/crypto/dkg/dkg_test.go @@ -0,0 +1,461 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package dkg + +import ( + "encoding/binary" + "math/rand" + "sort" + "sync" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" +) + +type DKGTestSuite struct { + suite.Suite +} + +type member struct { + id ID + prvShares *PrivateKeyShares + pubShares *PublicKeyShares + receivedPrvShares *PrivateKeyShares + receivedPubShares map[ID]*PublicKeyShares +} + +func (s *DKGTestSuite) genID(k int) IDs { + IDs := make(IDs, 0, k) + for i := 0; i < k; i++ { + id := make([]byte, 8) + binary.LittleEndian.PutUint64(id, rand.Uint64()) + IDs = append(IDs, NewID(id)) + } + return IDs +} + +func (s *DKGTestSuite) sendKey(senders []member, receivers []member) { + receiveFrom := make(map[ID][]member) + for _, sender := range senders { + for _, receiver := range receivers { + // Here's the demonstration of DKG protocol. `pubShares` is broadcasted + // and all the receiver would save it to the `receivedPubShares`. + // Do not optimize the memory usage of this part. + receiver.receivedPubShares[sender.id] = sender.pubShares + prvShare, ok := sender.prvShares.Share(receiver.id) + s.Require().True(ok) + pubShare, err := sender.pubShares.Share(receiver.id) + s.Require().NoError(err) + valid, err := receiver.receivedPubShares[sender.id]. + VerifyPrvShare(receiver.id, prvShare) + s.Require().NoError(err) + s.Require().True(valid) + valid, err = receiver.receivedPubShares[sender.id]. + VerifyPubShare(receiver.id, pubShare) + s.Require().NoError(err) + s.Require().True(valid) + receiveFrom[receiver.id] = append(receiveFrom[receiver.id], sender) + } + } + // The received order do not need to be the same. + for _, receiver := range receivers { + rand.Shuffle(len(senders), func(i, j int) { + receiveFrom[receiver.id][i], receiveFrom[receiver.id][j] = + receiveFrom[receiver.id][j], receiveFrom[receiver.id][i] + }) + for _, sender := range receiveFrom[receiver.id] { + prvShare, ok := sender.prvShares.Share(receiver.id) + s.Require().True(ok) + err := receiver.receivedPrvShares.AddShare(sender.id, prvShare) + s.Require().NoError(err) + } + } +} + +func (s *DKGTestSuite) signWithQualifyIDs( + member member, qualifyIDs IDs, hash common.Hash) PartialSignature { + prvKey, err := member.receivedPrvShares.RecoverPrivateKey(qualifyIDs) + s.Require().NoError(err) + sig, err := prvKey.Sign(hash) + s.Require().NoError(err) + return PartialSignature(sig) +} + +func (s *DKGTestSuite) verifySigWithQualifyIDs( + members []member, qualifyIDs IDs, + signer ID, hash common.Hash, sig PartialSignature) bool { + membersIdx := make(map[ID]int) + for idx, member := range members { + membersIdx[member.id] = idx + } + pubShares := NewEmptyPublicKeyShares() + for _, id := range qualifyIDs { + idx, exist := membersIdx[id] + s.Require().True(exist) + member := members[idx] + pubShare, err := member.pubShares.Share(signer) + s.Require().NoError(err) + err = pubShares.AddShare(id, pubShare) + s.Require().NoError(err) + } + pubKey, err := pubShares.RecoverPublicKey(qualifyIDs) + s.Require().NoError(err) + return pubKey.VerifySignature(hash, crypto.Signature(sig)) +} + +func (s *DKGTestSuite) TestVerifyKeyShares() { + invalidID := NewID([]byte{0}) + ids := []ID{NewID([]byte{1}), NewID([]byte{2}), NewID([]byte{3})} + members := []member{} + for _, id := range ids { + members = append(members, member{ + id: id, + receivedPubShares: make(map[ID]*PublicKeyShares), + }) + } + + prvShares, pubShares := NewPrivateKeyShares(2) + prvShares.SetParticipants(ids) + + _, ok := prvShares.Share(invalidID) + s.False(ok) + for _, id := range ids { + prvShare, ok := prvShares.Share(id) + s.Require().True(ok) + valid, err := pubShares.VerifyPrvShare(id, prvShare) + s.Require().NoError(err) + s.True(valid) + pubShare, err := pubShares.Share(id) + s.Require().NoError(err) + valid, err = pubShares.VerifyPubShare(id, pubShare) + s.Require().NoError(err) + s.True(valid) + } + + // Test of faulty private/public key. + invalidPrvShare := NewPrivateKey() + valid, err := pubShares.VerifyPrvShare(ids[0], invalidPrvShare) + s.Require().NoError(err) + s.False(valid) + + invalidPubShare, ok := invalidPrvShare.PublicKey().(PublicKey) + s.Require().True(ok) + valid, err = pubShares.VerifyPubShare(ids[0], &invalidPubShare) + s.Require().NoError(err) + s.False(valid) + + // Test of faulty signature. + for idx := range members { + members[idx].prvShares, members[idx].pubShares = NewPrivateKeyShares(2) + members[idx].prvShares.SetParticipants(ids) + members[idx].receivedPrvShares = NewEmptyPrivateKeyShares() + } + s.sendKey(members, members) + hash := crypto.Keccak256Hash([]byte("πΎπΎπΎπΎπΎπΎ")) + sig, err := invalidPrvShare.Sign(hash) + s.Require().NoError(err) + psig := PartialSignature(sig) + for _, member := range members { + valid = s.verifySigWithQualifyIDs(members, ids, member.id, hash, psig) + s.False(valid) + } + + // Test of faulty group signature. + groupPubShares := make([]*PublicKeyShares, 0, len(members)) + sigs := make([]PartialSignature, 0, len(members)) + for _, member := range members { + sigs = append(sigs, s.signWithQualifyIDs(member, ids, hash)) + groupPubShares = append(groupPubShares, member.pubShares) + } + sigs[0] = psig + recoverSig, err := RecoverSignature(sigs, ids) + s.Require().NoError(err) + + pubKey := RecoverGroupPublicKey(groupPubShares) + s.False(pubKey.VerifySignature(hash, recoverSig)) +} + +func (s *DKGTestSuite) TestDKGProtocol() { + k := 5 + members := []member{} + ids := s.genID((k + 1) * 2) + for _, id := range ids { + members = append(members, member{ + id: id, + receivedPubShares: make(map[ID]*PublicKeyShares), + }) + } + + for idx := range members { + members[idx].prvShares, members[idx].pubShares = NewPrivateKeyShares(k) + members[idx].prvShares.SetParticipants(ids) + members[idx].receivedPrvShares = NewEmptyPrivateKeyShares() + } + // Randomly select non-disqualified members. + nums := make([]int, len(members)) + for i := range nums { + nums[i] = i + } + rand.Shuffle(len(nums), func(i, j int) { + nums[i], nums[j] = nums[j], nums[i] + }) + nums = nums[:rand.Intn(len(members))] + sort.Ints(nums) + qualify := make([]member, 0, len(nums)) + for _, idx := range nums { + qualify = append(qualify, members[idx]) + } + // TODO(jimmy-dexon): Remove below line after finishing test of random select. + qualify = members + // Members are partitioned into two groups. + grp1, grp2 := members[:k+1], members[k+1:] + collectIDs := func(members []member) IDs { + IDs := make(IDs, 0, len(members)) + for _, member := range members { + IDs = append(IDs, member.id) + } + return IDs + } + signMsg := func( + members []member, qualify []member, hash common.Hash) []PartialSignature { + ids := collectIDs(qualify) + sigs := make([]PartialSignature, 0, len(members)) + for _, member := range members { + sig := s.signWithQualifyIDs(member, ids, hash) + sigs = append(sigs, sig) + } + return sigs + } + verifySig := func( + members []member, + signer []ID, sig []PartialSignature, qualify []member, hash common.Hash) bool { + ids := collectIDs(qualify) + for i := range sig { + if !s.verifySigWithQualifyIDs(members, ids, signer[i], hash, sig[i]) { + return false + } + } + return true + } + s.sendKey(qualify, grp1) + s.sendKey(qualify, grp2) + hash := crypto.Keccak256Hash([]byte("π«")) + sig1 := signMsg(grp1, qualify, hash) + sig2 := signMsg(grp2, qualify, hash) + s.True(verifySig(members, collectIDs(grp1), sig1, qualify, hash)) + s.True(verifySig(members, collectIDs(grp2), sig2, qualify, hash)) + recoverSig1, err := RecoverSignature(sig1, collectIDs(grp1)) + s.Require().NoError(err) + recoverSig2, err := RecoverSignature(sig2, collectIDs(grp2)) + s.Require().NoError(err) + s.Equal(recoverSig1, recoverSig2) + + pubShares := make([]*PublicKeyShares, 0, len(members)) + for _, member := range members { + pubShares = append(pubShares, member.pubShares) + } + groupPK := RecoverGroupPublicKey(pubShares) + s.True(groupPK.VerifySignature(hash, recoverSig1)) + 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)) +} + +func BenchmarkDKGProtocol(b *testing.B) { + t := 33 + n := 100 + s := new(DKGTestSuite) + + self := member{} + members := make([]*member, n-1) + ids := make(IDs, n) + + b.Run("DKG", func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + self.id = s.genID(1)[0] + self.receivedPubShares = make(map[ID]*PublicKeyShares, n) + for idx, id := range s.genID(n - 1) { + ids[idx] = id + } + for idx := range members { + members[idx] = &member{ + id: ids[idx], + receivedPubShares: make(map[ID]*PublicKeyShares), + receivedPrvShares: NewEmptyPrivateKeyShares(), + } + } + ids[n-1] = self.id + prvShares := make(map[ID]*PrivateKey, n) + for idx := range members { + members[idx].prvShares, members[idx].pubShares = NewPrivateKeyShares(t) + members[idx].prvShares.SetParticipants(ids) + prvShare, ok := members[idx].prvShares.Share(self.id) + if !ok { + b.FailNow() + } + prvShares[members[idx].id] = prvShare + } + + b.StartTimer() + self.prvShares, self.pubShares = NewPrivateKeyShares(t) + self.prvShares.SetParticipants(ids) + self.receivedPrvShares = NewEmptyPrivateKeyShares() + for _, member := range members { + self.receivedPubShares[member.id] = member.pubShares + } + self.receivedPubShares[self.id] = self.pubShares + prvShare, ok := self.prvShares.Share(self.id) + if !ok { + b.FailNow() + } + prvShares[self.id] = prvShare + for id, prvShare := range prvShares { + ok, err := self.receivedPubShares[id].VerifyPrvShare(self.id, prvShare) + if err != nil { + b.Fatalf("%v", err) + } + if !ok { + b.FailNow() + } + if err := self.receivedPrvShares.AddShare(id, prvShare); err != nil { + b.Fatalf("%v", err) + } + } + if _, err := self.receivedPrvShares.RecoverPrivateKey(ids); err != nil { + b.Fatalf("%v", err) + } + } + }) + + hash := crypto.Keccak256Hash([]byte("π")) + b.Run("Share-Sign", func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + prvKey, err := self.receivedPrvShares.RecoverPrivateKey(ids) + if err != nil { + b.Fatalf("%v", err) + } + b.StartTimer() + if _, err := prvKey.Sign(hash); err != nil { + b.Fatalf("%v", err) + } + } + }) + + sendKey := func(sender *member, receiver *member, b *testing.B) { + receiver.receivedPubShares[sender.id] = sender.pubShares + prvShare, ok := sender.prvShares.Share(receiver.id) + if !ok { + b.FailNow() + } + ok, err := receiver.receivedPubShares[sender.id].VerifyPrvShare( + receiver.id, prvShare) + if err != nil { + b.Fatalf("%v", err) + } + if !ok { + b.FailNow() + } + if err := receiver.receivedPrvShares.AddShare(sender.id, prvShare); err != nil { + b.Fatalf("%v", err) + } + } + + members = append(members, &self) + + for _, sender := range members { + wg := sync.WaitGroup{} + for _, receiver := range members { + if sender == receiver { + continue + } + wg.Add(1) + go func(receiver *member) { + sendKey(sender, receiver, b) + wg.Done() + }(receiver) + } + wg.Wait() + } + wg := sync.WaitGroup{} + for _, m := range members { + wg.Add(1) + go func(member *member) { + sendKey(member, member, b) + wg.Done() + }(m) + } + wg.Wait() + + sign := func(member *member) PartialSignature { + prvKey, err := member.receivedPrvShares.RecoverPrivateKey(ids) + if err != nil { + b.Fatalf("%v", err) + } + sig, err := prvKey.Sign(hash) + if err != nil { + b.Fatalf("%v", err) + } + return PartialSignature(sig) + } + + b.Run("Combine-Sign", func(b *testing.B) { + b.StopTimer() + sigs := make([]PartialSignature, n) + for idx, member := range members { + sigs[idx] = sign(member) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + if _, err := RecoverSignature(sigs, ids); err != nil { + b.Fatalf("%v", err) + } + } + }) + + b.Run("Recover-GroupPK", func(b *testing.B) { + b.StopTimer() + pubShares := make([]*PublicKeyShares, 0, len(members)) + for _, member := range members { + pubShares = append(pubShares, member.pubShares) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + RecoverGroupPublicKey(pubShares) + } + }) +} diff --git a/core/crypto/dkg/utils.go b/core/crypto/dkg/utils.go new file mode 100644 index 0000000..967973d --- /dev/null +++ b/core/crypto/dkg/utils.go @@ -0,0 +1,69 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package dkg + +import ( + "fmt" + + "github.com/Spiderpowa/bls/ffi/go/bls" + + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" +) + +// PartialSignature is a partial signature in DKG+TSIG protocol. +type PartialSignature crypto.Signature + +var ( + // ErrEmptySignature is reported if the signature is empty. + ErrEmptySignature = fmt.Errorf("invalid empty signature") +) + +// RecoverSignature recovers TSIG signature. +func RecoverSignature(sigs []PartialSignature, signerIDs IDs) ( + crypto.Signature, error) { + blsSigs := make([]bls.Sign, len(sigs)) + for i, sig := range sigs { + if len(sig) == 0 { + return nil, ErrEmptySignature + } + if err := blsSigs[i].Deserialize([]byte(sig)); err != nil { + return nil, err + } + } + var recoverSig bls.Sign + if err := recoverSig.Recover(blsSigs, []bls.ID(signerIDs)); err != nil { + return nil, err + } + return crypto.Signature(recoverSig.Serialize()), nil +} + +// RecoverGroupPublicKey recovers group public key. +func RecoverGroupPublicKey(pubShares []*PublicKeyShares) *PublicKey { + var pub *PublicKey + for _, pubShare := range pubShares { + pk0 := pubShare.masterPublicKey[0] + if pub == nil { + pub = &PublicKey{ + publicKey: pk0, + } + } else { + pub.publicKey.Add(&pk0) + } + } + return pub +} diff --git a/core/crypto/eth/eth.go b/core/crypto/eth/eth.go new file mode 100644 index 0000000..e82ac29 --- /dev/null +++ b/core/crypto/eth/eth.go @@ -0,0 +1,120 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package eth + +import ( + "crypto/ecdsa" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" +) + +// PrivateKey represents a private key structure used in geth and implments +// Crypto.PrivateKey interface. +type PrivateKey struct { + privateKey ecdsa.PrivateKey + publicKey publicKey +} + +// publicKey represents a public key structure used in geth and implements +// Crypto.PublicKey interface. +type publicKey struct { + publicKey []byte +} + +// NewPrivateKey creates a new PrivateKey structure. +func NewPrivateKey() (*PrivateKey, error) { + key, err := ethcrypto.GenerateKey() + if err != nil { + return nil, err + } + return &PrivateKey{ + privateKey: *key, + publicKey: *newPublicKey(key), + }, nil +} + +// newPublicKey creates a new PublicKey structure. +func newPublicKey(prvKey *ecdsa.PrivateKey) *publicKey { + return &publicKey{ + publicKey: ethcrypto.CompressPubkey(&prvKey.PublicKey), + } +} + +// decompressPubkey parses a public key in the 33-byte compressed format. +func decompressPubkey(pubkey []byte) (publicKey, error) { + _, err := ethcrypto.DecompressPubkey(pubkey) + return publicKey{ + publicKey: pubkey, + }, err +} + +// PublicKey returns the public key associate this private key. +func (prv *PrivateKey) PublicKey() crypto.PublicKey { + return prv.publicKey +} + +// Sign calculates an ECDSA signature. +// +// This function is susceptible to chosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be chosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// The produced signature is in the [R || S || V] format where V is 0 or 1. +func (prv *PrivateKey) Sign(hash common.Hash) ( + sig crypto.Signature, err error) { + s, err := ethcrypto.Sign(hash[:], &prv.privateKey) + sig = crypto.Signature(s) + return +} + +// VerifySignature checks that the given public key created signature over hash. +// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) +// format. +// The signature should have the 64 byte [R || S] format. +func (pub publicKey) VerifySignature( + hash common.Hash, signature crypto.Signature) bool { + if len(signature) == 65 { + // The last byte is for ecrecover. + signature = signature[:64] + } + return ethcrypto.VerifySignature(pub.publicKey, hash[:], signature) +} + +// Compress encodes a public key to the 33-byte compressed format. +func (pub publicKey) Compress() []byte { + return pub.publicKey +} + +// Bytes returns the []byte representation of public key. +func (pub publicKey) Bytes() []byte { + return pub.Compress() +} + +// SigToPub returns the PublicKey that created the given signature. +func SigToPub( + hash common.Hash, signature crypto.Signature) (crypto.PublicKey, error) { + key, err := ethcrypto.SigToPub(hash[:], signature[:]) + if err != nil { + return publicKey{}, err + } + return publicKey{publicKey: ethcrypto.CompressPubkey(key)}, nil +} diff --git a/core/crypto/eth/eth_test.go b/core/crypto/eth/eth_test.go new file mode 100644 index 0000000..acda1a8 --- /dev/null +++ b/core/crypto/eth/eth_test.go @@ -0,0 +1,93 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package eth + +import ( + "testing" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/stretchr/testify/suite" +) + +type ETHCryptoTestSuite struct { + suite.Suite +} + +func (s *ETHCryptoTestSuite) TestSignature() { + prv1, err := NewPrivateKey() + s.Require().Nil(err) + hash1 := common.NewRandomHash() + hash2 := common.NewRandomHash() + + // Test that same private key should produce same signature. + sig11, err := prv1.Sign(hash1) + s.Require().Nil(err) + sig112, err := prv1.Sign(hash1) + s.Require().Nil(err) + s.Equal(sig11, sig112) + + // Test that different private key should produce different signature. + prv2, err := NewPrivateKey() + s.Require().Nil(err) + sig21, err := prv2.Sign(hash1) + s.Require().Nil(err) + s.NotEqual(sig11, sig21) + + // Test that different hash should produce different signature. + sig12, err := prv1.Sign(hash2) + s.Require().Nil(err) + s.NotEqual(sig11, sig12) + + // Test VerifySignature with correct public key. + pub1, ok := prv1.PublicKey().(publicKey) + s.Require().True(ok) + s.True(pub1.VerifySignature(hash1, sig11)) + + // Test VerifySignature with wrong hash. + s.False(pub1.VerifySignature(hash2, sig11)) + // Test VerifySignature with wrong signature. + s.False(pub1.VerifySignature(hash1, sig21)) + // Test VerifySignature with wrong public key. + pub2 := prv2.PublicKey() + s.False(pub2.VerifySignature(hash1, sig11)) + + // Test compress and decompress of public key. + compressPub1 := pub1.Compress() + decompressPub1, err := decompressPubkey(compressPub1) + s.Require().Nil(err) + s.Equal(pub1, decompressPub1) + s.True(decompressPub1.VerifySignature(hash1, sig11)) +} + +func (s *ETHCryptoTestSuite) TestSigToPub() { + prv, err := NewPrivateKey() + s.Require().Nil(err) + data := "DEXON is infinitely scalable and low-latency." + hash := crypto.Keccak256Hash([]byte(data)) + sigmsg, err := prv.Sign(hash) + s.Require().Nil(err) + + pubkey, err := SigToPub(hash, sigmsg) + s.Require().Nil(err) + s.Equal(pubkey, prv.PublicKey()) +} + +func TestCrypto(t *testing.T) { + suite.Run(t, new(ETHCryptoTestSuite)) +} diff --git a/core/crypto/interfaces.go b/core/crypto/interfaces.go new file mode 100644 index 0000000..ac2754d --- /dev/null +++ b/core/crypto/interfaces.go @@ -0,0 +1,45 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package crypto + +import ( + "github.com/dexon-foundation/dexon-consensus-core/common" +) + +// Signature is the basic signature type in DEXON. +type Signature []byte + +// PrivateKey describes the asymmetric cryptography interface that interacts +// with the private key. +type PrivateKey interface { + // PublicKey returns the public key associate this private key. + PublicKey() PublicKey + + // Sign calculates a signature. + Sign(hash common.Hash) (Signature, error) +} + +// PublicKey describes the asymmetric cryptography interface that interacts +// with the public key. +type PublicKey interface { + // VerifySignature checks that the given public key created signature over hash. + VerifySignature(hash common.Hash, signature Signature) bool + + // Bytes returns the []byte representation of public key. + Bytes() []byte +} diff --git a/core/crypto/utils.go b/core/crypto/utils.go new file mode 100644 index 0000000..07a8b2b --- /dev/null +++ b/core/crypto/utils.go @@ -0,0 +1,41 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package crypto + +import ( + "encoding/hex" + + "github.com/ethereum/go-ethereum/crypto" + + "github.com/dexon-foundation/dexon-consensus-core/common" +) + +// Keccak256Hash calculates and returns the Keccak256 hash of the input data, +// converting it to an internal Hash data structure. +func Keccak256Hash(data ...[]byte) (h common.Hash) { + return common.Hash(crypto.Keccak256Hash(data...)) +} + +// Clone returns a deep copy of a signature. +func (sig Signature) Clone() Signature { + return append(Signature{}, sig...) +} + +func (sig Signature) String() string { + return hex.EncodeToString([]byte(sig[:])) +} diff --git a/core/crypto/utils_test.go b/core/crypto/utils_test.go new file mode 100644 index 0000000..977027a --- /dev/null +++ b/core/crypto/utils_test.go @@ -0,0 +1,52 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package crypto + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/suite" +) + +type CryptoTestSuite struct { + suite.Suite +} + +func (s *CryptoTestSuite) TestHash() { + cases := []struct { + input string + output string + }{ + {"DEXON ROCKS!", + "1a3f3a424aaa464e51b693585bba3a0c439d5f1ad3b5868e46d9f830225983bd"}, + {"Dexon Foundation", + "25ed4237aa978bfe706cc11c7a46a95de1a46302faea7ff6e900b03fa2b7b480"}, + {"INFINITELY SCALABLE AND LOW-LATENCY", + "ed3384c58a434fbc0bc887a85659eddf997e7da978ab66565ac865f995b77cf1"}, + } + for _, testcase := range cases { + hash := Keccak256Hash([]byte(testcase.input)) + output := hex.EncodeToString(hash[:]) + s.Equal(testcase.output, output) + } +} + +func TestCrypto(t *testing.T) { + suite.Run(t, new(CryptoTestSuite)) +} diff --git a/core/crypto_test.go b/core/crypto_test.go index 6c807da..4ff4606 100644 --- a/core/crypto_test.go +++ b/core/crypto_test.go @@ -22,10 +22,10 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/dkg" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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" ) diff --git a/core/dkg-tsig-protocol.go b/core/dkg-tsig-protocol.go index 0f5cb0b..a69cdd2 100644 --- a/core/dkg-tsig-protocol.go +++ b/core/dkg-tsig-protocol.go @@ -21,9 +21,9 @@ import ( "fmt" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/dkg" "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. diff --git a/core/dkg-tsig-protocol_test.go b/core/dkg-tsig-protocol_test.go index 0c938f7..97d9d34 100644 --- a/core/dkg-tsig-protocol_test.go +++ b/core/dkg-tsig-protocol_test.go @@ -23,11 +23,11 @@ import ( "github.com/stretchr/testify/suite" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/dkg" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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 { diff --git a/core/interfaces.go b/core/interfaces.go index 7f6ff2b..eebf8ff 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -21,8 +21,8 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // Application describes the application interface that interacts with DEXON @@ -56,7 +56,7 @@ type Debug interface { // TotalOrderingDelivered is called when the total ordering algorithm deliver // a set of block. - TotalOrderingDelivered(blockHashes common.Hashes, early bool) + TotalOrderingDelivered(common.Hashes, bool) } // Network describs the network interface that interacts with DEXON consensus @@ -101,7 +101,7 @@ type Governance interface { ProposeThresholdSignature(round uint64, signature crypto.Signature) // Get a ThresholdSignature of round. - GetThresholdSignature(round uint64) (sig crypto.Signature, exist bool) + GetThresholdSignature(round uint64) (crypto.Signature, bool) //// DKG-related methods. @@ -126,33 +126,3 @@ type Ticker interface { // Stop the ticker. Stop() } - -// Signer defines a role to sign data. -type Signer interface { - // SignBlock signs a block. - SignBlock(b *types.Block) error - - // SignVote signs a vote. - SignVote(v *types.Vote) error - - // SignCRS sign a block's CRS signature. - SignCRS(b *types.Block, crs common.Hash) error -} - -// CryptoVerifier defines a role to verify data in crypto's way. -type CryptoVerifier interface { - // VerifyBlock verifies if a block is properly signed or not. - VerifyBlock(b *types.Block) (ok bool, err error) - - // VerifyVote verfies if a vote is properly signed or not. - VerifyVote(v *types.Vote) (ok bool, err error) - - // VerifyCRS verifies if a CRS signature of one block is valid or not. - VerifyCRS(b *types.Block, crs common.Hash) (ok bool, err error) -} - -// Authenticator verify/sign who own the data. -type Authenticator interface { - Signer - CryptoVerifier -} diff --git a/core/leader-selector.go b/core/leader-selector.go index ce5134a..8a20055 100644 --- a/core/leader-selector.go +++ b/core/leader-selector.go @@ -22,8 +22,8 @@ import ( "math/big" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // Errors for leader module. diff --git a/core/leader-selector_test.go b/core/leader-selector_test.go index 211680d..02562ba 100644 --- a/core/leader-selector_test.go +++ b/core/leader-selector_test.go @@ -23,8 +23,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto/eth" ) type LeaderSelectorTestSuite struct { diff --git a/core/shard.go b/core/shard.go index 270b070..28b64f1 100644 --- a/core/shard.go +++ b/core/shard.go @@ -23,8 +23,8 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) // Shard represents a unit to produce a global ordering from multiple chains. diff --git a/core/shard_test.go b/core/shard_test.go index 42c8d7b..2a9016b 100644 --- a/core/shard_test.go +++ b/core/shard_test.go @@ -23,9 +23,9 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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/eth" "github.com/stretchr/testify/suite" ) diff --git a/core/test/blocks-generator.go b/core/test/blocks-generator.go index 4038450..eacc436 100644 --- a/core/test/blocks-generator.go +++ b/core/test/blocks-generator.go @@ -25,9 +25,9 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/blockdb" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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/eth" ) // TODO(mission): blocks generator should generate blocks based on chain, diff --git a/core/test/governance.go b/core/test/governance.go index 137662f..81b71e5 100644 --- a/core/test/governance.go +++ b/core/test/governance.go @@ -22,9 +22,9 @@ import ( "sync" "time" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/eth" "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/eth" ) var ( diff --git a/core/types/block.go b/core/types/block.go index ddd0abd..e4b4466 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -27,7 +27,7 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" - "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" ) var ( diff --git a/core/types/dkg.go b/core/types/dkg.go index a21c983..16737c6 100644 --- a/core/types/dkg.go +++ b/core/types/dkg.go @@ -20,8 +20,8 @@ package types import ( "fmt" - "github.com/dexon-foundation/dexon-consensus-core/crypto" - "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto/dkg" ) // DKGPrivateShare describe a secret share in DKG protocol. diff --git a/core/types/node.go b/core/types/node.go index 8a856de..177e407 100644 --- a/core/types/node.go +++ b/core/types/node.go @@ -21,7 +21,7 @@ import ( "bytes" "github.com/dexon-foundation/dexon-consensus-core/common" - "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" ) // NodeID is the ID type for nodes. diff --git a/core/types/vote.go b/core/types/vote.go index e92aa67..294bc74 100644 --- a/core/types/vote.go +++ b/core/types/vote.go @@ -21,7 +21,7 @@ import ( "fmt" "github.com/dexon-foundation/dexon-consensus-core/common" - "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" ) // VoteType is the type of vote. diff --git a/core/types/witness.go b/core/types/witness.go index 46aa1cc..3c455d8 100644 --- a/core/types/witness.go +++ b/core/types/witness.go @@ -23,7 +23,7 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" - "github.com/dexon-foundation/dexon-consensus-core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" ) // WitnessAck represents the acking to the compaction chain. diff --git a/core/utils.go b/core/utils.go index 30456ef..3c1d211 100644 --- a/core/utils.go +++ b/core/utils.go @@ -25,8 +25,8 @@ import ( "time" "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" "github.com/dexon-foundation/dexon-consensus-core/core/types" - "github.com/dexon-foundation/dexon-consensus-core/crypto" ) var ( |