status-go/server/pairing/payload_receiver.go

372 lines
11 KiB
Go

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
}