status-go/server/pairing/server.go

289 lines
7.8 KiB
Go

package pairing
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"fmt"
"net"
"time"
"go.uber.org/zap"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/server"
)
/*
|--------------------------------------------------------------------------
| type BaseServer struct {
|--------------------------------------------------------------------------
|
|
|
*/
type BaseServer struct {
server.Server
challengeGiver *ChallengeGiver
pk *ecdsa.PublicKey
ek []byte
}
// NewBaseServer returns a *BaseServer init from the given *SenderServerConfig
func NewBaseServer(logger *zap.Logger, e *PayloadEncryptor, config *ServerConfig) (*BaseServer, error) {
cg, err := NewChallengeGiver(e, logger)
if err != nil {
return nil, err
}
bs := &BaseServer{
Server: server.NewServer(
config.Cert,
config.Hostname,
nil,
logger,
),
challengeGiver: cg,
pk: config.PK,
ek: config.EK,
}
bs.SetTimeout(config.Timeout)
return bs, nil
}
// MakeConnectionParams generates a *ConnectionParams based on the Server's current state
func (s *BaseServer) MakeConnectionParams() (*ConnectionParams, error) {
hostname := s.GetHostname()
netIP := net.ParseIP(hostname)
if netIP == nil {
return nil, fmt.Errorf("invalid ip address given '%s'", hostname)
}
netIP4 := netIP.To4()
if netIP4 != nil {
netIP = netIP4
}
return NewConnectionParams(netIP, s.MustGetPort(), s.pk, s.ek), nil
}
func MakeServerConfig(config *ServerConfig) error {
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return err
}
AESKey := make([]byte, 32)
_, err = rand.Read(AESKey)
if err != nil {
return err
}
outboundIP, err := server.GetOutboundIP()
if err != nil {
return err
}
tlsCert, _, err := GenerateCertFromKey(tlsKey, time.Now(), outboundIP.String())
if err != nil {
return err
}
config.PK = &tlsKey.PublicKey
config.EK = AESKey
config.Cert = &tlsCert
config.Hostname = outboundIP.String()
return nil
}
/*
|--------------------------------------------------------------------------
| type SenderServer struct {
|--------------------------------------------------------------------------
|
| With AccountPayloadMounter, RawMessagePayloadMounter and InstallationPayloadMounterReceiver
|
*/
type SenderServer struct {
*BaseServer
accountMounter PayloadMounter
rawMessageMounter *RawMessagePayloadMounter
installationMounter *InstallationPayloadMounterReceiver
}
// NewSenderServer returns a *SenderServer init from the given *SenderServerConfig
func NewSenderServer(backend *api.GethStatusBackend, config *SenderServerConfig) (*SenderServer, error) {
logger := logutils.ZapLogger().Named("SenderServer")
e := NewPayloadEncryptor(config.ServerConfig.EK)
bs, err := NewBaseServer(logger, e, config.ServerConfig)
if err != nil {
return nil, err
}
am, rmm, imr, err := NewPayloadMounters(logger, e, backend, config.SenderConfig)
if err != nil {
return nil, err
}
return &SenderServer{
BaseServer: bs,
accountMounter: am,
rawMessageMounter: rmm,
installationMounter: imr,
}, nil
}
func (s *SenderServer) startSendingData() error {
s.SetHandlers(server.HandlerPatternMap{
pairingChallenge: handlePairingChallenge(s.challengeGiver),
pairingSendAccount: middlewareChallenge(s.challengeGiver, handleSendAccount(s, s.accountMounter)),
pairingSendSyncDevice: middlewareChallenge(s.challengeGiver, handlePairingSyncDeviceSend(s, s.rawMessageMounter)),
// TODO implement refactor of installation data exchange to follow the send/receive pattern of
// the other handlers.
// https://github.com/status-im/status-go/issues/3304
// receive installation data from receiver
pairingReceiveInstallation: middlewareChallenge(s.challengeGiver, handleReceiveInstallation(s, s.installationMounter)),
})
return s.Start()
}
// MakeFullSenderServer generates a fully configured and randomly seeded SenderServer
func MakeFullSenderServer(backend *api.GethStatusBackend, config *SenderServerConfig) (*SenderServer, error) {
err := MakeServerConfig(config.ServerConfig)
if err != nil {
return nil, err
}
config.SenderConfig.DB = backend.GetMultiaccountDB()
return NewSenderServer(backend, config)
}
// StartUpSenderServer generates a SenderServer, starts the sending server
// and returns the ConnectionParams string to allow a ReceiverClient to make a successful connection.
func StartUpSenderServer(backend *api.GethStatusBackend, configJSON string) (string, error) {
conf := NewSenderServerConfig()
err := json.Unmarshal([]byte(configJSON), conf)
if err != nil {
return "", err
}
ps, err := MakeFullSenderServer(backend, conf)
if err != nil {
return "", err
}
err = ps.startSendingData()
if err != nil {
return "", err
}
cp, err := ps.MakeConnectionParams()
if err != nil {
return "", err
}
return cp.ToString(), nil
}
/*
|--------------------------------------------------------------------------
| ReceiverServer
|--------------------------------------------------------------------------
|
| With AccountPayloadReceiver, RawMessagePayloadReceiver, InstallationPayloadMounterReceiver
|
*/
type ReceiverServer struct {
*BaseServer
accountReceiver PayloadReceiver
rawMessageReceiver PayloadReceiver
installationReceiver PayloadMounterReceiver
}
// NewReceiverServer returns a *SenderServer init from the given *ReceiverServerConfig
func NewReceiverServer(backend *api.GethStatusBackend, config *ReceiverServerConfig) (*ReceiverServer, error) {
logger := logutils.ZapLogger().Named("SenderServer")
e := NewPayloadEncryptor(config.ServerConfig.EK)
bs, err := NewBaseServer(logger, e, config.ServerConfig)
if err != nil {
return nil, err
}
ar, rmr, imr, err := NewPayloadReceivers(logger, e, backend, config.ReceiverConfig)
if err != nil {
return nil, err
}
return &ReceiverServer{
BaseServer: bs,
accountReceiver: ar,
rawMessageReceiver: rmr,
installationReceiver: imr,
}, nil
}
func (s *ReceiverServer) startReceivingData() error {
s.SetHandlers(server.HandlerPatternMap{
pairingChallenge: handlePairingChallenge(s.challengeGiver),
pairingReceiveAccount: handleReceiveAccount(s, s.accountReceiver),
pairingReceiveSyncDevice: handleParingSyncDeviceReceive(s, s.rawMessageReceiver),
// TODO implement refactor of installation data exchange to follow the send/receive pattern of
// the other handlers.
// https://github.com/status-im/status-go/issues/3304
// send installation data back to sender
pairingSendInstallation: middlewareChallenge(s.challengeGiver, handleSendInstallation(s, s.installationReceiver)),
})
return s.Start()
}
// MakeFullReceiverServer generates a fully configured and randomly seeded ReceiverServer
func MakeFullReceiverServer(backend *api.GethStatusBackend, config *ReceiverServerConfig) (*ReceiverServer, error) {
err := MakeServerConfig(config.ServerConfig)
if err != nil {
return nil, err
}
activeAccount, _ := backend.GetActiveAccount()
if activeAccount != nil {
config.ReceiverConfig.LoggedInKeyUID = activeAccount.KeyUID
}
config.ReceiverConfig.DB = backend.GetMultiaccountDB()
return NewReceiverServer(backend, config)
}
// StartUpReceiverServer generates a ReceiverServer, starts the sending server
// and returns the ConnectionParams string to allow a SenderClient to make a successful connection.
func StartUpReceiverServer(backend *api.GethStatusBackend, configJSON string) (string, error) {
conf := NewReceiverServerConfig()
err := json.Unmarshal([]byte(configJSON), conf)
if err != nil {
return "", err
}
ps, err := MakeFullReceiverServer(backend, conf)
if err != nil {
return "", err
}
err = ps.startReceivingData()
if err != nil {
return "", err
}
cp, err := ps.MakeConnectionParams()
if err != nil {
return "", err
}
return cp.ToString(), nil
}