forked from cerc-io/plugeth
first stab at integrating crypto in our p2p
- abstract the entire handshake logic in cryptoId.Run() taking session-relevant parameters - changes in peer to accomodate how the encryption layer would be switched on - modify arguments of handshake components - fixed test getting the wrong pubkey but it till crashes on DH in newSession()
This commit is contained in:
parent
1803c65e40
commit
e252c634cb
@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/obscuren/ecies"
|
"github.com/obscuren/ecies"
|
||||||
@ -53,19 +54,35 @@ func newCryptoId(id ClientIdentity) (self *cryptoId, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *cryptoId) Run(remotePubKeyDER []byte) (rw *secretRW) {
|
func (self *cryptoId) Run(conn io.ReadWriter, remotePubKeyDER []byte, sessionToken []byte, initiator bool) (token []byte, rw *secretRW, err error) {
|
||||||
if self.initiator {
|
var auth, initNonce, recNonce []byte
|
||||||
auth, initNonce, randomPrvKey, randomPubKey, err := initiator.initAuth(remotePubKeyDER, sessionToken)
|
var randomPrivKey *ecdsa.PrivateKey
|
||||||
|
var remoteRandomPubKey *ecdsa.PublicKey
|
||||||
respNonce, remoteRandomPubKey, _, _ := initiator.verifyAuthResp(response)
|
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 {
|
} 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.
|
// 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)
|
return self.newSession(initNonce, recNonce, auth, randomPrivKey, remoteRandomPubKey)
|
||||||
respSessionToken, respSecretRW, _ := responder.newSession(remoteInitNonce, remoteRespNonce, auth, remoteRandomPrivKey, randomPubKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* startHandshake is called by peer if it initiated the connection.
|
/* 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
|
// session init, common to both parties
|
||||||
remotePubKey := crypto.ToECDSAPub(remotePubKeyDER)
|
remotePubKey = crypto.ToECDSAPub(remotePubKeyDER)
|
||||||
if remotePubKey == nil {
|
if remotePubKey == nil {
|
||||||
err = fmt.Errorf("invalid remote public key")
|
err = fmt.Errorf("invalid remote public key")
|
||||||
return
|
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
|
// 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
|
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))
|
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,
|
// they prove that msg is meant for me,
|
||||||
// I prove I possess private key if i can read it
|
// I prove I possess private key if i can read it
|
||||||
@ -210,12 +233,12 @@ func (self *cryptoId) respondToHandshake(auth, sessionToken []byte, remotePubKey
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// generate random keypair for session
|
// generate random keypair for session
|
||||||
if randomPrvKey, err = crypto.GenerateKey(); err != nil {
|
if randomPrivKey, err = crypto.GenerateKey(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// responder auth message
|
// responder auth message
|
||||||
// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
|
// 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
|
// nonce is already in the slice
|
||||||
resp[resLen-1] = tokenFlag
|
resp[resLen-1] = tokenFlag
|
||||||
|
|
||||||
|
@ -11,44 +11,43 @@ import (
|
|||||||
func TestCryptoHandshake(t *testing.T) {
|
func TestCryptoHandshake(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
var sessionToken []byte
|
var sessionToken []byte
|
||||||
prvInit, _ := crypto.GenerateKey()
|
prv0, _ := crypto.GenerateKey()
|
||||||
pubInit := &prvInit.PublicKey
|
pub0 := &prv0.PublicKey
|
||||||
prvResp, _ := crypto.GenerateKey()
|
prv1, _ := crypto.GenerateKey()
|
||||||
pubResp := &prvResp.PublicKey
|
pub1 := &prv1.PublicKey
|
||||||
|
|
||||||
var initiator, responder *cryptoId
|
var initiator, receiver *cryptoId
|
||||||
if initiator, err = newCryptoId(&peerId{crypto.FromECDSA(prvInit), crypto.FromECDSAPub(pubInit)}); err != nil {
|
if initiator, err = newCryptoId(&peerId{crypto.FromECDSA(prv0), crypto.FromECDSAPub(pub0)}); err != nil {
|
||||||
return
|
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
|
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)
|
if !bytes.Equal(initSessionToken, recSessionToken) {
|
||||||
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) {
|
|
||||||
t.Errorf("session tokens do not match")
|
t.Errorf("session tokens do not match")
|
||||||
}
|
}
|
||||||
// aesSecret, macSecret, egressMac, ingressMac
|
// 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")
|
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")
|
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")
|
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")
|
t.Errorf("ingressMacs do not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
p2p/peer.go
33
p2p/peer.go
@ -222,9 +222,9 @@ func (p *Peer) loop() (reason DiscReason, err error) {
|
|||||||
defer close(p.closed)
|
defer close(p.closed)
|
||||||
defer p.conn.Close()
|
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 p.cryptoHandshake {
|
||||||
if readLoop, err := p.handleCryptoHandshake(); err != nil {
|
if readLoop, err = p.handleCryptoHandshake(); err != nil {
|
||||||
// from here on everything can be encrypted, authenticated
|
// from here on everything can be encrypted, authenticated
|
||||||
return DiscProtocolError, err // no graceful disconnect
|
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
|
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
|
// cryptoId is just created for the lifecycle of the handshake
|
||||||
// it is survived by an encrypted readwriter
|
// 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
|
initiator = true
|
||||||
}
|
}
|
||||||
// create crypto layer
|
// create crypto layer
|
||||||
cryptoId := newCryptoId(p.identity, initiator, sessionToken)
|
// this could in principle run only once but maybe we want to allow
|
||||||
// run on peer
|
// identity switching
|
||||||
if rw, err := cryptoId.Run(p.Pubkey()); err != nil {
|
var crypto *cryptoId
|
||||||
return err
|
if crypto, err = newCryptoId(p.ourID); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
p.conn = rw.Run(p.conn)
|
// run on peer
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
loop = func(msg chan<- Msg, err chan<- error, next <-chan bool) {
|
||||||
|
// this is the readloop :)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Peer) startBaseProtocol() {
|
func (p *Peer) startBaseProtocol() {
|
||||||
|
Loading…
Reference in New Issue
Block a user