Skip to content

[WIP] MSC4291 implementation #18685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions changelog.d/18685.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for MSC4291: Room IDs as hashes of create event.
40 changes: 40 additions & 0 deletions synapse/api/room_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ class EventFormatVersions:
ROOM_V1_V2 = 1 # $id:server event id format: used for room v1 and v2
ROOM_V3 = 2 # MSC1659-style $hash event id format: used for room v3
ROOM_V4_PLUS = 3 # MSC1884-style $hash format: introduced for room v4
ROOM_V11_PLUS_MSC4291 = 4 # MSC4291 room IDs as hashes: introduced for room MSC4291v11


KNOWN_EVENT_FORMAT_VERSIONS = {
EventFormatVersions.ROOM_V1_V2,
EventFormatVersions.ROOM_V3,
EventFormatVersions.ROOM_V4_PLUS,
EventFormatVersions.ROOM_V11_PLUS_MSC4291,
}


Expand Down Expand Up @@ -109,6 +111,8 @@ class RoomVersion:
msc3931_push_features: Tuple[str, ...] # values from PushRuleRoomFlag
# MSC3757: Restricting who can overwrite a state event
msc3757_enabled: bool
# MSC4291: Room IDs as hashes of the create event
msc4291_room_ids_as_hashes: bool


