@cryptotaxi247 / kubo / commits / 33a9e147d

Implemented a basic version of TLS.

Brendan Mc committed Sep 3, 2014 at 21:09 UTC 33a9e147d62a5fc8147364f2db49ee7c78cd2a23
3 files changed +461 -60
identify/identify.go
+391 -47
@@ -6,100 +6,230 @@ import (
6 "bytes"
7 "errors"
8
9 + "crypto/aes"
10 + "crypto/cipher"
11 + "crypto/elliptic"
12 + "crypto/hmac"
13 + "crypto/rand"
14 + "crypto/sha1"
15 + "crypto/sha256"
16 + "crypto/sha512"
17 + "hash"
18 + "math/big"
19 + "strings"
20 +
21 proto "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 +// List of supported protocols--each section in order of preference.
28 +// Takes the form: ECDH curves : Ciphers : Hashes
29 +var SupportedExchanges = "P-256,P-224,P-384,P-521"
30 +var SupportedCiphers = "AES-256,AES-128"
31 +var SupportedHashes = "SHA256,SHA512,SHA1"
32 +
33 // ErrUnsupportedKeyType is returned when a private key cast/type switch fails.
34 var ErrUnsupportedKeyType = errors.New("unsupported key type")
35
36 // Perform initial communication with this peer to share node ID's and
37 // initiate communication
20 -func Handshake(self, remote *peer.Peer, in, out chan []byte) error {
21 - encoded, err := buildHandshake(self)
38 +func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan []byte, error) {
39 + // Generate and send Hello packet.
40 + // Hello = (rand, PublicKey, Supported)
41 + nonce := make([]byte, 16)
42 + rand.Read(nonce)
43 +
44 + hello := new(Hello)
45 +
46 + myPubKey, err := self.PubKey.Bytes()
47 + if err != nil {
48 + return nil, nil, err
49 + }
50 +
51 + hello.Rand = nonce
52 + hello.Pubkey = myPubKey
53 + hello.Exchanges = &SupportedExchanges
54 + hello.Ciphers = &SupportedCiphers
55 + hello.Hashes = &SupportedHashes
56 +
57 + encoded, err := proto.Marshal(hello)
58 if err != nil {
23 - return err
59 + return nil, nil, err
60 }
61 +
62 out <- encoded
63 +
64 + // Parse their Hello packet and generate an Exchange packet.
65 + // Exchange = (EphemeralPubKey, Signature)
66 resp := <-in
67
28 - pbresp := new(Identify)
29 - err = proto.Unmarshal(resp, pbresp)
68 + helloResp := new(Hello)
69 + err = proto.Unmarshal(resp, helloResp)
70 if err != nil {
31 - return err
71 + return nil, nil, err
72 }
73
34 - // Verify that the given ID matches their given public key
35 - if verifyErr := verifyID(peer.ID(pbresp.GetId()), pbresp.GetPubkey()); verifyErr != nil {
36 - return verifyErr
74 + remote.PubKey, err = ci.UnmarshalPublicKey(helloResp.GetPubkey())
75 + if err != nil {
76 + return nil, nil, err
77 }
78
39 - pubkey, err := ci.UnmarshalPublicKey(pbresp.GetPubkey())
79 + remote.ID, err = IdFromPubKey(remote.PubKey)
80 if err != nil {
41 - return err
81 + return nil, nil, err
82 }
83
44 - // Challenge peer to ensure they own the given pubkey
45 - secret := self.PrivKey.GenSecret()
46 - encrypted, err := pubkey.Encrypt(secret)
84 + exchange, err := selectBest(SupportedExchanges, helloResp.GetExchanges())
85 if err != nil {
48 - //... this is odd
49 - return err
86 + return nil, nil, err
87 }
88
52 - out <- encrypted
53 - challenge := <-in
54 -
55 - // Decrypt challenge and send plaintext to partner
56 - plain, err := self.PrivKey.Decrypt(challenge)
89 + cipherType, err := selectBest(SupportedExchanges, helloResp.GetCiphers())
90 if err != nil {
58 - return err
91 + return nil, nil, err
92 }
93
61 - out <- plain
62 - chalResp := <-in
63 - if !bytes.Equal(chalResp, secret) {
64 - return errors.New("Recieved incorrect challenge response!")
94 + hashType, err := selectBest(SupportedExchanges, helloResp.GetHashes())
95 + if err != nil {
96 + return nil, nil, err
97 }
98
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())
99 + epubkey, done, err := generateEPubKey(exchange) // Generate EphemeralPubKey
100
71 - return nil
72 -}
101 + var handshake bytes.Buffer // Gather corpus to sign.
102 + handshake.Write(encoded)
103 + handshake.Write(resp)
104 + handshake.Write(epubkey)
105
74 -func buildHandshake(self *peer.Peer) ([]byte, error) {
75 - pkb, err := self.PubKey.Bytes()
106 + exPacket := new(Exchange)
107 +
108 + exPacket.Epubkey = epubkey
109 + exPacket.Signature, err = self.PrivKey.Sign(handshake.Bytes())
110 if err != nil {
77 - return nil, err
111 + return nil, nil, err
112 }
113
80 - pmes := new(Identify)
81 - pmes.Id = []byte(self.ID)
82 - pmes.Pubkey = pkb
114 + exEncoded, err := proto.Marshal(exPacket)
115 +
116 + out <- exEncoded
117 +
118 + // Parse their Exchange packet and generate a Finish packet.
119 + // Finish = E('Finish')
120 + resp1 := <-in
121
84 - encoded, err := proto.Marshal(pmes)
122 + exchangeResp := new(Exchange)
123 + err = proto.Unmarshal(resp1, exchangeResp)
124 if err != nil {
86 - return nil, err
125 + return nil, nil, err
126 }
127
89 - return encoded, nil
90 -}
128 + var theirHandshake bytes.Buffer
129 + theirHandshake.Write(resp)
130 + theirHandshake.Write(encoded)
131 + theirHandshake.Write(exchangeResp.GetEpubkey())
132
92 -func verifyID(id peer.ID, pubkey []byte) error {
93 - hash, err := u.Hash(pubkey)
133 + ok, err := remote.PubKey.Verify(theirHandshake.Bytes(), exchangeResp.GetSignature())
134 if err != nil {
95 - return err
135 + return nil, nil, err
136 + }
137 +
138 + if !ok {
139 + return nil, nil, errors.New("Bad signature!")
140 }
141
98 - if id.Equal(peer.ID(hash)) {
99 - return nil
142 + secret, err := done(exchangeResp.GetEpubkey())
143 + if err != nil {
144 + return nil, nil, err
145 }
146
102 - return errors.New("ID did not match public key!")
147 + cmp := bytes.Compare(myPubKey, helloResp.GetPubkey())
148 + mIV, tIV, mCKey, tCKey, mMKey, tMKey := keyGenerator(cmp, cipherType, hashType, secret)
149 +
150 + secureIn := make(chan []byte)
151 + secureOut := make(chan []byte)
152 +
153 + go func() {
154 + myBlock, _ := aes.NewCipher(mCKey)
155 + myCipher := cipher.NewCTR(myBlock, mIV)
156 +
157 + theirBlock, _ := aes.NewCipher(tCKey)
158 + theirCipher := cipher.NewCTR(theirBlock, tIV)
159 +
160 + var myMac, theirMac hash.Hash
161 + var macSize int
162 +
163 + switch hashType {
164 + case "SHA1":
165 + myMac = hmac.New(sha1.New, mMKey)
166 + theirMac = hmac.New(sha1.New, tMKey)
167 + macSize = 20
168 +
169 + case "SHA256":
170 + myMac = hmac.New(sha256.New, mMKey)
171 + theirMac = hmac.New(sha256.New, tMKey)
172 + macSize = 32
173 +
174 + case "SHA512":
175 + myMac = hmac.New(sha512.New, mMKey)
176 + theirMac = hmac.New(sha512.New, tMKey)
177 + macSize = 64
178 + }
179 +
180 + for {
181 + select {
182 + case data, ok := <-secureOut:
183 + if !ok {
184 + return
185 + }
186 +
187 + if len(data) == 0 {
188 + continue
189 + }
190 +
191 + buff := make([]byte, len(data)+macSize)
192 +
193 + myCipher.XORKeyStream(buff, data)
194 +
195 + myMac.Write(buff[0:len(data)])
196 + copy(buff[len(data):], myMac.Sum(nil))
197 + myMac.Reset()
198 +
199 + out <- buff
200 +
201 + case data, ok := <-in:
202 + if !ok {
203 + return
204 + }
205 +
206 + if len(data) <= macSize {
207 + continue
208 + }
209 +
210 + mark := len(data) - macSize
211 + buff := make([]byte, mark)
212 +
213 + theirCipher.XORKeyStream(buff, data[0:mark])
214 +
215 + theirMac.Write(data[0:mark])
216 + expected := theirMac.Sum(nil)
217 + theirMac.Reset()
218 +
219 + hmacOk := hmac.Equal(data[mark:], expected)
220 +
221 + if hmacOk {
222 + secureIn <- buff
223 + } else {
224 + secureIn <- nil
225 + }
226 + }
227 + }
228 + }()
229 +
230 + u.DOut("[%s] identify: Got node id: %s\n", self.ID.Pretty(), remote.ID.Pretty())
231 +
232 + return secureIn, secureOut, nil
233 }
234
235 func IdFromPubKey(pk ci.PubKey) (peer.ID, error) {
@@ -113,3 +243,217 @@ func IdFromPubKey(pk ci.PubKey) (peer.ID, error) {
243 }
244 return peer.ID(hash), nil
245 }
246 +
247 +// Generates a set of keys for each party by stretching the shared key.
248 +// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
249 +func keyGenerator(cmp int, cipherType string, hashType string, secret []byte) ([]byte, []byte, []byte, []byte, []byte, []byte) {
250 + var cipherKeySize int
251 + switch cipherType {
252 + case "AES128":
253 + cipherKeySize = 2 * 16
254 + case "AES256":
255 + cipherKeySize = 2 * 32
256 + }
257 +
258 + ivSize := 16
259 + hmacKeySize := 20
260 +
261 + seed := []byte("key expansion")
262 +
263 + result := make([]byte, 2*(ivSize+cipherKeySize+hmacKeySize))
264 +
265 + var h func() hash.Hash
266 +
267 + switch hashType {
268 + case "SHA1":
269 + h = sha1.New
270 + case "SHA256":
271 + h = sha256.New
272 + case "SHA512":
273 + h = sha512.New
274 + }
275 +
276 + m := hmac.New(h, secret)
277 + m.Write(seed)
278 +
279 + a := m.Sum(nil)
280 +
281 + j := 0
282 + for j < len(result) {
283 + m.Reset()
284 + m.Write(a)
285 + m.Write(seed)
286 + b := m.Sum(nil)
287 +
288 + todo := len(b)
289 +
290 + if j+todo > len(result) {
291 + todo = len(result) - j
292 + }
293 +
294 + copy(result[j:j+todo], b)
295 +
296 + j += todo
297 +
298 + m.Reset()
299 + m.Write(a)
300 + a = m.Sum(nil)
301 + }
302 +
303 + myResult := make([]byte, ivSize+cipherKeySize+hmacKeySize)
304 + theirResult := make([]byte, ivSize+cipherKeySize+hmacKeySize)
305 +
306 + half := len(result) / 2
307 +
308 + if cmp == 1 {
309 + copy(myResult, result[:half])
310 + copy(theirResult, result[half:])
311 + } else if cmp == -1 {
312 + copy(myResult, result[half:])
313 + copy(theirResult, result[:half])
314 + } else { // Shouldn't happen, but oh well.
315 + copy(myResult, result[half:])
316 + copy(theirResult, result[half:])
317 + }
318 +
319 + myIV := myResult[0:ivSize]
320 + myCKey := myResult[ivSize : ivSize+cipherKeySize]
321 + myMKey := myResult[ivSize+cipherKeySize:]
322 +
323 + theirIV := theirResult[0:ivSize]
324 + theirCKey := theirResult[ivSize : ivSize+cipherKeySize]
325 + theirMKey := theirResult[ivSize+cipherKeySize:]
326 +
327 + return myIV, theirIV, myCKey, theirCKey, myMKey, theirMKey
328 +}
329 +
330 +// Determines which algorithm to use. Note: f(a, b) = f(b, a)
331 +func selectBest(myPrefs, theirPrefs string) (string, error) {
332 + // Person with greatest hash gets first choice.
333 + myHash, err := u.Hash([]byte(myPrefs))
334 + if err != nil {
335 + return "", err
336 + }
337 +
338 + theirHash, err := u.Hash([]byte(theirPrefs))
339 + if err != nil {
340 + return "", err
341 + }
342 +
343 + cmp := bytes.Compare(myHash, theirHash)
344 + var firstChoiceArr, secChoiceArr []string
345 +
346 + if cmp == -1 {
347 + firstChoiceArr = strings.Split(theirPrefs, ",")
348 + secChoiceArr = strings.Split(myPrefs, ",")
349 + } else if cmp == 1 {
350 + firstChoiceArr = strings.Split(myPrefs, ",")
351 + secChoiceArr = strings.Split(theirPrefs, ",")
352 + } else { // Exact same preferences.
353 + myPrefsArr := strings.Split(myPrefs, ",")
354 + return myPrefsArr[0], nil
355 + }
356 +
357 + for _, secChoice := range secChoiceArr {
358 + for _, firstChoice := range firstChoiceArr {
359 + if firstChoice == secChoice {
360 + return firstChoice, nil
361 + }
362 + }
363 + }
364 +
365 + return "", errors.New("No algorithms in common!")
366 +}
367 +
368 +// Generates an ephemeral public key and returns a function that will compute
369 +// the shared secret key.
370 +//
371 +// Focuses only on ECDH now, but can be made more general in the future.
372 +func generateEPubKey(exchange string) ([]byte, func([]byte) ([]byte, error), error) {
373 + genKeyPair := func(curve elliptic.Curve) ([]byte, []byte, error) {
374 + priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
375 + if err != nil {
376 + return nil, nil, err
377 + }
378 +
379 + var pubKey bytes.Buffer
380 + pubKey.Write(x.Bytes())
381 + pubKey.Write(y.Bytes())
382 +
383 + return pubKey.Bytes(), priv, nil
384 + }
385 +
386 + genSec := func(curve elliptic.Curve, theirPub []byte, myPriv []byte) ([]byte, error) {
387 + // Verify and unpack node's public key.
388 + curveSize := curve.Params().BitSize
389 +
390 + if len(theirPub) != (curveSize / 2) {
391 + return nil, errors.New("Malformed public key.")
392 + }
393 +
394 + bound := (curveSize / 8)
395 + x := big.NewInt(0)
396 + y := big.NewInt(0)
397 +
398 + x.SetBytes(theirPub[0:bound])
399 + y.SetBytes(theirPub[bound : bound*2])
400 +
401 + if !curve.IsOnCurve(x, y) {
402 + return nil, errors.New("Invalid public key.")
403 + }
404 +
405 + // Generate shared secret.
406 + secret, _ := curve.ScalarMult(x, y, myPriv)
407 +
408 + return secret.Bytes(), nil
409 + }
410 +
411 + switch exchange {
412 + case "P-224":
413 + curve := elliptic.P224()
414 + pub, priv, err := genKeyPair(curve)
415 + if err != nil {
416 + return nil, nil, err
417 + }
418 +
419 + done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) }
420 +
421 + return pub, done, nil
422 +
423 + case "P-256":
424 + curve := elliptic.P256()
425 + pub, priv, err := genKeyPair(curve)
426 + if err != nil {
427 + return nil, nil, err
428 + }
429 +
430 + done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) }
431 +
432 + return pub, done, nil
433 +
434 + case "P-384":
435 + curve := elliptic.P384()
436 + pub, priv, err := genKeyPair(curve)
437 + if err != nil {
438 + return nil, nil, err
439 + }
440 +
441 + done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) }
442 +
443 + return pub, done, nil
444 +
445 + case "P-521":
446 + curve := elliptic.P521()
447 + pub, priv, err := genKeyPair(curve)
448 + if err != nil {
449 + return nil, nil, err
450 + }
451 +
452 + done := func(theirs []byte) ([]byte, error) { return genSec(curve, theirs, priv) }
453 +
454 + return pub, done, nil
455 +
456 + }
457 +
458 + return nil, nil, errors.New("Something silly happened.")
459 +}
identify/message.pb.go
+60 -11
@@ -9,7 +9,8 @@ It is generated from these files:
9 message.proto
10
11 It has these top-level messages:
12 - Identify
12 + Hello
13 + Exchange
14 */
15 package identify
16
@@ -20,29 +21,77 @@ import math "math"
21 var _ = proto.Marshal
22 var _ = math.Inf
23
23 -type Identify struct {
24 - Id []byte `protobuf:"bytes,1,req,name=id" json:"id,omitempty"`
25 - Pubkey []byte `protobuf:"bytes,2,req,name=pubkey" json:"pubkey,omitempty"`
26 - XXX_unrecognized []byte `json:"-"`
24 +type Hello struct {
25 + Rand []byte `protobuf:"bytes,1,req,name=rand" json:"rand,omitempty"`
26 + Pubkey []byte `protobuf:"bytes,2,req,name=pubkey" json:"pubkey,omitempty"`
27 + Exchanges *string `protobuf:"bytes,3,req,name=exchanges" json:"exchanges,omitempty"`
28 + Ciphers *string `protobuf:"bytes,4,req,name=ciphers" json:"ciphers,omitempty"`
29 + Hashes *string `protobuf:"bytes,5,req,name=hashes" json:"hashes,omitempty"`
30 + XXX_unrecognized []byte `json:"-"`
31 }
32
29 -func (m *Identify) Reset() { *m = Identify{} }
30 -func (m *Identify) String() string { return proto.CompactTextString(m) }
31 -func (*Identify) ProtoMessage() {}
33 +func (m *Hello) Reset() { *m = Hello{} }
34 +func (m *Hello) String() string { return proto.CompactTextString(m) }
35 +func (*Hello) ProtoMessage() {}
36
33 -func (m *Identify) GetId() []byte {
37 +func (m *Hello) GetRand() []byte {
38 if m != nil {
35 - return m.Id
39 + return m.Rand
40 }
41 return nil
42 }
43
40 -func (m *Identify) GetPubkey() []byte {
44 +func (m *Hello) GetPubkey() []byte {
45 if m != nil {
46 return m.Pubkey
47 }
48 return nil
49 }
50
51 +func (m *Hello) GetExchanges() string {
52 + if m != nil && m.Exchanges != nil {
53 + return *m.Exchanges
54 + }
55 + return ""
56 +}
57 +
58 +func (m *Hello) GetCiphers() string {
59 + if m != nil && m.Ciphers != nil {
60 + return *m.Ciphers
61 + }
62 + return ""
63 +}
64 +
65 +func (m *Hello) GetHashes() string {
66 + if m != nil && m.Hashes != nil {
67 + return *m.Hashes
68 + }
69 + return ""
70 +}
71 +
72 +type Exchange struct {
73 + Epubkey []byte `protobuf:"bytes,1,req,name=epubkey" json:"epubkey,omitempty"`
74 + Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"`
75 + XXX_unrecognized []byte `json:"-"`
76 +}
77 +
78 +func (m *Exchange) Reset() { *m = Exchange{} }
79 +func (m *Exchange) String() string { return proto.CompactTextString(m) }
80 +func (*Exchange) ProtoMessage() {}
81 +
82 +func (m *Exchange) GetEpubkey() []byte {
83 + if m != nil {
84 + return m.Epubkey
85 + }
86 + return nil
87 +}
88 +
89 +func (m *Exchange) GetSignature() []byte {
90 + if m != nil {
91 + return m.Signature
92 + }
93 + return nil
94 +}
95 +
96 func init() {
97 }
identify/message.proto
+10 -2
@@ -1,6 +1,14 @@
1 package identify;
2
3 -message Identify {
4 - required bytes id = 1;
3 +message Hello {
4 + required bytes rand = 1;
5 required bytes pubkey = 2;
6 + required string exchanges = 3;
7 + required string ciphers = 4;
8 + required string hashes = 5;
9 +}
10 +
11 +message Exchange {
12 + required bytes epubkey = 1;
13 + required bytes signature = 2;
14 }