When matrix-nio receives forwarded room keys, the receiver doesn't check if it requested the key from the forwarder
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.
| Package | Affected versions | Patched versions |
|---|---|---|
matrix-nioPyPI | < 0.20 | 0.20 |
Affected products
1- Range: < 0.20
Patches
1b1cbf234a831fix(crypto): Only accept forwarded room keys from our own trusted devices
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- github.com/advisories/GHSA-w4pr-4vjg-hffhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-39254ghsaADVISORY
- github.com/poljar/matrix-nio/commit/b1cbf234a831daa160673defd596e6450e9c29f0ghsax_refsource_MISCWEB
- github.com/poljar/matrix-nio/security/advisories/GHSA-w4pr-4vjg-hffhghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.