class RoomVersions:
Expand All @@ -131,6 +135,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V2 = RoomVersion(
"2",
Expand All @@ -151,6 +156,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V3 = RoomVersion(
"3",
Expand All @@ -171,6 +177,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V4 = RoomVersion(
"4",
Expand All @@ -191,6 +198,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V5 = RoomVersion(
"5",
Expand All @@ -211,6 +219,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V6 = RoomVersion(
"6",
Expand All @@ -231,6 +240,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V7 = RoomVersion(
"7",
Expand All @@ -251,6 +261,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V8 = RoomVersion(
"8",
Expand All @@ -271,6 +282,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V9 = RoomVersion(
"9",
Expand All @@ -291,6 +303,7 @@ class RoomVersions:
enforce_int_power_levels=False,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
V10 = RoomVersion(
"10",
Expand All @@ -311,6 +324,7 @@ class RoomVersions:
enforce_int_power_levels=True,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
MSC1767v10 = RoomVersion(
# MSC1767 (Extensible Events) based on room version "10"
Expand All @@ -332,6 +346,7 @@ class RoomVersions:
enforce_int_power_levels=True,
msc3931_push_features=(PushRuleRoomFlag.EXTENSIBLE_EVENTS,),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
MSC3757v10 = RoomVersion(
# MSC3757 (Restricting who can overwrite a state event) based on room version "10"
Expand All @@ -353,6 +368,7 @@ class RoomVersions:
enforce_int_power_levels=True,
msc3931_push_features=(),
msc3757_enabled=True,
msc4291_room_ids_as_hashes=False,
)
V11 = RoomVersion(
"11",
Expand All @@ -373,6 +389,7 @@ class RoomVersions:
enforce_int_power_levels=True,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=False,
)
MSC3757v11 = RoomVersion(
# MSC3757 (Restricting who can overwrite a state event) based on room version "11"
Expand All @@ -394,6 +411,28 @@ class RoomVersions:
enforce_int_power_levels=True,
msc3931_push_features=(),
msc3757_enabled=True,
msc4291_room_ids_as_hashes=False,
)
MSC4291v11 = RoomVersion(
"org.matrix.msc4291.11",
RoomDisposition.UNSTABLE,
EventFormatVersions.ROOM_V11_PLUS_MSC4291,
StateResolutionVersions.V2,
enforce_key_validity=True,
special_case_aliases_auth=False,
strict_canonicaljson=True,
limit_notifications_power_levels=True,
implicit_room_creator=True, # Used by MSC3820
updated_redaction_rules=True, # Used by MSC3820
restricted_join_rule=True,
restricted_join_rule_fix=True,
knock_join_rule=True,
msc3389_relation_redactions=False,
knock_restricted_join_rule=True,
enforce_int_power_levels=True,
msc3931_push_features=(),
msc3757_enabled=False,
msc4291_room_ids_as_hashes=True,
)


Expand All @@ -413,6 +452,7 @@ class RoomVersions:
RoomVersions.V11,
RoomVersions.MSC3757v10,
RoomVersions.MSC3757v11,
RoomVersions.MSC4291v11,
)
}

Expand Down
3 changes: 3 additions & 0 deletions synapse/crypto/event_signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ def compute_content_hash(
event_dict.pop("outlier", None)
event_dict.pop("destinations", None)

# N.B. no need to pop the room_id from create events in MSC4291 rooms
# as they shouldn't have one.

event_json_bytes = encode_canonical_json(event_dict)

hashed = hash_algorithm(event_json_bytes)
Expand Down
38 changes: 27 additions & 11 deletions synapse/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ async def check_state_independent_auth_rules(
f"Event {event.event_id} has unexpected auth_event for {k}: {auth_event_id}",
)

# We also need to check that the auth event itself is not rejected.
# 2.3 ... If there are entries which were themselves rejected under the checks performed on receipt
# of a PDU, reject.
if auth_event.rejected_reason:
raise AuthError(
403,
Expand All @@ -271,7 +272,7 @@ async def check_state_independent_auth_rules(

auth_dict[k] = auth_event_id

# 3. If event does not have a m.room.create in its auth_events, reject.
# 2.4. If event does not have a m.room.create in its auth_events, reject.
creation_event = auth_dict.get((EventTypes.Create, ""), None)
if not creation_event:
raise AuthError(403, "No create event in auth events")
Expand Down Expand Up @@ -311,13 +312,14 @@ def check_state_dependent_auth_rules(

# Later code relies on there being a create event e.g _can_federate, _is_membership_change_allowed
# so produce a more intelligible error if we don't have one.
if auth_dict.get(CREATE_KEY) is None:
create_event = auth_dict.get(CREATE_KEY)
if create_event is None:
raise AuthError(
403, f"Event {event.event_id} is missing a create event in auth_events."
)

# additional check for m.federate
creating_domain = get_domain_from_id(event.room_id)
creating_domain = get_domain_from_id(create_event.sender)
originating_domain = get_domain_from_id(event.sender)
if creating_domain != originating_domain:
if not _can_federate(event, auth_dict):
Expand Down Expand Up @@ -470,12 +472,20 @@ def _check_create(event: "EventBase") -> None:
if event.prev_event_ids():
raise AuthError(403, "Create event has prev events")

# 1.2 If the domain of the room_id does not match the domain of the sender,
# reject.
sender_domain = get_domain_from_id(event.sender)
room_id_domain = get_domain_from_id(event.room_id)
if room_id_domain != sender_domain:
raise AuthError(403, "Creation event's room_id domain does not match sender's")
if event.room_version.msc4291_room_ids_as_hashes:
# 1.2 If the create event has a room_id, reject
if "room_id" in event:
raise AuthError(403, "Create event has a room_id")
else:
# 1.2 If the domain of the room_id does not match the domain of the sender,
# reject.
if not event.room_version.msc4291_room_ids_as_hashes:
sender_domain = get_domain_from_id(event.sender)
room_id_domain = get_domain_from_id(event.room_id)
if room_id_domain != sender_domain:
raise AuthError(
403, "Creation event's room_id domain does not match sender's"
)

# 1.3 If content.room_version is present and is not a recognised version, reject
room_version_prop = event.content.get("room_version", "1")
Expand Down Expand Up @@ -533,7 +543,13 @@ def _is_membership_change_allowed(

target_user_id = event.state_key

creating_domain = get_domain_from_id(event.room_id)
# We need the create event in order to check if we can federate or not.
# If it's missing, yell loudly. Previously we only did this inside the
# _can_federate check.
create_event = auth_events.get((EventTypes.Create, ""))
if not create_event:
raise AuthError(403, "Create event missing from auth_events")
creating_domain = get_domain_from_id(create_event.sender)
target_domain = get_domain_from_id(target_user_id)
if creating_domain != target_domain:
if not _can_federate(event, auth_events):
Expand Down
Loading
Loading