diff --git a/account/priv_key.go b/account/priv_key.go index 1975749ba..63749612e 100644 --- a/account/priv_key.go +++ b/account/priv_key.go @@ -54,3 +54,10 @@ func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { func (privKey PrivKeyEd25519) String() string { return Fmt("PrivKeyEd25519{*****}") } + +func GenPrivKeyEd25519() PrivKeyEd25519 { + privKeyBytes := new([64]byte) + copy(privKeyBytes[:32], CRandBytes(32)) + ed25519.MakePublicKey(privKeyBytes) + return PrivKeyEd25519(privKeyBytes[:]) +} diff --git a/account/pub_key.go b/account/pub_key.go index 37d9af1e2..a3d671a24 100644 --- a/account/pub_key.go +++ b/account/pub_key.go @@ -1,7 +1,9 @@ package account import ( + "bytes" "errors" + "github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/tendermint/ed25519" "github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/tendermint/ed25519/extra25519" "github.com/tendermint/tendermint/binary" @@ -71,3 +73,11 @@ func (pubKey PubKeyEd25519) ValidateBasic() error { func (pubKey PubKeyEd25519) String() string { return Fmt("PubKeyEd25519{%X}", []byte(pubKey)) } + +func (pubKey PubKeyEd25519) Equals(other PubKey) bool { + if _, ok := other.(PubKeyEd25519); ok { + return bytes.Equal(pubKey, other.(PubKeyEd25519)) + } else { + return false + } +} diff --git a/node/node.go b/node/node.go index 858b7d0c8..88bc42207 100644 --- a/node/node.go +++ b/node/node.go @@ -12,6 +12,7 @@ import ( "time" "github.com/tendermint/tendermint/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid" + acm "github.com/tendermint/tendermint/account" "github.com/tendermint/tendermint/binary" bc "github.com/tendermint/tendermint/blockchain" . "github.com/tendermint/tendermint/common" @@ -46,6 +47,7 @@ type Node struct { consensusReactor *consensus.ConsensusReactor privValidator *sm.PrivValidator genDoc *sm.GenesisDoc + privKey acm.PrivKeyEd25519 } func NewNode() *Node { @@ -92,6 +94,10 @@ func NewNode() *Node { log.Info("Generated PrivValidator", "file", privValidatorFile) } + // Generate node PrivKey + privKey := acm.GenPrivKeyEd25519() + + // Make event switch eventSwitch := new(events.EventSwitch) eventSwitch.Start() @@ -113,6 +119,7 @@ func NewNode() *Node { consensusReactor.SetPrivValidator(privValidator) } + // Make Switch sw := p2p.NewSwitch() sw.AddReactor("PEX", pexReactor) sw.AddReactor("MEMPOOL", mempoolReactor) @@ -135,6 +142,7 @@ func NewNode() *Node { consensusReactor: consensusReactor, privValidator: privValidator, genDoc: genDoc, + privKey: privKey, } } @@ -142,7 +150,8 @@ func NewNode() *Node { func (n *Node) Start() { log.Info("Starting Node", "chainID", config.GetString("chain_id")) n.book.Start() - n.sw.SetNodeInfo(makeNodeInfo(n.sw)) + n.sw.SetNodeInfo(makeNodeInfo(n.sw, n.privKey)) + n.sw.SetNodePrivKey(n.privKey) n.sw.Start() } @@ -237,10 +246,12 @@ func (n *Node) EventSwitch() *events.EventSwitch { return n.evsw } -func makeNodeInfo(sw *p2p.Switch) *types.NodeInfo { +func makeNodeInfo(sw *p2p.Switch, privKey acm.PrivKeyEd25519) *types.NodeInfo { + nodeInfo := &types.NodeInfo{ - ChainID: config.GetString("chain_id"), + PubKey: privKey.PubKey().(acm.PubKeyEd25519), Moniker: config.GetString("moniker"), + ChainID: config.GetString("chain_id"), Version: config.GetString("version"), UUID: uuid.New(), } diff --git a/p2p/secret_connection.go b/p2p/secret_connection.go index fb35e94b0..40ea899e5 100644 --- a/p2p/secret_connection.go +++ b/p2p/secret_connection.go @@ -9,11 +9,10 @@ import ( "crypto/sha256" "encoding/binary" "errors" - "net" - "time" - //"fmt" "io" + "net" "sync" + "time" "golang.org/x/crypto/nacl/box" "golang.org/x/crypto/nacl/secretbox" diff --git a/p2p/switch.go b/p2p/switch.go index 21a4d8a1f..08ff4b685 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -8,6 +8,7 @@ import ( "sync/atomic" "time" + acm "github.com/tendermint/tendermint/account" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/types" ) @@ -48,7 +49,8 @@ type Switch struct { peers *PeerSet dialing *CMap running uint32 - nodeInfo *types.NodeInfo // our node info + nodeInfo *types.NodeInfo // our node info + nodePrivKey acm.PrivKeyEd25519 // our node privkey } var ( @@ -70,6 +72,7 @@ func NewSwitch() *Switch { dialing: NewCMap(), running: 0, nodeInfo: nil, + nodePrivKey: nil, } return sw } @@ -126,6 +129,15 @@ func (sw *Switch) NodeInfo() *types.NodeInfo { return sw.nodeInfo } +// Not goroutine safe. +// NOTE: Overwrites sw.nodeInfo.PubKey +func (sw *Switch) SetNodePrivKey(nodePrivKey acm.PrivKeyEd25519) { + sw.nodePrivKey = nodePrivKey + if sw.nodeInfo != nil { + sw.nodeInfo.PubKey = nodePrivKey.PubKey().(acm.PubKeyEd25519) + } +} + func (sw *Switch) Start() { if atomic.CompareAndSwapUint32(&sw.running, 0, 1) { // Start reactors @@ -165,40 +177,51 @@ func (sw *Switch) Stop() { // NOTE: This performs a blocking handshake before the peer is added. // CONTRACT: Iff error is returned, peer is nil, and conn is immediately closed. func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) { - // First, perform handshake - peerNodeInfo, err := peerHandshake(conn, sw.nodeInfo) + // First, encrypt the connection. + sconn, err := MakeSecretConnection(conn, sw.nodePrivKey) if err != nil { - conn.Close() return nil, err } - // check version, chain id + // Then, perform node handshake + peerNodeInfo, err := peerHandshake(sconn, sw.nodeInfo) + if err != nil { + sconn.Close() + return nil, err + } + // Check that the professed PubKey matches the sconn's. + if !peerNodeInfo.PubKey.Equals(sconn.RemotePubKey()) { + sconn.Close() + return nil, fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v", + peerNodeInfo.PubKey, sconn.RemotePubKey()) + } + // Check version, chain id if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil { - conn.Close() + sconn.Close() return nil, err } - // avoid self + // Avoid self if peerNodeInfo.UUID == sw.nodeInfo.UUID { - conn.Close() + sconn.Close() return nil, fmt.Errorf("Ignoring connection from self") } - // the peerNodeInfo is not verified, - // so we overwrite the IP with that from the conn + // The peerNodeInfo is not verified, so overwrite. + // Overwrite the IP with that from the conn // and if we dialed out, the port too - // everything else we just have to trust - ip, port, _ := net.SplitHostPort(conn.RemoteAddr().String()) + // Everything else we just have to trust + ip, port, _ := net.SplitHostPort(sconn.RemoteAddr().String()) peerNodeInfo.Host = ip if outbound { porti, _ := strconv.Atoi(port) peerNodeInfo.P2PPort = uint16(porti) } - peer := newPeer(conn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError) + peer := newPeer(sconn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError) // Add the peer to .peers // ignore if duplicate or if we already have too many for that IP range if err := sw.peers.Add(peer); err != nil { log.Info("Ignoring peer", "error", err, "peer", peer) - peer.stop() // will also close conn + peer.stop() // will also close sconn return nil, err } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index 41bf489ab..ca05c4e9d 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + acm "github.com/tendermint/tendermint/account" "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/types" @@ -74,21 +75,28 @@ func (tr *TestReactor) Receive(chId byte, peer *Peer, msgBytes []byte) { // convenience method for creating two switches connected to each other. func makeSwitchPair(t testing.TB, initSwitch func(*Switch) *Switch) (*Switch, *Switch) { + s1PrivKey := acm.GenPrivKeyEd25519() + s2PrivKey := acm.GenPrivKeyEd25519() + // Create two switches that will be interconnected. s1 := initSwitch(NewSwitch()) s1.SetNodeInfo(&types.NodeInfo{ + PubKey: s1PrivKey.PubKey().(acm.PubKeyEd25519), Moniker: "switch1", ChainID: "testing", Version: "123.123.123", UUID: uuid.New(), }) + s1.SetNodePrivKey(s1PrivKey) s2 := initSwitch(NewSwitch()) s2.SetNodeInfo(&types.NodeInfo{ + PubKey: s2PrivKey.PubKey().(acm.PubKeyEd25519), Moniker: "switch2", ChainID: "testing", Version: "123.123.123", UUID: uuid.New(), }) + s2.SetNodePrivKey(s2PrivKey) // Start switches s1.Start() diff --git a/types/node.go b/types/node.go index c700ed2fa..6b7226243 100644 --- a/types/node.go +++ b/types/node.go @@ -2,19 +2,20 @@ package types import ( "fmt" + acm "github.com/tendermint/tendermint/account" "strings" ) type NodeInfo struct { - Moniker string `json:"moniker"` - ChainID string `json:"chain_id"` - Version string `json:"version"` - Revision string `json:"revision"` - - UUID string `json:"uuid"` - Host string `json:"host"` - P2PPort uint16 `json:"p2p_port"` - RPCPort uint16 `json:"rpc_port"` + PubKey acm.PubKeyEd25519 `json:"pub_key"` + Moniker string `json:"moniker"` + ChainID string `json:"chain_id"` + Version string `json:"version"` + Revision string `json:"revision"` + UUID string `json:"uuid"` + Host string `json:"host"` + P2PPort uint16 `json:"p2p_port"` + RPCPort uint16 `json:"rpc_port"` } func (ni *NodeInfo) CompatibleWith(no *NodeInfo) error {