diff --git a/CHANGES.md b/CHANGES.md
index ab6fce3e7d..c2aa735908 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,23 @@
+Synapse 1.10.0rc2 (2020-02-06)
+==============================
+
+Bugfixes
+--------
+
+- Fix an issue with cross-signing where device signatures were not sent to remote servers. ([\#6844](https://github.com/matrix-org/synapse/issues/6844))
+- Fix to the unknown remote device detection which was introduced in 1.10.rc1. ([\#6848](https://github.com/matrix-org/synapse/issues/6848))
+
+
+Internal Changes
+----------------
+
+- Detect unexpected sender keys on remote encrypted events and resync device lists. ([\#6850](https://github.com/matrix-org/synapse/issues/6850))
+
+
Synapse 1.10.0rc1 (2020-01-31)
==============================
-**WARNING**: As of this release Synapse validates `client_secret` parameters in the Client-Server API as per the spec. See [\#6766](https://github.com/matrix-org/synapse/issues/6766) for details.
+**WARNING to client developers**: As of this release Synapse validates `client_secret` parameters in the Client-Server API as per the spec. See [\#6766](https://github.com/matrix-org/synapse/issues/6766) for details.
Features
diff --git a/synapse/__init__.py b/synapse/__init__.py
index bd942d3e1c..4f1859bd57 100644
--- a/synapse/__init__.py
+++ b/synapse/__init__.py
@@ -36,7 +36,7 @@ try:
except ImportError:
pass
-__version__ = "1.10.0rc1"
+__version__ = "1.10.0rc2"
if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)):
# We import here so that we don't have to install a bunch of deps when
diff --git a/synapse/api/constants.py b/synapse/api/constants.py
index 0ade47e624..cc8577552b 100644
--- a/synapse/api/constants.py
+++ b/synapse/api/constants.py
@@ -77,12 +77,11 @@ class EventTypes(object):
Aliases = "m.room.aliases"
Redaction = "m.room.redaction"
ThirdPartyInvite = "m.room.third_party_invite"
- Encryption = "m.room.encryption"
RelatedGroups = "m.room.related_groups"
RoomHistoryVisibility = "m.room.history_visibility"
CanonicalAlias = "m.room.canonical_alias"
- Encryption = "m.room.encryption"
+ Encrypted = "m.room.encrypted"
RoomAvatar = "m.room.avatar"
RoomEncryption = "m.room.encryption"
GuestAccess = "m.room.guest_access"
diff --git a/synapse/handlers/device.py b/synapse/handlers/device.py
index 26ef5e150c..a9bd431486 100644
--- a/synapse/handlers/device.py
+++ b/synapse/handlers/device.py
@@ -598,7 +598,13 @@ class DeviceListUpdater(object):
# happens if we've missed updates.
resync = yield self._need_to_do_resync(user_id, pending_updates)
- logger.debug("Need to re-sync devices for %r? %r", user_id, resync)
+ if logger.isEnabledFor(logging.INFO):
+ logger.info(
+ "Received device list update for %s, requiring resync: %s. Devices: %s",
+ user_id,
+ resync,
+ ", ".join(u[0] for u in pending_updates),
+ )
if resync:
yield self.user_device_resync(user_id)
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index 5728ea2ee7..8c6a142991 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -752,29 +752,75 @@ class FederationHandler(BaseHandler):
# For encrypted messages we check that we know about the sending device,
# if we don't then we mark the device cache for that user as stale.
- if event.type == EventTypes.Encryption:
+ if event.type == EventTypes.Encrypted:
device_id = event.content.get("device_id")
+ sender_key = event.content.get("sender_key")
+
+ cached_devices = await self.store.get_cached_devices_for_user(event.sender)
+
+ resync = False # Whether we should resync device lists.
+
+ device = None
if device_id is not None:
- cached_devices = await self.store.get_cached_devices_for_user(
- event.sender
- )
- if device_id not in cached_devices:
+ device = cached_devices.get(device_id)
+ if device is None:
logger.info(
"Received event from remote device not in our cache: %s %s",
event.sender,
device_id,
)
- await self.store.mark_remote_user_device_cache_as_stale(
- event.sender
+ resync = True
+
+ # We also check if the `sender_key` matches what we expect.
+ if sender_key is not None:
+ # Figure out what sender key we're expecting. If we know the
+ # device and recognize the algorithm then we can work out the
+ # exact key to expect. Otherwise check it matches any key we
+ # have for that device.
+ if device:
+ keys = device.get("keys", {}).get("keys", {})
+
+ if event.content.get("algorithm") == "m.megolm.v1.aes-sha2":
+ # For this algorithm we expect a curve25519 key.
+ key_name = "curve25519:%s" % (device_id,)
+ current_keys = [keys.get(key_name)]
+ else:
+ # We don't know understand the algorithm, so we just
+ # check it matches a key for the device.
+ current_keys = keys.values()
+ elif device_id:
+ # We don't have any keys for the device ID.
+ current_keys = []
+ else:
+ # The event didn't include a device ID, so we just look for
+ # keys across all devices.
+ current_keys = (
+ key
+ for device in cached_devices
+ for key in device.get("keys", {}).get("keys", {}).values()
)
- # Immediately attempt a resync in the background
- if self.config.worker_app:
- return run_in_background(self._user_device_resync, event.sender)
- else:
- return run_in_background(
- self._device_list_updater.user_device_resync, event.sender
- )
+ # We now check that the sender key matches (one of) the expected
+ # keys.
+ if sender_key not in current_keys:
+ logger.info(
+ "Received event from remote device with unexpected sender key: %s %s: %s",
+ event.sender,
+ device_id or "<no device_id>",
+ sender_key,
+ )
+ resync = True
+
+ if resync:
+ await self.store.mark_remote_user_device_cache_as_stale(event.sender)
+
+ # Immediately attempt a resync in the background
+ if self.config.worker_app:
+ return run_in_background(self._user_device_resync, event.sender)
+ else:
+ return run_in_background(
+ self._device_list_updater.user_device_resync, event.sender
+ )
@log_function
async def backfill(self, dest, room_id, limit, extremities):
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 1382399557..b609a65f47 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -360,7 +360,7 @@ class RoomCreationHandler(BaseHandler):
(EventTypes.RoomHistoryVisibility, ""),
(EventTypes.GuestAccess, ""),
(EventTypes.RoomAvatar, ""),
- (EventTypes.Encryption, ""),
+ (EventTypes.RoomEncryption, ""),
(EventTypes.ServerACL, ""),
(EventTypes.RelatedGroups, ""),
(EventTypes.PowerLevels, ""),
diff --git a/synapse/handlers/stats.py b/synapse/handlers/stats.py
index 7f7d56390e..68e6edace5 100644
--- a/synapse/handlers/stats.py
+++ b/synapse/handlers/stats.py
@@ -286,7 +286,7 @@ class StatsHandler(StateDeltasHandler):
room_state["history_visibility"] = event_content.get(
"history_visibility"
)
- elif typ == EventTypes.Encryption:
+ elif typ == EventTypes.RoomEncryption:
room_state["encryption"] = event_content.get("algorithm")
elif typ == EventTypes.Name:
room_state["name"] = event_content.get("name")
diff --git a/synapse/storage/data_stores/main/stats.py b/synapse/storage/data_stores/main/stats.py
index 7bc186e9a1..7af1495e47 100644
--- a/synapse/storage/data_stores/main/stats.py
+++ b/synapse/storage/data_stores/main/stats.py
@@ -744,7 +744,7 @@ class StatsStore(StateDeltasStore):
EventTypes.Create,
EventTypes.JoinRules,
EventTypes.RoomHistoryVisibility,
- EventTypes.Encryption,
+ EventTypes.RoomEncryption,
EventTypes.Name,
EventTypes.Topic,
EventTypes.RoomAvatar,
@@ -816,7 +816,7 @@ class StatsStore(StateDeltasStore):
room_state["history_visibility"] = event.content.get(
"history_visibility"
)
- elif event.type == EventTypes.Encryption:
+ elif event.type == EventTypes.RoomEncryption:
room_state["encryption"] = event.content.get("algorithm")
elif event.type == EventTypes.Name:
room_state["name"] = event.content.get("name")
|