VYPR
High severityNVD Advisory· Published Sep 29, 2022· Updated Apr 23, 2025

When matrix-nio receives forwarded room keys, the receiver doesn't check if it requested the key from the forwarder

CVE-2022-39254

Description

matrix-nio is a Python Matrix client library, designed according to sans I/O principles. Prior to version 0.20, when a users requests a room key from their devices, the software correctly remember the request. Once they receive a forwarded room key, they accept it without checking who the room key came from. This allows homeservers to try to insert room keys of questionable validity, potentially mounting an impersonation attack. Version 0.20 fixes the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
matrix-nioPyPI
< 0.200.20

Affected products

1

Patches

1
b1cbf234a831

fix(crypto): Only accept forwarded room keys from our own trusted devices

https://github.com/poljar/matrix-nioDamir JelićSep 20, 2022via ghsa
1 file changed · +37 17
  • nio/crypto/olm_machine.py+37 17 modified
    @@ -1182,30 +1182,22 @@ def _handle_room_key_event(
     
             return event
     
    -    # This function is copyrighted under the Apache 2.0 license Zil0
    -    def _handle_forwarded_room_key_event(
    +    def _should_accept_forward(
             self,
    -        sender,  # type: str
    -        sender_key,  # type: str
    -        payload,  # type: Dict[Any, Any]
    -    ):
    -        # type: (...) -> Union[ForwardedRoomKeyEvent, BadEventType, None]
    -        event = ForwardedRoomKeyEvent.from_dict(payload, sender, sender_key)
    -
    -        if isinstance(event, (BadEvent, UnknownBadEvent)):
    -            return event
    -
    +        sender: str,
    +        sender_key: str,
    +        event: ForwardedRoomKeyEvent,
    +    ) -> bool:
             if event.algorithm != "m.megolm.v1.aes-sha2":
                 logger.error(
                     f"Error: unsupported forwarded room key of type {event.algorithm}"
                 )
    -            return None
    -
    -        if event.session_id not in self.outgoing_key_requests:
    +            return False
    +        elif event.session_id not in self.outgoing_key_requests:
                 logger.info(
                     "Ignoring session key we have not requested from device {}.", sender_key
                 )
    -            return None
    +            return False
     
             key_request = self.outgoing_key_requests[event.session_id]
     
    @@ -1218,6 +1210,34 @@ def _handle_forwarded_room_key_event(
                     "Ignoring session key with mismatched algorithm, room_id, or "
                     "session id."
                 )
    +            return False
    +
    +        device = self.device_store.device_from_sender_key(event.sender, sender_key)
    +
    +        # Only accept forwarded room keys from our own trusted devices
    +        if not device or not device.verified or not device.user_id == self.user_id:
    +            logger.warn(
    +                "Received a forwarded room key from a untrusted device "
    +                f"{event.sender}, {sender_key}"
    +            )
    +            return False
    +
    +        return True
    +
    +    # This function is copyrighted under the Apache 2.0 license Zil0
    +    def _handle_forwarded_room_key_event(
    +        self,
    +        sender,  # type: str
    +        sender_key,  # type: str
    +        payload,  # type: Dict[Any, Any]
    +    ):
    +        # type: (...) -> Union[ForwardedRoomKeyEvent, BadEventType, None]
    +        event = ForwardedRoomKeyEvent.from_dict(payload, sender, sender_key)
    +
    +        if isinstance(event, (BadEvent, UnknownBadEvent)):
    +            return event
    +
    +        if not self._should_accept_forward(sender, sender_key, event):
                 return None
     
             content = payload["content"]
    @@ -1241,7 +1261,7 @@ def _handle_forwarded_room_key_event(
             if self.inbound_group_store.add(session):
                 self.save_inbound_group_session(session)
     
    -        key_request = self.outgoing_key_requests.pop(key_request.request_id)
    +        key_request = self.outgoing_key_requests.pop(event.session_id)
             self.store.remove_outgoing_key_request(key_request)
             self.outgoing_to_device_messages.append(
                 key_request.as_cancellation(self.user_id, self.device_id)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.