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
}