1#![doc = include_str!("../docs/encryption.md")]
17#![cfg_attr(target_family = "wasm", allow(unused_imports))]
18
19#[cfg(feature = "experimental-send-custom-to-device")]
20use std::ops::Deref;
21use std::{
22 collections::{BTreeMap, HashSet},
23 io::{Cursor, Read, Write},
24 iter,
25 path::PathBuf,
26 sync::Arc,
27};
28
29use eyeball::{SharedObservable, Subscriber};
30use futures_core::Stream;
31use futures_util::{
32 future::try_join,
33 stream::{self, StreamExt},
34};
35#[cfg(feature = "experimental-send-custom-to-device")]
36use matrix_sdk_base::crypto::CollectStrategy;
37use matrix_sdk_base::crypto::{
38 store::types::{RoomKeyBundleInfo, RoomKeyInfo},
39 types::requests::{
40 OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest,
41 },
42 CrossSigningBootstrapRequests, OlmMachine,
43};
44use matrix_sdk_common::{executor::spawn, locks::Mutex as StdMutex};
45use ruma::{
46 api::client::{
47 error::ErrorBody,
48 keys::{
49 get_keys, upload_keys, upload_signatures::v3::Request as UploadSignaturesRequest,
50 upload_signing_keys::v3::Request as UploadSigningKeysRequest,
51 },
52 message::send_message_event,
53 to_device::send_event_to_device::v3::{
54 Request as RumaToDeviceRequest, Response as ToDeviceResponse,
55 },
56 uiaa::{AuthData, UiaaInfo},
57 },
58 assign,
59 events::{
60 direct::DirectUserIdentifier,
61 room::{MediaSource, ThumbnailInfo},
62 },
63 DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, TransactionId, UserId,
64};
65#[cfg(feature = "experimental-send-custom-to-device")]
66use ruma::{events::AnyToDeviceEventContent, serde::Raw, to_device::DeviceIdOrAllDevices};
67use serde::Deserialize;
68use tasks::BundleReceiverTask;
69use tokio::sync::{Mutex, RwLockReadGuard};
70use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
71use tracing::{debug, error, instrument, trace, warn};
72use url::Url;
73use vodozemac::Curve25519PublicKey;
74
75use self::{
76 backups::{types::BackupClientState, Backups},
77 futures::UploadEncryptedFile,
78 identities::{Device, DeviceUpdates, IdentityUpdates, UserDevices, UserIdentity},
79 recovery::{Recovery, RecoveryState},
80 secret_storage::SecretStorage,
81 tasks::{BackupDownloadTask, BackupUploadingTask, ClientTasks},
82 verification::{SasVerification, Verification, VerificationRequest},
83};
84use crate::{
85 attachment::Thumbnail,
86 client::{ClientInner, WeakClient},
87 error::HttpResult,
88 store_locks::CrossProcessStoreLockGuard,
89 Client, Error, HttpError, Result, Room, RumaApiError, TransmissionProgress,
90};
91
92pub mod backups;
93pub mod futures;
94pub mod identities;
95pub mod recovery;
96pub mod secret_storage;
97pub(crate) mod tasks;
98pub mod verification;
99
100pub use matrix_sdk_base::crypto::{
101 olm::{
102 SessionCreationError as MegolmSessionCreationError,
103 SessionExportError as OlmSessionExportError,
104 },
105 vodozemac, CrossSigningStatus, CryptoStoreError, DecryptorError, EventError, KeyExportError,
106 LocalTrust, MediaEncryptionInfo, MegolmError, OlmError, RoomKeyImportResult, SecretImportError,
107 SessionCreationError, SignatureError, VERSION,
108};
109
110#[cfg(feature = "experimental-send-custom-to-device")]
111use crate::config::RequestConfig;
112pub use crate::error::RoomKeyImportError;
113
114pub(crate) struct EncryptionData {
116 pub tasks: StdMutex<ClientTasks>,
119
120 pub encryption_settings: EncryptionSettings,
122
123 pub backup_state: BackupClientState,
125
126 pub recovery_state: SharedObservable<RecoveryState>,
128}
129
130impl EncryptionData {
131 pub fn new(encryption_settings: EncryptionSettings) -> Self {
132 Self {
133 encryption_settings,
134
135 tasks: StdMutex::new(Default::default()),
136 backup_state: Default::default(),
137 recovery_state: Default::default(),
138 }
139 }
140
141 pub fn initialize_tasks(&self, client: &Arc<ClientInner>) {
142 let weak_client = WeakClient::from_inner(client);
143
144 let mut tasks = self.tasks.lock();
145 tasks.upload_room_keys = Some(BackupUploadingTask::new(weak_client.clone()));
146
147 if self.encryption_settings.backup_download_strategy
148 == BackupDownloadStrategy::AfterDecryptionFailure
149 {
150 tasks.download_room_keys = Some(BackupDownloadTask::new(weak_client));
151 }
152 }
153
154 pub fn initialize_recovery_state_update_task(&self, client: &Client) {
160 let mut guard = self.tasks.lock();
161
162 let future = Recovery::update_state_after_backup_state_change(client);
163 let join_handle = spawn(future);
164
165 guard.update_recovery_state_after_backup = Some(join_handle);
166 }
167}
168
169#[derive(Clone, Copy, Debug, Default)]
171pub struct EncryptionSettings {
172 pub auto_enable_cross_signing: bool,
178
179 pub backup_download_strategy: BackupDownloadStrategy,
184
185 pub auto_enable_backups: bool,
187}
188
189#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
191#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
192pub enum BackupDownloadStrategy {
193 OneShot,
204
205 AfterDecryptionFailure,
207
208 #[default]
213 Manual,
214}
215
216#[derive(Clone, Copy, Debug, Eq, PartialEq)]
221pub enum VerificationState {
222 Unknown,
224 Verified,
227 Unverified,
229}
230
231#[derive(Debug)]
233pub struct CrossProcessLockStoreGuardWithGeneration {
234 _guard: CrossProcessStoreLockGuard,
235 generation: u64,
236}
237
238impl CrossProcessLockStoreGuardWithGeneration {
239 pub fn generation(&self) -> u64 {
241 self.generation
242 }
243}
244
245#[derive(Debug)]
256pub struct CrossSigningResetHandle {
257 client: Client,
258 upload_request: UploadSigningKeysRequest,
259 signatures_request: UploadSignaturesRequest,
260 auth_type: CrossSigningResetAuthType,
261 is_cancelled: Mutex<bool>,
262}
263
264impl CrossSigningResetHandle {
265 pub fn new(
267 client: Client,
268 upload_request: UploadSigningKeysRequest,
269 signatures_request: UploadSignaturesRequest,
270 auth_type: CrossSigningResetAuthType,
271 ) -> Self {
272 Self {
273 client,
274 upload_request,
275 signatures_request,
276 auth_type,
277 is_cancelled: Mutex::new(false),
278 }
279 }
280
281 pub fn auth_type(&self) -> &CrossSigningResetAuthType {
284 &self.auth_type
285 }
286
287 pub async fn auth(&self, auth: Option<AuthData>) -> Result<()> {
291 let mut upload_request = self.upload_request.clone();
292 upload_request.auth = auth;
293
294 while let Err(e) = self.client.send(upload_request.clone()).await {
295 if *self.is_cancelled.lock().await {
296 return Ok(());
297 }
298
299 match e.as_uiaa_response() {
300 Some(uiaa_info) => {
301 if uiaa_info.auth_error.is_some() {
302 return Err(e.into());
303 }
304 }
305 None => return Err(e.into()),
306 }
307 }
308
309 self.client.send(self.signatures_request.clone()).await?;
310
311 Ok(())
312 }
313
314 pub async fn cancel(&self) {
316 *self.is_cancelled.lock().await = true;
317 }
318}
319
320#[derive(Debug, Clone)]
323pub enum CrossSigningResetAuthType {
324 Uiaa(UiaaInfo),
326 OAuth(OAuthCrossSigningResetInfo),
329}
330
331impl CrossSigningResetAuthType {
332 fn new(error: &HttpError) -> Result<Option<Self>> {
333 if let Some(auth_info) = error.as_uiaa_response() {
334 if let Ok(auth_info) = OAuthCrossSigningResetInfo::from_auth_info(auth_info) {
335 Ok(Some(CrossSigningResetAuthType::OAuth(auth_info)))
336 } else {
337 Ok(Some(CrossSigningResetAuthType::Uiaa(auth_info.clone())))
338 }
339 } else {
340 Ok(None)
341 }
342 }
343}
344
345#[derive(Debug, Clone, Deserialize)]
348pub struct OAuthCrossSigningResetInfo {
349 pub approval_url: Url,
351}
352
353impl OAuthCrossSigningResetInfo {
354 fn from_auth_info(auth_info: &UiaaInfo) -> Result<Self> {
355 let parameters = serde_json::from_str::<OAuthCrossSigningResetUiaaParameters>(
356 auth_info.params.as_ref().map(|value| value.get()).unwrap_or_default(),
357 )?;
358
359 Ok(OAuthCrossSigningResetInfo { approval_url: parameters.reset.url })
360 }
361}
362
363#[derive(Debug, Deserialize)]
366struct OAuthCrossSigningResetUiaaParameters {
367 #[serde(rename = "org.matrix.cross_signing_reset")]
369 reset: OAuthCrossSigningResetUiaaResetParameter,
370}
371
372#[derive(Debug, Deserialize)]
375struct OAuthCrossSigningResetUiaaResetParameter {
376 url: Url,
378}
379
380impl Client {
381 pub(crate) async fn olm_machine(&self) -> RwLockReadGuard<'_, Option<OlmMachine>> {
382 self.base_client().olm_machine().await
383 }
384
385 pub(crate) async fn mark_request_as_sent(
386 &self,
387 request_id: &TransactionId,
388 response: impl Into<matrix_sdk_base::crypto::types::requests::AnyIncomingResponse<'_>>,
389 ) -> Result<(), matrix_sdk_base::Error> {
390 Ok(self
391 .olm_machine()
392 .await
393 .as_ref()
394 .expect(
395 "We should have an olm machine once we try to mark E2EE related requests as sent",
396 )
397 .mark_request_as_sent(request_id, response)
398 .await?)
399 }
400
401 #[instrument(skip(self, device_keys))]
407 pub(crate) async fn keys_query(
408 &self,
409 request_id: &TransactionId,
410 device_keys: BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
411 ) -> Result<get_keys::v3::Response> {
412 let request = assign!(get_keys::v3::Request::new(), { device_keys });
413
414 let response = self.send(request).await?;
415 self.mark_request_as_sent(request_id, &response).await?;
416 self.encryption().update_state_after_keys_query(&response).await;
417
418 Ok(response)
419 }
420
421 pub fn upload_encrypted_file<'a, R: Read + ?Sized + 'a>(
455 &'a self,
456 reader: &'a mut R,
457 ) -> UploadEncryptedFile<'a, R> {
458 UploadEncryptedFile::new(self, reader)
459 }
460
461 pub(crate) async fn upload_encrypted_media_and_thumbnail(
464 &self,
465 data: &[u8],
466 thumbnail: Option<Thumbnail>,
467 send_progress: SharedObservable<TransmissionProgress>,
468 ) -> Result<(MediaSource, Option<(MediaSource, Box<ThumbnailInfo>)>)> {
469 let upload_thumbnail = self.upload_encrypted_thumbnail(thumbnail, send_progress.clone());
470
471 let upload_attachment = async {
472 let mut cursor = Cursor::new(data);
473 self.upload_encrypted_file(&mut cursor)
474 .with_send_progress_observable(send_progress)
475 .await
476 };
477
478 let (thumbnail, file) = try_join(upload_thumbnail, upload_attachment).await?;
479
480 Ok((MediaSource::Encrypted(Box::new(file)), thumbnail))
481 }
482
483 async fn upload_encrypted_thumbnail(
486 &self,
487 thumbnail: Option<Thumbnail>,
488 send_progress: SharedObservable<TransmissionProgress>,
489 ) -> Result<Option<(MediaSource, Box<ThumbnailInfo>)>> {
490 let Some(thumbnail) = thumbnail else {
491 return Ok(None);
492 };
493
494 let (data, _, thumbnail_info) = thumbnail.into_parts();
495 let mut cursor = Cursor::new(data);
496
497 let file = self
498 .upload_encrypted_file(&mut cursor)
499 .with_send_progress_observable(send_progress)
500 .await?;
501
502 Ok(Some((MediaSource::Encrypted(Box::new(file)), thumbnail_info)))
503 }
504
505 pub(crate) async fn claim_one_time_keys(
511 &self,
512 users: impl Iterator<Item = &UserId>,
513 ) -> Result<()> {
514 let _lock = self.locks().key_claim_lock.lock().await;
515
516 if let Some((request_id, request)) = self
517 .olm_machine()
518 .await
519 .as_ref()
520 .ok_or(Error::NoOlmMachine)?
521 .get_missing_sessions(users)
522 .await?
523 {
524 let response = self.send(request).await?;
525 self.mark_request_as_sent(&request_id, &response).await?;
526 }
527
528 Ok(())
529 }
530
531 #[instrument(skip(self, request))]
541 pub(crate) async fn keys_upload(
542 &self,
543 request_id: &TransactionId,
544 request: &upload_keys::v3::Request,
545 ) -> Result<upload_keys::v3::Response> {
546 debug!(
547 device_keys = request.device_keys.is_some(),
548 one_time_key_count = request.one_time_keys.len(),
549 "Uploading public encryption keys",
550 );
551
552 let response = self.send(request.clone()).await?;
553 self.mark_request_as_sent(request_id, &response).await?;
554
555 Ok(response)
556 }
557
558 pub(crate) async fn room_send_helper(
559 &self,
560 request: &RoomMessageRequest,
561 ) -> Result<send_message_event::v3::Response> {
562 let content = request.content.clone();
563 let txn_id = request.txn_id.clone();
564 let room_id = &request.room_id;
565
566 self.get_room(room_id)
567 .expect("Can't send a message to a room that isn't known to the store")
568 .send(*content)
569 .with_transaction_id(txn_id)
570 .await
571 }
572
573 pub(crate) async fn send_to_device(
574 &self,
575 request: &ToDeviceRequest,
576 ) -> HttpResult<ToDeviceResponse> {
577 let request = RumaToDeviceRequest::new_raw(
578 request.event_type.clone(),
579 request.txn_id.clone(),
580 request.messages.clone(),
581 );
582
583 self.send(request).await
584 }
585
586 pub(crate) async fn send_verification_request(
587 &self,
588 request: OutgoingVerificationRequest,
589 ) -> Result<()> {
590 use matrix_sdk_base::crypto::types::requests::OutgoingVerificationRequest::*;
591
592 match request {
593 ToDevice(t) => {
594 self.send_to_device(&t).await?;
595 }
596 InRoom(r) => {
597 self.room_send_helper(&r).await?;
598 }
599 }
600
601 Ok(())
602 }
603
604 pub fn get_dm_room(&self, user_id: &UserId) -> Option<Room> {
606 let rooms = self.joined_rooms();
607
608 let room = rooms.into_iter().find(|r| {
610 let targets = r.direct_targets();
611 targets.len() == 1 && targets.contains(<&DirectUserIdentifier>::from(user_id))
612 });
613
614 trace!(?room, "Found room");
615 room
616 }
617
618 async fn send_outgoing_request(&self, r: OutgoingRequest) -> Result<()> {
619 use matrix_sdk_base::crypto::types::requests::AnyOutgoingRequest;
620
621 match r.request() {
622 AnyOutgoingRequest::KeysQuery(request) => {
623 self.keys_query(r.request_id(), request.device_keys.clone()).await?;
624 }
625 AnyOutgoingRequest::KeysUpload(request) => {
626 self.keys_upload(r.request_id(), request).await.inspect_err(|e| {
627 match e.as_ruma_api_error() {
628 Some(RumaApiError::ClientApi(e)) if e.status_code == 400 => {
629 if let ErrorBody::Standard { message, .. } = &e.body {
630 if message.starts_with("One time key") {
635 tracing::error!(
636 sentry = true,
637 error_message = message,
638 "Duplicate one-time keys have been uploaded"
639 );
640 }
641 }
642 }
643 _ => {}
644 }
645 })?;
646 }
647 AnyOutgoingRequest::ToDeviceRequest(request) => {
648 let response = self.send_to_device(request).await?;
649 self.mark_request_as_sent(r.request_id(), &response).await?;
650 }
651 AnyOutgoingRequest::SignatureUpload(request) => {
652 let response = self.send(request.clone()).await?;
653 self.mark_request_as_sent(r.request_id(), &response).await?;
654 }
655 AnyOutgoingRequest::RoomMessage(request) => {
656 let response = self.room_send_helper(request).await?;
657 self.mark_request_as_sent(r.request_id(), &response).await?;
658 }
659 AnyOutgoingRequest::KeysClaim(request) => {
660 let response = self.send(request.clone()).await?;
661 self.mark_request_as_sent(r.request_id(), &response).await?;
662 }
663 }
664
665 Ok(())
666 }
667
668 #[instrument(skip_all)]
669 pub(crate) async fn send_outgoing_requests(&self) -> Result<()> {
670 const MAX_CONCURRENT_REQUESTS: usize = 20;
671
672 if let Err(e) = self.claim_one_time_keys(iter::empty()).await {
675 warn!("Error while claiming one-time keys {:?}", e);
676 }
677
678 let outgoing_requests = stream::iter(
679 self.olm_machine()
680 .await
681 .as_ref()
682 .ok_or(Error::NoOlmMachine)?
683 .outgoing_requests()
684 .await?,
685 )
686 .map(|r| self.send_outgoing_request(r));
687
688 let requests = outgoing_requests.buffer_unordered(MAX_CONCURRENT_REQUESTS);
689
690 requests
691 .for_each(|r| async move {
692 match r {
693 Ok(_) => (),
694 Err(e) => warn!(error = ?e, "Error when sending out an outgoing E2EE request"),
695 }
696 })
697 .await;
698
699 Ok(())
700 }
701}
702
703#[cfg(any(feature = "testing", test))]
704impl Client {
705 pub async fn olm_machine_for_testing(&self) -> RwLockReadGuard<'_, Option<OlmMachine>> {
707 self.olm_machine().await
708 }
709}
710
711#[derive(Debug, Clone)]
715pub struct Encryption {
716 client: Client,
718}
719
720impl Encryption {
721 pub(crate) fn new(client: Client) -> Self {
722 Self { client }
723 }
724
725 pub(crate) fn settings(&self) -> EncryptionSettings {
727 self.client.inner.e2ee.encryption_settings
728 }
729
730 pub async fn ed25519_key(&self) -> Option<String> {
733 self.client.olm_machine().await.as_ref().map(|o| o.identity_keys().ed25519.to_base64())
734 }
735
736 pub async fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
738 self.client.olm_machine().await.as_ref().map(|o| o.identity_keys().curve25519)
739 }
740
741 pub async fn device_creation_timestamp(&self) -> MilliSecondsSinceUnixEpoch {
743 match self.get_own_device().await {
744 Ok(Some(device)) => device.first_time_seen_ts(),
745 _ => MilliSecondsSinceUnixEpoch::now(),
747 }
748 }
749
750 pub(crate) async fn import_secrets_bundle(
751 &self,
752 bundle: &matrix_sdk_base::crypto::types::SecretsBundle,
753 ) -> Result<(), SecretImportError> {
754 let olm_machine = self.client.olm_machine().await;
755 let olm_machine =
756 olm_machine.as_ref().expect("This should only be called once we have an OlmMachine");
757
758 olm_machine.store().import_secrets_bundle(bundle).await
759 }
760
761 pub async fn cross_signing_status(&self) -> Option<CrossSigningStatus> {
766 let olm = self.client.olm_machine().await;
767 let machine = olm.as_ref()?;
768 Some(machine.cross_signing_status().await)
769 }
770
771 pub async fn tracked_users(&self) -> Result<HashSet<OwnedUserId>, CryptoStoreError> {
776 if let Some(machine) = self.client.olm_machine().await.as_ref() {
777 machine.tracked_users().await
778 } else {
779 Ok(HashSet::new())
780 }
781 }
782
783 pub fn verification_state(&self) -> Subscriber<VerificationState> {
806 self.client.inner.verification_state.subscribe_reset()
807 }
808
809 pub async fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option<Verification> {
811 let olm = self.client.olm_machine().await;
812 let olm = olm.as_ref()?;
813 #[allow(clippy::bind_instead_of_map)]
814 olm.get_verification(user_id, flow_id).and_then(|v| match v {
815 matrix_sdk_base::crypto::Verification::SasV1(sas) => {
816 Some(SasVerification { inner: sas, client: self.client.clone() }.into())
817 }
818 #[cfg(feature = "qrcode")]
819 matrix_sdk_base::crypto::Verification::QrV1(qr) => {
820 Some(verification::QrVerification { inner: qr, client: self.client.clone() }.into())
821 }
822 _ => None,
823 })
824 }
825
826 pub async fn get_verification_request(
829 &self,
830 user_id: &UserId,
831 flow_id: impl AsRef<str>,
832 ) -> Option<VerificationRequest> {
833 let olm = self.client.olm_machine().await;
834 let olm = olm.as_ref()?;
835
836 olm.get_verification_request(user_id, flow_id)
837 .map(|r| VerificationRequest { inner: r, client: self.client.clone() })
838 }
839
840 pub async fn get_device(
874 &self,
875 user_id: &UserId,
876 device_id: &DeviceId,
877 ) -> Result<Option<Device>, CryptoStoreError> {
878 let olm = self.client.olm_machine().await;
879 let Some(machine) = olm.as_ref() else { return Ok(None) };
880 let device = machine.get_device(user_id, device_id, None).await?;
881 Ok(device.map(|d| Device { inner: d, client: self.client.clone() }))
882 }
883
884 pub async fn get_own_device(&self) -> Result<Option<Device>, CryptoStoreError> {
891 let olm = self.client.olm_machine().await;
892 let Some(machine) = olm.as_ref() else { return Ok(None) };
893 let device = machine.get_device(machine.user_id(), machine.device_id(), None).await?;
894 Ok(device.map(|d| Device { inner: d, client: self.client.clone() }))
895 }
896
897 pub async fn get_user_devices(&self, user_id: &UserId) -> Result<UserDevices, Error> {
923 let devices = self
924 .client
925 .olm_machine()
926 .await
927 .as_ref()
928 .ok_or(Error::NoOlmMachine)?
929 .get_user_devices(user_id, None)
930 .await?;
931
932 Ok(UserDevices { inner: devices, client: self.client.clone() })
933 }
934
935 pub async fn get_user_identity(
971 &self,
972 user_id: &UserId,
973 ) -> Result<Option<UserIdentity>, CryptoStoreError> {
974 let olm = self.client.olm_machine().await;
975 let Some(olm) = olm.as_ref() else { return Ok(None) };
976 let identity = olm.get_identity(user_id, None).await?;
977
978 Ok(identity.map(|i| UserIdentity::new(self.client.clone(), i)))
979 }
980
981 pub async fn request_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentity>> {
1019 let olm = self.client.olm_machine().await;
1020 let Some(olm) = olm.as_ref() else { return Ok(None) };
1021
1022 let (request_id, request) = olm.query_keys_for_users(iter::once(user_id));
1023 self.client.keys_query(&request_id, request.device_keys).await?;
1024
1025 let identity = olm.get_identity(user_id, None).await?;
1026 Ok(identity.map(|i| UserIdentity::new(self.client.clone(), i)))
1027 }
1028
1029 pub async fn devices_stream(&self) -> Result<impl Stream<Item = DeviceUpdates>> {
1060 let olm = self.client.olm_machine().await;
1061 let olm = olm.as_ref().ok_or(Error::NoOlmMachine)?;
1062 let client = self.client.to_owned();
1063
1064 Ok(olm
1065 .store()
1066 .devices_stream()
1067 .map(move |updates| DeviceUpdates::new(client.to_owned(), updates)))
1068 }
1069
1070 pub async fn user_identities_stream(&self) -> Result<impl Stream<Item = IdentityUpdates>> {
1098 let olm = self.client.olm_machine().await;
1099 let olm = olm.as_ref().ok_or(Error::NoOlmMachine)?;
1100 let client = self.client.to_owned();
1101
1102 Ok(olm
1103 .store()
1104 .user_identities_stream()
1105 .map(move |updates| IdentityUpdates::new(client.to_owned(), updates)))
1106 }
1107
1108 pub async fn bootstrap_cross_signing(&self, auth_data: Option<AuthData>) -> Result<()> {
1147 let olm = self.client.olm_machine().await;
1148 let olm = olm.as_ref().ok_or(Error::NoOlmMachine)?;
1149
1150 let CrossSigningBootstrapRequests {
1151 upload_signing_keys_req,
1152 upload_keys_req,
1153 upload_signatures_req,
1154 } = olm.bootstrap_cross_signing(false).await?;
1155
1156 let upload_signing_keys_req = assign!(UploadSigningKeysRequest::new(), {
1157 auth: auth_data,
1158 master_key: upload_signing_keys_req.master_key.map(|c| c.to_raw()),
1159 self_signing_key: upload_signing_keys_req.self_signing_key.map(|c| c.to_raw()),
1160 user_signing_key: upload_signing_keys_req.user_signing_key.map(|c| c.to_raw()),
1161 });
1162
1163 if let Some(req) = upload_keys_req {
1164 self.client.send_outgoing_request(req).await?;
1165 }
1166 self.client.send(upload_signing_keys_req).await?;
1167 self.client.send(upload_signatures_req).await?;
1168
1169 Ok(())
1170 }
1171
1172 pub async fn reset_cross_signing(&self) -> Result<Option<CrossSigningResetHandle>> {
1209 let olm = self.client.olm_machine().await;
1210 let olm = olm.as_ref().ok_or(Error::NoOlmMachine)?;
1211
1212 let CrossSigningBootstrapRequests {
1213 upload_keys_req,
1214 upload_signing_keys_req,
1215 upload_signatures_req,
1216 } = olm.bootstrap_cross_signing(true).await?;
1217
1218 let upload_signing_keys_req = assign!(UploadSigningKeysRequest::new(), {
1219 auth: None,
1220 master_key: upload_signing_keys_req.master_key.map(|c| c.to_raw()),
1221 self_signing_key: upload_signing_keys_req.self_signing_key.map(|c| c.to_raw()),
1222 user_signing_key: upload_signing_keys_req.user_signing_key.map(|c| c.to_raw()),
1223 });
1224
1225 if let Some(req) = upload_keys_req {
1226 self.client.send_outgoing_request(req).await?;
1227 }
1228
1229 if let Err(error) = self.client.send(upload_signing_keys_req.clone()).await {
1230 if let Ok(Some(auth_type)) = CrossSigningResetAuthType::new(&error) {
1231 let client = self.client.clone();
1232
1233 Ok(Some(CrossSigningResetHandle::new(
1234 client,
1235 upload_signing_keys_req,
1236 upload_signatures_req,
1237 auth_type,
1238 )))
1239 } else {
1240 Err(error.into())
1241 }
1242 } else {
1243 self.client.send(upload_signatures_req).await?;
1244
1245 Ok(None)
1246 }
1247 }
1248
1249 async fn ensure_initial_key_query(&self) -> Result<()> {
1252 let olm_machine = self.client.olm_machine().await;
1253 let olm_machine = olm_machine.as_ref().ok_or(Error::NoOlmMachine)?;
1254
1255 let user_id = olm_machine.user_id();
1256
1257 if self.client.encryption().get_user_identity(user_id).await?.is_none() {
1258 let (request_id, request) = olm_machine.query_keys_for_users([olm_machine.user_id()]);
1259 self.client.keys_query(&request_id, request.device_keys).await?;
1260 }
1261
1262 Ok(())
1263 }
1264
1265 pub async fn bootstrap_cross_signing_if_needed(
1312 &self,
1313 auth_data: Option<AuthData>,
1314 ) -> Result<()> {
1315 let olm_machine = self.client.olm_machine().await;
1316 let olm_machine = olm_machine.as_ref().ok_or(Error::NoOlmMachine)?;
1317 let user_id = olm_machine.user_id();
1318
1319 self.ensure_initial_key_query().await?;
1320
1321 if self.client.encryption().get_user_identity(user_id).await?.is_none() {
1322 self.bootstrap_cross_signing(auth_data).await?;
1323 }
1324
1325 Ok(())
1326 }
1327
1328 #[cfg(not(target_family = "wasm"))]
1380 pub async fn export_room_keys(
1381 &self,
1382 path: PathBuf,
1383 passphrase: &str,
1384 predicate: impl FnMut(&matrix_sdk_base::crypto::olm::InboundGroupSession) -> bool,
1385 ) -> Result<()> {
1386 let olm = self.client.olm_machine().await;
1387 let olm = olm.as_ref().ok_or(Error::NoOlmMachine)?;
1388
1389 let keys = olm.store().export_room_keys(predicate).await?;
1390 let passphrase = zeroize::Zeroizing::new(passphrase.to_owned());
1391
1392 let encrypt = move || -> Result<()> {
1393 let export: String =
1394 matrix_sdk_base::crypto::encrypt_room_key_export(&keys, &passphrase, 500_000)?;
1395 let mut file = std::fs::File::create(path)?;
1396 file.write_all(&export.into_bytes())?;
1397 Ok(())
1398 };
1399
1400 let task = tokio::task::spawn_blocking(encrypt);
1401 task.await.expect("Task join error")
1402 }
1403
1404 #[cfg(not(target_family = "wasm"))]
1442 pub async fn import_room_keys(
1443 &self,
1444 path: PathBuf,
1445 passphrase: &str,
1446 ) -> Result<RoomKeyImportResult, RoomKeyImportError> {
1447 let olm = self.client.olm_machine().await;
1448 let olm = olm.as_ref().ok_or(RoomKeyImportError::StoreClosed)?;
1449 let passphrase = zeroize::Zeroizing::new(passphrase.to_owned());
1450
1451 let decrypt = move || {
1452 let file = std::fs::File::open(path)?;
1453 matrix_sdk_base::crypto::decrypt_room_key_export(file, &passphrase)
1454 };
1455
1456 let task = tokio::task::spawn_blocking(decrypt);
1457 let import = task.await.expect("Task join error")?;
1458
1459 let ret = olm.store().import_exported_room_keys(import, |_, _| {}).await?;
1460
1461 self.backups().maybe_trigger_backup();
1462
1463 Ok(ret)
1464 }
1465
1466 pub async fn room_keys_received_stream(
1497 &self,
1498 ) -> Option<impl Stream<Item = Result<Vec<RoomKeyInfo>, BroadcastStreamRecvError>>> {
1499 let olm = self.client.olm_machine().await;
1500 let olm = olm.as_ref()?;
1501
1502 Some(olm.store().room_keys_received_stream())
1503 }
1504
1505 pub async fn historic_room_key_stream(&self) -> Option<impl Stream<Item = RoomKeyBundleInfo>> {
1536 let olm = self.client.olm_machine().await;
1537 let olm = olm.as_ref()?;
1538
1539 Some(olm.store().historic_room_key_stream())
1540 }
1541
1542 pub fn secret_storage(&self) -> SecretStorage {
1544 SecretStorage { client: self.client.to_owned() }
1545 }
1546
1547 pub fn backups(&self) -> Backups {
1549 Backups { client: self.client.to_owned() }
1550 }
1551
1552 pub fn recovery(&self) -> Recovery {
1554 Recovery { client: self.client.to_owned() }
1555 }
1556
1557 pub async fn enable_cross_process_store_lock(&self, lock_value: String) -> Result<(), Error> {
1569 if let Some(prev_lock) = self.client.locks().cross_process_crypto_store_lock.get() {
1571 let prev_holder = prev_lock.lock_holder();
1572 if prev_holder == lock_value {
1573 return Ok(());
1574 }
1575 warn!(
1576 "Recreating cross-process store lock with a different holder value: \
1577 prev was {prev_holder}, new is {lock_value}"
1578 );
1579 }
1580
1581 let olm_machine = self.client.base_client().olm_machine().await;
1582 let olm_machine = olm_machine.as_ref().ok_or(Error::NoOlmMachine)?;
1583
1584 let lock =
1585 olm_machine.store().create_store_lock("cross_process_lock".to_owned(), lock_value);
1586
1587 {
1592 let guard = lock.try_lock_once().await?;
1593 if guard.is_some() {
1594 olm_machine
1595 .initialize_crypto_store_generation(
1596 &self.client.locks().crypto_store_generation,
1597 )
1598 .await?;
1599 }
1600 }
1601
1602 self.client
1603 .locks()
1604 .cross_process_crypto_store_lock
1605 .set(lock)
1606 .map_err(|_| Error::BadCryptoStoreState)?;
1607
1608 Ok(())
1609 }
1610
1611 async fn on_lock_newly_acquired(&self) -> Result<u64, Error> {
1616 let olm_machine_guard = self.client.olm_machine().await;
1617 if let Some(olm_machine) = olm_machine_guard.as_ref() {
1618 let (new_gen, generation_number) = olm_machine
1619 .maintain_crypto_store_generation(&self.client.locks().crypto_store_generation)
1620 .await?;
1621 if new_gen {
1623 drop(olm_machine_guard);
1625 self.client.base_client().regenerate_olm(None).await?;
1627 }
1628 Ok(generation_number)
1629 } else {
1630 warn!("Encryption::on_lock_newly_acquired: called before OlmMachine initialised");
1635 Ok(0)
1636 }
1637 }
1638
1639 pub async fn spin_lock_store(
1645 &self,
1646 max_backoff: Option<u32>,
1647 ) -> Result<Option<CrossProcessLockStoreGuardWithGeneration>, Error> {
1648 if let Some(lock) = self.client.locks().cross_process_crypto_store_lock.get() {
1649 let guard = lock.spin_lock(max_backoff).await?;
1650
1651 let generation = self.on_lock_newly_acquired().await?;
1652
1653 Ok(Some(CrossProcessLockStoreGuardWithGeneration { _guard: guard, generation }))
1654 } else {
1655 Ok(None)
1656 }
1657 }
1658
1659 pub async fn try_lock_store_once(
1664 &self,
1665 ) -> Result<Option<CrossProcessLockStoreGuardWithGeneration>, Error> {
1666 if let Some(lock) = self.client.locks().cross_process_crypto_store_lock.get() {
1667 let maybe_guard = lock.try_lock_once().await?;
1668
1669 let Some(guard) = maybe_guard else {
1670 return Ok(None);
1671 };
1672
1673 let generation = self.on_lock_newly_acquired().await?;
1674
1675 Ok(Some(CrossProcessLockStoreGuardWithGeneration { _guard: guard, generation }))
1676 } else {
1677 Ok(None)
1678 }
1679 }
1680
1681 #[cfg(any(test, feature = "testing"))]
1683 pub async fn uploaded_key_count(&self) -> Result<u64> {
1684 let olm_machine = self.client.olm_machine().await;
1685 let olm_machine = olm_machine.as_ref().ok_or(Error::AuthenticationRequired)?;
1686 Ok(olm_machine.uploaded_key_count().await?)
1687 }
1688
1689 pub(crate) async fn spawn_initialization_task(&self, auth_data: Option<AuthData>) {
1713 let bundle_receiver_task = if self.client.inner.enable_share_history_on_invite {
1717 Some(BundleReceiverTask::new(&self.client).await)
1718 } else {
1719 None
1720 };
1721
1722 let mut tasks = self.client.inner.e2ee.tasks.lock();
1723
1724 let this = self.clone();
1725
1726 tasks.setup_e2ee = Some(spawn(async move {
1727 this.update_verification_state().await;
1730
1731 if this.settings().auto_enable_cross_signing {
1732 if let Err(e) = this.bootstrap_cross_signing_if_needed(auth_data).await {
1733 error!("Couldn't bootstrap cross signing {e:?}");
1734 }
1735 }
1736
1737 if let Err(e) = this.backups().setup_and_resume().await {
1738 error!("Couldn't setup and resume backups {e:?}");
1739 }
1740 if let Err(e) = this.recovery().setup().await {
1741 error!("Couldn't setup and resume recovery {e:?}");
1742 }
1743 }));
1744
1745 tasks.receive_historic_room_key_bundles = bundle_receiver_task;
1746 }
1747
1748 pub async fn wait_for_e2ee_initialization_tasks(&self) {
1751 let task = self.client.inner.e2ee.tasks.lock().setup_e2ee.take();
1752
1753 if let Some(task) = task {
1754 if let Err(err) = task.await {
1755 warn!("Error when initializing backups: {err}");
1756 }
1757 }
1758 }
1759
1760 pub(crate) async fn ensure_device_keys_upload(&self) -> Result<()> {
1770 let olm = self.client.olm_machine().await;
1771 let olm = olm.as_ref().ok_or(Error::NoOlmMachine)?;
1772
1773 if let Some((request_id, request)) = olm.upload_device_keys().await? {
1774 self.client.keys_upload(&request_id, &request).await?;
1775
1776 let (request_id, request) = olm.query_keys_for_users([olm.user_id()]);
1777 self.client.keys_query(&request_id, request.device_keys).await?;
1778 }
1779
1780 Ok(())
1781 }
1782
1783 pub(crate) async fn update_state_after_keys_query(&self, response: &get_keys::v3::Response) {
1784 self.recovery().update_state_after_keys_query(response).await;
1785
1786 if let Some(user_id) = self.client.user_id() {
1788 let contains_own_device = response.device_keys.contains_key(user_id);
1789
1790 if contains_own_device {
1791 self.update_verification_state().await;
1792 }
1793 }
1794 }
1795
1796 async fn update_verification_state(&self) {
1797 match self.get_own_device().await {
1798 Ok(device) => {
1799 if let Some(device) = device {
1800 let is_verified = device.is_cross_signed_by_owner();
1801
1802 if is_verified {
1803 self.client.inner.verification_state.set(VerificationState::Verified);
1804 } else {
1805 self.client.inner.verification_state.set(VerificationState::Unverified);
1806 }
1807 } else {
1808 warn!("Couldn't find out own device in the store.");
1809 self.client.inner.verification_state.set(VerificationState::Unknown);
1810 }
1811 }
1812 Err(error) => {
1813 warn!("Failed retrieving own device: {error}");
1814 self.client.inner.verification_state.set(VerificationState::Unknown);
1815 }
1816 }
1817 }
1818
1819 #[cfg(feature = "experimental-send-custom-to-device")]
1828 pub async fn encrypt_and_send_raw_to_device(
1829 &self,
1830 recipient_devices: Vec<&Device>,
1831 event_type: &str,
1832 content: Raw<AnyToDeviceEventContent>,
1833 share_strategy: CollectStrategy,
1834 ) -> Result<Vec<(OwnedUserId, OwnedDeviceId)>> {
1835 let users = recipient_devices.iter().map(|device| device.user_id());
1836
1837 self.client.claim_one_time_keys(users).await?;
1841
1842 let olm = self.client.olm_machine().await;
1843 let olm = olm.as_ref().expect("Olm machine wasn't started");
1844
1845 let (requests, withhelds) = olm
1846 .encrypt_content_for_devices(
1847 recipient_devices.into_iter().map(|d| d.deref().clone()).collect(),
1848 event_type,
1849 &content
1850 .deserialize_as::<serde_json::Value>()
1851 .expect("Deserialize as Value will always work"),
1852 share_strategy,
1853 )
1854 .await?;
1855
1856 let mut failures: Vec<(OwnedUserId, OwnedDeviceId)> = Default::default();
1857
1858 withhelds.iter().for_each(|(d, _)| {
1860 failures.push((d.user_id().to_owned(), d.device_id().to_owned()));
1861 });
1862
1863 for request in requests {
1865 let ruma_request = RumaToDeviceRequest::new_raw(
1866 request.event_type.clone(),
1867 request.txn_id.clone(),
1868 request.messages.clone(),
1869 );
1870
1871 let send_result = self
1872 .client
1873 .send_inner(ruma_request, Some(RequestConfig::short_retry()), Default::default())
1874 .await;
1875
1876 if send_result.is_err() {
1878 for (user_id, device_map) in request.messages {
1880 for device_id in device_map.keys() {
1881 match device_id {
1882 DeviceIdOrAllDevices::DeviceId(device_id) => {
1883 failures.push((user_id.clone(), device_id.to_owned()));
1884 }
1885 DeviceIdOrAllDevices::AllDevices => {
1886 }
1888 }
1889 }
1890 }
1891 }
1892 }
1893
1894 Ok(failures)
1895 }
1896}
1897
1898#[cfg(all(test, not(target_family = "wasm")))]
1899mod tests {
1900 use std::{
1901 ops::Not,
1902 sync::{
1903 atomic::{AtomicBool, Ordering},
1904 Arc,
1905 },
1906 time::Duration,
1907 };
1908
1909 use matrix_sdk_test::{
1910 async_test, test_json, GlobalAccountDataTestEvent, JoinedRoomBuilder, StateTestEvent,
1911 SyncResponseBuilder, DEFAULT_TEST_ROOM_ID,
1912 };
1913 use ruma::{
1914 event_id,
1915 events::{reaction::ReactionEventContent, relation::Annotation},
1916 user_id,
1917 };
1918 use serde_json::json;
1919 use wiremock::{
1920 matchers::{header, method, path_regex},
1921 Mock, MockServer, Request, ResponseTemplate,
1922 };
1923
1924 use crate::{
1925 assert_next_matches_with_timeout,
1926 config::RequestConfig,
1927 encryption::{OAuthCrossSigningResetInfo, VerificationState},
1928 test_utils::{
1929 client::mock_matrix_session, logged_in_client, no_retry_test_client, set_client_session,
1930 },
1931 Client,
1932 };
1933
1934 #[async_test]
1935 async fn test_reaction_sending() {
1936 let server = MockServer::start().await;
1937 let client = logged_in_client(Some(server.uri())).await;
1938
1939 let event_id = event_id!("$2:example.org");
1940
1941 Mock::given(method("GET"))
1942 .and(path_regex(r"^/_matrix/client/r0/rooms/.*/state/m.*room.*encryption.?"))
1943 .and(header("authorization", "Bearer 1234"))
1944 .respond_with(
1945 ResponseTemplate::new(200)
1946 .set_body_json(&*test_json::sync_events::ENCRYPTION_CONTENT),
1947 )
1948 .mount(&server)
1949 .await;
1950
1951 Mock::given(method("PUT"))
1952 .and(path_regex(r"^/_matrix/client/r0/rooms/.*/send/m\.reaction/.*".to_owned()))
1953 .respond_with(ResponseTemplate::new(200).set_body_json(json!({
1954 "event_id": event_id,
1955 })))
1956 .mount(&server)
1957 .await;
1958
1959 let response = SyncResponseBuilder::default()
1960 .add_joined_room(
1961 JoinedRoomBuilder::default()
1962 .add_state_event(StateTestEvent::Member)
1963 .add_state_event(StateTestEvent::PowerLevels)
1964 .add_state_event(StateTestEvent::Encryption),
1965 )
1966 .build_sync_response();
1967
1968 client.base_client().receive_sync_response(response).await.unwrap();
1969
1970 let room = client.get_room(&DEFAULT_TEST_ROOM_ID).expect("Room should exist");
1971 assert!(room
1972 .latest_encryption_state()
1973 .await
1974 .expect("Getting encryption state")
1975 .is_encrypted());
1976
1977 let event_id = event_id!("$1:example.org");
1978 let reaction = ReactionEventContent::new(Annotation::new(event_id.into(), "🐈".to_owned()));
1979 room.send(reaction).await.expect("Sending the reaction should not fail");
1980
1981 room.send_raw("m.reaction", json!({})).await.expect("Sending the reaction should not fail");
1982 }
1983
1984 #[async_test]
1985 async fn test_get_dm_room_returns_the_room_we_have_with_this_user() {
1986 let server = MockServer::start().await;
1987 let client = logged_in_client(Some(server.uri())).await;
1988 let user_id = user_id!("@invited:localhost");
1992
1993 let response = SyncResponseBuilder::default()
1995 .add_joined_room(
1996 JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberAdditional),
1997 )
1998 .add_global_account_data_event(GlobalAccountDataTestEvent::Direct)
1999 .build_sync_response();
2000 client.base_client().receive_sync_response(response).await.unwrap();
2001
2002 let found_room = client.get_dm_room(user_id).expect("DM not found!");
2004 assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
2005 }
2006
2007 #[async_test]
2008 async fn test_get_dm_room_still_finds_room_where_participant_is_only_invited() {
2009 let server = MockServer::start().await;
2010 let client = logged_in_client(Some(server.uri())).await;
2011 let user_id = user_id!("@invited:localhost");
2013
2014 let response = SyncResponseBuilder::default()
2016 .add_joined_room(
2017 JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberInvite),
2018 )
2019 .add_global_account_data_event(GlobalAccountDataTestEvent::Direct)
2020 .build_sync_response();
2021 client.base_client().receive_sync_response(response).await.unwrap();
2022
2023 let found_room = client.get_dm_room(user_id).expect("DM not found!");
2025 assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
2026 }
2027
2028 #[async_test]
2029 async fn test_get_dm_room_still_finds_left_room() {
2030 let server = MockServer::start().await;
2034 let client = logged_in_client(Some(server.uri())).await;
2035 let user_id = user_id!("@invited:localhost");
2039
2040 let response = SyncResponseBuilder::default()
2042 .add_joined_room(
2043 JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberLeave),
2044 )
2045 .add_global_account_data_event(GlobalAccountDataTestEvent::Direct)
2046 .build_sync_response();
2047 client.base_client().receive_sync_response(response).await.unwrap();
2048
2049 let found_room = client.get_dm_room(user_id).expect("DM not found!");
2051 assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
2052 }
2053
2054 #[cfg(feature = "sqlite")]
2055 #[async_test]
2056 async fn test_generation_counter_invalidates_olm_machine() {
2057 use matrix_sdk_base::store::RoomLoadSettings;
2060 let sqlite_path = std::env::temp_dir().join("generation_counter_sqlite.db");
2061 let session = mock_matrix_session();
2062
2063 let client1 = Client::builder()
2064 .homeserver_url("http://localhost:1234")
2065 .request_config(RequestConfig::new().disable_retry())
2066 .sqlite_store(&sqlite_path, None)
2067 .build()
2068 .await
2069 .unwrap();
2070 client1
2071 .matrix_auth()
2072 .restore_session(session.clone(), RoomLoadSettings::default())
2073 .await
2074 .unwrap();
2075
2076 let client2 = Client::builder()
2077 .homeserver_url("http://localhost:1234")
2078 .request_config(RequestConfig::new().disable_retry())
2079 .sqlite_store(sqlite_path, None)
2080 .build()
2081 .await
2082 .unwrap();
2083 client2.matrix_auth().restore_session(session, RoomLoadSettings::default()).await.unwrap();
2084
2085 let guard = client1.encryption().try_lock_store_once().await.unwrap();
2087 assert!(guard.is_none());
2088
2089 client1.encryption().enable_cross_process_store_lock("client1".to_owned()).await.unwrap();
2090 client2.encryption().enable_cross_process_store_lock("client2".to_owned()).await.unwrap();
2091
2092 let acquired1 = client1.encryption().try_lock_store_once().await.unwrap();
2094 assert!(acquired1.is_some());
2095
2096 let initial_olm_machine =
2098 client1.olm_machine().await.clone().expect("must have an olm machine");
2099
2100 let decryption_key = matrix_sdk_base::crypto::store::types::BackupDecryptionKey::new()
2102 .expect("Can't create new recovery key");
2103 let backup_key = decryption_key.megolm_v1_public_key();
2104 backup_key.set_version("1".to_owned());
2105 initial_olm_machine
2106 .backup_machine()
2107 .save_decryption_key(Some(decryption_key.to_owned()), Some("1".to_owned()))
2108 .await
2109 .expect("Should save");
2110
2111 initial_olm_machine.backup_machine().enable_backup_v1(backup_key.clone()).await.unwrap();
2112
2113 assert!(client1.encryption().backups().are_enabled().await);
2114
2115 let acquired2 = client2.encryption().try_lock_store_once().await.unwrap();
2117 assert!(acquired2.is_none());
2118
2119 drop(acquired1);
2121 tokio::time::sleep(Duration::from_millis(100)).await;
2122
2123 let acquired1 = client1.encryption().try_lock_store_once().await.unwrap();
2125 assert!(acquired1.is_some());
2126
2127 let olm_machine = client1.olm_machine().await.clone().expect("must have an olm machine");
2129 assert!(initial_olm_machine.same_as(&olm_machine));
2130
2131 drop(acquired1);
2133 tokio::time::sleep(Duration::from_millis(100)).await;
2134
2135 let acquired2 = client2.encryption().try_lock_store_once().await.unwrap();
2137 assert!(acquired2.is_some());
2138
2139 drop(acquired2);
2141 tokio::time::sleep(Duration::from_millis(100)).await;
2142
2143 let acquired1 = client1.encryption().try_lock_store_once().await.unwrap();
2145 assert!(acquired1.is_some());
2146
2147 let olm_machine = client1.olm_machine().await.clone().expect("must have an olm machine");
2149
2150 assert!(!initial_olm_machine.same_as(&olm_machine));
2151
2152 let backup_key_new = olm_machine.backup_machine().get_backup_keys().await.unwrap();
2153 assert!(backup_key_new.decryption_key.is_some());
2154 assert_eq!(
2155 backup_key_new.decryption_key.unwrap().megolm_v1_public_key().to_base64(),
2156 backup_key.to_base64()
2157 );
2158 assert!(client1.encryption().backups().are_enabled().await);
2159 }
2160
2161 #[cfg(feature = "sqlite")]
2162 #[async_test]
2163 async fn test_generation_counter_no_spurious_invalidation() {
2164 use matrix_sdk_base::store::RoomLoadSettings;
2167 let sqlite_path =
2168 std::env::temp_dir().join("generation_counter_no_spurious_invalidations.db");
2169 let session = mock_matrix_session();
2170
2171 let client = Client::builder()
2172 .homeserver_url("http://localhost:1234")
2173 .request_config(RequestConfig::new().disable_retry())
2174 .sqlite_store(&sqlite_path, None)
2175 .build()
2176 .await
2177 .unwrap();
2178 client
2179 .matrix_auth()
2180 .restore_session(session.clone(), RoomLoadSettings::default())
2181 .await
2182 .unwrap();
2183
2184 let initial_olm_machine = client.olm_machine().await.as_ref().unwrap().clone();
2185
2186 client.encryption().enable_cross_process_store_lock("client1".to_owned()).await.unwrap();
2187
2188 let after_enabling_lock = client.olm_machine().await.as_ref().unwrap().clone();
2190 assert!(initial_olm_machine.same_as(&after_enabling_lock));
2191
2192 {
2193 let client2 = Client::builder()
2195 .homeserver_url("http://localhost:1234")
2196 .request_config(RequestConfig::new().disable_retry())
2197 .sqlite_store(sqlite_path, None)
2198 .build()
2199 .await
2200 .unwrap();
2201 client2
2202 .matrix_auth()
2203 .restore_session(session, RoomLoadSettings::default())
2204 .await
2205 .unwrap();
2206
2207 client2
2208 .encryption()
2209 .enable_cross_process_store_lock("client2".to_owned())
2210 .await
2211 .unwrap();
2212
2213 let guard = client2.encryption().spin_lock_store(None).await.unwrap();
2214 assert!(guard.is_some());
2215
2216 drop(guard);
2217 tokio::time::sleep(Duration::from_millis(100)).await;
2218 }
2219
2220 {
2221 let acquired = client.encryption().try_lock_store_once().await.unwrap();
2222 assert!(acquired.is_some());
2223 }
2224
2225 let after_taking_lock_first_time = client.olm_machine().await.as_ref().unwrap().clone();
2227 assert!(!initial_olm_machine.same_as(&after_taking_lock_first_time));
2228
2229 {
2230 let acquired = client.encryption().try_lock_store_once().await.unwrap();
2231 assert!(acquired.is_some());
2232 }
2233
2234 let after_taking_lock_second_time = client.olm_machine().await.as_ref().unwrap().clone();
2236 assert!(after_taking_lock_first_time.same_as(&after_taking_lock_second_time));
2237 }
2238
2239 #[async_test]
2240 async fn test_update_verification_state_is_updated_before_any_requests_happen() {
2241 let client = no_retry_test_client(None).await;
2243 let server = MockServer::start().await;
2244
2245 let mut verification_state = client.encryption().verification_state();
2247
2248 assert_next_matches_with_timeout!(verification_state, VerificationState::Unknown);
2250
2251 let keys_requested = Arc::new(AtomicBool::new(false));
2254 let inner_bool = keys_requested.clone();
2255
2256 Mock::given(method("GET"))
2257 .and(path_regex(
2258 r"/_matrix/client/r0/user/.*/account_data/m.secret_storage.default_key",
2259 ))
2260 .respond_with(move |_req: &Request| {
2261 inner_bool.fetch_or(true, Ordering::SeqCst);
2262 ResponseTemplate::new(200).set_body_json(json!({}))
2263 })
2264 .mount(&server)
2265 .await;
2266
2267 set_client_session(&client).await;
2269
2270 assert!(keys_requested.load(Ordering::SeqCst).not());
2272 assert_next_matches_with_timeout!(verification_state, VerificationState::Unverified);
2273 }
2274
2275 #[test]
2276 fn test_oauth_reset_info_from_uiaa_info() {
2277 let auth_info = json!({
2278 "session": "dummy",
2279 "flows": [
2280 {
2281 "stages": [
2282 "org.matrix.cross_signing_reset"
2283 ]
2284 }
2285 ],
2286 "params": {
2287 "org.matrix.cross_signing_reset": {
2288 "url": "https://example.org/account/account?action=org.matrix.cross_signing_reset"
2289 }
2290 },
2291 "msg": "To reset..."
2292 });
2293
2294 let auth_info = serde_json::from_value(auth_info)
2295 .expect("We should be able to deserialize the UiaaInfo");
2296 OAuthCrossSigningResetInfo::from_auth_info(&auth_info)
2297 .expect("We should be able to fetch the cross-signing reset info from the auth info");
2298 }
2299}