1use std::{
45 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
46 fmt::Debug,
47 ops::Deref,
48 pin::pin,
49 sync::{Arc, atomic::Ordering},
50 time::Duration,
51};
52
53use as_variant::as_variant;
54use futures_core::Stream;
55use futures_util::StreamExt;
56use itertools::{Either, Itertools};
57use ruma::{
58 DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, RoomId, UserId,
59 encryption::KeyUsage, events::secret::request::SecretName,
60};
61use serde::{Serialize, de::DeserializeOwned};
62use thiserror::Error;
63use tokio::sync::{Mutex, Notify, OwnedRwLockWriteGuard, RwLock};
64use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
65use tracing::{info, instrument, trace, warn};
66use types::{RoomKeyBundleInfo, StoredRoomKeyBundleData};
67use vodozemac::{Curve25519PublicKey, megolm::SessionOrdering};
68
69use self::types::{
70 Changes, CrossSigningKeyExport, DeviceChanges, DeviceUpdates, IdentityChanges, IdentityUpdates,
71 PendingChanges, RoomKeyInfo, RoomKeyWithheldInfo, RoomPendingKeyBundleDetails,
72 UserKeyQueryResult,
73};
74use crate::{
75 CrossSigningStatus, OwnUserIdentityData, RoomKeyImportResult,
76 gossiping::GossippedSecret,
77 identities::{Device, DeviceData, UserDevices, UserIdentityData, user::UserIdentity},
78 olm::{
79 Account, ExportedRoomKey, ForwarderData, InboundGroupSession, PrivateCrossSigningIdentity,
80 SenderData, Session, StaticAccountData,
81 },
82 store::types::{RoomKeyWithheldEntry, SecretsInboxItem},
83 types::{
84 BackupSecrets, CrossSigningSecrets, MegolmBackupV1Curve25519AesSha2Secrets, RoomKeyExport,
85 SecretsBundle,
86 },
87 verification::VerificationMachine,
88};
89#[cfg(doc)]
90use crate::{backups::BackupMachine, identities::OwnUserIdentity};
91
92pub mod caches;
93mod crypto_store_wrapper;
94mod error;
95mod memorystore;
96mod traits;
97pub mod types;
98
99#[cfg(any(test, feature = "testing"))]
100#[macro_use]
101#[allow(missing_docs)]
102pub mod integration_tests;
103
104pub(crate) use crypto_store_wrapper::CryptoStoreWrapper;
105pub use error::{CryptoStoreError, Result};
106use matrix_sdk_common::{
107 cross_process_lock::{CrossProcessLock, CrossProcessLockConfig, CrossProcessLockGeneration},
108 deserialized_responses::WithheldCode,
109 timeout::timeout,
110};
111pub use memorystore::MemoryStore;
112pub use traits::{CryptoStore, DynCryptoStore, IntoCryptoStore};
113
114use self::caches::{SequenceNumber, StoreCache, StoreCacheGuard, UsersForKeyQuery};
115use crate::types::{
116 events::room_key_withheld::RoomKeyWithheldContent, room_history::RoomKeyBundle,
117};
118#[cfg(feature = "experimental-x509-identity-verification")]
119use crate::x509::{X509Signer, X509Verifier};
120pub use crate::{
121 dehydrated_devices::DehydrationError,
122 gossiping::{GossipRequest, SecretInfo},
123};
124
125#[derive(Debug, Clone)]
132pub struct Store {
133 inner: Arc<StoreInner>,
134}
135
136#[derive(Debug, Default)]
137pub(crate) struct KeyQueryManager {
138 users_for_key_query: Mutex<UsersForKeyQuery>,
140
141 users_for_key_query_notify: Notify,
143}
144
145impl KeyQueryManager {
146 pub async fn synced<'a>(&'a self, cache: &'a StoreCache) -> Result<SyncedKeyQueryManager<'a>> {
147 self.ensure_sync_tracked_users(cache).await?;
148 Ok(SyncedKeyQueryManager { cache, manager: self })
149 }
150
151 async fn ensure_sync_tracked_users(&self, cache: &StoreCache) -> Result<()> {
158 let loaded = cache.loaded_tracked_users.read().await;
160 if *loaded {
161 return Ok(());
162 }
163
164 drop(loaded);
166 let mut loaded = cache.loaded_tracked_users.write().await;
167
168 if *loaded {
172 return Ok(());
173 }
174
175 let tracked_users = cache.store.load_tracked_users().await?;
176
177 let mut query_users_lock = self.users_for_key_query.lock().await;
178 let mut tracked_users_cache = cache.tracked_users.write();
179 for user in tracked_users {
180 tracked_users_cache.insert(user.user_id.to_owned());
181
182 if user.dirty {
183 query_users_lock.insert_user(&user.user_id);
184 }
185 }
186
187 *loaded = true;
188
189 Ok(())
190 }
191
192 pub async fn wait_if_user_key_query_pending(
202 &self,
203 cache: StoreCacheGuard,
204 timeout_duration: Duration,
205 user: &UserId,
206 ) -> Result<UserKeyQueryResult> {
207 {
208 self.ensure_sync_tracked_users(&cache).await?;
211 drop(cache);
212 }
213
214 let mut users_for_key_query = self.users_for_key_query.lock().await;
215 let Some(waiter) = users_for_key_query.maybe_register_waiting_task(user) else {
216 return Ok(UserKeyQueryResult::WasNotPending);
217 };
218
219 let wait_for_completion = async {
220 while !waiter.completed.load(Ordering::Relaxed) {
221 let mut notified = pin!(self.users_for_key_query_notify.notified());
225 notified.as_mut().enable();
226 drop(users_for_key_query);
227
228 notified.await;
230
231 users_for_key_query = self.users_for_key_query.lock().await;
235 }
236 };
237
238 match timeout(Box::pin(wait_for_completion), timeout_duration).await {
239 Err(_) => {
240 warn!(
241 user_id = ?user,
242 "The user has a pending `/keys/query` request which did \
243 not finish yet, some devices might be missing."
244 );
245
246 Ok(UserKeyQueryResult::TimeoutExpired)
247 }
248 _ => Ok(UserKeyQueryResult::WasPending),
249 }
250 }
251}
252
253pub(crate) struct SyncedKeyQueryManager<'a> {
254 cache: &'a StoreCache,
255 manager: &'a KeyQueryManager,
256}
257
258impl SyncedKeyQueryManager<'_> {
259 pub async fn update_tracked_users(&self, users: impl Iterator<Item = &UserId>) -> Result<()> {
264 let mut store_updates = Vec::new();
265 let mut key_query_lock = self.manager.users_for_key_query.lock().await;
266
267 {
268 let mut tracked_users = self.cache.tracked_users.write();
269 for user_id in users {
270 if tracked_users.insert(user_id.to_owned()) {
271 key_query_lock.insert_user(user_id);
272 store_updates.push((user_id, true))
273 }
274 }
275 }
276
277 self.cache.store.save_tracked_users(&store_updates).await
278 }
279
280 pub async fn mark_tracked_users_as_changed(
287 &self,
288 users: impl Iterator<Item = &UserId>,
289 ) -> Result<()> {
290 let mut store_updates: Vec<(&UserId, bool)> = Vec::new();
291 let mut key_query_lock = self.manager.users_for_key_query.lock().await;
292
293 {
294 let tracked_users = &self.cache.tracked_users.read();
295 for user_id in users {
296 if tracked_users.contains(user_id) {
297 key_query_lock.insert_user(user_id);
298 store_updates.push((user_id, true));
299 }
300 }
301 }
302
303 self.cache.store.save_tracked_users(&store_updates).await
304 }
305
306 pub async fn mark_tracked_users_as_up_to_date(
312 &self,
313 users: impl Iterator<Item = &UserId>,
314 sequence_number: SequenceNumber,
315 ) -> Result<()> {
316 let mut store_updates: Vec<(&UserId, bool)> = Vec::new();
317 let mut key_query_lock = self.manager.users_for_key_query.lock().await;
318
319 {
320 let tracked_users = self.cache.tracked_users.read();
321 for user_id in users {
322 if tracked_users.contains(user_id) {
323 let clean = key_query_lock.maybe_remove_user(user_id, sequence_number);
324 store_updates.push((user_id, !clean));
325 }
326 }
327 }
328
329 self.cache.store.save_tracked_users(&store_updates).await?;
330 self.manager.users_for_key_query_notify.notify_waiters();
332
333 Ok(())
334 }
335
336 pub async fn users_for_key_query(&self) -> (HashSet<OwnedUserId>, SequenceNumber) {
348 self.manager.users_for_key_query.lock().await.users_for_key_query()
349 }
350
351 pub fn tracked_users(&self) -> HashSet<OwnedUserId> {
353 self.cache.tracked_users.read().iter().cloned().collect()
354 }
355
356 pub async fn mark_user_as_changed(&self, user: &UserId) -> Result<()> {
362 self.manager.users_for_key_query.lock().await.insert_user(user);
363 self.cache.tracked_users.write().insert(user.to_owned());
364
365 self.cache.store.save_tracked_users(&[(user, true)]).await
366 }
367}
368
369fn collect_device_updates(
375 verification_machine: VerificationMachine,
376 own_identity: Option<OwnUserIdentityData>,
377 identities: IdentityChanges,
378 devices: DeviceChanges,
379 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<X509Verifier>,
380) -> DeviceUpdates {
381 let mut new: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
382 let mut changed: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
383
384 let (new_identities, changed_identities, unchanged_identities) = identities.into_maps();
385
386 let map_device = |device: DeviceData| {
387 let device_owner_identity = new_identities
388 .get(device.user_id())
389 .or_else(|| changed_identities.get(device.user_id()))
390 .or_else(|| unchanged_identities.get(device.user_id()))
391 .cloned();
392
393 Device {
394 inner: device,
395 verification_machine: verification_machine.to_owned(),
396 own_identity: own_identity.to_owned(),
397 device_owner_identity,
398 #[cfg(feature = "experimental-x509-identity-verification")]
399 x509_verifier: x509_verifier.to_owned(),
400 }
401 };
402
403 for device in devices.new {
404 let device = map_device(device);
405
406 new.entry(device.user_id().to_owned())
407 .or_default()
408 .insert(device.device_id().to_owned(), device);
409 }
410
411 for device in devices.changed {
412 let device = map_device(device);
413
414 changed
415 .entry(device.user_id().to_owned())
416 .or_default()
417 .insert(device.device_id().to_owned(), device.to_owned());
418 }
419
420 DeviceUpdates { new, changed }
421}
422
423#[allow(missing_debug_implementations)]
425pub struct StoreTransaction {
426 store: Store,
427 changes: PendingChanges,
428 cache: OwnedRwLockWriteGuard<StoreCache>,
430}
431
432impl StoreTransaction {
433 async fn new(store: Store) -> Self {
435 let cache = store.inner.cache.clone();
436
437 Self { store, changes: PendingChanges::default(), cache: cache.clone().write_owned().await }
438 }
439
440 pub(crate) fn cache(&self) -> &StoreCache {
441 &self.cache
442 }
443
444 pub fn store(&self) -> &Store {
446 &self.store
447 }
448
449 pub async fn account(&mut self) -> Result<&mut Account> {
456 if self.changes.account.is_none() {
457 let _ = self.cache.account().await?;
459 self.changes.account = self.cache.account.lock().await.take();
460 }
461 Ok(self.changes.account.as_mut().unwrap())
462 }
463
464 pub async fn commit(self) -> Result<()> {
467 if self.changes.is_empty() {
468 return Ok(());
469 }
470
471 let account = self.changes.account.as_ref().map(|acc| acc.deep_clone());
473
474 self.store.save_pending_changes(self.changes).await?;
475
476 if let Some(account) = account {
478 *self.cache.account.lock().await = Some(account);
479 }
480
481 Ok(())
482 }
483}
484
485#[derive(Debug)]
486struct StoreInner {
487 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
488 store: Arc<CryptoStoreWrapper>,
489
490 cache: Arc<RwLock<StoreCache>>,
494
495 verification_machine: VerificationMachine,
496
497 #[cfg(feature = "experimental-x509-identity-verification")]
500 x509_verifier: Option<X509Verifier>,
501
502 #[cfg(feature = "experimental-x509-identity-verification")]
505 x509_signer: Option<X509Signer>,
506
507 static_account: StaticAccountData,
510}
511
512#[derive(Debug, Error)]
515pub enum SecretImportError {
516 #[error("Error while importing {name}: {error}")]
518 Key {
519 name: SecretName,
521 error: vodozemac::KeyError,
523 },
524 #[error(
527 "Error while importing {name}: The public key of the imported private \
528 key doesn't match the public key that was uploaded to the server"
529 )]
530 MismatchedPublicKeys {
531 name: SecretName,
533 },
534 #[error(transparent)]
536 Store(#[from] CryptoStoreError),
537}
538
539#[derive(Debug, Error)]
544pub enum SecretsBundleExportError {
545 #[error(transparent)]
547 Store(#[from] CryptoStoreError),
548 #[error("The store is missing one or multiple cross-signing keys")]
550 MissingCrossSigningKey(KeyUsage),
551 #[error("The store doesn't contain any cross-signing keys")]
553 MissingCrossSigningKeys,
554 #[error("The store contains a backup key, but no backup version")]
557 MissingBackupVersion,
558}
559
560impl Store {
561 pub(crate) fn new(
563 account: StaticAccountData,
564 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
565 store: Arc<CryptoStoreWrapper>,
566 verification_machine: VerificationMachine,
567 ) -> Self {
568 Self::new_with_x509(
569 account,
570 identity,
571 store,
572 verification_machine,
573 #[cfg(feature = "experimental-x509-identity-verification")]
574 None,
575 #[cfg(feature = "experimental-x509-identity-verification")]
576 None,
577 )
578 }
579
580 pub(crate) fn new_with_x509(
582 account: StaticAccountData,
583 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
584 store: Arc<CryptoStoreWrapper>,
585 verification_machine: VerificationMachine,
586 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
587 X509Verifier,
588 >,
589 #[cfg(feature = "experimental-x509-identity-verification")] x509_signer: Option<X509Signer>,
590 ) -> Self {
591 Self {
592 inner: Arc::new(StoreInner {
593 static_account: account,
594 identity,
595 store: store.clone(),
596 verification_machine,
597 #[cfg(feature = "experimental-x509-identity-verification")]
598 x509_verifier,
599 #[cfg(feature = "experimental-x509-identity-verification")]
600 x509_signer,
601 cache: Arc::new(RwLock::new(StoreCache {
602 store,
603 tracked_users: Default::default(),
604 loaded_tracked_users: Default::default(),
605 account: Default::default(),
606 })),
607 }),
608 }
609 }
610
611 pub(crate) fn user_id(&self) -> &UserId {
613 &self.inner.static_account.user_id
614 }
615
616 pub(crate) fn device_id(&self) -> &DeviceId {
618 self.inner.verification_machine.own_device_id()
619 }
620
621 pub(crate) fn static_account(&self) -> &StaticAccountData {
623 &self.inner.static_account
624 }
625
626 #[cfg(feature = "experimental-x509-identity-verification")]
629 pub(crate) fn x509_signer(&self) -> Option<&X509Signer> {
630 self.inner.x509_signer.as_ref()
631 }
632
633 #[cfg(feature = "experimental-x509-identity-verification")]
636 pub(crate) fn x509_verifier(&self) -> Option<&X509Verifier> {
637 self.inner.x509_verifier.as_ref()
638 }
639
640 pub(crate) async fn cache(&self) -> Result<StoreCacheGuard> {
641 Ok(StoreCacheGuard { cache: self.inner.cache.clone().read_owned().await })
642 }
643
644 pub(crate) async fn transaction(&self) -> StoreTransaction {
645 StoreTransaction::new(self.clone()).await
646 }
647
648 pub(crate) async fn with_transaction<T>(
649 &self,
650 func: impl AsyncFnOnce(&mut StoreTransaction) -> Result<T, crate::OlmError>,
651 ) -> Result<T, crate::OlmError> {
652 let mut tr = self.transaction().await;
653 let res = func(&mut tr).await?;
654 tr.commit().await?;
655 Ok(res)
656 }
657
658 #[cfg(test)]
659 pub(crate) async fn reset_cross_signing_identity(&self) {
661 self.inner.identity.lock().await.reset();
662 }
663
664 pub(crate) fn private_identity(&self) -> Arc<Mutex<PrivateCrossSigningIdentity>> {
666 self.inner.identity.clone()
667 }
668
669 pub(crate) async fn save_sessions(&self, sessions: &[Session]) -> Result<()> {
671 let changes = Changes { sessions: sessions.to_vec(), ..Default::default() };
672
673 self.save_changes(changes).await
674 }
675
676 pub(crate) async fn get_sessions(
677 &self,
678 sender_key: &str,
679 ) -> Result<Option<Arc<Mutex<Vec<Session>>>>> {
680 self.inner.store.get_sessions(sender_key).await
681 }
682
683 pub(crate) async fn save_changes(&self, changes: Changes) -> Result<()> {
684 self.inner.store.save_changes(changes).await
685 }
686
687 pub(crate) async fn merge_received_group_session(
695 &self,
696 session: InboundGroupSession,
697 ) -> Result<Option<InboundGroupSession>> {
698 let old_session = self
699 .inner
700 .store
701 .get_inbound_group_session(session.room_id(), session.session_id())
702 .await?;
703
704 let Some(old_session) = old_session else {
706 info!("Received a new megolm room key");
707 return Ok(Some(session));
708 };
709
710 let index_comparison = session.compare_ratchet(&old_session).await;
711 let trust_level_comparison =
712 session.sender_data.compare_trust_level(&old_session.sender_data);
713
714 let result = match (index_comparison, trust_level_comparison) {
715 (SessionOrdering::Unconnected, _) => {
716 warn!(
720 "Received a group session with an ratchet that does not connect to the one in the store, discarding"
721 );
722 None
723 }
724
725 (SessionOrdering::Better, std::cmp::Ordering::Greater)
726 | (SessionOrdering::Better, std::cmp::Ordering::Equal)
727 | (SessionOrdering::Equal, std::cmp::Ordering::Greater) => {
728 info!(
730 ?index_comparison,
731 ?trust_level_comparison,
732 "Received a megolm room key that we have a worse version of, merging"
733 );
734 Some(session)
735 }
736
737 (SessionOrdering::Worse, std::cmp::Ordering::Less)
738 | (SessionOrdering::Worse, std::cmp::Ordering::Equal)
739 | (SessionOrdering::Equal, std::cmp::Ordering::Less) => {
740 warn!(
742 ?index_comparison,
743 ?trust_level_comparison,
744 "Received a megolm room key that we already have a better version \
745 of, discarding"
746 );
747 None
748 }
749
750 (SessionOrdering::Equal, std::cmp::Ordering::Equal) => {
751 info!("Received a megolm room key that we already have, discarding");
753 None
754 }
755
756 (SessionOrdering::Better, std::cmp::Ordering::Less) => {
757 info!("Upgrading a previously-received megolm session with new ratchet");
760 let result = old_session.with_ratchet(&session);
761 result.reset_backup_state();
763 Some(result)
764 }
765
766 (SessionOrdering::Worse, std::cmp::Ordering::Greater) => {
767 info!("Upgrading a previously-received megolm session with new sender data");
770 Some(session.with_ratchet(&old_session))
771 }
772 };
773
774 Ok(result)
775 }
776
777 #[cfg(test)]
778 pub(crate) async fn save_device_data(&self, devices: &[DeviceData]) -> Result<()> {
780 use types::DeviceChanges;
781
782 let changes = Changes {
783 devices: DeviceChanges { changed: devices.to_vec(), ..Default::default() },
784 ..Default::default()
785 };
786
787 self.save_changes(changes).await
788 }
789
790 pub(crate) async fn save_inbound_group_sessions(
792 &self,
793 sessions: &[InboundGroupSession],
794 ) -> Result<()> {
795 let changes = Changes { inbound_group_sessions: sessions.to_vec(), ..Default::default() };
796
797 self.save_changes(changes).await
798 }
799
800 pub(crate) async fn device_display_name(&self) -> Result<Option<String>, CryptoStoreError> {
802 Ok(self
803 .inner
804 .store
805 .get_device(self.user_id(), self.device_id())
806 .await?
807 .and_then(|d| d.display_name().map(|d| d.to_owned())))
808 }
809
810 pub(crate) async fn get_device_data(
815 &self,
816 user_id: &UserId,
817 device_id: &DeviceId,
818 ) -> Result<Option<DeviceData>> {
819 self.inner.store.get_device(user_id, device_id).await
820 }
821
822 pub(crate) async fn get_device_data_for_user_filtered(
830 &self,
831 user_id: &UserId,
832 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
833 self.inner.store.get_user_devices(user_id).await.map(|mut d| {
834 if user_id == self.user_id() {
835 d.remove(self.device_id());
836 }
837 d
838 })
839 }
840
841 pub(crate) async fn get_device_data_for_user(
850 &self,
851 user_id: &UserId,
852 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
853 self.inner.store.get_user_devices(user_id).await
854 }
855
856 pub(crate) async fn get_device_from_curve_key(
862 &self,
863 user_id: &UserId,
864 curve_key: Curve25519PublicKey,
865 ) -> Result<Option<Device>> {
866 self.get_user_devices(user_id)
867 .await
868 .map(|d| d.devices().find(|d| d.curve25519_key() == Some(curve_key)))
869 }
870
871 pub(crate) async fn get_user_devices(&self, user_id: &UserId) -> Result<UserDevices> {
881 let devices = self.get_device_data_for_user(user_id).await?;
882
883 let own_identity = self
884 .inner
885 .store
886 .get_user_identity(self.user_id())
887 .await?
888 .and_then(|i| i.own().cloned());
889 let device_owner_identity = self.inner.store.get_user_identity(user_id).await?;
890
891 Ok(UserDevices {
892 inner: devices,
893 verification_machine: self.inner.verification_machine.clone(),
894 own_identity,
895 device_owner_identity,
896 #[cfg(feature = "experimental-x509-identity-verification")]
897 x509_verifier: self.x509_verifier().cloned(),
898 })
899 }
900
901 pub(crate) async fn get_device(
911 &self,
912 user_id: &UserId,
913 device_id: &DeviceId,
914 ) -> Result<Option<Device>> {
915 if let Some(device_data) = self.inner.store.get_device(user_id, device_id).await? {
916 Ok(Some(self.wrap_device_data(device_data).await?))
917 } else {
918 Ok(None)
919 }
920 }
921
922 pub(crate) async fn wrap_device_data(&self, device_data: DeviceData) -> Result<Device> {
927 let own_identity = self
928 .inner
929 .store
930 .get_user_identity(self.user_id())
931 .await?
932 .and_then(|i| i.own().cloned());
933
934 let device_owner_identity =
935 self.inner.store.get_user_identity(device_data.user_id()).await?;
936
937 Ok(Device {
938 inner: device_data,
939 verification_machine: self.inner.verification_machine.clone(),
940 own_identity,
941 device_owner_identity,
942 #[cfg(feature = "experimental-x509-identity-verification")]
943 x509_verifier: self.x509_verifier().cloned(),
944 })
945 }
946
947 pub(crate) async fn get_identity(&self, user_id: &UserId) -> Result<Option<UserIdentity>> {
949 let own_identity = self
950 .inner
951 .store
952 .get_user_identity(self.user_id())
953 .await?
954 .and_then(as_variant!(UserIdentityData::Own));
955
956 Ok(self.inner.store.get_user_identity(user_id).await?.map(|i| {
957 UserIdentity::new(
958 self.clone(),
959 i,
960 self.inner.verification_machine.to_owned(),
961 own_identity,
962 )
963 }))
964 }
965
966 pub async fn export_secret(
975 &self,
976 secret_name: &SecretName,
977 ) -> Result<Option<String>, CryptoStoreError> {
978 Ok(match secret_name {
979 SecretName::CrossSigningMasterKey
980 | SecretName::CrossSigningUserSigningKey
981 | SecretName::CrossSigningSelfSigningKey => {
982 self.inner.identity.lock().await.export_secret(secret_name).await
983 }
984 SecretName::RecoveryKey => {
985 if let Some(key) = self.load_backup_keys().await?.decryption_key {
986 let exported = key.to_base64();
987 Some(exported)
988 } else {
989 None
990 }
991 }
992 name => {
993 warn!(secret = ?name, "Unknown secret was requested");
994 None
995 }
996 })
997 }
998
999 pub async fn export_cross_signing_keys(
1007 &self,
1008 ) -> Result<Option<CrossSigningKeyExport>, CryptoStoreError> {
1009 let master_key = self.export_secret(&SecretName::CrossSigningMasterKey).await?;
1010 let self_signing_key = self.export_secret(&SecretName::CrossSigningSelfSigningKey).await?;
1011 let user_signing_key = self.export_secret(&SecretName::CrossSigningUserSigningKey).await?;
1012
1013 Ok(if master_key.is_none() && self_signing_key.is_none() && user_signing_key.is_none() {
1014 None
1015 } else {
1016 Some(CrossSigningKeyExport { master_key, self_signing_key, user_signing_key })
1017 })
1018 }
1019
1020 pub async fn import_cross_signing_keys(
1025 &self,
1026 export: CrossSigningKeyExport,
1027 ) -> Result<CrossSigningStatus, SecretImportError> {
1028 if let Some(public_identity) =
1029 self.get_identity(self.user_id()).await?.and_then(|i| i.own())
1030 {
1031 let identity = self.inner.identity.lock().await;
1032
1033 identity
1034 .import_secrets(
1035 public_identity.to_owned(),
1036 export.master_key.as_deref(),
1037 export.self_signing_key.as_deref(),
1038 export.user_signing_key.as_deref(),
1039 )
1040 .await?;
1041
1042 let status = identity.status().await;
1043
1044 let diff = identity.get_public_identity_diff(&public_identity.inner).await;
1045
1046 let mut changes =
1047 Changes { private_identity: Some(identity.clone()), ..Default::default() };
1048
1049 if diff.none_differ() {
1050 public_identity.mark_as_verified();
1051 changes.identities.changed.push(UserIdentityData::Own(public_identity.inner));
1052 }
1053
1054 info!(?status, "Successfully imported the private cross-signing keys");
1055
1056 self.save_changes(changes).await?;
1057 } else {
1058 warn!(
1059 "No public identity found while importing cross-signing keys, \
1060 a /keys/query needs to be done"
1061 );
1062 }
1063
1064 Ok(self.inner.identity.lock().await.status().await)
1065 }
1066
1067 pub async fn export_secrets_bundle(&self) -> Result<SecretsBundle, SecretsBundleExportError> {
1079 let Some(cross_signing) = self.export_cross_signing_keys().await? else {
1080 return Err(SecretsBundleExportError::MissingCrossSigningKeys);
1081 };
1082
1083 let Some(master_key) = cross_signing.master_key.clone() else {
1084 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::Master));
1085 };
1086
1087 let Some(user_signing_key) = cross_signing.user_signing_key.clone() else {
1088 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::UserSigning));
1089 };
1090
1091 let Some(self_signing_key) = cross_signing.self_signing_key.clone() else {
1092 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::SelfSigning));
1093 };
1094
1095 let backup_keys = self.load_backup_keys().await?;
1096
1097 let backup = if let Some(key) = backup_keys.decryption_key {
1098 if let Some(backup_version) = backup_keys.backup_version {
1099 Some(BackupSecrets::MegolmBackupV1Curve25519AesSha2(
1100 MegolmBackupV1Curve25519AesSha2Secrets { key, backup_version },
1101 ))
1102 } else {
1103 return Err(SecretsBundleExportError::MissingBackupVersion);
1104 }
1105 } else {
1106 None
1107 };
1108
1109 Ok(SecretsBundle {
1110 cross_signing: CrossSigningSecrets { master_key, user_signing_key, self_signing_key },
1111 backup,
1112 })
1113 }
1114
1115 pub async fn import_secrets_bundle(
1128 &self,
1129 bundle: &SecretsBundle,
1130 ) -> Result<(), SecretImportError> {
1131 let mut changes = Changes::default();
1132
1133 if let Some(backup_bundle) = &bundle.backup {
1134 match backup_bundle {
1135 BackupSecrets::MegolmBackupV1Curve25519AesSha2(bundle) => {
1136 changes.backup_decryption_key = Some(bundle.key.clone());
1137 changes.backup_version = Some(bundle.backup_version.clone());
1138 }
1139 }
1140 }
1141
1142 let identity = self.inner.identity.lock().await;
1143
1144 identity
1145 .import_secrets_unchecked(
1146 Some(&bundle.cross_signing.master_key),
1147 Some(&bundle.cross_signing.self_signing_key),
1148 Some(&bundle.cross_signing.user_signing_key),
1149 )
1150 .await?;
1151
1152 let public_identity = identity.to_public_identity().await.expect(
1153 "We should be able to create a new public identity since we just imported \
1154 all the private cross-signing keys",
1155 );
1156
1157 changes.private_identity = Some(identity.clone());
1158 changes.identities.new.push(UserIdentityData::Own(public_identity));
1159
1160 Ok(self.save_changes(changes).await?)
1161 }
1162
1163 pub async fn import_secret(&self, secret: &GossippedSecret) -> Result<(), SecretImportError> {
1165 match &secret.secret_name {
1166 SecretName::CrossSigningMasterKey
1167 | SecretName::CrossSigningUserSigningKey
1168 | SecretName::CrossSigningSelfSigningKey => {
1169 if let Some(public_identity) =
1170 self.get_identity(self.user_id()).await?.and_then(|i| i.own())
1171 {
1172 let identity = self.inner.identity.lock().await;
1173
1174 identity
1175 .import_secret(
1176 public_identity,
1177 &secret.secret_name,
1178 &secret.event.content.secret,
1179 )
1180 .await?;
1181 info!(
1182 secret_name = ?secret.secret_name,
1183 "Successfully imported a private cross signing key"
1184 );
1185
1186 let changes =
1187 Changes { private_identity: Some(identity.clone()), ..Default::default() };
1188
1189 self.save_changes(changes).await?;
1190 }
1191 }
1192 SecretName::RecoveryKey => {
1193 }
1199 name => {
1200 warn!(secret = ?name, "Tried to import an unknown secret");
1201 }
1202 }
1203
1204 Ok(())
1205 }
1206
1207 pub async fn get_only_allow_trusted_devices(&self) -> Result<bool> {
1210 let value = self.get_value("only_allow_trusted_devices").await?.unwrap_or_default();
1211 Ok(value)
1212 }
1213
1214 pub async fn set_only_allow_trusted_devices(
1217 &self,
1218 block_untrusted_devices: bool,
1219 ) -> Result<()> {
1220 self.set_value("only_allow_trusted_devices", &block_untrusted_devices).await
1221 }
1222
1223 pub async fn get_value<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>> {
1225 let Some(value) = self.get_custom_value(key).await? else {
1226 return Ok(None);
1227 };
1228 let deserialized = self.deserialize_value(&value)?;
1229 Ok(Some(deserialized))
1230 }
1231
1232 pub async fn set_value(&self, key: &str, value: &impl Serialize) -> Result<()> {
1234 let serialized = self.serialize_value(value)?;
1235 self.set_custom_value(key, serialized).await?;
1236 Ok(())
1237 }
1238
1239 fn serialize_value(&self, value: &impl Serialize) -> Result<Vec<u8>> {
1240 let serialized =
1241 rmp_serde::to_vec_named(value).map_err(|x| CryptoStoreError::Backend(x.into()))?;
1242 Ok(serialized)
1243 }
1244
1245 fn deserialize_value<T: DeserializeOwned>(&self, value: &[u8]) -> Result<T> {
1246 let deserialized =
1247 rmp_serde::from_slice(value).map_err(|e| CryptoStoreError::Backend(e.into()))?;
1248 Ok(deserialized)
1249 }
1250
1251 pub fn room_keys_received_stream(
1263 &self,
1264 ) -> impl Stream<Item = Result<Vec<RoomKeyInfo>, BroadcastStreamRecvError>> + use<> {
1265 self.inner.store.room_keys_received_stream()
1266 }
1267
1268 pub fn room_keys_withheld_received_stream(
1277 &self,
1278 ) -> impl Stream<Item = Vec<RoomKeyWithheldInfo>> + use<> {
1279 self.inner.store.room_keys_withheld_received_stream()
1280 }
1281
1282 pub fn user_identities_stream(&self) -> impl Stream<Item = IdentityUpdates> + use<> {
1313 let verification_machine = self.inner.verification_machine.to_owned();
1314
1315 let this = self.clone();
1316 self.inner.store.identities_stream().map(move |(own_identity, identities, _)| {
1317 let (new_identities, changed_identities, unchanged_identities) = identities.into_maps();
1318
1319 let map_identity = |(user_id, identity)| {
1320 (
1321 user_id,
1322 UserIdentity::new(
1323 this.clone(),
1324 identity,
1325 verification_machine.to_owned(),
1326 own_identity.to_owned(),
1327 ),
1328 )
1329 };
1330
1331 let new = new_identities.into_iter().map(map_identity).collect();
1332 let changed = changed_identities.into_iter().map(map_identity).collect();
1333 let unchanged = unchanged_identities.into_iter().map(map_identity).collect();
1334
1335 IdentityUpdates { new, changed, unchanged }
1336 })
1337 }
1338
1339 pub fn devices_stream(&self) -> impl Stream<Item = DeviceUpdates> + use<> {
1371 let verification_machine = self.inner.verification_machine.to_owned();
1372 #[cfg(feature = "experimental-x509-identity-verification")]
1373 let x509_verifier = self.x509_verifier().cloned();
1374
1375 self.inner.store.identities_stream().map(move |(own_identity, identities, devices)| {
1376 collect_device_updates(
1377 verification_machine.to_owned(),
1378 own_identity,
1379 identities,
1380 devices,
1381 #[cfg(feature = "experimental-x509-identity-verification")]
1382 x509_verifier.to_owned(),
1383 )
1384 })
1385 }
1386
1387 pub fn identities_stream_raw(
1397 &self,
1398 ) -> impl Stream<Item = (IdentityChanges, DeviceChanges)> + use<> {
1399 self.inner.store.identities_stream().map(|(_, identities, devices)| (identities, devices))
1400 }
1401
1402 pub fn create_store_lock(
1405 &self,
1406 lock_key: String,
1407 config: CrossProcessLockConfig,
1408 ) -> CrossProcessLock<LockableCryptoStore> {
1409 self.inner.store.create_store_lock(lock_key, config)
1410 }
1411
1412 pub fn secrets_stream(&self) -> impl Stream<Item = SecretsInboxItem> + use<> {
1452 self.inner.store.secrets_stream()
1453 }
1454
1455 pub fn historic_room_key_stream(&self) -> impl Stream<Item = RoomKeyBundleInfo> + use<> {
1495 self.inner.store.historic_room_key_stream()
1496 }
1497
1498 pub async fn import_room_keys(
1511 &self,
1512 exported_keys: Vec<ExportedRoomKey>,
1513 from_backup_version: Option<&str>,
1514 progress_listener: impl Fn(usize, usize),
1515 ) -> Result<RoomKeyImportResult> {
1516 let exported_keys = exported_keys.iter().filter_map(|key| {
1517 key.try_into()
1518 .map_err(|e| {
1519 warn!(
1520 sender_key = key.sender_key().to_base64(),
1521 room_id = ?key.room_id(),
1522 session_id = key.session_id(),
1523 error = ?e,
1524 "Couldn't import a room key from a file export."
1525 );
1526 })
1527 .ok()
1528 });
1529 self.import_sessions_impl(exported_keys, from_backup_version, progress_listener).await
1530 }
1531
1532 pub async fn import_exported_room_keys(
1559 &self,
1560 exported_keys: Vec<ExportedRoomKey>,
1561 progress_listener: impl Fn(usize, usize),
1562 ) -> Result<RoomKeyImportResult> {
1563 self.import_room_keys(exported_keys, None, progress_listener).await
1564 }
1565
1566 async fn import_sessions_impl(
1567 &self,
1568 sessions: impl Iterator<Item = InboundGroupSession>,
1569 from_backup_version: Option<&str>,
1570 progress_listener: impl Fn(usize, usize),
1571 ) -> Result<RoomKeyImportResult> {
1572 let sessions: Vec<_> = sessions.collect();
1573 let mut imported_sessions = Vec::new();
1574
1575 let total_count = sessions.len();
1576 let mut keys = BTreeMap::new();
1577
1578 for (i, session) in sessions.into_iter().enumerate() {
1579 if let Some(merged) = self.merge_received_group_session(session).await? {
1582 if from_backup_version.is_some() {
1583 merged.mark_as_backed_up();
1584 }
1585
1586 keys.entry(merged.room_id().to_owned())
1587 .or_insert_with(BTreeMap::new)
1588 .entry(merged.sender_key().to_base64())
1589 .or_insert_with(BTreeSet::new)
1590 .insert(merged.session_id().to_owned());
1591
1592 imported_sessions.push(merged);
1593 }
1594
1595 progress_listener(i, total_count);
1596 }
1597
1598 let imported_count = imported_sessions.len();
1599
1600 self.inner
1601 .store
1602 .save_inbound_group_sessions(imported_sessions, from_backup_version)
1603 .await?;
1604
1605 info!(total_count, imported_count, room_keys = ?keys, "Successfully imported room keys");
1606
1607 Ok(RoomKeyImportResult::new(imported_count, total_count, keys))
1608 }
1609
1610 pub(crate) fn crypto_store(&self) -> Arc<CryptoStoreWrapper> {
1611 self.inner.store.clone()
1612 }
1613
1614 pub async fn export_room_keys(
1637 &self,
1638 predicate: impl FnMut(&InboundGroupSession) -> bool,
1639 ) -> Result<Vec<ExportedRoomKey>> {
1640 let mut exported = Vec::new();
1641
1642 let mut sessions = self.get_inbound_group_sessions().await?;
1643 sessions.retain(predicate);
1644
1645 for session in sessions {
1646 let export = session.export().await;
1647 exported.push(export);
1648 }
1649
1650 Ok(exported)
1651 }
1652
1653 pub async fn export_room_keys_stream(
1688 &self,
1689 predicate: impl FnMut(&InboundGroupSession) -> bool,
1690 ) -> Result<impl Stream<Item = ExportedRoomKey>> {
1691 let sessions = self.get_inbound_group_sessions().await?;
1693 Ok(futures_util::stream::iter(sessions.into_iter().filter(predicate))
1694 .then(|session| async move { session.export().await }))
1695 }
1696
1697 pub async fn build_room_key_bundle(
1702 &self,
1703 room_id: &RoomId,
1704 ) -> std::result::Result<RoomKeyBundle, CryptoStoreError> {
1705 let sessions = self.get_inbound_group_sessions_by_room_id(room_id).await?;
1706
1707 let mut bundle = RoomKeyBundle::default();
1708 for session in sessions {
1709 if session.shared_history() {
1710 bundle.room_keys.push(session.export().await.into());
1711 } else {
1712 bundle.withheld.push(RoomKeyWithheldContent::new(
1713 session.algorithm().to_owned(),
1714 WithheldCode::HistoryNotShared,
1715 session.room_id().to_owned(),
1716 session.session_id().to_owned(),
1717 session.sender_key().to_owned(),
1718 self.device_id().to_owned(),
1719 ));
1720 }
1721 }
1722
1723 let withhelds = self.get_withheld_sessions_by_room_id(room_id).await?;
1726 for withheld in withhelds {
1727 if withheld.content.withheld_code() == WithheldCode::HistoryNotShared {
1728 bundle.withheld.push(withheld.content);
1729 }
1730 }
1731
1732 Ok(bundle)
1733 }
1734
1735 #[instrument(skip(self, bundle, progress_listener), fields(bundle_size = bundle.room_keys.len(), sender_data))]
1745 pub async fn receive_room_key_bundle(
1746 &self,
1747 bundle_info: &StoredRoomKeyBundleData,
1748 bundle: RoomKeyBundle,
1749 progress_listener: impl Fn(usize, usize),
1750 ) -> Result<(), CryptoStoreError> {
1751 let sender_data = if bundle_info.sender_data.should_recalculate() {
1752 let device = self
1753 .get_device_from_curve_key(&bundle_info.sender_user, bundle_info.sender_key)
1754 .await?;
1755
1756 device
1757 .as_ref()
1758 .map(SenderData::from_device)
1759 .unwrap_or_else(|| bundle_info.sender_data.clone())
1760 } else {
1761 bundle_info.sender_data.clone()
1762 };
1763
1764 tracing::Span::current().record("sender_data", tracing::field::debug(&sender_data));
1765
1766 let Ok(forwarder_data) = (&sender_data).try_into() else {
1770 warn!(
1771 "Not accepting a historic room key bundle due to insufficient trust in the sender"
1772 );
1773 return Ok(());
1774 };
1775
1776 self.import_room_key_bundle_sessions(
1777 bundle_info,
1778 &bundle,
1779 &forwarder_data,
1780 progress_listener,
1781 )
1782 .await?;
1783 self.import_room_key_bundle_withheld_info(bundle_info, &bundle).await?;
1784
1785 Ok(())
1786 }
1787
1788 async fn import_room_key_bundle_sessions(
1789 &self,
1790 bundle_info: &StoredRoomKeyBundleData,
1791 bundle: &RoomKeyBundle,
1792 forwarder_data: &ForwarderData,
1793 progress_listener: impl Fn(usize, usize),
1794 ) -> Result<(), CryptoStoreError> {
1795 let (good, bad): (Vec<_>, Vec<_>) = bundle.room_keys.iter().partition_map(|key| {
1796 if key.room_id != bundle_info.bundle_data.room_id {
1797 trace!("Ignoring key for incorrect room {} in bundle", key.room_id);
1798 Either::Right(key)
1799 } else {
1800 Either::Left(key)
1801 }
1802 });
1803
1804 match (bad.is_empty(), good.is_empty()) {
1805 (true, true) => {
1807 warn!("Received a completely empty room key bundle");
1808 }
1809
1810 (false, true) => {
1812 let bad_keys: Vec<_> =
1813 bad.iter().map(|&key| (&key.room_id, &key.session_id)).collect();
1814
1815 warn!(
1816 ?bad_keys,
1817 "Received a room key bundle for the wrong room, ignoring all room keys from the bundle"
1818 );
1819 }
1820
1821 (_, false) => {
1823 if !bad.is_empty() {
1826 warn!(
1827 bad_key_count = bad.len(),
1828 "The room key bundle contained some room keys \
1829 that were meant for a different room"
1830 );
1831 }
1832
1833 let keys = good.iter().filter_map(|key| {
1834 key.try_into_inbound_group_session(forwarder_data)
1835 .map_err(|e| {
1836 warn!(
1837 sender_key = ?key.sender_key().to_base64(),
1838 room_id = ?key.room_id(),
1839 session_id = key.session_id(),
1840 error = ?e,
1841 "Couldn't import a room key from a key bundle."
1842 );
1843 })
1844 .ok()
1845 });
1846
1847 self.import_sessions_impl(keys, None, progress_listener).await?;
1848 }
1849 }
1850
1851 Ok(())
1852 }
1853
1854 async fn import_room_key_bundle_withheld_info(
1855 &self,
1856 bundle_info: &StoredRoomKeyBundleData,
1857 bundle: &RoomKeyBundle,
1858 ) -> Result<(), CryptoStoreError> {
1859 let mut session_id_to_withheld_code_map = BTreeMap::new();
1860
1861 let mut changes = Changes::default();
1862 for withheld in &bundle.withheld {
1863 let (room_id, session_id) = match withheld {
1864 RoomKeyWithheldContent::MegolmV1AesSha2(c) => match (c.room_id(), c.session_id()) {
1865 (Some(room_id), Some(session_id)) => (room_id, session_id),
1866 _ => continue,
1867 },
1868 #[cfg(feature = "experimental-algorithms")]
1869 RoomKeyWithheldContent::MegolmV2AesSha2(c) => match (c.room_id(), c.session_id()) {
1870 (Some(room_id), Some(session_id)) => (room_id, session_id),
1871 _ => continue,
1872 },
1873 RoomKeyWithheldContent::Unknown(_) => continue,
1874 };
1875
1876 if room_id != bundle_info.bundle_data.room_id {
1877 trace!("Ignoring withheld info for incorrect room {} in bundle", room_id);
1878 continue;
1879 }
1880
1881 changes.withheld_session_info.entry(room_id.to_owned()).or_default().insert(
1882 session_id.to_owned(),
1883 RoomKeyWithheldEntry {
1884 sender: bundle_info.sender_user.clone(),
1885 content: withheld.to_owned(),
1886 },
1887 );
1888 session_id_to_withheld_code_map.insert(session_id, withheld.withheld_code());
1889 }
1890
1891 self.save_changes(changes).await?;
1892
1893 info!(
1894 room_id = ?bundle_info.bundle_data.room_id,
1895 ?session_id_to_withheld_code_map,
1896 "Successfully imported withheld info from room key bundle",
1897 );
1898
1899 Ok(())
1900 }
1901
1902 pub async fn store_room_pending_key_bundle(
1908 &self,
1909 room_id: &RoomId,
1910 inviter: &UserId,
1911 ) -> Result<(), CryptoStoreError> {
1912 let invite_accepted_at = MilliSecondsSinceUnixEpoch::now();
1913 self.save_changes(Changes {
1914 rooms_pending_key_bundle: HashMap::from([(
1915 room_id.to_owned(),
1916 Some(RoomPendingKeyBundleDetails {
1917 room_id: room_id.to_owned(),
1918 invite_accepted_at,
1919 inviter: inviter.to_owned(),
1920 }),
1921 )]),
1922 ..Default::default()
1923 })
1924 .await?;
1925
1926 Ok(())
1927 }
1928
1929 pub async fn clear_room_pending_key_bundle(&self, room_id: &RoomId) -> Result<()> {
1933 self.save_changes(Changes {
1934 rooms_pending_key_bundle: HashMap::from([(room_id.to_owned(), None)]),
1935 ..Default::default()
1936 })
1937 .await
1938 }
1939}
1940
1941impl Deref for Store {
1942 type Target = DynCryptoStore;
1943
1944 fn deref(&self) -> &Self::Target {
1945 self.inner.store.deref().deref()
1946 }
1947}
1948
1949#[derive(Clone, Debug)]
1951pub struct LockableCryptoStore(Arc<dyn CryptoStore<Error = CryptoStoreError>>);
1952
1953impl matrix_sdk_common::cross_process_lock::TryLock for LockableCryptoStore {
1954 type LockError = CryptoStoreError;
1955
1956 async fn try_lock(
1957 &self,
1958 lease_duration_ms: u32,
1959 key: &str,
1960 holder: &str,
1961 ) -> std::result::Result<Option<CrossProcessLockGeneration>, Self::LockError> {
1962 self.0.try_take_leased_lock(lease_duration_ms, key, holder).await
1963 }
1964}
1965
1966#[cfg(test)]
1967mod tests {
1968 use std::{collections::BTreeMap, pin::pin};
1969
1970 use assert_matches2::{assert_let, assert_matches};
1971 use futures_util::StreamExt;
1972 use insta::{_macro_support::Content, assert_json_snapshot, internals::ContentPath};
1973 use matrix_sdk_test::async_test;
1974 use rand::RngExt;
1975 use ruma::{
1976 RoomId, device_id,
1977 events::room::{EncryptedFile, EncryptedFileHashes, V2EncryptedFileInfo},
1978 owned_device_id, owned_mxc_uri, room_id, user_id,
1979 };
1980 use serde_json::json;
1981 use vodozemac::{Ed25519Keypair, megolm::SessionKey};
1982
1983 use crate::{
1984 Account, OlmMachine,
1985 machine::test_helpers::get_machine_pair,
1986 olm::{InboundGroupSession, SenderData},
1987 store::types::{DehydratedDeviceKey, RoomKeyWithheldEntry, StoredRoomKeyBundleData},
1988 types::{
1989 EventEncryptionAlgorithm,
1990 events::{
1991 room_key_bundle::RoomKeyBundleContent,
1992 room_key_withheld::{MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent},
1993 },
1994 },
1995 };
1996
1997 #[async_test]
1998 async fn test_merge_received_group_session() {
1999 let alice_account = Account::with_device_id(user_id!("@a:s.co"), device_id!("ABC"));
2000 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("DEF")).await;
2001
2002 let room_id = room_id!("!test:localhost");
2003
2004 let megolm_signing_key = Ed25519Keypair::new();
2005 let inbound = make_inbound_group_session(&alice_account, &megolm_signing_key, room_id);
2006
2007 let mut inbound_at_index_5 =
2009 InboundGroupSession::from_export(&inbound.export_at_index(5).await).unwrap();
2010 inbound_at_index_5.sender_data = inbound.sender_data.clone();
2011 bob.store().save_inbound_group_sessions(&[inbound_at_index_5.clone()]).await.unwrap();
2012
2013 let disconnected = make_inbound_group_session(&alice_account, &megolm_signing_key, room_id);
2015 assert_eq!(bob.store().merge_received_group_session(disconnected).await.unwrap(), None);
2016
2017 let mut worse =
2019 InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
2020 worse.sender_data = inbound.sender_data.clone();
2021 assert_eq!(bob.store().merge_received_group_session(worse).await.unwrap(), None);
2022
2023 let mut copy = InboundGroupSession::from_pickle(inbound_at_index_5.pickle().await).unwrap();
2025 copy.sender_data = inbound.sender_data.clone();
2026 assert_eq!(bob.store().merge_received_group_session(copy).await.unwrap(), None);
2027
2028 let mut better =
2030 InboundGroupSession::from_export(&inbound.export_at_index(0).await).unwrap();
2031 better.sender_data = inbound.sender_data.clone();
2032 assert_let!(Some(update) = bob.store().merge_received_group_session(better).await.unwrap());
2033 assert_eq!(update.first_known_index(), 0);
2034
2035 {
2037 let mut worse_ratchet_better_trust =
2038 InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
2039 let updated_sender_data = SenderData::sender_verified(
2040 alice_account.user_id(),
2041 alice_account.device_id(),
2042 Ed25519Keypair::new().public_key(),
2043 );
2044 worse_ratchet_better_trust.sender_data = updated_sender_data.clone();
2045 assert_let!(
2046 Some(update) = bob
2047 .store()
2048 .merge_received_group_session(worse_ratchet_better_trust)
2049 .await
2050 .unwrap()
2051 );
2052 assert_eq!(update.sender_data, updated_sender_data);
2053 assert_eq!(update.first_known_index(), 5);
2054 assert_eq!(
2055 update.export_at_index(0).await.session_key.to_bytes(),
2056 inbound.export_at_index(5).await.session_key.to_bytes()
2057 );
2058 }
2059
2060 {
2062 let mut better_ratchet_worse_trust =
2063 InboundGroupSession::from_export(&inbound.export_at_index(0).await).unwrap();
2064 let updated_sender_data = SenderData::unknown();
2065 better_ratchet_worse_trust.sender_data = updated_sender_data.clone();
2066 assert_let!(
2067 Some(update) = bob
2068 .store()
2069 .merge_received_group_session(better_ratchet_worse_trust)
2070 .await
2071 .unwrap()
2072 );
2073 assert_eq!(update.sender_data, inbound.sender_data);
2074 assert_eq!(update.first_known_index(), 0);
2075 assert_eq!(
2076 update.export_at_index(0).await.session_key.to_bytes(),
2077 inbound.export_at_index(0).await.session_key.to_bytes()
2078 );
2079 }
2080 }
2081
2082 fn make_inbound_group_session(
2085 sender_account: &Account,
2086 signing_key: &Ed25519Keypair,
2087 room_id: &RoomId,
2088 ) -> InboundGroupSession {
2089 InboundGroupSession::new(
2090 sender_account.identity_keys.curve25519,
2091 sender_account.identity_keys.ed25519,
2092 room_id,
2093 &make_session_key(signing_key),
2094 SenderData::device_info(crate::types::DeviceKeys::new(
2095 sender_account.user_id().to_owned(),
2096 sender_account.device_id().to_owned(),
2097 vec![],
2098 BTreeMap::new(),
2099 crate::types::Signatures::new(),
2100 )),
2101 None,
2102 EventEncryptionAlgorithm::MegolmV1AesSha2,
2103 Some(ruma::events::room::history_visibility::HistoryVisibility::Shared),
2104 true,
2105 )
2106 .unwrap()
2107 }
2108
2109 fn make_session_key(signing_key: &Ed25519Keypair) -> SessionKey {
2112 let mut session_key_bytes = vec![0u8; 229];
2117 session_key_bytes[0] = 2;
2119 rand::rng().fill(&mut session_key_bytes[5..133]);
2122 session_key_bytes[133..165].copy_from_slice(signing_key.public_key().as_bytes());
2124 let sig = signing_key.sign(&session_key_bytes[0..165]);
2126 session_key_bytes[165..229].copy_from_slice(&sig.to_bytes());
2127
2128 SessionKey::from_bytes(&session_key_bytes).unwrap()
2129 }
2130
2131 #[async_test]
2132 async fn test_import_room_keys_notifies_stream() {
2133 use futures_util::FutureExt;
2134
2135 let (alice, bob, _) =
2136 get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2137
2138 let room1_id = room_id!("!room1:localhost");
2139 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2140 let exported_sessions = alice.store().export_room_keys(|_| true).await.unwrap();
2141
2142 let mut room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream());
2143 bob.store().import_room_keys(exported_sessions, None, |_, _| {}).await.unwrap();
2144
2145 let room_keys = room_keys_received_stream
2146 .next()
2147 .now_or_never()
2148 .flatten()
2149 .expect("We should have received an update of room key infos")
2150 .unwrap();
2151 assert_eq!(room_keys.len(), 1);
2152 assert_eq!(room_keys[0].room_id, "!room1:localhost");
2153 }
2154
2155 #[async_test]
2156 async fn test_export_room_keys_provides_selected_keys() {
2157 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2159 let room1_id = room_id!("!room1:localhost");
2160 let room2_id = room_id!("!room2:localhost");
2161 let room3_id = room_id!("!room3:localhost");
2162 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2163 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2164 alice.create_outbound_group_session_with_defaults_test_helper(room3_id).await.unwrap();
2165
2166 let keys = alice
2168 .store()
2169 .export_room_keys(|s| s.room_id() == room2_id || s.room_id() == room3_id)
2170 .await
2171 .unwrap();
2172
2173 assert_eq!(keys.len(), 2);
2175 assert_eq!(keys[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2176 assert_eq!(keys[1].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2177 assert_eq!(keys[0].room_id, "!room2:localhost");
2178 assert_eq!(keys[1].room_id, "!room3:localhost");
2179 assert_eq!(keys[0].session_key.to_base64().len(), 220);
2180 assert_eq!(keys[1].session_key.to_base64().len(), 220);
2181 }
2182
2183 #[async_test]
2184 async fn test_export_room_keys_stream_can_provide_all_keys() {
2185 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2187 let room1_id = room_id!("!room1:localhost");
2188 let room2_id = room_id!("!room2:localhost");
2189 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2190 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2191
2192 let mut keys = pin!(alice.store().export_room_keys_stream(|_| true).await.unwrap());
2194
2195 let mut collected = vec![];
2197 while let Some(key) = keys.next().await {
2198 collected.push(key);
2199 }
2200
2201 assert_eq!(collected.len(), 2);
2203 assert_eq!(collected[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2204 assert_eq!(collected[1].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2205 assert_eq!(collected[0].room_id, "!room1:localhost");
2206 assert_eq!(collected[1].room_id, "!room2:localhost");
2207 assert_eq!(collected[0].session_key.to_base64().len(), 220);
2208 assert_eq!(collected[1].session_key.to_base64().len(), 220);
2209 }
2210
2211 #[async_test]
2212 async fn test_export_room_keys_stream_can_provide_a_subset_of_keys() {
2213 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2215 let room1_id = room_id!("!room1:localhost");
2216 let room2_id = room_id!("!room2:localhost");
2217 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2218 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2219
2220 let mut keys =
2222 pin!(alice.store().export_room_keys_stream(|s| s.room_id() == room1_id).await.unwrap());
2223
2224 let mut collected = vec![];
2226 while let Some(key) = keys.next().await {
2227 collected.push(key);
2228 }
2229
2230 assert_eq!(collected.len(), 1);
2232 assert_eq!(collected[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2233 assert_eq!(collected[0].room_id, "!room1:localhost");
2234 assert_eq!(collected[0].session_key.to_base64().len(), 220);
2235 }
2236
2237 #[async_test]
2238 async fn test_export_secrets_bundle() {
2239 let user_id = user_id!("@alice:example.com");
2240 let (first, second, _) = get_machine_pair(user_id, user_id, false).await;
2241
2242 let _ = first
2243 .bootstrap_cross_signing(false)
2244 .await
2245 .expect("We should be able to bootstrap cross-signing");
2246
2247 let bundle = first.store().export_secrets_bundle().await.expect(
2248 "We should be able to export the secrets bundle, now that we \
2249 have the cross-signing keys",
2250 );
2251
2252 assert!(bundle.backup.is_none(), "The bundle should not contain a backup key");
2253
2254 second
2255 .store()
2256 .import_secrets_bundle(&bundle)
2257 .await
2258 .expect("We should be able to import the secrets bundle");
2259
2260 let status = second.cross_signing_status().await;
2261 let identity = second.get_identity(user_id, None).await.unwrap().unwrap().own().unwrap();
2262
2263 assert!(identity.is_verified(), "The public identity should be marked as verified.");
2264
2265 assert!(status.is_complete(), "We should have imported all the cross-signing keys");
2266 }
2267
2268 #[async_test]
2269 async fn test_create_dehydrated_device_key() {
2270 let pickle_key = DehydratedDeviceKey::new();
2271
2272 let to_vec = pickle_key.inner.to_vec();
2273 let pickle_key_from_slice = DehydratedDeviceKey::from_slice(to_vec.as_slice())
2274 .expect("Should be able to create a dehydrated device key from slice");
2275
2276 assert_eq!(pickle_key_from_slice.to_base64(), pickle_key.to_base64());
2277 }
2278
2279 #[async_test]
2280 async fn test_create_dehydrated_errors() {
2281 let too_small = [0u8; 22];
2282 let pickle_key = DehydratedDeviceKey::from_slice(&too_small);
2283
2284 assert!(pickle_key.is_err());
2285
2286 let too_big = [0u8; 40];
2287 let pickle_key = DehydratedDeviceKey::from_slice(&too_big);
2288
2289 assert!(pickle_key.is_err());
2290 }
2291
2292 #[async_test]
2293 async fn test_build_room_key_bundle() {
2294 let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2297 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2298
2299 let room1_id = room_id!("!room1:localhost");
2300 let room2_id = room_id!("!room2:localhost");
2301
2302 let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2307 let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2308 let session_key3 = "AgAAAAAM9KFsliaUUhGSXgwOzM5UemjkNH4n8NHgvC/y8hhw13zTF+ooGD4uIYEXYX630oNvQm/EvgZo+dkoc0re+vsqsx4sQeNODdSjcBsWOa0oDF+irQn9oYoLUDPI1IBtY1rX+FV99Zm/xnG7uFOX7aTVlko2GSdejy1w9mfobmfxu5aUc04A9zaKJP1pOthZvRAlhpymGYHgsDtWPrrjyc/yypMflE4kIUEEEtu1kT6mrAmcl615XYRAHYK9G2+fZsGvokwzbkl4nulGwcZMpQEoM0nD2o3GWgX81HW3nGfKBg";
2309 let session_key4 = "AgAAAAA4Kkesxq2h4v9PLD6Sm3Smxspz1PXTqytQPCMQMkkrHNmzV2bHlJ+6/Al9cu8vh1Oj69AK0WUAeJOJuaiskEeg/PI3P03+UYLeC379RzgqwSHdBgdQ41G2vD6zpgmE/8vYToe+qpCZACtPOswZxyqxHH+T/Iq0nv13JmlFGIeA6fEPfr5Y28B49viG74Fs9rxV9EH5PfjbuPM/p+Sz5obShuaBPKQBX1jT913nEXPoIJ06exNZGr0285nw/LgVvNlmWmbqNnbzO2cNZjQWA+xZYz5FSfyCxwqEBbEdUCuRCQ";
2310
2311 let sessions = [
2312 create_inbound_group_session_with_visibility(
2313 &alice,
2314 room1_id,
2315 &SessionKey::from_base64(session_key1).unwrap(),
2316 true,
2317 ),
2318 create_inbound_group_session_with_visibility(
2319 &alice,
2320 room1_id,
2321 &SessionKey::from_base64(session_key2).unwrap(),
2322 true,
2323 ),
2324 create_inbound_group_session_with_visibility(
2325 &alice,
2326 room1_id,
2327 &SessionKey::from_base64(session_key3).unwrap(),
2328 false,
2329 ),
2330 create_inbound_group_session_with_visibility(
2331 &alice,
2332 room2_id,
2333 &SessionKey::from_base64(session_key4).unwrap(),
2334 true,
2335 ),
2336 ];
2337 bob.store().save_inbound_group_sessions(&sessions).await.unwrap();
2338
2339 let mut bundle = bob.store().build_room_key_bundle(room1_id).await.unwrap();
2341
2342 bundle.room_keys.sort_by_key(|session| session.session_id.clone());
2346
2347 let algorithm = if cfg!(feature = "experimental-algorithms") {
2349 "m.megolm.v2.aes-sha2"
2350 } else {
2351 "m.megolm.v1.aes-sha2"
2352 };
2353 let map_algorithm = move |value: Content, _path: ContentPath<'_>| {
2354 assert_eq!(value.as_str().unwrap(), algorithm);
2355 "[algorithm]"
2356 };
2357
2358 let alice_curve_key = alice.identity_keys().curve25519.to_base64();
2360 let map_alice_curve_key = move |value: Content, _path: ContentPath<'_>| {
2361 assert_eq!(value.as_str().unwrap(), alice_curve_key);
2362 "[alice curve key]"
2363 };
2364 let alice_ed25519_key = alice.identity_keys().ed25519.to_base64();
2365 let map_alice_ed25519_key = move |value: Content, _path: ContentPath<'_>| {
2366 assert_eq!(value.as_str().unwrap(), alice_ed25519_key);
2367 "[alice ed25519 key]"
2368 };
2369
2370 insta::with_settings!({ sort_maps => true }, {
2371 assert_json_snapshot!(bundle, {
2372 ".withheld[].algorithm" => insta::dynamic_redaction(map_algorithm),
2373 ".room_keys[].algorithm" => insta::dynamic_redaction(map_algorithm),
2374 ".room_keys[].sender_key" => insta::dynamic_redaction(map_alice_curve_key.clone()),
2375 ".withheld[].sender_key" => insta::dynamic_redaction(map_alice_curve_key),
2376 ".room_keys[].sender_claimed_keys.ed25519" => insta::dynamic_redaction(map_alice_ed25519_key),
2377 });
2378 });
2379 }
2380
2381 #[async_test]
2382 async fn test_receive_room_key_bundle() {
2383 let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2384 let alice_key = alice.identity_keys().curve25519;
2385 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2386
2387 let room_id = room_id!("!room1:localhost");
2388
2389 let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2390 let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2391
2392 let sessions = [
2393 create_inbound_group_session_with_visibility(
2394 &alice,
2395 room_id,
2396 &SessionKey::from_base64(session_key1).unwrap(),
2397 true,
2398 ),
2399 create_inbound_group_session_with_visibility(
2400 &alice,
2401 room_id,
2402 &SessionKey::from_base64(session_key2).unwrap(),
2403 false,
2404 ),
2405 ];
2406
2407 alice.store().save_inbound_group_sessions(&sessions).await.unwrap();
2408 let bundle = alice.store().build_room_key_bundle(room_id).await.unwrap();
2409
2410 bob.store()
2411 .receive_room_key_bundle(
2412 &StoredRoomKeyBundleData {
2413 sender_user: alice.user_id().to_owned(),
2414 sender_key: alice_key,
2415 sender_data: SenderData::sender_verified(
2416 alice.user_id(),
2417 device_id!("ALICE"),
2418 alice.identity_keys().ed25519,
2419 ),
2420
2421 bundle_data: RoomKeyBundleContent {
2422 room_id: room_id.to_owned(),
2423 file: EncryptedFile::new(
2426 owned_mxc_uri!("mxc://example.com/0"),
2427 V2EncryptedFileInfo::encode([0; 32], [0; 16]).into(),
2428 EncryptedFileHashes::with_sha256([0; 32]),
2429 ),
2430 },
2431 },
2432 bundle,
2433 |_, _| {},
2434 )
2435 .await
2436 .unwrap();
2437
2438 let imported_sessions =
2440 bob.store().get_inbound_group_sessions_by_room_id(room_id).await.unwrap();
2441
2442 assert_eq!(imported_sessions.len(), 1);
2443 assert_eq!(imported_sessions[0].room_id(), room_id);
2444
2445 assert_eq!(
2447 imported_sessions[0]
2448 .forwarder_data
2449 .as_ref()
2450 .expect("Session should contain forwarder data.")
2451 .user_id(),
2452 alice.user_id()
2453 );
2454
2455 assert_matches!(
2456 bob.store()
2457 .get_withheld_info(room_id, sessions[1].session_id())
2458 .await
2459 .unwrap()
2460 .expect("Withheld info should be present in the store."),
2461 RoomKeyWithheldEntry {
2462 #[cfg(not(feature = "experimental-algorithms"))]
2463 content: RoomKeyWithheldContent::MegolmV1AesSha2(
2464 MegolmV1AesSha2WithheldContent::HistoryNotShared(_)
2465 ),
2466 #[cfg(feature = "experimental-algorithms")]
2467 content: RoomKeyWithheldContent::MegolmV2AesSha2(
2468 MegolmV1AesSha2WithheldContent::HistoryNotShared(_)
2469 ),
2470 ..
2471 }
2472 );
2473 }
2474
2475 #[async_test]
2481 async fn test_deserialize_room_key_withheld_entry_from_to_device_event() {
2482 let entry: RoomKeyWithheldEntry = serde_json::from_value(json!(
2483 {
2484 "content": {
2485 "algorithm": "m.megolm.v1.aes-sha2",
2486 "code": "m.unauthorised",
2487 "from_device": "ALICE",
2488 "reason": "You are not authorised to read the message.",
2489 "room_id": "!roomid:s.co",
2490 "sender_key": "7hIcOrEroXYdzjtCBvBjUiqvT0Me7g+ymeXqoc65RS0",
2491 "session_id": "session123"
2492 },
2493 "sender": "@alice:s.co",
2494 "type": "m.room_key.withheld"
2495 }
2496 ))
2497 .unwrap();
2498
2499 assert_matches!(
2500 entry,
2501 RoomKeyWithheldEntry {
2502 sender,
2503 content: RoomKeyWithheldContent::MegolmV1AesSha2(
2504 MegolmV1AesSha2WithheldContent::Unauthorised(withheld_content,)
2505 ),
2506 }
2507 );
2508
2509 assert_eq!(sender, "@alice:s.co");
2510 assert_eq!(withheld_content.room_id, "!roomid:s.co");
2511 assert_eq!(withheld_content.session_id, "session123");
2512 assert_eq!(
2513 withheld_content.sender_key.to_base64(),
2514 "7hIcOrEroXYdzjtCBvBjUiqvT0Me7g+ymeXqoc65RS0"
2515 );
2516 assert_eq!(withheld_content.from_device, Some(owned_device_id!("ALICE")));
2517 }
2518
2519 fn create_inbound_group_session_with_visibility(
2530 olm_machine: &OlmMachine,
2531 room_id: &RoomId,
2532 session_key: &SessionKey,
2533 shared_history: bool,
2534 ) -> InboundGroupSession {
2535 let identity_keys = &olm_machine.store().static_account().identity_keys;
2536 InboundGroupSession::new(
2537 identity_keys.curve25519,
2538 identity_keys.ed25519,
2539 room_id,
2540 session_key,
2541 SenderData::unknown(),
2542 None,
2543 #[cfg(not(feature = "experimental-algorithms"))]
2544 EventEncryptionAlgorithm::MegolmV1AesSha2,
2545 #[cfg(feature = "experimental-algorithms")]
2546 EventEncryptionAlgorithm::MegolmV2AesSha2,
2547 None,
2548 shared_history,
2549 )
2550 .unwrap()
2551 }
2552}