Improved Local Pairing Separation of Concerns (#3248)
* Moved all configs into config.go * Completed build out of new config structures * Completed SenderClient process flow * Completed sync data Mounter and client integration * Completed installation data Mounter and client integration * House keeping, small refactor to match conventions. PayloadEncryptor is passed by value and used as a pointer to the instance value and not a shared pointer. * Reintroduced explicit Mounter field type * Completed ReceiverClient structs and flows * Finished BaseClient function parity with old acc * Integrated new Clients into tests Solved some test breaks caused by encryptors sharing pointers to their managed payloads * Built out SenderServer and ReceiverServer structs With all associated functions and integrated with endpoints. * Updated tests to handle new Server types * Added docs and additional refinement * Renamed some files to better match the content of those files * Added json tags to config fields that were missing explicit tags. * fix tests relating to payload locking * Addressing feedback from @ilmotta * Addressed feedback from @qfrank
This commit is contained in:
parent
7bc03e22f7
commit
7cd7430d31
2
go.mod
2
go.mod
|
@ -83,6 +83,7 @@ require (
|
|||
github.com/waku-org/go-waku v0.5.2-0.20230308135126-4b52983fc483
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
||||
go.uber.org/multierr v1.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -251,7 +252,6 @@ require (
|
|||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/dig v1.15.0 // indirect
|
||||
go.uber.org/fx v1.18.2 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
|
|
|
@ -1008,7 +1008,7 @@ func GenerateImages(filepath string, aX, aY, bX, bY int) string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
// GetConnectionStringForBeingBootstrapped starts a pairing.Receiving pairing.PairingServer
|
||||
// GetConnectionStringForBeingBootstrapped starts a pairing.ReceiverServer
|
||||
// then generates a pairing.ConnectionParams. Used when the device is Logged out or has no Account keys
|
||||
// and the device has no camera to read a QR code with
|
||||
//
|
||||
|
@ -1017,14 +1017,14 @@ func GetConnectionStringForBeingBootstrapped(configJSON string) string {
|
|||
if configJSON == "" {
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
|
||||
}
|
||||
cs, err := pairing.StartUpPairingServer(statusBackend, pairing.Receiving, configJSON)
|
||||
cs, err := pairing.StartUpReceiverServer(statusBackend, pairing.Receiving, configJSON)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
// GetConnectionStringForBootstrappingAnotherDevice starts a pairing.Sending pairing.Server
|
||||
// GetConnectionStringForBootstrappingAnotherDevice starts a pairing.SenderServer
|
||||
// then generates a pairing.ConnectionParams. Used when the device is Logged in and therefore has Account keys
|
||||
// and the device might not have a camera
|
||||
//
|
||||
|
@ -1032,34 +1032,45 @@ func GetConnectionStringForBeingBootstrapped(configJSON string) string {
|
|||
// sending account data to a mobile (device with camera)
|
||||
func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string {
|
||||
if configJSON == "" {
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
|
||||
return makeJSONResponse(fmt.Errorf("no config given, SendingServerConfig is expected"))
|
||||
}
|
||||
cs, err := pairing.StartUpPairingServer(statusBackend, pairing.Sending, configJSON)
|
||||
cs, err := pairing.StartUpSenderServer(statusBackend, pairing.Sending, configJSON)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
// InputConnectionStringForBootstrapping starts a pairing.Client
|
||||
// InputConnectionStringForBootstrapping starts a pairing.ReceiverClient
|
||||
// The given server.ConnectionParams string will determine the server.Mode
|
||||
//
|
||||
// server.Mode = server.Sending
|
||||
// Used when the device is Logged in and therefore has Account keys and the has a camera to read a QR code
|
||||
//
|
||||
// Example: A mobile (device with camera) sending account data to a desktop device (device without camera)
|
||||
//
|
||||
// server.Mode = server.Receiving
|
||||
// Used when the device is Logged out or has no Account keys and has a camera to read a QR code
|
||||
//
|
||||
// Example: A mobile device (device with a camera) receiving account data from
|
||||
// a device with a screen (mobile or desktop devices)
|
||||
func InputConnectionStringForBootstrapping(cs, configJSON string) string {
|
||||
if configJSON == "" {
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
|
||||
return makeJSONResponse(fmt.Errorf("no config given, ReceiverClientConfig is expected"))
|
||||
}
|
||||
|
||||
err := pairing.StartUpPairingClient(statusBackend, cs, configJSON)
|
||||
err := pairing.StartUpReceivingClient(statusBackend, cs, configJSON)
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
// InputConnectionStringForBootstrappingAnotherDevice starts a pairing.SendingClient
|
||||
// The given server.ConnectionParams string will determine the server.Mode
|
||||
//
|
||||
// server.Mode = server.Receiving
|
||||
// Used when the device is Logged in and therefore has Account keys and the has a camera to read a QR code
|
||||
//
|
||||
// Example: A mobile (device with camera) sending account data to a desktop device (device without camera)
|
||||
func InputConnectionStringForBootstrappingAnotherDevice(cs, configJSON string) string {
|
||||
if configJSON == "" {
|
||||
return makeJSONResponse(fmt.Errorf("no config given, SenderClientConfig is expected"))
|
||||
}
|
||||
|
||||
err := pairing.StartUpSendingClient(statusBackend, cs, configJSON)
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ type NodeConfig struct {
|
|||
// NetworkID sets network to use for selecting peers to connect to
|
||||
NetworkID uint64 `json:"NetworkId" validate:"required"`
|
||||
|
||||
RootDataDir string `json:"-"`
|
||||
RootDataDir string `json:",omitempty"`
|
||||
|
||||
// DataDir is the file system folder the node should use for any data storage needs.
|
||||
DataDir string `validate:"required"`
|
||||
|
|
|
@ -18,6 +18,9 @@ import (
|
|||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
// TODO Reconcile duplicate function here and in server/certs.go
|
||||
// https://github.com/status-im/status-go/issues/3300
|
||||
|
||||
func makeSerialNumberFromKey(pk *ecdsa.PrivateKey) *big.Int {
|
||||
h := sha256.New()
|
||||
h.Write(append(pk.D.Bytes(), append(pk.Y.Bytes(), pk.X.Bytes()...)...))
|
||||
|
|
|
@ -2,9 +2,9 @@ package pairing
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -20,21 +20,26 @@ import (
|
|||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*http.Client
|
||||
PayloadManager
|
||||
rawMessagePayloadManager *RawMessagePayloadManager
|
||||
installationPayloadManager *InstallationPayloadManager
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| BaseClient
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
|
|
||||
|
|
||||
*/
|
||||
|
||||
baseAddress *url.URL
|
||||
certPEM []byte
|
||||
serverPK *ecdsa.PublicKey
|
||||
serverMode Mode
|
||||
// BaseClient is responsible for lower level pairing.Client functionality common to dependent Client types
|
||||
type BaseClient struct {
|
||||
*http.Client
|
||||
serverCert *x509.Certificate
|
||||
encryptor *PayloadEncryptor
|
||||
baseAddress *url.URL
|
||||
serverChallenge []byte
|
||||
}
|
||||
|
||||
func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, config *AccountPayloadManagerConfig) (*Client, error) {
|
||||
// NewBaseClient returns a fully qualified BaseClient from the given ConnectionParams
|
||||
func NewBaseClient(c *ConnectionParams) (*BaseClient, error) {
|
||||
u, err := c.URL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -44,10 +49,12 @@ func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, confi
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = verifyCert(serverCert, c.publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert.Raw})
|
||||
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
|
@ -71,117 +78,116 @@ func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, confi
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger := logutils.ZapLogger().Named("Client")
|
||||
pm, err := NewAccountPayloadManager(c.aesKey, config, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rmpm, err := NewRawMessagePayloadManager(logger, pm.accountPayload, c.aesKey, backend, config.GetNodeConfig(), config.GetSettingCurrentNetwork(), config.GetDeviceType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipm, err := NewInstallationPayloadManager(logger, c.aesKey, backend, config.GetDeviceType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{
|
||||
Client: &http.Client{Transport: tr, Jar: cj},
|
||||
baseAddress: u,
|
||||
certPEM: certPem,
|
||||
serverCert: serverCert,
|
||||
serverPK: c.publicKey,
|
||||
serverMode: c.serverMode,
|
||||
PayloadManager: pm,
|
||||
rawMessagePayloadManager: rmpm,
|
||||
installationPayloadManager: ipm,
|
||||
return &BaseClient{
|
||||
Client: &http.Client{Transport: tr, Jar: cj},
|
||||
serverCert: serverCert,
|
||||
encryptor: NewPayloadEncryptor(c.aesKey),
|
||||
baseAddress: u,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) PairAccount() error {
|
||||
switch c.serverMode {
|
||||
case Receiving:
|
||||
return c.sendAccountData()
|
||||
case Sending:
|
||||
err := c.getChallenge()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.receiveAccountData()
|
||||
default:
|
||||
return fmt.Errorf("unrecognised server mode '%d'", c.serverMode)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) PairSyncDevice() error {
|
||||
switch c.serverMode {
|
||||
case Receiving:
|
||||
return c.sendSyncDeviceData()
|
||||
case Sending:
|
||||
return c.receiveSyncDeviceData()
|
||||
default:
|
||||
return fmt.Errorf("unrecognised server mode '%d'", c.serverMode)
|
||||
}
|
||||
}
|
||||
|
||||
// PairInstallation transfer installation data from receiver to sender.
|
||||
// installation data from sender to receiver already processed within PairSyncDevice method on the receiver side.
|
||||
func (c *Client) PairInstallation() error {
|
||||
switch c.serverMode {
|
||||
case Receiving:
|
||||
return c.receiveInstallationData()
|
||||
case Sending:
|
||||
return c.sendInstallationData()
|
||||
default:
|
||||
return fmt.Errorf("unrecognised server mode '%d'", c.serverMode)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) sendInstallationData() error {
|
||||
err := c.installationPayloadManager.Mount()
|
||||
// getChallenge makes a call to the identified Server and receives a [32]byte challenge
|
||||
func (c *BaseClient) getChallenge() error {
|
||||
c.baseAddress.Path = pairingChallenge
|
||||
resp, err := c.Get(c.baseAddress.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.baseAddress.Path = pairingReceiveInstallation
|
||||
req, err := http.NewRequest(http.MethodPost, c.baseAddress.String(), bytes.NewBuffer(c.installationPayloadManager.ToSend()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
if c.serverChallenge != nil {
|
||||
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
||||
}
|
||||
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("[client] status not okay when sending installation data, status: %s", resp.Status)
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
return err
|
||||
return fmt.Errorf("[client] status not ok when getting challenge, received '%s'", resp.Status)
|
||||
}
|
||||
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
c.serverChallenge, err = ioutil.ReadAll(resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
// doChallenge checks if there is a serverChallenge and encrypts the challenge using the shared AES key
|
||||
func (c *BaseClient) doChallenge(req *http.Request) error {
|
||||
if c.serverChallenge != nil {
|
||||
ec, err := c.encryptor.encryptPlain(c.serverChallenge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) sendSyncDeviceData() error {
|
||||
err := c.rawMessagePayloadManager.Mount()
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SenderClient
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| With AccountPayloadMounter, RawMessagePayloadMounter and InstallationPayloadMounterReceiver
|
||||
|
|
||||
*/
|
||||
|
||||
// SenderClient is responsible for sending pairing data to a ReceiverServer
|
||||
type SenderClient struct {
|
||||
*BaseClient
|
||||
accountMounter PayloadMounter
|
||||
rawMessageMounter *RawMessagePayloadMounter
|
||||
installationMounter *InstallationPayloadMounterReceiver
|
||||
}
|
||||
|
||||
// NewSenderClient returns a fully qualified SenderClient created with the incoming parameters
|
||||
func NewSenderClient(backend *api.GethStatusBackend, c *ConnectionParams, config *SenderClientConfig) (*SenderClient, error) {
|
||||
logger := logutils.ZapLogger().Named("SenderClient")
|
||||
pe := NewPayloadEncryptor(c.aesKey)
|
||||
|
||||
bc, err := NewBaseClient(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
am, rmm, imr, err := NewPayloadMounters(logger, pe, backend, config.SenderConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SenderClient{
|
||||
BaseClient: bc,
|
||||
accountMounter: am,
|
||||
rawMessageMounter: rmm,
|
||||
installationMounter: imr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *SenderClient) sendAccountData() error {
|
||||
err := c.accountMounter.Mount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.baseAddress.Path = pairingSyncDeviceReceive
|
||||
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.rawMessagePayloadManager.ToSend()))
|
||||
c.baseAddress.Path = pairingReceiveAccount
|
||||
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.accountMounter.ToSend()))
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("[client] status not ok when sending account data, received '%s'", resp.Status)
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
return err
|
||||
}
|
||||
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||
|
||||
c.accountMounter.LockPayload()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SenderClient) sendSyncDeviceData() error {
|
||||
err := c.rawMessageMounter.Mount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.baseAddress.Path = pairingReceiveSyncDevice
|
||||
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.rawMessageMounter.ToSend()))
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
|
@ -197,7 +203,7 @@ func (c *Client) sendSyncDeviceData() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) receiveInstallationData() error {
|
||||
func (c *SenderClient) receiveInstallationData() error {
|
||||
c.baseAddress.Path = pairingSendInstallation
|
||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||
if err != nil {
|
||||
|
@ -223,7 +229,7 @@ func (c *Client) receiveInstallationData() error {
|
|||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
|
||||
err = c.installationPayloadManager.Receive(payload)
|
||||
err = c.installationMounter.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
return err
|
||||
|
@ -232,89 +238,94 @@ func (c *Client) receiveInstallationData() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) receiveSyncDeviceData() error {
|
||||
c.baseAddress.Path = pairingSyncDeviceSend
|
||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||
// setupSendingClient creates a new SenderClient after parsing string inputs
|
||||
func setupSendingClient(backend *api.GethStatusBackend, cs, configJSON string) (*SenderClient, error) {
|
||||
ccp := new(ConnectionParams)
|
||||
err := ccp.FromString(cs)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.serverChallenge != nil {
|
||||
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
||||
}
|
||||
|
||||
resp, err := c.Do(req)
|
||||
conf := NewSenderClientConfig()
|
||||
err = json.Unmarshal([]byte(configJSON), conf)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("[client] status not ok when receiving sync device data, received '%s'", resp.Status)
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
conf.SenderConfig.DB = backend.GetMultiaccountDB()
|
||||
|
||||
payload, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
||||
|
||||
err = c.rawMessagePayloadManager.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
|
||||
return nil
|
||||
return NewSenderClient(backend, ccp, conf)
|
||||
}
|
||||
|
||||
func (c *Client) sendAccountData() error {
|
||||
err := c.Mount()
|
||||
// StartUpSendingClient creates a SenderClient and triggers all `send` calls in sequence to the ReceiverServer
|
||||
func StartUpSendingClient(backend *api.GethStatusBackend, cs, configJSON string) error {
|
||||
c, err := setupSendingClient(backend, cs, configJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.baseAddress.Path = pairingReceiveAccount
|
||||
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.PayloadManager.ToSend()))
|
||||
err = c.sendAccountData()
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("[client] status not ok when sending account data, received '%s'", resp.Status)
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
err = c.sendSyncDeviceData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||
|
||||
c.PayloadManager.LockPayload()
|
||||
return nil
|
||||
return c.receiveInstallationData()
|
||||
}
|
||||
|
||||
func (c *Client) receiveAccountData() error {
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ReceiverClient
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| With AccountPayloadReceiver, RawMessagePayloadReceiver, InstallationPayloadMounterReceiver
|
||||
|
|
||||
*/
|
||||
|
||||
// ReceiverClient is responsible for accepting pairing data to a SenderServer
|
||||
type ReceiverClient struct {
|
||||
*BaseClient
|
||||
|
||||
accountReceiver PayloadReceiver
|
||||
rawMessageReceiver *RawMessagePayloadReceiver
|
||||
installationReceiver *InstallationPayloadMounterReceiver
|
||||
}
|
||||
|
||||
// NewReceiverClient returns a fully qualified ReceiverClient created with the incoming parameters
|
||||
func NewReceiverClient(backend *api.GethStatusBackend, c *ConnectionParams, config *ReceiverClientConfig) (*ReceiverClient, error) {
|
||||
bc, err := NewBaseClient(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger := logutils.ZapLogger().Named("ReceiverClient")
|
||||
pe := NewPayloadEncryptor(c.aesKey)
|
||||
|
||||
ar, rmr, imr, err := NewPayloadReceivers(logger, pe, backend, config.ReceiverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ReceiverClient{
|
||||
BaseClient: bc,
|
||||
accountReceiver: ar,
|
||||
rawMessageReceiver: rmr,
|
||||
installationReceiver: imr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *ReceiverClient) receiveAccountData() error {
|
||||
c.baseAddress.Path = pairingSendAccount
|
||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.serverChallenge != nil {
|
||||
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
||||
err = c.doChallenge(req)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.Do(req)
|
||||
|
@ -336,7 +347,7 @@ func (c *Client) receiveAccountData() error {
|
|||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||
|
||||
err = c.PayloadManager.Receive(payload)
|
||||
err = c.accountReceiver.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
return err
|
||||
|
@ -345,56 +356,118 @@ func (c *Client) receiveAccountData() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) getChallenge() error {
|
||||
c.baseAddress.Path = pairingChallenge
|
||||
resp, err := c.Get(c.baseAddress.String())
|
||||
func (c *ReceiverClient) receiveSyncDeviceData() error {
|
||||
c.baseAddress.Path = pairingSendSyncDevice
|
||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.doChallenge(req)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("[client] status not ok when getting challenge, received '%s'", resp.Status)
|
||||
err = fmt.Errorf("[client] status not ok when receiving sync device data, received '%s'", resp.Status)
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
|
||||
c.serverChallenge, err = ioutil.ReadAll(resp.Body)
|
||||
return err
|
||||
payload, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
||||
|
||||
err = c.rawMessageReceiver.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
return err
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartUpPairingClient(backend *api.GethStatusBackend, cs, configJSON string) error {
|
||||
c, err := setupClient(backend, cs, configJSON)
|
||||
func (c *ReceiverClient) sendInstallationData() error {
|
||||
err := c.installationReceiver.Mount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.PairAccount()
|
||||
|
||||
c.baseAddress.Path = pairingReceiveInstallation
|
||||
req, err := http.NewRequest(http.MethodPost, c.baseAddress.String(), bytes.NewBuffer(c.installationReceiver.ToSend()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.PairSyncDevice()
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
|
||||
err = c.doChallenge(req)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
return err
|
||||
}
|
||||
return c.PairInstallation()
|
||||
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("[client] status not okay when sending installation data, status: %s", resp.Status)
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
return err
|
||||
}
|
||||
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupClient(backend *api.GethStatusBackend, cs string, configJSON string) (*Client, error) {
|
||||
// setupReceivingClient creates a new ReceiverClient after parsing string inputs
|
||||
func setupReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) (*ReceiverClient, error) {
|
||||
ccp := new(ConnectionParams)
|
||||
err := ccp.FromString(cs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf, err := NewPayloadSourceForClient(configJSON, ccp.serverMode)
|
||||
conf := NewReceiverClientConfig()
|
||||
err = json.Unmarshal([]byte(configJSON), conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accountPayloadManagerConfig := &AccountPayloadManagerConfig{DB: backend.GetMultiaccountDB(), PayloadSourceConfig: conf}
|
||||
if ccp.serverMode == Sending {
|
||||
updateLoggedInKeyUID(accountPayloadManagerConfig, backend)
|
||||
}
|
||||
c, err := NewPairingClient(backend, ccp, accountPayloadManagerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
conf.ReceiverConfig.DB = backend.GetMultiaccountDB()
|
||||
|
||||
return NewReceiverClient(backend, ccp, conf)
|
||||
}
|
||||
|
||||
// StartUpReceivingClient creates a ReceiverClient and triggers all `receive` calls in sequence to the SenderServer
|
||||
func StartUpReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) error {
|
||||
c, err := setupReceivingClient(backend, cs, configJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.getChallenge()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.receiveAccountData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.receiveSyncDeviceData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.sendInstallationData()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,61 @@
|
|||
package pairing
|
||||
|
||||
import "github.com/status-im/status-go/api"
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
func updateLoggedInKeyUID(accountPayloadManagerConfig *AccountPayloadManagerConfig, backend *api.GethStatusBackend) {
|
||||
activeAccount, _ := backend.GetActiveAccount()
|
||||
if activeAccount != nil {
|
||||
accountPayloadManagerConfig.LoggedInKeyUID = activeAccount.KeyUID
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
"github.com/status-im/status-go/eth-node/keystore"
|
||||
)
|
||||
|
||||
func validateKeys(keys map[string][]byte, password string) error {
|
||||
for _, key := range keys {
|
||||
k, err := keystore.DecryptKey(key, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = generator.ValidateKeystoreExtendedKey(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func emptyDir(dir string) error {
|
||||
// Open the directory
|
||||
d, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
// Get all the directory entries
|
||||
entries, err := d.Readdir(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove all the files and directories
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if name == "." || name == ".." {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, name)
|
||||
if entry.IsDir() {
|
||||
err = os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/asn1"
|
||||
|
||||
"math/big"
|
||||
"net"
|
||||
"testing"
|
||||
|
@ -90,7 +89,8 @@ type TestPairingServerComponents struct {
|
|||
EphemeralAES []byte
|
||||
OutboundIP net.IP
|
||||
Cert tls.Certificate
|
||||
PS *Server
|
||||
SS *SenderServer
|
||||
RS *ReceiverServer
|
||||
}
|
||||
|
||||
func (tpsc *TestPairingServerComponents) SetupPairingServerComponents(t *testing.T) {
|
||||
|
@ -113,12 +113,16 @@ func (tpsc *TestPairingServerComponents) SetupPairingServerComponents(t *testing
|
|||
tpsc.Cert, _, err = GenerateCertFromKey(tpsc.EphemeralPK, time.Now(), tpsc.OutboundIP.String())
|
||||
require.NoError(t, err)
|
||||
|
||||
tpsc.PS, err = NewPairingServer(nil, &Config{
|
||||
PK: &tpsc.EphemeralPK.PublicKey,
|
||||
EK: tpsc.EphemeralAES,
|
||||
Cert: &tpsc.Cert,
|
||||
Hostname: tpsc.OutboundIP.String(),
|
||||
AccountPayloadManagerConfig: &AccountPayloadManagerConfig{}})
|
||||
sc := &ServerConfig{
|
||||
PK: &tpsc.EphemeralPK.PublicKey,
|
||||
EK: tpsc.EphemeralAES,
|
||||
Cert: &tpsc.Cert,
|
||||
Hostname: tpsc.OutboundIP.String(),
|
||||
}
|
||||
|
||||
tpsc.SS, err = NewSenderServer(nil, &SenderServerConfig{ServerConfig: sc, SenderConfig: &SenderConfig{}})
|
||||
require.NoError(t, err)
|
||||
tpsc.RS, err = NewReceiverServer(nil, &ReceiverServerConfig{ServerConfig: sc, ReceiverConfig: &ReceiverConfig{}})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
@ -130,22 +134,33 @@ func (tlc *TestLoggerComponents) SetupLoggerComponents() {
|
|||
tlc.Logger = logutils.ZapLogger()
|
||||
}
|
||||
|
||||
type MockEncryptOnlyPayloadManager struct {
|
||||
*PayloadEncryptionManager
|
||||
type MockPayloadReceiver struct {
|
||||
encryptor *PayloadEncryptor
|
||||
}
|
||||
|
||||
func NewMockEncryptOnlyPayloadManager(aesKey []byte) (*MockEncryptOnlyPayloadManager, error) {
|
||||
pem, err := NewPayloadEncryptionManager(aesKey, logutils.ZapLogger())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MockEncryptOnlyPayloadManager{
|
||||
pem,
|
||||
}, nil
|
||||
func NewMockPayloadReceiver(aesKey []byte) *MockPayloadReceiver {
|
||||
return &MockPayloadReceiver{NewPayloadEncryptor(aesKey)}
|
||||
}
|
||||
|
||||
func (m *MockEncryptOnlyPayloadManager) Mount() error {
|
||||
func (m *MockPayloadReceiver) Receive(data []byte) error {
|
||||
return m.encryptor.decrypt(data)
|
||||
}
|
||||
|
||||
func (m *MockPayloadReceiver) Received() []byte {
|
||||
return m.encryptor.getDecrypted()
|
||||
}
|
||||
|
||||
func (m *MockPayloadReceiver) LockPayload() {}
|
||||
|
||||
type MockPayloadMounter struct {
|
||||
encryptor *PayloadEncryptor
|
||||
}
|
||||
|
||||
func NewMockPayloadMounter(aesKey []byte) *MockPayloadMounter {
|
||||
return &MockPayloadMounter{NewPayloadEncryptor(aesKey)}
|
||||
}
|
||||
|
||||
func (m *MockPayloadMounter) Mount() error {
|
||||
// Make a random payload
|
||||
data := make([]byte, 32)
|
||||
_, err := rand.Read(data)
|
||||
|
@ -153,9 +168,13 @@ func (m *MockEncryptOnlyPayloadManager) Mount() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return m.Encrypt(data)
|
||||
return m.encryptor.encrypt(data)
|
||||
}
|
||||
|
||||
func (m *MockEncryptOnlyPayloadManager) Receive(data []byte) error {
|
||||
return m.Decrypt(data)
|
||||
func (m *MockPayloadMounter) ToSend() []byte {
|
||||
return m.encryptor.getEncrypted()
|
||||
}
|
||||
|
||||
func (m *MockPayloadMounter) LockPayload() {
|
||||
m.encryptor.lockPayload()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
)
|
||||
|
||||
type SenderConfig struct {
|
||||
// SenderConfig.KeystorePath must end with keyUID
|
||||
KeystorePath string `json:"keystorePath"`
|
||||
// DeviceType SendPairInstallation need this information
|
||||
DeviceType string `json:"deviceType"`
|
||||
|
||||
KeyUID string `json:"keyUID"`
|
||||
Password string `json:"password"`
|
||||
|
||||
DB *multiaccounts.Database `json:"-"`
|
||||
}
|
||||
|
||||
type ReceiverConfig struct {
|
||||
NodeConfig *params.NodeConfig `json:"nodeConfig"`
|
||||
|
||||
// ReceiverConfig.KeystorePath must not end with keyUID (because keyUID is not known yet)
|
||||
KeystorePath string `json:"keystorePath"`
|
||||
// DeviceType SendPairInstallation need this information
|
||||
DeviceType string `json:"deviceType"`
|
||||
KDFIterations int `json:"kdfIterations"`
|
||||
// SettingCurrentNetwork corresponding to field current_network from table settings, so that we can override current network from sender
|
||||
SettingCurrentNetwork string `json:"settingCurrentNetwork"`
|
||||
|
||||
DB *multiaccounts.Database `json:"-"`
|
||||
LoggedInKeyUID string `json:"-"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
// Timeout the number of milliseconds after which the pairing server will automatically terminate
|
||||
Timeout uint `json:"timeout"`
|
||||
|
||||
// Connection fields, not json (un)marshalled
|
||||
// Required for the server, but MUST NOT come from client
|
||||
|
||||
PK *ecdsa.PublicKey `json:"-"`
|
||||
EK []byte `json:"-"`
|
||||
Cert *tls.Certificate `json:"-"`
|
||||
Hostname string `json:"-"`
|
||||
Mode Mode `json:"-"`
|
||||
}
|
||||
|
||||
type ClientConfig struct{}
|
||||
|
||||
type SenderServerConfig struct {
|
||||
SenderConfig *SenderConfig `json:"senderConfig"`
|
||||
ServerConfig *ServerConfig `json:"serverConfig"`
|
||||
}
|
||||
|
||||
type SenderClientConfig struct {
|
||||
SenderConfig *SenderConfig `json:"senderConfig"`
|
||||
ClientConfig *ClientConfig `json:"clientConfig"`
|
||||
}
|
||||
|
||||
type ReceiverClientConfig struct {
|
||||
ReceiverConfig *ReceiverConfig `json:"receiverConfig"`
|
||||
ClientConfig *ClientConfig `json:"clientConfig"`
|
||||
}
|
||||
|
||||
type ReceiverServerConfig struct {
|
||||
ReceiverConfig *ReceiverConfig `json:"receiverConfig"`
|
||||
ServerConfig *ServerConfig `json:"serverConfig"`
|
||||
}
|
||||
|
||||
func NewSenderServerConfig() *SenderServerConfig {
|
||||
return &SenderServerConfig{
|
||||
SenderConfig: new(SenderConfig),
|
||||
ServerConfig: new(ServerConfig),
|
||||
}
|
||||
}
|
||||
|
||||
func NewSenderClientConfig() *SenderClientConfig {
|
||||
return &SenderClientConfig{
|
||||
SenderConfig: new(SenderConfig),
|
||||
ClientConfig: new(ClientConfig),
|
||||
}
|
||||
}
|
||||
|
||||
func NewReceiverClientConfig() *ReceiverClientConfig {
|
||||
return &ReceiverClientConfig{
|
||||
ReceiverConfig: new(ReceiverConfig),
|
||||
ClientConfig: new(ClientConfig),
|
||||
}
|
||||
}
|
||||
|
||||
func NewReceiverServerConfig() *ReceiverServerConfig {
|
||||
return &ReceiverServerConfig{
|
||||
ReceiverConfig: new(ReceiverConfig),
|
||||
ServerConfig: new(ServerConfig),
|
||||
}
|
||||
}
|
|
@ -69,6 +69,9 @@ func (cp *ConnectionParams) ToString() string {
|
|||
ek := base58.Encode(cp.aesKey)
|
||||
m := base58.Encode(new(big.Int).SetInt64(int64(cp.serverMode)).Bytes())
|
||||
|
||||
// TODO remove server mode from the connection string, rely on specific function calls rather than algorithmic orchestration
|
||||
// https://github.com/status-im/status-go/issues/3301
|
||||
|
||||
return fmt.Sprintf("%s%s:%s:%s:%s:%s:%s", connectionStringID, v, ip, p, k, ek, m)
|
||||
}
|
||||
|
||||
|
@ -178,7 +181,7 @@ func (cp *ConnectionParams) validateAESKey() error {
|
|||
|
||||
func (cp *ConnectionParams) validateServerMode() error {
|
||||
switch cp.serverMode {
|
||||
case Receiving, Sending:
|
||||
case 0, Receiving, Sending:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid server mode '%d'", cp.serverMode)
|
||||
|
|
|
@ -22,7 +22,7 @@ type ConnectionParamsSuite struct {
|
|||
TestCertComponents
|
||||
TestLoggerComponents
|
||||
|
||||
server *Server
|
||||
server *BaseServer
|
||||
}
|
||||
|
||||
func (s *ConnectionParamsSuite) SetupSuite() {
|
||||
|
@ -37,7 +37,7 @@ func (s *ConnectionParamsSuite) SetupSuite() {
|
|||
err = bs.SetPort(1337)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.server = &Server{
|
||||
s.server = &BaseServer{
|
||||
Server: bs,
|
||||
pk: &s.PK.PublicKey,
|
||||
ek: s.AES,
|
||||
|
|
|
@ -29,7 +29,7 @@ type Event struct {
|
|||
type Action int
|
||||
|
||||
const (
|
||||
ActionConnect = iota + 1
|
||||
ActionConnect Action = iota + 1
|
||||
ActionPairingAccount
|
||||
ActionSyncDevice
|
||||
ActionPairingInstallation
|
||||
|
|
|
@ -9,18 +9,17 @@ import (
|
|||
"github.com/btcsuite/btcutil/base58"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
const (
|
||||
// Handler routes for pairing
|
||||
pairingBase = "/pairing"
|
||||
pairingChallenge = pairingBase + "/challenge"
|
||||
pairingSendAccount = pairingBase + "/sendAccount"
|
||||
pairingReceiveAccount = pairingBase + "/receiveAccount"
|
||||
pairingChallenge = pairingBase + "/challenge"
|
||||
pairingSyncDeviceSend = pairingBase + "/sendSyncDevice"
|
||||
pairingSyncDeviceReceive = pairingBase + "/receiveSyncDevice"
|
||||
pairingSendSyncDevice = pairingBase + "/sendSyncDevice"
|
||||
pairingReceiveSyncDevice = pairingBase + "/receiveSyncDevice"
|
||||
pairingSendInstallation = pairingBase + "/sendInstallation"
|
||||
pairingReceiveInstallation = pairingBase + "/receiveInstallation"
|
||||
|
||||
|
@ -29,145 +28,172 @@ const (
|
|||
sessionBlocked = "blocked"
|
||||
)
|
||||
|
||||
func handleReceiveAccount(ps *Server) http.HandlerFunc {
|
||||
// Account handling
|
||||
|
||||
func handleReceiveAccount(hs HandlerServer, pr PayloadReceiver) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
|
||||
logger := ps.GetLogger()
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
logger.Error("handleReceiveAccount io.ReadAll(r.Body)", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||
|
||||
err = ps.PayloadManager.Receive(payload)
|
||||
err = pr.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
logger.Error("ps.PayloadManager.Receive(payload)", zap.Error(err))
|
||||
logger.Error("handleReceiveAccount pr.Receive(payload)", zap.Error(err), zap.Binary("payload", payload))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingAccount})
|
||||
}
|
||||
}
|
||||
|
||||
func handleReceiveInstallation(ps *Server) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingInstallation})
|
||||
logger := ps.GetLogger()
|
||||
func handleSendAccount(hs HandlerServer, pm PayloadMounter) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
err := pm.Mount()
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("handleReceiveInstallation io.ReadAll(r.Body)", zap.Error(err))
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
logger.Error("handleSendAccount pm.Mount()", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
|
||||
err = ps.installationPayloadManager.Receive(payload)
|
||||
_, err = w.Write(pm.ToSend())
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("ps.installationPayloadManager.Receive(payload)", zap.Error(err))
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
logger.Error("handleSendAccount w.Write(pm.ToSend())", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingInstallation})
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||
|
||||
pm.LockPayload()
|
||||
}
|
||||
}
|
||||
|
||||
func handleParingSyncDeviceReceive(ps *Server) http.HandlerFunc {
|
||||
// Device sync handling
|
||||
|
||||
func handleParingSyncDeviceReceive(hs HandlerServer, pr PayloadReceiver) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
|
||||
logger := ps.GetLogger()
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
logger.Error("handleParingSyncDeviceReceive io.ReadAll(r.Body)", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
||||
|
||||
err = ps.rawMessagePayloadManager.Receive(payload)
|
||||
err = pr.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
logger.Error("ps.rawMessagePayloadManager.Receive(payload)", zap.Error(err))
|
||||
logger.Error("handleParingSyncDeviceReceive pr.Receive(payload)", zap.Error(err), zap.Binary("payload", payload))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
|
||||
}
|
||||
}
|
||||
|
||||
func handleSendAccount(ps *Server) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
|
||||
logger := ps.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
_, err := w.Write(ps.PayloadManager.ToSend())
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||
logger.Error("w.Write(ps.PayloadManager.ToSend())", zap.Error(err))
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||
|
||||
ps.PayloadManager.LockPayload()
|
||||
}
|
||||
}
|
||||
|
||||
func handleSendInstallation(ps *Server) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingInstallation})
|
||||
logger := ps.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
err := ps.installationPayloadManager.Mount()
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("ps.installationPayloadManager.Mount()", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(ps.installationPayloadManager.ToSend())
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("w.Write(ps.installationPayloadManager.ToSend())", zap.Error(err))
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
|
||||
ps.installationPayloadManager.LockPayload()
|
||||
}
|
||||
}
|
||||
|
||||
func handlePairingSyncDeviceSend(ps *Server) http.HandlerFunc {
|
||||
func handlePairingSyncDeviceSend(hs HandlerServer, pm PayloadMounter) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
|
||||
logger := ps.GetLogger()
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
|
||||
err := ps.rawMessagePayloadManager.Mount()
|
||||
err := pm.Mount()
|
||||
if err != nil {
|
||||
// maybe better to use a new event type here instead of EventTransferError?
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
logger.Error("ps.rawMessagePayloadManager.Mount()", zap.Error(err))
|
||||
logger.Error("handlePairingSyncDeviceSend pm.Mount()", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(ps.rawMessagePayloadManager.ToSend())
|
||||
_, err = w.Write(pm.ToSend())
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||
logger.Error("w.Write(ps.rawMessagePayloadManager.ToSend())", zap.Error(err))
|
||||
logger.Error("handlePairingSyncDeviceSend w.Write(pm.ToSend())", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
||||
|
||||
ps.rawMessagePayloadManager.LockPayload()
|
||||
pm.LockPayload()
|
||||
}
|
||||
}
|
||||
|
||||
func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
||||
logger := ps.GetLogger()
|
||||
// Installation data handling
|
||||
|
||||
func handleReceiveInstallation(hs HandlerServer, pmr PayloadMounterReceiver) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingInstallation})
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
s, err := ps.cookieStore.Get(r, sessionChallenge)
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
logger.Error("ps.cookieStore.Get(r, pairingStoreChallenge)", zap.Error(err))
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("handleReceiveInstallation io.ReadAll(r.Body)", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
|
||||
err = pmr.Receive(payload)
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("handleReceiveInstallation pmr.Receive(payload)", zap.Error(err), zap.Binary("payload", payload))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingInstallation})
|
||||
}
|
||||
}
|
||||
|
||||
func handleSendInstallation(hs HandlerServer, pmr PayloadMounterReceiver) http.HandlerFunc {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingInstallation})
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
err := pmr.Mount()
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("handleSendInstallation pmr.Mount()", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(pmr.ToSend())
|
||||
if err != nil {
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||
logger.Error("handleSendInstallation w.Write(pmr.ToSend())", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||
|
||||
pmr.LockPayload()
|
||||
}
|
||||
}
|
||||
|
||||
// Challenge middleware and handling
|
||||
|
||||
func middlewareChallenge(hs HandlerServer, next http.Handler) http.HandlerFunc {
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
s, err := hs.GetCookieStore().Get(r, sessionChallenge)
|
||||
if err != nil {
|
||||
logger.Error("middlewareChallenge: hs.GetCookieStore().Get(r, sessionChallenge)", zap.Error(err), zap.String("sessionChallenge", sessionChallenge))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -185,9 +211,9 @@ func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c, err := common.Decrypt(base58.Decode(pc), ps.ek)
|
||||
c, err := hs.DecryptPlain(base58.Decode(pc))
|
||||
if err != nil {
|
||||
logger.Error("c, err := common.Decrypt(rc, ps.ek)", zap.Error(err))
|
||||
logger.Error("middlewareChallenge: c, err := hs.DecryptPlain(base58.Decode(pc))", zap.Error(err), zap.String("pc", pc))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -205,7 +231,7 @@ func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
|||
s.Values[sessionBlocked] = true
|
||||
err = s.Save(r, w)
|
||||
if err != nil {
|
||||
logger.Error("err = s.Save(r, w)", zap.Error(err))
|
||||
logger.Error("middlewareChallenge: err = s.Save(r, w)", zap.Error(err))
|
||||
}
|
||||
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
|
@ -216,12 +242,13 @@ func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func handlePairingChallenge(ps *Server) http.HandlerFunc {
|
||||
logger := ps.GetLogger()
|
||||
func handlePairingChallenge(hs HandlerServer) http.HandlerFunc {
|
||||
logger := hs.GetLogger()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
s, err := ps.cookieStore.Get(r, sessionChallenge)
|
||||
s, err := hs.GetCookieStore().Get(r, sessionChallenge)
|
||||
if err != nil {
|
||||
logger.Error("ps.cookieStore.Get(r, pairingStoreChallenge)", zap.Error(err))
|
||||
logger.Error("handlePairingChallenge: hs.GetCookieStore().Get(r, sessionChallenge)", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -231,14 +258,16 @@ func handlePairingChallenge(ps *Server) http.HandlerFunc {
|
|||
challenge = make([]byte, 64)
|
||||
_, err = rand.Read(challenge)
|
||||
if err != nil {
|
||||
logger.Error("_, err = rand.Read(auth)", zap.Error(err))
|
||||
logger.Error("handlePairingChallenge: _, err = rand.Read(challenge)", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.Values[sessionChallenge] = challenge
|
||||
err = s.Save(r, w)
|
||||
if err != nil {
|
||||
logger.Error("err = s.Save(r, w)", zap.Error(err))
|
||||
logger.Error("handlePairingChallenge: err = s.Save(r, w)", zap.Error(err))
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +275,7 @@ func handlePairingChallenge(ps *Server) http.HandlerFunc {
|
|||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
_, err = w.Write(challenge)
|
||||
if err != nil {
|
||||
logger.Error("_, err = w.Write(challenge)", zap.Error(err))
|
||||
logger.Error("handlePairingChallenge: _, err = w.Write(challenge)", zap.Error(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"github.com/gorilla/sessions"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// PayloadMounterReceiver represents a struct that can:
|
||||
// - mount payload data from a PayloadRepository or a PayloadLoader into memory (PayloadMounter.Mount)
|
||||
// - prepare data to be sent encrypted (PayloadMounter.ToSend) via some transport
|
||||
// - receive and prepare encrypted transport data (PayloadReceiver.Receive) to be stored
|
||||
// - prepare the received (PayloadReceiver.Received) data to be stored to a PayloadRepository or a PayloadStorer
|
||||
type PayloadMounterReceiver interface {
|
||||
PayloadMounter
|
||||
PayloadReceiver
|
||||
}
|
||||
|
||||
// PayloadRepository represents a struct that can both load and store data to an internally managed data store
|
||||
type PayloadRepository interface {
|
||||
PayloadLoader
|
||||
PayloadStorer
|
||||
}
|
||||
|
||||
type PayloadLocker interface {
|
||||
// LockPayload prevents future excess to outbound safe and received data
|
||||
LockPayload()
|
||||
}
|
||||
|
||||
type HandlerServer interface {
|
||||
GetLogger() *zap.Logger
|
||||
GetCookieStore() *sessions.CookieStore
|
||||
DecryptPlain([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type ProtobufMarshaler interface {
|
||||
MarshalProtobuf() ([]byte, error)
|
||||
}
|
||||
|
||||
type ProtobufUnmarshaler interface {
|
||||
UnmarshalProtobuf([]byte) error
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
)
|
||||
|
||||
// EncryptionPayload represents the plain text and encrypted text of payload data
|
||||
type EncryptionPayload struct {
|
||||
plain []byte
|
||||
encrypted []byte
|
||||
locked bool
|
||||
}
|
||||
|
||||
func (ep *EncryptionPayload) lock() {
|
||||
ep.locked = true
|
||||
}
|
||||
|
||||
// TODO resolve the many cases of other structs simply wrapping their encryptor rather than embedding the functionality
|
||||
// https://github.com/status-im/status-go/issues/3302
|
||||
|
||||
// PayloadEncryptor is responsible for encrypting and decrypting payload data
|
||||
type PayloadEncryptor struct {
|
||||
aesKey []byte
|
||||
payload *EncryptionPayload
|
||||
}
|
||||
|
||||
func NewPayloadEncryptor(aesKey []byte) *PayloadEncryptor {
|
||||
return &PayloadEncryptor{
|
||||
aesKey,
|
||||
new(EncryptionPayload),
|
||||
}
|
||||
}
|
||||
|
||||
// Renew regenerates the whole PayloadEncryptor and returns the new instance, only the aesKey is preserved
|
||||
func (pem *PayloadEncryptor) Renew() *PayloadEncryptor {
|
||||
return &PayloadEncryptor{
|
||||
aesKey: pem.aesKey,
|
||||
payload: new(EncryptionPayload),
|
||||
}
|
||||
}
|
||||
|
||||
// encryptPlain encrypts any given plain text using the internal AES key and returns the encrypted value
|
||||
// This function is different to Encrypt as the internal EncryptionPayload.encrypted value is not set
|
||||
func (pem *PayloadEncryptor) encryptPlain(plaintext []byte) ([]byte, error) {
|
||||
return common.Encrypt(plaintext, pem.aesKey, rand.Reader)
|
||||
}
|
||||
|
||||
// decryptPlain decrypts any given plain text using the internal AES key and returns the encrypted value
|
||||
// This function is different to Decrypt as the internal EncryptionPayload.plain value is not set
|
||||
func (pem *PayloadEncryptor) decryptPlain(plaintext []byte) ([]byte, error) {
|
||||
return common.Decrypt(plaintext, pem.aesKey)
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptor) encrypt(data []byte) error {
|
||||
ep, err := common.Encrypt(data, pem.aesKey, rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pem.payload.plain = data
|
||||
pem.payload.encrypted = ep
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptor) decrypt(data []byte) error {
|
||||
pd, err := common.Decrypt(data, pem.aesKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pem.payload.encrypted = data
|
||||
pem.payload.plain = pd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptor) getEncrypted() []byte {
|
||||
if pem.payload.locked {
|
||||
return nil
|
||||
}
|
||||
return pem.payload.encrypted
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptor) getDecrypted() []byte {
|
||||
if pem.payload.locked {
|
||||
return nil
|
||||
}
|
||||
return pem.payload.plain
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptor) lockPayload() {
|
||||
pem.payload.lock()
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/multiaccounts"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
const keystoreDir = "keystore"
|
||||
|
||||
var (
|
||||
// TODO add validation on config to ensure required fields have valid values
|
||||
// https://github.com/status-im/status-go/issues/3303
|
||||
|
||||
ErrKeyFileAlreadyExists = errors.New("key file already exists")
|
||||
ErrKeyUIDEmptyAsSender = errors.New("keyUID must be provided as sender")
|
||||
ErrNodeConfigNilAsReceiver = errors.New("node config must be provided as receiver")
|
||||
ErrLoggedInKeyUIDConflict = errors.New("logged in keyUID not same as keyUID in payload")
|
||||
)
|
||||
|
||||
// AccountPayload represents the payload structure a Server handles
|
||||
type AccountPayload struct {
|
||||
keys map[string][]byte
|
||||
multiaccount *multiaccounts.Account
|
||||
password string
|
||||
//flag if account already exist before sync account
|
||||
exist bool
|
||||
}
|
||||
|
||||
// AccountPayloadMarshaller is responsible for marshalling and unmarshalling Server payload data
|
||||
type AccountPayloadMarshaller struct {
|
||||
logger *zap.Logger
|
||||
*AccountPayload
|
||||
}
|
||||
|
||||
func NewPairingPayloadMarshaller(ap *AccountPayload, logger *zap.Logger) *AccountPayloadMarshaller {
|
||||
return &AccountPayloadMarshaller{logger: logger, AccountPayload: ap}
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) MarshalProtobuf() ([]byte, error) {
|
||||
return proto.Marshal(&protobuf.LocalPairingPayload{
|
||||
Keys: ppm.accountKeysToProtobuf(),
|
||||
Multiaccount: ppm.multiaccount.ToProtobuf(),
|
||||
Password: ppm.password,
|
||||
})
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) accountKeysToProtobuf() []*protobuf.LocalPairingPayload_Key {
|
||||
var keys []*protobuf.LocalPairingPayload_Key
|
||||
for name, data := range ppm.keys {
|
||||
keys = append(keys, &protobuf.LocalPairingPayload_Key{Name: name, Data: data})
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
|
||||
l := ppm.logger.Named("UnmarshalProtobuf()")
|
||||
l.Debug("fired")
|
||||
|
||||
pb := new(protobuf.LocalPairingPayload)
|
||||
err := proto.Unmarshal(data, pb)
|
||||
l.Debug(
|
||||
"after protobuf.LocalPairingPayload",
|
||||
zap.Any("pb", pb),
|
||||
zap.Any("pb.Multiaccount", pb.Multiaccount),
|
||||
zap.Any("pb.Keys", pb.Keys),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ppm.accountKeysFromProtobuf(pb.Keys)
|
||||
ppm.multiaccountFromProtobuf(pb.Multiaccount)
|
||||
ppm.password = pb.Password
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.LocalPairingPayload_Key) {
|
||||
l := ppm.logger.Named("accountKeysFromProtobuf()")
|
||||
l.Debug("fired")
|
||||
|
||||
if ppm.keys == nil {
|
||||
ppm.keys = make(map[string][]byte)
|
||||
}
|
||||
|
||||
for _, key := range pbKeys {
|
||||
ppm.keys[key.Name] = key.Data
|
||||
}
|
||||
l.Debug(
|
||||
"after for _, key := range pbKeys",
|
||||
zap.Any("pbKeys", pbKeys),
|
||||
zap.Any("accountPayloadMarshaller.keys", ppm.keys),
|
||||
)
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) multiaccountFromProtobuf(pbMultiAccount *protobuf.MultiAccount) {
|
||||
ppm.multiaccount = new(multiaccounts.Account)
|
||||
ppm.multiaccount.FromProtobuf(pbMultiAccount)
|
||||
}
|
||||
|
||||
// InstallationPayloadMounterReceiver represents an InstallationPayload Repository
|
||||
type InstallationPayloadMounterReceiver struct {
|
||||
*InstallationPayloadMounter
|
||||
*InstallationPayloadReceiver
|
||||
}
|
||||
|
||||
func NewInstallationPayloadMounterReceiver(logger *zap.Logger, encryptor *PayloadEncryptor, backend *api.GethStatusBackend, deviceType string) *InstallationPayloadMounterReceiver {
|
||||
l := logger.Named("InstallationPayloadMounterReceiver")
|
||||
return &InstallationPayloadMounterReceiver{
|
||||
NewInstallationPayloadMounter(l, encryptor, backend, deviceType),
|
||||
NewInstallationPayloadReceiver(l, encryptor, backend, deviceType),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadMounterReceiver) LockPayload() {
|
||||
i.InstallationPayloadMounter.LockPayload()
|
||||
i.InstallationPayloadReceiver.LockPayload()
|
||||
}
|
|
@ -44,8 +44,8 @@ type PayloadMarshallerSuite struct {
|
|||
|
||||
teardown func()
|
||||
|
||||
config1 *AccountPayloadManagerConfig
|
||||
config2 *AccountPayloadManagerConfig
|
||||
config1 *SenderConfig
|
||||
config2 *ReceiverConfig
|
||||
}
|
||||
|
||||
func setupTestDB(t *testing.T) (*multiaccounts.Database, func()) {
|
||||
|
@ -68,9 +68,9 @@ func makeKeystores(t *testing.T) (string, string, func()) {
|
|||
emptyKeyStoreDir, err := os.MkdirTemp(os.TempDir(), "accounts_empty")
|
||||
require.NoError(t, err)
|
||||
|
||||
keyStoreDir = filepath.Join(keyStoreDir, "keystore", keyUID)
|
||||
keyStoreDir = filepath.Join(keyStoreDir, keystoreDir, keyUID)
|
||||
// TODO test case where the keystore dir does not yet exist because the device is new
|
||||
emptyKeyStoreDir = filepath.Join(emptyKeyStoreDir, "keystore")
|
||||
emptyKeyStoreDir = filepath.Join(emptyKeyStoreDir, keystoreDir)
|
||||
|
||||
err = os.MkdirAll(keyStoreDir, 0777)
|
||||
require.NoError(t, err)
|
||||
|
@ -132,26 +132,16 @@ func (pms *PayloadMarshallerSuite) SetupTest() {
|
|||
err := db1.SaveAccount(expected)
|
||||
pms.Require().NoError(err)
|
||||
|
||||
pms.config1 = &AccountPayloadManagerConfig{
|
||||
DB: db1,
|
||||
PayloadSourceConfig: &PayloadSourceConfig{
|
||||
KeystorePath: keystore1,
|
||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
||||
KeyUID: keyUID,
|
||||
Password: password,
|
||||
},
|
||||
},
|
||||
pms.config1 = &SenderConfig{
|
||||
DB: db1,
|
||||
KeystorePath: keystore1,
|
||||
KeyUID: keyUID,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
pms.config2 = &AccountPayloadManagerConfig{
|
||||
DB: db2,
|
||||
PayloadSourceConfig: &PayloadSourceConfig{
|
||||
KeystorePath: keystore2,
|
||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
||||
KeyUID: keyUID,
|
||||
Password: password,
|
||||
},
|
||||
},
|
||||
pms.config2 = &ReceiverConfig{
|
||||
DB: db2,
|
||||
KeystorePath: keystore2,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,13 +153,13 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() {
|
|||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
||||
// Make and Load() PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||
pms.Require().NoError(err)
|
||||
err = ppr.LoadFromSource()
|
||||
err = ppr.Load()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// TEST PairingPayloadRepository 1 LoadFromSource()
|
||||
// TEST PairingPayloadRepository 1 Load()
|
||||
pms.Require().Len(ppr.keys, 2)
|
||||
pms.Require().Len(ppr.keys[utils.GetAccount1PKFile()], 489)
|
||||
pms.Require().Len(ppr.keys[utils.GetAccount2PKFile()], 489)
|
||||
|
@ -197,17 +187,17 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() {
|
|||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
||||
// Make and Load() PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||
pms.Require().NoError(err)
|
||||
err = ppr.LoadFromSource()
|
||||
err = ppr.Load()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make and Load PairingPayloadMarshaller 1
|
||||
// Make and Load() PairingPayloadMarshaller 1
|
||||
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
||||
|
||||
// TEST PairingPayloadMarshaller 1 MarshalToProtobuf()
|
||||
pb, err := ppm.MarshalToProtobuf()
|
||||
// TEST PairingPayloadMarshaller 1 MarshalProtobuf()
|
||||
pb, err := ppm.MarshalProtobuf()
|
||||
pms.Require().NoError(err)
|
||||
pms.Require().Len(pb, 1384)
|
||||
|
||||
|
@ -227,16 +217,16 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
|
|||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
||||
// Make and Load() PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||
pms.Require().NoError(err)
|
||||
err = ppr.LoadFromSource()
|
||||
err = ppr.Load()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make and Load PairingPayloadMarshaller 1
|
||||
// Make and Load() PairingPayloadMarshaller 1
|
||||
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
||||
|
||||
pb, err := ppm.MarshalToProtobuf()
|
||||
pb, err := ppm.MarshalProtobuf()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make a Payload
|
||||
|
@ -281,16 +271,16 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
|||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
||||
// Make and Load() PairingPayloadRepository 1
|
||||
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||
pms.Require().NoError(err)
|
||||
err = ppr.LoadFromSource()
|
||||
err = ppr.Load()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make and Load PairingPayloadMarshaller 1
|
||||
// Make and Load() PairingPayloadMarshaller 1
|
||||
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
||||
|
||||
pb, err := ppm.MarshalToProtobuf()
|
||||
pb, err := ppm.MarshalProtobuf()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make a Payload
|
||||
|
@ -302,14 +292,14 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
|||
err = ppm2.UnmarshalProtobuf(pb)
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make and Load PairingPayloadRepository 2
|
||||
ppr2, err := NewAccountPayloadRepository(pp2, pms.config2)
|
||||
// Make and Load() PairingPayloadRepository 2
|
||||
ppr2, err := NewAccountPayloadStorer(pp2, pms.config2)
|
||||
require.NoError(pms.T(), err)
|
||||
err = ppr2.StoreToSource()
|
||||
err = ppr2.Store()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// TEST PairingPayloadRepository 2 StoreToSource()
|
||||
keys := getFiles(pms.T(), filepath.Join(pms.config2.KeystorePath, pms.config2.KeyUID))
|
||||
// TEST PairingPayloadRepository 2 Store()
|
||||
keys := getFiles(pms.T(), filepath.Join(pms.config2.KeystorePath, keyUID))
|
||||
|
||||
pms.Require().Len(keys, 2)
|
||||
pms.Require().Len(keys[utils.GetAccount1PKFile()], 489)
|
||||
|
@ -344,8 +334,7 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LockPayload() {
|
|||
_, err := rand.Read(AESKey)
|
||||
pms.Require().NoError(err)
|
||||
|
||||
pm, err := NewMockEncryptOnlyPayloadManager(AESKey)
|
||||
pms.Require().NoError(err)
|
||||
pm := NewMockPayloadMounter(AESKey)
|
||||
|
||||
err = pm.Mount()
|
||||
pms.Require().NoError(err)
|
|
@ -1,835 +0,0 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/eth-node/keystore"
|
||||
"github.com/status-im/status-go/multiaccounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrKeyFileAlreadyExists = errors.New("key file already exists")
|
||||
ErrKeyUIDEmptyAsSender = errors.New("keyUID must be provided as sender")
|
||||
ErrNodeConfigNilAsReceiver = errors.New("node config must be provided as receiver")
|
||||
ErrPayloadSourceConfigBothSet = errors.New("payloadSourceSenderConfig and payloadSourceReceiverConfig cannot be both set")
|
||||
ErrLoggedInKeyUIDConflict = errors.New("logged in keyUID not same as keyUID in payload")
|
||||
)
|
||||
|
||||
// PayloadManager is the interface for PayloadManagers and wraps the basic functions for fulfilling payload management
|
||||
type PayloadManager interface {
|
||||
// Mount Loads the payload into the PayloadManager's state
|
||||
Mount() error
|
||||
|
||||
// Receive stores data from an inbound source into the PayloadManager's state
|
||||
Receive(data []byte) error
|
||||
|
||||
// ToSend returns an outbound safe (encrypted) payload
|
||||
ToSend() []byte
|
||||
|
||||
// Received returns a decrypted and parsed payload from an inbound source
|
||||
Received() []byte
|
||||
|
||||
// ResetPayload resets all payloads the PayloadManager has in its state
|
||||
ResetPayload()
|
||||
|
||||
// EncryptPlain encrypts the given plaintext using internal key(s)
|
||||
EncryptPlain(plaintext []byte) ([]byte, error)
|
||||
|
||||
// LockPayload prevents future excess to outbound safe and received data
|
||||
LockPayload()
|
||||
}
|
||||
|
||||
type PayloadSourceSenderConfig struct {
|
||||
KeyUID string `json:"keyUID"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type PayloadSourceReceiverConfig struct {
|
||||
KDFIterations int `json:"kdfIterations"`
|
||||
NodeConfig *params.NodeConfig
|
||||
// this field already exists within params.NodeConfig, but it doesn't support json marshalling, so we need to duplicate it here
|
||||
RootDataDir string
|
||||
// corresponding to field current_network from table settings, so that we can override current network from sender
|
||||
SettingCurrentNetwork string
|
||||
}
|
||||
|
||||
// PayloadSourceConfig represents location and access data of the pairing payload
|
||||
// ONLY available from the application client
|
||||
type PayloadSourceConfig struct {
|
||||
// required for sender and receiver, there are some different cases:
|
||||
// 1. for sender, KeystorePath must end with keyUID
|
||||
// 2. for receiver, KeystorePath must not end with keyUID (because keyUID is not known yet)
|
||||
KeystorePath string `json:"keystorePath"`
|
||||
// required for sender and receiver, SendPairInstallation need this information
|
||||
DeviceType string `json:"deviceType"`
|
||||
*PayloadSourceSenderConfig
|
||||
*PayloadSourceReceiverConfig
|
||||
// Timeout the number of milliseconds after which the pairing server will automatically terminate
|
||||
Timeout uint `json:"timeout"`
|
||||
}
|
||||
|
||||
type payloadSourceUnmarshalCallback func(conf *PayloadSourceConfig) (*PayloadSourceConfig, error)
|
||||
|
||||
func NewPayloadSourceForClient(configJSON string, mode Mode) (*PayloadSourceConfig, error) {
|
||||
return unmarshalPayloadSourceConfig(configJSON, func(conf *PayloadSourceConfig) (*PayloadSourceConfig, error) {
|
||||
if mode == Sending && conf.NodeConfig == nil {
|
||||
return nil, ErrNodeConfigNilAsReceiver
|
||||
}
|
||||
if mode == Receiving && conf.KeyUID == "" {
|
||||
return nil, ErrKeyUIDEmptyAsSender
|
||||
}
|
||||
return updateRootDataDirToNodeConfig(conf)
|
||||
})
|
||||
}
|
||||
|
||||
func NewPayloadSourceForServer(configJSON string, mode Mode) (*PayloadSourceConfig, error) {
|
||||
return unmarshalPayloadSourceConfig(configJSON, func(conf *PayloadSourceConfig) (*PayloadSourceConfig, error) {
|
||||
if mode == Sending && conf.KeyUID == "" {
|
||||
return nil, ErrKeyUIDEmptyAsSender
|
||||
}
|
||||
if mode == Receiving && conf.NodeConfig == nil {
|
||||
return nil, ErrNodeConfigNilAsReceiver
|
||||
}
|
||||
return updateRootDataDirToNodeConfig(conf)
|
||||
})
|
||||
}
|
||||
|
||||
func updateRootDataDirToNodeConfig(conf *PayloadSourceConfig) (*PayloadSourceConfig, error) {
|
||||
if conf.PayloadSourceReceiverConfig != nil && conf.PayloadSourceReceiverConfig.NodeConfig != nil {
|
||||
conf.NodeConfig.RootDataDir = conf.RootDataDir
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func unmarshalPayloadSourceConfig(configJSON string, successCallback payloadSourceUnmarshalCallback) (*PayloadSourceConfig, error) {
|
||||
var conf = PayloadSourceConfig{}
|
||||
err := json.Unmarshal([]byte(configJSON), &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return successCallback(&conf)
|
||||
}
|
||||
|
||||
// AccountPayloadManagerConfig represents the initialisation parameters required for a AccountPayloadManager
|
||||
type AccountPayloadManagerConfig struct {
|
||||
DB *multiaccounts.Database
|
||||
*PayloadSourceConfig
|
||||
// only used for the receiver side
|
||||
LoggedInKeyUID string
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetNodeConfig() *params.NodeConfig {
|
||||
if a.PayloadSourceConfig != nil && a.PayloadSourceConfig.PayloadSourceReceiverConfig != nil {
|
||||
return a.NodeConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetSettingCurrentNetwork() string {
|
||||
if a.PayloadSourceConfig != nil && a.PayloadSourceConfig.PayloadSourceReceiverConfig != nil {
|
||||
return a.SettingCurrentNetwork
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetDeviceType() string {
|
||||
if a.PayloadSourceConfig != nil {
|
||||
return a.DeviceType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetPayloadSourceSenderConfig() *PayloadSourceSenderConfig {
|
||||
if a.PayloadSourceConfig != nil && a.PayloadSourceConfig.PayloadSourceSenderConfig != nil {
|
||||
return a.PayloadSourceSenderConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetPayloadSourceReceiverConfig() *PayloadSourceReceiverConfig {
|
||||
if a.PayloadSourceConfig != nil && a.PayloadSourceConfig.PayloadSourceReceiverConfig != nil {
|
||||
return a.PayloadSourceReceiverConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetKeystorePath() string {
|
||||
if a.PayloadSourceConfig != nil {
|
||||
return a.KeystorePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *AccountPayloadManagerConfig) GetTimeout() uint {
|
||||
if a.PayloadSourceConfig != nil {
|
||||
return a.Timeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// AccountPayloadManager is responsible for the whole lifecycle of a AccountPayload
|
||||
type AccountPayloadManager struct {
|
||||
logger *zap.Logger
|
||||
accountPayload *AccountPayload
|
||||
*PayloadEncryptionManager
|
||||
accountPayloadMarshaller *AccountPayloadMarshaller
|
||||
payloadRepository PayloadRepository
|
||||
}
|
||||
|
||||
// NewAccountPayloadManager generates a new and initialised AccountPayloadManager
|
||||
func NewAccountPayloadManager(aesKey []byte, config *AccountPayloadManagerConfig, logger *zap.Logger) (*AccountPayloadManager, error) {
|
||||
l := logger.Named("AccountPayloadManager")
|
||||
l.Debug("fired", zap.Binary("aesKey", aesKey), zap.Any("config", config))
|
||||
|
||||
pem, err := NewPayloadEncryptionManager(aesKey, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// A new SHARED AccountPayload
|
||||
p := new(AccountPayload)
|
||||
accountPayloadRepository, err := NewAccountPayloadRepository(p, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AccountPayloadManager{
|
||||
logger: l,
|
||||
accountPayload: p,
|
||||
PayloadEncryptionManager: pem,
|
||||
accountPayloadMarshaller: NewPairingPayloadMarshaller(p, l),
|
||||
payloadRepository: accountPayloadRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mount loads and prepares the payload to be stored in the AccountPayloadManager's state ready for later access
|
||||
func (apm *AccountPayloadManager) Mount() error {
|
||||
l := apm.logger.Named("Mount()")
|
||||
l.Debug("fired")
|
||||
|
||||
err := apm.payloadRepository.LoadFromSource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug("after LoadFromSource")
|
||||
|
||||
pb, err := apm.accountPayloadMarshaller.MarshalToProtobuf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug(
|
||||
"after MarshalToProtobuf",
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
|
||||
zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
|
||||
zap.Binary("pb", pb),
|
||||
)
|
||||
|
||||
return apm.Encrypt(pb)
|
||||
}
|
||||
|
||||
// Receive takes a []byte representing raw data, parses and stores the data
|
||||
func (apm *AccountPayloadManager) Receive(data []byte) error {
|
||||
l := apm.logger.Named("Receive()")
|
||||
l.Debug("fired")
|
||||
|
||||
err := apm.Decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug("after Decrypt")
|
||||
|
||||
err = apm.accountPayloadMarshaller.UnmarshalProtobuf(apm.Received())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug(
|
||||
"after UnmarshalProtobuf",
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
|
||||
zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
|
||||
zap.Binary("accountPayloadMarshaller.Received()", apm.Received()),
|
||||
)
|
||||
|
||||
signal.SendLocalPairingEvent(Event{Type: EventReceivedAccount, Action: ActionPairingAccount, Data: apm.accountPayload.multiaccount})
|
||||
|
||||
return apm.payloadRepository.StoreToSource()
|
||||
}
|
||||
|
||||
// ResetPayload resets all payload state managed by the AccountPayloadManager
|
||||
func (apm *AccountPayloadManager) ResetPayload() {
|
||||
apm.accountPayload.ResetPayload()
|
||||
apm.PayloadEncryptionManager.ResetPayload()
|
||||
}
|
||||
|
||||
// EncryptionPayload represents the plain text and encrypted text of payload data
|
||||
type EncryptionPayload struct {
|
||||
plain []byte
|
||||
encrypted []byte
|
||||
locked bool
|
||||
}
|
||||
|
||||
func (ep *EncryptionPayload) lock() {
|
||||
ep.locked = true
|
||||
}
|
||||
|
||||
// PayloadEncryptionManager is responsible for encrypting and decrypting payload data
|
||||
type PayloadEncryptionManager struct {
|
||||
logger *zap.Logger
|
||||
aesKey []byte
|
||||
toSend *EncryptionPayload
|
||||
received *EncryptionPayload
|
||||
}
|
||||
|
||||
func NewPayloadEncryptionManager(aesKey []byte, logger *zap.Logger) (*PayloadEncryptionManager, error) {
|
||||
return &PayloadEncryptionManager{logger.Named("PayloadEncryptionManager"), aesKey, new(EncryptionPayload), new(EncryptionPayload)}, nil
|
||||
}
|
||||
|
||||
// EncryptPlain encrypts any given plain text using the internal AES key and returns the encrypted value
|
||||
// This function is different to Encrypt as the internal EncryptionPayload.encrypted value is not set
|
||||
func (pem *PayloadEncryptionManager) EncryptPlain(plaintext []byte) ([]byte, error) {
|
||||
l := pem.logger.Named("EncryptPlain()")
|
||||
l.Debug("fired")
|
||||
|
||||
return common.Encrypt(plaintext, pem.aesKey, rand.Reader)
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptionManager) Encrypt(data []byte) error {
|
||||
l := pem.logger.Named("Encrypt()")
|
||||
l.Debug("fired")
|
||||
|
||||
ep, err := common.Encrypt(data, pem.aesKey, rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pem.toSend.plain = data
|
||||
pem.toSend.encrypted = ep
|
||||
|
||||
l.Debug(
|
||||
"after common.Encrypt",
|
||||
zap.Binary("data", data),
|
||||
zap.Binary("pem.aesKey", pem.aesKey),
|
||||
zap.Binary("ep", ep),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptionManager) Decrypt(data []byte) error {
|
||||
l := pem.logger.Named("Decrypt()")
|
||||
l.Debug("fired")
|
||||
|
||||
pd, err := common.Decrypt(data, pem.aesKey)
|
||||
l.Debug(
|
||||
"after common.Decrypt(data, pem.aesKey)",
|
||||
zap.Binary("data", data),
|
||||
zap.Binary("pem.aesKey", pem.aesKey),
|
||||
zap.Binary("pd", pd),
|
||||
zap.Error(err),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pem.received.encrypted = data
|
||||
pem.received.plain = pd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptionManager) ToSend() []byte {
|
||||
if pem.toSend.locked {
|
||||
return nil
|
||||
}
|
||||
return pem.toSend.encrypted
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptionManager) Received() []byte {
|
||||
if pem.toSend.locked {
|
||||
return nil
|
||||
}
|
||||
return pem.received.plain
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptionManager) ResetPayload() {
|
||||
pem.toSend = new(EncryptionPayload)
|
||||
pem.received = new(EncryptionPayload)
|
||||
}
|
||||
|
||||
func (pem *PayloadEncryptionManager) LockPayload() {
|
||||
l := pem.logger.Named("LockPayload")
|
||||
l.Debug("fired")
|
||||
|
||||
pem.toSend.lock()
|
||||
pem.received.lock()
|
||||
}
|
||||
|
||||
// AccountPayload represents the payload structure a Server handles
|
||||
type AccountPayload struct {
|
||||
keys map[string][]byte
|
||||
multiaccount *multiaccounts.Account
|
||||
password string
|
||||
//flag if account already exist before sync account
|
||||
exist bool
|
||||
}
|
||||
|
||||
func (ap *AccountPayload) ResetPayload() {
|
||||
*ap = AccountPayload{}
|
||||
}
|
||||
|
||||
// AccountPayloadMarshaller is responsible for marshalling and unmarshalling Server payload data
|
||||
type AccountPayloadMarshaller struct {
|
||||
logger *zap.Logger
|
||||
*AccountPayload
|
||||
}
|
||||
|
||||
func NewPairingPayloadMarshaller(ap *AccountPayload, logger *zap.Logger) *AccountPayloadMarshaller {
|
||||
return &AccountPayloadMarshaller{logger: logger, AccountPayload: ap}
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) MarshalToProtobuf() ([]byte, error) {
|
||||
return proto.Marshal(&protobuf.LocalPairingPayload{
|
||||
Keys: ppm.accountKeysToProtobuf(),
|
||||
Multiaccount: ppm.multiaccount.ToProtobuf(),
|
||||
Password: ppm.password,
|
||||
})
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) accountKeysToProtobuf() []*protobuf.LocalPairingPayload_Key {
|
||||
var keys []*protobuf.LocalPairingPayload_Key
|
||||
for name, data := range ppm.keys {
|
||||
keys = append(keys, &protobuf.LocalPairingPayload_Key{Name: name, Data: data})
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
|
||||
l := ppm.logger.Named("UnmarshalProtobuf()")
|
||||
l.Debug("fired")
|
||||
|
||||
pb := new(protobuf.LocalPairingPayload)
|
||||
err := proto.Unmarshal(data, pb)
|
||||
l.Debug(
|
||||
"after protobuf.LocalPairingPayload",
|
||||
zap.Any("pb", pb),
|
||||
zap.Any("pb.Multiaccount", pb.Multiaccount),
|
||||
zap.Any("pb.Keys", pb.Keys),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ppm.accountKeysFromProtobuf(pb.Keys)
|
||||
ppm.multiaccountFromProtobuf(pb.Multiaccount)
|
||||
ppm.password = pb.Password
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.LocalPairingPayload_Key) {
|
||||
l := ppm.logger.Named("accountKeysFromProtobuf()")
|
||||
l.Debug("fired")
|
||||
|
||||
if ppm.keys == nil {
|
||||
ppm.keys = make(map[string][]byte)
|
||||
}
|
||||
|
||||
for _, key := range pbKeys {
|
||||
ppm.keys[key.Name] = key.Data
|
||||
}
|
||||
l.Debug(
|
||||
"after for _, key := range pbKeys",
|
||||
zap.Any("pbKeys", pbKeys),
|
||||
zap.Any("accountPayloadMarshaller.keys", ppm.keys),
|
||||
)
|
||||
}
|
||||
|
||||
func (ppm *AccountPayloadMarshaller) multiaccountFromProtobuf(pbMultiAccount *protobuf.MultiAccount) {
|
||||
ppm.multiaccount = new(multiaccounts.Account)
|
||||
ppm.multiaccount.FromProtobuf(pbMultiAccount)
|
||||
}
|
||||
|
||||
type PayloadRepository interface {
|
||||
LoadFromSource() error
|
||||
StoreToSource() error
|
||||
}
|
||||
|
||||
// AccountPayloadRepository is responsible for loading, parsing, validating and storing Server payload data
|
||||
type AccountPayloadRepository struct {
|
||||
*AccountPayload
|
||||
|
||||
multiaccountsDB *multiaccounts.Database
|
||||
|
||||
keystorePath, keyUID string
|
||||
|
||||
kdfIterations int
|
||||
|
||||
loggedInKeyUID string
|
||||
}
|
||||
|
||||
func NewAccountPayloadRepository(p *AccountPayload, config *AccountPayloadManagerConfig) (*AccountPayloadRepository, error) {
|
||||
ppr := &AccountPayloadRepository{
|
||||
AccountPayload: p,
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return ppr, nil
|
||||
}
|
||||
|
||||
ppr.multiaccountsDB = config.DB
|
||||
|
||||
if config.GetPayloadSourceSenderConfig() != nil && config.GetPayloadSourceReceiverConfig() != nil {
|
||||
return nil, ErrPayloadSourceConfigBothSet
|
||||
}
|
||||
if config.GetPayloadSourceSenderConfig() != nil {
|
||||
ppr.keyUID = config.KeyUID
|
||||
ppr.password = config.Password
|
||||
} else if config.GetPayloadSourceReceiverConfig() != nil {
|
||||
ppr.kdfIterations = config.KDFIterations
|
||||
}
|
||||
ppr.keystorePath = config.GetKeystorePath()
|
||||
ppr.loggedInKeyUID = config.LoggedInKeyUID
|
||||
return ppr, nil
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadRepository) LoadFromSource() error {
|
||||
err := apr.loadKeys(apr.keystorePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = apr.validateKeys(apr.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apr.multiaccount, err = apr.multiaccountsDB.GetAccount(apr.keyUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadRepository) loadKeys(keyStorePath string) error {
|
||||
apr.keys = make(map[string][]byte)
|
||||
|
||||
fileWalker := func(path string, fileInfo os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fileInfo.IsDir() || filepath.Dir(path) != keyStorePath {
|
||||
return nil
|
||||
}
|
||||
|
||||
rawKeyFile, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid account key file: %v", err)
|
||||
}
|
||||
|
||||
accountKey := new(keystore.EncryptedKeyJSONV3)
|
||||
if err := json.Unmarshal(rawKeyFile, &accountKey); err != nil {
|
||||
return fmt.Errorf("failed to read key file: %s", err)
|
||||
}
|
||||
|
||||
if len(accountKey.Address) != 40 {
|
||||
return fmt.Errorf("account key address has invalid length '%s'", accountKey.Address)
|
||||
}
|
||||
|
||||
apr.keys[fileInfo.Name()] = rawKeyFile
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := filepath.Walk(keyStorePath, fileWalker)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot traverse key store folder: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadRepository) StoreToSource() error {
|
||||
keyUID := apr.multiaccount.KeyUID
|
||||
if apr.loggedInKeyUID != "" && apr.loggedInKeyUID != keyUID {
|
||||
return ErrLoggedInKeyUIDConflict
|
||||
}
|
||||
if apr.loggedInKeyUID == keyUID {
|
||||
// skip storing keys if user is logged in with the same key
|
||||
return nil
|
||||
}
|
||||
|
||||
err := apr.validateKeys(apr.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = apr.storeKeys(apr.keystorePath); err != nil && err != ErrKeyFileAlreadyExists {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip storing multiaccount if key already exists
|
||||
if err == ErrKeyFileAlreadyExists {
|
||||
apr.exist = true
|
||||
apr.multiaccount, err = apr.multiaccountsDB.GetAccount(keyUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = apr.storeMultiAccount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO install PublicKey into settings, probably do this outside of StoreToSource
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadRepository) validateKeys(password string) error {
|
||||
for _, key := range apr.keys {
|
||||
k, err := keystore.DecryptKey(key, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = generator.ValidateKeystoreExtendedKey(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadRepository) storeKeys(keyStorePath string) error {
|
||||
if keyStorePath == "" {
|
||||
return fmt.Errorf("keyStorePath can not be empty")
|
||||
}
|
||||
|
||||
_, lastDir := filepath.Split(keyStorePath)
|
||||
|
||||
// If lastDir == "keystore" we presume we need to create the rest of the keystore path
|
||||
// else we presume the provided keystore is valid
|
||||
if lastDir == "keystore" {
|
||||
if apr.multiaccount == nil || apr.multiaccount.KeyUID == "" {
|
||||
return fmt.Errorf("no known Key UID")
|
||||
}
|
||||
keyStorePath = filepath.Join(keyStorePath, apr.multiaccount.KeyUID)
|
||||
_, err := os.Stat(keyStorePath)
|
||||
if os.IsNotExist(err) {
|
||||
err := os.MkdirAll(keyStorePath, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
return ErrKeyFileAlreadyExists
|
||||
}
|
||||
}
|
||||
|
||||
for name, data := range apr.keys {
|
||||
accountKey := new(keystore.EncryptedKeyJSONV3)
|
||||
if err := json.Unmarshal(data, &accountKey); err != nil {
|
||||
return fmt.Errorf("failed to read key file: %s", err)
|
||||
}
|
||||
|
||||
if len(accountKey.Address) != 40 {
|
||||
return fmt.Errorf("account key address has invalid length '%s'", accountKey.Address)
|
||||
}
|
||||
|
||||
err := ioutil.WriteFile(filepath.Join(keyStorePath, name), data, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadRepository) storeMultiAccount() error {
|
||||
apr.multiaccount.KDFIterations = apr.kdfIterations
|
||||
return apr.multiaccountsDB.SaveAccount(*apr.multiaccount)
|
||||
}
|
||||
|
||||
type RawMessagePayloadManager struct {
|
||||
logger *zap.Logger
|
||||
// reference from AccountPayloadManager#accountPayload
|
||||
accountPayload *AccountPayload
|
||||
*PayloadEncryptionManager
|
||||
payloadRepository *RawMessageRepository
|
||||
}
|
||||
|
||||
func NewRawMessagePayloadManager(logger *zap.Logger, accountPayload *AccountPayload, aesKey []byte, backend *api.GethStatusBackend, nodeConfig *params.NodeConfig, settingCurrentNetwork, deviceType string) (*RawMessagePayloadManager, error) {
|
||||
l := logger.Named("RawMessagePayloadManager")
|
||||
pem, err := NewPayloadEncryptionManager(aesKey, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RawMessagePayloadManager{
|
||||
logger: l,
|
||||
accountPayload: accountPayload,
|
||||
PayloadEncryptionManager: pem,
|
||||
payloadRepository: NewRawMessageRepository(backend, accountPayload, nodeConfig, settingCurrentNetwork, deviceType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadManager) Mount() error {
|
||||
err := r.payloadRepository.LoadFromSource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Encrypt(r.payloadRepository.payload)
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadManager) Receive(data []byte) error {
|
||||
err := r.Decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.payloadRepository.payload = r.Received()
|
||||
return r.payloadRepository.StoreToSource()
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadManager) ResetPayload() {
|
||||
r.payloadRepository.payload = make([]byte, 0)
|
||||
r.PayloadEncryptionManager.ResetPayload()
|
||||
}
|
||||
|
||||
type RawMessageRepository struct {
|
||||
payload []byte
|
||||
syncRawMessageHandler *SyncRawMessageHandler
|
||||
accountPayload *AccountPayload
|
||||
nodeConfig *params.NodeConfig
|
||||
settingCurrentNetwork string
|
||||
deviceType string
|
||||
}
|
||||
|
||||
func NewRawMessageRepository(backend *api.GethStatusBackend, accountPayload *AccountPayload, config *params.NodeConfig, settingCurrentNetwork, deviceType string) *RawMessageRepository {
|
||||
return &RawMessageRepository{
|
||||
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
|
||||
payload: make([]byte, 0),
|
||||
accountPayload: accountPayload,
|
||||
nodeConfig: config,
|
||||
settingCurrentNetwork: settingCurrentNetwork,
|
||||
deviceType: deviceType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RawMessageRepository) LoadFromSource() error {
|
||||
account := r.accountPayload.multiaccount
|
||||
if account == nil || account.KeyUID == "" {
|
||||
return fmt.Errorf("no known KeyUID when loading raw messages")
|
||||
}
|
||||
payload, err := r.syncRawMessageHandler.PrepareRawMessage(account.KeyUID, r.deviceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.payload = payload
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RawMessageRepository) StoreToSource() error {
|
||||
accountPayload := r.accountPayload
|
||||
if accountPayload == nil || accountPayload.multiaccount == nil {
|
||||
return fmt.Errorf("no known multiaccount when storing raw messages")
|
||||
}
|
||||
return r.syncRawMessageHandler.HandleRawMessage(accountPayload, r.nodeConfig, r.settingCurrentNetwork, r.deviceType, r.payload)
|
||||
}
|
||||
|
||||
type InstallationPayloadManager struct {
|
||||
logger *zap.Logger
|
||||
*PayloadEncryptionManager
|
||||
installationPayloadRepository *InstallationPayloadRepository
|
||||
}
|
||||
|
||||
func NewInstallationPayloadManager(logger *zap.Logger, aesKey []byte, backend *api.GethStatusBackend, deviceType string) (*InstallationPayloadManager, error) {
|
||||
l := logger.Named("InstallationPayloadManager")
|
||||
pem, err := NewPayloadEncryptionManager(aesKey, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &InstallationPayloadManager{
|
||||
logger: l,
|
||||
PayloadEncryptionManager: pem,
|
||||
installationPayloadRepository: NewInstallationPayloadRepository(backend, deviceType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadManager) Mount() error {
|
||||
err := i.installationPayloadRepository.LoadFromSource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.Encrypt(i.installationPayloadRepository.payload)
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadManager) Receive(data []byte) error {
|
||||
err := i.Decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.installationPayloadRepository.payload = i.Received()
|
||||
return i.installationPayloadRepository.StoreToSource()
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadManager) ResetPayload() {
|
||||
i.installationPayloadRepository.payload = make([]byte, 0)
|
||||
i.PayloadEncryptionManager.ResetPayload()
|
||||
}
|
||||
|
||||
type InstallationPayloadRepository struct {
|
||||
payload []byte
|
||||
syncRawMessageHandler *SyncRawMessageHandler
|
||||
deviceType string
|
||||
backend *api.GethStatusBackend
|
||||
}
|
||||
|
||||
func NewInstallationPayloadRepository(backend *api.GethStatusBackend, deviceType string) *InstallationPayloadRepository {
|
||||
return &InstallationPayloadRepository{
|
||||
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
|
||||
deviceType: deviceType,
|
||||
backend: backend,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InstallationPayloadRepository) LoadFromSource() error {
|
||||
rawMessageCollector := new(RawMessageCollector)
|
||||
err := r.syncRawMessageHandler.CollectInstallationData(rawMessageCollector, r.deviceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.payload, err = proto.Marshal(rawMessageCollector.convertToSyncRawMessage())
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *InstallationPayloadRepository) StoreToSource() error {
|
||||
messenger := r.backend.Messenger()
|
||||
if messenger == nil {
|
||||
return fmt.Errorf("messenger is nil when invoke InstallationPayloadRepository#StoreToSource")
|
||||
}
|
||||
rawMessages, _, _, err := r.syncRawMessageHandler.unmarshalSyncRawMessage(r.payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = messenger.SetInstallationDeviceType(r.deviceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return messenger.HandleSyncRawMessages(rawMessages)
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/multiaccounts"
|
||||
)
|
||||
|
||||
type PayloadMounter interface {
|
||||
PayloadLocker
|
||||
|
||||
// Mount Loads the payload into the PayloadManager's state
|
||||
Mount() error
|
||||
|
||||
// ToSend returns an outbound safe (encrypted) payload
|
||||
ToSend() []byte
|
||||
}
|
||||
|
||||
type PayloadLoader interface {
|
||||
Load() error
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| AccountPayload
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| AccountPayloadMounter, AccountPayloadLoader and AccountPayloadMarshaller
|
||||
|
|
||||
*/
|
||||
|
||||
// AccountPayloadMounter is responsible for the whole lifecycle of an AccountPayload
|
||||
type AccountPayloadMounter struct {
|
||||
logger *zap.Logger
|
||||
accountPayload *AccountPayload
|
||||
encryptor *PayloadEncryptor
|
||||
accountPayloadMarshaller *AccountPayloadMarshaller
|
||||
payloadLoader PayloadLoader
|
||||
}
|
||||
|
||||
// NewAccountPayloadMounter generates a new and initialised AccountPayloadMounter
|
||||
func NewAccountPayloadMounter(pe *PayloadEncryptor, config *SenderConfig, logger *zap.Logger) (*AccountPayloadMounter, error) {
|
||||
l := logger.Named("AccountPayloadLoader")
|
||||
l.Debug("fired", zap.Any("config", config))
|
||||
|
||||
// A new SHARED AccountPayload
|
||||
p := new(AccountPayload)
|
||||
apl, err := NewAccountPayloadLoader(p, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AccountPayloadMounter{
|
||||
logger: l,
|
||||
accountPayload: p,
|
||||
encryptor: pe.Renew(),
|
||||
accountPayloadMarshaller: NewPairingPayloadMarshaller(p, l),
|
||||
payloadLoader: apl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mount loads and prepares the payload to be stored in the AccountPayloadLoader's state ready for later access
|
||||
func (apm *AccountPayloadMounter) Mount() error {
|
||||
l := apm.logger.Named("Mount()")
|
||||
l.Debug("fired")
|
||||
|
||||
err := apm.payloadLoader.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug("after Load()")
|
||||
|
||||
pb, err := apm.accountPayloadMarshaller.MarshalProtobuf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug(
|
||||
"after MarshalProtobuf",
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
|
||||
zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
|
||||
zap.Binary("pb", pb),
|
||||
)
|
||||
|
||||
return apm.encryptor.encrypt(pb)
|
||||
}
|
||||
|
||||
func (apm *AccountPayloadMounter) ToSend() []byte {
|
||||
return apm.encryptor.getEncrypted()
|
||||
}
|
||||
|
||||
func (apm *AccountPayloadMounter) LockPayload() {
|
||||
apm.encryptor.lockPayload()
|
||||
}
|
||||
|
||||
// AccountPayloadLoader is responsible for loading, parsing and validating AccountPayload data
|
||||
type AccountPayloadLoader struct {
|
||||
*AccountPayload
|
||||
|
||||
multiaccountsDB *multiaccounts.Database
|
||||
keystorePath string
|
||||
keyUID string
|
||||
}
|
||||
|
||||
func NewAccountPayloadLoader(p *AccountPayload, config *SenderConfig) (*AccountPayloadLoader, error) {
|
||||
ppr := &AccountPayloadLoader{
|
||||
AccountPayload: p,
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return ppr, nil
|
||||
}
|
||||
|
||||
ppr.multiaccountsDB = config.DB
|
||||
ppr.keyUID = config.KeyUID
|
||||
ppr.password = config.Password
|
||||
ppr.keystorePath = config.KeystorePath
|
||||
return ppr, nil
|
||||
}
|
||||
|
||||
func (apl *AccountPayloadLoader) Load() error {
|
||||
err := apl.loadKeys(apl.keystorePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validateKeys(apl.keys, apl.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apl.multiaccount, err = apl.multiaccountsDB.GetAccount(apl.keyUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (apl *AccountPayloadLoader) loadKeys(keyStorePath string) error {
|
||||
apl.keys = make(map[string][]byte)
|
||||
|
||||
fileWalker := func(path string, dirEntry fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dirEntry.IsDir() || filepath.Dir(path) != keyStorePath {
|
||||
return nil
|
||||
}
|
||||
|
||||
rawKeyFile, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid account key file: %v", err)
|
||||
}
|
||||
|
||||
apl.keys[dirEntry.Name()] = rawKeyFile
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := filepath.WalkDir(keyStorePath, fileWalker)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot traverse key store folder: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| RawMessagePayload
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| RawMessagePayloadMounter and RawMessageLoader
|
||||
|
|
||||
*/
|
||||
|
||||
type RawMessagePayloadMounter struct {
|
||||
logger *zap.Logger
|
||||
|
||||
encryptor *PayloadEncryptor
|
||||
loader *RawMessageLoader
|
||||
}
|
||||
|
||||
func NewRawMessagePayloadMounter(logger *zap.Logger, pe *PayloadEncryptor, backend *api.GethStatusBackend, config *SenderConfig) *RawMessagePayloadMounter {
|
||||
l := logger.Named("RawMessagePayloadManager")
|
||||
return &RawMessagePayloadMounter{
|
||||
logger: l,
|
||||
encryptor: pe.Renew(),
|
||||
loader: NewRawMessageLoader(backend, config),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadMounter) Mount() error {
|
||||
err := r.loader.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.encryptor.encrypt(r.loader.payload)
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadMounter) ToSend() []byte {
|
||||
return r.encryptor.getEncrypted()
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadMounter) LockPayload() {
|
||||
r.encryptor.lockPayload()
|
||||
}
|
||||
|
||||
type RawMessageLoader struct {
|
||||
payload []byte
|
||||
syncRawMessageHandler *SyncRawMessageHandler
|
||||
keyUID string
|
||||
deviceType string
|
||||
}
|
||||
|
||||
func NewRawMessageLoader(backend *api.GethStatusBackend, config *SenderConfig) *RawMessageLoader {
|
||||
return &RawMessageLoader{
|
||||
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
|
||||
payload: make([]byte, 0),
|
||||
keyUID: config.KeyUID,
|
||||
deviceType: config.DeviceType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RawMessageLoader) Load() (err error) {
|
||||
r.payload, err = r.syncRawMessageHandler.PrepareRawMessage(r.keyUID, r.deviceType)
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| InstallationPayload
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| InstallationPayloadMounter and InstallationPayloadLoader
|
||||
|
|
||||
*/
|
||||
|
||||
type InstallationPayloadMounter struct {
|
||||
logger *zap.Logger
|
||||
encryptor *PayloadEncryptor
|
||||
loader *InstallationPayloadLoader
|
||||
}
|
||||
|
||||
func NewInstallationPayloadMounter(logger *zap.Logger, pe *PayloadEncryptor, backend *api.GethStatusBackend, deviceType string) *InstallationPayloadMounter {
|
||||
return &InstallationPayloadMounter{
|
||||
logger: logger.Named("InstallationPayloadManager"),
|
||||
encryptor: pe.Renew(),
|
||||
loader: NewInstallationPayloadLoader(backend, deviceType),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadMounter) Mount() error {
|
||||
err := i.loader.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.encryptor.encrypt(i.loader.payload)
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadMounter) ToSend() []byte {
|
||||
return i.encryptor.getEncrypted()
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadMounter) LockPayload() {
|
||||
i.encryptor.lockPayload()
|
||||
}
|
||||
|
||||
type InstallationPayloadLoader struct {
|
||||
payload []byte
|
||||
syncRawMessageHandler *SyncRawMessageHandler
|
||||
deviceType string
|
||||
}
|
||||
|
||||
func NewInstallationPayloadLoader(backend *api.GethStatusBackend, deviceType string) *InstallationPayloadLoader {
|
||||
return &InstallationPayloadLoader{
|
||||
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
|
||||
deviceType: deviceType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InstallationPayloadLoader) Load() error {
|
||||
rawMessageCollector := new(RawMessageCollector)
|
||||
err := r.syncRawMessageHandler.CollectInstallationData(rawMessageCollector, r.deviceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.payload, err = proto.Marshal(rawMessageCollector.convertToSyncRawMessage())
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PayloadMounters
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Funcs for all PayloadMounters AccountPayloadMounter, RawMessagePayloadMounter and InstallationPayloadMounter
|
||||
|
|
||||
*/
|
||||
|
||||
func NewPayloadMounters(logger *zap.Logger, pe *PayloadEncryptor, backend *api.GethStatusBackend, config *SenderConfig) (*AccountPayloadMounter, *RawMessagePayloadMounter, *InstallationPayloadMounterReceiver, error) {
|
||||
am, err := NewAccountPayloadMounter(pe, config, logger)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
rmm := NewRawMessagePayloadMounter(logger, pe, backend, config)
|
||||
imr := NewInstallationPayloadMounterReceiver(logger, pe, backend, config.DeviceType)
|
||||
return am, rmm, imr, nil
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
package pairing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/multiaccounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
type PayloadReceiver interface {
|
||||
PayloadLocker
|
||||
|
||||
// Receive accepts data from an inbound source into the PayloadReceiver's state
|
||||
Receive(data []byte) error
|
||||
|
||||
// Received returns a decrypted and parsed payload from an inbound source
|
||||
Received() []byte
|
||||
}
|
||||
|
||||
type PayloadStorer interface {
|
||||
Store() error
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| AccountPayload
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| AccountPayloadReceiver, AccountPayloadStorer and AccountPayloadMarshaller
|
||||
|
|
||||
*/
|
||||
|
||||
// AccountPayloadReceiver is responsible for the whole lifecycle of a AccountPayload
|
||||
type AccountPayloadReceiver struct {
|
||||
logger *zap.Logger
|
||||
accountPayload *AccountPayload
|
||||
encryptor *PayloadEncryptor
|
||||
accountPayloadMarshaller *AccountPayloadMarshaller
|
||||
accountStorer *AccountPayloadStorer
|
||||
}
|
||||
|
||||
// NewAccountPayloadReceiver generates a new and initialised AccountPayloadManager
|
||||
func NewAccountPayloadReceiver(encryptor *PayloadEncryptor, config *ReceiverConfig, logger *zap.Logger) (*AccountPayloadReceiver, error) {
|
||||
l := logger.Named("AccountPayloadManager")
|
||||
l.Debug("fired", zap.Any("config", config))
|
||||
|
||||
// A new SHARED AccountPayload
|
||||
p := new(AccountPayload)
|
||||
accountPayloadRepository, err := NewAccountPayloadStorer(p, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AccountPayloadReceiver{
|
||||
logger: l,
|
||||
accountPayload: p,
|
||||
encryptor: encryptor.Renew(),
|
||||
accountPayloadMarshaller: NewPairingPayloadMarshaller(p, l),
|
||||
accountStorer: accountPayloadRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Receive takes a []byte representing raw data, parses and stores the data
|
||||
func (apr *AccountPayloadReceiver) Receive(data []byte) error {
|
||||
l := apr.logger.Named("Receive()")
|
||||
l.Debug("fired")
|
||||
|
||||
err := apr.encryptor.decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug("after Decrypt")
|
||||
|
||||
err = apr.accountPayloadMarshaller.UnmarshalProtobuf(apr.Received())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug(
|
||||
"after UnmarshalProtobuf",
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apr.accountPayloadMarshaller.keys),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apr.accountPayloadMarshaller.multiaccount),
|
||||
zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apr.accountPayloadMarshaller.password),
|
||||
zap.Binary("accountPayloadMarshaller.Received()", apr.Received()),
|
||||
)
|
||||
|
||||
signal.SendLocalPairingEvent(Event{Type: EventReceivedAccount, Action: ActionPairingAccount, Data: apr.accountPayload.multiaccount})
|
||||
|
||||
return apr.accountStorer.Store()
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadReceiver) Received() []byte {
|
||||
return apr.encryptor.getDecrypted()
|
||||
}
|
||||
|
||||
func (apr *AccountPayloadReceiver) LockPayload() {
|
||||
apr.encryptor.lockPayload()
|
||||
}
|
||||
|
||||
// AccountPayloadStorer is responsible for parsing, validating and storing AccountPayload data
|
||||
type AccountPayloadStorer struct {
|
||||
*AccountPayload
|
||||
multiaccountsDB *multiaccounts.Database
|
||||
|
||||
keystorePath string
|
||||
kdfIterations int
|
||||
loggedInKeyUID string
|
||||
}
|
||||
|
||||
func NewAccountPayloadStorer(p *AccountPayload, config *ReceiverConfig) (*AccountPayloadStorer, error) {
|
||||
ppr := &AccountPayloadStorer{
|
||||
AccountPayload: p,
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return ppr, nil
|
||||
}
|
||||
|
||||
ppr.multiaccountsDB = config.DB
|
||||
ppr.kdfIterations = config.KDFIterations
|
||||
ppr.keystorePath = config.KeystorePath
|
||||
ppr.loggedInKeyUID = config.LoggedInKeyUID
|
||||
return ppr, nil
|
||||
}
|
||||
|
||||
func (aps *AccountPayloadStorer) Store() error {
|
||||
keyUID := aps.multiaccount.KeyUID
|
||||
if aps.loggedInKeyUID != "" && aps.loggedInKeyUID != keyUID {
|
||||
return ErrLoggedInKeyUIDConflict
|
||||
}
|
||||
if aps.loggedInKeyUID == keyUID {
|
||||
// skip storing keys if user is logged in with the same key
|
||||
return nil
|
||||
}
|
||||
|
||||
err := validateKeys(aps.keys, aps.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = aps.storeKeys(aps.keystorePath); err != nil && err != ErrKeyFileAlreadyExists {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip storing multiaccount if key already exists
|
||||
if err == ErrKeyFileAlreadyExists {
|
||||
aps.exist = true
|
||||
aps.multiaccount, err = aps.multiaccountsDB.GetAccount(keyUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return aps.storeMultiAccount()
|
||||
}
|
||||
|
||||
func (aps *AccountPayloadStorer) storeKeys(keyStorePath string) error {
|
||||
if keyStorePath == "" {
|
||||
return fmt.Errorf("keyStorePath can not be empty")
|
||||
}
|
||||
|
||||
_, lastDir := filepath.Split(keyStorePath)
|
||||
|
||||
// If lastDir == keystoreDir we presume we need to create the rest of the keystore path
|
||||
// else we presume the provided keystore is valid
|
||||
if lastDir == keystoreDir {
|
||||
if aps.multiaccount == nil || aps.multiaccount.KeyUID == "" {
|
||||
return fmt.Errorf("no known Key UID")
|
||||
}
|
||||
keyStorePath = filepath.Join(keyStorePath, aps.multiaccount.KeyUID)
|
||||
_, err := os.Stat(keyStorePath)
|
||||
if os.IsNotExist(err) {
|
||||
err := os.MkdirAll(keyStorePath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
return ErrKeyFileAlreadyExists
|
||||
}
|
||||
}
|
||||
|
||||
for name, data := range aps.keys {
|
||||
err := ioutil.WriteFile(filepath.Join(keyStorePath, name), data, 0600)
|
||||
if err != nil {
|
||||
writeErr := fmt.Errorf("failed to write key to path '%s' : %w", filepath.Join(keyStorePath, name), err)
|
||||
// If we get an error on any of the key files attempt to revert
|
||||
err := emptyDir(keyStorePath)
|
||||
if err != nil {
|
||||
// If we get an error when trying to empty the dir combine the write error and empty error
|
||||
emptyDirErr := fmt.Errorf("failed to revert and cleanup storeKeys : %w", err)
|
||||
return multierr.Combine(writeErr, emptyDirErr)
|
||||
}
|
||||
return writeErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (aps *AccountPayloadStorer) storeMultiAccount() error {
|
||||
aps.multiaccount.KDFIterations = aps.kdfIterations
|
||||
return aps.multiaccountsDB.SaveAccount(*aps.multiaccount)
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| RawMessagePayload
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| RawMessagePayloadReceiver and RawMessageStorer
|
||||
|
|
||||
*/
|
||||
|
||||
type RawMessagePayloadReceiver struct {
|
||||
logger *zap.Logger
|
||||
encryptor *PayloadEncryptor
|
||||
storer *RawMessageStorer
|
||||
}
|
||||
|
||||
func NewRawMessagePayloadReceiver(logger *zap.Logger, accountPayload *AccountPayload, encryptor *PayloadEncryptor, backend *api.GethStatusBackend, config *ReceiverConfig) *RawMessagePayloadReceiver {
|
||||
l := logger.Named("RawMessagePayloadManager")
|
||||
return &RawMessagePayloadReceiver{
|
||||
logger: l,
|
||||
encryptor: encryptor.Renew(),
|
||||
storer: NewRawMessageStorer(backend, accountPayload, config),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadReceiver) Receive(data []byte) error {
|
||||
err := r.encryptor.decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.storer.payload = r.Received()
|
||||
return r.storer.Store()
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadReceiver) Received() []byte {
|
||||
return r.encryptor.getDecrypted()
|
||||
}
|
||||
|
||||
func (r *RawMessagePayloadReceiver) LockPayload() {
|
||||
r.encryptor.lockPayload()
|
||||
}
|
||||
|
||||
type RawMessageStorer struct {
|
||||
payload []byte
|
||||
syncRawMessageHandler *SyncRawMessageHandler
|
||||
accountPayload *AccountPayload
|
||||
nodeConfig *params.NodeConfig
|
||||
settingCurrentNetwork string
|
||||
deviceType string
|
||||
}
|
||||
|
||||
func NewRawMessageStorer(backend *api.GethStatusBackend, accountPayload *AccountPayload, config *ReceiverConfig) *RawMessageStorer {
|
||||
return &RawMessageStorer{
|
||||
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
|
||||
payload: make([]byte, 0),
|
||||
accountPayload: accountPayload,
|
||||
nodeConfig: config.NodeConfig,
|
||||
settingCurrentNetwork: config.SettingCurrentNetwork,
|
||||
deviceType: config.DeviceType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RawMessageStorer) Store() error {
|
||||
accountPayload := r.accountPayload
|
||||
if accountPayload == nil || accountPayload.multiaccount == nil {
|
||||
return fmt.Errorf("no known multiaccount when storing raw messages")
|
||||
}
|
||||
return r.syncRawMessageHandler.HandleRawMessage(accountPayload, r.nodeConfig, r.settingCurrentNetwork, r.deviceType, r.payload)
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| InstallationPayload
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| InstallationPayloadReceiver and InstallationPayloadStorer
|
||||
|
|
||||
*/
|
||||
|
||||
type InstallationPayloadReceiver struct {
|
||||
logger *zap.Logger
|
||||
encryptor *PayloadEncryptor
|
||||
storer *InstallationPayloadStorer
|
||||
}
|
||||
|
||||
func NewInstallationPayloadReceiver(logger *zap.Logger, encryptor *PayloadEncryptor, backend *api.GethStatusBackend, deviceType string) *InstallationPayloadReceiver {
|
||||
l := logger.Named("InstallationPayloadManager")
|
||||
return &InstallationPayloadReceiver{
|
||||
logger: l,
|
||||
encryptor: encryptor.Renew(),
|
||||
storer: NewInstallationPayloadStorer(backend, deviceType),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadReceiver) Receive(data []byte) error {
|
||||
err := i.encryptor.decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.storer.payload = i.encryptor.getDecrypted()
|
||||
return i.storer.Store()
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadReceiver) Received() []byte {
|
||||
return i.encryptor.getDecrypted()
|
||||
}
|
||||
|
||||
func (i *InstallationPayloadReceiver) LockPayload() {
|
||||
i.encryptor.lockPayload()
|
||||
}
|
||||
|
||||
type InstallationPayloadStorer struct {
|
||||
payload []byte
|
||||
syncRawMessageHandler *SyncRawMessageHandler
|
||||
deviceType string
|
||||
backend *api.GethStatusBackend
|
||||
}
|
||||
|
||||
func NewInstallationPayloadStorer(backend *api.GethStatusBackend, deviceType string) *InstallationPayloadStorer {
|
||||
return &InstallationPayloadStorer{
|
||||
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
|
||||
deviceType: deviceType,
|
||||
backend: backend,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InstallationPayloadStorer) Store() error {
|
||||
messenger := r.backend.Messenger()
|
||||
if messenger == nil {
|
||||
return fmt.Errorf("messenger is nil when invoke InstallationPayloadRepository#Store()")
|
||||
}
|
||||
rawMessages, _, _, err := r.syncRawMessageHandler.unmarshalSyncRawMessage(r.payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = messenger.SetInstallationDeviceType(r.deviceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return messenger.HandleSyncRawMessages(rawMessages)
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PayloadReceivers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Funcs for all PayloadReceivers AccountPayloadReceiver, RawMessagePayloadReceiver and InstallationPayloadMounter
|
||||
|
|
||||
*/
|
||||
|
||||
func NewPayloadReceivers(logger *zap.Logger, pe *PayloadEncryptor, backend *api.GethStatusBackend, config *ReceiverConfig) (*AccountPayloadReceiver, *RawMessagePayloadReceiver, *InstallationPayloadMounterReceiver, error) {
|
||||
ar, err := NewAccountPayloadReceiver(pe, config, logger)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
rmr := NewRawMessagePayloadReceiver(logger, ar.accountPayload, pe, backend, config)
|
||||
imr := NewInstallationPayloadMounterReceiver(logger, pe, backend, config.DeviceType)
|
||||
return ar, rmr, imr, nil
|
||||
}
|
|
@ -24,6 +24,8 @@ func NewSyncRawMessageHandler(backend *api.GethStatusBackend) *SyncRawMessageHan
|
|||
}
|
||||
|
||||
func (s *SyncRawMessageHandler) CollectInstallationData(rawMessageCollector *RawMessageCollector, deviceType string) error {
|
||||
// TODO Could this function be part of the installation data exchange flow?
|
||||
// https://github.com/status-im/status-go/issues/3304
|
||||
messenger := s.backend.Messenger()
|
||||
if messenger == nil {
|
||||
return fmt.Errorf("messenger is nil when CollectInstallationData")
|
||||
|
|
|
@ -4,41 +4,63 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"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 Server struct {
|
||||
server.Server
|
||||
PayloadManager
|
||||
rawMessagePayloadManager *RawMessagePayloadManager
|
||||
installationPayloadManager *InstallationPayloadManager
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| type BaseServer struct {
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
|
|
||||
|
|
||||
*/
|
||||
|
||||
pk *ecdsa.PublicKey
|
||||
ek []byte
|
||||
mode Mode
|
||||
type BaseServer struct {
|
||||
server.Server
|
||||
|
||||
cookieStore *sessions.CookieStore
|
||||
encryptor *PayloadEncryptor
|
||||
|
||||
pk *ecdsa.PublicKey
|
||||
ek []byte
|
||||
// TODO remove mode from pairing process
|
||||
// https://github.com/status-im/status-go/issues/3301
|
||||
mode Mode
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
// Connection fields
|
||||
PK *ecdsa.PublicKey
|
||||
EK []byte
|
||||
Cert *tls.Certificate
|
||||
Hostname string
|
||||
Mode Mode
|
||||
// NewBaseServer returns a *BaseServer init from the given *SenderServerConfig
|
||||
func NewBaseServer(logger *zap.Logger, e *PayloadEncryptor, config *ServerConfig) (*BaseServer, error) {
|
||||
cs, err := makeCookieStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// AccountPayload management fields
|
||||
*AccountPayloadManagerConfig
|
||||
bs := &BaseServer{
|
||||
Server: server.NewServer(
|
||||
config.Cert,
|
||||
config.Hostname,
|
||||
nil,
|
||||
logger,
|
||||
),
|
||||
encryptor: e,
|
||||
cookieStore: cs,
|
||||
pk: config.PK,
|
||||
ek: config.EK,
|
||||
mode: config.Mode,
|
||||
}
|
||||
bs.SetTimeout(config.Timeout)
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func makeCookieStore() (*sessions.CookieStore, error) {
|
||||
|
@ -57,50 +79,8 @@ func makeCookieStore() (*sessions.CookieStore, error) {
|
|||
return sessions.NewCookieStore(auth, enc), nil
|
||||
}
|
||||
|
||||
// NewPairingServer returns a *Server init from the given *Config
|
||||
func NewPairingServer(backend *api.GethStatusBackend, config *Config) (*Server, error) {
|
||||
logger := logutils.ZapLogger().Named("Server")
|
||||
accountPayloadManagerConfig := config.AccountPayloadManagerConfig
|
||||
pm, err := NewAccountPayloadManager(config.EK, accountPayloadManagerConfig, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cs, err := makeCookieStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rmpm, err := NewRawMessagePayloadManager(logger, pm.accountPayload, config.EK, backend, accountPayloadManagerConfig.GetNodeConfig(), accountPayloadManagerConfig.GetSettingCurrentNetwork(), accountPayloadManagerConfig.GetDeviceType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipm, err := NewInstallationPayloadManager(logger, config.EK, backend, accountPayloadManagerConfig.GetDeviceType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Server{Server: server.NewServer(
|
||||
config.Cert,
|
||||
config.Hostname,
|
||||
nil,
|
||||
logger,
|
||||
),
|
||||
pk: config.PK,
|
||||
ek: config.EK,
|
||||
mode: config.Mode,
|
||||
PayloadManager: pm,
|
||||
cookieStore: cs,
|
||||
rawMessagePayloadManager: rmpm,
|
||||
installationPayloadManager: ipm,
|
||||
}
|
||||
s.SetTimeout(config.GetTimeout())
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// MakeConnectionParams generates a *ConnectionParams based on the Server's current state
|
||||
func (s *Server) MakeConnectionParams() (*ConnectionParams, error) {
|
||||
func (s *BaseServer) MakeConnectionParams() (*ConnectionParams, error) {
|
||||
hostname := s.GetHostname()
|
||||
netIP := net.ParseIP(hostname)
|
||||
if netIP == nil {
|
||||
|
@ -115,106 +95,218 @@ func (s *Server) MakeConnectionParams() (*ConnectionParams, error) {
|
|||
return NewConnectionParams(netIP, s.MustGetPort(), s.pk, s.ek, s.mode), nil
|
||||
}
|
||||
|
||||
func (s *Server) StartPairing() error {
|
||||
switch s.mode {
|
||||
case Receiving:
|
||||
return s.startReceivingData()
|
||||
case Sending:
|
||||
return s.startSendingData()
|
||||
default:
|
||||
return fmt.Errorf("invalid server mode '%d'", s.mode)
|
||||
}
|
||||
func (s *BaseServer) GetCookieStore() *sessions.CookieStore {
|
||||
return s.cookieStore
|
||||
}
|
||||
|
||||
func (s *Server) startReceivingData() error {
|
||||
s.SetHandlers(server.HandlerPatternMap{
|
||||
pairingReceiveAccount: handleReceiveAccount(s),
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
pairingSyncDeviceReceive: handleParingSyncDeviceReceive(s),
|
||||
// send installation data back to sender
|
||||
pairingSendInstallation: handleSendInstallation(s),
|
||||
})
|
||||
return s.Start()
|
||||
func (s *BaseServer) DecryptPlain(data []byte) ([]byte, error) {
|
||||
return s.encryptor.decryptPlain(data)
|
||||
}
|
||||
|
||||
func (s *Server) startSendingData() error {
|
||||
err := s.Mount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.SetHandlers(server.HandlerPatternMap{
|
||||
pairingSendAccount: challengeMiddleware(s, handleSendAccount(s)),
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
pairingSyncDeviceSend: challengeMiddleware(s, handlePairingSyncDeviceSend(s)),
|
||||
// receive installation data from receiver
|
||||
pairingReceiveInstallation: challengeMiddleware(s, handleReceiveInstallation(s)),
|
||||
})
|
||||
return s.Start()
|
||||
}
|
||||
|
||||
// MakeFullPairingServer generates a fully configured and randomly seeded Server
|
||||
func MakeFullPairingServer(backend *api.GethStatusBackend, mode Mode, storeConfig *PayloadSourceConfig) (*Server, error) {
|
||||
func MakeServerConfig(config *ServerConfig) error {
|
||||
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
AESKey := make([]byte, 32)
|
||||
_, err = rand.Read(AESKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
outboundIP, err := server.GetOutboundIP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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
|
||||
}
|
||||
|
||||
accountPayloadManagerConfig := &AccountPayloadManagerConfig{
|
||||
// Things that can't be generated, but DO NOT come from app client
|
||||
DB: backend.GetMultiaccountDB(),
|
||||
|
||||
// Things that can't be generated, but DO come from the app client
|
||||
PayloadSourceConfig: storeConfig,
|
||||
}
|
||||
if mode == Receiving {
|
||||
updateLoggedInKeyUID(accountPayloadManagerConfig, backend)
|
||||
am, rmm, imr, err := NewPayloadMounters(logger, e, backend, config.SenderConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPairingServer(backend, &Config{
|
||||
// Things that can be generated, and CANNOT come from the app client (well they could be this is better)
|
||||
PK: &tlsKey.PublicKey,
|
||||
EK: AESKey,
|
||||
Cert: &tlsCert,
|
||||
Hostname: outboundIP.String(),
|
||||
|
||||
// Things that can't be generated, but DO come from the app client
|
||||
Mode: mode,
|
||||
|
||||
AccountPayloadManagerConfig: accountPayloadManagerConfig,
|
||||
})
|
||||
return &SenderServer{
|
||||
BaseServer: bs,
|
||||
accountMounter: am,
|
||||
rawMessageMounter: rmm,
|
||||
installationMounter: imr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StartUpPairingServer generates a Server, starts the pairing server in the correct mode
|
||||
// and returns the ConnectionParams string to allow a Client to make a successful connection.
|
||||
func StartUpPairingServer(backend *api.GethStatusBackend, mode Mode, configJSON string) (string, error) {
|
||||
conf, err := NewPayloadSourceForServer(configJSON, mode)
|
||||
func (s *SenderServer) startSendingData() error {
|
||||
s.SetHandlers(server.HandlerPatternMap{
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
pairingSendAccount: middlewareChallenge(s, handleSendAccount(s, s.accountMounter)),
|
||||
pairingSendSyncDevice: middlewareChallenge(s, 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, handleReceiveInstallation(s, s.installationMounter)),
|
||||
})
|
||||
return s.Start()
|
||||
}
|
||||
|
||||
// MakeFullSenderServer generates a fully configured and randomly seeded SenderServer
|
||||
func MakeFullSenderServer(backend *api.GethStatusBackend, mode Mode, 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 in the correct mode
|
||||
// and returns the ConnectionParams string to allow a ReceiverClient to make a successful connection.
|
||||
func StartUpSenderServer(backend *api.GethStatusBackend, mode Mode, configJSON string) (string, error) {
|
||||
conf := NewSenderServerConfig()
|
||||
err := json.Unmarshal([]byte(configJSON), conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ps, err := MakeFullPairingServer(backend, mode, conf)
|
||||
ps, err := MakeFullSenderServer(backend, mode, conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = ps.StartPairing()
|
||||
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),
|
||||
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: handleSendInstallation(s, s.installationReceiver),
|
||||
})
|
||||
return s.Start()
|
||||
}
|
||||
|
||||
// MakeFullReceiverServer generates a fully configured and randomly seeded ReceiverServer
|
||||
func MakeFullReceiverServer(backend *api.GethStatusBackend, mode Mode, 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 in the correct mode
|
||||
// and returns the ConnectionParams string to allow a SenderClient to make a successful connection.
|
||||
func StartUpReceiverServer(backend *api.GethStatusBackend, mode Mode, configJSON string) (string, error) {
|
||||
conf := NewReceiverServerConfig()
|
||||
err := json.Unmarshal([]byte(configJSON), conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ps, err := MakeFullReceiverServer(backend, mode, conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = ps.startReceivingData()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -30,133 +30,61 @@ func (s *PairingServerSuite) SetupTest() {
|
|||
}
|
||||
|
||||
func (s *PairingServerSuite) TestMultiBackgroundForeground() {
|
||||
err := s.PS.Start()
|
||||
err := s.SS.Start()
|
||||
s.Require().NoError(err)
|
||||
s.PS.ToBackground()
|
||||
s.PS.ToForeground()
|
||||
s.PS.ToBackground()
|
||||
s.PS.ToBackground()
|
||||
s.PS.ToForeground()
|
||||
s.PS.ToForeground()
|
||||
s.Require().Regexp(regexp.MustCompile("(https://\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:\\d{1,5})"), s.PS.MakeBaseURL().String()) // nolint: gosimple
|
||||
s.SS.ToBackground()
|
||||
s.SS.ToForeground()
|
||||
s.SS.ToBackground()
|
||||
s.SS.ToBackground()
|
||||
s.SS.ToForeground()
|
||||
s.SS.ToForeground()
|
||||
s.Require().Regexp(regexp.MustCompile("(https://\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:\\d{1,5})"), s.SS.MakeBaseURL().String()) // nolint: gosimple
|
||||
}
|
||||
|
||||
func (s *PairingServerSuite) TestMultiTimeout() {
|
||||
s.PS.SetTimeout(20)
|
||||
s.SS.SetTimeout(20)
|
||||
|
||||
err := s.PS.Start()
|
||||
err := s.SS.Start()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.PS.ToBackground()
|
||||
s.PS.ToForeground()
|
||||
s.PS.ToBackground()
|
||||
s.PS.ToBackground()
|
||||
s.PS.ToForeground()
|
||||
s.PS.ToForeground()
|
||||
s.SS.ToBackground()
|
||||
s.SS.ToForeground()
|
||||
s.SS.ToBackground()
|
||||
s.SS.ToBackground()
|
||||
s.SS.ToForeground()
|
||||
s.SS.ToForeground()
|
||||
|
||||
s.Require().Regexp(regexp.MustCompile("(https://\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:\\d{1,5})"), s.PS.MakeBaseURL().String()) // nolint: gosimple
|
||||
s.Require().Regexp(regexp.MustCompile("(https://\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:\\d{1,5})"), s.SS.MakeBaseURL().String()) // nolint: gosimple
|
||||
|
||||
time.Sleep(7 * time.Millisecond)
|
||||
s.PS.ToBackground()
|
||||
s.SS.ToBackground()
|
||||
time.Sleep(7 * time.Millisecond)
|
||||
s.PS.ToForeground()
|
||||
s.SS.ToForeground()
|
||||
time.Sleep(7 * time.Millisecond)
|
||||
s.PS.ToBackground()
|
||||
s.SS.ToBackground()
|
||||
time.Sleep(7 * time.Millisecond)
|
||||
s.PS.ToBackground()
|
||||
s.SS.ToBackground()
|
||||
time.Sleep(7 * time.Millisecond)
|
||||
s.PS.ToForeground()
|
||||
s.SS.ToForeground()
|
||||
time.Sleep(7 * time.Millisecond)
|
||||
s.PS.ToForeground()
|
||||
s.SS.ToForeground()
|
||||
|
||||
// Wait for timeout to expire
|
||||
time.Sleep(40 * time.Millisecond)
|
||||
s.Require().False(s.PS.IsRunning())
|
||||
s.Require().False(s.SS.IsRunning())
|
||||
}
|
||||
|
||||
func newAccountPayloadManagerConfig() *AccountPayloadManagerConfig {
|
||||
return &AccountPayloadManagerConfig{}
|
||||
}
|
||||
// TestPairingServer_StartPairingSend tests that a Server can send data to a ReceiverClient
|
||||
func (s *PairingServerSuite) TestPairingServer_StartPairingSend() {
|
||||
// Replace PairingServer.accountMounter with a MockPayloadMounter
|
||||
pm := NewMockPayloadMounter(s.EphemeralAES)
|
||||
s.SS.accountMounter = pm
|
||||
s.SS.mode = Sending
|
||||
|
||||
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)
|
||||
|
||||
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(nil, ccp, newAccountPayloadManagerConfig())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Compare cert values
|
||||
cert := c.serverCert
|
||||
cl := s.PS.GetCert().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()
|
||||
s.PS.ResetPort()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PairingServerSuite) sendingSetup() *Client {
|
||||
// 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()
|
||||
err := s.SS.startSendingData()
|
||||
s.Require().NoError(err)
|
||||
|
||||
cp, err := s.PS.MakeConnectionParams()
|
||||
cp, err := s.SS.MakeConnectionParams()
|
||||
s.Require().NoError(err)
|
||||
|
||||
qr := cp.ToString()
|
||||
|
@ -166,11 +94,104 @@ func (s *PairingServerSuite) sendingSetup() *Client {
|
|||
err = ccp.FromString(qr)
|
||||
s.Require().NoError(err)
|
||||
|
||||
c, err := NewPairingClient(nil, ccp, newAccountPayloadManagerConfig())
|
||||
c, err := NewReceiverClient(nil, ccp, NewReceiverClientConfig())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Compare cert values
|
||||
cert := c.serverCert
|
||||
cl := s.SS.GetCert().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 ReceivingClient.accountReceiver with a MockPayloadReceiver
|
||||
c.accountReceiver = NewMockPayloadReceiver(s.EphemeralAES)
|
||||
|
||||
err = c.getChallenge()
|
||||
s.Require().NoError(err)
|
||||
err = c.receiveAccountData()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(c.accountReceiver.Received(), s.SS.accountMounter.(*MockPayloadMounter).encryptor.payload.plain)
|
||||
s.Require().Equal(c.accountReceiver.(*MockPayloadReceiver).encryptor.payload.encrypted, s.SS.accountMounter.(*MockPayloadMounter).encryptor.payload.encrypted)
|
||||
}
|
||||
|
||||
// TestPairingServer_StartPairingReceive tests that a Server can receive data to a SenderClient
|
||||
func (s *PairingServerSuite) TestPairingServer_StartPairingReceive() {
|
||||
// Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager
|
||||
pm := NewMockPayloadReceiver(s.EphemeralAES)
|
||||
s.RS.accountReceiver = pm
|
||||
|
||||
s.RS.mode = Receiving
|
||||
|
||||
err := s.RS.startReceivingData()
|
||||
s.Require().NoError(err)
|
||||
|
||||
cp, err := s.RS.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 := NewSenderClient(nil, ccp, &SenderClientConfig{SenderConfig: &SenderConfig{}, ClientConfig: &ClientConfig{}})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Compare cert values
|
||||
cert := c.serverCert
|
||||
cl := s.RS.GetCert().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 SendingClient.accountMounter with a MockPayloadMounter
|
||||
c.accountMounter = NewMockPayloadMounter(s.EphemeralAES)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = c.sendAccountData()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(c.accountMounter.(*MockPayloadMounter).encryptor.payload.plain, s.RS.accountReceiver.Received())
|
||||
s.Require().Equal(s.RS.accountReceiver.(*MockPayloadReceiver).encryptor.getEncrypted(), c.accountMounter.(*MockPayloadMounter).encryptor.payload.encrypted)
|
||||
}
|
||||
|
||||
func (s *PairingServerSuite) sendingSetup() *ReceiverClient {
|
||||
// Replace PairingServer.PayloadManager with a MockPayloadReceiver
|
||||
pm := NewMockPayloadMounter(s.EphemeralAES)
|
||||
s.SS.accountMounter = pm
|
||||
s.SS.mode = Sending
|
||||
|
||||
err := s.SS.startSendingData()
|
||||
s.Require().NoError(err)
|
||||
|
||||
cp, err := s.SS.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 := NewReceiverClient(nil, ccp, NewReceiverClientConfig())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
|
||||
c.PayloadManager, err = NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
|
||||
c.accountReceiver = NewMockPayloadReceiver(s.EphemeralAES)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return c
|
||||
|
@ -257,17 +278,17 @@ func makeThingToSay() (string, error) {
|
|||
}
|
||||
|
||||
func (s *PairingServerSuite) TestGetOutboundIPWithFullServerE2e() {
|
||||
s.PS.mode = Sending
|
||||
s.PS.SetHandlers(server.HandlerPatternMap{"/hello": testHandler(s.T())})
|
||||
s.SS.mode = Sending
|
||||
s.SS.SetHandlers(server.HandlerPatternMap{"/hello": testHandler(s.T())})
|
||||
|
||||
err := s.PS.Start()
|
||||
err := s.SS.Start()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Give time for the sever to be ready, hacky I know, I'll iron this out
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Server generates a QR code connection string
|
||||
cp, err := s.PS.MakeConnectionParams()
|
||||
cp, err := s.SS.MakeConnectionParams()
|
||||
s.Require().NoError(err)
|
||||
|
||||
qr := cp.ToString()
|
||||
|
@ -277,7 +298,7 @@ func (s *PairingServerSuite) TestGetOutboundIPWithFullServerE2e() {
|
|||
err = ccp.FromString(qr)
|
||||
s.Require().NoError(err)
|
||||
|
||||
c, err := NewPairingClient(nil, ccp, newAccountPayloadManagerConfig())
|
||||
c, err := NewReceiverClient(nil, ccp, NewReceiverClientConfig())
|
||||
s.Require().NoError(err)
|
||||
|
||||
thing, err := makeThingToSay()
|
||||
|
|
|
@ -23,17 +23,16 @@ import (
|
|||
"github.com/status-im/status-go/sqlite"
|
||||
)
|
||||
|
||||
const pathWalletRoot = "m/44'/60'/0'/0"
|
||||
const pathEIP1581 = "m/43'/60'/1581'"
|
||||
const pathDefaultChat = pathEIP1581 + "/0'/0"
|
||||
const pathDefaultWallet = pathWalletRoot + "/0"
|
||||
const (
|
||||
pathWalletRoot = "m/44'/60'/0'/0"
|
||||
pathEIP1581 = "m/43'/60'/1581'"
|
||||
pathDefaultChat = pathEIP1581 + "/0'/0"
|
||||
pathDefaultWallet = pathWalletRoot + "/0"
|
||||
currentNetwork = "mainnet_rpc"
|
||||
)
|
||||
|
||||
var paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet}
|
||||
|
||||
const keystoreDir = "keystore"
|
||||
|
||||
const currentNetwork = "mainnet_rpc"
|
||||
|
||||
func TestSyncDeviceSuite(t *testing.T) {
|
||||
suite.Run(t, new(SyncDeviceSuite))
|
||||
}
|
||||
|
@ -141,19 +140,20 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
|
|||
require.NoError(s.T(), err)
|
||||
expectedKDFIterations := 1024
|
||||
serverKeystoreDir := filepath.Join(serverTmpDir, keystoreDir)
|
||||
serverPayloadSourceConfig := PayloadSourceConfig{
|
||||
KeystorePath: serverKeystoreDir,
|
||||
DeviceType: "desktop",
|
||||
PayloadSourceReceiverConfig: &PayloadSourceReceiverConfig{
|
||||
KDFIterations: expectedKDFIterations,
|
||||
serverPayloadSourceConfig := &ReceiverServerConfig{
|
||||
ReceiverConfig: &ReceiverConfig{
|
||||
NodeConfig: serverNodeConfig,
|
||||
RootDataDir: serverTmpDir,
|
||||
KeystorePath: serverKeystoreDir,
|
||||
DeviceType: "desktop",
|
||||
KDFIterations: expectedKDFIterations,
|
||||
SettingCurrentNetwork: currentNetwork,
|
||||
},
|
||||
ServerConfig: new(ServerConfig),
|
||||
}
|
||||
serverNodeConfig.RootDataDir = serverTmpDir
|
||||
serverConfigBytes, err := json.Marshal(serverPayloadSourceConfig)
|
||||
require.NoError(s.T(), err)
|
||||
cs, err := StartUpPairingServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||
cs, err := StartUpReceiverServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// generate some data for the client
|
||||
|
@ -167,17 +167,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
|
|||
clientActiveAccount, err := clientBackend.GetActiveAccount()
|
||||
require.NoError(s.T(), err)
|
||||
clientKeystorePath := filepath.Join(clientTmpDir, keystoreDir, clientActiveAccount.KeyUID)
|
||||
clientPayloadSourceConfig := PayloadSourceConfig{
|
||||
KeystorePath: clientKeystorePath,
|
||||
DeviceType: "android",
|
||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
||||
KeyUID: clientActiveAccount.KeyUID,
|
||||
Password: s.password,
|
||||
clientPayloadSourceConfig := SenderClientConfig{
|
||||
SenderConfig: &SenderConfig{
|
||||
KeystorePath: clientKeystorePath,
|
||||
DeviceType: "android",
|
||||
KeyUID: clientActiveAccount.KeyUID,
|
||||
Password: s.password,
|
||||
},
|
||||
ClientConfig: new(ClientConfig),
|
||||
}
|
||||
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
|
||||
require.NoError(s.T(), err)
|
||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
|
||||
|
@ -206,18 +207,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
|
|||
require.False(s.T(), serverMessenger.HasPairedDevices())
|
||||
|
||||
// repeat local pairing, we should expect no error after receiver logged in
|
||||
cs, err = StartUpPairingServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||
cs, err = StartUpReceiverServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
require.True(s.T(), clientMessenger.HasPairedDevices())
|
||||
require.True(s.T(), serverMessenger.HasPairedDevices())
|
||||
|
||||
// test if it's okay when account already exist but not logged in
|
||||
require.NoError(s.T(), serverBackend.Logout())
|
||||
cs, err = StartUpPairingServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||
cs, err = StartUpReceiverServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
@ -235,17 +236,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
|
|||
serverActiveAccount, err := serverBackend.GetActiveAccount()
|
||||
require.NoError(s.T(), err)
|
||||
serverKeystorePath := filepath.Join(serverTmpDir, keystoreDir, serverActiveAccount.KeyUID)
|
||||
var config = PayloadSourceConfig{
|
||||
KeystorePath: serverKeystorePath,
|
||||
DeviceType: "desktop",
|
||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
||||
KeyUID: serverActiveAccount.KeyUID,
|
||||
Password: s.password,
|
||||
var config = &SenderServerConfig{
|
||||
SenderConfig: &SenderConfig{
|
||||
KeystorePath: serverKeystorePath,
|
||||
DeviceType: "desktop",
|
||||
KeyUID: serverActiveAccount.KeyUID,
|
||||
Password: s.password,
|
||||
},
|
||||
ServerConfig: new(ServerConfig),
|
||||
}
|
||||
configBytes, err := json.Marshal(config)
|
||||
require.NoError(s.T(), err)
|
||||
cs, err := StartUpPairingServer(serverBackend, Sending, string(configBytes))
|
||||
cs, err := StartUpSenderServer(serverBackend, Sending, string(configBytes))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// generate some data for the server
|
||||
|
@ -264,19 +266,20 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
|
|||
require.NoError(s.T(), err)
|
||||
expectedKDFIterations := 2048
|
||||
clientKeystoreDir := filepath.Join(clientTmpDir, keystoreDir)
|
||||
clientPayloadSourceConfig := PayloadSourceConfig{
|
||||
KeystorePath: clientKeystoreDir,
|
||||
DeviceType: "iphone",
|
||||
PayloadSourceReceiverConfig: &PayloadSourceReceiverConfig{
|
||||
clientPayloadSourceConfig := ReceiverClientConfig{
|
||||
ReceiverConfig: &ReceiverConfig{
|
||||
KeystorePath: clientKeystoreDir,
|
||||
DeviceType: "iphone",
|
||||
KDFIterations: expectedKDFIterations,
|
||||
NodeConfig: clientNodeConfig,
|
||||
RootDataDir: clientTmpDir,
|
||||
SettingCurrentNetwork: currentNetwork,
|
||||
},
|
||||
ClientConfig: new(ClientConfig),
|
||||
}
|
||||
clientNodeConfig.RootDataDir = clientTmpDir
|
||||
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
|
||||
require.NoError(s.T(), err)
|
||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
|
||||
|
@ -305,18 +308,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
|
|||
require.False(s.T(), clientMessenger.HasPairedDevices())
|
||||
|
||||
// repeat local pairing, we should expect no error after receiver logged in
|
||||
cs, err = StartUpPairingServer(serverBackend, Sending, string(configBytes))
|
||||
cs, err = StartUpSenderServer(serverBackend, Sending, string(configBytes))
|
||||
require.NoError(s.T(), err)
|
||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
require.True(s.T(), serverMessenger.HasPairedDevices())
|
||||
require.True(s.T(), clientMessenger.HasPairedDevices())
|
||||
|
||||
// test if it's okay when account already exist but not logged in
|
||||
require.NoError(s.T(), clientBackend.Logout())
|
||||
cs, err = StartUpPairingServer(serverBackend, Sending, string(configBytes))
|
||||
cs, err = StartUpSenderServer(serverBackend, Sending, string(configBytes))
|
||||
require.NoError(s.T(), err)
|
||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes))
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ func (t *timeoutManager) run(terminate func(), exit chan struct{}) {
|
|||
return
|
||||
case <-time.After(time.Duration(t.timeout) * time.Millisecond):
|
||||
terminate()
|
||||
// TODO fire signal to let UI know
|
||||
// https://github.com/status-im/status-go/issues/3305
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue