identify.go 6.94 KB
Newer Older
Siraj Ravel's avatar
Siraj Ravel committed
1
// Package identify handles how peers identify with eachother upon
2 3 4 5
// connection to the network
package identify

import (
6
	"bytes"
Brendan Mc's avatar
Brendan Mc committed
7 8 9
	"errors"
	"strings"

10 11 12 13 14 15 16 17 18
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"hash"

19
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
20
	ci "github.com/jbenet/go-ipfs/crypto"
21
	peer "github.com/jbenet/go-ipfs/peer"
22
	u "github.com/jbenet/go-ipfs/util"
23 24
)

25 26 27 28 29 30
// List of supported protocols--each section in order of preference.
// Takes the form:  ECDH curves : Ciphers : Hashes
var SupportedExchanges = "P-256,P-224,P-384,P-521"
var SupportedCiphers = "AES-256,AES-128"
var SupportedHashes = "SHA256,SHA512,SHA1"

31 32 33
// ErrUnsupportedKeyType is returned when a private key cast/type switch fails.
var ErrUnsupportedKeyType = errors.New("unsupported key type")

34 35
// Performs initial communication with this peer to share node ID's and
// initiate communication.  (secureIn, secureOut, error)
36 37 38 39 40 41 42 43 44 45
func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) {
	// Generate and send Hello packet.
	// Hello = (rand, PublicKey, Supported)
	nonce := make([]byte, 16)
	_, err := rand.Read(nonce)
	if err != nil {
		return nil, nil, err
	}

	hello := new(Hello)
46

47
	myPubKey, err := self.PubKey.Bytes()
48
	if err != nil {
49
		return nil, nil, err
50
	}
51

52 53 54 55 56 57 58 59 60 61 62 63
	hello.Rand = nonce
	hello.Pubkey = myPubKey
	hello.Exchanges = &SupportedExchanges
	hello.Ciphers = &SupportedCiphers
	hello.Hashes = &SupportedHashes

	encoded, err := proto.Marshal(hello)
	if err != nil {
		return nil, nil, err
	}

	out <- encoded
64 65 66

	// Parse their Hello packet and generate an Exchange packet.
	// Exchange = (EphemeralPubKey, Signature)
67
	resp := <-in
68

69 70
	helloResp := new(Hello)
	err = proto.Unmarshal(resp, helloResp)
71
	if err != nil {
72
		return nil, nil, err
73 74
	}

75 76 77
	remote.PubKey, err = ci.UnmarshalPublicKey(helloResp.GetPubkey())
	if err != nil {
		return nil, nil, err
78 79
	}

Siraj Ravel's avatar
Siraj Ravel committed
80
	remote.ID, err = IDFromPubKey(remote.PubKey)
81
	if err != nil {
82
		return nil, nil, err
83 84
	}

85
	exchange, err := selectBest(SupportedExchanges, helloResp.GetExchanges())
86
	if err != nil {
87
		return nil, nil, err
88 89
	}

Brendan Mc's avatar
Brendan Mc committed
90
	cipherType, err := selectBest(SupportedCiphers, helloResp.GetCiphers())
91
	if err != nil {
92
		return nil, nil, err
93 94
	}

Brendan Mc's avatar
Brendan Mc committed
95
	hashType, err := selectBest(SupportedHashes, helloResp.GetHashes())
96 97
	if err != nil {
		return nil, nil, err
98 99
	}

Brendan Mc's avatar
Brendan Mc committed
100
	epubkey, done, err := ci.GenerateEKeyPair(exchange) // Generate EphemeralPubKey
101 102 103
	if err != nil {
		return nil, nil, err
	}
104

105
	var handshake bytes.Buffer // Gather corpus to sign.
106
	handshake.Write(encoded)
107 108
	handshake.Write(resp)
	handshake.Write(epubkey)
109

110 111 112 113
	exPacket := new(Exchange)

	exPacket.Epubkey = epubkey
	exPacket.Signature, err = self.PrivKey.Sign(handshake.Bytes())
114
	if err != nil {
115
		return nil, nil, err
116 117
	}

118
	exEncoded, err := proto.Marshal(exPacket)
119 120 121
	if err != nil {
		return nil, nil, err
	}
122 123 124 125 126 127

	out <- exEncoded

	// Parse their Exchange packet and generate a Finish packet.
	// Finish = E('Finish')
	resp1 := <-in
128

129 130
	exchangeResp := new(Exchange)
	err = proto.Unmarshal(resp1, exchangeResp)
131
	if err != nil {
132
		return nil, nil, err
133 134
	}

135
	var theirHandshake bytes.Buffer
136 137 138 139
	_, err = theirHandshake.Write(resp)
	if err != nil {
		return nil, nil, err
	}
140
	_, err = theirHandshake.Write(encoded)
141 142 143 144 145 146 147
	if err != nil {
		return nil, nil, err
	}
	_, err = theirHandshake.Write(exchangeResp.GetEpubkey())
	if err != nil {
		return nil, nil, err
	}
148

149
	ok, err := remote.PubKey.Verify(theirHandshake.Bytes(), exchangeResp.GetSignature())
150
	if err != nil {
151 152 153 154 155
		return nil, nil, err
	}

	if !ok {
		return nil, nil, errors.New("Bad signature!")
156 157
	}

158 159 160
	secret, err := done(exchangeResp.GetEpubkey())
	if err != nil {
		return nil, nil, err
161 162
	}

163
	cmp := bytes.Compare(myPubKey, helloResp.GetPubkey())
Brendan Mc's avatar
Brendan Mc committed
164
	mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret)
