support local pairing after logged in as receiver; pair installation;(#3202)

This commit is contained in:
frank 2023-02-28 20:32:45 +08:00 committed by GitHub
parent 411607d43d
commit 837bf2ca42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 802 additions and 306 deletions

View File

@ -1 +1 @@
0.133.3
0.134.0

View File

@ -215,6 +215,11 @@ func (b *GethStatusBackend) DeleteMultiaccount(keyUID string, keyStoreDir string
}
}
if b.account != nil && b.account.KeyUID == keyUID {
// reset active account
b.account = nil
}
return os.RemoveAll(keyStoreDir)
}
@ -1362,6 +1367,7 @@ func (b *GethStatusBackend) Logout() error {
b.AccountManager().Logout()
b.appDB = nil
b.account = nil
if b.statusNode != nil {
if err := b.statusNode.Stop(); err != nil {

View File

@ -208,7 +208,7 @@ func (db *Database) GetAccounts() (rst []Account, err error) {
}
func (db *Database) GetAccount(keyUID string) (*Account, error) {
rows, err := db.db.Query("SELECT a.name, a.loginTimestamp, a.identicon, a.colorHash, a.colorId, a.keycardPairing, a.keyUid, ii.key_uid, ii.name, ii.image_payload, ii.width, ii.height, ii.file_size, ii.resize_target, ii.clock FROM accounts AS a LEFT JOIN identity_images AS ii ON ii.key_uid = a.keyUid WHERE a.keyUid = ? ORDER BY loginTimestamp DESC", keyUID)
rows, err := db.db.Query("SELECT a.name, a.loginTimestamp, a.identicon, a.colorHash, a.colorId, a.keycardPairing, a.keyUid, a.kdfIterations, ii.key_uid, ii.name, ii.image_payload, ii.width, ii.height, ii.file_size, ii.resize_target, ii.clock FROM accounts AS a LEFT JOIN identity_images AS ii ON ii.key_uid = a.keyUid WHERE a.keyUid = ? ORDER BY loginTimestamp DESC", keyUID)
if err != nil {
return nil, err
}
@ -241,6 +241,7 @@ func (db *Database) GetAccount(keyUID string) (*Account, error) {
&accColorID,
&acc.KeycardPairing,
&acc.KeyUID,
&acc.KDFIterations,
&iiKeyUID,
&iiName,
&ii.Payload,

View File

@ -182,7 +182,7 @@ func TestDatabase_GetAccount(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
expected := Account{Name: "string", KeyUID: keyUID, ColorHash: ColourHash{{4, 3}, {4, 0}, {4, 3}, {4, 0}}, ColorID: 10}
expected := Account{Name: "string", KeyUID: keyUID, ColorHash: ColourHash{{4, 3}, {4, 0}, {4, 3}, {4, 0}}, ColorID: 10, KDFIterations: sqlite.ReducedKDFIterationsNumber}
require.NoError(t, db.SaveAccount(expected))
account, err := db.GetAccount(expected.KeyUID)

View File

@ -2384,7 +2384,7 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity_RequestToJoin() {
func (s *MessengerCommunitiesSuite) pairTwoDevices(device1, device2 *Messenger, deviceName, deviceType string) {
// Send pairing data
response, err := device1.SendPairInstallation(context.Background())
response, err := device1.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Len(response.Chats(), 1)

View File

@ -401,6 +401,10 @@ func (p *Protocol) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *
return p.multidevice.AddInstallations(bundle.GetIdentity(), bundle.GetTimestamp(), installations, enabled)
}
func (p *Protocol) GetMultiDevice() *multidevice.Multidevice {
return p.multidevice
}
// recoverInstallationsFromBundle extracts installations from the bundle.
// It returns extracted installations and true if the installations
// are ours, i.e. the bundle was created by our identity key.

View File

@ -750,11 +750,6 @@ func (m *Messenger) Start() (*MessengerResponse, error) {
return nil, err
}
err = m.setInstallationHostname()
if err != nil {
return nil, err
}
return response, nil
}
@ -762,6 +757,10 @@ func (m *Messenger) IdentityPublicKey() *ecdsa.PublicKey {
return &m.identity.PublicKey
}
func (m *Messenger) IdentityPublicKeyCompressed() []byte {
return crypto.CompressPubkey(m.IdentityPublicKey())
}
// cleanTopics remove any topic that does not have a Listen flag set
func (m *Messenger) cleanTopics() error {
if m.mailserversDatabase == nil {
@ -1572,6 +1571,11 @@ func (m *Messenger) Init() error {
m.allInstallations.Store(installation.ID, installation)
}
err = m.setInstallationHostname()
if err != nil {
return err
}
_, err = m.transport.InitFilters(publicChatIDs, publicKeys)
return err
}
@ -1767,6 +1771,10 @@ func (m *Messenger) hasPairedDevices() bool {
return count > 1
}
func (m *Messenger) HasPairedDevices() bool {
return m.hasPairedDevices()
}
// sendToPairedDevices will check if we have any paired devices and send to them if necessary
func (m *Messenger) sendToPairedDevices(ctx context.Context, spec common.RawMessage) error {
hasPairedDevices := m.hasPairedDevices()
@ -1780,23 +1788,23 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, spec common.RawMess
return nil
}
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec common.RawMessage) ([]byte, error) {
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec common.RawMessage) (common.RawMessage, error) {
var err error
var id []byte
id, err = m.sender.SendPairInstallation(ctx, &m.identity.PublicKey, spec)
if err != nil {
return nil, err
return spec, err
}
spec.ID = types.EncodeHex(id)
spec.SendCount++
err = m.persistence.SaveRawMessage(&spec)
if err != nil {
return nil, err
return spec, err
}
return id, nil
return spec, nil
}
func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMessage) (common.RawMessage, error) {
@ -2505,7 +2513,7 @@ func (m *Messenger) getLastClockWithRelatedChat() (uint64, *Chat) {
}
// SendPairInstallation sends a pair installation message
func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerResponse, error) {
func (m *Messenger) SendPairInstallation(ctx context.Context, rawMessageHandler RawMessageHandler) (*MessengerResponse, error) {
var err error
var response MessengerResponse
@ -2524,13 +2532,17 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons
Clock: clock,
Name: installation.InstallationMetadata.Name,
InstallationId: installation.ID,
DeviceType: installation.InstallationMetadata.DeviceType}
DeviceType: installation.InstallationMetadata.DeviceType,
Version: installation.Version}
encodedMessage, err := proto.Marshal(pairMessage)
if err != nil {
return nil, err
}
_, err = m.dispatchPairInstallationMessage(ctx, common.RawMessage{
if rawMessageHandler == nil {
rawMessageHandler = m.dispatchPairInstallationMessage
}
_, err = rawMessageHandler(ctx, common.RawMessage{
LocalChatID: chat.ID,
Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION,

View File

@ -84,7 +84,7 @@ func (s *MessengerDeleteMessageForMeSuite) Pair() {
DeviceType: "alice2",
})
s.Require().NoError(err)
response, err := s.alice2.SendPairInstallation(context.Background())
response, err := s.alice2.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -153,17 +153,9 @@ func (m *Messenger) SetSocialLinks(socialLinks *identity.SocialLinks) error {
}
func (m *Messenger) setInstallationHostname() error {
ourInstallation, ok := m.allInstallations.Load(m.installationID)
if !ok {
m.logger.Error("Messenger's installationID is not set or not loadable")
return nil
}
var imd *multidevice.InstallationMetadata
if ourInstallation.InstallationMetadata == nil {
imd = new(multidevice.InstallationMetadata)
} else {
imd = ourInstallation.InstallationMetadata
imd, err := m.getOurInstallationMetadata()
if err != nil {
return err
}
// If the name is already set, don't do anything
@ -178,3 +170,35 @@ func (m *Messenger) setInstallationHostname() error {
imd.Name = fmt.Sprintf("%s %s", hn, imd.Name)
return m.setInstallationMetadata(m.installationID, imd)
}
func (m *Messenger) getOurInstallationMetadata() (*multidevice.InstallationMetadata, error) {
ourInstallation, ok := m.allInstallations.Load(m.installationID)
if !ok {
return nil, fmt.Errorf("messenger's installationID is not set or not loadable")
}
if ourInstallation.InstallationMetadata == nil {
return new(multidevice.InstallationMetadata), nil
}
return ourInstallation.InstallationMetadata, nil
}
func (m *Messenger) SetInstallationDeviceType(deviceType string) error {
if strings.TrimSpace(deviceType) == "" {
return errors.New("device type is empty")
}
imd, err := m.getOurInstallationMetadata()
if err != nil {
return err
}
// If the name is already set, don't do anything
if len(imd.DeviceType) != 0 {
return nil
}
imd.DeviceType = deviceType
return m.setInstallationMetadata(m.installationID, imd)
}

View File

@ -78,7 +78,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)
@ -217,7 +217,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)
@ -311,7 +311,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := bob2.SendPairInstallation(context.Background())
response, err := bob2.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -85,7 +85,7 @@ func (s *MessengerSyncBookmarkSuite) TestSyncBookmark() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -86,7 +86,7 @@ func (s *MessengerSyncChatSuite) Pair() {
DeviceType: "alice2",
})
s.Require().NoError(err)
response, err := s.alice2.SendPairInstallation(context.Background())
response, err := s.alice2.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -76,7 +76,7 @@ func (s *MessengerSyncClearHistory) pair() *Messenger {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)

View File

@ -59,7 +59,7 @@ func (s *MessengerSyncKeycardChangeSuite) SetupTest() {
}
err = s.other.SetInstallationMetadata(s.other.installationID, imOther)
s.Require().NoError(err)
response, err := s.other.SendPairInstallation(context.Background())
response, err := s.other.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)

View File

@ -60,7 +60,7 @@ func (s *MessengerSyncKeycardsStateSuite) SetupTest() {
}
err = s.other.SetInstallationMetadata(s.other.installationID, imOther)
s.Require().NoError(err)
response, err := s.other.SendPairInstallation(context.Background())
response, err := s.other.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)

View File

@ -80,7 +80,7 @@ func (s *MessengerSyncProfilePictureSuite) TestSyncProfilePicture() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -2,16 +2,16 @@ package protocol
import (
"context"
"errors"
"github.com/golang/protobuf/proto"
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/protobuf"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/protocol/protobuf"
)
type RawMessageHandler func(ctx context.Context, rawMessage common.RawMessage) (common.RawMessage, error)
@ -199,6 +199,47 @@ func (m *Messenger) HandleSyncRawMessages(rawMessages []*protobuf.RawMessage) er
m.logger.Error("failed to handleSyncKeycards when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION:
var message protobuf.PairInstallation
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
identity := m.myHexIdentity()
installations := []*multidevice.Installation{
{
Identity: identity,
ID: message.InstallationId,
Version: message.Version,
Enabled: true,
Timestamp: int64(message.Clock),
InstallationMetadata: &multidevice.InstallationMetadata{
DeviceType: message.DeviceType,
Name: message.Name,
},
}}
m.handleInstallations(installations)
// set WhisperTimestamp to pass the validation in HandlePairInstallation
state.CurrentMessageState = &CurrentMessageState{WhisperTimestamp: message.Clock}
err = m.HandlePairInstallation(state, message)
if err != nil {
return err
}
multidevice := m.encryptor.GetMultiDevice()
if multidevice == nil {
return errors.New("multidevice is nil")
}
_, err = multidevice.AddInstallations(m.IdentityPublicKeyCompressed(), int64(message.GetClock()), installations, true)
if err != nil {
return err
}
// if receiver already logged in before local pairing, we need force enable the installation,
// AddInstallations won't make sure enable it, e.g. installation maybe already exist in db but not enabled yet
err = m.EnableInstallation(message.InstallationId)
if err != nil {
return err
}
}
}
response, err := m.saveDataAndPrepareResponse(state)

View File

@ -62,7 +62,7 @@ func (s *MessengerSyncSavedAddressesSuite) SetupTest() {
}
err = s.other.SetInstallationMetadata(s.other.installationID, imOther)
s.Require().NoError(err)
response, err := s.other.SendPairInstallation(context.Background())
response, err := s.other.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)

View File

@ -182,7 +182,7 @@ func (s *MessengerSyncSettingsSuite) newMessenger() *Messenger {
func pairTwoDevices(s *suite.Suite, device1, device2 *Messenger) {
// Send pairing data
response, err := device1.SendPairInstallation(context.Background())
response, err := device1.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Len(response.Chats(), 1)

View File

@ -72,7 +72,7 @@ func (s *MessengerSyncVerificationRequests) TestSyncVerificationRequests() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)
@ -136,7 +136,7 @@ func (s *MessengerSyncVerificationRequests) TestSyncTrust() {
DeviceType: "their-device-type",
})
s.Require().NoError(err)
response, err := theirMessenger.SendPairInstallation(context.Background())
response, err := theirMessenger.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -116,7 +116,7 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
}
err = alicesOtherDevice.SetInstallationMetadata(alicesOtherDevice.installationID, im1)
s.Require().NoError(err)
response, err := alicesOtherDevice.SendPairInstallation(context.Background())
response, err := alicesOtherDevice.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)

View File

@ -647,6 +647,8 @@ type PairInstallation struct {
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
DeviceType string `protobuf:"bytes,3,opt,name=device_type,json=deviceType,proto3" json:"device_type,omitempty"`
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
// following fields used for local pairing
Version uint32 `protobuf:"varint,5,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -705,6 +707,13 @@ func (m *PairInstallation) GetName() string {
return ""
}
func (m *PairInstallation) GetVersion() uint32 {
if m != nil {
return m.Version
}
return 0
}
type SyncInstallationContact struct {
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
@ -2759,168 +2768,168 @@ func init() {
}
var fileDescriptor_d61ab7221f0b5518 = []byte{
// 2596 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x39, 0x4d, 0x73, 0x1b, 0xc7,
0xb1, 0x5e, 0x00, 0xc4, 0x47, 0x03, 0x04, 0xa1, 0x11, 0x2d, 0x41, 0x14, 0x5d, 0x92, 0xd6, 0x76,
0x59, 0xaf, 0xca, 0x8f, 0x7e, 0x4f, 0x4e, 0xe2, 0xc4, 0x1f, 0x65, 0x43, 0x00, 0x62, 0xd1, 0x14,
0x41, 0xd6, 0x90, 0x90, 0x63, 0x57, 0xaa, 0xb6, 0x86, 0xbb, 0x23, 0x62, 0xc3, 0xc5, 0xee, 0x66,
0x67, 0x40, 0x05, 0xb9, 0xe5, 0x92, 0x1f, 0x90, 0x4b, 0x72, 0xf4, 0x39, 0xb9, 0xa5, 0xca, 0x39,
0xe5, 0x90, 0x63, 0x6e, 0x39, 0xe4, 0x92, 0xaa, 0xe4, 0x17, 0xe4, 0x57, 0xa4, 0xa6, 0x67, 0x16,
0xbb, 0x0b, 0x02, 0x8c, 0x54, 0x39, 0xe5, 0xc4, 0xe9, 0xde, 0xee, 0x9e, 0x9e, 0xfe, 0x6e, 0x10,
0x36, 0x63, 0xe6, 0x27, 0x7e, 0x78, 0xbe, 0x17, 0x27, 0x91, 0x8c, 0x48, 0x1d, 0xff, 0x9c, 0xcd,
0x9e, 0xef, 0xdc, 0x14, 0xf3, 0xd0, 0x75, 0x04, 0x97, 0xd2, 0x0f, 0xcf, 0x85, 0xfe, 0xbc, 0x63,
0xb3, 0x38, 0x0e, 0x7c, 0x97, 0x49, 0x3f, 0x0a, 0x9d, 0x29, 0x97, 0xcc, 0x63, 0x92, 0x39, 0x53,
0x2e, 0x04, 0x3b, 0xe7, 0x9a, 0xc6, 0x66, 0x70, 0xf7, 0x87, 0x5c, 0xba, 0x13, 0x3f, 0x3c, 0x7f,
0xcc, 0xdc, 0x0b, 0xee, 0x8d, 0xe3, 0x01, 0x93, 0x6c, 0xc0, 0x25, 0xf3, 0x03, 0x41, 0xee, 0x41,
0x13, 0x99, 0xc2, 0xd9, 0xf4, 0x8c, 0x27, 0x5d, 0xeb, 0xbe, 0xf5, 0x70, 0x93, 0x82, 0x42, 0x8d,
0x10, 0x43, 0x1e, 0x40, 0x4b, 0x46, 0x92, 0x05, 0x29, 0x45, 0x09, 0x29, 0x9a, 0x88, 0xd3, 0x24,
0xf6, 0xdf, 0x2a, 0x50, 0x55, 0xb2, 0x67, 0x31, 0xd9, 0x86, 0x0d, 0x37, 0x88, 0xdc, 0x0b, 0x14,
0x54, 0xa1, 0x1a, 0x20, 0x6d, 0x28, 0xf9, 0x1e, 0x72, 0x36, 0x68, 0xc9, 0xf7, 0xc8, 0xa7, 0x50,
0x77, 0xa3, 0x50, 0x32, 0x57, 0x8a, 0x6e, 0xf9, 0x7e, 0xf9, 0x61, 0xf3, 0xd1, 0x9b, 0x7b, 0xe9,
0x4b, 0xf7, 0x4e, 0xe6, 0xa1, 0xbb, 0x1f, 0x0a, 0xc9, 0x82, 0x00, 0x1f, 0xd6, 0xd7, 0x94, 0xcf,
0x1e, 0xd1, 0x05, 0x13, 0xf9, 0x01, 0x34, 0xdd, 0x68, 0x3a, 0x9d, 0x85, 0xbe, 0xf4, 0xb9, 0xe8,
0x56, 0x50, 0xc6, 0xed, 0xa2, 0x8c, 0xbe, 0x21, 0x98, 0xd3, 0x3c, 0x2d, 0x39, 0x82, 0xad, 0x54,
0x8c, 0xb1, 0x41, 0x77, 0xe3, 0xbe, 0xf5, 0xb0, 0xf9, 0xe8, 0xed, 0x8c, 0xfd, 0x1a, 0x83, 0xd1,
0x65, 0x6e, 0x32, 0x06, 0x92, 0x93, 0x9f, 0xca, 0xac, 0xbe, 0x8a, 0xcc, 0x15, 0x02, 0xc8, 0xfb,
0x50, 0x8b, 0x93, 0xe8, 0xb9, 0x1f, 0xf0, 0x6e, 0x0d, 0x65, 0xdd, 0xc9, 0x64, 0xa5, 0x32, 0x8e,
0x35, 0x01, 0x4d, 0x29, 0xc9, 0x21, 0xb4, 0xcd, 0x31, 0xd5, 0xa3, 0xfe, 0x2a, 0x7a, 0x2c, 0x31,
0x93, 0xf7, 0xa0, 0x66, 0x22, 0xae, 0xdb, 0x40, 0x39, 0xaf, 0x17, 0x4d, 0x7c, 0xa2, 0x3f, 0xd2,
0x94, 0x4a, 0x19, 0x37, 0x0d, 0xd1, 0x54, 0x01, 0x78, 0x25, 0xe3, 0x2e, 0x71, 0xdb, 0x7f, 0xac,
0x40, 0xeb, 0x70, 0x16, 0x48, 0xbf, 0xe7, 0xba, 0xd1, 0x2c, 0x94, 0x84, 0x40, 0x25, 0x64, 0x53,
0x8e, 0xf1, 0xd5, 0xa0, 0x78, 0x26, 0xbb, 0xd0, 0x90, 0xfe, 0x94, 0x0b, 0xc9, 0xa6, 0x31, 0x46,
0x59, 0x99, 0x66, 0x08, 0xf5, 0xd5, 0xf7, 0x78, 0x28, 0x7d, 0x37, 0x0a, 0xbb, 0x65, 0x64, 0xcb,
0x10, 0xe4, 0x33, 0x00, 0x37, 0x0a, 0xa2, 0xc4, 0x99, 0x30, 0x31, 0x31, 0x81, 0xf4, 0x20, 0x53,
0x36, 0x7f, 0xf7, 0x5e, 0x3f, 0x0a, 0xa2, 0x59, 0xf2, 0x84, 0x89, 0x09, 0x6d, 0x20, 0x93, 0x3a,
0x92, 0x2e, 0xd4, 0x10, 0xd8, 0xf7, 0x30, 0x90, 0xca, 0x34, 0x05, 0xc9, 0x3b, 0xb0, 0x75, 0xc1,
0xe7, 0x2e, 0x4b, 0x3c, 0xc7, 0xa4, 0x35, 0x86, 0x45, 0x83, 0xb6, 0x0d, 0xfa, 0x58, 0x63, 0xc9,
0x6d, 0xa8, 0x5d, 0xf0, 0xb9, 0x33, 0xf3, 0x3d, 0xf4, 0x75, 0x83, 0x56, 0x2f, 0xf8, 0x7c, 0xec,
0x7b, 0xe4, 0x63, 0xa8, 0xfa, 0x53, 0x76, 0xce, 0x95, 0x1f, 0x95, 0x66, 0x6f, 0xad, 0xd1, 0x6c,
0x1f, 0xdf, 0x23, 0xe7, 0xfb, 0x8a, 0x98, 0x1a, 0x9e, 0x1d, 0x1b, 0x20, 0x53, 0x59, 0xa5, 0xa6,
0x1f, 0x7a, 0xfc, 0x67, 0x5d, 0xeb, 0x7e, 0xf9, 0x61, 0x99, 0x6a, 0x60, 0xe7, 0xef, 0x16, 0x6c,
0x16, 0xb8, 0xf3, 0xca, 0x58, 0x05, 0x65, 0x52, 0xd3, 0x97, 0x72, 0xa6, 0xef, 0x42, 0x2d, 0x66,
0xf3, 0x20, 0x62, 0x1e, 0x9a, 0xb6, 0x45, 0x53, 0x50, 0x5d, 0xf7, 0xc2, 0xf7, 0xa4, 0xb2, 0xa9,
0x32, 0x8a, 0x06, 0xc8, 0x2d, 0xa8, 0x4e, 0xb8, 0x7f, 0x3e, 0x91, 0xc6, 0x56, 0x06, 0x22, 0x3b,
0x50, 0x57, 0x81, 0x27, 0xfc, 0x9f, 0x73, 0xb4, 0x51, 0x99, 0x2e, 0x60, 0xf2, 0x26, 0x6c, 0x26,
0x78, 0x72, 0x24, 0x4b, 0xce, 0xb9, 0x44, 0x1b, 0x95, 0x69, 0x4b, 0x23, 0x4f, 0x11, 0x97, 0x15,
0x9e, 0x7a, 0xae, 0xf0, 0xd8, 0x7f, 0xb5, 0xe0, 0xe6, 0xd3, 0xc8, 0x65, 0x81, 0xb1, 0xf4, 0xb1,
0x51, 0xee, 0xbb, 0x50, 0xb9, 0xe0, 0x73, 0x81, 0xa6, 0x28, 0xf8, 0x7b, 0x05, 0xf1, 0xde, 0x01,
0x9f, 0x53, 0x24, 0x27, 0x1f, 0x42, 0x6b, 0xaa, 0xcc, 0xce, 0xb4, 0xd9, 0xd1, 0x12, 0xcd, 0x47,
0xb7, 0x56, 0x3b, 0x85, 0x16, 0x68, 0xd5, 0x0b, 0x63, 0x26, 0xc4, 0x8b, 0x28, 0xf1, 0x4c, 0x14,
0x2e, 0xe0, 0x9d, 0xff, 0x85, 0xf2, 0x01, 0x9f, 0xaf, 0x8c, 0x6d, 0x02, 0x15, 0x55, 0x8c, 0xf1,
0xaa, 0x16, 0xc5, 0xb3, 0xfd, 0x4b, 0x0b, 0x3a, 0x4a, 0xc7, 0x7c, 0x95, 0x5c, 0x53, 0x79, 0xdf,
0x81, 0x2d, 0x3f, 0x47, 0xe5, 0x2c, 0xca, 0x70, 0x3b, 0x8f, 0xde, 0xf7, 0xb0, 0x0f, 0xf0, 0x4b,
0xdf, 0xe5, 0x8e, 0x9c, 0xc7, 0xdc, 0x68, 0x08, 0x1a, 0x75, 0x3a, 0x8f, 0xf9, 0x42, 0xb9, 0x4a,
0xa6, 0x9c, 0xfd, 0x4f, 0x0b, 0x6e, 0xaf, 0x29, 0xd7, 0x2f, 0xd9, 0x09, 0xde, 0x84, 0x4d, 0x53,
0x73, 0x1c, 0x0c, 0x5a, 0x73, 0x71, 0xcb, 0x20, 0x75, 0x44, 0xde, 0x81, 0x3a, 0x0f, 0x85, 0x93,
0xbb, 0xbe, 0xc6, 0x43, 0x31, 0x52, 0xe6, 0x79, 0x00, 0xad, 0x80, 0x09, 0xe9, 0xcc, 0x62, 0x8f,
0x49, 0xae, 0x33, 0xb0, 0x42, 0x9b, 0x0a, 0x37, 0xd6, 0x28, 0xf5, 0x32, 0x31, 0x17, 0x92, 0x4f,
0x1d, 0xc9, 0xce, 0x55, 0x61, 0x2e, 0xab, 0x97, 0x69, 0xd4, 0x29, 0x3b, 0x17, 0xe4, 0x6d, 0x68,
0x07, 0xca, 0xed, 0x4e, 0xe8, 0xbb, 0x17, 0x78, 0x89, 0x4e, 0xc2, 0x4d, 0xc4, 0x8e, 0x0c, 0xd2,
0xfe, 0x45, 0x15, 0xee, 0xac, 0xed, 0x4d, 0xe4, 0xff, 0x60, 0x3b, 0xaf, 0x88, 0x83, 0xbc, 0xc1,
0xdc, 0xbc, 0x9e, 0xe4, 0x14, 0x7a, 0xaa, 0xbf, 0xfc, 0x17, 0x9b, 0x42, 0xf9, 0x96, 0x79, 0x1e,
0xf7, 0xb0, 0x2b, 0xd4, 0xa9, 0x06, 0x54, 0x2d, 0x38, 0x53, 0x4e, 0xe6, 0x1e, 0x16, 0xfd, 0x3a,
0x4d, 0x41, 0x45, 0x3f, 0x9d, 0x29, 0x9d, 0x9a, 0x9a, 0x1e, 0x01, 0x45, 0x9f, 0xf0, 0x69, 0x74,
0xc9, 0xbd, 0x6e, 0x4b, 0xd3, 0x1b, 0x90, 0xdc, 0x87, 0xd6, 0x84, 0x09, 0x07, 0xc5, 0x3a, 0x33,
0xd1, 0xdd, 0xc4, 0xcf, 0x30, 0x61, 0xa2, 0xa7, 0x50, 0x63, 0xd5, 0x99, 0x6e, 0x5e, 0xf2, 0xc4,
0x7f, 0x9e, 0x0e, 0x3f, 0x42, 0x32, 0x39, 0x13, 0xdd, 0x36, 0x56, 0x06, 0x92, 0xff, 0x74, 0x82,
0x5f, 0x70, 0x8c, 0x49, 0x66, 0x42, 0xa6, 0x94, 0x5b, 0x48, 0xd9, 0x44, 0x9c, 0x21, 0xf9, 0x04,
0xee, 0x9a, 0xde, 0xee, 0x24, 0xfc, 0xa7, 0x33, 0x2e, 0xa4, 0xf6, 0x22, 0xb2, 0xf0, 0x6e, 0x07,
0x39, 0xba, 0x86, 0x84, 0x6a, 0x0a, 0x74, 0xa6, 0xe2, 0xe7, 0xeb, 0xd9, 0x75, 0x1a, 0xdc, 0x58,
0xcb, 0xde, 0xc7, 0xcc, 0xf8, 0x14, 0x76, 0x97, 0xd9, 0x95, 0x39, 0x24, 0x37, 0xd7, 0x13, 0xe4,
0xbf, 0x53, 0xe4, 0xa7, 0x48, 0xa1, 0xef, 0x5f, 0x2f, 0x40, 0x2b, 0x70, 0x73, 0xbd, 0x00, 0xad,
0xc1, 0x03, 0x68, 0x79, 0xbe, 0x88, 0x03, 0x36, 0xd7, 0xf1, 0xb5, 0x8d, 0xae, 0x6f, 0x1a, 0x9c,
0x8a, 0x31, 0xfb, 0xc5, 0xd5, 0x7c, 0x4f, 0x1b, 0xf3, 0xea, 0x7c, 0xbf, 0x12, 0xd4, 0xa5, 0x15,
0x41, 0xbd, 0x1c, 0xb9, 0xe5, 0x2b, 0x91, 0x6b, 0x3f, 0x86, 0x9d, 0xe5, 0x8b, 0x8f, 0x67, 0x67,
0x81, 0xef, 0xf6, 0x27, 0xec, 0x25, 0x6b, 0x8d, 0xfd, 0x6d, 0x19, 0x36, 0x0b, 0x83, 0xe1, 0xbf,
0xe5, 0x6b, 0x61, 0x62, 0xde, 0x83, 0x66, 0x9c, 0xf8, 0x97, 0x4c, 0x72, 0xe7, 0x82, 0xcf, 0x4d,
0x9f, 0x03, 0x83, 0x52, 0x75, 0xfb, 0xbe, 0xaa, 0x9d, 0xc2, 0x4d, 0xfc, 0x58, 0xe9, 0x85, 0x79,
0xd9, 0xa2, 0x79, 0x94, 0x6a, 0x7b, 0x3f, 0x89, 0xfc, 0xd0, 0x64, 0x65, 0x9d, 0x1a, 0x48, 0x35,
0x05, 0x1d, 0xab, 0xdc, 0xc3, 0xb6, 0x57, 0xa7, 0x0b, 0x38, 0x4b, 0x9a, 0x5a, 0x3e, 0x69, 0x8e,
0xa0, 0x63, 0xbc, 0x2b, 0x1c, 0x19, 0x39, 0x4a, 0x8e, 0x99, 0x0d, 0xde, 0x5e, 0x37, 0xfe, 0x1a,
0xf2, 0xd3, 0xe8, 0x8b, 0xc8, 0x0f, 0x69, 0x3b, 0x29, 0xc0, 0xe4, 0x23, 0xa8, 0xa7, 0x43, 0x97,
0x19, 0xf2, 0xee, 0xad, 0x11, 0x64, 0xa6, 0x3d, 0x41, 0x17, 0x0c, 0x6a, 0xb6, 0xe2, 0xa1, 0x9b,
0xcc, 0x63, 0xb9, 0x48, 0xfa, 0x0c, 0xa1, 0xbe, 0x8a, 0x98, 0xbb, 0x92, 0x65, 0xa9, 0x9f, 0x21,
0x54, 0x6b, 0x32, 0xa4, 0x2a, 0x81, 0xb1, 0x1d, 0xb7, 0xd0, 0x72, 0xed, 0x0c, 0x7d, 0xc0, 0xe7,
0xc2, 0xfe, 0x8b, 0x05, 0x77, 0xaf, 0x79, 0x91, 0xf1, 0x97, 0xb5, 0xf0, 0xd7, 0x1b, 0x00, 0x31,
0xc6, 0x06, 0xba, 0x4b, 0xfb, 0xbf, 0xa1, 0x31, 0xca, 0x5b, 0x0b, 0xa7, 0x97, 0xf3, 0x4e, 0xbf,
0xa6, 0xb0, 0xde, 0x86, 0x9a, 0x3b, 0x61, 0x52, 0xf5, 0xce, 0x0d, 0x3d, 0x10, 0x29, 0x70, 0xdf,
0x53, 0x71, 0x9b, 0x0e, 0xee, 0x73, 0xf5, 0xb5, 0xaa, 0x1d, 0xbf, 0xc0, 0xed, 0xa3, 0x13, 0x75,
0xfa, 0xd6, 0xf4, 0x65, 0x08, 0xd8, 0xbf, 0x2a, 0x41, 0x67, 0x39, 0x9c, 0xc9, 0x27, 0xb9, 0xa5,
0xe8, 0xca, 0x5c, 0xb2, 0xa6, 0xf1, 0xe4, 0x56, 0xa2, 0xcf, 0xa1, 0x65, 0x5e, 0xad, 0xb4, 0x13,
0xdd, 0xd2, 0xf2, 0xc0, 0xb8, 0x3e, 0x7f, 0x68, 0x33, 0x5e, 0x9c, 0x05, 0xf9, 0x08, 0x6a, 0xe9,
0x7c, 0x53, 0xc6, 0x78, 0xb8, 0x46, 0x8d, 0x74, 0xd4, 0x49, 0x39, 0xfe, 0x83, 0xc5, 0xcc, 0xfe,
0x00, 0xb6, 0xf0, 0xab, 0x52, 0xc8, 0xf4, 0x81, 0x97, 0xcb, 0xeb, 0x8f, 0x61, 0x3b, 0x65, 0x3c,
0xd4, 0xab, 0xaf, 0xa0, 0x9c, 0xbd, 0x2c, 0xf7, 0x67, 0x70, 0x4b, 0x71, 0xf7, 0x5c, 0xe9, 0x5f,
0xfa, 0x72, 0xde, 0xe7, 0xa1, 0xe4, 0xc9, 0x35, 0xfc, 0x1d, 0x28, 0xfb, 0x9e, 0x36, 0x6f, 0x8b,
0xaa, 0xa3, 0x3d, 0xd0, 0xb5, 0xa9, 0x28, 0xa1, 0xe7, 0xba, 0x1c, 0x93, 0xe0, 0x65, 0xa5, 0x0c,
0x75, 0x90, 0x17, 0xa5, 0x0c, 0x7c, 0x31, 0xf5, 0x85, 0x78, 0x05, 0x31, 0xdf, 0x58, 0xd0, 0x52,
0x72, 0x1e, 0x47, 0xd1, 0xc5, 0x94, 0x25, 0x17, 0xeb, 0x19, 0x67, 0x49, 0x60, 0xcc, 0xa0, 0x8e,
0x8b, 0xf9, 0xae, 0x9c, 0x1b, 0x3e, 0xef, 0x42, 0x03, 0xab, 0xb6, 0xa3, 0x68, 0x75, 0x56, 0xd4,
0x11, 0x31, 0x4e, 0x82, 0x7c, 0xfb, 0xde, 0x28, 0xb6, 0xef, 0x37, 0x00, 0x3c, 0x1e, 0x70, 0x35,
0x06, 0x31, 0x89, 0x59, 0x51, 0xa1, 0x0d, 0x83, 0xe9, 0x49, 0xfb, 0x0b, 0x1d, 0xfc, 0xfd, 0x80,
0xb3, 0xe4, 0x89, 0x2f, 0x64, 0x94, 0xcc, 0xf3, 0x39, 0x66, 0x15, 0x72, 0xec, 0x0d, 0x00, 0x57,
0x11, 0x6a, 0x59, 0x25, 0x2d, 0xcb, 0x60, 0x7a, 0xd2, 0xfe, 0xb3, 0x05, 0x44, 0x09, 0x33, 0x9b,
0xf0, 0xb1, 0xef, 0xca, 0x59, 0xc2, 0x57, 0x4e, 0xd2, 0xb9, 0x55, 0xa5, 0xb4, 0x66, 0x55, 0x29,
0xe3, 0x6f, 0x1b, 0x57, 0x56, 0x95, 0x0a, 0xa2, 0xd3, 0x55, 0xe5, 0x2e, 0x34, 0xb0, 0x9f, 0xe1,
0xae, 0xb2, 0x81, 0x9f, 0x70, 0x57, 0x39, 0x59, 0xb9, 0xab, 0x54, 0x91, 0x60, 0xcd, 0xae, 0x52,
0xcb, 0xef, 0x2a, 0x13, 0xb8, 0x79, 0xf5, 0x25, 0x62, 0xfd, 0x3a, 0xf6, 0x7d, 0xa8, 0xc7, 0x86,
0xc8, 0x24, 0xfb, 0x6e, 0x31, 0xcf, 0x8a, 0x92, 0xe8, 0x82, 0xda, 0xfe, 0x5d, 0x09, 0x6e, 0x28,
0x82, 0x2f, 0x59, 0x10, 0x70, 0x79, 0x7d, 0x03, 0xef, 0x42, 0x8d, 0x79, 0x5e, 0xc2, 0x85, 0x48,
0xad, 0x66, 0x40, 0x65, 0x9f, 0x17, 0x28, 0x00, 0xcd, 0x56, 0xa7, 0x06, 0x52, 0xb6, 0x57, 0xbe,
0x43, 0xab, 0xd5, 0x29, 0x9e, 0x15, 0x0e, 0xd7, 0x0a, 0x5d, 0x3f, 0xf1, 0xac, 0x24, 0x2b, 0xdf,
0xab, 0xa1, 0x40, 0x6f, 0xc5, 0x29, 0xa8, 0xa8, 0x63, 0x26, 0x27, 0x66, 0xf6, 0xc4, 0xb3, 0xea,
0x25, 0x8b, 0x12, 0x8e, 0x3b, 0x5e, 0x2b, 0x5f, 0xd3, 0x53, 0x7f, 0x37, 0x72, 0xfe, 0x56, 0xef,
0x51, 0x8b, 0x38, 0xf6, 0xa5, 0x06, 0xd5, 0x00, 0x7a, 0xd5, 0xf7, 0x3c, 0x1e, 0x9a, 0x86, 0x64,
0xa0, 0xf5, 0xc3, 0xa8, 0x7d, 0xa8, 0x23, 0xac, 0x60, 0x2c, 0x41, 0x3e, 0x80, 0xba, 0xa9, 0x79,
0x69, 0xb5, 0xbe, 0x5b, 0xb4, 0x7e, 0x81, 0x9e, 0x2e, 0x88, 0xed, 0x3f, 0x58, 0x3a, 0xfc, 0x4f,
0xd8, 0x25, 0xf7, 0x7a, 0xc6, 0x96, 0x39, 0x2b, 0x5b, 0x45, 0x2b, 0xaf, 0x5a, 0xba, 0x77, 0xa1,
0xf1, 0x9c, 0x5d, 0x46, 0xb3, 0xc4, 0x97, 0xdc, 0x18, 0x3f, 0x43, 0xa8, 0x4e, 0xe6, 0x4e, 0x98,
0x8f, 0xbb, 0x5e, 0x05, 0x5d, 0x59, 0x43, 0x78, 0xdf, 0xbb, 0x26, 0x65, 0x1f, 0x40, 0x4b, 0x4f,
0x5f, 0x4e, 0x3e, 0x32, 0x9b, 0x1a, 0x87, 0xe3, 0xa1, 0xfd, 0x6b, 0x0b, 0x5e, 0x5f, 0x39, 0x0f,
0xac, 0x89, 0x9c, 0xe5, 0xee, 0xa8, 0x5f, 0x50, 0xe8, 0x8e, 0x43, 0xb8, 0x37, 0xd1, 0x05, 0xc0,
0x61, 0x89, 0x3b, 0xf1, 0x2f, 0xb9, 0x23, 0x66, 0x71, 0x1c, 0x25, 0xd2, 0xe1, 0x21, 0x3b, 0x0b,
0xcc, 0x2c, 0x58, 0xa7, 0xbb, 0x86, 0xac, 0xa7, 0xa9, 0x4e, 0x34, 0xd1, 0x50, 0xd3, 0xd8, 0xbf,
0xb7, 0x74, 0xeb, 0x38, 0x55, 0xc3, 0xbc, 0x5a, 0x0f, 0x78, 0xf2, 0x92, 0xeb, 0xe7, 0x27, 0x50,
0x35, 0xfb, 0x80, 0xba, 0xa7, 0xbd, 0x3c, 0x43, 0xe5, 0x04, 0xee, 0x9d, 0x66, 0x9b, 0x02, 0x35,
0x4c, 0xf6, 0x87, 0xd0, 0xcc, 0xa1, 0x49, 0x13, 0x6a, 0xe3, 0xd1, 0xc1, 0xe8, 0xe8, 0xcb, 0x51,
0xe7, 0x35, 0x05, 0x9c, 0xd2, 0xf1, 0xc9, 0xe9, 0x70, 0xd0, 0xb1, 0xc8, 0x0d, 0xd8, 0x1c, 0x8f,
0x10, 0xfc, 0xf2, 0x88, 0x9e, 0x3e, 0xf9, 0xaa, 0x53, 0xb2, 0xbf, 0x29, 0xeb, 0x59, 0xfa, 0x59,
0x6e, 0x57, 0x31, 0x83, 0xcd, 0x1a, 0xe5, 0x09, 0x54, 0x9e, 0x27, 0xd1, 0x34, 0x0d, 0x05, 0x75,
0x56, 0x0f, 0x92, 0x91, 0xa9, 0xd9, 0x25, 0x19, 0xa9, 0xd0, 0x70, 0x27, 0x2a, 0xf2, 0xc2, 0xf3,
0x74, 0x8e, 0xc9, 0x10, 0xca, 0x25, 0x66, 0xfa, 0xd3, 0xe5, 0xd4, 0xac, 0x88, 0x0b, 0x5c, 0x0f,
0x7f, 0xa6, 0x48, 0xb8, 0x88, 0xa3, 0x50, 0xa4, 0x69, 0xb9, 0x80, 0x55, 0x2d, 0x4e, 0x78, 0x1c,
0xf8, 0x9a, 0x59, 0x87, 0x48, 0xc3, 0x60, 0x7a, 0x92, 0xf0, 0xd5, 0x3b, 0x59, 0x1d, 0x2d, 0xfb,
0x9d, 0xa2, 0x65, 0x57, 0xbc, 0x7a, 0xef, 0xd9, 0x95, 0xad, 0x6d, 0xe5, 0x26, 0xa7, 0x7d, 0xd8,
0x58, 0x34, 0xf0, 0x1f, 0x01, 0xb9, 0xca, 0x79, 0xc5, 0x17, 0xc7, 0xc3, 0xd1, 0x60, 0x7f, 0xf4,
0x79, 0xc7, 0x22, 0x2d, 0xa8, 0xf7, 0xfa, 0xfd, 0xe1, 0xb1, 0xf2, 0x4c, 0x49, 0x41, 0x83, 0x61,
0xff, 0xe9, 0xfe, 0x68, 0x38, 0xe8, 0x94, 0x15, 0xd4, 0xef, 0x8d, 0xfa, 0xc3, 0xa7, 0xc3, 0x41,
0xa7, 0x62, 0xff, 0xc3, 0xd2, 0x9d, 0xbd, 0x5f, 0x58, 0x99, 0x06, 0xdc, 0xf5, 0xc5, 0xfa, 0x5f,
0x5c, 0x76, 0xa1, 0x61, 0xec, 0xb9, 0x9f, 0x46, 0x5a, 0x86, 0x20, 0x3f, 0x86, 0x2d, 0xcf, 0xf0,
0x3b, 0x85, 0xc8, 0x7b, 0x7f, 0x79, 0x46, 0x5a, 0x75, 0xe5, 0x5e, 0x7a, 0x30, 0xe6, 0x69, 0x7b,
0x05, 0xd8, 0x7e, 0x17, 0xda, 0x45, 0x8a, 0xc2, 0x63, 0x5f, 0x2b, 0x3c, 0xd6, 0xb2, 0xbf, 0xb5,
0x60, 0x6b, 0xe9, 0x97, 0xe4, 0xf5, 0xdd, 0x66, 0x79, 0x39, 0x2c, 0x5d, 0x59, 0x0e, 0xc9, 0xbb,
0x40, 0xf2, 0x24, 0x4e, 0x7e, 0xca, 0xee, 0xe4, 0x08, 0xf5, 0xb6, 0x99, 0x6f, 0x5f, 0x95, 0x57,
0x6a, 0x5f, 0x02, 0x80, 0xb2, 0x17, 0x66, 0xd4, 0xcb, 0xb7, 0x75, 0xab, 0xd8, 0xd6, 0x0f, 0xa0,
0x69, 0xfe, 0x15, 0x72, 0xaa, 0x7a, 0x4f, 0x09, 0xed, 0xfc, 0x3f, 0xd9, 0x25, 0xbd, 0xec, 0x9f,
0x27, 0x87, 0xe6, 0x7f, 0x27, 0x46, 0xe8, 0x9e, 0x62, 0xa0, 0x79, 0x6e, 0xfb, 0xb7, 0x16, 0xb4,
0x95, 0x56, 0xb9, 0x9b, 0xbf, 0x07, 0xcd, 0x64, 0x01, 0xa5, 0x5d, 0x60, 0x3b, 0x93, 0x9f, 0x91,
0xd2, 0x3c, 0x21, 0x79, 0x04, 0xdb, 0x62, 0x76, 0x96, 0x76, 0x92, 0x2f, 0x44, 0x14, 0x3e, 0x9e,
0x4b, 0x9e, 0xf6, 0xd7, 0x95, 0xdf, 0xc8, 0xbb, 0x70, 0x23, 0x5d, 0xba, 0x32, 0x06, 0xbd, 0x89,
0x5e, 0xfd, 0x60, 0xff, 0xc6, 0x82, 0xa6, 0x52, 0xf6, 0x40, 0xff, 0xcc, 0x8c, 0xd3, 0xde, 0xc2,
0xa3, 0xea, 0xb8, 0xb2, 0xad, 0xdc, 0x82, 0xaa, 0xf9, 0xf9, 0xc6, 0x34, 0x74, 0xf3, 0xeb, 0x4d,
0x2e, 0x26, 0x2a, 0x85, 0x98, 0xd8, 0x85, 0x86, 0x69, 0x53, 0x5c, 0x74, 0x37, 0x70, 0x06, 0xcd,
0x10, 0x59, 0x7a, 0x54, 0xf3, 0x53, 0xce, 0x9f, 0xcc, 0xec, 0x61, 0x54, 0x53, 0xe3, 0x6e, 0x14,
0x92, 0x0f, 0xa1, 0xca, 0xf0, 0x84, 0x3a, 0xb6, 0x1f, 0xd9, 0xc5, 0x50, 0x28, 0x10, 0xef, 0xe9,
0x3f, 0xd4, 0x70, 0x90, 0xb7, 0x60, 0x33, 0x0a, 0x3c, 0x43, 0x32, 0x5e, 0x94, 0xf7, 0x22, 0x92,
0xbc, 0x87, 0x8f, 0x50, 0x90, 0xd9, 0x6a, 0x5e, 0x5f, 0x79, 0x05, 0x4d, 0xa9, 0x54, 0xbb, 0xab,
0x1a, 0xed, 0x6e, 0xc0, 0xe6, 0xc1, 0xf0, 0xab, 0x7e, 0x8f, 0x0e, 0x9c, 0xde, 0x60, 0x80, 0x99,
0x44, 0xa0, 0xdd, 0xeb, 0xf7, 0x8f, 0xc6, 0xa3, 0xd3, 0x13, 0x83, 0xb3, 0xc8, 0x4d, 0xd8, 0x4a,
0xc9, 0x06, 0xc3, 0xa7, 0x43, 0x5d, 0x5f, 0xb6, 0xa1, 0xb3, 0x20, 0xa4, 0xc3, 0xc3, 0xa3, 0x67,
0x58, 0x67, 0x00, 0xaa, 0x4f, 0x8f, 0xfa, 0x07, 0xaa, 0xca, 0xa8, 0xa4, 0x1c, 0x8f, 0x0c, 0xb4,
0x41, 0xb6, 0xa0, 0x39, 0xde, 0x1f, 0x38, 0xe3, 0xe3, 0x41, 0x4f, 0x09, 0xa8, 0x92, 0x0e, 0xb4,
0x46, 0xbd, 0xc3, 0xa1, 0xd3, 0x7f, 0xd2, 0x1b, 0x7d, 0x3e, 0x1c, 0x74, 0x6a, 0xf6, 0xd7, 0xba,
0xdb, 0xf5, 0x82, 0xc0, 0x28, 0x2d, 0xc8, 0xff, 0x43, 0xdd, 0xe8, 0x9d, 0xc6, 0xe1, 0x9a, 0xe7,
0x2d, 0xc8, 0x32, 0xf7, 0x94, 0x72, 0xee, 0x79, 0xbc, 0xf9, 0x75, 0x73, 0xef, 0xbd, 0x8f, 0x52,
0xd6, 0xb3, 0x2a, 0x9e, 0xde, 0xff, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xde, 0xb0, 0x84,
0x97, 0x1c, 0x00, 0x00,
// 2602 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x39, 0x4b, 0x73, 0x1b, 0xc7,
0xd1, 0x5e, 0x00, 0xc2, 0xa3, 0xf1, 0x20, 0x34, 0xa2, 0x25, 0x88, 0xa2, 0x4b, 0xd2, 0xda, 0x2e,
0xeb, 0xab, 0xf2, 0x47, 0x27, 0x72, 0x12, 0x27, 0x7e, 0x94, 0x0d, 0x01, 0x88, 0x05, 0x53, 0x04,
0x59, 0x43, 0x42, 0x8e, 0x5d, 0xa9, 0xda, 0x1a, 0xee, 0x8e, 0x88, 0x0d, 0x17, 0xbb, 0x9b, 0x9d,
0x01, 0x15, 0xe4, 0x96, 0xbf, 0x90, 0x4b, 0x72, 0xf4, 0x39, 0xb9, 0xa5, 0xca, 0x39, 0xe5, 0x90,
0x63, 0x6e, 0x39, 0xe4, 0x92, 0xaa, 0xe4, 0x17, 0xe4, 0x57, 0xa4, 0xa6, 0x67, 0x16, 0xd8, 0x05,
0x01, 0x86, 0xaa, 0x9c, 0x72, 0xe2, 0x74, 0x6f, 0x77, 0x4f, 0x4f, 0xbf, 0x1b, 0x84, 0x66, 0xcc,
0xfc, 0xc4, 0x0f, 0xcf, 0xf6, 0xe2, 0x24, 0x92, 0x11, 0xa9, 0xe2, 0x9f, 0xd3, 0xd9, 0x8b, 0x9d,
0x5b, 0x62, 0x1e, 0xba, 0x8e, 0xe0, 0x52, 0xfa, 0xe1, 0x99, 0xd0, 0x9f, 0x77, 0x6c, 0x16, 0xc7,
0x81, 0xef, 0x32, 0xe9, 0x47, 0xa1, 0x33, 0xe5, 0x92, 0x79, 0x4c, 0x32, 0x67, 0xca, 0x85, 0x60,
0x67, 0x5c, 0xd3, 0xd8, 0x0c, 0xee, 0xfd, 0x98, 0x4b, 0x77, 0xe2, 0x87, 0x67, 0x4f, 0x98, 0x7b,
0xce, 0xbd, 0x71, 0xdc, 0x67, 0x92, 0xf5, 0xb9, 0x64, 0x7e, 0x20, 0xc8, 0x7d, 0xa8, 0x23, 0x53,
0x38, 0x9b, 0x9e, 0xf2, 0xa4, 0x63, 0x3d, 0xb0, 0x1e, 0x35, 0x29, 0x28, 0xd4, 0x08, 0x31, 0xe4,
0x21, 0x34, 0x64, 0x24, 0x59, 0x90, 0x52, 0x14, 0x90, 0xa2, 0x8e, 0x38, 0x4d, 0x62, 0xff, 0xbd,
0x04, 0x65, 0x25, 0x7b, 0x16, 0x93, 0x6d, 0xb8, 0xe1, 0x06, 0x91, 0x7b, 0x8e, 0x82, 0x4a, 0x54,
0x03, 0xa4, 0x05, 0x05, 0xdf, 0x43, 0xce, 0x1a, 0x2d, 0xf8, 0x1e, 0xf9, 0x14, 0xaa, 0x6e, 0x14,
0x4a, 0xe6, 0x4a, 0xd1, 0x29, 0x3e, 0x28, 0x3e, 0xaa, 0x3f, 0x7e, 0x73, 0x2f, 0x7d, 0xe9, 0xde,
0xf1, 0x3c, 0x74, 0x87, 0xa1, 0x90, 0x2c, 0x08, 0xf0, 0x61, 0x3d, 0x4d, 0xf9, 0xfc, 0x31, 0x5d,
0x30, 0x91, 0x1f, 0x41, 0xdd, 0x8d, 0xa6, 0xd3, 0x59, 0xe8, 0x4b, 0x9f, 0x8b, 0x4e, 0x09, 0x65,
0xdc, 0xc9, 0xcb, 0xe8, 0x19, 0x82, 0x39, 0xcd, 0xd2, 0x92, 0x43, 0xd8, 0x4a, 0xc5, 0x18, 0x1b,
0x74, 0x6e, 0x3c, 0xb0, 0x1e, 0xd5, 0x1f, 0xbf, 0xbd, 0x64, 0xbf, 0xc2, 0x60, 0x74, 0x95, 0x9b,
0x8c, 0x81, 0x64, 0xe4, 0xa7, 0x32, 0xcb, 0xaf, 0x22, 0x73, 0x8d, 0x00, 0xf2, 0x3e, 0x54, 0xe2,
0x24, 0x7a, 0xe1, 0x07, 0xbc, 0x53, 0x41, 0x59, 0x77, 0x97, 0xb2, 0x52, 0x19, 0x47, 0x9a, 0x80,
0xa6, 0x94, 0xe4, 0x00, 0x5a, 0xe6, 0x98, 0xea, 0x51, 0x7d, 0x15, 0x3d, 0x56, 0x98, 0xc9, 0x7b,
0x50, 0x31, 0x11, 0xd7, 0xa9, 0xa1, 0x9c, 0xd7, 0xf3, 0x26, 0x3e, 0xd6, 0x1f, 0x69, 0x4a, 0xa5,
0x8c, 0x9b, 0x86, 0x68, 0xaa, 0x00, 0xbc, 0x92, 0x71, 0x57, 0xb8, 0xed, 0x3f, 0x95, 0xa0, 0x71,
0x30, 0x0b, 0xa4, 0xdf, 0x75, 0xdd, 0x68, 0x16, 0x4a, 0x42, 0xa0, 0x14, 0xb2, 0x29, 0xc7, 0xf8,
0xaa, 0x51, 0x3c, 0x93, 0x5d, 0xa8, 0x49, 0x7f, 0xca, 0x85, 0x64, 0xd3, 0x18, 0xa3, 0xac, 0x48,
0x97, 0x08, 0xf5, 0xd5, 0xf7, 0x78, 0x28, 0x7d, 0x37, 0x0a, 0x3b, 0x45, 0x64, 0x5b, 0x22, 0xc8,
0x67, 0x00, 0x6e, 0x14, 0x44, 0x89, 0x33, 0x61, 0x62, 0x62, 0x02, 0xe9, 0xe1, 0x52, 0xd9, 0xec,
0xdd, 0x7b, 0xbd, 0x28, 0x88, 0x66, 0xc9, 0x53, 0x26, 0x26, 0xb4, 0x86, 0x4c, 0xea, 0x48, 0x3a,
0x50, 0x41, 0x60, 0xe8, 0x61, 0x20, 0x15, 0x69, 0x0a, 0x92, 0x77, 0x60, 0xeb, 0x9c, 0xcf, 0x5d,
0x96, 0x78, 0x8e, 0x49, 0x6b, 0x0c, 0x8b, 0x1a, 0x6d, 0x19, 0xf4, 0x91, 0xc6, 0x92, 0x3b, 0x50,
0x39, 0xe7, 0x73, 0x67, 0xe6, 0x7b, 0xe8, 0xeb, 0x1a, 0x2d, 0x9f, 0xf3, 0xf9, 0xd8, 0xf7, 0xc8,
0xc7, 0x50, 0xf6, 0xa7, 0xec, 0x8c, 0x2b, 0x3f, 0x2a, 0xcd, 0xde, 0xda, 0xa0, 0xd9, 0x10, 0xdf,
0x23, 0xe7, 0x43, 0x45, 0x4c, 0x0d, 0xcf, 0x8e, 0x0d, 0xb0, 0x54, 0x59, 0xa5, 0xa6, 0x1f, 0x7a,
0xfc, 0x17, 0x1d, 0xeb, 0x41, 0xf1, 0x51, 0x91, 0x6a, 0x60, 0xe7, 0x1f, 0x16, 0x34, 0x73, 0xdc,
0x59, 0x65, 0xac, 0x9c, 0x32, 0xa9, 0xe9, 0x0b, 0x19, 0xd3, 0x77, 0xa0, 0x12, 0xb3, 0x79, 0x10,
0x31, 0x0f, 0x4d, 0xdb, 0xa0, 0x29, 0xa8, 0xae, 0x7b, 0xe9, 0x7b, 0x52, 0xd9, 0x54, 0x19, 0x45,
0x03, 0xe4, 0x36, 0x94, 0x27, 0xdc, 0x3f, 0x9b, 0x48, 0x63, 0x2b, 0x03, 0x91, 0x1d, 0xa8, 0xaa,
0xc0, 0x13, 0xfe, 0x2f, 0x39, 0xda, 0xa8, 0x48, 0x17, 0x30, 0x79, 0x13, 0x9a, 0x09, 0x9e, 0x1c,
0xc9, 0x92, 0x33, 0x2e, 0xd1, 0x46, 0x45, 0xda, 0xd0, 0xc8, 0x13, 0xc4, 0x2d, 0x0b, 0x4f, 0x35,
0x53, 0x78, 0xec, 0xbf, 0x59, 0x70, 0xeb, 0x59, 0xe4, 0xb2, 0xc0, 0x58, 0xfa, 0xc8, 0x28, 0xf7,
0x7d, 0x28, 0x9d, 0xf3, 0xb9, 0x40, 0x53, 0xe4, 0xfc, 0xbd, 0x86, 0x78, 0x6f, 0x9f, 0xcf, 0x29,
0x92, 0x93, 0x0f, 0xa1, 0x31, 0x55, 0x66, 0x67, 0xda, 0xec, 0x68, 0x89, 0xfa, 0xe3, 0xdb, 0xeb,
0x9d, 0x42, 0x73, 0xb4, 0xea, 0x85, 0x31, 0x13, 0xe2, 0x65, 0x94, 0x78, 0x26, 0x0a, 0x17, 0xf0,
0xce, 0xff, 0x43, 0x71, 0x9f, 0xcf, 0xd7, 0xc6, 0x36, 0x81, 0x92, 0x2a, 0xc6, 0x78, 0x55, 0x83,
0xe2, 0xd9, 0xfe, 0xc6, 0x82, 0xb6, 0xd2, 0x31, 0x5b, 0x25, 0x37, 0x54, 0xde, 0x77, 0x60, 0xcb,
0xcf, 0x50, 0x39, 0x8b, 0x32, 0xdc, 0xca, 0xa2, 0x87, 0x1e, 0xf6, 0x01, 0x7e, 0xe1, 0xbb, 0xdc,
0x91, 0xf3, 0x98, 0x1b, 0x0d, 0x41, 0xa3, 0x4e, 0xe6, 0x31, 0x5f, 0x28, 0x57, 0xca, 0x7b, 0xff,
0x82, 0x27, 0xc2, 0x8f, 0x42, 0x74, 0x67, 0x93, 0xa6, 0xa0, 0xfd, 0x2f, 0x0b, 0xee, 0x6c, 0x28,
0xe4, 0xd7, 0xec, 0x11, 0x6f, 0x42, 0xd3, 0x54, 0x23, 0x07, 0xc3, 0xd9, 0xa8, 0xd4, 0x30, 0x48,
0x1d, 0xab, 0x77, 0xa1, 0xca, 0x43, 0xe1, 0x64, 0x14, 0xab, 0xf0, 0x50, 0x8c, 0x94, 0x6e, 0x0f,
0xa1, 0x11, 0x30, 0x21, 0x9d, 0x59, 0xec, 0x31, 0xc9, 0x75, 0x6e, 0x96, 0x68, 0x5d, 0xe1, 0xc6,
0x1a, 0xa5, 0xde, 0x2c, 0xe6, 0x42, 0xf2, 0xa9, 0x23, 0xd9, 0x99, 0x2a, 0xd9, 0x45, 0xf5, 0x66,
0x8d, 0x3a, 0x61, 0x67, 0x82, 0xbc, 0x0d, 0xad, 0x40, 0x05, 0x84, 0x13, 0xfa, 0xee, 0x39, 0x5e,
0xa2, 0xd3, 0xb3, 0x89, 0xd8, 0x91, 0x41, 0xda, 0xbf, 0x2a, 0xc3, 0xdd, 0x8d, 0x5d, 0x8b, 0x7c,
0x07, 0xb6, 0xb3, 0x8a, 0x38, 0xc8, 0x1b, 0xcc, 0xcd, 0xeb, 0x49, 0x46, 0xa1, 0x67, 0xfa, 0xcb,
0xff, 0xb0, 0x29, 0x94, 0x6f, 0x99, 0xe7, 0x71, 0x0f, 0xfb, 0x45, 0x95, 0x6a, 0x40, 0xc5, 0xc9,
0xa9, 0x72, 0x32, 0xf7, 0xb0, 0x1d, 0x54, 0x69, 0x0a, 0x2a, 0xfa, 0xe9, 0x4c, 0xe9, 0x54, 0xd7,
0xf4, 0x08, 0x28, 0xfa, 0x84, 0x4f, 0xa3, 0x0b, 0xee, 0x75, 0x1a, 0x9a, 0xde, 0x80, 0xe4, 0x01,
0x34, 0x26, 0x4c, 0x38, 0x28, 0xd6, 0x99, 0x89, 0x4e, 0x13, 0x3f, 0xc3, 0x84, 0x89, 0xae, 0x42,
0x8d, 0x55, 0xcf, 0xba, 0x75, 0xc1, 0x13, 0xff, 0x45, 0x3a, 0x16, 0x09, 0xc9, 0xe4, 0x4c, 0x74,
0x5a, 0x58, 0x33, 0x48, 0xf6, 0xd3, 0x31, 0x7e, 0xc1, 0x01, 0x27, 0x99, 0x09, 0x99, 0x52, 0x6e,
0x21, 0x65, 0x1d, 0x71, 0x86, 0xe4, 0x13, 0xb8, 0x67, 0xba, 0xbe, 0x93, 0xf0, 0x9f, 0xcf, 0xb8,
0x90, 0xda, 0x8b, 0xc8, 0xc2, 0x3b, 0x6d, 0xe4, 0xe8, 0x18, 0x12, 0xaa, 0x29, 0xd0, 0x99, 0x8a,
0x9f, 0x6f, 0x66, 0xd7, 0x69, 0x70, 0x73, 0x23, 0x7b, 0x0f, 0x33, 0xe3, 0x53, 0xd8, 0x5d, 0x65,
0x57, 0xe6, 0x90, 0xdc, 0x5c, 0x4f, 0x90, 0xff, 0x6e, 0x9e, 0x9f, 0x22, 0x85, 0xbe, 0x7f, 0xb3,
0x00, 0xad, 0xc0, 0xad, 0xcd, 0x02, 0xb4, 0x06, 0x0f, 0xa1, 0xe1, 0xf9, 0x22, 0x0e, 0xd8, 0x5c,
0xc7, 0xd7, 0x36, 0xba, 0xbe, 0x6e, 0x70, 0x2a, 0xc6, 0xec, 0x97, 0x97, 0xf3, 0x3d, 0x6d, 0xd9,
0xeb, 0xf3, 0xfd, 0x52, 0x50, 0x17, 0xd6, 0x04, 0xf5, 0x6a, 0xe4, 0x16, 0x2f, 0x45, 0xae, 0xfd,
0x04, 0x76, 0x56, 0x2f, 0x3e, 0x9a, 0x9d, 0x06, 0xbe, 0xdb, 0x9b, 0xb0, 0x6b, 0xd6, 0x1a, 0xfb,
0xdb, 0x22, 0x34, 0x73, 0x23, 0xe3, 0x7f, 0xe4, 0x6b, 0x60, 0x62, 0xde, 0x87, 0x7a, 0x9c, 0xf8,
0x17, 0x4c, 0x72, 0xe7, 0x9c, 0xcf, 0x4d, 0x07, 0x04, 0x83, 0x52, 0x15, 0xfd, 0x81, 0xaa, 0xaa,
0xc2, 0x4d, 0xfc, 0x58, 0xe9, 0x85, 0x79, 0xd9, 0xa0, 0x59, 0x94, 0x6a, 0x88, 0x3f, 0x8b, 0xfc,
0xd0, 0x64, 0x65, 0x95, 0x1a, 0x48, 0xb5, 0x0b, 0x1d, 0xab, 0xdc, 0xc3, 0x86, 0x58, 0xa5, 0x0b,
0x78, 0x99, 0x34, 0x95, 0x6c, 0xd2, 0x1c, 0x42, 0xdb, 0x78, 0x57, 0x38, 0x32, 0x72, 0x94, 0x1c,
0x33, 0x35, 0xbc, 0xbd, 0x69, 0x30, 0x36, 0xe4, 0x27, 0xd1, 0x17, 0x91, 0x1f, 0xd2, 0x56, 0x92,
0x83, 0xc9, 0x47, 0x50, 0x4d, 0xc7, 0x31, 0x33, 0xfe, 0xdd, 0xdf, 0x20, 0xc8, 0xcc, 0x81, 0x82,
0x2e, 0x18, 0xd4, 0xd4, 0xc5, 0x43, 0x37, 0x99, 0xc7, 0x72, 0x91, 0xf4, 0x4b, 0x84, 0xfa, 0x2a,
0x62, 0xee, 0x4a, 0xb6, 0x4c, 0xfd, 0x25, 0x42, 0x35, 0x2d, 0x43, 0xaa, 0x12, 0x18, 0x1b, 0x75,
0x03, 0x2d, 0xd7, 0x5a, 0xa2, 0xf7, 0xf9, 0x5c, 0xd8, 0x7f, 0xb5, 0xe0, 0xde, 0x15, 0x2f, 0x32,
0xfe, 0xb2, 0x16, 0xfe, 0x7a, 0x03, 0x20, 0xc6, 0xd8, 0x40, 0x77, 0x69, 0xff, 0xd7, 0x34, 0x46,
0x79, 0x6b, 0xe1, 0xf4, 0x62, 0xd6, 0xe9, 0x57, 0x14, 0xd6, 0x3b, 0x50, 0x71, 0x27, 0x4c, 0xaa,
0xae, 0x7a, 0x43, 0x8f, 0x4a, 0x0a, 0x1c, 0x7a, 0x2a, 0x6e, 0xd3, 0x91, 0x7e, 0xae, 0xbe, 0x96,
0xb5, 0xe3, 0x17, 0xb8, 0x21, 0x3a, 0x51, 0xa7, 0x6f, 0x45, 0x5f, 0x86, 0x80, 0xfd, 0xeb, 0x02,
0xb4, 0x57, 0xc3, 0x99, 0x7c, 0x92, 0x59, 0x97, 0x2e, 0x4d, 0x2c, 0x1b, 0x1a, 0x4f, 0x66, 0x59,
0xfa, 0x1c, 0x1a, 0xe6, 0xd5, 0x4a, 0x3b, 0xd1, 0x29, 0xac, 0x8e, 0x92, 0x9b, 0xf3, 0x87, 0xd6,
0xe3, 0xc5, 0x59, 0x90, 0x8f, 0xa0, 0x92, 0x4e, 0x3e, 0x45, 0x8c, 0x87, 0x2b, 0xd4, 0x48, 0x87,
0xa0, 0x94, 0xe3, 0xbf, 0x58, 0xd9, 0xec, 0x0f, 0x60, 0x0b, 0xbf, 0x2a, 0x85, 0x4c, 0x1f, 0xb8,
0x5e, 0x5e, 0x7f, 0x0c, 0xdb, 0x29, 0xe3, 0x81, 0x5e, 0x8a, 0x05, 0xe5, 0xec, 0xba, 0xdc, 0x9f,
0xc1, 0x6d, 0xc5, 0xdd, 0x75, 0xa5, 0x7f, 0xe1, 0xcb, 0x79, 0x8f, 0x87, 0x92, 0x27, 0x57, 0xf0,
0xb7, 0xa1, 0xe8, 0x7b, 0xda, 0xbc, 0x0d, 0xaa, 0x8e, 0x76, 0x5f, 0xd7, 0xa6, 0xbc, 0x84, 0xae,
0xeb, 0x72, 0x4c, 0x82, 0xeb, 0x4a, 0x19, 0xe8, 0x20, 0xcf, 0x4b, 0xe9, 0xfb, 0x62, 0xea, 0x0b,
0xf1, 0x0a, 0x62, 0xbe, 0xb1, 0xa0, 0xa1, 0xe4, 0x3c, 0x89, 0xa2, 0xf3, 0x29, 0x4b, 0xce, 0x37,
0x33, 0xce, 0x92, 0xc0, 0x98, 0x41, 0x1d, 0x17, 0x93, 0x5f, 0x31, 0x33, 0xf9, 0xdd, 0x83, 0x1a,
0x56, 0x6d, 0x47, 0xd1, 0xea, 0xac, 0xa8, 0x22, 0x62, 0x9c, 0x04, 0xd9, 0xf6, 0x7d, 0x23, 0xdf,
0xbe, 0xdf, 0x00, 0xf0, 0x78, 0xc0, 0xd5, 0x18, 0xc4, 0x24, 0x66, 0x45, 0x89, 0xd6, 0x0c, 0xa6,
0x2b, 0xed, 0x2f, 0x74, 0xf0, 0xf7, 0x02, 0xce, 0x92, 0xa7, 0xbe, 0x90, 0x51, 0x32, 0xcf, 0xe6,
0x98, 0x95, 0xcb, 0xb1, 0x37, 0x00, 0x5c, 0x45, 0xa8, 0x65, 0x15, 0xb4, 0x2c, 0x83, 0xe9, 0x4a,
0xfb, 0x2f, 0x16, 0x10, 0x25, 0xcc, 0xec, 0xc8, 0x47, 0xbe, 0x2b, 0x67, 0x09, 0x5f, 0x3b, 0x63,
0x67, 0x96, 0x98, 0xc2, 0x86, 0x25, 0xa6, 0x88, 0xe3, 0xed, 0xa5, 0x25, 0xa6, 0x84, 0xe8, 0x74,
0x89, 0xb9, 0x07, 0x35, 0xec, 0x67, 0xb8, 0xc5, 0xe8, 0x81, 0x18, 0xb7, 0x98, 0xe3, 0xb5, 0x5b,
0x4c, 0x19, 0x09, 0x36, 0x6c, 0x31, 0x95, 0xec, 0x16, 0x33, 0x81, 0x5b, 0x97, 0x5f, 0x22, 0x36,
0x2f, 0x6a, 0x3f, 0x84, 0x6a, 0x6c, 0x88, 0x4c, 0xb2, 0xef, 0xe6, 0xf3, 0x2c, 0x2f, 0x89, 0x2e,
0xa8, 0xed, 0xdf, 0x17, 0xe0, 0xa6, 0x22, 0xf8, 0x92, 0x05, 0x01, 0x97, 0x57, 0x37, 0xf0, 0x0e,
0x54, 0x98, 0xe7, 0x25, 0x5c, 0x88, 0xd4, 0x6a, 0x06, 0x54, 0xf6, 0x79, 0x89, 0x02, 0xd0, 0x6c,
0x55, 0x6a, 0x20, 0x65, 0x7b, 0xe5, 0x3b, 0xb4, 0x5a, 0x95, 0xe2, 0x59, 0xe1, 0x70, 0xe1, 0xd0,
0xf5, 0x13, 0xcf, 0x4a, 0xb2, 0xf2, 0xbd, 0x1a, 0x0a, 0xf4, 0xbe, 0x9c, 0x82, 0x8a, 0x3a, 0x66,
0x72, 0x62, 0x66, 0x4f, 0x3c, 0xab, 0x5e, 0xb2, 0x28, 0xe1, 0xb8, 0xfd, 0x35, 0xb2, 0x35, 0x3d,
0xf5, 0x77, 0x2d, 0xe3, 0x6f, 0xf5, 0x1e, 0xb5, 0xa2, 0x63, 0x5f, 0xaa, 0x51, 0x0d, 0xa0, 0x57,
0x7d, 0xcf, 0xe3, 0xa1, 0x69, 0x48, 0x06, 0xda, 0x3c, 0x8c, 0xda, 0x07, 0x3a, 0xc2, 0x72, 0xc6,
0x12, 0xe4, 0x03, 0xa8, 0x9a, 0x9a, 0x97, 0x56, 0xeb, 0x7b, 0x79, 0xeb, 0xe7, 0xe8, 0xe9, 0x82,
0xd8, 0xfe, 0xa3, 0xa5, 0xc3, 0xff, 0x98, 0x5d, 0x70, 0xaf, 0x6b, 0x6c, 0x99, 0xb1, 0xb2, 0x95,
0xb7, 0xf2, 0xba, 0x75, 0x7c, 0x17, 0x6a, 0x2f, 0xd8, 0x45, 0x34, 0x4b, 0x7c, 0xc9, 0x8d, 0xf1,
0x97, 0x08, 0xd5, 0xc9, 0xdc, 0x09, 0xf3, 0x71, 0x0b, 0x2c, 0xa1, 0x2b, 0x2b, 0x08, 0x0f, 0xbd,
0x2b, 0x52, 0xf6, 0x21, 0x34, 0xf4, 0xf4, 0xe5, 0x64, 0x23, 0xb3, 0xae, 0x71, 0x38, 0x1e, 0xda,
0xbf, 0xb1, 0xe0, 0xf5, 0xb5, 0xf3, 0xc0, 0x86, 0xc8, 0x59, 0xed, 0x8e, 0xfa, 0x05, 0xb9, 0xee,
0x38, 0x80, 0xfb, 0x13, 0x5d, 0x00, 0x1c, 0x96, 0xb8, 0x13, 0xff, 0x82, 0x3b, 0x62, 0x16, 0xc7,
0x51, 0x22, 0x1d, 0x1e, 0xb2, 0xd3, 0xc0, 0xcc, 0x82, 0x55, 0xba, 0x6b, 0xc8, 0xba, 0x9a, 0xea,
0x58, 0x13, 0x0d, 0x34, 0x8d, 0xfd, 0x07, 0x4b, 0xb7, 0x8e, 0x13, 0x35, 0xcc, 0xab, 0xf5, 0x80,
0x27, 0xd7, 0x5c, 0x3f, 0x3f, 0x81, 0xb2, 0xd9, 0x07, 0xd4, 0x3d, 0xad, 0xd5, 0x19, 0x2a, 0x23,
0x70, 0xef, 0x64, 0xb9, 0x29, 0x50, 0xc3, 0x64, 0x7f, 0x08, 0xf5, 0x0c, 0x9a, 0xd4, 0xa1, 0x32,
0x1e, 0xed, 0x8f, 0x0e, 0xbf, 0x1c, 0xb5, 0x5f, 0x53, 0xc0, 0x09, 0x1d, 0x1f, 0x9f, 0x0c, 0xfa,
0x6d, 0x8b, 0xdc, 0x84, 0xe6, 0x78, 0x84, 0xe0, 0x97, 0x87, 0xf4, 0xe4, 0xe9, 0x57, 0xed, 0x82,
0xfd, 0x4d, 0x51, 0xcf, 0xd2, 0xcf, 0x33, 0xbb, 0x8a, 0x19, 0x6c, 0x36, 0x28, 0x4f, 0xa0, 0xf4,
0x22, 0x89, 0xa6, 0x69, 0x28, 0xa8, 0xb3, 0x7a, 0x90, 0x8c, 0x4c, 0xcd, 0x2e, 0xc8, 0x48, 0x85,
0x86, 0x3b, 0x51, 0x91, 0x17, 0x9e, 0xa5, 0x73, 0xcc, 0x12, 0xa1, 0x5c, 0x62, 0xa6, 0x3f, 0x5d,
0x4e, 0xcd, 0x8a, 0xb8, 0xc0, 0x75, 0xf1, 0x07, 0x8c, 0x84, 0x8b, 0x38, 0x0a, 0x45, 0x9a, 0x96,
0x0b, 0x58, 0xd5, 0xe2, 0x84, 0xc7, 0x81, 0xaf, 0x99, 0x75, 0x88, 0xd4, 0x0c, 0xa6, 0x2b, 0x09,
0x5f, 0xbf, 0x93, 0x55, 0xd1, 0xb2, 0xdf, 0xcb, 0x5b, 0x76, 0xcd, 0xab, 0xf7, 0x9e, 0x5f, 0xda,
0xda, 0xd6, 0x6e, 0x72, 0xda, 0x87, 0xb5, 0x45, 0x03, 0xff, 0x09, 0x90, 0xcb, 0x9c, 0x97, 0x7c,
0x71, 0x34, 0x18, 0xf5, 0x87, 0xa3, 0xcf, 0xdb, 0x16, 0x69, 0x40, 0xb5, 0xdb, 0xeb, 0x0d, 0x8e,
0x94, 0x67, 0x0a, 0x0a, 0xea, 0x0f, 0x7a, 0xcf, 0x86, 0xa3, 0x41, 0xbf, 0x5d, 0x54, 0x50, 0xaf,
0x3b, 0xea, 0x0d, 0x9e, 0x0d, 0xfa, 0xed, 0x92, 0xfd, 0x4f, 0x4b, 0x77, 0xf6, 0x5e, 0x6e, 0x65,
0xea, 0x73, 0xd7, 0x17, 0x9b, 0x7f, 0x8b, 0xd9, 0x85, 0x9a, 0xb1, 0xe7, 0x30, 0x8d, 0xb4, 0x25,
0x82, 0xfc, 0x14, 0xb6, 0x3c, 0xc3, 0xef, 0xe4, 0x22, 0xef, 0xfd, 0xd5, 0x19, 0x69, 0xdd, 0x95,
0x7b, 0xe9, 0xc1, 0x98, 0xa7, 0xe5, 0xe5, 0x60, 0xfb, 0x5d, 0x68, 0xe5, 0x29, 0x72, 0x8f, 0x7d,
0x2d, 0xf7, 0x58, 0xcb, 0xfe, 0xd6, 0x82, 0xad, 0x95, 0xdf, 0x98, 0x37, 0x77, 0x9b, 0xd5, 0xe5,
0xb0, 0x70, 0x69, 0x39, 0x24, 0xef, 0x02, 0xc9, 0x92, 0x38, 0xd9, 0x29, 0xbb, 0x9d, 0x21, 0xd4,
0xdb, 0x66, 0xb6, 0x7d, 0x95, 0x5e, 0xa9, 0x7d, 0x09, 0x00, 0xca, 0x5e, 0x9a, 0x51, 0x2f, 0xdb,
0xd6, 0xad, 0x7c, 0x5b, 0xdf, 0x87, 0xba, 0xf9, 0x27, 0xc9, 0x89, 0xea, 0x3d, 0x05, 0xb4, 0xf3,
0xff, 0x2d, 0x2f, 0xe9, 0x2e, 0xff, 0xad, 0x72, 0x60, 0xfe, 0xab, 0x62, 0x84, 0xee, 0x29, 0x06,
0x9a, 0xe5, 0xb6, 0x7f, 0x67, 0x41, 0x4b, 0x69, 0x95, 0xb9, 0xf9, 0x07, 0x50, 0x4f, 0x16, 0x50,
0xda, 0x05, 0xb6, 0x97, 0xf2, 0x97, 0xa4, 0x34, 0x4b, 0x48, 0x1e, 0xc3, 0xb6, 0x98, 0x9d, 0xa6,
0x9d, 0xe4, 0x0b, 0x11, 0x85, 0x4f, 0xe6, 0x92, 0xa7, 0xfd, 0x75, 0xed, 0x37, 0xf2, 0x2e, 0xdc,
0x4c, 0x97, 0xae, 0x25, 0x83, 0xde, 0x44, 0x2f, 0x7f, 0xb0, 0x7f, 0x6b, 0x41, 0x5d, 0x29, 0xbb,
0xaf, 0x7f, 0x80, 0xc6, 0x69, 0x6f, 0xe1, 0x51, 0x75, 0x5c, 0xdb, 0x56, 0x6e, 0x43, 0xd9, 0xfc,
0x7c, 0x63, 0x1a, 0xba, 0xf9, 0xf5, 0x26, 0x13, 0x13, 0xa5, 0x5c, 0x4c, 0xec, 0x42, 0xcd, 0xb4,
0x29, 0x2e, 0x3a, 0x37, 0x70, 0x06, 0x5d, 0x22, 0x96, 0xe9, 0x51, 0xce, 0x4e, 0x39, 0x7f, 0x36,
0xb3, 0x87, 0x51, 0x4d, 0x8d, 0xbb, 0x51, 0x48, 0x3e, 0x84, 0x32, 0xc3, 0x13, 0xea, 0xd8, 0x7a,
0x6c, 0xe7, 0x43, 0x21, 0x47, 0xbc, 0xa7, 0xff, 0x50, 0xc3, 0x41, 0xde, 0x82, 0x66, 0x14, 0x78,
0x86, 0x64, 0xbc, 0x28, 0xef, 0x79, 0x24, 0x79, 0x0f, 0x1f, 0xa1, 0x20, 0xb3, 0xd5, 0xbc, 0xbe,
0xf6, 0x0a, 0x9a, 0x52, 0xa9, 0x76, 0x57, 0x36, 0xda, 0xdd, 0x84, 0xe6, 0xfe, 0xe0, 0xab, 0x5e,
0x97, 0xf6, 0x9d, 0x6e, 0xbf, 0x8f, 0x99, 0x44, 0xa0, 0xd5, 0xed, 0xf5, 0x0e, 0xc7, 0xa3, 0x93,
0x63, 0x83, 0xb3, 0xc8, 0x2d, 0xd8, 0x4a, 0xc9, 0xfa, 0x83, 0x67, 0x03, 0x5d, 0x5f, 0xb6, 0xa1,
0xbd, 0x20, 0xa4, 0x83, 0x83, 0xc3, 0xe7, 0x58, 0x67, 0x00, 0xca, 0xcf, 0x0e, 0x7b, 0xfb, 0xaa,
0xca, 0xa8, 0xa4, 0x1c, 0x8f, 0x0c, 0x74, 0x83, 0x6c, 0x41, 0x7d, 0x3c, 0xec, 0x3b, 0xe3, 0xa3,
0x7e, 0x57, 0x09, 0x28, 0x93, 0x36, 0x34, 0x46, 0xdd, 0x83, 0x81, 0xd3, 0x7b, 0xda, 0x1d, 0x7d,
0x3e, 0xe8, 0xb7, 0x2b, 0xf6, 0xd7, 0xba, 0xdb, 0x75, 0x83, 0xc0, 0x28, 0x2d, 0xc8, 0x77, 0xa1,
0x6a, 0xf4, 0x4e, 0xe3, 0x70, 0xc3, 0xf3, 0x16, 0x64, 0x4b, 0xf7, 0x14, 0x32, 0xee, 0x79, 0xd2,
0xfc, 0xba, 0xbe, 0xf7, 0xde, 0x47, 0x29, 0xeb, 0x69, 0x19, 0x4f, 0xef, 0xff, 0x3b, 0x00, 0x00,
0xff, 0xff, 0x48, 0xc1, 0x44, 0x9e, 0xb1, 0x1c, 0x00, 0x00,
}

View File

@ -70,6 +70,8 @@ message PairInstallation {
string installation_id = 2;
string device_type = 3;
string name = 4;
// following fields used for local pairing
uint32 version = 5;
}
message SyncInstallationContact {

View File

@ -24,6 +24,7 @@ type Client struct {
*http.Client
PayloadManager
rawMessagePayloadManager *RawMessagePayloadManager
installationPayloadManager *InstallationPayloadManager
baseAddress *url.URL
certPEM []byte
@ -70,12 +71,18 @@ func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, confi
return nil, err
}
pm, err := NewAccountPayloadManager(c.aesKey, config, logutils.ZapLogger().Named("Client"))
logger := logutils.ZapLogger().Named("Client")
pm, err := NewAccountPayloadManager(c.aesKey, config, logger)
if err != nil {
return nil, err
}
rmpm, err := NewRawMessagePayloadManager(logutils.ZapLogger().Named("Client"), pm.accountPayload, c.aesKey, backend, config.GetNodeConfig(), config.GetSettingCurrentNetwork())
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
}
@ -88,6 +95,7 @@ func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, confi
serverMode: c.serverMode,
PayloadManager: pm,
rawMessagePayloadManager: rmpm,
installationPayloadManager: ipm,
}, nil
}
@ -117,6 +125,55 @@ func (c *Client) PairSyncDevice() error {
}
}
// 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 {
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 (c *Client) sendSyncDeviceData() error {
err := c.rawMessagePayloadManager.Mount()
if err != nil {
@ -131,14 +188,50 @@ func (c *Client) sendSyncDeviceData() error {
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("[client] status not okay when sending sync device data, status: %s", resp.Status)
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
return fmt.Errorf("status not ok, received '%s'", resp.Status)
return err
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
return nil
}
func (c *Client) receiveInstallationData() error {
c.baseAddress.Path = pairingSendInstallation
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
if err != nil {
return err
}
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 ok when receiving installation data, received '%s'", resp.Status)
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
return err
}
payload, err := io.ReadAll(resp.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
err = c.installationPayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingInstallation})
return nil
}
func (c *Client) receiveSyncDeviceData() error {
c.baseAddress.Path = pairingSyncDeviceSend
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
@ -162,7 +255,7 @@ func (c *Client) receiveSyncDeviceData() error {
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("status not ok, 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
}
@ -197,8 +290,9 @@ func (c *Client) sendAccountData() error {
}
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 fmt.Errorf("status not ok, received '%s'", resp.Status)
return err
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
@ -230,12 +324,12 @@ func (c *Client) receiveAccountData() error {
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("status not ok, received '%s'", resp.Status)
err = fmt.Errorf("[client] status not ok when receiving account data, received '%s'", resp.Status)
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return err
}
payload, err := ioutil.ReadAll(resp.Body)
payload, err := io.ReadAll(resp.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return err
@ -259,7 +353,7 @@ func (c *Client) getChallenge() error {
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("status not ok, received '%s'", resp.Status)
return fmt.Errorf("[client] status not ok when getting challenge, received '%s'", resp.Status)
}
c.serverChallenge, err = ioutil.ReadAll(resp.Body)
@ -275,7 +369,11 @@ func StartUpPairingClient(backend *api.GethStatusBackend, cs, configJSON string)
if err != nil {
return err
}
return c.PairSyncDevice()
err = c.PairSyncDevice()
if err != nil {
return err
}
return c.PairInstallation()
}
func setupClient(backend *api.GethStatusBackend, cs string, configJSON string) (*Client, error) {
@ -290,7 +388,11 @@ func setupClient(backend *api.GethStatusBackend, cs string, configJSON string) (
return nil, err
}
c, err := NewPairingClient(backend, ccp, &AccountPayloadManagerConfig{backend.GetMultiaccountDB(), conf})
accountPayloadManagerConfig := &AccountPayloadManagerConfig{DB: backend.GetMultiaccountDB(), PayloadSourceConfig: conf}
if ccp.serverMode == Sending {
updateLoggedInKeyUID(accountPayloadManagerConfig, backend)
}
c, err := NewPairingClient(backend, ccp, accountPayloadManagerConfig)
if err != nil {
return nil, err
}

10
server/pairing/common.go Normal file
View File

@ -0,0 +1,10 @@
package pairing
import "github.com/status-im/status-go/api"
func updateLoggedInKeyUID(accountPayloadManagerConfig *AccountPayloadManagerConfig, backend *api.GethStatusBackend) {
activeAccount, _ := backend.GetActiveAccount()
if activeAccount != nil {
accountPayloadManagerConfig.LoggedInKeyUID = activeAccount.KeyUID
}
}

View File

@ -32,4 +32,5 @@ const (
ActionConnect = iota + 1
ActionPairingAccount
ActionSyncDevice
ActionPairingInstallation
)

View File

@ -21,20 +21,22 @@ const (
pairingChallenge = pairingBase + "/challenge"
pairingSyncDeviceSend = pairingBase + "/sendSyncDevice"
pairingSyncDeviceReceive = pairingBase + "/receiveSyncDevice"
pairingSendInstallation = pairingBase + "/sendInstallation"
pairingReceiveInstallation = pairingBase + "/receiveInstallation"
// Session names
sessionChallenge = "challenge"
sessionBlocked = "blocked"
)
func handlePairingReceive(ps *Server) http.HandlerFunc {
func handleReceiveAccount(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
payload, err := io.ReadAll(r.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
logger.Error("ioutil.ReadAll(r.Body)", zap.Error(err))
logger.Error("handleReceiveAccount io.ReadAll(r.Body)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
@ -49,6 +51,28 @@ func handlePairingReceive(ps *Server) http.HandlerFunc {
}
}
func handleReceiveInstallation(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingInstallation})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
payload, err := io.ReadAll(r.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingInstallation})
logger.Error("handleReceiveInstallation io.ReadAll(r.Body)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingInstallation})
err = ps.installationPayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingInstallation})
logger.Error("ps.installationPayloadManager.Receive(payload)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingInstallation})
}
}
func handleParingSyncDeviceReceive(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
logger := ps.GetLogger()
@ -56,7 +80,7 @@ func handleParingSyncDeviceReceive(ps *Server) http.HandlerFunc {
payload, err := io.ReadAll(r.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
logger.Error("io.ReadAll(r.Body)", zap.Error(err))
logger.Error("handleParingSyncDeviceReceive io.ReadAll(r.Body)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
@ -71,7 +95,7 @@ func handleParingSyncDeviceReceive(ps *Server) http.HandlerFunc {
}
}
func handlePairingSend(ps *Server) http.HandlerFunc {
func handleSendAccount(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
@ -88,6 +112,30 @@ func handlePairingSend(ps *Server) http.HandlerFunc {
}
}
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})
logger := ps.GetLogger()

View File

@ -27,6 +27,7 @@ var (
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
@ -74,6 +75,8 @@ type PayloadSourceConfig struct {
// 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
@ -126,6 +129,8 @@ func unmarshalPayloadSourceConfig(configJSON string, successCallback payloadSour
type AccountPayloadManagerConfig struct {
DB *multiaccounts.Database
*PayloadSourceConfig
// only used for the receiver side
LoggedInKeyUID string
}
func (a *AccountPayloadManagerConfig) GetNodeConfig() *params.NodeConfig {
@ -142,6 +147,13 @@ func (a *AccountPayloadManagerConfig) GetSettingCurrentNetwork() string {
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
@ -372,6 +384,8 @@ 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() {
@ -463,6 +477,8 @@ type AccountPayloadRepository struct {
keystorePath, keyUID string
kdfIterations int
loggedInKeyUID string
}
func NewAccountPayloadRepository(p *AccountPayload, config *AccountPayloadManagerConfig) (*AccountPayloadRepository, error) {
@ -486,6 +502,7 @@ func NewAccountPayloadRepository(p *AccountPayload, config *AccountPayloadManage
ppr.kdfIterations = config.KDFIterations
}
ppr.keystorePath = config.GetKeystorePath()
ppr.loggedInKeyUID = config.LoggedInKeyUID
return ppr, nil
}
@ -548,16 +565,33 @@ func (apr *AccountPayloadRepository) loadKeys(keyStorePath string) error {
}
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
}
err = apr.storeKeys(apr.keystorePath)
if err != nil {
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
@ -641,7 +675,7 @@ type RawMessagePayloadManager struct {
payloadRepository *RawMessageRepository
}
func NewRawMessagePayloadManager(logger *zap.Logger, accountPayload *AccountPayload, aesKey []byte, backend *api.GethStatusBackend, nodeConfig *params.NodeConfig, settingCurrentNetwork string) (*RawMessagePayloadManager, error) {
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 {
@ -651,7 +685,7 @@ func NewRawMessagePayloadManager(logger *zap.Logger, accountPayload *AccountPayl
logger: l,
accountPayload: accountPayload,
PayloadEncryptionManager: pem,
payloadRepository: NewRawMessageRepository(backend, accountPayload, nodeConfig, settingCurrentNetwork),
payloadRepository: NewRawMessageRepository(backend, accountPayload, nodeConfig, settingCurrentNetwork, deviceType),
}, nil
}
@ -683,15 +717,17 @@ type RawMessageRepository struct {
accountPayload *AccountPayload
nodeConfig *params.NodeConfig
settingCurrentNetwork string
deviceType string
}
func NewRawMessageRepository(backend *api.GethStatusBackend, accountPayload *AccountPayload, config *params.NodeConfig, settingCurrentNetwork string) *RawMessageRepository {
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,
}
}
@ -700,7 +736,7 @@ func (r *RawMessageRepository) LoadFromSource() error {
if account == nil || account.KeyUID == "" {
return fmt.Errorf("no known KeyUID when loading raw messages")
}
payload, err := r.syncRawMessageHandler.PrepareRawMessage(account.KeyUID)
payload, err := r.syncRawMessageHandler.PrepareRawMessage(account.KeyUID, r.deviceType)
if err != nil {
return err
}
@ -713,5 +749,87 @@ func (r *RawMessageRepository) StoreToSource() error {
if accountPayload == nil || accountPayload.multiaccount == nil {
return fmt.Errorf("no known multiaccount when storing raw messages")
}
return r.syncRawMessageHandler.HandleRawMessage(accountPayload.multiaccount, accountPayload.password, r.nodeConfig, r.settingCurrentNetwork, r.payload)
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)
}

View File

@ -335,7 +335,7 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
pms.Require().Exactly(expected.Timestamp, acc.Timestamp)
pms.Require().Len(acc.Images, 2)
err = ppr2.StoreToSource()
err = ppr2.storeKeys(ppr2.keystorePath)
pms.Require().ErrorIs(err, ErrKeyFileAlreadyExists)
}

View File

@ -3,6 +3,8 @@ package pairing
import (
"context"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/common"
)
@ -18,3 +20,14 @@ func (r *RawMessageCollector) dispatchMessage(_ context.Context, rawMessage comm
func (r *RawMessageCollector) getRawMessages() []*common.RawMessage {
return r.rawMessages
}
func (r *RawMessageCollector) convertToSyncRawMessage() *protobuf.SyncRawMessage {
syncRawMessage := new(protobuf.SyncRawMessage)
for _, m := range r.getRawMessages() {
rawMessage := new(protobuf.RawMessage)
rawMessage.Payload = m.Payload
rawMessage.MessageType = m.MessageType
syncRawMessage.RawMessages = append(syncRawMessage.RawMessages, rawMessage)
}
return syncRawMessage
}

View File

@ -9,7 +9,6 @@ import (
"github.com/golang/protobuf/proto"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/params"
@ -24,7 +23,20 @@ func NewSyncRawMessageHandler(backend *api.GethStatusBackend) *SyncRawMessageHan
return &SyncRawMessageHandler{backend: backend}
}
func (s *SyncRawMessageHandler) PrepareRawMessage(keyUID string) ([]byte, error) {
func (s *SyncRawMessageHandler) CollectInstallationData(rawMessageCollector *RawMessageCollector, deviceType string) error {
messenger := s.backend.Messenger()
if messenger == nil {
return fmt.Errorf("messenger is nil when CollectInstallationData")
}
err := messenger.SetInstallationDeviceType(deviceType)
if err != nil {
return err
}
_, err = messenger.SendPairInstallation(context.TODO(), rawMessageCollector.dispatchMessage)
return err
}
func (s *SyncRawMessageHandler) PrepareRawMessage(keyUID, deviceType string) ([]byte, error) {
messenger := s.backend.Messenger()
if messenger == nil {
return nil, fmt.Errorf("messenger is nil when PrepareRawMessage")
@ -47,14 +59,14 @@ func (s *SyncRawMessageHandler) PrepareRawMessage(keyUID string) ([]byte, error)
if err != nil {
return nil, err
}
syncRawMessage := new(protobuf.SyncRawMessage)
for _, m := range rawMessageCollector.getRawMessages() {
rawMessage := new(protobuf.RawMessage)
rawMessage.Payload = m.Payload
rawMessage.MessageType = m.MessageType
syncRawMessage.RawMessages = append(syncRawMessage.RawMessages, rawMessage)
err = s.CollectInstallationData(rawMessageCollector, deviceType)
if err != nil {
return nil, err
}
syncRawMessage := rawMessageCollector.convertToSyncRawMessage()
accountService := s.backend.StatusNode().AccountService()
var (
subAccounts []*accounts.Account
@ -80,33 +92,45 @@ func (s *SyncRawMessageHandler) PrepareRawMessage(keyUID string) ([]byte, error)
return proto.Marshal(syncRawMessage)
}
func (s *SyncRawMessageHandler) HandleRawMessage(account *multiaccounts.Account, password string, nodeConfig *params.NodeConfig, settingCurrentNetwork string, payload []byte) error {
rawMessages, subAccounts, setting, err := s.unmarshalSyncRawMessage(payload)
func (s *SyncRawMessageHandler) HandleRawMessage(accountPayload *AccountPayload, nodeConfig *params.NodeConfig, settingCurrentNetwork, deviceType string, rawMessagePayload []byte) error {
account := accountPayload.multiaccount
rawMessages, subAccounts, setting, err := s.unmarshalSyncRawMessage(rawMessagePayload)
if err != nil {
return err
}
activeAccount, _ := s.backend.GetActiveAccount()
if activeAccount == nil { // not login yet
s.backend.UpdateRootDataDir(nodeConfig.RootDataDir)
// because client don't know keyUID before received data, we need help client to update keystore dir
newKeystoreDir := filepath.Join(nodeConfig.KeyStoreDir, account.KeyUID)
nodeConfig.KeyStoreDir = newKeystoreDir
keystoreDir := filepath.Join(nodeConfig.KeyStoreDir, account.KeyUID)
nodeConfig.KeyStoreDir = keystoreDir
if accountPayload.exist {
err = s.backend.StartNodeWithAccount(*account, accountPayload.password, nodeConfig)
} else {
accountManager := s.backend.AccountManager()
err = accountManager.InitKeystore(filepath.Join(nodeConfig.RootDataDir, newKeystoreDir))
err = accountManager.InitKeystore(filepath.Join(nodeConfig.RootDataDir, keystoreDir))
if err != nil {
return err
}
setting.InstallationID = nodeConfig.ShhextConfig.InstallationID
setting.CurrentNetwork = settingCurrentNetwork
err = s.backend.StartNodeWithAccountAndInitialConfig(*account, password, *setting, nodeConfig, subAccounts)
err = s.backend.StartNodeWithAccountAndInitialConfig(*account, accountPayload.password, *setting, nodeConfig, subAccounts)
}
if err != nil {
return err
}
}
messenger := s.backend.Messenger()
if messenger == nil {
return fmt.Errorf("messenger is nil when HandleRawMessage")
}
err = messenger.SetInstallationDeviceType(deviceType)
if err != nil {
return err
}
return messenger.HandleSyncRawMessages(rawMessages)
}
@ -120,13 +144,17 @@ func (s *SyncRawMessageHandler) unmarshalSyncRawMessage(payload []byte) ([]*prot
if err != nil {
return nil, nil, nil, err
}
if syncRawMessage.SubAccountsJsonBytes != nil {
err = json.Unmarshal(syncRawMessage.SubAccountsJsonBytes, &subAccounts)
if err != nil {
return nil, nil, nil, err
}
}
if syncRawMessage.SettingsJsonBytes != nil {
err = json.Unmarshal(syncRawMessage.SettingsJsonBytes, &setting)
if err != nil {
return nil, nil, nil, err
}
}
return syncRawMessage.RawMessages, subAccounts, setting, nil
}

View File

@ -20,6 +20,7 @@ type Server struct {
server.Server
PayloadManager
rawMessagePayloadManager *RawMessagePayloadManager
installationPayloadManager *InstallationPayloadManager
pk *ecdsa.PublicKey
ek []byte
@ -70,11 +71,15 @@ func NewPairingServer(backend *api.GethStatusBackend, config *Config) (*Server,
return nil, err
}
rmpm, err := NewRawMessagePayloadManager(logger, pm.accountPayload, config.EK, backend, accountPayloadManagerConfig.GetNodeConfig(), accountPayloadManagerConfig.GetSettingCurrentNetwork())
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,
@ -87,6 +92,7 @@ func NewPairingServer(backend *api.GethStatusBackend, config *Config) (*Server,
PayloadManager: pm,
cookieStore: cs,
rawMessagePayloadManager: rmpm,
installationPayloadManager: ipm,
}
s.SetTimeout(config.GetTimeout())
@ -122,9 +128,11 @@ func (s *Server) StartPairing() error {
func (s *Server) startReceivingData() error {
s.SetHandlers(server.HandlerPatternMap{
pairingReceiveAccount: handlePairingReceive(s),
pairingReceiveAccount: handleReceiveAccount(s),
pairingChallenge: handlePairingChallenge(s),
pairingSyncDeviceReceive: handleParingSyncDeviceReceive(s),
// send installation data back to sender
pairingSendInstallation: handleSendInstallation(s),
})
return s.Start()
}
@ -136,9 +144,11 @@ func (s *Server) startSendingData() error {
}
s.SetHandlers(server.HandlerPatternMap{
pairingSendAccount: challengeMiddleware(s, handlePairingSend(s)),
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()
}
@ -165,6 +175,18 @@ func MakeFullPairingServer(backend *api.GethStatusBackend, mode Mode, storeConfi
if err != nil {
return nil, err
}
accountPayloadManagerConfig := &AccountPayloadManagerConfig{
// Things that can't be generated, but DO NOT come from app client
DB: backend.GetMultiaccountDB(),
// Things that can't be generated, but DO come from the app client
PayloadSourceConfig: storeConfig,
}
if mode == Receiving {
updateLoggedInKeyUID(accountPayloadManagerConfig, backend)
}
return NewPairingServer(backend, &Config{
// Things that can be generated, and CANNOT come from the app client (well they could be this is better)
PK: &tlsKey.PublicKey,
@ -175,13 +197,7 @@ func MakeFullPairingServer(backend *api.GethStatusBackend, mode Mode, storeConfi
// Things that can't be generated, but DO come from the app client
Mode: mode,
AccountPayloadManagerConfig: &AccountPayloadManagerConfig{
// Things that can't be generated, but DO NOT come from app client
DB: backend.GetMultiaccountDB(),
// Things that can't be generated, but DO come from the app client
PayloadSourceConfig: storeConfig,
},
AccountPayloadManagerConfig: accountPayloadManagerConfig,
})
}

View File

@ -182,7 +182,7 @@ func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware(
// Attempt to get the private key data, this should fail because there is no challenge
err := c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
s.Require().Equal("[client] status not ok when receiving account data, received '403 Forbidden'", err.Error())
err = c.getChallenge()
s.Require().NoError(err)
@ -205,7 +205,7 @@ func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware_
// Attempt to get the private key data, this should fail because there is no challenge
err := c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
s.Require().Equal("[client] status not ok when receiving account data, received '403 Forbidden'", err.Error())
// Get the challenge
err = c.getChallenge()
@ -220,7 +220,7 @@ func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware_
// behind the scenes the server will block the session if the client fails the challenge. There is no forgiveness!
err = c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
s.Require().Equal("[client] status not ok when receiving account data, received '403 Forbidden'", err.Error())
// Get the real challenge
err = c.getChallenge()
@ -229,7 +229,7 @@ func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware_
// Attempt to get the account data, should fail because the client is now blocked.
err = c.receiveAccountData()
s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
s.Require().Equal("[client] status not ok when receiving account data, received '403 Forbidden'", err.Error())
}
func testHandler(t *testing.T) func(w http.ResponseWriter, r *http.Request) {

View File

@ -113,6 +113,7 @@ func (s *SyncDeviceSuite) prepareBackendWithAccount(tmpdir string) *api.GethStat
accounts := []*accounts.Account{walletAccount, chatAccount}
err = backend.StartNodeWithAccountAndInitialConfig(account, s.password, *settings, nodeConfig, accounts)
require.NoError(s.T(), err)
return backend
}
@ -125,11 +126,11 @@ func (s *SyncDeviceSuite) prepareBackendWithoutAccount(tmpdir string) *api.GethS
func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
clientTmpDir := filepath.Join(s.clientAsSenderTmpdir, "client")
clientBackend := s.prepareBackendWithAccount(clientTmpDir)
serverTmpDir := filepath.Join(s.clientAsSenderTmpdir, "server")
serverBackend := s.prepareBackendWithoutAccount(serverTmpDir)
defer func() {
require.NoError(s.T(), serverBackend.Logout())
require.NoError(s.T(), clientBackend.Logout())
}()
err := serverBackend.AccountManager().InitKeystore(filepath.Join(serverTmpDir, keystoreDir))
@ -142,6 +143,7 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
serverKeystoreDir := filepath.Join(serverTmpDir, keystoreDir)
serverPayloadSourceConfig := PayloadSourceConfig{
KeystorePath: serverKeystoreDir,
DeviceType: "desktop",
PayloadSourceReceiverConfig: &PayloadSourceReceiverConfig{
KDFIterations: expectedKDFIterations,
NodeConfig: serverNodeConfig,
@ -167,6 +169,7 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
clientKeystorePath := filepath.Join(clientTmpDir, keystoreDir, clientActiveAccount.KeyUID)
clientPayloadSourceConfig := PayloadSourceConfig{
KeystorePath: clientKeystorePath,
DeviceType: "android",
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
KeyUID: clientActiveAccount.KeyUID,
Password: s.password,
@ -176,7 +179,6 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
require.NoError(s.T(), err)
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
require.NoError(s.T(), err)
require.NoError(s.T(), clientBackend.Logout())
serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
bookmarks, err := serverBrowserAPI.GetBookmarks(context.TODO())
@ -188,6 +190,35 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
require.NoError(s.T(), err)
require.Equal(s.T(), serverActiveAccount.Name, clientActiveAccount.Name)
require.Equal(s.T(), serverActiveAccount.KDFIterations, expectedKDFIterations)
serverMessenger := serverBackend.Messenger()
clientMessenger := clientBackend.Messenger()
require.True(s.T(), serverMessenger.HasPairedDevices())
require.True(s.T(), clientMessenger.HasPairedDevices())
err = clientMessenger.DisableInstallation(serverNodeConfig.ShhextConfig.InstallationID)
require.NoError(s.T(), err)
require.False(s.T(), clientMessenger.HasPairedDevices())
clientNodeConfig, err := clientBackend.GetNodeConfig()
require.NoError(s.T(), err)
err = serverMessenger.DisableInstallation(clientNodeConfig.ShhextConfig.InstallationID)
require.NoError(s.T(), err)
require.False(s.T(), serverMessenger.HasPairedDevices())
// repeat local pairing, we should expect no error after receiver logged in
cs, err = StartUpPairingServer(serverBackend, Receiving, string(serverConfigBytes))
require.NoError(s.T(), err)
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
require.NoError(s.T(), err)
require.True(s.T(), clientMessenger.HasPairedDevices())
require.True(s.T(), serverMessenger.HasPairedDevices())
// test if it's okay when account already exist but not logged in
require.NoError(s.T(), serverBackend.Logout())
cs, err = StartUpPairingServer(serverBackend, Receiving, string(serverConfigBytes))
require.NoError(s.T(), err)
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
require.NoError(s.T(), err)
}
func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
@ -198,6 +229,7 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
serverBackend := s.prepareBackendWithAccount(serverTmpDir)
defer func() {
require.NoError(s.T(), clientBackend.Logout())
require.NoError(s.T(), serverBackend.Logout())
}()
serverActiveAccount, err := serverBackend.GetActiveAccount()
@ -205,6 +237,7 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
serverKeystorePath := filepath.Join(serverTmpDir, keystoreDir, serverActiveAccount.KeyUID)
var config = PayloadSourceConfig{
KeystorePath: serverKeystorePath,
DeviceType: "desktop",
PayloadSourceSenderConfig: &PayloadSourceSenderConfig{
KeyUID: serverActiveAccount.KeyUID,
Password: s.password,
@ -233,6 +266,7 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
clientKeystoreDir := filepath.Join(clientTmpDir, keystoreDir)
clientPayloadSourceConfig := PayloadSourceConfig{
KeystorePath: clientKeystoreDir,
DeviceType: "iphone",
PayloadSourceReceiverConfig: &PayloadSourceReceiverConfig{
KDFIterations: expectedKDFIterations,
NodeConfig: clientNodeConfig,
@ -245,8 +279,6 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
require.NoError(s.T(), err)
require.NoError(s.T(), serverBackend.Logout())
clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
bookmarks, err := clientBrowserAPI.GetBookmarks(context.TODO())
require.NoError(s.T(), err)
@ -257,6 +289,35 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
require.NoError(s.T(), err)
require.Equal(s.T(), serverActiveAccount.Name, clientActiveAccount.Name)
require.Equal(s.T(), clientActiveAccount.KDFIterations, expectedKDFIterations)
serverMessenger := serverBackend.Messenger()
clientMessenger := clientBackend.Messenger()
require.True(s.T(), serverMessenger.HasPairedDevices())
require.True(s.T(), clientMessenger.HasPairedDevices())
err = serverMessenger.DisableInstallation(clientNodeConfig.ShhextConfig.InstallationID)
require.NoError(s.T(), err)
require.False(s.T(), serverMessenger.HasPairedDevices())
serverNodeConfig, err := serverBackend.GetNodeConfig()
require.NoError(s.T(), err)
err = clientMessenger.DisableInstallation(serverNodeConfig.ShhextConfig.InstallationID)
require.NoError(s.T(), err)
require.False(s.T(), clientMessenger.HasPairedDevices())
// repeat local pairing, we should expect no error after receiver logged in
cs, err = StartUpPairingServer(serverBackend, Sending, string(configBytes))
require.NoError(s.T(), err)
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
require.NoError(s.T(), err)
require.True(s.T(), serverMessenger.HasPairedDevices())
require.True(s.T(), clientMessenger.HasPairedDevices())
// test if it's okay when account already exist but not logged in
require.NoError(s.T(), clientBackend.Logout())
cs, err = StartUpPairingServer(serverBackend, Sending, string(configBytes))
require.NoError(s.T(), err)
err = StartUpPairingClient(clientBackend, cs, string(clientConfigBytes))
require.NoError(s.T(), err)
}
func defaultSettings(generatedAccountInfo generator.GeneratedAccountInfo, derivedAddresses map[string]generator.AccountInfo, mnemonic *string) (*settings.Settings, error) {

View File

@ -868,7 +868,7 @@ func (api *PublicAPI) VerifiedUntrustworthy(ctx context.Context, request *reques
}
func (api *PublicAPI) SendPairInstallation(ctx context.Context) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendPairInstallation(ctx)
return api.service.messenger.SendPairInstallation(ctx, nil)
}
func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) error {