Skip to content

Commit

Permalink
Merge branch 'main' into bnjbvr/permalink-mvp
Browse files Browse the repository at this point in the history
  • Loading branch information
Hywan committed Apr 25, 2024
2 parents 397a26e + 4156170 commit 25f893b
Show file tree
Hide file tree
Showing 16 changed files with 385 additions and 58 deletions.
10 changes: 4 additions & 6 deletions bindings/matrix-sdk-ffi/src/authentication_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
};

use matrix_sdk::{
encryption::BackupDownloadStrategy,
oidc::{
registrations::{ClientId, OidcRegistrations, OidcRegistrationsError},
types::{
Expand Down Expand Up @@ -621,12 +622,9 @@ impl AuthenticationService {
.passphrase(self.passphrase.clone())
.homeserver_url(homeserver_url)
.sliding_sync_proxy(sliding_sync_proxy)
.with_encryption_settings(matrix_sdk::encryption::EncryptionSettings {
auto_enable_cross_signing: true,
backup_download_strategy:
matrix_sdk::encryption::BackupDownloadStrategy::AfterDecryptionFailure,
auto_enable_backups: true,
})
.auto_enable_cross_signing(true)
.backup_download_strategy(BackupDownloadStrategy::AfterDecryptionFailure)
.auto_enable_backups(true)
.username(user_id.to_string());

if let Some(proxy) = &self.proxy {
Expand Down
49 changes: 37 additions & 12 deletions bindings/matrix-sdk-ffi/src/client_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub struct ClientBuilder {
cross_process_refresh_lock_id: Option<String>,
session_delegate: Option<Arc<dyn ClientSessionDelegate>>,
additional_root_certificates: Vec<Vec<u8>>,
encryption_settings: EncryptionSettings,
}

#[uniffi::export(async_runtime = "tokio")]
Expand All @@ -93,14 +94,16 @@ impl ClientBuilder {
proxy: None,
disable_ssl_verification: false,
disable_automatic_token_refresh: false,
inner: MatrixClient::builder().with_encryption_settings(EncryptionSettings {
auto_enable_cross_signing: false,
backup_download_strategy: BackupDownloadStrategy::AfterDecryptionFailure,
auto_enable_backups: false,
}),
inner: MatrixClient::builder(),
cross_process_refresh_lock_id: None,
session_delegate: None,
additional_root_certificates: Default::default(),
encryption_settings: EncryptionSettings {
auto_enable_cross_signing: false,
backup_download_strategy:
matrix_sdk::encryption::BackupDownloadStrategy::AfterDecryptionFailure,
auto_enable_backups: false,
},
})
}

Expand Down Expand Up @@ -203,21 +206,41 @@ impl ClientBuilder {
Arc::new(builder)
}

pub async fn build(self: Arc<Self>) -> Result<Arc<Client>, ClientBuildError> {
Ok(Arc::new(self.build_inner().await?))
pub fn auto_enable_cross_signing(
self: Arc<Self>,
auto_enable_cross_signing: bool,
) -> Arc<Self> {
let mut builder = unwrap_or_clone_arc(self);
builder.encryption_settings.auto_enable_cross_signing = auto_enable_cross_signing;
Arc::new(builder)
}
}

impl ClientBuilder {
pub(crate) fn with_encryption_settings(
/// Select a strategy to download room keys from the backup. By default
/// we download after a decryption failure.
///
/// Take a look at the [`BackupDownloadStrategy`] enum for more options.
pub fn backup_download_strategy(
self: Arc<Self>,
settings: EncryptionSettings,
backup_download_strategy: BackupDownloadStrategy,
) -> Arc<Self> {
let mut builder = unwrap_or_clone_arc(self);
builder.inner = builder.inner.with_encryption_settings(settings);
builder.encryption_settings.backup_download_strategy = backup_download_strategy;
Arc::new(builder)
}

/// Automatically create a backup version if no backup exists.
pub fn auto_enable_backups(self: Arc<Self>, auto_enable_backups: bool) -> Arc<Self> {
let mut builder = unwrap_or_clone_arc(self);
builder.encryption_settings.auto_enable_backups = auto_enable_backups;
Arc::new(builder)
}

pub async fn build(self: Arc<Self>) -> Result<Arc<Client>, ClientBuildError> {
Ok(Arc::new(self.build_inner().await?))
}
}

impl ClientBuilder {
pub(crate) fn enable_cross_process_refresh_lock_inner(
self: Arc<Self>,
process_id: String,
Expand Down Expand Up @@ -316,6 +339,8 @@ impl ClientBuilder {
);
}

inner_builder = inner_builder.with_encryption_settings(builder.encryption_settings);

let sdk_client = inner_builder.build().await?;

// At this point, `sdk_client` might contain a `sliding_sync_proxy` that has
Expand Down
7 changes: 6 additions & 1 deletion bindings/matrix-sdk-ffi/src/sync_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use std::{fmt::Debug, sync::Arc, time::Duration};

use futures_util::pin_mut;
use matrix_sdk::Client;
use matrix_sdk::{crypto::types::events::UtdCause, Client};
use matrix_sdk_ui::{
sync_service::{
State as MatrixSyncServiceState, SyncService as MatrixSyncService,
Expand Down Expand Up @@ -187,13 +187,18 @@ pub struct UnableToDecryptInfo {
///
/// If set, this is in milliseconds.
pub time_to_decrypt_ms: Option<u64>,

/// What we know about what caused this UTD. E.g. was this event sent when
/// we were not a member of this room?
pub cause: UtdCause,
}

impl From<SdkUnableToDecryptInfo> for UnableToDecryptInfo {
fn from(value: SdkUnableToDecryptInfo) -> Self {
Self {
event_id: value.event_id.to_string(),
time_to_decrypt_ms: value.time_to_decrypt.map(|ttd| ttd.as_millis() as u64),
cause: value.cause,
}
}
}
10 changes: 7 additions & 3 deletions bindings/matrix-sdk-ffi/src/timeline/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use std::{collections::HashMap, sync::Arc};

use matrix_sdk::room::power_levels::power_level_user_changes;
use matrix_sdk::{crypto::types::events::UtdCause, room::power_levels::power_level_user_changes};
use matrix_sdk_ui::timeline::{PollResult, TimelineDetails};
use tracing::warn;

Expand Down Expand Up @@ -214,6 +214,10 @@ pub enum EncryptedMessage {
MegolmV1AesSha2 {
/// The ID of the session used to encrypt the message.
session_id: String,

/// What we know about what caused this UTD. E.g. was this event sent
/// when we were not a member of this room?
cause: UtdCause,
},
Unknown,
}
Expand All @@ -227,9 +231,9 @@ impl EncryptedMessage {
let sender_key = sender_key.clone();
Self::OlmV1Curve25519AesSha2 { sender_key }
}
Message::MegolmV1AesSha2 { session_id, .. } => {
Message::MegolmV1AesSha2 { session_id, cause, .. } => {
let session_id = session_id.clone();
Self::MegolmV1AesSha2 { session_id }
Self::MegolmV1AesSha2 { session_id, cause: *cause }
}
Message::Unknown => Self::Unknown,
}
Expand Down
2 changes: 1 addition & 1 deletion bindings/matrix-sdk-ffi/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl Timeline {
/// Paginate forwards, when in focused mode.
///
/// Returns whether we hit the end of the timeline or not.
pub async fn paginate_forwards(&self, num_events: u16) -> Result<bool, ClientError> {
pub async fn focused_paginate_forwards(&self, num_events: u16) -> Result<bool, ClientError> {
Ok(self.inner.focused_paginate_forwards(num_events).await?)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ qrcode = ["matrix-sdk-crypto?/qrcode"]
automatic-room-key-forwarding = ["matrix-sdk-crypto?/automatic-room-key-forwarding"]
message-ids = ["matrix-sdk-crypto?/message-ids"]
experimental-sliding-sync = ["ruma/unstable-msc3575"]
uniffi = ["dep:uniffi"]
uniffi = ["dep:uniffi", "matrix-sdk-crypto?/uniffi"]

# helpers for testing features build upon this
testing = [
Expand Down
2 changes: 2 additions & 0 deletions crates/matrix-sdk-crypto/src/types/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ pub mod room_key_request;
pub mod room_key_withheld;
pub mod secret_send;
mod to_device;
mod utd_cause;

use ruma::serde::Raw;
pub use to_device::{ToDeviceCustomEvent, ToDeviceEvent, ToDeviceEvents};
pub use utd_cause::UtdCause;

/// A trait for event contents to define their event type.
pub trait EventType {
Expand Down
153 changes: 153 additions & 0 deletions crates/matrix-sdk-crypto/src/types/events/utd_cause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2024 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ruma::{events::AnySyncTimelineEvent, serde::Raw};
use serde::Deserialize;

/// Our best guess at the reason why an event can't be decrypted.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum UtdCause {
/// We don't have an explanation for why this UTD happened - it is probably
/// a bug, or a network split between the two homeservers.
#[default]
Unknown = 0,

/// This event was sent when we were not a member of the room (or invited),
/// so it is impossible to decrypt (without MSC3061).
Membership = 1,
//
// TODO: Other causes for UTDs. For example, this message is device-historical, information
// extracted from the WithheldCode in the MissingRoomKey object, or various types of Olm
// session problems.
//
// Note: This needs to be a simple enum so we can export it via FFI, so if more information
// needs to be provided, it should be through a separate type.
}

/// MSC4115 membership info in the unsigned area.
#[derive(Deserialize)]
struct UnsignedWithMembership {
#[serde(alias = "io.element.msc4115.membership")]
membership: Membership,
}

/// MSC4115 contents of the membership property
#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
enum Membership {
Leave,
Invite,
Join,
}

impl UtdCause {
/// Decide the cause of this UTD, based on the evidence we have.
pub fn determine(raw_event: Option<&Raw<AnySyncTimelineEvent>>) -> Self {
// TODO: in future, use more information to give a richer answer. E.g.
// is this event device-historical? Was the Olm communication disrupted?
// Did the sender refuse to send the key because we're not verified?

// Look in the unsigned area for a `membership` field.
if let Some(raw_event) = raw_event {
if let Ok(Some(unsigned)) = raw_event.get_field::<UnsignedWithMembership>("unsigned") {
if let Membership::Leave = unsigned.membership {
// We were not a member - this is the cause of the UTD
return UtdCause::Membership;
}
}
}

// We can't find an explanation for this UTD
UtdCause::Unknown
}
}

#[cfg(test)]
mod tests {
use ruma::{events::AnySyncTimelineEvent, serde::Raw};
use serde_json::{json, value::to_raw_value};

use crate::types::events::UtdCause;

#[test]
fn a_missing_raw_event_means_we_guess_unknown() {
// When we don't provide any JSON to check for membership, then we guess the UTD
// is unknown.
assert_eq!(UtdCause::determine(None), UtdCause::Unknown);
}

#[test]
fn if_there_is_no_membership_info_we_guess_unknown() {
// If our JSON contains no membership info, then we guess the UTD is unknown.
assert_eq!(UtdCause::determine(Some(&raw_event(json!({})))), UtdCause::Unknown);
}

#[test]
fn if_membership_info_cant_be_parsed_we_guess_unknown() {
// If our JSON contains a membership property but not the JSON we expected, then
// we guess the UTD is unknown.
assert_eq!(
UtdCause::determine(Some(&raw_event(json!({ "unsigned": { "membership": 3 } })))),
UtdCause::Unknown
);
}

#[test]
fn if_membership_is_invite_we_guess_unknown() {
// If membership=invite then we expected to be sent the keys so the cause of the
// UTD is unknown.
assert_eq!(
UtdCause::determine(Some(&raw_event(
json!({ "unsigned": { "membership": "invite" } }),
))),
UtdCause::Unknown
);
}

#[test]
fn if_membership_is_join_we_guess_unknown() {
// If membership=join then we expected to be sent the keys so the cause of the
// UTD is unknown.
assert_eq!(
UtdCause::determine(Some(&raw_event(json!({ "unsigned": { "membership": "join" } })))),
UtdCause::Unknown
);
}

#[test]
fn if_membership_is_leave_we_guess_membership() {
// If membership=leave then we have an explanation for why we can't decrypt,
// until we have MSC3061.
assert_eq!(
UtdCause::determine(Some(&raw_event(json!({ "unsigned": { "membership": "leave" } })))),
UtdCause::Membership
);
}

#[test]
fn if_unstable_prefix_membership_is_leave_we_guess_membership() {
// Before MSC4115 is merged, we support the unstable prefix too.
assert_eq!(
UtdCause::determine(Some(&raw_event(
json!({ "unsigned": { "io.element.msc4115.membership": "leave" } })
))),
UtdCause::Membership
);
}

fn raw_event(value: serde_json::Value) -> Raw<AnySyncTimelineEvent> {
Raw::from_json(to_raw_value(&value).unwrap())
}
}
2 changes: 1 addition & 1 deletion crates/matrix-sdk-ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ experimental-room-list-with-unified-invites = []
native-tls = ["matrix-sdk/native-tls"]
rustls-tls = ["matrix-sdk/rustls-tls"]

uniffi = ["dep:uniffi"]
uniffi = ["dep:uniffi", "matrix-sdk/uniffi", "matrix-sdk-base/uniffi"]

[dependencies]
as_variant = { workspace = true }
Expand Down
Loading

0 comments on commit 25f893b

Please sign in to comment.