diff options
author | Patrick Cloke <patrickc@matrix.org> | 2023-07-11 14:35:34 -0400 |
---|---|---|
committer | Patrick Cloke <patrickc@matrix.org> | 2023-07-17 11:05:43 -0400 |
commit | b38ba4a8b1d6816f195330df5b2188796fc4761d (patch) | |
tree | 12390ea61593e9a796cde7469de8458fc1103de7 | |
parent | Ignore non-state events sent in state. (diff) | |
download | synapse-b38ba4a8b1d6816f195330df5b2188796fc4761d.tar.xz |
Handle LPDU content hash.
-rw-r--r-- | synapse/crypto/event_signing.py | 61 | ||||
-rw-r--r-- | synapse/events/__init__.py | 6 | ||||
-rw-r--r-- | synapse/federation/federation_base.py | 4 |
3 files changed, 55 insertions, 16 deletions
diff --git a/synapse/crypto/event_signing.py b/synapse/crypto/event_signing.py index 1a293f1df0..ef6c763433 100644 --- a/synapse/crypto/event_signing.py +++ b/synapse/crypto/event_signing.py @@ -36,31 +36,37 @@ logger = logging.getLogger(__name__) Hasher = Callable[[bytes], "hashlib._Hash"] -@trace -def check_event_content_hash( - event: EventBase, hash_algorithm: Hasher = hashlib.sha256 +def _check_dict_hash( + event_id: str, + hash_log: str, + hashes: Any, + d: JsonDict, + hash_algorithm: Hasher = hashlib.sha256, ) -> bool: - """Check whether the hash for this PDU matches the contents""" - name, expected_hash = compute_content_hash(event.get_pdu_json(), hash_algorithm) + name, expected_hash = compute_content_hash(d, hash_algorithm) logger.debug( - "Verifying content hash on %s (expecting: %s)", - event.event_id, + "Verifying %s hash on %s (expecting: %s)", + hash_log, + event_id, encode_base64(expected_hash), ) - # some malformed events lack a 'hashes'. Protect against it being missing - # or a weird type by basically treating it the same as an unhashed event. - hashes = event.get("hashes") # nb it might be a immutabledict or a dict if not isinstance(hashes, collections.abc.Mapping): raise SynapseError( - 400, "Malformed 'hashes': %s" % (type(hashes),), Codes.UNAUTHORIZED + 400, + "Malformed %s hashes: %s" + % ( + hash_log, + type(hashes), + ), + Codes.UNAUTHORIZED, ) if name not in hashes: raise SynapseError( 400, - "Algorithm %s not in hashes %s" % (name, list(hashes)), + "Algorithm %s not in %s hashes %s" % (name, hash_log, list(hashes)), Codes.UNAUTHORIZED, ) message_hash_base64 = hashes[name] @@ -73,6 +79,37 @@ def check_event_content_hash( return message_hash_bytes == expected_hash +@trace +def check_event_content_hash( + event: EventBase, hash_algorithm: Hasher = hashlib.sha256 +) -> bool: + """Check whether the hash for this PDU matches the contents""" + + # some malformed events lack a 'hashes'. Protect against it being missing + # or a weird type by basically treating it the same as an unhashed event. + hashes = event.get("hashes") + + if not _check_dict_hash( + event.event_id, "content", hashes, event.get_pdu_json(), hash_algorithm + ): + return False + + # Check the content hash of the LPDU, if this was sent via a hub. + if event.room_version.linearized_matrix and event.hub_server: + # hashes must be a dictionary to have passed _check_dict_hash above. + lpdu_hashes = hashes.get("lpdu") + return _check_dict_hash( + event.event_id, + "linearized content", + lpdu_hashes, + event.get_linearized_pdu_json(), + hash_algorithm, + ) + + # Non-linearized matrix doesn't care about other checks. + return True + + def compute_content_hash( event_dict: Dict[str, Any], hash_algorithm: Hasher ) -> Tuple[str, bytes]: diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py index d628abc0a6..99b9d61847 100644 --- a/synapse/events/__init__.py +++ b/synapse/events/__init__.py @@ -389,6 +389,9 @@ class EventBase(metaclass=abc.ABCMeta): return pdu_json + def get_linearized_pdu_json(self) -> JsonDict: + raise NotImplementedError() + def get_templated_pdu_json(self) -> JsonDict: """ Return a JSON object suitable for a templated event, as used in the @@ -620,7 +623,8 @@ class FrozenLinearizedEvent(FrozenEventV3): def get_linearized_pdu_json(self) -> JsonDict: # Get the full PDU and then remove fields from it. pdu = self.get_pdu_json() - pdu.pop("hashes") + # Strip everything except for the lpdu property from the hashes. + pdu["hashes"] = {"lpdu": pdu["hashes"]["lpdu"]} pdu.pop("auth_events") pdu.pop("prev_events") return pdu diff --git a/synapse/federation/federation_base.py b/synapse/federation/federation_base.py index a0c5048efc..21cb3e4675 100644 --- a/synapse/federation/federation_base.py +++ b/synapse/federation/federation_base.py @@ -20,7 +20,7 @@ from synapse.api.errors import Codes, SynapseError from synapse.api.room_versions import EventFormatVersions, RoomVersion from synapse.crypto.event_signing import check_event_content_hash from synapse.crypto.keyring import Keyring -from synapse.events import EventBase, FrozenLinearizedEvent, make_event_from_dict +from synapse.events import EventBase, make_event_from_dict from synapse.events.utils import prune_event, prune_event_dict, validate_canonicaljson from synapse.http.servlet import assert_params_in_dict from synapse.logging.opentracing import log_kv, trace @@ -253,8 +253,6 @@ async def _check_sigs_on_pdu( # If this is a linearized PDU we may need to check signatures of the hub # and sender. if room_version.event_format == EventFormatVersions.LINEARIZED: - assert isinstance(pdu, FrozenLinearizedEvent) - # If the event was sent via a hub server, check the signature of the # sender against the Linear PDU. (But only if the sender isn't the hub.) # |