diff --git a/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs b/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs index 40329522028..aaf3154ff23 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs @@ -79,6 +79,7 @@ pub fn make_test_event_with_event_id( let encryption_info = Arc::new(EncryptionInfo { sender: (*ALICE).into(), sender_device: None, + forwarder: None, algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: "1337".to_owned(), sender_claimed_keys: Default::default(), diff --git a/crates/matrix-sdk-common/CHANGELOG.md b/crates/matrix-sdk-common/CHANGELOG.md index 1a9cb7d7d22..034190daed9 100644 --- a/crates/matrix-sdk-common/CHANGELOG.md +++ b/crates/matrix-sdk-common/CHANGELOG.md @@ -14,6 +14,8 @@ All notable changes to this project will be documented in this file. ### Features +- Add field `forwarder` of type `ForwarderInfo` to `EncryptionInfo`, which which exposes information about the forwarder of the keys with which an event was encrypted if they were shared as part of an [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268) room key bundle. + ([#5945](https://github.com/matrix-org/matrix-rust-sdk/pull/5945)). - [**breaking**] Cross-process lock can be dirty. The `CrossProcess::try_lock_once` now returns a new type `CrossProcessResult`, which is an enum with `Clean`, `Dirty` or `Unobtained` variants. When the lock is dirty it means it's been acquired once, then acquired another time from another holder, so the current holder may want to refresh its internal state. ([#5672](https://github.com/matrix-org/matrix-rust-sdk/pull/5672)). diff --git a/crates/matrix-sdk-common/src/deserialized_responses.rs b/crates/matrix-sdk-common/src/deserialized_responses.rs index a0211abc79d..f00fff36b2b 100644 --- a/crates/matrix-sdk-common/src/deserialized_responses.rs +++ b/crates/matrix-sdk-common/src/deserialized_responses.rs @@ -319,6 +319,16 @@ pub enum AlgorithmInfo { }, } +/// Struct containing information on the forwarder of the keys used to decrypt +/// an event. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ForwarderInfo { + /// The user ID of the forwarder. + pub user_id: OwnedUserId, + /// The device ID of the forwarder. + pub device_id: OwnedDeviceId, +} + /// Struct containing information on how an event was decrypted. #[derive(Clone, Debug, PartialEq, Serialize)] pub struct EncryptionInfo { @@ -328,6 +338,11 @@ pub struct EncryptionInfo { /// The device ID of the device that sent us the event, note this is /// untrusted data unless `verification_state` is `Verified` as well. pub sender_device: Option, + /// If the keys for this message were shared-on-invite as part of an + /// [MSC4268] key bundle, information about the forwarder. + /// + /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268 + pub forwarder: Option, /// Information about the algorithm that was used to encrypt the event. pub algorithm_info: AlgorithmInfo, /// The verification state of the device that sent us the event, note this @@ -361,14 +376,21 @@ impl<'de> Deserialize<'de> for EncryptionInfo { struct Helper { pub sender: OwnedUserId, pub sender_device: Option, + pub forwarder: Option, pub algorithm_info: AlgorithmInfo, pub verification_state: VerificationState, #[serde(rename = "session_id")] pub old_session_id: Option, } - let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } = - Helper::deserialize(deserializer)?; + let Helper { + sender, + sender_device, + forwarder, + algorithm_info, + verification_state, + old_session_id, + } = Helper::deserialize(deserializer)?; let algorithm_info = match algorithm_info { AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => { @@ -382,7 +404,7 @@ impl<'de> Deserialize<'de> for EncryptionInfo { other => other, }; - Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state }) + Ok(EncryptionInfo { sender, sender_device, forwarder, algorithm_info, verification_state }) } } @@ -1617,6 +1639,7 @@ mod tests { encryption_info: Arc::new(EncryptionInfo { sender: user_id!("@sender:example.com").to_owned(), sender_device: None, + forwarder: None, algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: "xxx".to_owned(), sender_claimed_keys: Default::default(), @@ -1657,6 +1680,7 @@ mod tests { "encryption_info": { "sender": "@sender:example.com", "sender_device": null, + "forwarder": null, "algorithm_info": { "MegolmV1AesSha2": { "curve25519_key": "xxx", @@ -2041,6 +2065,7 @@ mod tests { let info = EncryptionInfo { sender: user_id!("@alice:localhost").to_owned(), sender_device: Some(device_id!("ABCDEFGH").to_owned()), + forwarder: None, algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: "curvecurvecurve".into(), sender_claimed_keys: Default::default(), @@ -2062,6 +2087,7 @@ mod tests { encryption_info: Arc::new(EncryptionInfo { sender: user_id!("@sender:example.com").to_owned(), sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()), + forwarder: None, algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: "xxx".to_owned(), sender_claimed_keys: BTreeMap::from([ diff --git a/crates/matrix-sdk-common/src/snapshots/matrix_sdk_common__deserialized_responses__tests__encryption_info_migration.snap b/crates/matrix-sdk-common/src/snapshots/matrix_sdk_common__deserialized_responses__tests__encryption_info_migration.snap index 51377c846b8..61070b2b2b3 100644 --- a/crates/matrix-sdk-common/src/snapshots/matrix_sdk_common__deserialized_responses__tests__encryption_info_migration.snap +++ b/crates/matrix-sdk-common/src/snapshots/matrix_sdk_common__deserialized_responses__tests__encryption_info_migration.snap @@ -5,6 +5,7 @@ expression: deserialized { "sender": "@alice:localhost", "sender_device": "ABCDEFGH", + "forwarder": null, "algorithm_info": { "MegolmV1AesSha2": { "curve25519_key": "curvecurvecurve", diff --git a/crates/matrix-sdk-common/src/snapshots/snapshot_test_encryption_info.snap b/crates/matrix-sdk-common/src/snapshots/snapshot_test_encryption_info.snap index ed209f9e0cb..e0ca8bf37ee 100644 --- a/crates/matrix-sdk-common/src/snapshots/snapshot_test_encryption_info.snap +++ b/crates/matrix-sdk-common/src/snapshots/snapshot_test_encryption_info.snap @@ -5,6 +5,7 @@ expression: info { "sender": "@alice:localhost", "sender_device": "ABCDEFGH", + "forwarder": null, "algorithm_info": { "MegolmV1AesSha2": { "curve25519_key": "curvecurvecurve", diff --git a/crates/matrix-sdk-common/src/snapshots/snapshot_test_sync_timeline_event.snap b/crates/matrix-sdk-common/src/snapshots/snapshot_test_sync_timeline_event.snap index c4ece33c750..dc9010e36c8 100644 --- a/crates/matrix-sdk-common/src/snapshots/snapshot_test_sync_timeline_event.snap +++ b/crates/matrix-sdk-common/src/snapshots/snapshot_test_sync_timeline_event.snap @@ -16,6 +16,7 @@ expression: "serde_json::to_value(&room_event).unwrap()" "session_id": "mysessionid112" } }, + "forwarder": null, "sender": "@sender:example.com", "sender_device": "ABCDEFGHIJ", "verification_state": "Verified" diff --git a/crates/matrix-sdk-crypto/CHANGELOG.md b/crates/matrix-sdk-crypto/CHANGELOG.md index 92316bd08d6..21aaf2d6628 100644 --- a/crates/matrix-sdk-crypto/CHANGELOG.md +++ b/crates/matrix-sdk-crypto/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. ### Features +- Added a new field `forwarder` to `InboundGroupSession` of type `ForwarderData`, which stores information about the forwarder of a session shared in a room key bundle under [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268). + ([#5980])(https://github.com/matrix-org/matrix-rust-sdk/pull/5980) - The `OutboundGroupSession` and `OlmMachine` now return the `EncryptionInfo` used when encrypting raw events. ([#5936](https://github.com/matrix-org/matrix-rust-sdk/pull/5936)) diff --git a/crates/matrix-sdk-crypto/src/machine/mod.rs b/crates/matrix-sdk-crypto/src/machine/mod.rs index 6b3cb8db975..bcfd9088813 100644 --- a/crates/matrix-sdk-crypto/src/machine/mod.rs +++ b/crates/matrix-sdk-crypto/src/machine/mod.rs @@ -26,7 +26,7 @@ use matrix_sdk_common::deserialized_responses::WithheldCode; use matrix_sdk_common::{ BoxFuture, deserialized_responses::{ - AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, + AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ForwarderInfo, ProcessedToDeviceEvent, ToDeviceUnableToDecryptInfo, ToDeviceUnableToDecryptReason, UnableToDecryptInfo, UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel, VerificationState, @@ -1136,6 +1136,7 @@ impl OlmMachine { EncryptionInfo { sender: self.inner.user_id.clone(), sender_device: Some(self.inner.device_id.clone()), + forwarder: None, algorithm_info, verification_state: VerificationState::Verified, } @@ -2016,11 +2017,18 @@ impl OlmMachine { let (verification_state, device_id) = self.get_room_event_verification_state(session, sender).await?; - let sender = sender.to_owned(); - Ok(Arc::new(EncryptionInfo { - sender, + sender: sender.to_owned(), sender_device: device_id, + forwarder: session.forwarder_data.as_ref().and_then(|data| { + // Per the comment on `KnownSenderData::device_id`, we should never encounter a + // `None` value here, but must still deal with an `Optional` for backwards + // compatibility. The approach below allows us to avoid unwrapping. + data.device_id().map(|device_id| ForwarderInfo { + device_id: device_id.to_owned(), + user_id: data.user_id().to_owned(), + }) + }), algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: session.sender_key().to_base64(), sender_claimed_keys: session diff --git a/crates/matrix-sdk-crypto/src/olm/account.rs b/crates/matrix-sdk-crypto/src/olm/account.rs index 30edc4fea37..88c43c973f8 100644 --- a/crates/matrix-sdk-crypto/src/olm/account.rs +++ b/crates/matrix-sdk-crypto/src/olm/account.rs @@ -1704,6 +1704,7 @@ impl Account { EncryptionInfo { sender: sender_id.to_owned(), sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()), + forwarder: None, algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 { curve25519_public_key_base64: sender_key.to_base64(), }, diff --git a/crates/matrix-sdk-ui/src/timeline/controller/decryption_retry_task.rs b/crates/matrix-sdk-ui/src/timeline/controller/decryption_retry_task.rs index 211841491d7..68fa53bb0ed 100644 --- a/crates/matrix-sdk-ui/src/timeline/controller/decryption_retry_task.rs +++ b/crates/matrix-sdk-ui/src/timeline/controller/decryption_retry_task.rs @@ -296,6 +296,7 @@ mod tests { encryption_info: Some(Arc::new(EncryptionInfo { sender: owned_user_id!("@u:s.co"), sender_device: None, + forwarder: None, algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: "".to_owned(), sender_claimed_keys: BTreeMap::new(), diff --git a/crates/matrix-sdk-ui/src/timeline/tests/edit.rs b/crates/matrix-sdk-ui/src/timeline/tests/edit.rs index 4d6dcba969c..7ffbc25948f 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/edit.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/edit.rs @@ -163,6 +163,7 @@ async fn test_edit_updates_encryption_info() { let mut encryption_info = Arc::new(EncryptionInfo { sender: (*ALICE).into(), sender_device: None, + forwarder: None, algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { curve25519_key: "123".to_owned(), sender_claimed_keys: BTreeMap::new(), diff --git a/crates/matrix-sdk/tests/integration/encryption/shared_history.rs b/crates/matrix-sdk/tests/integration/encryption/shared_history.rs index 84fe576311c..5e4c694ad98 100644 --- a/crates/matrix-sdk/tests/integration/encryption/shared_history.rs +++ b/crates/matrix-sdk/tests/integration/encryption/shared_history.rs @@ -195,6 +195,13 @@ async fn test_shared_history_out_of_order() { .await .expect("Bob should be able to fetch the event Alice has sent"); + let encryption_info = event.encryption_info().expect("Event did not have encryption info"); + + // Check Bob stored information about the key forwarder. + let forwarder_info = encryption_info.forwarder.as_ref().unwrap(); + assert_eq!(forwarder_info.user_id, alice_user_id); + assert_eq!(forwarder_info.device_id, alice_device_id); + assert_decrypted_message_eq!( event, "It's a secret to everybody",