From e8cf202c7ff23d8f9bb8670bf3bdae59be890227 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 29 Apr 2025 11:28:22 +0100 Subject: [PATCH] wip --- crates/matrix-sdk-crypto/src/machine/mod.rs | 38 ++++++++++ .../src/olm/group_sessions/inbound.rs | 38 +++++++++- crates/matrix-sdk/src/room/mod.rs | 12 ++++ .../src/room/shared_room_history.rs | 69 ++++++++++++++++++- 4 files changed, 153 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/machine/mod.rs b/crates/matrix-sdk-crypto/src/machine/mod.rs index cfe81e936..acd6aa9df 100644 --- a/crates/matrix-sdk-crypto/src/machine/mod.rs +++ b/crates/matrix-sdk-crypto/src/machine/mod.rs @@ -95,6 +95,7 @@ use crate::{ AnyIncomingResponse, KeysQueryRequest, OutgoingRequest, ToDeviceRequest, UploadSigningKeysRequest, }, + room_history::RoomKeyBundle, EventEncryptionAlgorithm, ProcessedToDeviceEvent, Signatures, }, utilities::timestamp_to_iso8601, @@ -968,6 +969,43 @@ impl OlmMachine { Ok(()) } + /// Import the contents of a downloaded and decrypted [MSC4268] key bundle. + /// + /// # Arguments + /// + /// * `bundle` - The decrypted and deserialized bundle itself. + /// * `room_id` - The room that we expect this bundle to correspond to. + /// * `sender_user` - The user that sent us the to-device message pointing + /// to this data. + /// * `sender_data` - Information on the sending device at the time we + /// received that message. + /// + /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268 + #[instrument(skip(bundle))] + pub async fn receive_room_key_bundle( + &self, + bundle: &RoomKeyBundle, + room_id: &RoomId, + sender_user: &UserId, + sender_data: &SenderData, + ) -> OlmResult<()> { + for key in bundle.room_keys.iter() { + if key.room_id != room_id { + warn!("Ignoring key for incorrect room {} in bundle", key.room_id); + continue; + } + + let _session = match InboundGroupSession::try_from(key) { + Ok(session) => session, + Err(e) => { + warn!("Ignoring key in bundle with import error: {}", e); + continue; + } + }; + } + Ok(()) + } + fn add_withheld_info(&self, changes: &mut Changes, event: &RoomKeyWithheldEvent) { debug!(?event.content, "Processing `m.room_key.withheld` event"); diff --git a/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs b/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs index ebd557768..cfb9ff69f 100644 --- a/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs +++ b/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs @@ -55,10 +55,10 @@ use crate::{ room::encrypted::{EncryptedEvent, RoomEventEncryptionScheme}, room_key, }, + room_history::HistoricRoomKey, serialize_curve_key, EventEncryptionAlgorithm, SigningKeys, }, }; - // TODO: add creation times to the inbound group sessions so we can export // sessions that were created between some time period, this should only be set // for non-imported sessions. @@ -672,6 +672,42 @@ fn default_algorithm() -> EventEncryptionAlgorithm { EventEncryptionAlgorithm::MegolmV1AesSha2 } +impl TryFrom<&HistoricRoomKey> for InboundGroupSession { + type Error = SessionCreationError; + + fn try_from(key: &HistoricRoomKey) -> Result { + let HistoricRoomKey { + algorithm, + room_id, + sender_key, + session_id, + session_key, + sender_claimed_keys, + } = key; + + let config = OutboundGroupSession::session_config(algorithm)?; + let session = InnerSession::import(session_key, config); + let first_known_index = session.first_known_index(); + + Ok(InboundGroupSession { + inner: Mutex::new(session).into(), + session_id: session_id.to_owned().into(), + creator_info: SessionCreatorInfo { + curve25519_key: *sender_key, + signing_keys: sender_claimed_keys.to_owned().into(), + }, + sender_data: SenderData::default(), + history_visibility: None.into(), + first_known_index, + room_id: room_id.to_owned(), + imported: true, + algorithm: algorithm.to_owned().into(), + backed_up: AtomicBool::from(false).into(), + shared_history: true, + }) + } +} + impl TryFrom<&ExportedRoomKey> for InboundGroupSession { type Error = SessionCreationError; diff --git a/crates/matrix-sdk/src/room/mod.rs b/crates/matrix-sdk/src/room/mod.rs index 5a594ba30..2367453fb 100644 --- a/crates/matrix-sdk/src/room/mod.rs +++ b/crates/matrix-sdk/src/room/mod.rs @@ -267,12 +267,24 @@ impl Room { false }); + #[cfg(all(feature = "experimental-share-history-on-invite", feature = "e2e-encryption"))] + let inviter = if prev_room_state == RoomState::Invited { + self.invite_details().await?.inviter + } else { + None + }; + self.client.join_room_by_id(self.room_id()).await?; if mark_as_direct { self.set_is_direct(true).await?; } + #[cfg(all(feature = "experimental-share-history-on-invite", feature = "e2e-encryption"))] + if let Some(inviter) = inviter { + shared_room_history::accept_key_bundle(&self, inviter.user_id()).await?; + } + Ok(()) } diff --git a/crates/matrix-sdk/src/room/shared_room_history.rs b/crates/matrix-sdk/src/room/shared_room_history.rs index a5b9e1292..4c305c15b 100644 --- a/crates/matrix-sdk/src/room/shared_room_history.rs +++ b/crates/matrix-sdk/src/room/shared_room_history.rs @@ -12,12 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::iter; +use std::{iter, ops::Deref}; -use ruma::OwnedUserId; +use matrix_sdk_base::media::{MediaFormat, MediaRequestParameters}; +use ruma::{api::client::authenticated_media, events::room::MediaSource, OwnedUserId, UserId}; use tracing::{info, instrument, warn}; -use crate::{crypto::types::events::room_key_bundle::RoomKeyBundleContent, Error, Result, Room}; +use crate::{ + crypto::types::{events::room_key_bundle::RoomKeyBundleContent, room_history::RoomKeyBundle}, + Error, Result, Room, +}; /// Share any shareable E2EE history in the given room with the given recipient, /// as per [MSC4268]. @@ -85,3 +89,62 @@ pub async fn share_room_history(room: &Room, user_id: OwnedUserId) -> Result<()> } Ok(()) } + +/// Having accepted an invite for the given room from the given user, download a +/// key bundle and import the room keys, as per [MSC4268]. +/// +/// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268 +#[instrument(skip(room), fields(room_id = ?room.room_id()))] +pub async fn accept_key_bundle(room: &Room, user_id: &UserId) -> Result<()> { + // TODO: retry this if it gets interrupted or it fails. + // TODO: do this in the background. + + let client = &room.client; + let olm_machine = client.olm_machine().await; + let Some(olm_machine) = olm_machine.deref() else { + warn!("Not fetching room key bundle as the Olm machine is not available"); + return Ok(()); + }; + + let Some(bundle) = + olm_machine.store().get_received_room_key_bundle_data(room.room_id(), user_id).await? + else { + // No bundle received (yet). + // TODO: deal with the bundle arriving later (https://github.com/matrix-org/matrix-rust-sdk/issues/4926) + return Ok(()); + }; + + // TODO + // olm_machine.store().clear_received_room_key_bundle_data(room.room_id(), + // user_id).await?; + + let bundle_content = client + .media() + .get_media_content( + &MediaRequestParameters { + source: MediaSource::Encrypted(Box::new(bundle.bundle_data.file)), + format: MediaFormat::File, + }, + false, + ) + .await?; + + let bundle_content: RoomKeyBundle = match serde_json::from_slice(&bundle_content) { + Ok(bundle_content) => bundle_content, + Err(err) => { + warn!("Failed to deserialize room key bundle: {}", err); + return Ok(()); + } + }; + + olm_machine + .receive_room_key_bundle( + &bundle_content, + room.room_id(), + &bundle.sender_user, + &bundle.sender_data, + ) + .await?; + + Ok(()) +}