[Fixes #9] Increase multidevice response (#16)

We only generate the bundle once every 6 hours (or if pairing
information changes).
This means that in some cases where many devices appear/disappear,
new devices are not picked up until the bundle is refreshed.

For example:

Alice creates a device with timestamp t1 A1
Alice creates a device with timestamp t2 A2
Alice creates a device with timestamp t3 A3
Alice creates a device with timestamp t4 A4

Alice propagates this to bob, bob's list of active devices for alice is now: t2 A2,
t3 A3, t4 A4 as we only keep maximum 3.

If Alice sends a message to Bob from A1, previously it would be still
using t1 (as long as less than 6 hours have passed).

Now instead the new bundle will have t5 instead, so upon receiving the
message bob will have: t3 A3, t4 A4, t5 A1.

This commit changes the behavior to always update the timestamp before
publishing the bundle, so that as soon as a message is received from a
new device, it will increase the timestamp for that device.
This commit is contained in:
Andrea Maria Piana 2019-07-17 08:42:27 +02:00 committed by GitHub
parent 5c6039f77f
commit 7623131be4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 2 deletions

View File

@ -265,3 +265,74 @@ func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevices() {
s.Require().NotNil(payload["alice2"])
s.Require().NotNil(payload["alice3"])
}
func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevicesRefreshedBundle() {
alice1 := s.services[aliceUser].services[0]
alice2 := s.services[aliceUser].services[1]
alice3 := s.services[aliceUser].services[2]
alice4 := s.services[aliceUser].services[3]
bob1 := s.services[bobUser].services[0]
bobKey := s.services[bobUser].key
aliceKey := s.services[aliceUser].key
// We create alice bundles, in order
alice1Bundle, err := alice1.GetBundle(aliceKey)
s.Require().NoError(err)
alice2Bundle, err := alice2.GetBundle(aliceKey)
s.Require().NoError(err)
alice3Bundle, err := alice3.GetBundle(aliceKey)
s.Require().NoError(err)
alice4Bundle, err := alice4.GetBundle(aliceKey)
s.Require().NoError(err)
// We send all the bundles to bob
_, err = bob1.ProcessPublicBundle(bobKey, alice1Bundle)
s.Require().NoError(err)
_, err = bob1.ProcessPublicBundle(bobKey, alice2Bundle)
s.Require().NoError(err)
_, err = bob1.ProcessPublicBundle(bobKey, alice3Bundle)
s.Require().NoError(err)
_, err = bob1.ProcessPublicBundle(bobKey, alice4Bundle)
s.Require().NoError(err)
// Bob sends a message to alice
msg1, err := bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
s.Require().NoError(err)
payload := msg1.Message.GetDirectMessage()
s.Require().Equal(3, len(payload))
// Alice1 is the oldest bundle and is rotated out
// as we send maximum to 3 devices
s.Require().Nil(payload["alice1"])
s.Require().NotNil(payload["alice2"])
s.Require().NotNil(payload["alice3"])
s.Require().NotNil(payload["alice4"])
// We send a message to bob from alice1, the timestamp should be refreshed
msg2, err := alice1.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("test"))
s.Require().NoError(err)
alice1Bundle = msg2.Message.GetBundle()
// Bob processes the bundle
_, err = bob1.ProcessPublicBundle(bobKey, alice1Bundle)
s.Require().NoError(err)
// Bob sends a message to alice
msg3, err := bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
s.Require().NoError(err)
payload = msg3.Message.GetDirectMessage()
s.Require().Equal(3, len(payload))
// Alice 1 is added back to the list of active devices
s.Require().NotNil(payload["alice1"])
// Alice 2 is rotated out as the oldest device in terms of activity
s.Require().Nil(payload["alice2"])
// Alice 3, 4 are still in
s.Require().NotNil(payload["alice3"])
s.Require().NotNil(payload["alice4"])
}

View File

@ -88,7 +88,8 @@ func (s *EncryptionServiceTestSuite) TestGetBundle() {
aliceBundle2, err := s.alice.GetBundle(aliceKey)
s.Require().NoError(err)
s.Equal(aliceBundle1, aliceBundle2, "It returns the same bundle")
s.Equal(aliceBundle1.GetSignedPreKeys(), aliceBundle2.GetSignedPreKeys(), "It returns the same signed pre keys")
s.NotEqual(aliceBundle1.Timestamp, aliceBundle2.Timestamp, "It refreshes the timestamp")
}
// Alice sends Bob an encrypted message with DH using an ephemeral key

View File

@ -44,7 +44,9 @@ func buildSignatureMaterial(bundle *Bundle) []byte {
}
// SignBundle signs the bundle and refreshes the timestamps
func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *BundleContainer) error {
bundleContainer.Bundle.Timestamp = time.Now().UnixNano()
signatureMaterial := buildSignatureMaterial(bundleContainer.GetBundle())
signature, err := crypto.Sign(crypto.Keccak256(signatureMaterial), identity)

View File

@ -81,7 +81,7 @@ func (s *MessengerSuite) SetupTest() {
config := whisper.DefaultConfig
config.MinimumAcceptedPOW = 0
shh := whisper.New(&config)
shh.Start(nil)
s.Require().NoError(shh.Start(nil))
s.m, err = NewMessenger(
s.privateKey,