From 01842fbd51425dbcc6ca123a4c3c8106022caa7a Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 27 Sep 2023 14:20:15 +0200 Subject: [PATCH 1/2] Expose secret request API --- bindings/matrix-sdk-crypto-ffi/src/machine.rs | 10 ++ crates/matrix-sdk-crypto/src/machine.rs | 133 +++++++++++++++++- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-crypto-ffi/src/machine.rs b/bindings/matrix-sdk-crypto-ffi/src/machine.rs index 5ab3af7b1e6..af50a0a06f9 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/machine.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/machine.rs @@ -1239,6 +1239,16 @@ impl OlmMachine { Ok(()) } + /// Request missing local secrets from our devices (cross signing private + /// keys, megolm backup). This will ask the sdk to create outgoing + /// request to get the missing secrets. + /// + /// The requests will be processed as soon as `outgoing_requests()` is + /// called to process them. + pub fn query_missing_secrets_from_other_sessions(&self) -> Result { + Ok(self.runtime.block_on(self.inner.query_missing_secrets_from_other_sessions())?) + } + /// Activate the given backup key to be used with the given backup version. /// /// **Warning**: The caller needs to make sure that the given `BackupKey` is diff --git a/crates/matrix-sdk-crypto/src/machine.rs b/crates/matrix-sdk-crypto/src/machine.rs index ee5a1a9c9b4..b5cbcbf79de 100644 --- a/crates/matrix-sdk-crypto/src/machine.rs +++ b/crates/matrix-sdk-crypto/src/machine.rs @@ -19,6 +19,7 @@ use std::{ }; use dashmap::DashMap; +use itertools::Itertools; use matrix_sdk_common::deserialized_responses::{ AlgorithmInfo, DeviceLinkProblem, EncryptionInfo, TimelineEvent, VerificationLevel, VerificationState, @@ -1220,6 +1221,64 @@ impl OlmMachine { }) } + /// Request missing local secrets from our devices (cross signing private + /// keys, megolm backup). This will ask the sdk to create outgoing + /// request to get the missing secrets. + /// + /// The requests will be processed as soon as `outgoing_requests()` is + /// called to process them. + /// + /// # Returns + /// + /// A bool result saying if actual secrets were missing and have been + /// requested + /// + /// # Examples + // + /// ``` + /// if machine.query_missing_secrets_from_other_sessions().await.unwrap() { + /// let to_send = machine.outgoing_requests().await.unwrap(); + /// // send the to device requests + /// } + /// ``` + pub async fn query_missing_secrets_from_other_sessions(&self) -> StoreResult { + let identity = self.inner.user_identity.lock().await; + #[allow(unused_mut)] + let mut secrets = identity.get_missing_secrets().await; + + #[cfg(feature = "backups_v1")] + if self.store().load_backup_keys().await?.recovery_key.is_none() { + secrets.push(SecretName::RecoveryKey); + } + + if secrets.is_empty() { + debug!("No missing requests to query"); + return Ok(false); + } + + let secret_requests = GossipMachine::request_missing_secrets(self.user_id(), secrets); + + // Check if there are already inflight requests for these secrets? + let unsent_request = self.store().get_unsent_secret_requests().await?; + let not_yet_requested = secret_requests + .into_iter() + .filter(|request| !unsent_request.iter().any(|unsent| unsent.info == request.info)) + .collect_vec(); + + if not_yet_requested.is_empty() { + debug!("The missing secrets have already been requested"); + Ok(false) + } else { + debug!("Requesting missing secrets"); + + let mut changes = Changes::default(); + changes.key_requests = not_yet_requested; + + self.store().save_changes(changes).await?; + Ok(true) + } + } + /// Get some metadata pertaining to a given group session. /// /// This includes the session owner's Matrix user ID, their device ID, info @@ -1751,6 +1810,7 @@ pub(crate) mod tests { use assert_matches::assert_matches; use futures_util::{FutureExt, StreamExt}; + use itertools::Itertools; use matrix_sdk_common::deserialized_responses::{ DeviceLinkProblem, ShieldState, VerificationLevel, VerificationState, }; @@ -1801,8 +1861,8 @@ pub(crate) mod tests { }, utilities::json_convert, verification::tests::{outgoing_request_to_event, request_to_event}, - EncryptionSettings, LocalTrust, MegolmError, OlmError, ReadOnlyDevice, ToDeviceRequest, - UserIdentities, + EncryptionSettings, LocalTrust, MegolmError, OlmError, OutgoingRequests, ReadOnlyDevice, + ToDeviceRequest, UserIdentities, }; /// These keys need to be periodically uploaded to the server. @@ -2316,6 +2376,75 @@ pub(crate) mod tests { assert!(session.unwrap().is_some()); } + #[async_test] + async fn test_request_missing_secrets() { + let (alice, _) = get_machine_pair_with_session(false).await; + + let should_query_secrets = alice.query_missing_secrets_from_other_sessions().await.unwrap(); + + assert!(should_query_secrets); + + let outgoing_to_device = alice + .outgoing_requests() + .await + .unwrap() + .into_iter() + .filter(|outgoing| match outgoing.request.as_ref() { + OutgoingRequests::ToDeviceRequest(request) => { + request.event_type.to_string() == "m.secret.request" + } + _ => false, + }) + .collect_vec(); + + if cfg!(feature = "backups_v1") { + assert_eq!(outgoing_to_device.len(), 4); + } else { + assert_eq!(outgoing_to_device.len(), 3); + } + + // The second time, as there are already in-flight requests, it should have no + // effect. + let should_query_secrets_now = + alice.query_missing_secrets_from_other_sessions().await.unwrap(); + assert!(!should_query_secrets_now); + } + + #[async_test] + async fn test_request_missing_secrets_cross_signed() { + let (alice, bob) = get_machine_pair_with_session(false).await; + + setup_cross_signing_for_machine(&alice, &bob).await; + + let should_query_secrets = alice.query_missing_secrets_from_other_sessions().await.unwrap(); + + if cfg!(feature = "backups_v1") { + assert!(should_query_secrets); + + let outgoing_to_device = alice + .outgoing_requests() + .await + .unwrap() + .into_iter() + .filter(|outgoing| match outgoing.request.as_ref() { + OutgoingRequests::ToDeviceRequest(request) => { + request.event_type.to_string() == "m.secret.request" + } + _ => false, + }) + .collect_vec(); + assert_eq!(outgoing_to_device.len(), 1); + } else { + assert!(!should_query_secrets); + } + + // The second time, as there are already in-flight requests, it should have no + // effect. + let should_query_secrets_now = + alice.query_missing_secrets_from_other_sessions().await.unwrap(); + assert!(!should_query_secrets_now); + } + #[async_test] async fn test_megolm_encryption() { let (alice, bob) = get_machine_pair_with_setup_sessions().await; From 461052a3c9d8486370274f76c1fd85a08171748a Mon Sep 17 00:00:00 2001 From: Valere Date: Sun, 1 Oct 2023 11:20:24 +0200 Subject: [PATCH 2/2] fix rust doc --- crates/matrix-sdk-crypto/src/machine.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/matrix-sdk-crypto/src/machine.rs b/crates/matrix-sdk-crypto/src/machine.rs index b5cbcbf79de..02ab02b6d71 100644 --- a/crates/matrix-sdk-crypto/src/machine.rs +++ b/crates/matrix-sdk-crypto/src/machine.rs @@ -1236,10 +1236,14 @@ impl OlmMachine { /// # Examples // /// ``` + /// # async { + /// # use matrix_sdk_crypto::OlmMachine; + /// # let machine: OlmMachine = unimplemented!(); /// if machine.query_missing_secrets_from_other_sessions().await.unwrap() { /// let to_send = machine.outgoing_requests().await.unwrap(); /// // send the to device requests - /// } + /// }; + /// # anyhow::Ok(()) }; /// ``` pub async fn query_missing_secrets_from_other_sessions(&self) -> StoreResult { let identity = self.inner.user_identity.lock().await;