identify.go 2.39 KB
Newer Older
1 2 3 4 5
// The identify package handles how peers identify with eachother upon
// connection to the network
package identify

import (
6
	"bytes"
7 8
	"errors"

9
	proto "code.google.com/p/goprotobuf/proto"
10
	ci "github.com/jbenet/go-ipfs/crypto"
11
	peer "github.com/jbenet/go-ipfs/peer"
12
	u "github.com/jbenet/go-ipfs/util"
13 14
)

15 16 17
// ErrUnsupportedKeyType is returned when a private key cast/type switch fails.
var ErrUnsupportedKeyType = errors.New("unsupported key type")

18 19
// Perform initial communication with this peer to share node ID's and
// initiate communication
20
func Handshake(self, remote *peer.Peer, in, out chan []byte) error {
21 22 23 24 25
	encoded, err := buildHandshake(self)
	if err != nil {
		return err
	}
	out <- encoded
26
	resp := <-in
27 28 29 30 31 32 33 34 35 36 37 38

	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
	}

39
	pubkey, err := ci.UnmarshalPublicKey(pbresp.GetPubkey())
40 41 42 43 44
	if err != nil {
		return err
	}

	// Challenge peer to ensure they own the given pubkey
45 46
	secret := self.PrivKey.GenSecret()
	encrypted, err := pubkey.Encrypt(secret)
47 48 49 50 51 52 53 54
	if err != nil {
		//... this is odd
		return err
	}

	out <- encrypted
	challenge := <-in

Jeromy's avatar
Jeromy committed
55
	// Decrypt challenge and send plaintext to partner
56
	plain, err := self.PrivKey.Decrypt(challenge)
57 58 59 60 61 62 63 64 65 66
	if err != nil {
		return err
	}

	out <- plain
	chalResp := <-in
	if !bytes.Equal(chalResp, secret) {
		return errors.New("Recieved incorrect challenge response!")
	}

67
	remote.ID = peer.ID(pbresp.GetId())
68
	remote.PubKey = pubkey
69
	u.DOut("[%s] identify: Got node id: %s\n", self.ID.Pretty(), remote.ID.Pretty())
70 71 72

	return nil
}
73

74
func buildHandshake(self *peer.Peer) ([]byte, error) {
75
	pkb, err := self.PubKey.Bytes()
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	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!")
}

105 106
func IdFromPubKey(pk ci.PubKey) (peer.ID, error) {
	b, err := pk.Bytes()
107 108 109
	if err != nil {
		return nil, err
	}
110
	hash, err := u.Hash(b)
111 112 113 114 115
	if err != nil {
		return nil, err
	}
	return peer.ID(hash), nil
}