github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/crypto/spipe/handshake.go (about) 1 // package spipe handles establishing secure communication between two peers. 2 package spipe 3 4 import ( 5 "bytes" 6 "errors" 7 "fmt" 8 "strings" 9 10 "crypto/aes" 11 "crypto/cipher" 12 "crypto/hmac" 13 "crypto/rand" 14 "crypto/sha1" 15 "crypto/sha256" 16 "crypto/sha512" 17 bfish "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.crypto/blowfish" 18 "hash" 19 20 proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" 21 22 ci "github.com/jbenet/go-ipfs/crypto" 23 pb "github.com/jbenet/go-ipfs/crypto/spipe/internal/pb" 24 peer "github.com/jbenet/go-ipfs/peer" 25 u "github.com/jbenet/go-ipfs/util" 26 ) 27 28 var log = u.Logger("handshake") 29 30 // List of supported ECDH curves 31 var SupportedExchanges = "P-256,P-224,P-384,P-521" 32 33 // List of supported Ciphers 34 var SupportedCiphers = "AES-256,AES-128,Blowfish" 35 36 // List of supported Hashes 37 var SupportedHashes = "SHA256,SHA512" 38 39 // ErrUnsupportedKeyType is returned when a private key cast/type switch fails. 40 var ErrUnsupportedKeyType = errors.New("unsupported key type") 41 42 // ErrClosed signals the closing of a connection. 43 var ErrClosed = errors.New("connection closed") 44 45 // handsahke performs initial communication over insecure channel to share 46 // keys, IDs, and initiate communication. 47 func (s *SecurePipe) handshake() error { 48 // Generate and send Hello packet. 49 // Hello = (rand, PublicKey, Supported) 50 nonce := make([]byte, 16) 51 _, err := rand.Read(nonce) 52 if err != nil { 53 return err 54 } 55 56 log.Debugf("handshake: %s <--> %s", s.local, s.remote) 57 myPubKey, err := s.local.PubKey().Bytes() 58 if err != nil { 59 return err 60 } 61 62 proposeMsg := new(pb.Propose) 63 proposeMsg.Rand = nonce 64 proposeMsg.Pubkey = myPubKey 65 proposeMsg.Exchanges = &SupportedExchanges 66 proposeMsg.Ciphers = &SupportedCiphers 67 proposeMsg.Hashes = &SupportedHashes 68 69 encoded, err := proto.Marshal(proposeMsg) 70 if err != nil { 71 return err 72 } 73 74 // Send our Propose packet 75 select { 76 case s.insecure.Out <- encoded: 77 case <-s.ctx.Done(): 78 return ErrClosed 79 } 80 81 // Parse their Propose packet and generate an Exchange packet. 82 // Exchange = (EphemeralPubKey, Signature) 83 var resp []byte 84 select { 85 case <-s.ctx.Done(): 86 return ErrClosed 87 case resp = <-s.insecure.In: 88 } 89 90 // u.POut("received encoded handshake\n") 91 proposeResp := new(pb.Propose) 92 err = proto.Unmarshal(resp, proposeResp) 93 if err != nil { 94 return err 95 } 96 97 // get remote identity 98 remotePubKey, err := ci.UnmarshalPublicKey(proposeResp.GetPubkey()) 99 if err != nil { 100 return err 101 } 102 103 // get or construct peer 104 s.remote, err = getOrConstructPeer(s.peers, remotePubKey) 105 if err != nil { 106 return err 107 } 108 log.Debugf("%s Remote Peer Identified as %s", s.local, s.remote) 109 110 exchange, err := SelectBest(SupportedExchanges, proposeResp.GetExchanges()) 111 if err != nil { 112 return err 113 } 114 115 cipherType, err := SelectBest(SupportedCiphers, proposeResp.GetCiphers()) 116 if err != nil { 117 return err 118 } 119 120 hashType, err := SelectBest(SupportedHashes, proposeResp.GetHashes()) 121 if err != nil { 122 return err 123 } 124 125 // u.POut("Selected %s %s %s\n", exchange, cipherType, hashType) 126 epubkey, genSharedKey, err := ci.GenerateEKeyPair(exchange) // Generate EphemeralPubKey 127 128 var handshake bytes.Buffer // Gather corpus to sign. 129 handshake.Write(encoded) 130 handshake.Write(resp) 131 handshake.Write(epubkey) 132 133 exPacket := new(pb.Exchange) 134 135 exPacket.Epubkey = epubkey 136 exPacket.Signature, err = s.local.PrivKey().Sign(handshake.Bytes()) 137 if err != nil { 138 return err 139 } 140 141 exEncoded, err := proto.Marshal(exPacket) 142 143 // send out Exchange packet 144 select { 145 case s.insecure.Out <- exEncoded: 146 case <-s.ctx.Done(): 147 return ErrClosed 148 } 149 150 // Parse their Exchange packet and generate a Finish packet. 151 // Finish = E('Finish') 152 var resp1 []byte 153 select { 154 case <-s.ctx.Done(): 155 return ErrClosed 156 case resp1 = <-s.insecure.In: 157 } 158 159 exchangeResp := new(pb.Exchange) 160 err = proto.Unmarshal(resp1, exchangeResp) 161 if err != nil { 162 return err 163 } 164 165 var theirHandshake bytes.Buffer 166 theirHandshake.Write(resp) 167 theirHandshake.Write(encoded) 168 theirHandshake.Write(exchangeResp.GetEpubkey()) 169 170 // u.POut("Remote Peer Identified as %s\n", s.remote) 171 ok, err := s.remote.PubKey().Verify(theirHandshake.Bytes(), exchangeResp.GetSignature()) 172 if err != nil { 173 return err 174 } 175 176 if !ok { 177 return errors.New("Bad signature!") 178 } 179 180 secret, err := genSharedKey(exchangeResp.GetEpubkey()) 181 if err != nil { 182 return err 183 } 184 185 cmp := bytes.Compare(myPubKey, proposeResp.GetPubkey()) 186 187 mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret) 188 189 go s.handleSecureIn(hashType, cipherType, tIV, tCKey, tMKey) 190 go s.handleSecureOut(hashType, cipherType, mIV, mCKey, mMKey) 191 192 finished := []byte("Finished") 193 194 // send finished msg 195 select { 196 case <-s.ctx.Done(): 197 return ErrClosed 198 case s.Out <- finished: 199 } 200 201 // recv finished msg 202 var resp2 []byte 203 select { 204 case <-s.ctx.Done(): 205 return ErrClosed 206 case resp2 = <-s.In: 207 } 208 209 if bytes.Compare(resp2, finished) != 0 { 210 return fmt.Errorf("Negotiation failed, got: %s", resp2) 211 } 212 213 log.Debugf("%s handshake: Got node id: %s", s.local, s.remote) 214 return nil 215 } 216 217 func makeMac(hashType string, key []byte) (hash.Hash, int) { 218 switch hashType { 219 case "SHA1": 220 return hmac.New(sha1.New, key), sha1.Size 221 case "SHA512": 222 return hmac.New(sha512.New, key), sha512.Size 223 default: 224 return hmac.New(sha256.New, key), sha256.Size 225 } 226 } 227 228 func makeCipher(cipherType string, CKey []byte) (cipher.Block, error) { 229 switch cipherType { 230 case "AES-128", "AES-256": 231 return aes.NewCipher(CKey) 232 case "Blowfish": 233 return bfish.NewCipher(CKey) 234 default: 235 return nil, fmt.Errorf("Unrecognized cipher string: %s", cipherType) 236 } 237 } 238 239 func (s *SecurePipe) handleSecureIn(hashType, cipherType string, tIV, tCKey, tMKey []byte) { 240 theirBlock, err := makeCipher(cipherType, tCKey) 241 if err != nil { 242 log.Criticalf("Invalid Cipher: %s", err) 243 s.cancel() 244 return 245 } 246 theirCipher := cipher.NewCTR(theirBlock, tIV) 247 248 theirMac, macSize := makeMac(hashType, tMKey) 249 250 for { 251 var data []byte 252 ok := true 253 254 select { 255 case <-s.ctx.Done(): 256 ok = false // return out 257 case data, ok = <-s.insecure.In: 258 } 259 260 if !ok { 261 close(s.Duplex.In) 262 return 263 } 264 265 // log.Debug("[peer %s] secure in [from = %s] %d", s.local, s.remote, len(data)) 266 if len(data) <= macSize { 267 continue 268 } 269 270 mark := len(data) - macSize 271 272 theirMac.Write(data[0:mark]) 273 expected := theirMac.Sum(nil) 274 theirMac.Reset() 275 276 hmacOk := hmac.Equal(data[mark:], expected) 277 if !hmacOk { 278 continue 279 } 280 281 theirCipher.XORKeyStream(data, data[0:mark]) 282 283 s.Duplex.In <- data[:mark] 284 } 285 } 286 287 func (s *SecurePipe) handleSecureOut(hashType, cipherType string, mIV, mCKey, mMKey []byte) { 288 myBlock, err := makeCipher(cipherType, mCKey) 289 if err != nil { 290 log.Criticalf("Invalid Cipher: %s", err) 291 s.cancel() 292 return 293 } 294 myCipher := cipher.NewCTR(myBlock, mIV) 295 296 myMac, macSize := makeMac(hashType, mMKey) 297 298 for { 299 var data []byte 300 ok := true 301 302 select { 303 case <-s.ctx.Done(): 304 ok = false // return out 305 case data, ok = <-s.Out: 306 } 307 308 if !ok { 309 close(s.insecure.Out) 310 return 311 } 312 313 if len(data) == 0 { 314 continue 315 } 316 317 buff := make([]byte, len(data)+macSize) 318 319 myCipher.XORKeyStream(buff, data) 320 321 myMac.Write(buff[0:len(data)]) 322 copy(buff[len(data):], myMac.Sum(nil)) 323 myMac.Reset() 324 325 // log.Debug("[peer %s] secure out [to = %s] %d", s.local, s.remote, len(buff)) 326 s.insecure.Out <- buff 327 } 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 := u.Hash([]byte(myPrefs)) 334 theirHash := u.Hash([]byte(theirPrefs)) 335 336 cmp := bytes.Compare(myHash, theirHash) 337 var firstChoiceArr, secChoiceArr []string 338 339 if cmp == -1 { 340 firstChoiceArr = strings.Split(theirPrefs, ",") 341 secChoiceArr = strings.Split(myPrefs, ",") 342 } else if cmp == 1 { 343 firstChoiceArr = strings.Split(myPrefs, ",") 344 secChoiceArr = strings.Split(theirPrefs, ",") 345 } else { // Exact same preferences. 346 myPrefsArr := strings.Split(myPrefs, ",") 347 return myPrefsArr[0], nil 348 } 349 350 for _, secChoice := range secChoiceArr { 351 for _, firstChoice := range firstChoiceArr { 352 if firstChoice == secChoice { 353 return firstChoice, nil 354 } 355 } 356 } 357 358 return "", errors.New("No algorithms in common!") 359 } 360 361 // getOrConstructPeer attempts to fetch a peer from a peerstore. 362 // if succeeds, verify ID and PubKey match. 363 // else, construct it. 364 func getOrConstructPeer(peers peer.Peerstore, rpk ci.PubKey) (peer.Peer, error) { 365 366 rid, err := peer.IDFromPubKey(rpk) 367 if err != nil { 368 return nil, err 369 } 370 371 npeer, err := peers.Get(rid) 372 if err != nil { 373 return nil, err // unexpected error happened. 374 } 375 376 // public key verification happens in Peer.VerifyAndSetPubKey 377 if err := npeer.VerifyAndSetPubKey(rpk); err != nil { 378 return nil, err // pubkey mismatch or other problem 379 } 380 return npeer, nil 381 }