CVE-2022-39252: Key Exchange without Entity Authentication (CWE-322)
value: matrix-rust-sdk is an implementation of a Matrix client-server library in Rust, and matrix-sdk-crypto is the Matrix encryption library. Prior to version 0.6, when a user requests a room key from their devices, the software correctly remembers the request. When the user receives a forwarded room key, the software accepts 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.6 fixes this issue.
CWE description: The product performs a key exchange with an actor without verifying the identity of that actor.
CVSS v3 base score: 7.5
Published: 2022-09-29T15:15Z
Repository: https://github.com/matrix-org/matrix-rust-sdk
Language: Rust
Files changed: crates/matrix-sdk-crypto/src/gossiping/machine.rs
Commit date: 2022-09-28 16:03:17 +0200
Commit message: fix(crypto): Only accept forwarded room keys from our own trusted devices

Diff: diff --git a/crates/matrix-sdk-crypto/src/gossiping/machine.rs b/crates/matrix-sdk-crypto/src/gossiping/machine.rs
index 42a3711d..f50104e5 100644
--- a/crates/matrix-sdk-crypto/src/gossiping/machine.rs
+++ b/crates/matrix-sdk-crypto/src/gossiping/machine.rs
@@ -943,10 +943,25 @@ impl GossipMachine {
                 Err(e.into())
             }
         }
     }
 
+    async fn should_accept_forward(
+        &self,
+        info: &GossipRequest,
+        sender_key: Curve25519PublicKey,
+    ) -> Result<bool, CryptoStoreError> {
+        let device =
+            self.store.get_device_from_curve_key(&info.request_recipient, sender_key).await?;
+
+        if let Some(device) = device {
+            Ok(device.user_id() == self.user_id() && device.is_verified())
+        } else {
+            Ok(false)
+        }
+    }
+
     /// Receive a forwarded room key event that was sent using any of our
     /// supported content types.
     async fn receive_supported_keys(
         &self,
         sender_key: Curve25519PublicKey,
@@ -954,12 +969,26 @@ impl GossipMachine {
         content: &ForwardedMegolmV1AesSha2Content,
     ) -> Result<Option<InboundGroupSession>, CryptoStoreError> {
         let algorithm = event.content.algorithm();
 
         if let Some(info) = self.get_key_info(event).await? {
-            self.accept_forwarded_room_key(&info, &event.sender, sender_key, algorithm, content)
-                .await
+            if self.should_accept_forward(&info, sender_key).await? {
+                self.accept_forwarded_room_key(&info, &event.sender, sender_key, algorithm, content)
+                    .await
+            } else {
+                warn!(
+                    sender = %event.sender,
+                    %sender_key,
+                    room_id = %content.room_id,
+                    session_id = content.session_id.as_str(),
+                    claimed_sender_key = %content.claimed_sender_key,
+                    "Received a forwarded room key from an unknown device, or \
+                     from a device that the key request recipient doesn't own",
+                );
+
+                Ok(None)
+            }
         } else {
             warn!(
                 sender = %event.sender,
                 sender_key = %sender_key,
                 room_id = %content.room_id,

Vulnerable code:             self.accept_forwarded_room_key(&info, &event.sender, sender_key, algorithm, content)
                .await
Fixed code:     async fn should_accept_forward(
        &self,
        info: &GossipRequest,
        sender_key: Curve25519PublicKey,
    ) -> Result<bool, CryptoStoreError> {
        let device =
            self.store.get_device_from_curve_key(&info.request_recipient, sender_key).await?;

        if let Some(device) = device {
            Ok(device.user_id() == self.user_id() && device.is_verified())
        } else {
            Ok(false)
        }
    }

            if self.should_accept_forward(&info, sender_key).await? {
                self.accept_forwarded_room_key(&info, &event.sender, sender_key, algorithm, content)
                    .await
            } else {
                warn!(
                    sender = %event.sender,
                    %sender_key,
                    room_id = %content.room_id,
                    session_id = content.session_id.as_str(),
                    claimed_sender_key = %content.claimed_sender_key,
                    "Received a forwarded room key from an unknown device, or \
                     from a device that the key request recipient doesn't own",
                );

                Ok(None)
            }