Skip to content

WIP: receiving room key bundles #4989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions crates/matrix-sdk-crypto/src/machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ use crate::{
AnyIncomingResponse, KeysQueryRequest, OutgoingRequest, ToDeviceRequest,
UploadSigningKeysRequest,
},
room_history::RoomKeyBundle,
EventEncryptionAlgorithm, ProcessedToDeviceEvent, Signatures,
},
utilities::timestamp_to_iso8601,
Expand Down Expand Up @@ -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");

Expand Down
38 changes: 37 additions & 1 deletion crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -672,6 +672,42 @@ fn default_algorithm() -> EventEncryptionAlgorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2
}

impl TryFrom<&HistoricRoomKey> for InboundGroupSession {
type Error = SessionCreationError;

fn try_from(key: &HistoricRoomKey) -> Result<Self, Self::Error> {
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;

Expand Down
12 changes: 12 additions & 0 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

Expand Down
69 changes: 66 additions & 3 deletions crates/matrix-sdk/src/room/shared_room_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -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(())
}
Loading