identify.go 7.4 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 20
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

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

27 28 29 30 31 32
// 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"

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

36 37
// Performs initial communication with this peer to share node ID's and
// initiate communication.  (secureIn, secureOut, error)
38
func Handshake(ctx context.Context, self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) {
39

40
	encodedHello, err := encodedProtoHello(self)
41
	if err != nil {
42
		return nil, nil, err
43
	}
44

45 46
	// TODO(brian): select on |ctx|
	out <- encodedHello
47 48 49

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

52 53
	helloResp := new(Hello)
	err = proto.Unmarshal(resp, helloResp)
54
	if err != nil {
55
		return nil, nil, err
56 57
	}

58 59 60
	remote.PubKey, err = ci.UnmarshalPublicKey(helloResp.GetPubkey())
	if err != nil {
		return nil, nil, err
61 62
	}

Siraj Ravel's avatar
Siraj Ravel committed
63
	remote.ID, err = IDFromPubKey(remote.PubKey)
64
	if err != nil {
65
		return nil, nil, err
66 67
	}

68
	exchange, err := selectBest(SupportedExchanges, helloResp.GetExchanges())
69
	if err != nil {
70
		return nil, nil, err
71 72
	}

Brendan Mc's avatar
Brendan Mc committed
73
	cipherType, err := selectBest(SupportedCiphers, helloResp.GetCiphers())
74
	if err != nil {
75
		return nil, nil, err
76 77
	}

Brendan Mc's avatar
Brendan Mc committed
78
	hashType, err := selectBest(SupportedHashes, helloResp.GetHashes())
79 80
	if err != nil {
		return nil, nil, err
81 82
	}

Brendan Mc's avatar
Brendan Mc committed
83
	epubkey, done, err := ci.GenerateEKeyPair(exchange) // Generate EphemeralPubKey
84 85 86
	if err != nil {
		return nil, nil, err
	}
87

88
	var handshake bytes.Buffer // Gather corpus to sign.
89
	handshake.Write(encodedHello)
90 91
	handshake.Write(resp)
	handshake.Write(epubkey)
92

93 94 95 96
	exPacket := new(Exchange)

	exPacket.Epubkey = epubkey
	exPacket.Signature, err = self.PrivKey.Sign(handshake.Bytes())
97
	if err != nil {
98
		return nil, nil, err
99 100
	}

101
	exEncoded, err := proto.Marshal(exPacket)
102 103 104
	if err != nil {
		return nil, nil, err
	}
105 106 107 108 109 110

	out <- exEncoded

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

112 113
	exchangeResp := new(Exchange)
	err = proto.Unmarshal(resp1, exchangeResp)
114
	if err != nil {
115
		return nil, nil, err
116 117
	}

118
	var theirHandshake bytes.Buffer
119 120 121 122
	_, err = theirHandshake.Write(resp)
	if err != nil {
		return nil, nil, err
	}
123
	_, err = theirHandshake.Write(encodedHello)
124 125 126 127 128 129 130
	if err != nil {
		return nil, nil, err
	}
	_, err = theirHandshake.Write(exchangeResp.GetEpubkey())
	if err != nil {
		return nil, nil, err
	}
131

132
	ok, err := remote.PubKey.Verify(theirHandshake.Bytes(), exchangeResp.GetSignature())
133
	if err != nil {
134 135 136 137 138
		return nil, nil, err
	}

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

141 142 143
	secret, err := done(exchangeResp.GetEpubkey())
	if err != nil {
		return nil, nil, err
144 145
	}

146 147 148 149 150
	myPubKey, err := self.PubKey.Bytes()
	if err != nil {
		return nil, nil, err
	}

151
	cmp := bytes.Compare(myPubKey, helloResp.GetPubkey())
Brendan Mc's avatar
Brendan Mc committed
152
	mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret)
153 154 155 156

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

Brendan Mc's avatar
Brendan Mc committed
157 158
	go secureInProxy(in, secureIn, hashType, tIV, tCKey, tMKey)
	go secureOutProxy(out, secureOut, hashType, mIV, mCKey, mMKey)
159

Brendan Mc's avatar
Brendan Mc committed
160 161 162 163 164 165 166 167 168
	finished := []byte("Finished")

	secureOut <- finished
	resp2 := <-secureIn

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

169 170 171
	u.DOut("[%s] identify: Got node id: %s\n", self.ID.Pretty(), remote.ID.Pretty())

	return secureIn, secureOut, nil
172 173
}

Brendan Mc's avatar
Brendan Mc committed
174 175 176 177 178 179 180 181
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
182 183
	}
}
184

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

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

Brendan Mc's avatar
Brendan Mc committed
191 192 193
	for {
		data, ok := <-in
		if !ok {
194
			close(secureIn)
Brendan Mc's avatar
Brendan Mc committed
195 196
			return
		}
197

Brendan Mc's avatar
Brendan Mc committed
198 199 200
		if len(data) <= macSize {
			continue
		}
201

Brendan Mc's avatar
Brendan Mc committed
202 203
		mark := len(data) - macSize
		buff := make([]byte, mark)
204

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

Brendan Mc's avatar
Brendan Mc committed
207 208 209
		theirMac.Write(data[0:mark])
		expected := theirMac.Sum(nil)
		theirMac.Reset()
210

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

Brendan Mc's avatar
Brendan Mc committed
213 214 215 216
		if hmacOk {
			secureIn <- buff
		} else {
			secureIn <- nil
217
		}
Brendan Mc's avatar
Brendan Mc committed
218 219
	}
}
220

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

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

Brendan Mc's avatar
Brendan Mc committed
227 228 229
	for {
		data, ok := <-secureOut
		if !ok {
230
			close(out)
Brendan Mc's avatar
Brendan Mc committed
231 232
			return
		}
233

Brendan Mc's avatar
Brendan Mc committed
234 235 236
		if len(data) == 0 {
			continue
		}
237

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

Brendan Mc's avatar
Brendan Mc committed
240
		myCipher.XORKeyStream(buff, data)
241

Brendan Mc's avatar
Brendan Mc committed
242 243 244
		myMac.Write(buff[0:len(data)])
		copy(buff[len(data):], myMac.Sum(nil))
		myMac.Reset()
245

Brendan Mc's avatar
Brendan Mc committed
246 247 248
		out <- buff
	}
}
Siraj Ravel's avatar
Siraj Ravel committed
249 250

// IDFromPubKey returns Nodes ID given its public key
Siraj Ravel's avatar
Siraj Ravel committed
251
func IDFromPubKey(pk ci.PubKey) (peer.ID, error) {
Brendan Mc's avatar
Brendan Mc committed
252 253 254 255 256 257 258 259 260
	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
261 262 263 264 265 266 267 268 269 270 271 272 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
}

// 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!")
}
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323

func genRandHello(self *peer.Peer) (*Hello, error) {
	hello := new(Hello)

	// Generate and send Hello packet.
	// Hello = (rand, PublicKey, Supported)
	nonce := make([]byte, 16)
	_, err := rand.Read(nonce)
	if err != nil {
		return nil, err
	}

	myPubKey, err := self.PubKey.Bytes()
	if err != nil {
		return nil, err
	}

	hello.Rand = nonce
	hello.Pubkey = myPubKey
	hello.Exchanges = &SupportedExchanges
	hello.Ciphers = &SupportedCiphers
	hello.Hashes = &SupportedHashes
	return hello, nil
}
324 325 326 327 328 329 330 331 332

func encodedProtoHello(self *peer.Peer) ([]byte, error) {
	h, err := genRandHello(self)
	if err != nil {
		return nil, err
	}

	return proto.Marshal(h)
}