diff --git a/p2p/protocol/identify/id.go b/p2p/protocol/identify/id.go index 39f49f614..d947901d4 100644 --- a/p2p/protocol/identify/id.go +++ b/p2p/protocol/identify/id.go @@ -6,6 +6,8 @@ import ( semver "github.com/coreos/go-semver/semver" ggio "github.com/gogo/protobuf/io" + ic "github.com/ipfs/go-libp2p-crypto" + peer "github.com/ipfs/go-libp2p-peer" pstore "github.com/ipfs/go-libp2p-peerstore" ma "github.com/jbenet/go-multiaddr" host "github.com/libp2p/go-libp2p/p2p/host" @@ -164,6 +166,18 @@ func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) { } log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs) + // set our public key + ownKey := ids.Host.Peerstore().PubKey(ids.Host.ID()) + if ownKey == nil { + log.Errorf("did not have own public key in Peerstore") + } else { + if kb, err := ownKey.Bytes(); err != nil { + log.Errorf("failed to convert key to bytes") + } else { + mes.PublicKey = kb + } + } + // set protocol versions pv := LibP2PVersion av := ClientVersion @@ -219,6 +233,83 @@ func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) { ids.Host.Peerstore().Put(p, "ProtocolVersion", pv) ids.Host.Peerstore().Put(p, "AgentVersion", av) + + // get the key from the other side. we may not have it (no-auth transport) + ids.consumeReceivedPubKey(c, mes.PublicKey) +} + +func (ids *IDService) consumeReceivedPubKey(c inet.Conn, kb []byte) { + lp := c.LocalPeer() + rp := c.RemotePeer() + + if kb == nil { + log.Debugf("%s did not receive public key for remote peer: %s", lp, rp) + return + } + + newKey, err := ic.UnmarshalPublicKey(kb) + if err != nil { + log.Errorf("%s cannot unmarshal key from remote peer: %s", lp, rp) + return + } + + // verify key matches peer.ID + np, err := peer.IDFromPublicKey(newKey) + if err != nil { + log.Debugf("%s cannot get peer.ID from key of remote peer: %s, %s", lp, rp, err) + return + } + + if np != rp { + // if the newKey's peer.ID does not match known peer.ID... + + if rp == "" && np != "" { + // if local peerid is empty, then use the new, sent key. + err := ids.Host.Peerstore().AddPubKey(rp, newKey) + if err != nil { + log.Debugf("%s could not add key for %s to peerstore: %s", lp, rp, err) + } + + } else { + // we have a local peer.ID and it does not match the sent key... error. + log.Errorf("%s received key for remote peer %s mismatch: %s", lp, rp, np) + } + return + } + + currKey := ids.Host.Peerstore().PubKey(rp) + if currKey == nil { + // no key? no auth transport. set this one. + err := ids.Host.Peerstore().AddPubKey(rp, newKey) + if err != nil { + log.Debugf("%s could not add key for %s to peerstore: %s", lp, rp, err) + } + return + } + + // ok, we have a local key, we should verify they match. + if currKey.Equals(newKey) { + return // ok great. we're done. + } + + // weird, got a different key... but the different key MATCHES the peer.ID. + // this odd. let's log error and investigate. this should basically never happen + // and it means we have something funky going on and possibly a bug. + log.Errorf("%s identify got a different key for: %s", lp, rp) + + // okay... does ours NOT match the remote peer.ID? + cp, err := peer.IDFromPublicKey(currKey) + if err != nil { + log.Errorf("%s cannot get peer.ID from local key of remote peer: %s, %s", lp, rp, err) + return + } + if cp != rp { + log.Errorf("%s local key for remote peer %s yields different peer.ID: %s", lp, rp, cp) + return + } + + // okay... curr key DOES NOT match new key. both match peer.ID. wat? + log.Errorf("%s local key and received key for %s do not match, but match peer.ID", lp, rp) } // HasConsistentTransport returns true if the address 'a' shares a diff --git a/p2p/protocol/identify/id_test.go b/p2p/protocol/identify/id_test.go index d0c147c49..cf7f814cb 100644 --- a/p2p/protocol/identify/id_test.go +++ b/p2p/protocol/identify/id_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + ic "github.com/ipfs/go-libp2p-crypto" peer "github.com/ipfs/go-libp2p-peer" host "github.com/libp2p/go-libp2p/p2p/host" identify "github.com/libp2p/go-libp2p/p2p/protocol/identify" @@ -41,6 +42,7 @@ func subtestIDService(t *testing.T, postDialWait time.Duration) { t.Log("test peer1 has peer2 addrs correctly") testKnowsAddrs(t, h1, h2p, h2.Peerstore().Addrs(h2p)) // has them testHasProtocolVersions(t, h1, h2p) + testHasPublicKey(t, h1, h2p, h2.Peerstore().PubKey(h2p)) // h1 should have h2's public key // now, this wait we do have to do. it's the wait for the Listening side // to be done identifying the connection. @@ -57,6 +59,7 @@ func subtestIDService(t *testing.T, postDialWait time.Duration) { t.Log("test peer2 has peer1 addrs correctly") testKnowsAddrs(t, h2, h1p, addrs) // has them testHasProtocolVersions(t, h2, h1p) + testHasPublicKey(t, h2, h1p, h1.Peerstore().PubKey(h1p)) // h1 should have h2's public key } func testKnowsAddrs(t *testing.T, h host.Host, p peer.ID, expected []ma.Multiaddr) { @@ -95,6 +98,25 @@ func testHasProtocolVersions(t *testing.T, h host.Host, p peer.ID) { } } +func testHasPublicKey(t *testing.T, h host.Host, p peer.ID, shouldBe ic.PubKey) { + k := h.Peerstore().PubKey(p) + if k == nil { + t.Error("no public key") + return + } + if !k.Equals(shouldBe) { + t.Error("key mismatch") + return + } + + p2, err := peer.IDFromPublicKey(k) + if err != nil { + t.Error("could not make key") + } else if p != p2 { + t.Error("key does not match peerid") + } +} + // TestIDServiceWait gives the ID service 100ms to finish after dialing // this is becasue it used to be concurrent. Now, Dial wait till the // id service is done.