diff --git a/identify/identify.go b/identify/identify.go index 5bc664390..3fdb5e0df 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -3,6 +3,7 @@ package identify import ( + "bytes" "crypto" "crypto/rand" "crypto/rsa" @@ -10,6 +11,7 @@ import ( "errors" "io/ioutil" + proto "code.google.com/p/goprotobuf/proto" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ) @@ -17,15 +19,90 @@ import ( // Perform initial communication with this peer to share node ID's and // initiate communication func Handshake(self, remote *peer.Peer, in, out chan []byte) error { - // TODO: make this more... secure. - out <- self.ID + encoded, err := buildHandshake(self) + if err != nil { + return err + } + out <- encoded resp := <-in + + pbresp := new(Identify) + err = proto.Unmarshal(resp, pbresp) + if err != nil { + return err + } + + // Verify that the given ID matches their given public key + if verifyErr := verifyID(peer.ID(pbresp.GetId()), pbresp.GetPubkey()); verifyErr != nil { + return verifyErr + } + + pubkey, err := x509.ParsePKIXPublicKey(pbresp.GetPubkey()) + if err != nil { + return err + } + + // Challenge peer to ensure they own the given pubkey + secret := make([]byte, 32) + rand.Read(secret) + encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, pubkey.(*rsa.PublicKey), secret) + if err != nil { + //... this is odd + return err + } + + out <- encrypted + challenge := <-in + + plain, err := rsa.DecryptPKCS1v15(rand.Reader, self.PrivKey.(*rsa.PrivateKey), challenge) + if err != nil { + return err + } + + out <- plain + chalResp := <-in + if !bytes.Equal(chalResp, secret) { + return errors.New("Recieved incorrect challenge response!") + } + remote.ID = peer.ID(resp) + remote.PubKey = pubkey u.DOut("[%s] identify: Got node id: %s\n", self.ID.Pretty(), remote.ID.Pretty()) return nil } +func buildHandshake(self *peer.Peer) ([]byte, error) { + pkb, err := x509.MarshalPKIXPublicKey(self.PubKey) + if err != nil { + return nil, err + } + + pmes := new(Identify) + pmes.Id = []byte(self.ID) + pmes.Pubkey = pkb + + encoded, err := proto.Marshal(pmes) + if err != nil { + return nil, err + } + + return encoded, nil +} + +func verifyID(id peer.ID, pubkey []byte) error { + hash, err := u.Hash(pubkey) + if err != nil { + return err + } + + if id.Equal(peer.ID(hash)) { + return nil + } + + return errors.New("ID did not match public key!") +} + type KeyPair struct { Pub crypto.PublicKey Priv crypto.PrivateKey diff --git a/identify/message.pb.go b/identify/message.pb.go new file mode 100644 index 000000000..0e5c45627 --- /dev/null +++ b/identify/message.pb.go @@ -0,0 +1,48 @@ +// Code generated by protoc-gen-go. +// source: message.proto +// DO NOT EDIT! + +/* +Package identify is a generated protocol buffer package. + +It is generated from these files: + message.proto + +It has these top-level messages: + Identify +*/ +package identify + +import proto "code.google.com/p/goprotobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type Identify struct { + Id []byte `protobuf:"bytes,1,req,name=id" json:"id,omitempty"` + Pubkey []byte `protobuf:"bytes,2,req,name=pubkey" json:"pubkey,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Identify) Reset() { *m = Identify{} } +func (m *Identify) String() string { return proto.CompactTextString(m) } +func (*Identify) ProtoMessage() {} + +func (m *Identify) GetId() []byte { + if m != nil { + return m.Id + } + return nil +} + +func (m *Identify) GetPubkey() []byte { + if m != nil { + return m.Pubkey + } + return nil +} + +func init() { +} diff --git a/identify/message.proto b/identify/message.proto index 71804c883..df9ff515f 100644 --- a/identify/message.proto +++ b/identify/message.proto @@ -1,3 +1,6 @@ +package identify; + message Identify { required bytes id = 1; + required bytes pubkey = 2; } diff --git a/peer/peer.go b/peer/peer.go index 144189f58..18530c250 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -1,6 +1,7 @@ package peer import ( + "crypto" "sync" "time" @@ -33,6 +34,9 @@ type Peer struct { ID ID Addresses []*ma.Multiaddr + PubKey crypto.PublicKey + PrivKey crypto.PrivateKey + latency time.Duration latenLock sync.RWMutex }