165 166 167 168

	secureIn := make(chan []byte)
	secureOut := make(chan []byte)

Brendan Mc's avatar
Brendan Mc committed
169 170
	go secureInProxy(in, secureIn, hashType, tIV, tCKey, tMKey)
	go secureOutProxy(out, secureOut, hashType, mIV, mCKey, mMKey)
171

Brendan Mc's avatar
Brendan Mc committed
172 173 174 175 176 177 178 179 180
	finished := []byte("Finished")

	secureOut <- finished
	resp2 := <-secureIn

	if bytes.Compare(resp2, finished) != 0 {
		return nil, nil, errors.New("Negotiation failed.")
	}

181 182 183
	u.DOut("[%s] identify: Got node id: %s\n", self.ID.Pretty(), remote.ID.Pretty())

	return secureIn, secureOut, nil
184 185
}

Brendan Mc's avatar
Brendan Mc committed
186 187 188 189 190 191 192 193
func makeMac(hashType string, key []byte) (hash.Hash, int) {
	switch hashType {
	case "SHA1":
		return hmac.New(sha1.New, key), sha1.Size
	case "SHA512":
		return hmac.New(sha512.New, key), sha512.Size
	default:
		return hmac.New(sha256.New, key), sha256.Size
194 195
	}
}
196

197
func secureInProxy(in <-chan []byte, secureIn chan<- []byte, hashType string, tIV, tCKey, tMKey []byte) {
Brendan Mc's avatar
Brendan Mc committed
198 199
	theirBlock, _ := aes.NewCipher(tCKey)
	theirCipher := cipher.NewCTR(theirBlock, tIV)
200

Brendan Mc's avatar
Brendan Mc committed
201
	theirMac, macSize := makeMac(hashType, tMKey)
202

Brendan Mc's avatar
Brendan Mc committed
203 204 205
	for {
		data, ok := <-in
		if !ok {
206
			close(secureIn)
Brendan Mc's avatar
Brendan Mc committed
207 208
			return
		}
209

Brendan Mc's avatar
Brendan Mc committed
210 211 212
		if len(data) <= macSize {
			continue
		}
213

Brendan Mc's avatar
Brendan Mc committed
214 215
		mark := len(data) - macSize
		buff := make([]byte, mark)
216

Brendan Mc's avatar
Brendan Mc committed
217
		theirCipher.XORKeyStream(buff, data[0:mark])
218

Brendan Mc's avatar
Brendan Mc committed
219 220 221
		theirMac.Write(data[0:mark])
		expected := theirMac.Sum(nil)
		theirMac.Reset()
222

Brendan Mc's avatar
Brendan Mc committed
223
		hmacOk := hmac.Equal(data[mark:], expected)
224

Brendan Mc's avatar
Brendan Mc committed
225 226 227 228
		if hmacOk {
			secureIn <- buff
		} else {
			secureIn <- nil
229
		}
Brendan Mc's avatar
Brendan Mc committed
230 231
	}
}
232

