Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit a58860e

Browse files
authored
Check sender_key matches on inbound encrypted events. (#6850)
If they don't then the device lists are probably out of sync.
1 parent 60d0672 commit a58860e

File tree

3 files changed

+67
-14
lines changed

3 files changed

+67
-14
lines changed

changelog.d/6850.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Detect unexpected sender keys on inbound encrypted events and resync device lists.

synapse/handlers/device.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,13 @@ def _handle_device_updates(self, user_id):
598598
# happens if we've missed updates.
599599
resync = yield self._need_to_do_resync(user_id, pending_updates)
600600

601-
logger.debug("Need to re-sync devices for %r? %r", user_id, resync)
601+
if logger.isEnabledFor(logging.INFO):
602+
logger.info(
603+
"Received device list update for %s, requiring resync: %s. Devices: %s",
604+
user_id,
605+
resync,
606+
", ".join(u[0] for u in pending_updates),
607+
)
602608

603609
if resync:
604610
yield self.user_device_resync(user_id)

synapse/handlers/federation.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -754,27 +754,73 @@ async def _process_received_pdu(
754754
# if we don't then we mark the device cache for that user as stale.
755755
if event.type == EventTypes.Encrypted:
756756
device_id = event.content.get("device_id")
757+
sender_key = event.content.get("sender_key")
758+
759+
cached_devices = await self.store.get_cached_devices_for_user(event.sender)
760+
761+
resync = False # Whether we should resync device lists.
762+
763+
device = None
757764
if device_id is not None:
758-
cached_devices = await self.store.get_cached_devices_for_user(
759-
event.sender
760-
)
761-
if device_id not in cached_devices:
765+
device = cached_devices.get(device_id)
766+
if device is None:
762767
logger.info(
763768
"Received event from remote device not in our cache: %s %s",
764769
event.sender,
765770
device_id,
766771
)
767-
await self.store.mark_remote_user_device_cache_as_stale(
768-
event.sender
772+
resync = True
773+
774+
# We also check if the `sender_key` matches what we expect.
775+
if sender_key is not None:
776+
# Figure out what sender key we're expecting. If we know the
777+
# device and recognize the algorithm then we can work out the
778+
# exact key to expect. Otherwise check it matches any key we
779+
# have for that device.
780+
if device:
781+
keys = device.get("keys", {}).get("keys", {})
782+
783+
if event.content.get("algorithm") == "m.megolm.v1.aes-sha2":
784+
# For this algorithm we expect a curve25519 key.
785+
key_name = "curve25519:%s" % (device_id,)
786+
current_keys = [keys.get(key_name)]
787+
else:
788+
# We don't know understand the algorithm, so we just
789+
# check it matches a key for the device.
790+
current_keys = keys.values()
791+
elif device_id:
792+
# We don't have any keys for the device ID.
793+
current_keys = []
794+
else:
795+
# The event didn't include a device ID, so we just look for
796+
# keys across all devices.
797+
current_keys = (
798+
key
799+
for device in cached_devices
800+
for key in device.get("keys", {}).get("keys", {}).values()
769801
)
770802

771-
# Immediately attempt a resync in the background
772-
if self.config.worker_app:
773-
return run_in_background(self._user_device_resync, event.sender)
774-
else:
775-
return run_in_background(
776-
self._device_list_updater.user_device_resync, event.sender
777-
)
803+
# We now check that the sender key matches (one of) the expected
804+
# keys.
805+
if sender_key not in current_keys:
806+
logger.info(
807+
"Received event from remote device with unexpected sender key: %s %s: %s",
808+
event.sender,
809+
device_id or "<no device_id>",
810+
sender_key,
811+
)
812+
resync = True
813+
814+
if resync:
815+
await self.store.mark_remote_user_device_cache_as_stale(event.sender)
816+
817+
# Immediately attempt a resync in the background
818+
if self.config.worker_app:
819+
return run_in_background(self._user_device_resync, event.sender)
820+
else:
821+
return run_in_background(
822+
self._device_list_updater.user_device_resync, event.sender
823+
)
778824

779825
@log_function
780826
async def backfill(self, dest, room_id, limit, extremities):

0 commit comments

Comments
 (0)