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 }