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  }