github.com/status-im/status-go@v1.1.0/protocol/encryption/encryption_multi_device_test.go (about)

     1  package encryption
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/status-im/status-go/appdatabase"
     9  	"github.com/status-im/status-go/protocol/sqlite"
    10  	"github.com/status-im/status-go/protocol/tt"
    11  	"github.com/status-im/status-go/t/helpers"
    12  
    13  	"github.com/stretchr/testify/suite"
    14  	"go.uber.org/zap"
    15  
    16  	"github.com/status-im/status-go/eth-node/crypto"
    17  
    18  	"github.com/status-im/status-go/protocol/encryption/multidevice"
    19  )
    20  
    21  const (
    22  	aliceUser = "alice"
    23  	bobUser   = "bob"
    24  )
    25  
    26  func TestEncryptionServiceMultiDeviceSuite(t *testing.T) {
    27  	suite.Run(t, new(EncryptionServiceMultiDeviceSuite))
    28  }
    29  
    30  type serviceAndKey struct {
    31  	services []*Protocol
    32  	key      *ecdsa.PrivateKey
    33  }
    34  
    35  type EncryptionServiceMultiDeviceSuite struct {
    36  	suite.Suite
    37  	services map[string]*serviceAndKey
    38  	logger   *zap.Logger
    39  }
    40  
    41  func setupUser(user string, s *EncryptionServiceMultiDeviceSuite, n int) error {
    42  	key, err := crypto.GenerateKey()
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	s.services[user] = &serviceAndKey{
    48  		key:      key,
    49  		services: make([]*Protocol, n),
    50  	}
    51  
    52  	for i := 0; i < n; i++ {
    53  		installationID := fmt.Sprintf("%s%d", user, i+1)
    54  
    55  		db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
    56  		if err != nil {
    57  			return err
    58  		}
    59  		err = sqlite.Migrate(db)
    60  		if err != nil {
    61  			return err
    62  		}
    63  
    64  		protocol := New(
    65  			db,
    66  			installationID,
    67  			s.logger.With(zap.String("user", user)),
    68  		)
    69  		s.services[user].services[i] = protocol
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
    76  	s.logger = tt.MustCreateTestLogger()
    77  
    78  	s.services = make(map[string]*serviceAndKey)
    79  	err := setupUser(aliceUser, s, 4)
    80  	s.Require().NoError(err)
    81  
    82  	err = setupUser(bobUser, s, 4)
    83  	s.Require().NoError(err)
    84  }
    85  
    86  func (s *EncryptionServiceMultiDeviceSuite) TearDownTest() {
    87  	_ = s.logger.Sync()
    88  }
    89  
    90  func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() {
    91  	aliceKey := s.services[aliceUser].key
    92  
    93  	alice2Bundle, err := s.services[aliceUser].services[1].GetBundle(aliceKey)
    94  	s.Require().NoError(err)
    95  
    96  	alice2IdentityPK, err := ExtractIdentity(alice2Bundle)
    97  	s.Require().NoError(err)
    98  
    99  	alice2Identity := fmt.Sprintf("0x%x", crypto.FromECDSAPub(alice2IdentityPK))
   100  
   101  	alice3Bundle, err := s.services[aliceUser].services[2].GetBundle(aliceKey)
   102  	s.Require().NoError(err)
   103  
   104  	alice3IdentityPK, err := ExtractIdentity(alice2Bundle)
   105  	s.Require().NoError(err)
   106  
   107  	alice3Identity := fmt.Sprintf("0x%x", crypto.FromECDSAPub(alice3IdentityPK))
   108  
   109  	// Add alice2 bundle
   110  	response, err := s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice2Bundle)
   111  	s.Require().NoError(err)
   112  	s.Require().Equal(multidevice.Installation{
   113  		Identity: alice2Identity,
   114  		Version:  1,
   115  		ID:       "alice2",
   116  	}, *response[0])
   117  
   118  	// Add alice3 bundle
   119  	response, err = s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice3Bundle)
   120  	s.Require().NoError(err)
   121  	s.Require().Equal(multidevice.Installation{
   122  		Identity: alice3Identity,
   123  		Version:  1,
   124  		ID:       "alice3",
   125  	}, *response[0])
   126  
   127  	// No installation is enabled
   128  	alice1MergedBundle1, err := s.services[aliceUser].services[0].GetBundle(aliceKey)
   129  	s.Require().NoError(err)
   130  
   131  	s.Require().Equal(1, len(alice1MergedBundle1.GetSignedPreKeys()))
   132  	s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"])
   133  
   134  	// We enable the installations
   135  	err = s.services[aliceUser].services[0].EnableInstallation(&aliceKey.PublicKey, "alice2")
   136  	s.Require().NoError(err)
   137  
   138  	err = s.services[aliceUser].services[0].EnableInstallation(&aliceKey.PublicKey, "alice3")
   139  	s.Require().NoError(err)
   140  
   141  	alice1MergedBundle2, err := s.services[aliceUser].services[0].GetBundle(aliceKey)
   142  	s.Require().NoError(err)
   143  
   144  	// We get back a bundle with all the installations
   145  	s.Require().Equal(3, len(alice1MergedBundle2.GetSignedPreKeys()))
   146  	s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice1"])
   147  	s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice2"])
   148  	s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice3"])
   149  
   150  	// We disable the installations
   151  	err = s.services[aliceUser].services[0].DisableInstallation(&aliceKey.PublicKey, "alice2")
   152  	s.Require().NoError(err)
   153  
   154  	alice1MergedBundle3, err := s.services[aliceUser].services[0].GetBundle(aliceKey)
   155  	s.Require().NoError(err)
   156  
   157  	// We get back a bundle with all the installations
   158  	s.Require().Equal(2, len(alice1MergedBundle3.GetSignedPreKeys()))
   159  	s.Require().NotNil(alice1MergedBundle3.GetSignedPreKeys()["alice1"])
   160  	s.Require().NotNil(alice1MergedBundle3.GetSignedPreKeys()["alice3"])
   161  }
   162  
   163  func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundleOutOfOrder() {
   164  	aliceKey, err := crypto.GenerateKey()
   165  	s.Require().NoError(err)
   166  
   167  	// Alice1 creates a bundle
   168  	alice1Bundle, err := s.services[aliceUser].services[0].GetBundle(aliceKey)
   169  	s.Require().NoError(err)
   170  
   171  	// Alice2 Receives the bundle
   172  	_, err = s.services[aliceUser].services[1].ProcessPublicBundle(aliceKey, alice1Bundle)
   173  	s.Require().NoError(err)
   174  
   175  	// Alice2 Creates a Bundle
   176  	_, err = s.services[aliceUser].services[1].GetBundle(aliceKey)
   177  	s.Require().NoError(err)
   178  
   179  	// We enable the installation
   180  	err = s.services[aliceUser].services[1].EnableInstallation(&aliceKey.PublicKey, "alice1")
   181  	s.Require().NoError(err)
   182  
   183  	// It should contain both bundles
   184  	alice2MergedBundle1, err := s.services[aliceUser].services[1].GetBundle(aliceKey)
   185  	s.Require().NoError(err)
   186  
   187  	s.Require().NotNil(alice2MergedBundle1.GetSignedPreKeys()["alice1"])
   188  	s.Require().NotNil(alice2MergedBundle1.GetSignedPreKeys()["alice2"])
   189  }
   190  
   191  func pairDevices(s *serviceAndKey, target int) error {
   192  	device := s.services[target]
   193  	for i := 0; i < len(s.services); i++ {
   194  		b, err := s.services[i].GetBundle(s.key)
   195  
   196  		if err != nil {
   197  			return err
   198  		}
   199  
   200  		_, err = device.ProcessPublicBundle(s.key, b)
   201  		if err != nil {
   202  			return err
   203  		}
   204  
   205  		err = device.EnableInstallation(&s.key.PublicKey, s.services[i].encryptor.config.InstallationID)
   206  		if err != nil {
   207  			return nil
   208  		}
   209  	}
   210  	return nil
   211  }
   212  
   213  func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevices() {
   214  	err := pairDevices(s.services[aliceUser], 0)
   215  	s.Require().NoError(err)
   216  	alice1 := s.services[aliceUser].services[0]
   217  	bob1 := s.services[bobUser].services[0]
   218  	aliceKey := s.services[aliceUser].key
   219  	bobKey := s.services[bobUser].key
   220  
   221  	// Check bundle is ok
   222  	// No installation is enabled
   223  	aliceBundle, err := alice1.GetBundle(aliceKey)
   224  	s.Require().NoError(err)
   225  
   226  	// Check all installations are correctly working, and that the oldest device is not there
   227  	preKeys := aliceBundle.GetSignedPreKeys()
   228  	s.Require().Equal(3, len(preKeys))
   229  	s.Require().NotNil(preKeys["alice1"])
   230  	// alice2 being the oldest device is rotated out, as we reached the maximum
   231  	s.Require().Nil(preKeys["alice2"])
   232  	s.Require().NotNil(preKeys["alice3"])
   233  	s.Require().NotNil(preKeys["alice4"])
   234  
   235  	// We propagate this to bob
   236  	_, err = bob1.ProcessPublicBundle(bobKey, aliceBundle)
   237  	s.Require().NoError(err)
   238  
   239  	// Bob sends a message to alice
   240  	msg, err := bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
   241  	s.Require().NoError(err)
   242  	payload := msg.Message.GetEncryptedMessage()
   243  	s.Require().Equal(3, len(payload))
   244  	s.Require().NotNil(payload["alice1"])
   245  	s.Require().NotNil(payload["alice3"])
   246  	s.Require().NotNil(payload["alice4"])
   247  
   248  	// We disable the last installation
   249  	err = s.services[aliceUser].services[0].DisableInstallation(&aliceKey.PublicKey, "alice4")
   250  	s.Require().NoError(err)
   251  
   252  	// We check the bundle is updated
   253  	aliceBundle, err = alice1.GetBundle(aliceKey)
   254  	s.Require().NoError(err)
   255  
   256  	// Check all installations are there
   257  	preKeys = aliceBundle.GetSignedPreKeys()
   258  	s.Require().Equal(3, len(preKeys))
   259  	s.Require().NotNil(preKeys["alice1"])
   260  	s.Require().NotNil(preKeys["alice2"])
   261  	s.Require().NotNil(preKeys["alice3"])
   262  	// alice4 is disabled at this point, alice2 is back in
   263  	s.Require().Nil(preKeys["alice4"])
   264  
   265  	// We propagate this to bob
   266  	_, err = bob1.ProcessPublicBundle(bobKey, aliceBundle)
   267  	s.Require().NoError(err)
   268  
   269  	// Bob sends a message to alice
   270  	msg, err = bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
   271  	s.Require().NoError(err)
   272  	payload = msg.Message.GetEncryptedMessage()
   273  	s.Require().Equal(3, len(payload))
   274  	s.Require().NotNil(payload["alice1"])
   275  	s.Require().NotNil(payload["alice2"])
   276  	s.Require().NotNil(payload["alice3"])
   277  }
   278  
   279  func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevicesRefreshedBundle() {
   280  	alice1 := s.services[aliceUser].services[0]
   281  	alice2 := s.services[aliceUser].services[1]
   282  	alice3 := s.services[aliceUser].services[2]
   283  	alice4 := s.services[aliceUser].services[3]
   284  	bob1 := s.services[bobUser].services[0]
   285  	bobKey := s.services[bobUser].key
   286  	aliceKey := s.services[aliceUser].key
   287  
   288  	// We create alice bundles, in order
   289  	alice1Bundle, err := alice1.GetBundle(aliceKey)
   290  	s.Require().NoError(err)
   291  
   292  	alice2Bundle, err := alice2.GetBundle(aliceKey)
   293  	s.Require().NoError(err)
   294  
   295  	alice3Bundle, err := alice3.GetBundle(aliceKey)
   296  	s.Require().NoError(err)
   297  
   298  	alice4Bundle, err := alice4.GetBundle(aliceKey)
   299  	s.Require().NoError(err)
   300  
   301  	// We send all the bundles to bob
   302  	_, err = bob1.ProcessPublicBundle(bobKey, alice1Bundle)
   303  	s.Require().NoError(err)
   304  
   305  	_, err = bob1.ProcessPublicBundle(bobKey, alice2Bundle)
   306  	s.Require().NoError(err)
   307  
   308  	_, err = bob1.ProcessPublicBundle(bobKey, alice3Bundle)
   309  	s.Require().NoError(err)
   310  
   311  	_, err = bob1.ProcessPublicBundle(bobKey, alice4Bundle)
   312  	s.Require().NoError(err)
   313  
   314  	// Bob sends a message to alice
   315  	msg1, err := bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
   316  	s.Require().NoError(err)
   317  	payload := msg1.Message.GetEncryptedMessage()
   318  	s.Require().Equal(3, len(payload))
   319  	// Alice1 is the oldest bundle and is rotated out
   320  	// as we send maximum to 3 devices
   321  	s.Require().Nil(payload["alice1"])
   322  	s.Require().NotNil(payload["alice2"])
   323  	s.Require().NotNil(payload["alice3"])
   324  	s.Require().NotNil(payload["alice4"])
   325  
   326  	// We send a message to bob from alice1, the timestamp should be refreshed
   327  	msg2, err := alice1.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, []byte("test"))
   328  	s.Require().NoError(err)
   329  
   330  	alice1Bundle = msg2.Message.GetBundles()[0]
   331  
   332  	// Bob processes the bundle
   333  	_, err = bob1.ProcessPublicBundle(bobKey, alice1Bundle)
   334  	s.Require().NoError(err)
   335  
   336  	// Bob sends a message to alice
   337  	msg3, err := bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
   338  	s.Require().NoError(err)
   339  	payload = msg3.Message.GetEncryptedMessage()
   340  	s.Require().Equal(3, len(payload))
   341  	// Alice 1 is added back to the list of active devices
   342  	s.Require().NotNil(payload["alice1"])
   343  	// Alice 2 is rotated out as the oldest device in terms of activity
   344  	s.Require().Nil(payload["alice2"])
   345  	// Alice 3, 4 are still in
   346  	s.Require().NotNil(payload["alice3"])
   347  	s.Require().NotNil(payload["alice4"])
   348  }