| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | package p2p | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"crypto/ecdsa" | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	"crypto/elliptic" | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	"crypto/rand" | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 	"hash" | 
					
						
							| 
									
										
										
										
											2015-01-19 23:42:13 +00:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2015-02-14 00:25:47 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto/ecies" | 
					
						
							| 
									
										
										
										
											2015-01-26 14:50:12 +00:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto/secp256k1" | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto/sha3" | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/p2p/discover" | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-26 14:50:12 +00:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 | 
					
						
							|  |  |  | 	sigLen = 65 // elliptic S256 | 
					
						
							|  |  |  | 	pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte | 
					
						
							|  |  |  | 	shaLen = 32 // hash length (for nonce etc) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	authMsgLen  = sigLen + shaLen + pubLen + shaLen + 1 | 
					
						
							|  |  |  | 	authRespLen = pubLen + shaLen + 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	eciesBytes     = 65 + 16 + 32 | 
					
						
							|  |  |  | 	encAuthMsgLen  = authMsgLen + eciesBytes  // size of the final ECIES payload sent as initiator's handshake | 
					
						
							|  |  |  | 	encAuthRespLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // conn represents a remote connection after encryption handshake | 
					
						
							|  |  |  | // and protocol handshake have completed. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The MsgReadWriter is usually layered as follows: | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-03-04 16:27:37 +01:00
										 |  |  | //     netWrapper       (I/O timeouts, thread-safe ReadMsg, WriteMsg) | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | //     rlpxFrameRW      (message encoding, encryption, authentication) | 
					
						
							|  |  |  | //     bufio.ReadWriter (buffering) | 
					
						
							|  |  |  | //     net.Conn         (network I/O) | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | type conn struct { | 
					
						
							| 
									
										
										
										
											2015-02-27 03:06:55 +00:00
										 |  |  | 	MsgReadWriter | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	*protoHandshake | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-21 14:45:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | // secrets represents the connection secrets | 
					
						
							|  |  |  | // which are negotiated during the encryption handshake. | 
					
						
							|  |  |  | type secrets struct { | 
					
						
							|  |  |  | 	RemoteID              discover.NodeID | 
					
						
							|  |  |  | 	AES, MAC              []byte | 
					
						
							|  |  |  | 	EgressMAC, IngressMAC hash.Hash | 
					
						
							|  |  |  | 	Token                 []byte | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // protoHandshake is the RLP structure of the protocol handshake. | 
					
						
							|  |  |  | type protoHandshake struct { | 
					
						
							|  |  |  | 	Version    uint64 | 
					
						
							|  |  |  | 	Name       string | 
					
						
							|  |  |  | 	Caps       []Cap | 
					
						
							|  |  |  | 	ListenPort uint64 | 
					
						
							|  |  |  | 	ID         discover.NodeID | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // setupConn starts a protocol session on the given connection. | 
					
						
							|  |  |  | // It runs the encryption handshake and the protocol handshake. | 
					
						
							|  |  |  | // If dial is non-nil, the connection the local node is the initiator. | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | // If atcap is true, the connection will be disconnected with DiscTooManyPeers | 
					
						
							|  |  |  | // after the key exchange. | 
					
						
							|  |  |  | func setupConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node, atcap bool) (*conn, error) { | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if dial == nil { | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		return setupInboundConn(fd, prv, our, atcap) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		return setupOutboundConn(fd, prv, our, dial, atcap) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | func setupInboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, atcap bool) (*conn, error) { | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	secrets, err := receiverEncHandshake(fd, prv, nil) | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("encryption handshake failed: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-27 03:06:55 +00:00
										 |  |  | 	rw := newRlpxFrameRW(fd, secrets) | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 	if atcap { | 
					
						
							|  |  |  | 		SendItems(rw, discMsg, DiscTooManyPeers) | 
					
						
							|  |  |  | 		return nil, errors.New("we have too many peers") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Run the protocol handshake using authenticated messages. | 
					
						
							|  |  |  | 	rhs, err := readProtocolHandshake(rw, secrets.RemoteID, our) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-19 15:11:02 +01:00
										 |  |  | 	if err := Send(rw, handshakeMsg, our); err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		return nil, fmt.Errorf("protocol handshake write error: %v", err) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-04 16:27:37 +01:00
										 |  |  | 	return &conn{rw, rhs}, nil | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | func setupOutboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node, atcap bool) (*conn, error) { | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	secrets, err := initiatorEncHandshake(fd, prv, dial.ID, nil) | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("encryption handshake failed: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-27 03:06:55 +00:00
										 |  |  | 	rw := newRlpxFrameRW(fd, secrets) | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 	if atcap { | 
					
						
							|  |  |  | 		SendItems(rw, discMsg, DiscTooManyPeers) | 
					
						
							|  |  |  | 		return nil, errors.New("we have too many peers") | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 	// Run the protocol handshake using authenticated messages. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Note that even though writing the handshake is first, we prefer | 
					
						
							|  |  |  | 	// returning the handshake read error. If the remote side | 
					
						
							|  |  |  | 	// disconnects us early with a valid reason, we should return it | 
					
						
							|  |  |  | 	// as the error so it can be tracked elsewhere. | 
					
						
							|  |  |  | 	werr := make(chan error) | 
					
						
							|  |  |  | 	go func() { werr <- Send(rw, handshakeMsg, our) }() | 
					
						
							|  |  |  | 	rhs, err := readProtocolHandshake(rw, secrets.RemoteID, our) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := <-werr; err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("protocol handshake write error: %v", err) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if rhs.ID != dial.ID { | 
					
						
							|  |  |  | 		return nil, errors.New("dialed node id mismatch") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-04 16:27:37 +01:00
										 |  |  | 	return &conn{rw, rhs}, nil | 
					
						
							| 
									
										
										
										
											2015-01-29 03:16:10 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // encHandshake contains the state of the encryption handshake. | 
					
						
							|  |  |  | type encHandshake struct { | 
					
						
							|  |  |  | 	initiator bool | 
					
						
							|  |  |  | 	remoteID  discover.NodeID | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	remotePub            *ecies.PublicKey  // remote-pubk | 
					
						
							|  |  |  | 	initNonce, respNonce []byte            // nonce | 
					
						
							|  |  |  | 	randomPrivKey        *ecies.PrivateKey // ecdhe-random | 
					
						
							|  |  |  | 	remoteRandomPub      *ecies.PublicKey  // ecdhe-random-pubk | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // secrets is called after the handshake is completed. | 
					
						
							|  |  |  | // It extracts the connection secrets from the handshake values. | 
					
						
							|  |  |  | func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) { | 
					
						
							|  |  |  | 	ecdheSecret, err := h.randomPrivKey.GenerateShared(h.remoteRandomPub, sskLen, sskLen) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return secrets{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// derive base secrets from ephemeral key agreement | 
					
						
							|  |  |  | 	sharedSecret := crypto.Sha3(ecdheSecret, crypto.Sha3(h.respNonce, h.initNonce)) | 
					
						
							|  |  |  | 	aesSecret := crypto.Sha3(ecdheSecret, sharedSecret) | 
					
						
							|  |  |  | 	s := secrets{ | 
					
						
							|  |  |  | 		RemoteID: h.remoteID, | 
					
						
							|  |  |  | 		AES:      aesSecret, | 
					
						
							|  |  |  | 		MAC:      crypto.Sha3(ecdheSecret, aesSecret), | 
					
						
							|  |  |  | 		Token:    crypto.Sha3(sharedSecret), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// setup sha3 instances for the MACs | 
					
						
							|  |  |  | 	mac1 := sha3.NewKeccak256() | 
					
						
							|  |  |  | 	mac1.Write(xor(s.MAC, h.respNonce)) | 
					
						
							|  |  |  | 	mac1.Write(auth) | 
					
						
							|  |  |  | 	mac2 := sha3.NewKeccak256() | 
					
						
							|  |  |  | 	mac2.Write(xor(s.MAC, h.initNonce)) | 
					
						
							|  |  |  | 	mac2.Write(authResp) | 
					
						
							|  |  |  | 	if h.initiator { | 
					
						
							|  |  |  | 		s.EgressMAC, s.IngressMAC = mac1, mac2 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		s.EgressMAC, s.IngressMAC = mac2, mac1 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return s, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (h *encHandshake) ecdhShared(prv *ecdsa.PrivateKey) ([]byte, error) { | 
					
						
							|  |  |  | 	return ecies.ImportECDSA(prv).GenerateShared(h.remotePub, sskLen, sskLen) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // initiatorEncHandshake negotiates a session token on conn. | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | // it should be called on the dialing side of the connection. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // prv is the local client's private key. | 
					
						
							|  |  |  | // token is the token from a previous session with this node. | 
					
						
							|  |  |  | func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID, token []byte) (s secrets, err error) { | 
					
						
							|  |  |  | 	h, err := newInitiatorHandshake(remoteID) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return s, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	auth, err := h.authMsg(prv, token) | 
					
						
							| 
									
										
										
										
											2015-01-29 03:16:10 +00:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-01-29 03:16:10 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if _, err = conn.Write(auth); err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-01-20 16:47:46 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	response := make([]byte, encAuthRespLen) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if _, err = io.ReadFull(conn, response); err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	if err := h.decodeAuthResp(response, prv); err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-01-20 16:47:46 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	return h.secrets(auth, response) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-21 10:22:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) { | 
					
						
							|  |  |  | 	// generate random initiator nonce | 
					
						
							|  |  |  | 	n := make([]byte, shaLen) | 
					
						
							|  |  |  | 	if _, err := rand.Read(n); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// generate random keypair to use for signing | 
					
						
							|  |  |  | 	randpriv, err := ecies.GenerateKey(rand.Reader, crypto.S256(), nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rpub, err := remoteID.Pubkey() | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 		return nil, fmt.Errorf("bad remoteID: %v", err) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	h := &encHandshake{ | 
					
						
							|  |  |  | 		initiator:     true, | 
					
						
							|  |  |  | 		remoteID:      remoteID, | 
					
						
							|  |  |  | 		remotePub:     ecies.ImportECDSAPublic(rpub), | 
					
						
							|  |  |  | 		initNonce:     n, | 
					
						
							|  |  |  | 		randomPrivKey: randpriv, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return h, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // authMsg creates an encrypted initiator handshake message. | 
					
						
							|  |  |  | func (h *encHandshake) authMsg(prv *ecdsa.PrivateKey, token []byte) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 	var tokenFlag byte | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	if token == nil { | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 		// no session token found means we need to generate shared secret. | 
					
						
							|  |  |  | 		// ecies shared secret is used as initial session token for new peers | 
					
						
							|  |  |  | 		// generate shared key from prv and remote pubkey | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 		var err error | 
					
						
							|  |  |  | 		if token, err = h.ecdhShared(prv); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// for known peers, we use stored token from the previous session | 
					
						
							|  |  |  | 		tokenFlag = 0x01 | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	// sign known message: | 
					
						
							|  |  |  | 	//   ecdh-shared-secret^nonce for new peers | 
					
						
							|  |  |  | 	//   token^nonce for old peers | 
					
						
							|  |  |  | 	signed := xor(token, h.initNonce) | 
					
						
							|  |  |  | 	signature, err := crypto.Sign(signed, h.randomPrivKey.ExportECDSA()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	// encode auth message | 
					
						
							|  |  |  | 	// signature || sha3(ecdhe-random-pubk) || pubk || nonce || token-flag | 
					
						
							|  |  |  | 	msg := make([]byte, authMsgLen) | 
					
						
							|  |  |  | 	n := copy(msg, signature) | 
					
						
							|  |  |  | 	n += copy(msg[n:], crypto.Sha3(exportPubkey(&h.randomPrivKey.PublicKey))) | 
					
						
							|  |  |  | 	n += copy(msg[n:], crypto.FromECDSAPub(&prv.PublicKey)[1:]) | 
					
						
							|  |  |  | 	n += copy(msg[n:], h.initNonce) | 
					
						
							|  |  |  | 	msg[n] = tokenFlag | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	// encrypt auth message using remote-pubk | 
					
						
							|  |  |  | 	return ecies.Encrypt(rand.Reader, h.remotePub, msg, nil, nil) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // decodeAuthResp decode an encrypted authentication response message. | 
					
						
							|  |  |  | func (h *encHandshake) decodeAuthResp(auth []byte, prv *ecdsa.PrivateKey) error { | 
					
						
							|  |  |  | 	msg, err := crypto.Decrypt(prv, auth) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("could not decrypt auth response (%v)", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	h.respNonce = msg[pubLen : pubLen+shaLen] | 
					
						
							|  |  |  | 	h.remoteRandomPub, err = importPublicKey(msg[:pubLen]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// ignore token flag for now | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // receiverEncHandshake negotiates a session token on conn. | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | // it should be called on the listening side of the connection. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | // prv is the local client's private key. | 
					
						
							|  |  |  | // token is the token from a previous session with this node. | 
					
						
							|  |  |  | func receiverEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, token []byte) (s secrets, err error) { | 
					
						
							|  |  |  | 	// read remote auth sent by initiator. | 
					
						
							|  |  |  | 	auth := make([]byte, encAuthMsgLen) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if _, err := io.ReadFull(conn, auth); err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	h, err := decodeAuthMsg(prv, token, auth) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// send auth response | 
					
						
							|  |  |  | 	resp, err := h.authResp(prv, token) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return s, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, err = conn.Write(resp); err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 		return s, err | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-27 02:09:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	return h.secrets(auth, resp) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | func decodeAuthMsg(prv *ecdsa.PrivateKey, token []byte, auth []byte) (*encHandshake, error) { | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	h := new(encHandshake) | 
					
						
							|  |  |  | 	// generate random keypair for session | 
					
						
							|  |  |  | 	h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	// generate random nonce | 
					
						
							|  |  |  | 	h.respNonce = make([]byte, shaLen) | 
					
						
							|  |  |  | 	if _, err = rand.Read(h.respNonce); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-01-19 00:55:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	msg, err := crypto.Decrypt(prv, auth) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("could not decrypt auth message (%v)", err) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// decode message parameters | 
					
						
							|  |  |  | 	// signature || sha3(ecdhe-random-pubk) || pubk || nonce || token-flag | 
					
						
							|  |  |  | 	h.initNonce = msg[authMsgLen-shaLen-1 : authMsgLen-1] | 
					
						
							|  |  |  | 	copy(h.remoteID[:], msg[sigLen+shaLen:sigLen+shaLen+pubLen]) | 
					
						
							|  |  |  | 	rpub, err := h.remoteID.Pubkey() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("bad remoteID: %#v", err) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	h.remotePub = ecies.ImportECDSAPublic(rpub) | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	// recover remote random pubkey from signed message. | 
					
						
							|  |  |  | 	if token == nil { | 
					
						
							|  |  |  | 		// TODO: it is an error if the initiator has a token and we don't. check that. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// no session token means we need to generate shared secret. | 
					
						
							|  |  |  | 		// ecies shared secret is used as initial session token for new peers. | 
					
						
							|  |  |  | 		// generate shared key from prv and remote pubkey. | 
					
						
							|  |  |  | 		if token, err = h.ecdhShared(prv); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	signedMsg := xor(token, h.initNonce) | 
					
						
							|  |  |  | 	remoteRandomPub, err := secp256k1.RecoverPubkey(signedMsg, msg[:sigLen]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	h.remoteRandomPub, _ = importPublicKey(remoteRandomPub) | 
					
						
							|  |  |  | 	return h, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // authResp generates the encrypted authentication response message. | 
					
						
							|  |  |  | func (h *encHandshake) authResp(prv *ecdsa.PrivateKey, token []byte) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2015-01-19 00:55:24 +00:00
										 |  |  | 	// responder auth message | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 	// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	resp := make([]byte, authRespLen) | 
					
						
							|  |  |  | 	n := copy(resp, exportPubkey(&h.randomPrivKey.PublicKey)) | 
					
						
							|  |  |  | 	n += copy(resp[n:], h.respNonce) | 
					
						
							|  |  |  | 	if token == nil { | 
					
						
							|  |  |  | 		resp[n] = 0 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		resp[n] = 1 | 
					
						
							| 
									
										
										
										
											2015-01-20 16:47:46 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 	// encrypt using remote-pubk | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	return ecies.Encrypt(rand.Reader, h.remotePub, resp, nil, nil) | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | // importPublicKey unmarshals 512 bit public keys. | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) { | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	var pubKey65 []byte | 
					
						
							|  |  |  | 	switch len(pubKey) { | 
					
						
							|  |  |  | 	case 64: | 
					
						
							|  |  |  | 		// add 'uncompressed key' flag | 
					
						
							|  |  |  | 		pubKey65 = append([]byte{0x04}, pubKey...) | 
					
						
							|  |  |  | 	case 65: | 
					
						
							|  |  |  | 		pubKey65 = pubKey | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey)) | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	// TODO: fewer pointless conversions | 
					
						
							|  |  |  | 	return ecies.ImportECDSAPublic(crypto.ToECDSAPub(pubKey65)), nil | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | func exportPubkey(pub *ecies.PublicKey) []byte { | 
					
						
							|  |  |  | 	if pub == nil { | 
					
						
							|  |  |  | 		panic("nil pubkey") | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-02 15:26:44 +01:00
										 |  |  | 	return elliptic.Marshal(pub.Curve, pub.X, pub.Y)[1:] | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | func xor(one, other []byte) (xor []byte) { | 
					
						
							| 
									
										
										
										
											2015-01-18 23:53:45 +00:00
										 |  |  | 	xor = make([]byte, len(one)) | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | 	for i := 0; i < len(one); i++ { | 
					
						
							|  |  |  | 		xor[i] = one[i] ^ other[i] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	return xor | 
					
						
							| 
									
										
										
										
											2015-01-18 09:46:08 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | func readProtocolHandshake(rw MsgReadWriter, wantID discover.NodeID, our *protoHandshake) (*protoHandshake, error) { | 
					
						
							|  |  |  | 	msg, err := rw.ReadMsg() | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if msg.Code == discMsg { | 
					
						
							|  |  |  | 		// disconnect before protocol handshake is valid according to the | 
					
						
							|  |  |  | 		// spec and we send it ourself if Server.addPeer fails. | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		var reason [1]DiscReason | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		rlp.Decode(msg.Payload, &reason) | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		return nil, reason[0] | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if msg.Code != handshakeMsg { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("expected handshake, got %x", msg.Code) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if msg.Size > baseProtocolMaxMsgSize { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("message too big (%d > %d)", msg.Size, baseProtocolMaxMsgSize) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var hs protoHandshake | 
					
						
							|  |  |  | 	if err := msg.Decode(&hs); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// validate handshake info | 
					
						
							|  |  |  | 	if hs.Version != our.Version { | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		SendItems(rw, discMsg, DiscIncompatibleVersion) | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("required version %d, received %d\n", baseProtocolVersion, hs.Version) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (hs.ID == discover.NodeID{}) { | 
					
						
							| 
									
										
										
										
											2015-04-10 13:25:35 +02:00
										 |  |  | 		SendItems(rw, discMsg, DiscInvalidIdentity) | 
					
						
							|  |  |  | 		return nil, errors.New("invalid public key in handshake") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if hs.ID != wantID { | 
					
						
							|  |  |  | 		SendItems(rw, discMsg, DiscUnexpectedIdentity) | 
					
						
							|  |  |  | 		return nil, errors.New("handshake node ID does not match encryption handshake") | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return &hs, nil | 
					
						
							|  |  |  | } |