diff --git a/p2p/crypto.go b/p2p/crypto.go
new file mode 100644
index 0000000000000000000000000000000000000000..9204fa9d0f59622d0b5269b2a2ddd3b863dac76e
--- /dev/null
+++ b/p2p/crypto.go
@@ -0,0 +1,174 @@
+package p2p
+
+import (
+	"bytes"
+	"crypto/ecdsa"
+	"crypto/rand"
+	"fmt"
+	"io"
+
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/obscuren/ecies"
+	"github.com/obscuren/secp256k1-go"
+)
+
+var (
+	skLen     int = 32                             // ecies.MaxSharedKeyLength(pubKey) / 2
+	sigLen    int = 32                             // elliptic S256
+	pubKeyLen int = 32                             // ECDSA
+	msgLen    int = sigLen + 1 + pubKeyLen + skLen // 97
+)
+
+//, aesSecret, macSecret, egressMac, ingress
+type secretRW struct {
+	aesSecret, macSecret, egressMac, ingressMac []byte
+}
+
+type cryptoId struct {
+	prvKey  *ecdsa.PrivateKey
+	pubKey  *ecdsa.PublicKey
+	pubKeyR io.ReaderAt
+}
+
+func newCryptoId(id ClientIdentity) (self *cryptoId, err error) {
+	// will be at server  init
+	var prvKeyDER []byte = id.PrivKey()
+	if prvKeyDER == nil {
+		err = fmt.Errorf("no private key for client")
+		return
+	}
+	// initialise ecies private key via importing DER encoded keys (known via our own clientIdentity)
+	var prvKey = crypto.ToECDSA(prvKeyDER)
+	if prvKey == nil {
+		err = fmt.Errorf("invalid private key for client")
+		return
+	}
+	self = &cryptoId{
+		prvKey: prvKey,
+		// initialise public key from the imported private key
+		pubKey: &prvKey.PublicKey,
+		// to be created at server init shared between peers and sessions
+		// for reuse, call wth ReadAt, no reset seek needed
+	}
+	self.pubKeyR = bytes.NewReader(id.Pubkey())
+	return
+}
+
+//
+func (self *cryptoId) setupAuth(remotePubKeyDER, sessionToken []byte) (auth []byte, nonce []byte, sharedKnowledge []byte, err error) {
+	// session init, common to both parties
+	var remotePubKey = crypto.ToECDSAPub(remotePubKeyDER)
+	if remotePubKey == nil {
+		err = fmt.Errorf("invalid remote public key")
+		return
+	}
+	var sharedSecret []byte
+	// generate shared key from prv and remote pubkey
+	sharedSecret, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remotePubKey), skLen, skLen)
+	if err != nil {
+		return
+	}
+	// check previous session token
+	if sessionToken == nil {
+		err = fmt.Errorf("no session token for peer")
+		return
+	}
+	// allocate msgLen long message
+	var msg []byte = make([]byte, msgLen)
+	// generate skLen long nonce at the end
+	nonce = msg[msgLen-skLen:]
+	if _, err = rand.Read(nonce); err != nil {
+		return
+	}
+	// create known message
+	// should use
+	// cipher.xorBytes from crypto/cipher/xor.go for fast xor
+	sharedKnowledge = Xor(sharedSecret, sessionToken)
+	var signedMsg = Xor(sharedKnowledge, nonce)
+
+	// generate random keypair to use for signing
+	var ecdsaRandomPrvKey *ecdsa.PrivateKey
+	if ecdsaRandomPrvKey, err = crypto.GenerateKey(); err != nil {
+		return
+	}
+	// var ecdsaRandomPubKey *ecdsa.PublicKey
+	//  ecdsaRandomPubKey= &ecdsaRandomPrvKey.PublicKey
+
+	// message known to both parties ecdh-shared-secret^nonce^token
+	var signature []byte
+	// signature = sign(ecdhe-random, ecdh-shared-secret^nonce^token)
+	// uses secp256k1.Sign
+	if signature, err = crypto.Sign(signedMsg, ecdsaRandomPrvKey); err != nil {
+		return
+	}
+	// msg = signature || 0x80 || pubk || nonce
+	copy(msg, signature)
+	msg[sigLen] = 0x80
+	self.pubKeyR.ReadAt(msg[sigLen+1:], int64(pubKeyLen)) // gives pubKeyLen, io.EOF (since we dont read onto the nonce)
+
+	// auth = eciesEncrypt(remote-pubk, msg)
+	if auth, err = crypto.Encrypt(remotePubKey, msg); err != nil {
+		return
+	}
+	return
+}
+
+func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionToken []byte, rw *secretRW, err error) {
+	var msg []byte
+	// they prove that msg is meant for me,
+	// I prove I possess private key if i can read it
+	if msg, err = crypto.Decrypt(self.prvKey, auth); err != nil {
+		return
+	}
+
+	var remoteNonce []byte = msg[msgLen-skLen:]
+	// I prove that i possess prv key (to derive shared secret, and read nonce off encrypted msg) and that I posessed the earlier one , our shared history
+	// they prove they possess their private key to derive the same shared secret, plus the same shared history (previous session token)
+	var signedMsg = Xor(sharedKnowledge, remoteNonce)
+	var remoteRandomPubKeyDER []byte
+	if remoteRandomPubKeyDER, err = secp256k1.RecoverPubkey(signedMsg, msg[:32]); err != nil {
+		return
+	}
+	var remoteRandomPubKey = crypto.ToECDSAPub(remoteRandomPubKeyDER)
+	if remoteRandomPubKey == nil {
+		err = fmt.Errorf("invalid remote public key")
+		return
+	}
+	// 3) Now we can trust ecdhe-random-pubk to derive keys
+	//ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk)
+	var dhSharedSecret []byte
+	dhSharedSecret, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remoteRandomPubKey), skLen, skLen)
+	if err != nil {
+		return
+	}
+	// shared-secret = crypto.Sha3(ecdhe-shared-secret || crypto.Sha3(nonce || initiator-nonce))
+	var sharedSecret []byte = crypto.Sha3(append(dhSharedSecret, crypto.Sha3(append(nonce, remoteNonce...))...))
+	// token = crypto.Sha3(shared-secret)
+	sessionToken = crypto.Sha3(sharedSecret)
+	// aes-secret = crypto.Sha3(ecdhe-shared-secret || shared-secret)
+	var aesSecret = crypto.Sha3(append(dhSharedSecret, sharedSecret...))
+	// # destroy shared-secret
+	// mac-secret = crypto.Sha3(ecdhe-shared-secret || aes-secret)
+	var macSecret = crypto.Sha3(append(dhSharedSecret, aesSecret...))
+	// # destroy ecdhe-shared-secret
+	// egress-mac = crypto.Sha3(mac-secret^nonce || auth)
+	var egressMac = crypto.Sha3(append(Xor(macSecret, nonce), auth...))
+	// # destroy nonce
+	// ingress-mac = crypto.Sha3(mac-secret^initiator-nonce || auth),
+	var ingressMac = crypto.Sha3(append(Xor(macSecret, remoteNonce), auth...))
+	// # destroy remote-nonce
+	rw = &secretRW{
+		aesSecret:  aesSecret,
+		macSecret:  macSecret,
+		egressMac:  egressMac,
+		ingressMac: ingressMac,
+	}
+	return
+}
+
+func Xor(one, other []byte) (xor []byte) {
+	for i := 0; i < len(one); i++ {
+		xor[i] = one[i] ^ other[i]
+	}
+	return
+}