peer-level integration test for crypto handshake
- add const length params for handshake messages - add length check to fail early - add debug logs to help interop testing (!ABSOLUTELY SHOULD BE DELETED LATER) - wrap connection read/writes in error check - add cryptoReady channel in peer to signal when secure session setup is finished - wait for cryptoReady or timeout in TestPeersHandshake
This commit is contained in:
parent
20aade56c3
commit
faa069a126
@ -21,6 +21,8 @@ var (
|
|||||||
keyLen int = 32 // ECDSA
|
keyLen int = 32 // ECDSA
|
||||||
msgLen int = 194 // sigLen + keyLen + pubLen + keyLen + 1 = 194
|
msgLen int = 194 // sigLen + keyLen + pubLen + keyLen + 1 = 194
|
||||||
resLen int = 97 // pubLen + keyLen + 1
|
resLen int = 97 // pubLen + keyLen + 1
|
||||||
|
iHSLen int = 307 // size of the final ECIES payload sent as initiator's handshake
|
||||||
|
rHSLen int = 210 // size of the final ECIES payload sent as receiver's handshake
|
||||||
)
|
)
|
||||||
|
|
||||||
// secretRW implements a message read writer with encryption and authentication
|
// secretRW implements a message read writer with encryption and authentication
|
||||||
@ -66,7 +68,8 @@ func newCryptoId(id ClientIdentity) (self *cryptoId, err error) {
|
|||||||
// for reuse, call wth ReadAt, no reset seek needed
|
// for reuse, call wth ReadAt, no reset seek needed
|
||||||
}
|
}
|
||||||
self.pubKeyS = id.Pubkey()[1:]
|
self.pubKeyS = id.Pubkey()[1:]
|
||||||
clogger.Debugf("crytoid starting for %v", hexkey(self.pubKeyS))
|
clogger.Debugf("initialise crypto for NodeId %v", hexkey(self.pubKeyS))
|
||||||
|
clogger.Debugf("private-key %v\npublic key %v", hexkey(prvKeyS), hexkey(self.pubKeyS))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,22 +95,51 @@ Run(connection, remotePublicKey, sessionToken) is called when the peer connectio
|
|||||||
|
|
||||||
func (self *cryptoId) Run(conn io.ReadWriter, remotePubKeyS []byte, sessionToken []byte, initiator bool) (token []byte, rw *secretRW, err error) {
|
func (self *cryptoId) Run(conn io.ReadWriter, remotePubKeyS []byte, sessionToken []byte, initiator bool) (token []byte, rw *secretRW, err error) {
|
||||||
var auth, initNonce, recNonce []byte
|
var auth, initNonce, recNonce []byte
|
||||||
|
var read int
|
||||||
var randomPrivKey *ecdsa.PrivateKey
|
var randomPrivKey *ecdsa.PrivateKey
|
||||||
var remoteRandomPubKey *ecdsa.PublicKey
|
var remoteRandomPubKey *ecdsa.PublicKey
|
||||||
|
clogger.Debugf("attempting session with %v", hexkey(remotePubKeyS))
|
||||||
if initiator {
|
if initiator {
|
||||||
if auth, initNonce, randomPrivKey, _, err = self.startHandshake(remotePubKeyS, sessionToken); err != nil {
|
if auth, initNonce, randomPrivKey, _, err = self.startHandshake(remotePubKeyS, sessionToken); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn.Write(auth)
|
clogger.Debugf("initiator-nonce: %v", hexkey(initNonce))
|
||||||
var response []byte
|
clogger.Debugf("initiator-random-private-key: %v", hexkey(crypto.FromECDSA(randomPrivKey)))
|
||||||
conn.Read(response)
|
randomPublicKeyS, _ := ExportPublicKey(&randomPrivKey.PublicKey)
|
||||||
|
clogger.Debugf("initiator-random-public-key: %v", hexkey(randomPublicKeyS))
|
||||||
|
|
||||||
|
if _, err = conn.Write(auth); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clogger.Debugf("initiator handshake (sent to %v):\n%v", hexkey(remotePubKeyS), hexkey(auth))
|
||||||
|
var response []byte = make([]byte, rHSLen)
|
||||||
|
if read, err = conn.Read(response); err != nil || read == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if read != rHSLen {
|
||||||
|
err = fmt.Errorf("remote receiver's handshake has invalid length. expect %v, got %v", rHSLen, read)
|
||||||
|
return
|
||||||
|
}
|
||||||
// write out auth message
|
// write out auth message
|
||||||
// wait for response, then call complete
|
// wait for response, then call complete
|
||||||
if recNonce, remoteRandomPubKey, _, err = self.completeHandshake(response); err != nil {
|
if recNonce, remoteRandomPubKey, _, err = self.completeHandshake(response); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
clogger.Debugf("receiver-nonce: %v", hexkey(recNonce))
|
||||||
|
remoteRandomPubKeyS, _ := ExportPublicKey(remoteRandomPubKey)
|
||||||
|
clogger.Debugf("receiver-random-public-key: %v", hexkey(remoteRandomPubKeyS))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
conn.Read(auth)
|
auth = make([]byte, iHSLen)
|
||||||
|
clogger.Debugf("waiting for initiator handshake (from %v)", hexkey(remotePubKeyS))
|
||||||
|
if read, err = conn.Read(auth); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if read != iHSLen {
|
||||||
|
err = fmt.Errorf("remote initiator's handshake has invalid length. expect %v, got %v", iHSLen, read)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clogger.Debugf("received initiator handshake (from %v):\n%v", hexkey(remotePubKeyS), hexkey(auth))
|
||||||
// we are listening connection. we are responders in the handshake.
|
// 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.
|
||||||
// so we read auth message first, then respond
|
// so we read auth message first, then respond
|
||||||
@ -115,7 +147,12 @@ func (self *cryptoId) Run(conn io.ReadWriter, remotePubKeyS []byte, sessionToken
|
|||||||
if response, recNonce, initNonce, randomPrivKey, remoteRandomPubKey, err = self.respondToHandshake(auth, remotePubKeyS, sessionToken); err != nil {
|
if response, recNonce, initNonce, randomPrivKey, remoteRandomPubKey, err = self.respondToHandshake(auth, remotePubKeyS, sessionToken); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn.Write(response)
|
clogger.Debugf("receiver-nonce: %v", hexkey(recNonce))
|
||||||
|
clogger.Debugf("receiver-random-priv-key: %v", hexkey(crypto.FromECDSA(randomPrivKey)))
|
||||||
|
if _, err = conn.Write(response); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clogger.Debugf("receiver handshake (sent to %v):\n%v", hexkey(remotePubKeyS), hexkey(response))
|
||||||
}
|
}
|
||||||
return self.newSession(initNonce, recNonce, auth, randomPrivKey, remoteRandomPubKey)
|
return self.newSession(initNonce, recNonce, auth, randomPrivKey, remoteRandomPubKey)
|
||||||
}
|
}
|
||||||
@ -354,6 +391,10 @@ func (self *cryptoId) newSession(initNonce, respNonce, auth []byte, privKey *ecd
|
|||||||
egressMac: egressMac,
|
egressMac: egressMac,
|
||||||
ingressMac: ingressMac,
|
ingressMac: ingressMac,
|
||||||
}
|
}
|
||||||
|
clogger.Debugf("aes-secret: %v", hexkey(aesSecret))
|
||||||
|
clogger.Debugf("mac-secret: %v", hexkey(macSecret))
|
||||||
|
clogger.Debugf("egress-mac: %v", hexkey(egressMac))
|
||||||
|
clogger.Debugf("ingress-mac: %v", hexkey(ingressMac))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/obscuren/ecies"
|
"github.com/obscuren/ecies"
|
||||||
@ -184,7 +185,17 @@ func TestPeersHandshake(t *testing.T) {
|
|||||||
_, err := receiver.loop()
|
_, err := receiver.loop()
|
||||||
errc1 <- err
|
errc1 <- err
|
||||||
}()
|
}()
|
||||||
|
ready := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
<-initiator.cryptoReady
|
||||||
|
<-receiver.cryptoReady
|
||||||
|
close(ready)
|
||||||
|
}()
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
select {
|
select {
|
||||||
|
case <-ready:
|
||||||
|
case <-timeout:
|
||||||
|
t.Errorf("crypto handshake hanging for too long")
|
||||||
case err = <-errc0:
|
case err = <-errc0:
|
||||||
t.Errorf("peer 0 quit with error: %v", err)
|
t.Errorf("peer 0 quit with error: %v", err)
|
||||||
case err = <-errc1:
|
case err = <-errc1:
|
||||||
|
@ -71,6 +71,7 @@ type Peer struct {
|
|||||||
protocols []Protocol
|
protocols []Protocol
|
||||||
runBaseProtocol bool // for testing
|
runBaseProtocol bool // for testing
|
||||||
cryptoHandshake bool // for testing
|
cryptoHandshake bool // for testing
|
||||||
|
cryptoReady chan struct{}
|
||||||
|
|
||||||
runlock sync.RWMutex // protects running
|
runlock sync.RWMutex // protects running
|
||||||
running map[string]*proto
|
running map[string]*proto
|
||||||
@ -129,6 +130,7 @@ func newPeer(conn net.Conn, protocols []Protocol, dialAddr *peerAddr) *Peer {
|
|||||||
disc: make(chan DiscReason),
|
disc: make(chan DiscReason),
|
||||||
protoErr: make(chan error),
|
protoErr: make(chan error),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
|
cryptoReady: make(chan struct{}),
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
@ -240,6 +242,7 @@ func (p *Peer) loop() (reason DiscReason, err error) {
|
|||||||
go readLoop(readMsg, readErr, readNext)
|
go readLoop(readMsg, readErr, readNext)
|
||||||
readNext <- true
|
readNext <- true
|
||||||
|
|
||||||
|
close(p.cryptoReady)
|
||||||
if p.runBaseProtocol {
|
if p.runBaseProtocol {
|
||||||
p.startBaseProtocol()
|
p.startBaseProtocol()
|
||||||
}
|
}
|
||||||
@ -353,6 +356,7 @@ func (p *Peer) handleCryptoHandshake() (loop readLoop, err error) {
|
|||||||
// this bit handles the handshake and creates a secure communications channel with
|
// this bit handles the handshake and creates a secure communications channel with
|
||||||
// var rw *secretRW
|
// var rw *secretRW
|
||||||
if sessionToken, _, err = crypto.Run(p.conn, p.Pubkey(), sessionToken, initiator); err != nil {
|
if sessionToken, _, err = crypto.Run(p.conn, p.Pubkey(), sessionToken, initiator); err != nil {
|
||||||
|
p.Debugf("unable to setup secure session: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
loop = func(msg chan<- Msg, err chan<- error, next <-chan bool) {
|
loop = func(msg chan<- Msg, err chan<- error, next <-chan bool) {
|
||||||
|
Loading…
Reference in New Issue
Block a user