diff --git a/p2p/crypto.go b/p2p/crypto.go
index 728b8e884ec18cb260aef6fe1fa09fdf7022c31c..b6d600826f7106d856752300a5be9aee25997baa 100644
--- a/p2p/crypto.go
+++ b/p2p/crypto.go
@@ -4,6 +4,7 @@ import (
 	"crypto/ecdsa"
 	"crypto/rand"
 	"fmt"
+	"io"
 
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/obscuren/ecies"
@@ -53,19 +54,35 @@ func newCryptoId(id ClientIdentity) (self *cryptoId, err error) {
 	return
 }
 
-func (self *cryptoId) Run(remotePubKeyDER []byte) (rw *secretRW) {
-	if self.initiator {
-		auth, initNonce, randomPrvKey, randomPubKey, err := initiator.initAuth(remotePubKeyDER, sessionToken)
-
-		respNonce, remoteRandomPubKey, _, _ := initiator.verifyAuthResp(response)
+func (self *cryptoId) Run(conn io.ReadWriter, remotePubKeyDER []byte, sessionToken []byte, initiator bool) (token []byte, rw *secretRW, err error) {
+	var auth, initNonce, recNonce []byte
+	var randomPrivKey *ecdsa.PrivateKey
+	var remoteRandomPubKey *ecdsa.PublicKey
+	if initiator {
+		if auth, initNonce, randomPrivKey, _, err = self.startHandshake(remotePubKeyDER, sessionToken); err != nil {
+			return
+		}
+		conn.Write(auth)
+		var response []byte
+		conn.Read(response)
+		// write out auth message
+		// wait for response, then call complete
+		if recNonce, remoteRandomPubKey, _, err = self.completeHandshake(response); err != nil {
+			return
+		}
 	} else {
-		// we are listening connection. we are responders in the haandshake.
+		conn.Read(auth)
+		// we are listening connection. we are responders in the handshake.
 		// Extract info from the authentication. The initiator starts by sending us a handshake that we need to respond to.
-		response, remoteRespNonce, remoteInitNonce, remoteRandomPrivKey, _ := responder.verifyAuth(auth, sessionToken, pubInit)
-
+		// so we read auth message first, then respond
+		var response []byte
+		if response, recNonce, initNonce, randomPrivKey, err = self.respondToHandshake(auth, remotePubKeyDER, sessionToken); err != nil {
+			return
+		}
+		remoteRandomPubKey = &randomPrivKey.PublicKey
+		conn.Write(response)
 	}
-	initSessionToken, initSecretRW, _ := initiator.newSession(initNonce, respNonce, auth, randomPrvKey, remoteRandomPubKey)
-	respSessionToken, respSecretRW, _ := responder.newSession(remoteInitNonce, remoteRespNonce, auth, remoteRandomPrivKey, randomPubKey)
+	return self.newSession(initNonce, recNonce, auth, randomPrivKey, remoteRandomPubKey)
 }
 
 /* startHandshake is called by peer if it initiated the connection.
@@ -83,9 +100,9 @@ The handshake is the process by which the peers establish their connection for a
 
 */
 
-func (self *cryptoId) startHandshake(remotePubKeyDER, sessionToken []byte) (auth []byte, initNonce []byte, randomPrvKey *ecdsa.PrivateKey, randomPubKey *ecdsa.PublicKey, err error) {
+func (self *cryptoId) startHandshake(remotePubKeyDER, sessionToken []byte) (auth []byte, initNonce []byte, randomPrvKey *ecdsa.PrivateKey, remotePubKey *ecdsa.PublicKey, err error) {
 	// session init, common to both parties
-	remotePubKey := crypto.ToECDSAPub(remotePubKeyDER)
+	remotePubKey = crypto.ToECDSAPub(remotePubKeyDER)
 	if remotePubKey == nil {
 		err = fmt.Errorf("invalid remote public key")
 		return
@@ -160,8 +177,14 @@ func (self *cryptoId) startHandshake(remotePubKeyDER, sessionToken []byte) (auth
 }
 
 // verifyAuth is called by peer if it accepted (but not initiated) the connection
-func (self *cryptoId) respondToHandshake(auth, sessionToken []byte, remotePubKey *ecdsa.PublicKey) (authResp []byte, respNonce []byte, initNonce []byte, randomPrvKey *ecdsa.PrivateKey, err error) {
+func (self *cryptoId) respondToHandshake(auth, remotePubKeyDER, sessionToken []byte) (authResp []byte, respNonce []byte, initNonce []byte, randomPrivKey *ecdsa.PrivateKey, err error) {
 	var msg []byte
+	remotePubKey := crypto.ToECDSAPub(remotePubKeyDER)
+	if remotePubKey == nil {
+		err = fmt.Errorf("invalid public key")
+		return
+	}
+
 	fmt.Printf("encrypted message received: %v %x\n used pubkey: %x\n", len(auth), auth, crypto.FromECDSAPub(self.pubKey))
 	// they prove that msg is meant for me,
 	// I prove I possess private key if i can read it
@@ -210,12 +233,12 @@ func (self *cryptoId) respondToHandshake(auth, sessionToken []byte, remotePubKey
 		return
 	}
 	// generate random keypair for session
-	if randomPrvKey, err = crypto.GenerateKey(); err != nil {
+	if randomPrivKey, err = crypto.GenerateKey(); err != nil {
 		return
 	}
 	// responder auth message
 	// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
-	copy(resp[:keyLen], crypto.FromECDSAPub(&randomPrvKey.PublicKey))
+	copy(resp[:keyLen], crypto.FromECDSAPub(&randomPrivKey.PublicKey))
 	// nonce is already in the slice
 	resp[resLen-1] = tokenFlag
 
diff --git a/p2p/crypto_test.go b/p2p/crypto_test.go
index cfb2d19d1a9d7f02deea15f5011f9854f4c35fbc..fb7df6b50ef88fb469296cc3fd34d309c336def7 100644
--- a/p2p/crypto_test.go
+++ b/p2p/crypto_test.go
@@ -11,44 +11,43 @@ import (
 func TestCryptoHandshake(t *testing.T) {
 	var err error
 	var sessionToken []byte
-	prvInit, _ := crypto.GenerateKey()
-	pubInit := &prvInit.PublicKey
-	prvResp, _ := crypto.GenerateKey()
-	pubResp := &prvResp.PublicKey
+	prv0, _ := crypto.GenerateKey()
+	pub0 := &prv0.PublicKey
+	prv1, _ := crypto.GenerateKey()
+	pub1 := &prv1.PublicKey
 
-	var initiator, responder *cryptoId
-	if initiator, err = newCryptoId(&peerId{crypto.FromECDSA(prvInit), crypto.FromECDSAPub(pubInit)}); err != nil {
+	var initiator, receiver *cryptoId
+	if initiator, err = newCryptoId(&peerId{crypto.FromECDSA(prv0), crypto.FromECDSAPub(pub0)}); err != nil {
 		return
 	}
-	if responder, err = newCryptoId(&peerId{crypto.FromECDSA(prvResp), crypto.FromECDSAPub(pubResp)}); err != nil {
+	if receiver, err = newCryptoId(&peerId{crypto.FromECDSA(prv1), crypto.FromECDSAPub(pub1)}); err != nil {
 		return
 	}
 
-	auth, initNonce, randomPrvKey, randomPubKey, _ := initiator.initAuth(responder.pubKeyDER, sessionToken)
+	// simulate handshake by feeding output to input
+	auth, initNonce, randomPrivKey, _, _ := initiator.startHandshake(receiver.pubKeyDER, sessionToken)
+	response, remoteRecNonce, remoteInitNonce, remoteRandomPrivKey, _ := receiver.respondToHandshake(auth, crypto.FromECDSAPub(pub0), sessionToken)
+	recNonce, remoteRandomPubKey, _, _ := initiator.completeHandshake(response)
 
-	response, remoteRespNonce, remoteInitNonce, remoteRandomPrivKey, _ := responder.verifyAuth(auth, sessionToken, pubInit)
+	initSessionToken, initSecretRW, _ := initiator.newSession(initNonce, recNonce, auth, randomPrivKey, remoteRandomPubKey)
+	recSessionToken, recSecretRW, _ := receiver.newSession(remoteInitNonce, remoteRecNonce, auth, remoteRandomPrivKey, &randomPrivKey.PublicKey)
 
-	respNonce, remoteRandomPubKey, _, _ := initiator.verifyAuthResp(response)
+	fmt.Printf("%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n", auth, initNonce, response, remoteRecNonce, remoteInitNonce, remoteRandomPubKey, recNonce, &randomPrivKey.PublicKey, initSessionToken, initSecretRW)
 
-	initSessionToken, initSecretRW, _ := initiator.newSession(initNonce, respNonce, auth, randomPrvKey, remoteRandomPubKey)
-	respSessionToken, respSecretRW, _ := responder.newSession(remoteInitNonce, remoteRespNonce, auth, remoteRandomPrivKey, randomPubKey)
-
-	fmt.Printf("%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n%x\n", auth, initNonce, response, remoteRespNonce, remoteInitNonce, remoteRandomPubKey, respNonce, randomPubKey, initSessionToken, initSecretRW)
-
-	if !bytes.Equal(initSessionToken, respSessionToken) {
+	if !bytes.Equal(initSessionToken, recSessionToken) {
 		t.Errorf("session tokens do not match")
 	}
 	// aesSecret, macSecret, egressMac, ingressMac
-	if !bytes.Equal(initSecretRW.aesSecret, respSecretRW.aesSecret) {
+	if !bytes.Equal(initSecretRW.aesSecret, recSecretRW.aesSecret) {
 		t.Errorf("AES secrets do not match")
 	}
-	if !bytes.Equal(initSecretRW.macSecret, respSecretRW.macSecret) {
+	if !bytes.Equal(initSecretRW.macSecret, recSecretRW.macSecret) {
 		t.Errorf("macSecrets do not match")
 	}
-	if !bytes.Equal(initSecretRW.egressMac, respSecretRW.egressMac) {
+	if !bytes.Equal(initSecretRW.egressMac, recSecretRW.egressMac) {
 		t.Errorf("egressMacs do not match")
 	}
-	if !bytes.Equal(initSecretRW.ingressMac, respSecretRW.ingressMac) {
+	if !bytes.Equal(initSecretRW.ingressMac, recSecretRW.ingressMac) {
 		t.Errorf("ingressMacs do not match")
 	}
 
diff --git a/p2p/peer.go b/p2p/peer.go
index e98c3d5608e5158deb8e2b0c4b82f53607146a1e..e3e04ee6527ee063c5a9f3d406aba8375731cd5a 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -222,9 +222,9 @@ func (p *Peer) loop() (reason DiscReason, err error) {
 	defer close(p.closed)
 	defer p.conn.Close()
 
-	var readLoop func(chan Msg, chan error, chan bool)
+	var readLoop func(chan<- Msg, chan<- error, <-chan bool)
 	if p.cryptoHandshake {
-		if readLoop, err := p.handleCryptoHandshake(); err != nil {
+		if readLoop, err = p.handleCryptoHandshake(); err != nil {
 			// from here on everything can be encrypted, authenticated
 			return DiscProtocolError, err // no graceful disconnect
 		}
@@ -332,20 +332,33 @@ func (p *Peer) dispatch(msg Msg, protoDone chan struct{}) (wait bool, err error)
 	return wait, nil
 }
 
-func (p *Peer) handleCryptoHandshake() (err error) {
+type readLoop func(chan<- Msg, chan<- error, <-chan bool)
+
+func (p *Peer) handleCryptoHandshake() (loop readLoop, err error) {
 	// cryptoId is just created for the lifecycle of the handshake
 	// it is survived by an encrypted readwriter
-	if p.dialAddr != 0 { // this should have its own method Outgoing() bool
+	var initiator bool
+	var sessionToken []byte
+	if p.dialAddr != nil { // this should have its own method Outgoing() bool
 		initiator = true
 	}
 	// create crypto layer
-	cryptoId := newCryptoId(p.identity, initiator, sessionToken)
+	// this could in principle run only once but maybe we want to allow
+	// identity switching
+	var crypto *cryptoId
+	if crypto, err = newCryptoId(p.ourID); err != nil {
+		return
+	}
 	// run on peer
-	if rw, err := cryptoId.Run(p.Pubkey()); err != nil {
-		return err
+	// this bit handles the handshake and creates a secure communications channel with
+	// var rw *secretRW
+	if sessionToken, _, err = crypto.Run(p.conn, p.Pubkey(), sessionToken, initiator); err != nil {
+		return
 	}
-	p.conn = rw.Run(p.conn)
-
+	loop = func(msg chan<- Msg, err chan<- error, next <-chan bool) {
+		// this is the readloop :)
+	}
+	return
 }
 
 func (p *Peer) startBaseProtocol() {