From b855f671a508a7e8160cbdd27197ba83310b264c Mon Sep 17 00:00:00 2001
From: zelig <viktor.tron@gmail.com>
Date: Sun, 18 Jan 2015 23:53:45 +0000
Subject: [PATCH] rewrite to comply with latest spec - correct sizes for the
 blocks : sec signature 65, ecies sklen 16, keylength 32 - added allocation to
 Xor (should be optimized later) - no pubkey reader needed, just do with copy
 - restructuring now into INITIATE, RESPOND, COMPLETE -> newSession
 initialises the encryption/authentication layer - crypto identity can be part
 of client identity, some initialisation when server created

---
 p2p/crypto.go | 191 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 138 insertions(+), 53 deletions(-)

diff --git a/p2p/crypto.go b/p2p/crypto.go
index 9204fa9d0..6e3f360d9 100644
--- a/p2p/crypto.go
+++ b/p2p/crypto.go
@@ -1,11 +1,11 @@
 package p2p
 
 import (
-	"bytes"
+	// "bytes"
 	"crypto/ecdsa"
 	"crypto/rand"
 	"fmt"
-	"io"
+	// "io"
 
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/obscuren/ecies"
@@ -13,21 +13,22 @@ import (
 )
 
 var (
-	skLen     int = 32                             // ecies.MaxSharedKeyLength(pubKey) / 2
-	sigLen    int = 32                             // elliptic S256
-	pubKeyLen int = 32                             // ECDSA
-	msgLen    int = sigLen + 1 + pubKeyLen + skLen // 97
+	sskLen int = 16                    // ecies.MaxSharedKeyLength(pubKey) / 2
+	sigLen int = 65                    // elliptic S256
+	keyLen int = 32                    // ECDSA
+	msgLen int = sigLen + 3*keyLen + 1 // 162
+	resLen int = 65
 )
 
-//, aesSecret, macSecret, egressMac, ingress
+// aesSecret, macSecret, egressMac, ingress
 type secretRW struct {
 	aesSecret, macSecret, egressMac, ingressMac []byte
 }
 
 type cryptoId struct {
-	prvKey  *ecdsa.PrivateKey
-	pubKey  *ecdsa.PublicKey
-	pubKeyR io.ReaderAt
+	prvKey    *ecdsa.PrivateKey
+	pubKey    *ecdsa.PublicKey
+	pubKeyDER []byte
 }
 
 func newCryptoId(id ClientIdentity) (self *cryptoId, err error) {
@@ -50,99 +51,181 @@ func newCryptoId(id ClientIdentity) (self *cryptoId, err error) {
 		// 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())
+	self.pubKeyDER = id.Pubkey()
 	return
 }
 
-//
-func (self *cryptoId) setupAuth(remotePubKeyDER, sessionToken []byte) (auth []byte, nonce []byte, sharedKnowledge []byte, err error) {
+// initAuth is called by peer if it initiated the connection
+func (self *cryptoId) initAuth(remotePubKeyDER, sessionToken []byte) (auth []byte, initNonce []byte, remotePubKey *ecdsa.PublicKey, err error) {
 	// session init, common to both parties
-	var remotePubKey = crypto.ToECDSAPub(remotePubKeyDER)
+	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
+
+	var tokenFlag byte
 	if sessionToken == nil {
-		err = fmt.Errorf("no session token for peer")
-		return
+		// 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
+		if sessionToken, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remotePubKey), sskLen, sskLen); err != nil {
+			return
+		}
+		fmt.Printf("secret generated: %v %x", len(sessionToken), sessionToken)
+		// tokenFlag = 0x00 // redundant
+	} else {
+		// for known peers, we use stored token from the previous session
+		tokenFlag = 0x01
 	}
-	// allocate msgLen long message
+
+	//E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
+	// E(remote-pubk, S(ecdhe-random, token^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x1)
+	// 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 {
+	// generate sskLen long nonce
+	initNonce = msg[msgLen-keyLen-1 : msgLen-1]
+	// nonce = msg[msgLen-sskLen-1 : msgLen-1]
+	if _, err = rand.Read(initNonce); 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)
+	// ecdh-shared-secret^nonce for new peers
+	// token^nonce for old peers
+	var sharedSecret = Xor(sessionToken, initNonce)
 
 	// 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
+	// sign shared secret (message known to both parties): shared-secret
 	var signature []byte
-	// signature = sign(ecdhe-random, ecdh-shared-secret^nonce^token)
+	// signature = sign(ecdhe-random, shared-secret)
 	// uses secp256k1.Sign
-	if signature, err = crypto.Sign(signedMsg, ecdsaRandomPrvKey); err != nil {
+	if signature, err = crypto.Sign(sharedSecret, 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)
+	fmt.Printf("signature generated: %v %x", len(signature), signature)
 
+	// message
+	// signed-shared-secret || H(ecdhe-random-pubk) || pubk || nonce || 0x0
+	copy(msg, signature) // copy signed-shared-secret
+	// H(ecdhe-random-pubk)
+	copy(msg[sigLen:sigLen+keyLen], crypto.Sha3(crypto.FromECDSAPub(&ecdsaRandomPrvKey.PublicKey)))
+	// pubkey copied to the correct segment.
+	copy(msg[sigLen+keyLen:sigLen+2*keyLen], self.pubKeyDER)
+	// nonce is already in the slice
+	// stick tokenFlag byte to the end
+	msg[msgLen-1] = tokenFlag
+
+	fmt.Printf("plaintext message generated: %v %x", len(msg), msg)
+
+	// encrypt using remote-pubk
 	// auth = eciesEncrypt(remote-pubk, msg)
+
 	if auth, err = crypto.Encrypt(remotePubKey, msg); err != nil {
 		return
 	}
+	fmt.Printf("encrypted message generated: %v %x\n used pubkey: %x\n", len(auth), auth, crypto.FromECDSAPub(remotePubKey))
+
 	return
 }
 
-func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionToken []byte, rw *secretRW, err error) {
+// verifyAuth is called by peer if it accepted (but not initiated) the connection
+func (self *cryptoId) verifyAuth(auth, sharedSecret []byte, remotePubKey *ecdsa.PublicKey) (authResp []byte, respNonce []byte, initNonce []byte, remoteRandomPubKey *ecdsa.PublicKey, err error) {
 	var msg []byte
+	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
 	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 remoteNonce []byte = msg[msgLen-skLen-1 : msgLen-1]
+	initNonce = msg[msgLen-keyLen-1 : msgLen-1]
+	// I prove that i own prv key (to derive shared secret, and read nonce off encrypted msg) and that I own shared secret
+	// they prove they own the private key belonging to ecdhe-random-pubk
+	var signedMsg = Xor(sharedSecret, initNonce)
 	var remoteRandomPubKeyDER []byte
-	if remoteRandomPubKeyDER, err = secp256k1.RecoverPubkey(signedMsg, msg[:32]); err != nil {
+	if remoteRandomPubKeyDER, err = secp256k1.RecoverPubkey(signedMsg, msg[:sigLen]); err != nil {
 		return
 	}
-	var remoteRandomPubKey = crypto.ToECDSAPub(remoteRandomPubKeyDER)
+	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
+
+	var resp = make([]byte, 2*keyLen+1)
+	// generate sskLen long nonce
+	respNonce = msg[msgLen-keyLen-1 : msgLen-1]
+	if _, err = rand.Read(respNonce); err != nil {
+		return
+	}
+	// generate random keypair
+	var ecdsaRandomPrvKey *ecdsa.PrivateKey
+	if ecdsaRandomPrvKey, err = crypto.GenerateKey(); err != nil {
+		return
+	}
+	// var ecdsaRandomPubKey *ecdsa.PublicKey
+	//  ecdsaRandomPubKey= &ecdsaRandomPrvKey.PublicKey
+
+	// message
+	// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
+	copy(resp[:keyLen], crypto.FromECDSAPub(&ecdsaRandomPrvKey.PublicKey))
+	// pubkey copied to the correct segment.
+	copy(resp[keyLen:2*keyLen], self.pubKeyDER)
+	// nonce is already in the slice
+	// stick tokenFlag byte to the end
+	var tokenFlag byte
+	if sharedSecret == nil {
+	} else {
+		// for known peers, we use stored token from the previous session
+		tokenFlag = 0x01
+	}
+	resp[resLen] = tokenFlag
+
+	// encrypt using remote-pubk
+	// auth = eciesEncrypt(remote-pubk, msg)
+	// why not encrypt with ecdhe-random-remote
+	if authResp, err = crypto.Encrypt(remotePubKey, resp); err != nil {
+		return
+	}
+	return
+}
+
+func (self *cryptoId) verifyAuthResp(auth []byte) (respNonce []byte, remoteRandomPubKey *ecdsa.PublicKey, tokenFlag bool, 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
+	}
+
+	respNonce = msg[resLen-keyLen-1 : resLen-1]
+	var remoteRandomPubKeyDER = msg[:keyLen]
+	remoteRandomPubKey = crypto.ToECDSAPub(remoteRandomPubKeyDER)
+	if remoteRandomPubKey == nil {
+		err = fmt.Errorf("invalid ecdh random remote public key")
+		return
+	}
+	if msg[resLen-1] == 0x01 {
+		tokenFlag = true
+	}
+	return
+}
+
+func (self *cryptoId) newSession(initNonce, respNonce, auth []byte, remoteRandomPubKey *ecdsa.PublicKey) (sessionToken []byte, rw *secretRW, err error) {
+	// 3) Now we can trust ecdhe-random-pubk to derive new 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)
+	dhSharedSecret, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remoteRandomPubKey), sskLen, sskLen)
 	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...))...))
