status-go/server/server_pairing_test.go

183 lines
5.6 KiB
Go

package server
import (
"crypto/ecdsa"
"crypto/rand"
"testing"
"time"
"github.com/stretchr/testify/suite"
)
func TestPairingServerSuite(t *testing.T) {
suite.Run(t, new(PairingServerSuite))
}
type PairingServerSuite struct {
suite.Suite
TestPairingServerComponents
}
func (s *PairingServerSuite) SetupSuite() {
s.SetupPairingServerComponents(s.T())
}
func (s *PairingServerSuite) TestPairingServer_StartPairing() {
// Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager
pm, err := NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
s.Require().NoError(err)
s.PS.PayloadManager = pm
modes := []Mode{
Receiving,
Sending,
}
for _, m := range modes {
s.PS.mode = m
err = s.PS.StartPairing()
s.Require().NoError(err)
// Give time for the sever to be ready, hacky I know, I'll iron this out
time.Sleep(10 * time.Millisecond)
cp, err := s.PS.MakeConnectionParams()
s.Require().NoError(err)
qr := cp.ToString()
// Client reads QR code and parses the connection string
ccp := new(ConnectionParams)
err = ccp.FromString(qr)
s.Require().NoError(err)
c, err := NewPairingClient(ccp, nil)
s.Require().NoError(err)
// Compare cert values
cert := c.serverCert
cl := s.PS.cert.Leaf
s.Require().Equal(cl.Signature, cert.Signature)
s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).X.Cmp(cert.PublicKey.(*ecdsa.PublicKey).X))
s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).Y.Cmp(cert.PublicKey.(*ecdsa.PublicKey).Y))
s.Require().Equal(cl.Version, cert.Version)
s.Require().Zero(cl.SerialNumber.Cmp(cert.SerialNumber))
s.Require().Exactly(cl.NotBefore, cert.NotBefore)
s.Require().Exactly(cl.NotAfter, cert.NotAfter)
s.Require().Exactly(cl.IPAddresses, cert.IPAddresses)
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
c.PayloadManager, err = NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
s.Require().NoError(err)
err = c.PairAccount()
s.Require().NoError(err)
switch m {
case Receiving:
s.Require().Equal(c.PayloadManager.(*MockEncryptOnlyPayloadManager).toSend.plain, s.PS.Received())
s.Require().Equal(s.PS.PayloadManager.(*MockEncryptOnlyPayloadManager).received.encrypted, c.PayloadManager.(*MockEncryptOnlyPayloadManager).toSend.encrypted)
s.Require().Nil(s.PS.ToSend())
s.Require().Nil(c.Received())
case Sending:
s.Require().Equal(c.Received(), s.PS.PayloadManager.(*MockEncryptOnlyPayloadManager).toSend.plain)
s.Require().Equal(c.PayloadManager.(*MockEncryptOnlyPayloadManager).received.encrypted, s.PS.PayloadManager.(*MockEncryptOnlyPayloadManager).toSend.encrypted)
s.Require().Nil(c.ToSend())
s.Require().Nil(s.PS.Received())
}
// Reset the server's PayloadEncryptionManager
s.PS.PayloadManager.(*MockEncryptOnlyPayloadManager).ResetPayload()
}
}
func (s *PairingServerSuite) sendingSetup() *PairingClient {
// Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager
pm, err := NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
s.Require().NoError(err)
s.PS.PayloadManager = pm
s.PS.mode = Sending
err = s.PS.StartPairing()
s.Require().NoError(err)
// Give time for the sever to be ready, hacky I know, I'll iron this out
time.Sleep(10 * time.Millisecond)
cp, err := s.PS.MakeConnectionParams()
s.Require().NoError(err)
qr := cp.ToString()
// Client reads QR code and parses the connection string
ccp := new(ConnectionParams)
err = ccp.FromString(qr)
s.Require().NoError(err)
c, err := NewPairingClient(ccp, nil)
s.Require().NoError(err)
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
c.PayloadManager, err = NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
s.Require().NoError(err)
return c
}
func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware() {
c := s.sendingSetup()
// Attempt to get the private key data, this should fail because there is no challenge
err := c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
err = c.getChallenge()
s.Require().NoError(err)
challenge := c.serverChallenge
// This is NOT a mistake! Call c.getChallenge() twice to check that the client gets the same challenge
// the server will only generate 1 challenge per session per connection
err = c.getChallenge()
s.Require().NoError(err)
s.Require().Equal(challenge, c.serverChallenge)
// receiving account data should now work.
err = c.receiveAccountData()
s.Require().NoError(err)
}
func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware_block() {
c := s.sendingSetup()
// Attempt to get the private key data, this should fail because there is no challenge
err := c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
// Get the challenge
err = c.getChallenge()
s.Require().NoError(err)
// Simulate encrypting with a dodgy key, write some nonsense to the challenge field
c.serverChallenge = make([]byte, 64)
_, err = rand.Read(c.serverChallenge)
s.Require().NoError(err)
// Attempt again to get the account data, should fail
// behind the scenes the server will block the session if the client fails the challenge. There is no forgiveness!
err = c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
// Get the real challenge
err = c.getChallenge()
s.Require().NoError(err)
// Attempt to get the account data, should fail because the client is now blocked.
err = c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
}