package pairing import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "encoding/json" "net" "runtime" "time" "go.uber.org/zap" "github.com/status-im/status-go/timesource" "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 config ServerConfig } // 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.ListenIP.String(), nil, logger, ), challengeGiver: cg, config: *config, } bs.SetTimeout(config.Timeout) return bs, nil } // MakeConnectionParams generates a *ConnectionParams based on the Server's current state func (s *BaseServer) MakeConnectionParams() (*ConnectionParams, error) { return NewConnectionParams(s.config.IPAddresses, s.MustGetPort(), s.config.PK, s.config.EK, s.config.InstallationID, s.config.KeyUID), 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 } ips, err := server.GetLocalAddressesForPairingServer() if err != nil { return err } now := timesource.GetCurrentTime() logutils.ZapLogger().Debug("pairing server generate cert", logutils.UnixTimeMs("system time", time.Now()), logutils.UnixTimeMs("timesource time", now), ) tlsCert, _, err := GenerateCertFromKey(tlsKey, now, ips, []string{}) if err != nil { return err } config.PK = &tlsKey.PublicKey config.EK = AESKey config.Cert = &tlsCert config.IPAddresses = ips config.ListenIP = net.IPv4zero return nil } /* |-------------------------------------------------------------------------- | type SenderServer struct { |-------------------------------------------------------------------------- | | With AccountPayloadMounter, RawMessagePayloadMounter and InstallationPayloadMounterReceiver | */ type SenderServer struct { *BaseServer accountMounter PayloadMounter rawMessageMounter PayloadMounter installationMounter PayloadMounterReceiver backend *api.GethStatusBackend } // 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, backend: backend, }, nil } func (s *SenderServer) startSendingData() error { logger := s.GetLogger() beforeSending := func() { if s.backend != nil { err := s.backend.LocalPairingStarted() if err != nil { logger.Error("startSendingData backend.LocalPairingStarted()", zap.Error(err)) } } } s.SetHandlers(server.HandlerPatternMap{ pairingChallenge: handlePairingChallenge(s.challengeGiver), pairingSendAccount: middlewareChallenge(s.challengeGiver, handleSendAccount(logger, s.accountMounter, beforeSending)), pairingSendSyncDevice: middlewareChallenge(s.challengeGiver, handlePairingSyncDeviceSend(logger, s.rawMessageMounter, beforeSending)), // 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.GetLogger(), s.installationMounter)), }) return s.Start() } // MakeFullSenderServer generates a fully configured and randomly seeded SenderServer func MakeFullSenderServer(backend *api.GethStatusBackend, config *SenderServerConfig) (*SenderServer, error) { config.ServerConfig.InstallationID = backend.InstallationID() config.ServerConfig.KeyUID = backend.KeyUID() 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 } if len(conf.SenderConfig.ChatKey) == 0 { err = validateAndVerifyPassword(conf, conf.SenderConfig) 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 backend *api.GethStatusBackend } // 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, backend: backend, }, nil } func (s *ReceiverServer) startReceivingData() error { logger := s.GetLogger() beforeSending := func() { if s.backend != nil { err := s.backend.LocalPairingStarted() if err != nil { logger.Error("startSendingData backend.LocalPairingStarted()", zap.Error(err)) } } } s.SetHandlers(server.HandlerPatternMap{ pairingChallenge: handlePairingChallenge(s.challengeGiver), pairingReceiveAccount: handleReceiveAccount(logger, s.accountReceiver), pairingReceiveSyncDevice: handleParingSyncDeviceReceive(logger, 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(logger, s.installationReceiver, beforeSending)), }) return s.Start() } // MakeFullReceiverServer generates a fully configured and randomly seeded ReceiverServer func MakeFullReceiverServer(backend *api.GethStatusBackend, config *ReceiverServerConfig) (*ReceiverServer, error) { config.ServerConfig.InstallationID = backend.InstallationID() config.ServerConfig.KeyUID = backend.KeyUID() err := MakeServerConfig(config.ServerConfig) if err != nil { return nil, err } // ignore err because we allow no active account here 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 } // This is a temporal solution to allow clients not to pass DeviceType. // Check DeviceType deprecation reason for more info. conf.ReceiverConfig.DeviceType = runtime.GOOS err = validateReceiverConfig(conf, conf.ReceiverConfig) 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 } /* |-------------------------------------------------------------------------- | type KeystoreFilesSenderServer struct { |-------------------------------------------------------------------------- */ type KeystoreFilesSenderServer struct { *BaseServer keystoreFilesMounter PayloadMounter backend *api.GethStatusBackend } func NewKeystoreFilesSenderServer(backend *api.GethStatusBackend, config *KeystoreFilesSenderServerConfig) (*KeystoreFilesSenderServer, error) { logger := logutils.ZapLogger().Named("SenderServer") e := NewPayloadEncryptor(config.ServerConfig.EK) bs, err := NewBaseServer(logger, e, config.ServerConfig) if err != nil { return nil, err } kfm, err := NewKeystoreFilesPayloadMounter(backend, e, config.SenderConfig, logger) if err != nil { return nil, err } return &KeystoreFilesSenderServer{ BaseServer: bs, keystoreFilesMounter: kfm, backend: backend, }, nil } func (s *KeystoreFilesSenderServer) startSendingData() error { logger := s.GetLogger() beforeSending := func() { if s.backend != nil { err := s.backend.LocalPairingStarted() if err != nil { logger.Error("startSendingData backend.LocalPairingStarted()", zap.Error(err)) } } } s.SetHandlers(server.HandlerPatternMap{ pairingChallenge: handlePairingChallenge(s.challengeGiver), pairingSendAccount: middlewareChallenge(s.challengeGiver, handleSendAccount(logger, s.keystoreFilesMounter, beforeSending)), }) return s.Start() } // MakeFullSenderServer generates a fully configured and randomly seeded KeystoreFilesSenderServer func MakeKeystoreFilesSenderServer(backend *api.GethStatusBackend, config *KeystoreFilesSenderServerConfig) (*KeystoreFilesSenderServer, error) { config.ServerConfig.InstallationID = backend.InstallationID() config.ServerConfig.KeyUID = backend.KeyUID() err := MakeServerConfig(config.ServerConfig) if err != nil { return nil, err } return NewKeystoreFilesSenderServer(backend, config) } // StartUpKeystoreFilesSenderServer generates a KeystoreFilesSenderServer, starts the sending server // and returns the ConnectionParams string to allow a ReceiverClient to make a successful connection. func StartUpKeystoreFilesSenderServer(backend *api.GethStatusBackend, configJSON string) (string, error) { conf := NewKeystoreFilesSenderServerConfig() err := json.Unmarshal([]byte(configJSON), conf) if err != nil { return "", err } err = validateKeystoreFilesConfig(backend, conf) if err != nil { return "", err } ps, err := MakeKeystoreFilesSenderServer(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 }