+	var sharedSecret = crypto.Sha3(append(dhSharedSecret, crypto.Sha3(append(respNonce, initNonce...))...))
 	// token = crypto.Sha3(shared-secret)
 	sessionToken = crypto.Sha3(sharedSecret)
 	// aes-secret = crypto.Sha3(ecdhe-shared-secret || shared-secret)
@@ -152,10 +235,10 @@ func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionTo
 	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...))
+	var egressMac = crypto.Sha3(append(Xor(macSecret, respNonce), auth...))
 	// # destroy nonce
 	// ingress-mac = crypto.Sha3(mac-secret^initiator-nonce || auth),
-	var ingressMac = crypto.Sha3(append(Xor(macSecret, remoteNonce), auth...))
+	var ingressMac = crypto.Sha3(append(Xor(macSecret, initNonce), auth...))
 	// # destroy remote-nonce
 	rw = &secretRW{
 		aesSecret:  aesSecret,
@@ -166,7 +249,9 @@ func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionTo
 	return
 }
 
+// should use cipher.xorBytes from crypto/cipher/xor.go for fast xor
 func Xor(one, other []byte) (xor []byte) {
+	xor = make([]byte, len(one))
 	for i := 0; i < len(one); i++ {
 		xor[i] = one[i] ^ other[i]
 	}
-- 
GitLab