233
func secureOutProxy(out chan<- []byte, secureOut <-chan []byte, hashType string, mIV, mCKey, mMKey []byte) {
Brendan Mc's avatar
Brendan Mc committed
234 235
	myBlock, _ := aes.NewCipher(mCKey)
	myCipher := cipher.NewCTR(myBlock, mIV)
236

Brendan Mc's avatar
Brendan Mc committed
237
	myMac, macSize := makeMac(hashType, mMKey)
238

Brendan Mc's avatar
Brendan Mc committed
239 240 241
	for {
		data, ok := <-secureOut
		if !ok {
242
			close(out)
Brendan Mc's avatar
Brendan Mc committed
243 244
			return
		}
245

Brendan Mc's avatar
Brendan Mc committed
246 247 248
		if len(data) == 0 {
			continue
		}
249

Brendan Mc's avatar
Brendan Mc committed
250
		buff := make([]byte, len(data)+macSize)
251

Brendan Mc's avatar
Brendan Mc committed
252
		myCipher.XORKeyStream(buff, data)
253

Brendan Mc's avatar
Brendan Mc committed
254 255 256
		myMac.Write(buff[0:len(data)])
		copy(buff[len(data):], myMac.Sum(nil))
		myMac.Reset()
257

Brendan Mc's avatar
Brendan Mc committed
258 259 260
		out <- buff
	}
}
Siraj Ravel's avatar
Siraj Ravel committed
261 262

// IDFromPubKey returns Nodes ID given its public key
Siraj Ravel's avatar
Siraj Ravel committed
263
func IDFromPubKey(pk ci.PubKey) (peer.ID, error) {
Brendan Mc's avatar
Brendan Mc committed
264 265 266 267 268 269 270 271 272
	b, err := pk.Bytes()
	if err != nil {
		return nil, err
	}
	hash, err := u.Hash(b)
	if err != nil {
		return nil, err
	}
	return peer.ID(hash), nil
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
}

// Determines which algorithm to use.  Note:  f(a, b) = f(b, a)
func selectBest(myPrefs, theirPrefs string) (string, error) {
	// Person with greatest hash gets first choice.
	myHash, err := u.Hash([]byte(myPrefs))
	if err != nil {
		return "", err
	}

	theirHash, err := u.Hash([]byte(theirPrefs))
	if err != nil {
		return "", err
	}

	cmp := bytes.Compare(myHash, theirHash)
	var firstChoiceArr, secChoiceArr []string

	if cmp == -1 {
		firstChoiceArr = strings.Split(theirPrefs, ",")
		secChoiceArr = strings.Split(myPrefs, ",")
	} else if cmp == 1 {
		firstChoiceArr = strings.Split(myPrefs, ",")
		secChoiceArr = strings.Split(theirPrefs, ",")
	} else { // Exact same preferences.
		myPrefsArr := strings.Split(myPrefs, ",")
		return myPrefsArr[0], nil
	}

	for _, secChoice := range secChoiceArr {
		for _, firstChoice := range firstChoiceArr {
			if firstChoice == secChoice {
				return firstChoice, nil
			}
		}
	}

	return "", errors.New("No algorithms in common!")
}