Drop federation signing key updates from non-compliant user IDs

This commit is contained in:
Paul Chobert committed 2026-10-09 15:24:07 +02:00
1 parent 1fc46fdbb9
commit 8c3c35e95d
2 files changed
+48

No files matched your search

+9
View File
@@ -44,6 +44,7 @@ from synapse.types import (
UserID, UserID,
get_domain_from_id, get_domain_from_id,
get_verify_key_from_cross_signing_key, get_verify_key_from_cross_signing_key,
is_compliant_user_id,
) )
from synapse.util.async_helpers import Linearizer, concurrently_execute from synapse.util.async_helpers import Linearizer, concurrently_execute
from synapse.util.cancellation import cancellable from synapse.util.cancellation import cancellable
@@ -1835,6 +1836,14 @@ class SigningKeyEduUpdater:
logger.warning("Got signing key update edu for %r from %r", user_id, origin) logger.warning("Got signing key update edu for %r from %r", user_id, origin)
return return
if not is_compliant_user_id(user_id):
logger.warning(
"Dropping signing key update edu for non-compliant user ID %r from %r",
user_id,
origin,
)
return
room_ids = await self.store.get_rooms_for_user(user_id) room_ids = await self.store.get_rooms_for_user(user_id)
if not room_ids: if not room_ids:
# We don't share any rooms with this user. Ignore update, as we # We don't share any rooms with this user. Ignore update, as we
@@ -23,6 +23,7 @@ from unittest.mock import AsyncMock, Mock
from twisted.internet.testing import MemoryReactor from twisted.internet.testing import MemoryReactor
from synapse.api.constants import EduTypes
from synapse.handlers.device import DeviceListUpdater from synapse.handlers.device import DeviceListUpdater
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.types import JsonDict from synapse.types import JsonDict
@@ -155,6 +156,44 @@ class DeviceListResyncTestCase(unittest.HomeserverTestCase):
) )
self.assertNotIn(remote_user_id, need_resync) self.assertNotIn(remote_user_id, need_resync)
def test_non_compliant_user_id_signing_key_update_dropped(self) -> None:
"""Tests that signing key updates from non-compliant (grandfathered
historical) user IDs are dropped, while those from compliant user IDs
sharing the same room are stored.
"""
compliant_user_id = "@john:test_remote"
non_compliant_user_id = "@héllo:test_remote"
remote_origin = "test_remote"
remote_master_key = "85T7JXPFBAySB/jwby4S3lBPTqY3+Zg53nYuGmu1ggY"
# Share room with both users.
self.store.get_rooms_for_user = AsyncMock(return_value=["!someroom:test"])
federation_registry = self.hs.get_federation_registry()
for user_id in (compliant_user_id, non_compliant_user_id):
self.get_success(
federation_registry.on_edu(
EduTypes.SIGNING_KEY_UPDATE,
remote_origin,
{
"user_id": user_id,
"master_key": {
"user_id": user_id,
"usage": ["master"],
"keys": {"ed25519:" + remote_master_key: remote_master_key},
},
},
)
)
keys = self.get_success(
self.store.get_e2e_cross_signing_keys_bulk(
user_ids=[compliant_user_id, non_compliant_user_id]
),
)
self.assertIsNotNone(keys[compliant_user_id])
self.assertIsNone(keys[non_compliant_user_id])
def test_cross_signing_keys_retry(self) -> None: def test_cross_signing_keys_retry(self) -> None:
"""Tests that resyncing a device list correctly processes cross-signing keys from """Tests that resyncing a device list correctly processes cross-signing keys from
the remote server. the remote server.