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/waku-org/go-waku v0.5.2-0.20230308135126-4b52983fc483
|
||||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
||||||
|
go.uber.org/multierr v1.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -251,7 +252,6 @@ require (
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
go.uber.org/dig v1.15.0 // indirect
|
go.uber.org/dig v1.15.0 // indirect
|
||||||
go.uber.org/fx v1.18.2 // 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/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
golang.org/x/mod v0.7.0 // indirect
|
||||||
golang.org/x/net v0.4.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)
|
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
|
// 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
|
// and the device has no camera to read a QR code with
|
||||||
//
|
//
|
||||||
|
@ -1017,14 +1017,14 @@ func GetConnectionStringForBeingBootstrapped(configJSON string) string {
|
||||||
if configJSON == "" {
|
if configJSON == "" {
|
||||||
return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
|
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 {
|
if err != nil {
|
||||||
return makeJSONResponse(err)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
return cs
|
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
|
// 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
|
// 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)
|
// sending account data to a mobile (device with camera)
|
||||||
func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string {
|
func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string {
|
||||||
if configJSON == "" {
|
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 {
|
if err != nil {
|
||||||
return makeJSONResponse(err)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
return cs
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// InputConnectionStringForBootstrapping starts a pairing.Client
|
// InputConnectionStringForBootstrapping starts a pairing.ReceiverClient
|
||||||
// The given server.ConnectionParams string will determine the server.Mode
|
// The given server.ConnectionParams string will determine the server.Mode
|
||||||
//
|
//
|
||||||
// server.Mode = server.Sending
|
// 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
|
// 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
|
// Example: A mobile device (device with a camera) receiving account data from
|
||||||
// a device with a screen (mobile or desktop devices)
|
// a device with a screen (mobile or desktop devices)
|
||||||
func InputConnectionStringForBootstrapping(cs, configJSON string) string {
|
func InputConnectionStringForBootstrapping(cs, configJSON string) string {
|
||||||
if configJSON == "" {
|
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)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -310,7 +310,7 @@ type NodeConfig struct {
|
||||||
// NetworkID sets network to use for selecting peers to connect to
|
// NetworkID sets network to use for selecting peers to connect to
|
||||||
NetworkID uint64 `json:"NetworkId" validate:"required"`
|
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 is the file system folder the node should use for any data storage needs.
|
||||||
DataDir string `validate:"required"`
|
DataDir string `validate:"required"`
|
||||||
|
|
|
@ -18,6 +18,9 @@ import (
|
||||||
"github.com/status-im/status-go/signal"
|
"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 {
|
func makeSerialNumberFromKey(pk *ecdsa.PrivateKey) *big.Int {
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(append(pk.D.Bytes(), append(pk.Y.Bytes(), pk.X.Bytes()...)...))
|
h.Write(append(pk.D.Bytes(), append(pk.Y.Bytes(), pk.X.Bytes()...)...))
|
||||||
|
|
|
@ -2,9 +2,9 @@ package pairing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -20,21 +20,26 @@ import (
|
||||||
"github.com/status-im/status-go/signal"
|
"github.com/status-im/status-go/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
/*
|
||||||
*http.Client
|
|--------------------------------------------------------------------------
|
||||||
PayloadManager
|
| BaseClient
|
||||||
rawMessagePayloadManager *RawMessagePayloadManager
|
|--------------------------------------------------------------------------
|
||||||
installationPayloadManager *InstallationPayloadManager
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
baseAddress *url.URL
|
// BaseClient is responsible for lower level pairing.Client functionality common to dependent Client types
|
||||||
certPEM []byte
|
type BaseClient struct {
|
||||||
serverPK *ecdsa.PublicKey
|
*http.Client
|
||||||
serverMode Mode
|
|
||||||
serverCert *x509.Certificate
|
serverCert *x509.Certificate
|
||||||
|
encryptor *PayloadEncryptor
|
||||||
|
baseAddress *url.URL
|
||||||
serverChallenge []byte
|
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()
|
u, err := c.URL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -44,10 +49,12 @@ func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, confi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = verifyCert(serverCert, c.publicKey)
|
err = verifyCert(serverCert, c.publicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert.Raw})
|
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert.Raw})
|
||||||
|
|
||||||
rootCAs, err := x509.SystemCertPool()
|
rootCAs, err := x509.SystemCertPool()
|
||||||
|
@ -71,117 +78,116 @@ func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, confi
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := logutils.ZapLogger().Named("Client")
|
return &BaseClient{
|
||||||
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},
|
Client: &http.Client{Transport: tr, Jar: cj},
|
||||||
baseAddress: u,
|
|
||||||
certPEM: certPem,
|
|
||||||
serverCert: serverCert,
|
serverCert: serverCert,
|
||||||
serverPK: c.publicKey,
|
encryptor: NewPayloadEncryptor(c.aesKey),
|
||||||
serverMode: c.serverMode,
|
baseAddress: u,
|
||||||
PayloadManager: pm,
|
|
||||||
rawMessagePayloadManager: rmpm,
|
|
||||||
installationPayloadManager: ipm,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PairAccount() error {
|
// getChallenge makes a call to the identified Server and receives a [32]byte challenge
|
||||||
switch c.serverMode {
|
func (c *BaseClient) getChallenge() error {
|
||||||
case Receiving:
|
c.baseAddress.Path = pairingChallenge
|
||||||
return c.sendAccountData()
|
resp, err := c.Get(c.baseAddress.String())
|
||||||
case Sending:
|
|
||||||
err := c.getChallenge()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
|
||||||
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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
err = fmt.Errorf("[client] status not okay when sending installation data, status: %s", resp.Status)
|
return fmt.Errorf("[client] status not ok when getting challenge, received '%s'", resp.Status)
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
c.serverChallenge, err = ioutil.ReadAll(resp.Body)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) sendSyncDeviceData() error {
|
// doChallenge checks if there is a serverChallenge and encrypts the challenge using the shared AES key
|
||||||
err := c.rawMessagePayloadManager.Mount()
|
func (c *BaseClient) doChallenge(req *http.Request) error {
|
||||||
|
if c.serverChallenge != nil {
|
||||||
|
ec, err := c.encryptor.encryptPlain(c.serverChallenge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.baseAddress.Path = pairingSyncDeviceReceive
|
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
||||||
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.rawMessagePayloadManager.ToSend()))
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| 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 = 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 {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||||
return err
|
return err
|
||||||
|
@ -197,7 +203,7 @@ func (c *Client) sendSyncDeviceData() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) receiveInstallationData() error {
|
func (c *SenderClient) receiveInstallationData() error {
|
||||||
c.baseAddress.Path = pairingSendInstallation
|
c.baseAddress.Path = pairingSendInstallation
|
||||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -223,7 +229,7 @@ func (c *Client) receiveInstallationData() error {
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
||||||
|
|
||||||
err = c.installationPayloadManager.Receive(payload)
|
err = c.installationMounter.Receive(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
|
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||||
return err
|
return err
|
||||||
|
@ -232,91 +238,96 @@ func (c *Client) receiveInstallationData() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) receiveSyncDeviceData() error {
|
// setupSendingClient creates a new SenderClient after parsing string inputs
|
||||||
c.baseAddress.Path = pairingSyncDeviceSend
|
func setupSendingClient(backend *api.GethStatusBackend, cs, configJSON string) (*SenderClient, error) {
|
||||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
ccp := new(ConnectionParams)
|
||||||
|
err := ccp.FromString(cs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.serverChallenge != nil {
|
conf := NewSenderClientConfig()
|
||||||
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
|
err = json.Unmarshal([]byte(configJSON), conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
conf.SenderConfig.DB = backend.GetMultiaccountDB()
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
return NewSenderClient(backend, ccp, conf)
|
||||||
if err != nil {
|
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
|
||||||
return 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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) sendAccountData() error {
|
// StartUpSendingClient creates a SenderClient and triggers all `send` calls in sequence to the ReceiverServer
|
||||||
err := c.Mount()
|
func StartUpSendingClient(backend *api.GethStatusBackend, cs, configJSON string) error {
|
||||||
|
c, err := setupSendingClient(backend, cs, configJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = c.sendAccountData()
|
||||||
c.baseAddress.Path = pairingReceiveAccount
|
|
||||||
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.PayloadManager.ToSend()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = c.sendSyncDeviceData()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if err != nil {
|
||||||
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
|
return err
|
||||||
}
|
}
|
||||||
|
return c.receiveInstallationData()
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
|
||||||
|
|
||||||
c.PayloadManager.LockPayload()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
c.baseAddress.Path = pairingSendAccount
|
||||||
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.serverChallenge != nil {
|
err = c.doChallenge(req)
|
||||||
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set(sessionChallenge, base58.Encode(ec))
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||||
|
@ -336,7 +347,7 @@ func (c *Client) receiveAccountData() error {
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||||
|
|
||||||
err = c.PayloadManager.Receive(payload)
|
err = c.accountReceiver.Receive(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
|
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
|
||||||
return err
|
return err
|
||||||
|
@ -345,56 +356,118 @@ func (c *Client) receiveAccountData() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getChallenge() error {
|
func (c *ReceiverClient) receiveSyncDeviceData() error {
|
||||||
c.baseAddress.Path = pairingChallenge
|
c.baseAddress.Path = pairingSendSyncDevice
|
||||||
resp, err := c.Get(c.baseAddress.String())
|
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
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)
|
payload, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||||
return err
|
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 {
|
func (c *ReceiverClient) sendInstallationData() error {
|
||||||
c, err := setupClient(backend, cs, configJSON)
|
err := c.installationReceiver.Mount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = c.PairSyncDevice()
|
req.Header.Set("Content-Type", "application/octet-stream")
|
||||||
|
|
||||||
|
err = c.doChallenge(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
||||||
return err
|
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)
|
ccp := new(ConnectionParams)
|
||||||
err := ccp.FromString(cs)
|
err := ccp.FromString(cs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := NewPayloadSourceForClient(configJSON, ccp.serverMode)
|
conf := NewReceiverClientConfig()
|
||||||
|
err = json.Unmarshal([]byte(configJSON), conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPayloadManagerConfig := &AccountPayloadManagerConfig{DB: backend.GetMultiaccountDB(), PayloadSourceConfig: conf}
|
conf.ReceiverConfig.DB = backend.GetMultiaccountDB()
|
||||||
if ccp.serverMode == Sending {
|
|
||||||
updateLoggedInKeyUID(accountPayloadManagerConfig, backend)
|
return NewReceiverClient(backend, ccp, conf)
|
||||||
}
|
}
|
||||||
c, err := NewPairingClient(backend, ccp, accountPayloadManagerConfig)
|
|
||||||
if err != nil {
|
// StartUpReceivingClient creates a ReceiverClient and triggers all `receive` calls in sequence to the SenderServer
|
||||||
return nil, err
|
func StartUpReceivingClient(backend *api.GethStatusBackend, cs, configJSON string) error {
|
||||||
}
|
c, err := setupReceivingClient(backend, cs, configJSON)
|
||||||
return c, nil
|
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
|
package pairing
|
||||||
|
|
||||||
import "github.com/status-im/status-go/api"
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
func updateLoggedInKeyUID(accountPayloadManagerConfig *AccountPayloadManagerConfig, backend *api.GethStatusBackend) {
|
"github.com/status-im/status-go/account/generator"
|
||||||
activeAccount, _ := backend.GetActiveAccount()
|
"github.com/status-im/status-go/eth-node/keystore"
|
||||||
if activeAccount != nil {
|
)
|
||||||
accountPayloadManagerConfig.LoggedInKeyUID = activeAccount.KeyUID
|
|
||||||
|
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/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -90,7 +89,8 @@ type TestPairingServerComponents struct {
|
||||||
EphemeralAES []byte
|
EphemeralAES []byte
|
||||||
OutboundIP net.IP
|
OutboundIP net.IP
|
||||||
Cert tls.Certificate
|
Cert tls.Certificate
|
||||||
PS *Server
|
SS *SenderServer
|
||||||
|
RS *ReceiverServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tpsc *TestPairingServerComponents) SetupPairingServerComponents(t *testing.T) {
|
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())
|
tpsc.Cert, _, err = GenerateCertFromKey(tpsc.EphemeralPK, time.Now(), tpsc.OutboundIP.String())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tpsc.PS, err = NewPairingServer(nil, &Config{
|
sc := &ServerConfig{
|
||||||
PK: &tpsc.EphemeralPK.PublicKey,
|
PK: &tpsc.EphemeralPK.PublicKey,
|
||||||
EK: tpsc.EphemeralAES,
|
EK: tpsc.EphemeralAES,
|
||||||
Cert: &tpsc.Cert,
|
Cert: &tpsc.Cert,
|
||||||
Hostname: tpsc.OutboundIP.String(),
|
Hostname: tpsc.OutboundIP.String(),
|
||||||
AccountPayloadManagerConfig: &AccountPayloadManagerConfig{}})
|
}
|
||||||
|
|
||||||
|
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)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,22 +134,33 @@ func (tlc *TestLoggerComponents) SetupLoggerComponents() {
|
||||||
tlc.Logger = logutils.ZapLogger()
|
tlc.Logger = logutils.ZapLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
type MockEncryptOnlyPayloadManager struct {
|
type MockPayloadReceiver struct {
|
||||||
*PayloadEncryptionManager
|
encryptor *PayloadEncryptor
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockEncryptOnlyPayloadManager(aesKey []byte) (*MockEncryptOnlyPayloadManager, error) {
|
func NewMockPayloadReceiver(aesKey []byte) *MockPayloadReceiver {
|
||||||
pem, err := NewPayloadEncryptionManager(aesKey, logutils.ZapLogger())
|
return &MockPayloadReceiver{NewPayloadEncryptor(aesKey)}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MockEncryptOnlyPayloadManager{
|
|
||||||
pem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Make a random payload
|
||||||
data := make([]byte, 32)
|
data := make([]byte, 32)
|
||||||
_, err := rand.Read(data)
|
_, err := rand.Read(data)
|
||||||
|
@ -153,9 +168,13 @@ func (m *MockEncryptOnlyPayloadManager) Mount() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.Encrypt(data)
|
return m.encryptor.encrypt(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockEncryptOnlyPayloadManager) Receive(data []byte) error {
|
func (m *MockPayloadMounter) ToSend() []byte {
|
||||||
return m.Decrypt(data)
|
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)
|
ek := base58.Encode(cp.aesKey)
|
||||||
m := base58.Encode(new(big.Int).SetInt64(int64(cp.serverMode)).Bytes())
|
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)
|
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 {
|
func (cp *ConnectionParams) validateServerMode() error {
|
||||||
switch cp.serverMode {
|
switch cp.serverMode {
|
||||||
case Receiving, Sending:
|
case 0, Receiving, Sending:
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid server mode '%d'", cp.serverMode)
|
return fmt.Errorf("invalid server mode '%d'", cp.serverMode)
|
||||||
|
|
|
@ -22,7 +22,7 @@ type ConnectionParamsSuite struct {
|
||||||
TestCertComponents
|
TestCertComponents
|
||||||
TestLoggerComponents
|
TestLoggerComponents
|
||||||
|
|
||||||
server *Server
|
server *BaseServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConnectionParamsSuite) SetupSuite() {
|
func (s *ConnectionParamsSuite) SetupSuite() {
|
||||||
|
@ -37,7 +37,7 @@ func (s *ConnectionParamsSuite) SetupSuite() {
|
||||||
err = bs.SetPort(1337)
|
err = bs.SetPort(1337)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.server = &Server{
|
s.server = &BaseServer{
|
||||||
Server: bs,
|
Server: bs,
|
||||||
pk: &s.PK.PublicKey,
|
pk: &s.PK.PublicKey,
|
||||||
ek: s.AES,
|
ek: s.AES,
|
||||||
|
|
|
@ -29,7 +29,7 @@ type Event struct {
|
||||||
type Action int
|
type Action int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ActionConnect = iota + 1
|
ActionConnect Action = iota + 1
|
||||||
ActionPairingAccount
|
ActionPairingAccount
|
||||||
ActionSyncDevice
|
ActionSyncDevice
|
||||||
ActionPairingInstallation
|
ActionPairingInstallation
|
||||||
|
|
|
@ -9,18 +9,17 @@ import (
|
||||||
"github.com/btcsuite/btcutil/base58"
|
"github.com/btcsuite/btcutil/base58"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/common"
|
|
||||||
"github.com/status-im/status-go/signal"
|
"github.com/status-im/status-go/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Handler routes for pairing
|
// Handler routes for pairing
|
||||||
pairingBase = "/pairing"
|
pairingBase = "/pairing"
|
||||||
|
pairingChallenge = pairingBase + "/challenge"
|
||||||
pairingSendAccount = pairingBase + "/sendAccount"
|
pairingSendAccount = pairingBase + "/sendAccount"
|
||||||
pairingReceiveAccount = pairingBase + "/receiveAccount"
|
pairingReceiveAccount = pairingBase + "/receiveAccount"
|
||||||
pairingChallenge = pairingBase + "/challenge"
|
pairingSendSyncDevice = pairingBase + "/sendSyncDevice"
|
||||||
pairingSyncDeviceSend = pairingBase + "/sendSyncDevice"
|
pairingReceiveSyncDevice = pairingBase + "/receiveSyncDevice"
|
||||||
pairingSyncDeviceReceive = pairingBase + "/receiveSyncDevice"
|
|
||||||
pairingSendInstallation = pairingBase + "/sendInstallation"
|
pairingSendInstallation = pairingBase + "/sendInstallation"
|
||||||
pairingReceiveInstallation = pairingBase + "/receiveInstallation"
|
pairingReceiveInstallation = pairingBase + "/receiveInstallation"
|
||||||
|
|
||||||
|
@ -29,145 +28,172 @@ const (
|
||||||
sessionBlocked = "blocked"
|
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})
|
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
|
||||||
logger := ps.GetLogger()
|
logger := hs.GetLogger()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
payload, err := io.ReadAll(r.Body)
|
payload, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||||
logger.Error("handleReceiveAccount io.ReadAll(r.Body)", zap.Error(err))
|
logger.Error("handleReceiveAccount io.ReadAll(r.Body)", zap.Error(err))
|
||||||
|
http.Error(w, "error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
|
||||||
|
|
||||||
err = ps.PayloadManager.Receive(payload)
|
err = pr.Receive(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
|
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
|
return
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingAccount})
|
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingAccount})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleReceiveInstallation(ps *Server) http.HandlerFunc {
|
func handleSendAccount(hs HandlerServer, pm PayloadMounter) http.HandlerFunc {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingInstallation})
|
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
|
||||||
logger := ps.GetLogger()
|
logger := hs.GetLogger()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
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 {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||||
logger.Error("handleReceiveInstallation io.ReadAll(r.Body)", zap.Error(err))
|
logger.Error("handleSendAccount pm.Mount()", zap.Error(err))
|
||||||
|
http.Error(w, "error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
|
|
||||||
|
|
||||||
err = ps.installationPayloadManager.Receive(payload)
|
_, err = w.Write(pm.ToSend())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
|
||||||
logger.Error("ps.installationPayloadManager.Receive(payload)", zap.Error(err))
|
logger.Error("handleSendAccount w.Write(pm.ToSend())", zap.Error(err))
|
||||||
|
http.Error(w, "error", http.StatusInternalServerError)
|
||||||
return
|
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})
|
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
|
||||||
logger := ps.GetLogger()
|
logger := hs.GetLogger()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
payload, err := io.ReadAll(r.Body)
|
payload, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
||||||
logger.Error("handleParingSyncDeviceReceive io.ReadAll(r.Body)", zap.Error(err))
|
logger.Error("handleParingSyncDeviceReceive io.ReadAll(r.Body)", zap.Error(err))
|
||||||
|
http.Error(w, "error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
||||||
|
|
||||||
err = ps.rawMessagePayloadManager.Receive(payload)
|
err = pr.Receive(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice})
|
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
|
return
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
|
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSendAccount(ps *Server) http.HandlerFunc {
|
func handlePairingSyncDeviceSend(hs HandlerServer, pm PayloadMounter) 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 {
|
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
|
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
|
||||||
logger := ps.GetLogger()
|
logger := hs.GetLogger()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
|
||||||
err := ps.rawMessagePayloadManager.Mount()
|
err := pm.Mount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// maybe better to use a new event type here instead of EventTransferError?
|
// maybe better to use a new event type here instead of EventTransferError?
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = w.Write(ps.rawMessagePayloadManager.ToSend())
|
_, err = w.Write(pm.ToSend())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
|
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
|
return
|
||||||
}
|
}
|
||||||
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
|
||||||
|
|
||||||
ps.rawMessagePayloadManager.LockPayload()
|
pm.LockPayload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
// Installation data handling
|
||||||
logger := ps.GetLogger()
|
|
||||||
|
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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
s, err := ps.cookieStore.Get(r, sessionChallenge)
|
payload, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
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)
|
http.Error(w, "error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -185,9 +211,9 @@ func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := common.Decrypt(base58.Decode(pc), ps.ek)
|
c, err := hs.DecryptPlain(base58.Decode(pc))
|
||||||
if err != nil {
|
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)
|
http.Error(w, "error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -205,7 +231,7 @@ func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
|
||||||
s.Values[sessionBlocked] = true
|
s.Values[sessionBlocked] = true
|
||||||
err = s.Save(r, w)
|
err = s.Save(r, w)
|
||||||
if err != nil {
|
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)
|
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 {
|
func handlePairingChallenge(hs HandlerServer) http.HandlerFunc {
|
||||||
logger := ps.GetLogger()
|
logger := hs.GetLogger()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,14 +258,16 @@ func handlePairingChallenge(ps *Server) http.HandlerFunc {
|
||||||
challenge = make([]byte, 64)
|
challenge = make([]byte, 64)
|
||||||
_, err = rand.Read(challenge)
|
_, err = rand.Read(challenge)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Values[sessionChallenge] = challenge
|
s.Values[sessionChallenge] = challenge
|
||||||
err = s.Save(r, w)
|
err = s.Save(r, w)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,7 +275,7 @@ func handlePairingChallenge(ps *Server) http.HandlerFunc {
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
_, err = w.Write(challenge)
|
_, err = w.Write(challenge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("_, err = w.Write(challenge)", zap.Error(err))
|
logger.Error("handlePairingChallenge: _, err = w.Write(challenge)", zap.Error(err))
|
||||||
return
|
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()
|
teardown func()
|
||||||
|
|
||||||
config1 *AccountPayloadManagerConfig
|
config1 *SenderConfig
|
||||||
config2 *AccountPayloadManagerConfig
|
config2 *ReceiverConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTestDB(t *testing.T) (*multiaccounts.Database, func()) {
|
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")
|
emptyKeyStoreDir, err := os.MkdirTemp(os.TempDir(), "accounts_empty")
|
||||||
require.NoError(t, err)
|
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
|
// 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)
|
err = os.MkdirAll(keyStoreDir, 0777)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -132,26 +132,16 @@ func (pms *PayloadMarshallerSuite) SetupTest() {
|
||||||
err := db1.SaveAccount(expected)
|
err := db1.SaveAccount(expected)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
pms.config1 = &AccountPayloadManagerConfig{
|
pms.config1 = &SenderConfig{
|
||||||
DB: db1,
|
DB: db1,
|
||||||
PayloadSourceConfig: &PayloadSourceConfig{
|
|
||||||
KeystorePath: keystore1,
|
KeystorePath: keystore1,
|
||||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
|
||||||
KeyUID: keyUID,
|
KeyUID: keyUID,
|
||||||
Password: password,
|
Password: password,
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pms.config2 = &AccountPayloadManagerConfig{
|
pms.config2 = &ReceiverConfig{
|
||||||
DB: db2,
|
DB: db2,
|
||||||
PayloadSourceConfig: &PayloadSourceConfig{
|
|
||||||
KeystorePath: keystore2,
|
KeystorePath: keystore2,
|
||||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
|
||||||
KeyUID: keyUID,
|
|
||||||
Password: password,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,13 +153,13 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() {
|
||||||
// Make a Payload
|
// Make a Payload
|
||||||
pp := new(AccountPayload)
|
pp := new(AccountPayload)
|
||||||
|
|
||||||
// Make and LoadFromSource PairingPayloadRepository 1
|
// Make and Load() PairingPayloadRepository 1
|
||||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
err = ppr.LoadFromSource()
|
err = ppr.Load()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// TEST PairingPayloadRepository 1 LoadFromSource()
|
// TEST PairingPayloadRepository 1 Load()
|
||||||
pms.Require().Len(ppr.keys, 2)
|
pms.Require().Len(ppr.keys, 2)
|
||||||
pms.Require().Len(ppr.keys[utils.GetAccount1PKFile()], 489)
|
pms.Require().Len(ppr.keys[utils.GetAccount1PKFile()], 489)
|
||||||
pms.Require().Len(ppr.keys[utils.GetAccount2PKFile()], 489)
|
pms.Require().Len(ppr.keys[utils.GetAccount2PKFile()], 489)
|
||||||
|
@ -197,17 +187,17 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() {
|
||||||
// Make a Payload
|
// Make a Payload
|
||||||
pp := new(AccountPayload)
|
pp := new(AccountPayload)
|
||||||
|
|
||||||
// Make and LoadFromSource PairingPayloadRepository 1
|
// Make and Load() PairingPayloadRepository 1
|
||||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
err = ppr.LoadFromSource()
|
err = ppr.Load()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// Make and Load PairingPayloadMarshaller 1
|
// Make and Load() PairingPayloadMarshaller 1
|
||||||
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
||||||
|
|
||||||
// TEST PairingPayloadMarshaller 1 MarshalToProtobuf()
|
// TEST PairingPayloadMarshaller 1 MarshalProtobuf()
|
||||||
pb, err := ppm.MarshalToProtobuf()
|
pb, err := ppm.MarshalProtobuf()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
pms.Require().Len(pb, 1384)
|
pms.Require().Len(pb, 1384)
|
||||||
|
|
||||||
|
@ -227,16 +217,16 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
|
||||||
// Make a Payload
|
// Make a Payload
|
||||||
pp := new(AccountPayload)
|
pp := new(AccountPayload)
|
||||||
|
|
||||||
// Make and LoadFromSource PairingPayloadRepository 1
|
// Make and Load() PairingPayloadRepository 1
|
||||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
err = ppr.LoadFromSource()
|
err = ppr.Load()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// Make and Load PairingPayloadMarshaller 1
|
// Make and Load() PairingPayloadMarshaller 1
|
||||||
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
||||||
|
|
||||||
pb, err := ppm.MarshalToProtobuf()
|
pb, err := ppm.MarshalProtobuf()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// Make a Payload
|
// Make a Payload
|
||||||
|
@ -281,16 +271,16 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
||||||
// Make a Payload
|
// Make a Payload
|
||||||
pp := new(AccountPayload)
|
pp := new(AccountPayload)
|
||||||
|
|
||||||
// Make and LoadFromSource PairingPayloadRepository 1
|
// Make and Load() PairingPayloadRepository 1
|
||||||
ppr, err := NewAccountPayloadRepository(pp, pms.config1)
|
ppr, err := NewAccountPayloadLoader(pp, pms.config1)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
err = ppr.LoadFromSource()
|
err = ppr.Load()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// Make and Load PairingPayloadMarshaller 1
|
// Make and Load() PairingPayloadMarshaller 1
|
||||||
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
ppm := NewPairingPayloadMarshaller(pp, pms.Logger)
|
||||||
|
|
||||||
pb, err := ppm.MarshalToProtobuf()
|
pb, err := ppm.MarshalProtobuf()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// Make a Payload
|
// Make a Payload
|
||||||
|
@ -302,14 +292,14 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
||||||
err = ppm2.UnmarshalProtobuf(pb)
|
err = ppm2.UnmarshalProtobuf(pb)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// Make and Load PairingPayloadRepository 2
|
// Make and Load() PairingPayloadRepository 2
|
||||||
ppr2, err := NewAccountPayloadRepository(pp2, pms.config2)
|
ppr2, err := NewAccountPayloadStorer(pp2, pms.config2)
|
||||||
require.NoError(pms.T(), err)
|
require.NoError(pms.T(), err)
|
||||||
err = ppr2.StoreToSource()
|
err = ppr2.Store()
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
// TEST PairingPayloadRepository 2 StoreToSource()
|
// TEST PairingPayloadRepository 2 Store()
|
||||||
keys := getFiles(pms.T(), filepath.Join(pms.config2.KeystorePath, pms.config2.KeyUID))
|
keys := getFiles(pms.T(), filepath.Join(pms.config2.KeystorePath, keyUID))
|
||||||
|
|
||||||
pms.Require().Len(keys, 2)
|
pms.Require().Len(keys, 2)
|
||||||
pms.Require().Len(keys[utils.GetAccount1PKFile()], 489)
|
pms.Require().Len(keys[utils.GetAccount1PKFile()], 489)
|
||||||
|
@ -344,8 +334,7 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LockPayload() {
|
||||||
_, err := rand.Read(AESKey)
|
_, err := rand.Read(AESKey)
|
||||||
pms.Require().NoError(err)
|
pms.Require().NoError(err)
|
||||||
|
|
||||||
pm, err := NewMockEncryptOnlyPayloadManager(AESKey)
|
pm := NewMockPayloadMounter(AESKey)
|
||||||
pms.Require().NoError(err)
|
|
||||||
|
|
||||||
err = pm.Mount()
|
err = pm.Mount()
|
||||||
pms.Require().NoError(err)
|
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 {
|
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()
|
messenger := s.backend.Messenger()
|
||||||
if messenger == nil {
|
if messenger == nil {
|
||||||
return fmt.Errorf("messenger is nil when CollectInstallationData")
|
return fmt.Errorf("messenger is nil when CollectInstallationData")
|
||||||
|
|
|
@ -4,41 +4,63 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/api"
|
"github.com/status-im/status-go/api"
|
||||||
"github.com/status-im/status-go/logutils"
|
"github.com/status-im/status-go/logutils"
|
||||||
"github.com/status-im/status-go/server"
|
"github.com/status-im/status-go/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| type BaseServer struct {
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
type BaseServer struct {
|
||||||
server.Server
|
server.Server
|
||||||
PayloadManager
|
|
||||||
rawMessagePayloadManager *RawMessagePayloadManager
|
cookieStore *sessions.CookieStore
|
||||||
installationPayloadManager *InstallationPayloadManager
|
encryptor *PayloadEncryptor
|
||||||
|
|
||||||
pk *ecdsa.PublicKey
|
pk *ecdsa.PublicKey
|
||||||
ek []byte
|
ek []byte
|
||||||
|
// TODO remove mode from pairing process
|
||||||
|
// https://github.com/status-im/status-go/issues/3301
|
||||||
mode Mode
|
mode Mode
|
||||||
|
|
||||||
cookieStore *sessions.CookieStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
// NewBaseServer returns a *BaseServer init from the given *SenderServerConfig
|
||||||
// Connection fields
|
func NewBaseServer(logger *zap.Logger, e *PayloadEncryptor, config *ServerConfig) (*BaseServer, error) {
|
||||||
PK *ecdsa.PublicKey
|
cs, err := makeCookieStore()
|
||||||
EK []byte
|
if err != nil {
|
||||||
Cert *tls.Certificate
|
return nil, err
|
||||||
Hostname string
|
}
|
||||||
Mode Mode
|
|
||||||
|
|
||||||
// AccountPayload management fields
|
bs := &BaseServer{
|
||||||
*AccountPayloadManagerConfig
|
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) {
|
func makeCookieStore() (*sessions.CookieStore, error) {
|
||||||
|
@ -57,50 +79,8 @@ func makeCookieStore() (*sessions.CookieStore, error) {
|
||||||
return sessions.NewCookieStore(auth, enc), nil
|
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
|
// 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()
|
hostname := s.GetHostname()
|
||||||
netIP := net.ParseIP(hostname)
|
netIP := net.ParseIP(hostname)
|
||||||
if netIP == nil {
|
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
|
return NewConnectionParams(netIP, s.MustGetPort(), s.pk, s.ek, s.mode), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) StartPairing() error {
|
func (s *BaseServer) GetCookieStore() *sessions.CookieStore {
|
||||||
switch s.mode {
|
return s.cookieStore
|
||||||
case Receiving:
|
|
||||||
return s.startReceivingData()
|
|
||||||
case Sending:
|
|
||||||
return s.startSendingData()
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid server mode '%d'", s.mode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) startReceivingData() error {
|
func (s *BaseServer) DecryptPlain(data []byte) ([]byte, error) {
|
||||||
s.SetHandlers(server.HandlerPatternMap{
|
return s.encryptor.decryptPlain(data)
|
||||||
pairingReceiveAccount: handleReceiveAccount(s),
|
|
||||||
pairingChallenge: handlePairingChallenge(s),
|
|
||||||
pairingSyncDeviceReceive: handleParingSyncDeviceReceive(s),
|
|
||||||
// send installation data back to sender
|
|
||||||
pairingSendInstallation: handleSendInstallation(s),
|
|
||||||
})
|
|
||||||
return s.Start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) startSendingData() error {
|
func MakeServerConfig(config *ServerConfig) 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) {
|
|
||||||
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
AESKey := make([]byte, 32)
|
AESKey := make([]byte, 32)
|
||||||
_, err = rand.Read(AESKey)
|
_, err = rand.Read(AESKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
outboundIP, err := server.GetOutboundIP()
|
outboundIP, err := server.GetOutboundIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsCert, _, err := GenerateCertFromKey(tlsKey, time.Now(), outboundIP.String())
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPayloadManagerConfig := &AccountPayloadManagerConfig{
|
am, rmm, imr, err := NewPayloadMounters(logger, e, backend, config.SenderConfig)
|
||||||
// Things that can't be generated, but DO NOT come from app client
|
if err != nil {
|
||||||
DB: backend.GetMultiaccountDB(),
|
return nil, err
|
||||||
|
|
||||||
// Things that can't be generated, but DO come from the app client
|
|
||||||
PayloadSourceConfig: storeConfig,
|
|
||||||
}
|
|
||||||
if mode == Receiving {
|
|
||||||
updateLoggedInKeyUID(accountPayloadManagerConfig, backend)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewPairingServer(backend, &Config{
|
return &SenderServer{
|
||||||
// Things that can be generated, and CANNOT come from the app client (well they could be this is better)
|
BaseServer: bs,
|
||||||
PK: &tlsKey.PublicKey,
|
accountMounter: am,
|
||||||
EK: AESKey,
|
rawMessageMounter: rmm,
|
||||||
Cert: &tlsCert,
|
installationMounter: imr,
|
||||||
Hostname: outboundIP.String(),
|
}, nil
|
||||||
|
|
||||||
// Things that can't be generated, but DO come from the app client
|
|
||||||
Mode: mode,
|
|
||||||
|
|
||||||
AccountPayloadManagerConfig: accountPayloadManagerConfig,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartUpPairingServer generates a Server, starts the pairing server in the correct mode
|
func (s *SenderServer) startSendingData() error {
|
||||||
// and returns the ConnectionParams string to allow a Client to make a successful connection.
|
s.SetHandlers(server.HandlerPatternMap{
|
||||||
func StartUpPairingServer(backend *api.GethStatusBackend, mode Mode, configJSON string) (string, error) {
|
pairingChallenge: handlePairingChallenge(s),
|
||||||
conf, err := NewPayloadSourceForServer(configJSON, mode)
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
ps, err := MakeFullPairingServer(backend, mode, conf)
|
ps, err := MakeFullSenderServer(backend, mode, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,72 +30,61 @@ func (s *PairingServerSuite) SetupTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PairingServerSuite) TestMultiBackgroundForeground() {
|
func (s *PairingServerSuite) TestMultiBackgroundForeground() {
|
||||||
err := s.PS.Start()
|
err := s.SS.Start()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
s.PS.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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PairingServerSuite) TestMultiTimeout() {
|
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.Require().NoError(err)
|
||||||
|
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
s.PS.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)
|
time.Sleep(7 * time.Millisecond)
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
time.Sleep(7 * time.Millisecond)
|
time.Sleep(7 * time.Millisecond)
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
time.Sleep(7 * time.Millisecond)
|
time.Sleep(7 * time.Millisecond)
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
time.Sleep(7 * time.Millisecond)
|
time.Sleep(7 * time.Millisecond)
|
||||||
s.PS.ToBackground()
|
s.SS.ToBackground()
|
||||||
time.Sleep(7 * time.Millisecond)
|
time.Sleep(7 * time.Millisecond)
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
time.Sleep(7 * time.Millisecond)
|
time.Sleep(7 * time.Millisecond)
|
||||||
s.PS.ToForeground()
|
s.SS.ToForeground()
|
||||||
|
|
||||||
// Wait for timeout to expire
|
// Wait for timeout to expire
|
||||||
time.Sleep(40 * time.Millisecond)
|
time.Sleep(40 * time.Millisecond)
|
||||||
s.Require().False(s.PS.IsRunning())
|
s.Require().False(s.SS.IsRunning())
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAccountPayloadManagerConfig() *AccountPayloadManagerConfig {
|
// TestPairingServer_StartPairingSend tests that a Server can send data to a ReceiverClient
|
||||||
return &AccountPayloadManagerConfig{}
|
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() {
|
err := s.SS.startSendingData()
|
||||||
// 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)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
cp, err := s.PS.MakeConnectionParams()
|
cp, err := s.SS.MakeConnectionParams()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
qr := cp.ToString()
|
qr := cp.ToString()
|
||||||
|
@ -105,12 +94,12 @@ func (s *PairingServerSuite) TestPairingServer_StartPairing() {
|
||||||
err = ccp.FromString(qr)
|
err = ccp.FromString(qr)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
c, err := NewPairingClient(nil, ccp, newAccountPayloadManagerConfig())
|
c, err := NewReceiverClient(nil, ccp, NewReceiverClientConfig())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Compare cert values
|
// Compare cert values
|
||||||
cert := c.serverCert
|
cert := c.serverCert
|
||||||
cl := s.PS.GetCert().Leaf
|
cl := s.SS.GetCert().Leaf
|
||||||
s.Require().Equal(cl.Signature, cert.Signature)
|
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).X.Cmp(cert.PublicKey.(*ecdsa.PublicKey).X))
|
||||||
s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).Y.Cmp(cert.PublicKey.(*ecdsa.PublicKey).Y))
|
s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).Y.Cmp(cert.PublicKey.(*ecdsa.PublicKey).Y))
|
||||||
|
@ -120,43 +109,30 @@ func (s *PairingServerSuite) TestPairingServer_StartPairing() {
|
||||||
s.Require().Exactly(cl.NotAfter, cert.NotAfter)
|
s.Require().Exactly(cl.NotAfter, cert.NotAfter)
|
||||||
s.Require().Exactly(cl.IPAddresses, cert.IPAddresses)
|
s.Require().Exactly(cl.IPAddresses, cert.IPAddresses)
|
||||||
|
|
||||||
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
|
// Replace ReceivingClient.accountReceiver with a MockPayloadReceiver
|
||||||
c.PayloadManager, err = NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
|
c.accountReceiver = NewMockPayloadReceiver(s.EphemeralAES)
|
||||||
|
|
||||||
|
err = c.getChallenge()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
err = c.receiveAccountData()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
err = c.PairAccount()
|
s.Require().Equal(c.accountReceiver.Received(), s.SS.accountMounter.(*MockPayloadMounter).encryptor.payload.plain)
|
||||||
s.Require().NoError(err)
|
s.Require().Equal(c.accountReceiver.(*MockPayloadReceiver).encryptor.payload.encrypted, s.SS.accountMounter.(*MockPayloadMounter).encryptor.payload.encrypted)
|
||||||
|
|
||||||
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 {
|
// TestPairingServer_StartPairingReceive tests that a Server can receive data to a SenderClient
|
||||||
|
func (s *PairingServerSuite) TestPairingServer_StartPairingReceive() {
|
||||||
// Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager
|
// Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager
|
||||||
pm, err := NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
|
pm := NewMockPayloadReceiver(s.EphemeralAES)
|
||||||
s.Require().NoError(err)
|
s.RS.accountReceiver = pm
|
||||||
s.PS.PayloadManager = pm
|
|
||||||
s.PS.mode = Sending
|
|
||||||
|
|
||||||
err = s.PS.StartPairing()
|
s.RS.mode = Receiving
|
||||||
|
|
||||||
|
err := s.RS.startReceivingData()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
cp, err := s.PS.MakeConnectionParams()
|
cp, err := s.RS.MakeConnectionParams()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
qr := cp.ToString()
|
qr := cp.ToString()
|
||||||
|
@ -166,11 +142,56 @@ func (s *PairingServerSuite) sendingSetup() *Client {
|
||||||
err = ccp.FromString(qr)
|
err = ccp.FromString(qr)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
c, err := NewPairingClient(nil, ccp, newAccountPayloadManagerConfig())
|
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)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
|
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
|
||||||
c.PayloadManager, err = NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
|
c.accountReceiver = NewMockPayloadReceiver(s.EphemeralAES)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
@ -257,17 +278,17 @@ func makeThingToSay() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PairingServerSuite) TestGetOutboundIPWithFullServerE2e() {
|
func (s *PairingServerSuite) TestGetOutboundIPWithFullServerE2e() {
|
||||||
s.PS.mode = Sending
|
s.SS.mode = Sending
|
||||||
s.PS.SetHandlers(server.HandlerPatternMap{"/hello": testHandler(s.T())})
|
s.SS.SetHandlers(server.HandlerPatternMap{"/hello": testHandler(s.T())})
|
||||||
|
|
||||||
err := s.PS.Start()
|
err := s.SS.Start()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Give time for the sever to be ready, hacky I know, I'll iron this out
|
// Give time for the sever to be ready, hacky I know, I'll iron this out
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
// Server generates a QR code connection string
|
// Server generates a QR code connection string
|
||||||
cp, err := s.PS.MakeConnectionParams()
|
cp, err := s.SS.MakeConnectionParams()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
qr := cp.ToString()
|
qr := cp.ToString()
|
||||||
|
@ -277,7 +298,7 @@ func (s *PairingServerSuite) TestGetOutboundIPWithFullServerE2e() {
|
||||||
err = ccp.FromString(qr)
|
err = ccp.FromString(qr)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
c, err := NewPairingClient(nil, ccp, newAccountPayloadManagerConfig())
|
c, err := NewReceiverClient(nil, ccp, NewReceiverClientConfig())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
thing, err := makeThingToSay()
|
thing, err := makeThingToSay()
|
||||||
|
|
|
@ -23,17 +23,16 @@ import (
|
||||||
"github.com/status-im/status-go/sqlite"
|
"github.com/status-im/status-go/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pathWalletRoot = "m/44'/60'/0'/0"
|
const (
|
||||||
const pathEIP1581 = "m/43'/60'/1581'"
|
pathWalletRoot = "m/44'/60'/0'/0"
|
||||||
const pathDefaultChat = pathEIP1581 + "/0'/0"
|
pathEIP1581 = "m/43'/60'/1581'"
|
||||||
const pathDefaultWallet = pathWalletRoot + "/0"
|
pathDefaultChat = pathEIP1581 + "/0'/0"
|
||||||
|
pathDefaultWallet = pathWalletRoot + "/0"
|
||||||
|
currentNetwork = "mainnet_rpc"
|
||||||
|
)
|
||||||
|
|
||||||
var paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet}
|
var paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet}
|
||||||
|
|
||||||
const keystoreDir = "keystore"
|
|
||||||
|
|
||||||
const currentNetwork = "mainnet_rpc"
|
|
||||||
|
|
||||||
func TestSyncDeviceSuite(t *testing.T) {
|
func TestSyncDeviceSuite(t *testing.T) {
|
||||||
suite.Run(t, new(SyncDeviceSuite))
|
suite.Run(t, new(SyncDeviceSuite))
|
||||||
}
|
}
|
||||||
|
@ -141,19 +140,20 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
expectedKDFIterations := 1024
|
expectedKDFIterations := 1024
|
||||||
serverKeystoreDir := filepath.Join(serverTmpDir, keystoreDir)
|
serverKeystoreDir := filepath.Join(serverTmpDir, keystoreDir)
|
||||||
serverPayloadSourceConfig := PayloadSourceConfig{
|
serverPayloadSourceConfig := &ReceiverServerConfig{
|
||||||
|
ReceiverConfig: &ReceiverConfig{
|
||||||
|
NodeConfig: serverNodeConfig,
|
||||||
KeystorePath: serverKeystoreDir,
|
KeystorePath: serverKeystoreDir,
|
||||||
DeviceType: "desktop",
|
DeviceType: "desktop",
|
||||||
PayloadSourceReceiverConfig: &PayloadSourceReceiverConfig{
|
|
||||||
KDFIterations: expectedKDFIterations,
|
KDFIterations: expectedKDFIterations,
|
||||||
NodeConfig: serverNodeConfig,
|
|
||||||
RootDataDir: serverTmpDir,
|
|
||||||
SettingCurrentNetwork: currentNetwork,
|
SettingCurrentNetwork: currentNetwork,
|
||||||
},
|
},
|
||||||
|
ServerConfig: new(ServerConfig),
|
||||||
}
|
}
|
||||||
|
serverNodeConfig.RootDataDir = serverTmpDir
|
||||||
serverConfigBytes, err := json.Marshal(serverPayloadSourceConfig)
|
serverConfigBytes, err := json.Marshal(serverPayloadSourceConfig)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
cs, err := StartUpPairingServer(serverBackend, Receiving, string(serverConfigBytes))
|
cs, err := StartUpReceiverServer(serverBackend, Receiving, string(serverConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// generate some data for the client
|
// generate some data for the client
|
||||||
|
@ -167,17 +167,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
|
||||||
clientActiveAccount, err := clientBackend.GetActiveAccount()
|
clientActiveAccount, err := clientBackend.GetActiveAccount()
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
clientKeystorePath := filepath.Join(clientTmpDir, keystoreDir, clientActiveAccount.KeyUID)
|
clientKeystorePath := filepath.Join(clientTmpDir, keystoreDir, clientActiveAccount.KeyUID)
|
||||||
clientPayloadSourceConfig := PayloadSourceConfig{
|
clientPayloadSourceConfig := SenderClientConfig{
|
||||||
|
SenderConfig: &SenderConfig{
|
||||||
KeystorePath: clientKeystorePath,
|
KeystorePath: clientKeystorePath,
|
||||||
DeviceType: "android",
|
DeviceType: "android",
|
||||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
|
||||||
KeyUID: clientActiveAccount.KeyUID,
|
KeyUID: clientActiveAccount.KeyUID,
|
||||||
Password: s.password,
|
Password: s.password,
|
||||||
},
|
},
|
||||||
|
ClientConfig: new(ClientConfig),
|
||||||
}
|
}
|
||||||
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
|
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
|
serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
|
||||||
|
@ -206,18 +207,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
|
||||||
require.False(s.T(), serverMessenger.HasPairedDevices())
|
require.False(s.T(), serverMessenger.HasPairedDevices())
|
||||||
|
|
||||||
// repeat local pairing, we should expect no error after receiver logged in
|
// 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)
|
require.NoError(s.T(), err)
|
||||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
require.True(s.T(), clientMessenger.HasPairedDevices())
|
require.True(s.T(), clientMessenger.HasPairedDevices())
|
||||||
require.True(s.T(), serverMessenger.HasPairedDevices())
|
require.True(s.T(), serverMessenger.HasPairedDevices())
|
||||||
|
|
||||||
// test if it's okay when account already exist but not logged in
|
// test if it's okay when account already exist but not logged in
|
||||||
require.NoError(s.T(), serverBackend.Logout())
|
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)
|
require.NoError(s.T(), err)
|
||||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,17 +236,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
|
||||||
serverActiveAccount, err := serverBackend.GetActiveAccount()
|
serverActiveAccount, err := serverBackend.GetActiveAccount()
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
serverKeystorePath := filepath.Join(serverTmpDir, keystoreDir, serverActiveAccount.KeyUID)
|
serverKeystorePath := filepath.Join(serverTmpDir, keystoreDir, serverActiveAccount.KeyUID)
|
||||||
var config = PayloadSourceConfig{
|
var config = &SenderServerConfig{
|
||||||
|
SenderConfig: &SenderConfig{
|
||||||
KeystorePath: serverKeystorePath,
|
KeystorePath: serverKeystorePath,
|
||||||
DeviceType: "desktop",
|
DeviceType: "desktop",
|
||||||
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
|
|
||||||
KeyUID: serverActiveAccount.KeyUID,
|
KeyUID: serverActiveAccount.KeyUID,
|
||||||
Password: s.password,
|
Password: s.password,
|
||||||
},
|
},
|
||||||
|
ServerConfig: new(ServerConfig),
|
||||||
}
|
}
|
||||||
configBytes, err := json.Marshal(config)
|
configBytes, err := json.Marshal(config)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
cs, err := StartUpPairingServer(serverBackend, Sending, string(configBytes))
|
cs, err := StartUpSenderServer(serverBackend, Sending, string(configBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// generate some data for the server
|
// generate some data for the server
|
||||||
|
@ -264,19 +266,20 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
expectedKDFIterations := 2048
|
expectedKDFIterations := 2048
|
||||||
clientKeystoreDir := filepath.Join(clientTmpDir, keystoreDir)
|
clientKeystoreDir := filepath.Join(clientTmpDir, keystoreDir)
|
||||||
clientPayloadSourceConfig := PayloadSourceConfig{
|
clientPayloadSourceConfig := ReceiverClientConfig{
|
||||||
|
ReceiverConfig: &ReceiverConfig{
|
||||||
KeystorePath: clientKeystoreDir,
|
KeystorePath: clientKeystoreDir,
|
||||||
DeviceType: "iphone",
|
DeviceType: "iphone",
|
||||||
PayloadSourceReceiverConfig: &PayloadSourceReceiverConfig{
|
|
||||||
KDFIterations: expectedKDFIterations,
|
KDFIterations: expectedKDFIterations,
|
||||||
NodeConfig: clientNodeConfig,
|
NodeConfig: clientNodeConfig,
|
||||||
RootDataDir: clientTmpDir,
|
|
||||||
SettingCurrentNetwork: currentNetwork,
|
SettingCurrentNetwork: currentNetwork,
|
||||||
},
|
},
|
||||||
|
ClientConfig: new(ClientConfig),
|
||||||
}
|
}
|
||||||
|
clientNodeConfig.RootDataDir = clientTmpDir
|
||||||
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
|
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
|
clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
|
||||||
|
@ -305,18 +308,18 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
|
||||||
require.False(s.T(), clientMessenger.HasPairedDevices())
|
require.False(s.T(), clientMessenger.HasPairedDevices())
|
||||||
|
|
||||||
// repeat local pairing, we should expect no error after receiver logged in
|
// 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)
|
require.NoError(s.T(), err)
|
||||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
require.True(s.T(), serverMessenger.HasPairedDevices())
|
require.True(s.T(), serverMessenger.HasPairedDevices())
|
||||||
require.True(s.T(), clientMessenger.HasPairedDevices())
|
require.True(s.T(), clientMessenger.HasPairedDevices())
|
||||||
|
|
||||||
// test if it's okay when account already exist but not logged in
|
// test if it's okay when account already exist but not logged in
|
||||||
require.NoError(s.T(), clientBackend.Logout())
|
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)
|
require.NoError(s.T(), err)
|
||||||
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
|
err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,8 @@ func (t *timeoutManager) run(terminate func(), exit chan struct{}) {
|
||||||
return
|
return
|
||||||
case <-time.After(time.Duration(t.timeout) * time.Millisecond):
|
case <-time.After(time.Duration(t.timeout) * time.Millisecond):
|
||||||
terminate()
|
terminate()
|
||||||
|
// TODO fire signal to let UI know
|
||||||
|
// https://github.com/status-im/status-go/issues/3305
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue