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) -> DeviceUpdates {
380 let mut new: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
381 let mut changed: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
382
383 let (new_identities, changed_identities, unchanged_identities) = identities.into_maps();
384
385 let map_device = |device: DeviceData| {
386 let device_owner_identity = new_identities
387 .get(device.user_id())
388 .or_else(|| changed_identities.get(device.user_id()))
389 .or_else(|| unchanged_identities.get(device.user_id()))
390 .cloned();
391
392 Device {
393 inner: device,
394 verification_machine: verification_machine.to_owned(),
395 own_identity: own_identity.to_owned(),
396 device_owner_identity,
397 }
398 };
399
400 for device in devices.new {
401 let device = map_device(device);
402
403 new.entry(device.user_id().to_owned())
404 .or_default()
405 .insert(device.device_id().to_owned(), device);
406 }
407
408 for device in devices.changed {
409 let device = map_device(device);
410
411 changed
412 .entry(device.user_id().to_owned())
413 .or_default()
414 .insert(device.device_id().to_owned(), device.to_owned());
415 }
416
417 DeviceUpdates { new, changed }
418}
419
420#[allow(missing_debug_implementations)]
422pub struct StoreTransaction {
423 store: Store,
424 changes: PendingChanges,
425 cache: OwnedRwLockWriteGuard<StoreCache>,
427}
428
429impl StoreTransaction {
430 async fn new(store: Store) -> Self {
432 let cache = store.inner.cache.clone();
433
434 Self { store, changes: PendingChanges::default(), cache: cache.clone().write_owned().await }
435 }
436
437 pub(crate) fn cache(&self) -> &StoreCache {
438 &self.cache
439 }
440
441 pub fn store(&self) -> &Store {
443 &self.store
444 }
445
446 pub async fn account(&mut self) -> Result<&mut Account> {
453 if self.changes.account.is_none() {
454 let _ = self.cache.account().await?;
456 self.changes.account = self.cache.account.lock().await.take();
457 }
458 Ok(self.changes.account.as_mut().unwrap())
459 }
460
461 pub async fn commit(self) -> Result<()> {
464 if self.changes.is_empty() {
465 return Ok(());
466 }
467
468 let account = self.changes.account.as_ref().map(|acc| acc.deep_clone());
470
471 self.store.save_pending_changes(self.changes).await?;
472
473 if let Some(account) = account {
475 *self.cache.account.lock().await = Some(account);
476 }
477
478 Ok(())
479 }
480}
481
482#[derive(Debug)]
483struct StoreInner {
484 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
485 store: Arc<CryptoStoreWrapper>,
486
487 cache: Arc<RwLock<StoreCache>>,
491
492 verification_machine: VerificationMachine,
493
494 #[cfg(feature = "experimental-x509-identity-verification")]
497 x509_verifier: Option<X509Verifier>,
498
499 #[cfg(feature = "experimental-x509-identity-verification")]
502 x509_signer: Option<X509Signer>,
503
504 static_account: StaticAccountData,
507}
508
509#[derive(Debug, Error)]
512pub enum SecretImportError {
513 #[error("Error while importing {name}: {error}")]
515 Key {
516 name: SecretName,
518 error: vodozemac::KeyError,
520 },
521 #[error(
524 "Error while importing {name}: The public key of the imported private \
525 key doesn't match the public key that was uploaded to the server"
526 )]
527 MismatchedPublicKeys {
528 name: SecretName,
530 },
531 #[error(transparent)]
533 Store(#[from] CryptoStoreError),
534}
535
536#[derive(Debug, Error)]
541pub enum SecretsBundleExportError {
542 #[error(transparent)]
544 Store(#[from] CryptoStoreError),
545 #[error("The store is missing one or multiple cross-signing keys")]
547 MissingCrossSigningKey(KeyUsage),
548 #[error("The store doesn't contain any cross-signing keys")]
550 MissingCrossSigningKeys,
551 #[error("The store contains a backup key, but no backup version")]
554 MissingBackupVersion,
555}
556
557impl Store {
558 pub(crate) fn new(
560 account: StaticAccountData,
561 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
562 store: Arc<CryptoStoreWrapper>,
563 verification_machine: VerificationMachine,
564 ) -> Self {
565 Self::new_with_x509(
566 account,
567 identity,
568 store,
569 verification_machine,
570 #[cfg(feature = "experimental-x509-identity-verification")]
571 None,
572 #[cfg(feature = "experimental-x509-identity-verification")]
573 None,
574 )
575 }
576
577 pub(crate) fn new_with_x509(
579 account: StaticAccountData,
580 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
581 store: Arc<CryptoStoreWrapper>,
582 verification_machine: VerificationMachine,
583 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
584 X509Verifier,
585 >,
586 #[cfg(feature = "experimental-x509-identity-verification")] x509_signer: Option<X509Signer>,
587 ) -> Self {
588 Self {
589 inner: Arc::new(StoreInner {
590 static_account: account,
591 identity,
592 store: store.clone(),
593 verification_machine,
594 #[cfg(feature = "experimental-x509-identity-verification")]
595 x509_verifier,
596 #[cfg(feature = "experimental-x509-identity-verification")]
597 x509_signer,
598 cache: Arc::new(RwLock::new(StoreCache {
599 store,
600 tracked_users: Default::default(),
601 loaded_tracked_users: Default::default(),
602 account: Default::default(),
603 })),
604 }),
605 }
606 }
607
608 pub(crate) fn user_id(&self) -> &UserId {
610 &self.inner.static_account.user_id
611 }
612
613 pub(crate) fn device_id(&self) -> &DeviceId {
615 self.inner.verification_machine.own_device_id()
616 }
617
618 pub(crate) fn static_account(&self) -> &StaticAccountData {
620 &self.inner.static_account
621 }
622
623 #[cfg(feature = "experimental-x509-identity-verification")]
626 pub(crate) fn x509_signer(&self) -> Option<&X509Signer> {
627 self.inner.x509_signer.as_ref()
628 }
629
630 #[cfg(feature = "experimental-x509-identity-verification")]
633 pub(crate) fn x509_verifier(&self) -> Option<&X509Verifier> {
634 self.inner.x509_verifier.as_ref()
635 }
636
637 pub(crate) async fn cache(&self) -> Result<StoreCacheGuard> {
638 Ok(StoreCacheGuard { cache: self.inner.cache.clone().read_owned().await })
639 }
640
641 pub(crate) async fn transaction(&self) -> StoreTransaction {
642 StoreTransaction::new(self.clone()).await
643 }
644
645 pub(crate) async fn with_transaction<T>(
646 &self,
647 func: impl AsyncFnOnce(&mut StoreTransaction) -> Result<T, crate::OlmError>,
648 ) -> Result<T, crate::OlmError> {
649 let mut tr = self.transaction().await;
650 let res = func(&mut tr).await?;
651 tr.commit().await?;
652 Ok(res)
653 }
654
655 #[cfg(test)]
656 pub(crate) async fn reset_cross_signing_identity(&self) {
658 self.inner.identity.lock().await.reset();
659 }
660
661 pub(crate) fn private_identity(&self) -> Arc<Mutex<PrivateCrossSigningIdentity>> {
663 self.inner.identity.clone()
664 }
665
666 pub(crate) async fn save_sessions(&self, sessions: &[Session]) -> Result<()> {
668 let changes = Changes { sessions: sessions.to_vec(), ..Default::default() };
669
670 self.save_changes(changes).await
671 }
672
673 pub(crate) async fn get_sessions(
674 &self,
675 sender_key: &str,
676 ) -> Result<Option<Arc<Mutex<Vec<Session>>>>> {
677 self.inner.store.get_sessions(sender_key).await
678 }
679
680 pub(crate) async fn save_changes(&self, changes: Changes) -> Result<()> {
681 self.inner.store.save_changes(changes).await
682 }
683
684 pub(crate) async fn merge_received_group_session(
692 &self,
693 session: InboundGroupSession,
694 ) -> Result<Option<InboundGroupSession>> {
695 let old_session = self
696 .inner
697 .store
698 .get_inbound_group_session(session.room_id(), session.session_id())
699 .await?;
700
701 let Some(old_session) = old_session else {
703 info!("Received a new megolm room key");
704 return Ok(Some(session));
705 };
706
707 let index_comparison = session.compare_ratchet(&old_session).await;
708 let trust_level_comparison =
709 session.sender_data.compare_trust_level(&old_session.sender_data);
710
711 let result = match (index_comparison, trust_level_comparison) {
712 (SessionOrdering::Unconnected, _) => {
713 warn!(
717 "Received a group session with an ratchet that does not connect to the one in the store, discarding"
718 );
719 None
720 }
721
722 (SessionOrdering::Better, std::cmp::Ordering::Greater)
723 | (SessionOrdering::Better, std::cmp::Ordering::Equal)
724 | (SessionOrdering::Equal, std::cmp::Ordering::Greater) => {
725 info!(
727 ?index_comparison,
728 ?trust_level_comparison,
729 "Received a megolm room key that we have a worse version of, merging"
730 );
731 Some(session)
732 }
733
734 (SessionOrdering::Worse, std::cmp::Ordering::Less)
735 | (SessionOrdering::Worse, std::cmp::Ordering::Equal)
736 | (SessionOrdering::Equal, std::cmp::Ordering::Less) => {
737 warn!(
739 ?index_comparison,
740 ?trust_level_comparison,
741 "Received a megolm room key that we already have a better version \
742 of, discarding"
743 );
744 None
745 }
746
747 (SessionOrdering::Equal, std::cmp::Ordering::Equal) => {
748 info!("Received a megolm room key that we already have, discarding");
750 None
751 }
752
753 (SessionOrdering::Better, std::cmp::Ordering::Less) => {
754 info!("Upgrading a previously-received megolm session with new ratchet");
757 let result = old_session.with_ratchet(&session);
758 result.reset_backup_state();
760 Some(result)
761 }
762
763 (SessionOrdering::Worse, std::cmp::Ordering::Greater) => {
764 info!("Upgrading a previously-received megolm session with new sender data");
767 Some(session.with_ratchet(&old_session))
768 }
769 };
770
771 Ok(result)
772 }
773
774 #[cfg(test)]
775 pub(crate) async fn save_device_data(&self, devices: &[DeviceData]) -> Result<()> {
777 use types::DeviceChanges;
778
779 let changes = Changes {
780 devices: DeviceChanges { changed: devices.to_vec(), ..Default::default() },
781 ..Default::default()
782 };
783
784 self.save_changes(changes).await
785 }
786
787 pub(crate) async fn save_inbound_group_sessions(
789 &self,
790 sessions: &[InboundGroupSession],
791 ) -> Result<()> {
792 let changes = Changes { inbound_group_sessions: sessions.to_vec(), ..Default::default() };
793
794 self.save_changes(changes).await
795 }
796
797 pub(crate) async fn device_display_name(&self) -> Result<Option<String>, CryptoStoreError> {
799 Ok(self
800 .inner
801 .store
802 .get_device(self.user_id(), self.device_id())
803 .await?
804 .and_then(|d| d.display_name().map(|d| d.to_owned())))
805 }
806
807 pub(crate) async fn get_device_data(
812 &self,
813 user_id: &UserId,
814 device_id: &DeviceId,
815 ) -> Result<Option<DeviceData>> {
816 self.inner.store.get_device(user_id, device_id).await
817 }
818
819 pub(crate) async fn get_device_data_for_user_filtered(
827 &self,
828 user_id: &UserId,
829 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
830 self.inner.store.get_user_devices(user_id).await.map(|mut d| {
831 if user_id == self.user_id() {
832 d.remove(self.device_id());
833 }
834 d
835 })
836 }
837
838 pub(crate) async fn get_device_data_for_user(
847 &self,
848 user_id: &UserId,
849 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
850 self.inner.store.get_user_devices(user_id).await
851 }
852
853 pub(crate) async fn get_device_from_curve_key(
859 &self,
860 user_id: &UserId,
861 curve_key: Curve25519PublicKey,
862 ) -> Result<Option<Device>> {
863 self.get_user_devices(user_id)
864 .await
865 .map(|d| d.devices().find(|d| d.curve25519_key() == Some(curve_key)))
866 }
867
868 pub(crate) async fn get_user_devices(&self, user_id: &UserId) -> Result<UserDevices> {
878 let devices = self.get_device_data_for_user(user_id).await?;
879
880 let own_identity = self
881 .inner
882 .store
883 .get_user_identity(self.user_id())
884 .await?
885 .and_then(|i| i.own().cloned());
886 let device_owner_identity = self.inner.store.get_user_identity(user_id).await?;
887
888 Ok(UserDevices {
889 inner: devices,
890 verification_machine: self.inner.verification_machine.clone(),
891 own_identity,
892 device_owner_identity,
893 })
894 }
895
896 pub(crate) async fn get_device(
906 &self,
907 user_id: &UserId,
908 device_id: &DeviceId,
909 ) -> Result<Option<Device>> {
910 if let Some(device_data) = self.inner.store.get_device(user_id, device_id).await? {
911 Ok(Some(self.wrap_device_data(device_data).await?))
912 } else {
913 Ok(None)
914 }
915 }
916
917 pub(crate) async fn wrap_device_data(&self, device_data: DeviceData) -> Result<Device> {
922 let own_identity = self
923 .inner
924 .store
925 .get_user_identity(self.user_id())
926 .await?
927 .and_then(|i| i.own().cloned());
928
929 let device_owner_identity =
930 self.inner.store.get_user_identity(device_data.user_id()).await?;
931
932 Ok(Device {
933 inner: device_data,
934 verification_machine: self.inner.verification_machine.clone(),
935 own_identity,
936 device_owner_identity,
937 })
938 }
939
940 pub(crate) async fn get_identity(&self, user_id: &UserId) -> Result<Option<UserIdentity>> {
942 let own_identity = self
943 .inner
944 .store
945 .get_user_identity(self.user_id())
946 .await?
947 .and_then(as_variant!(UserIdentityData::Own));
948
949 Ok(self.inner.store.get_user_identity(user_id).await?.map(|i| {
950 UserIdentity::new(
951 self.clone(),
952 i,
953 self.inner.verification_machine.to_owned(),
954 own_identity,
955 )
956 }))
957 }
958
959 pub async fn export_secret(
968 &self,
969 secret_name: &SecretName,
970 ) -> Result<Option<String>, CryptoStoreError> {
971 Ok(match secret_name {
972 SecretName::CrossSigningMasterKey
973 | SecretName::CrossSigningUserSigningKey
974 | SecretName::CrossSigningSelfSigningKey => {
975 self.inner.identity.lock().await.export_secret(secret_name).await
976 }
977 SecretName::RecoveryKey => {
978 if let Some(key) = self.load_backup_keys().await?.decryption_key {
979 let exported = key.to_base64();
980 Some(exported)
981 } else {
982 None
983 }
984 }
985 name => {
986 warn!(secret = ?name, "Unknown secret was requested");
987 None
988 }
989 })
990 }
991
992 pub async fn export_cross_signing_keys(
1000 &self,
1001 ) -> Result<Option<CrossSigningKeyExport>, CryptoStoreError> {
1002 let master_key = self.export_secret(&SecretName::CrossSigningMasterKey).await?;
1003 let self_signing_key = self.export_secret(&SecretName::CrossSigningSelfSigningKey).await?;
1004 let user_signing_key = self.export_secret(&SecretName::CrossSigningUserSigningKey).await?;
1005
1006 Ok(if master_key.is_none() && self_signing_key.is_none() && user_signing_key.is_none() {
1007 None
1008 } else {
1009 Some(CrossSigningKeyExport { master_key, self_signing_key, user_signing_key })
1010 })
1011 }
1012
1013 pub async fn import_cross_signing_keys(
1018 &self,
1019 export: CrossSigningKeyExport,
1020 ) -> Result<CrossSigningStatus, SecretImportError> {
1021 if let Some(public_identity) =
1022 self.get_identity(self.user_id()).await?.and_then(|i| i.own())
1023 {
1024 let identity = self.inner.identity.lock().await;
1025
1026 identity
1027 .import_secrets(
1028 public_identity.to_owned(),
1029 export.master_key.as_deref(),
1030 export.self_signing_key.as_deref(),
1031 export.user_signing_key.as_deref(),
1032 )
1033 .await?;
1034
1035 let status = identity.status().await;
1036
1037 let diff = identity.get_public_identity_diff(&public_identity.inner).await;
1038
1039 let mut changes =
1040 Changes { private_identity: Some(identity.clone()), ..Default::default() };
1041
1042 if diff.none_differ() {
1043 public_identity.mark_as_verified();
1044 changes.identities.changed.push(UserIdentityData::Own(public_identity.inner));
1045 }
1046
1047 info!(?status, "Successfully imported the private cross-signing keys");
1048
1049 self.save_changes(changes).await?;
1050 } else {
1051 warn!(
1052 "No public identity found while importing cross-signing keys, \
1053 a /keys/query needs to be done"
1054 );
1055 }
1056
1057 Ok(self.inner.identity.lock().await.status().await)
1058 }
1059
1060 pub async fn export_secrets_bundle(&self) -> Result<SecretsBundle, SecretsBundleExportError> {
1072 let Some(cross_signing) = self.export_cross_signing_keys().await? else {
1073 return Err(SecretsBundleExportError::MissingCrossSigningKeys);
1074 };
1075
1076 let Some(master_key) = cross_signing.master_key.clone() else {
1077 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::Master));
1078 };
1079
1080 let Some(user_signing_key) = cross_signing.user_signing_key.clone() else {
1081 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::UserSigning));
1082 };
1083
1084 let Some(self_signing_key) = cross_signing.self_signing_key.clone() else {
1085 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::SelfSigning));
1086 };
1087
1088 let backup_keys = self.load_backup_keys().await?;
1089
1090 let backup = if let Some(key) = backup_keys.decryption_key {
1091 if let Some(backup_version) = backup_keys.backup_version {
1092 Some(BackupSecrets::MegolmBackupV1Curve25519AesSha2(
1093 MegolmBackupV1Curve25519AesSha2Secrets { key, backup_version },
1094 ))
1095 } else {
1096 return Err(SecretsBundleExportError::MissingBackupVersion);
1097 }
1098 } else {
1099 None
1100 };
1101
1102 Ok(SecretsBundle {
1103 cross_signing: CrossSigningSecrets { master_key, user_signing_key, self_signing_key },
1104 backup,
1105 })
1106 }
1107
1108 pub async fn import_secrets_bundle(
1121 &self,
1122 bundle: &SecretsBundle,
1123 ) -> Result<(), SecretImportError> {
1124 let mut changes = Changes::default();
1125
1126 if let Some(backup_bundle) = &bundle.backup {
1127 match backup_bundle {
1128 BackupSecrets::MegolmBackupV1Curve25519AesSha2(bundle) => {
1129 changes.backup_decryption_key = Some(bundle.key.clone());
1130 changes.backup_version = Some(bundle.backup_version.clone());
1131 }
1132 }
1133 }
1134
1135 let identity = self.inner.identity.lock().await;
1136
1137 identity
1138 .import_secrets_unchecked(
1139 Some(&bundle.cross_signing.master_key),
1140 Some(&bundle.cross_signing.self_signing_key),
1141 Some(&bundle.cross_signing.user_signing_key),
1142 )
1143 .await?;
1144
1145 let public_identity = identity.to_public_identity().await.expect(
1146 "We should be able to create a new public identity since we just imported \
1147 all the private cross-signing keys",
1148 );
1149
1150 changes.private_identity = Some(identity.clone());
1151 changes.identities.new.push(UserIdentityData::Own(public_identity));
1152
1153 Ok(self.save_changes(changes).await?)
1154 }
1155
1156 pub async fn import_secret(&self, secret: &GossippedSecret) -> Result<(), SecretImportError> {
1158 match &secret.secret_name {
1159 SecretName::CrossSigningMasterKey
1160 | SecretName::CrossSigningUserSigningKey
1161 | SecretName::CrossSigningSelfSigningKey => {
1162 if let Some(public_identity) =
1163 self.get_identity(self.user_id()).await?.and_then(|i| i.own())
1164 {
1165 let identity = self.inner.identity.lock().await;
1166
1167 identity
1168 .import_secret(
1169 public_identity,
1170 &secret.secret_name,
1171 &secret.event.content.secret,
1172 )
1173 .await?;
1174 info!(
1175 secret_name = ?secret.secret_name,
1176 "Successfully imported a private cross signing key"
1177 );
1178
1179 let changes =
1180 Changes { private_identity: Some(identity.clone()), ..Default::default() };
1181
1182 self.save_changes(changes).await?;
1183 }
1184 }
1185 SecretName::RecoveryKey => {
1186 }
1192 name => {
1193 warn!(secret = ?name, "Tried to import an unknown secret");
1194 }
1195 }
1196
1197 Ok(())
1198 }
1199
1200 pub async fn get_only_allow_trusted_devices(&self) -> Result<bool> {
1203 let value = self.get_value("only_allow_trusted_devices").await?.unwrap_or_default();
1204 Ok(value)
1205 }
1206
1207 pub async fn set_only_allow_trusted_devices(
1210 &self,
1211 block_untrusted_devices: bool,
1212 ) -> Result<()> {
1213 self.set_value("only_allow_trusted_devices", &block_untrusted_devices).await
1214 }
1215
1216 pub async fn get_value<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>> {
1218 let Some(value) = self.get_custom_value(key).await? else {
1219 return Ok(None);
1220 };
1221 let deserialized = self.deserialize_value(&value)?;
1222 Ok(Some(deserialized))
1223 }
1224
1225 pub async fn set_value(&self, key: &str, value: &impl Serialize) -> Result<()> {
1227 let serialized = self.serialize_value(value)?;
1228 self.set_custom_value(key, serialized).await?;
1229 Ok(())
1230 }
1231
1232 fn serialize_value(&self, value: &impl Serialize) -> Result<Vec<u8>> {
1233 let serialized =
1234 rmp_serde::to_vec_named(value).map_err(|x| CryptoStoreError::Backend(x.into()))?;
1235 Ok(serialized)
1236 }
1237
1238 fn deserialize_value<T: DeserializeOwned>(&self, value: &[u8]) -> Result<T> {
1239 let deserialized =
1240 rmp_serde::from_slice(value).map_err(|e| CryptoStoreError::Backend(e.into()))?;
1241 Ok(deserialized)
1242 }
1243
1244 pub fn room_keys_received_stream(
1256 &self,
1257 ) -> impl Stream<Item = Result<Vec<RoomKeyInfo>, BroadcastStreamRecvError>> + use<> {
1258 self.inner.store.room_keys_received_stream()
1259 }
1260
1261 pub fn room_keys_withheld_received_stream(
1270 &self,
1271 ) -> impl Stream<Item = Vec<RoomKeyWithheldInfo>> + use<> {
1272 self.inner.store.room_keys_withheld_received_stream()
1273 }
1274
1275 pub fn user_identities_stream(&self) -> impl Stream<Item = IdentityUpdates> + use<> {
1306 let verification_machine = self.inner.verification_machine.to_owned();
1307
1308 let this = self.clone();
1309 self.inner.store.identities_stream().map(move |(own_identity, identities, _)| {
1310 let (new_identities, changed_identities, unchanged_identities) = identities.into_maps();
1311
1312 let map_identity = |(user_id, identity)| {
1313 (
1314 user_id,
1315 UserIdentity::new(
1316 this.clone(),
1317 identity,
1318 verification_machine.to_owned(),
1319 own_identity.to_owned(),
1320 ),
1321 )
1322 };
1323
1324 let new = new_identities.into_iter().map(map_identity).collect();
1325 let changed = changed_identities.into_iter().map(map_identity).collect();
1326 let unchanged = unchanged_identities.into_iter().map(map_identity).collect();
1327
1328 IdentityUpdates { new, changed, unchanged }
1329 })
1330 }
1331
1332 pub fn devices_stream(&self) -> impl Stream<Item = DeviceUpdates> + use<> {
1364 let verification_machine = self.inner.verification_machine.to_owned();
1365
1366 self.inner.store.identities_stream().map(move |(own_identity, identities, devices)| {
1367 collect_device_updates(
1368 verification_machine.to_owned(),
1369 own_identity,
1370 identities,
1371 devices,
1372 )
1373 })
1374 }
1375
1376 pub fn identities_stream_raw(
1386 &self,
1387 ) -> impl Stream<Item = (IdentityChanges, DeviceChanges)> + use<> {
1388 self.inner.store.identities_stream().map(|(_, identities, devices)| (identities, devices))
1389 }
1390
1391 pub fn create_store_lock(
1394 &self,
1395 lock_key: String,
1396 config: CrossProcessLockConfig,
1397 ) -> CrossProcessLock<LockableCryptoStore> {
1398 self.inner.store.create_store_lock(lock_key, config)
1399 }
1400
1401 pub fn secrets_stream(&self) -> impl Stream<Item = SecretsInboxItem> + use<> {
1441 self.inner.store.secrets_stream()
1442 }
1443
1444 pub fn historic_room_key_stream(&self) -> impl Stream<Item = RoomKeyBundleInfo> + use<> {
1484 self.inner.store.historic_room_key_stream()
1485 }
1486
1487 pub async fn import_room_keys(
1500 &self,
1501 exported_keys: Vec<ExportedRoomKey>,
1502 from_backup_version: Option<&str>,
1503 progress_listener: impl Fn(usize, usize),
1504 ) -> Result<RoomKeyImportResult> {
1505 let exported_keys = exported_keys.iter().filter_map(|key| {
1506 key.try_into()
1507 .map_err(|e| {
1508 warn!(
1509 sender_key = key.sender_key().to_base64(),
1510 room_id = ?key.room_id(),
1511 session_id = key.session_id(),
1512 error = ?e,
1513 "Couldn't import a room key from a file export."
1514 );
1515 })
1516 .ok()
1517 });
1518 self.import_sessions_impl(exported_keys, from_backup_version, progress_listener).await
1519 }
1520
1521 pub async fn import_exported_room_keys(
1548 &self,
1549 exported_keys: Vec<ExportedRoomKey>,
1550 progress_listener: impl Fn(usize, usize),
1551 ) -> Result<RoomKeyImportResult> {
1552 self.import_room_keys(exported_keys, None, progress_listener).await
1553 }
1554
1555 async fn import_sessions_impl(
1556 &self,
1557 sessions: impl Iterator<Item = InboundGroupSession>,
1558 from_backup_version: Option<&str>,
1559 progress_listener: impl Fn(usize, usize),
1560 ) -> Result<RoomKeyImportResult> {
1561 let sessions: Vec<_> = sessions.collect();
1562 let mut imported_sessions = Vec::new();
1563
1564 let total_count = sessions.len();
1565 let mut keys = BTreeMap::new();
1566
1567 for (i, session) in sessions.into_iter().enumerate() {
1568 if let Some(merged) = self.merge_received_group_session(session).await? {
1571 if from_backup_version.is_some() {
1572 merged.mark_as_backed_up();
1573 }
1574
1575 keys.entry(merged.room_id().to_owned())
1576 .or_insert_with(BTreeMap::new)
1577 .entry(merged.sender_key().to_base64())
1578 .or_insert_with(BTreeSet::new)
1579 .insert(merged.session_id().to_owned());
1580
1581 imported_sessions.push(merged);
1582 }
1583
1584 progress_listener(i, total_count);
1585 }
1586
1587 let imported_count = imported_sessions.len();
1588
1589 self.inner
1590 .store
1591 .save_inbound_group_sessions(imported_sessions, from_backup_version)
1592 .await?;
1593
1594 info!(total_count, imported_count, room_keys = ?keys, "Successfully imported room keys");
1595
1596 Ok(RoomKeyImportResult::new(imported_count, total_count, keys))
1597 }
1598
1599 pub(crate) fn crypto_store(&self) -> Arc<CryptoStoreWrapper> {
1600 self.inner.store.clone()
1601 }
1602
1603 pub async fn export_room_keys(
1626 &self,
1627 predicate: impl FnMut(&InboundGroupSession) -> bool,
1628 ) -> Result<Vec<ExportedRoomKey>> {
1629 let mut exported = Vec::new();
1630
1631 let mut sessions = self.get_inbound_group_sessions().await?;
1632 sessions.retain(predicate);
1633
1634 for session in sessions {
1635 let export = session.export().await;
1636 exported.push(export);
1637 }
1638
1639 Ok(exported)
1640 }
1641
1642 pub async fn export_room_keys_stream(
1677 &self,
1678 predicate: impl FnMut(&InboundGroupSession) -> bool,
1679 ) -> Result<impl Stream<Item = ExportedRoomKey>> {
1680 let sessions = self.get_inbound_group_sessions().await?;
1682 Ok(futures_util::stream::iter(sessions.into_iter().filter(predicate))
1683 .then(|session| async move { session.export().await }))
1684 }
1685
1686 pub async fn build_room_key_bundle(
1691 &self,
1692 room_id: &RoomId,
1693 ) -> std::result::Result<RoomKeyBundle, CryptoStoreError> {
1694 let sessions = self.get_inbound_group_sessions_by_room_id(room_id).await?;
1695
1696 let mut bundle = RoomKeyBundle::default();
1697 for session in sessions {
1698 if session.shared_history() {
1699 bundle.room_keys.push(session.export().await.into());
1700 } else {
1701 bundle.withheld.push(RoomKeyWithheldContent::new(
1702 session.algorithm().to_owned(),
1703 WithheldCode::HistoryNotShared,
1704 session.room_id().to_owned(),
1705 session.session_id().to_owned(),
1706 session.sender_key().to_owned(),
1707 self.device_id().to_owned(),
1708 ));
1709 }
1710 }
1711
1712 let withhelds = self.get_withheld_sessions_by_room_id(room_id).await?;
1715 for withheld in withhelds {
1716 if withheld.content.withheld_code() == WithheldCode::HistoryNotShared {
1717 bundle.withheld.push(withheld.content);
1718 }
1719 }
1720
1721 Ok(bundle)
1722 }
1723
1724 #[instrument(skip(self, bundle, progress_listener), fields(bundle_size = bundle.room_keys.len(), sender_data))]
1734 pub async fn receive_room_key_bundle(
1735 &self,
1736 bundle_info: &StoredRoomKeyBundleData,
1737 bundle: RoomKeyBundle,
1738 progress_listener: impl Fn(usize, usize),
1739 ) -> Result<(), CryptoStoreError> {
1740 let sender_data = if bundle_info.sender_data.should_recalculate() {
1741 let device = self
1742 .get_device_from_curve_key(&bundle_info.sender_user, bundle_info.sender_key)
1743 .await?;
1744
1745 device
1746 .as_ref()
1747 .map(SenderData::from_device)
1748 .unwrap_or_else(|| bundle_info.sender_data.clone())
1749 } else {
1750 bundle_info.sender_data.clone()
1751 };
1752
1753 tracing::Span::current().record("sender_data", tracing::field::debug(&sender_data));
1754
1755 let Ok(forwarder_data) = (&sender_data).try_into() else {
1759 warn!(
1760 "Not accepting a historic room key bundle due to insufficient trust in the sender"
1761 );
1762 return Ok(());
1763 };
1764
1765 self.import_room_key_bundle_sessions(
1766 bundle_info,
1767 &bundle,
1768 &forwarder_data,
1769 progress_listener,
1770 )
1771 .await?;
1772 self.import_room_key_bundle_withheld_info(bundle_info, &bundle).await?;
1773
1774 Ok(())
1775 }
1776
1777 async fn import_room_key_bundle_sessions(
1778 &self,
1779 bundle_info: &StoredRoomKeyBundleData,
1780 bundle: &RoomKeyBundle,
1781 forwarder_data: &ForwarderData,
1782 progress_listener: impl Fn(usize, usize),
1783 ) -> Result<(), CryptoStoreError> {
1784 let (good, bad): (Vec<_>, Vec<_>) = bundle.room_keys.iter().partition_map(|key| {
1785 if key.room_id != bundle_info.bundle_data.room_id {
1786 trace!("Ignoring key for incorrect room {} in bundle", key.room_id);
1787 Either::Right(key)
1788 } else {
1789 Either::Left(key)
1790 }
1791 });
1792
1793 match (bad.is_empty(), good.is_empty()) {
1794 (true, true) => {
1796 warn!("Received a completely empty room key bundle");
1797 }
1798
1799 (false, true) => {
1801 let bad_keys: Vec<_> =
1802 bad.iter().map(|&key| (&key.room_id, &key.session_id)).collect();
1803
1804 warn!(
1805 ?bad_keys,
1806 "Received a room key bundle for the wrong room, ignoring all room keys from the bundle"
1807 );
1808 }
1809
1810 (_, false) => {
1812 if !bad.is_empty() {
1815 warn!(
1816 bad_key_count = bad.len(),
1817 "The room key bundle contained some room keys \
1818 that were meant for a different room"
1819 );
1820 }
1821
1822 let keys = good.iter().filter_map(|key| {
1823 key.try_into_inbound_group_session(forwarder_data)
1824 .map_err(|e| {
1825 warn!(
1826 sender_key = ?key.sender_key().to_base64(),
1827 room_id = ?key.room_id(),
1828 session_id = key.session_id(),
1829 error = ?e,
1830 "Couldn't import a room key from a key bundle."
1831 );
1832 })
1833 .ok()
1834 });
1835
1836 self.import_sessions_impl(keys, None, progress_listener).await?;
1837 }
1838 }
1839
1840 Ok(())
1841 }
1842
1843 async fn import_room_key_bundle_withheld_info(
1844 &self,
1845 bundle_info: &StoredRoomKeyBundleData,
1846 bundle: &RoomKeyBundle,
1847 ) -> Result<(), CryptoStoreError> {
1848 let mut session_id_to_withheld_code_map = BTreeMap::new();
1849
1850 let mut changes = Changes::default();
1851 for withheld in &bundle.withheld {
1852 let (room_id, session_id) = match withheld {
1853 RoomKeyWithheldContent::MegolmV1AesSha2(c) => match (c.room_id(), c.session_id()) {
1854 (Some(room_id), Some(session_id)) => (room_id, session_id),
1855 _ => continue,
1856 },
1857 #[cfg(feature = "experimental-algorithms")]
1858 RoomKeyWithheldContent::MegolmV2AesSha2(c) => match (c.room_id(), c.session_id()) {
1859 (Some(room_id), Some(session_id)) => (room_id, session_id),
1860 _ => continue,
1861 },
1862 RoomKeyWithheldContent::Unknown(_) => continue,
1863 };
1864
1865 if room_id != bundle_info.bundle_data.room_id {
1866 trace!("Ignoring withheld info for incorrect room {} in bundle", room_id);
1867 continue;
1868 }
1869
1870 changes.withheld_session_info.entry(room_id.to_owned()).or_default().insert(
1871 session_id.to_owned(),
1872 RoomKeyWithheldEntry {
1873 sender: bundle_info.sender_user.clone(),
1874 content: withheld.to_owned(),
1875 },
1876 );
1877 session_id_to_withheld_code_map.insert(session_id, withheld.withheld_code());
1878 }
1879
1880 self.save_changes(changes).await?;
1881
1882 info!(
1883 room_id = ?bundle_info.bundle_data.room_id,
1884 ?session_id_to_withheld_code_map,
1885 "Successfully imported withheld info from room key bundle",
1886 );
1887
1888 Ok(())
1889 }
1890
1891 pub async fn store_room_pending_key_bundle(
1897 &self,
1898 room_id: &RoomId,
1899 inviter: &UserId,
1900 ) -> Result<(), CryptoStoreError> {
1901 let invite_accepted_at = MilliSecondsSinceUnixEpoch::now();
1902 self.save_changes(Changes {
1903 rooms_pending_key_bundle: HashMap::from([(
1904 room_id.to_owned(),
1905 Some(RoomPendingKeyBundleDetails {
1906 room_id: room_id.to_owned(),
1907 invite_accepted_at,
1908 inviter: inviter.to_owned(),
1909 }),
1910 )]),
1911 ..Default::default()
1912 })
1913 .await?;
1914
1915 Ok(())
1916 }
1917
1918 pub async fn clear_room_pending_key_bundle(&self, room_id: &RoomId) -> Result<()> {
1922 self.save_changes(Changes {
1923 rooms_pending_key_bundle: HashMap::from([(room_id.to_owned(), None)]),
1924 ..Default::default()
1925 })
1926 .await
1927 }
1928}
1929
1930impl Deref for Store {
1931 type Target = DynCryptoStore;
1932
1933 fn deref(&self) -> &Self::Target {
1934 self.inner.store.deref().deref()
1935 }
1936}
1937
1938#[derive(Clone, Debug)]
1940pub struct LockableCryptoStore(Arc<dyn CryptoStore<Error = CryptoStoreError>>);
1941
1942impl matrix_sdk_common::cross_process_lock::TryLock for LockableCryptoStore {
1943 type LockError = CryptoStoreError;
1944
1945 async fn try_lock(
1946 &self,
1947 lease_duration_ms: u32,
1948 key: &str,
1949 holder: &str,
1950 ) -> std::result::Result<Option<CrossProcessLockGeneration>, Self::LockError> {
1951 self.0.try_take_leased_lock(lease_duration_ms, key, holder).await
1952 }
1953}
1954
1955#[cfg(test)]
1956mod tests {
1957 use std::{collections::BTreeMap, pin::pin};
1958
1959 use assert_matches2::{assert_let, assert_matches};
1960 use futures_util::StreamExt;
1961 use insta::{_macro_support::Content, assert_json_snapshot, internals::ContentPath};
1962 use matrix_sdk_test::async_test;
1963 use rand::RngExt;
1964 use ruma::{
1965 RoomId, device_id,
1966 events::room::{EncryptedFile, EncryptedFileHashes, V2EncryptedFileInfo},
1967 owned_device_id, owned_mxc_uri, room_id, user_id,
1968 };
1969 use serde_json::json;
1970 use vodozemac::{Ed25519Keypair, megolm::SessionKey};
1971
1972 use crate::{
1973 Account, OlmMachine,
1974 machine::test_helpers::get_machine_pair,
1975 olm::{InboundGroupSession, SenderData},
1976 store::types::{DehydratedDeviceKey, RoomKeyWithheldEntry, StoredRoomKeyBundleData},
1977 types::{
1978 EventEncryptionAlgorithm,
1979 events::{
1980 room_key_bundle::RoomKeyBundleContent,
1981 room_key_withheld::{MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent},
1982 },
1983 },
1984 };
1985
1986 #[async_test]
1987 async fn test_merge_received_group_session() {
1988 let alice_account = Account::with_device_id(user_id!("@a:s.co"), device_id!("ABC"));
1989 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("DEF")).await;
1990
1991 let room_id = room_id!("!test:localhost");
1992
1993 let megolm_signing_key = Ed25519Keypair::new();
1994 let inbound = make_inbound_group_session(&alice_account, &megolm_signing_key, room_id);
1995
1996 let mut inbound_at_index_5 =
1998 InboundGroupSession::from_export(&inbound.export_at_index(5).await).unwrap();
1999 inbound_at_index_5.sender_data = inbound.sender_data.clone();
2000 bob.store().save_inbound_group_sessions(&[inbound_at_index_5.clone()]).await.unwrap();
2001
2002 let disconnected = make_inbound_group_session(&alice_account, &megolm_signing_key, room_id);
2004 assert_eq!(bob.store().merge_received_group_session(disconnected).await.unwrap(), None);
2005
2006 let mut worse =
2008 InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
2009 worse.sender_data = inbound.sender_data.clone();
2010 assert_eq!(bob.store().merge_received_group_session(worse).await.unwrap(), None);
2011
2012 let mut copy = InboundGroupSession::from_pickle(inbound_at_index_5.pickle().await).unwrap();
2014 copy.sender_data = inbound.sender_data.clone();
2015 assert_eq!(bob.store().merge_received_group_session(copy).await.unwrap(), None);
2016
2017 let mut better =
2019 InboundGroupSession::from_export(&inbound.export_at_index(0).await).unwrap();
2020 better.sender_data = inbound.sender_data.clone();
2021 assert_let!(Some(update) = bob.store().merge_received_group_session(better).await.unwrap());
2022 assert_eq!(update.first_known_index(), 0);
2023
2024 {
2026 let mut worse_ratchet_better_trust =
2027 InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
2028 let updated_sender_data = SenderData::sender_verified(
2029 alice_account.user_id(),
2030 alice_account.device_id(),
2031 Ed25519Keypair::new().public_key(),
2032 );
2033 worse_ratchet_better_trust.sender_data = updated_sender_data.clone();
2034 assert_let!(
2035 Some(update) = bob
2036 .store()
2037 .merge_received_group_session(worse_ratchet_better_trust)
2038 .await
2039 .unwrap()
2040 );
2041 assert_eq!(update.sender_data, updated_sender_data);
2042 assert_eq!(update.first_known_index(), 5);
2043 assert_eq!(
2044 update.export_at_index(0).await.session_key.to_bytes(),
2045 inbound.export_at_index(5).await.session_key.to_bytes()
2046 );
2047 }
2048
2049 {
2051 let mut better_ratchet_worse_trust =
2052 InboundGroupSession::from_export(&inbound.export_at_index(0).await).unwrap();
2053 let updated_sender_data = SenderData::unknown();
2054 better_ratchet_worse_trust.sender_data = updated_sender_data.clone();
2055 assert_let!(
2056 Some(update) = bob
2057 .store()
2058 .merge_received_group_session(better_ratchet_worse_trust)
2059 .await
2060 .unwrap()
2061 );
2062 assert_eq!(update.sender_data, inbound.sender_data);
2063 assert_eq!(update.first_known_index(), 0);
2064 assert_eq!(
2065 update.export_at_index(0).await.session_key.to_bytes(),
2066 inbound.export_at_index(0).await.session_key.to_bytes()
2067 );
2068 }
2069 }
2070
2071 fn make_inbound_group_session(
2074 sender_account: &Account,
2075 signing_key: &Ed25519Keypair,
2076 room_id: &RoomId,
2077 ) -> InboundGroupSession {
2078 InboundGroupSession::new(
2079 sender_account.identity_keys.curve25519,
2080 sender_account.identity_keys.ed25519,
2081 room_id,
2082 &make_session_key(signing_key),
2083 SenderData::device_info(crate::types::DeviceKeys::new(
2084 sender_account.user_id().to_owned(),
2085 sender_account.device_id().to_owned(),
2086 vec![],
2087 BTreeMap::new(),
2088 crate::types::Signatures::new(),
2089 )),
2090 None,
2091 EventEncryptionAlgorithm::MegolmV1AesSha2,
2092 Some(ruma::events::room::history_visibility::HistoryVisibility::Shared),
2093 true,
2094 )
2095 .unwrap()
2096 }
2097
2098 fn make_session_key(signing_key: &Ed25519Keypair) -> SessionKey {
2101 let mut session_key_bytes = vec![0u8; 229];
2106 session_key_bytes[0] = 2;
2108 rand::rng().fill(&mut session_key_bytes[5..133]);
2111 session_key_bytes[133..165].copy_from_slice(signing_key.public_key().as_bytes());
2113 let sig = signing_key.sign(&session_key_bytes[0..165]);
2115 session_key_bytes[165..229].copy_from_slice(&sig.to_bytes());
2116
2117 SessionKey::from_bytes(&session_key_bytes).unwrap()
2118 }
2119
2120 #[async_test]
2121 async fn test_import_room_keys_notifies_stream() {
2122 use futures_util::FutureExt;
2123
2124 let (alice, bob, _) =
2125 get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2126
2127 let room1_id = room_id!("!room1:localhost");
2128 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2129 let exported_sessions = alice.store().export_room_keys(|_| true).await.unwrap();
2130
2131 let mut room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream());
2132 bob.store().import_room_keys(exported_sessions, None, |_, _| {}).await.unwrap();
2133
2134 let room_keys = room_keys_received_stream
2135 .next()
2136 .now_or_never()
2137 .flatten()
2138 .expect("We should have received an update of room key infos")
2139 .unwrap();
2140 assert_eq!(room_keys.len(), 1);
2141 assert_eq!(room_keys[0].room_id, "!room1:localhost");
2142 }
2143
2144 #[async_test]
2145 async fn test_export_room_keys_provides_selected_keys() {
2146 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2148 let room1_id = room_id!("!room1:localhost");
2149 let room2_id = room_id!("!room2:localhost");
2150 let room3_id = room_id!("!room3:localhost");
2151 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2152 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2153 alice.create_outbound_group_session_with_defaults_test_helper(room3_id).await.unwrap();
2154
2155 let keys = alice
2157 .store()
2158 .export_room_keys(|s| s.room_id() == room2_id || s.room_id() == room3_id)
2159 .await
2160 .unwrap();
2161
2162 assert_eq!(keys.len(), 2);
2164 assert_eq!(keys[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2165 assert_eq!(keys[1].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2166 assert_eq!(keys[0].room_id, "!room2:localhost");
2167 assert_eq!(keys[1].room_id, "!room3:localhost");
2168 assert_eq!(keys[0].session_key.to_base64().len(), 220);
2169 assert_eq!(keys[1].session_key.to_base64().len(), 220);
2170 }
2171
2172 #[async_test]
2173 async fn test_export_room_keys_stream_can_provide_all_keys() {
2174 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2176 let room1_id = room_id!("!room1:localhost");
2177 let room2_id = room_id!("!room2:localhost");
2178 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2179 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2180
2181 let mut keys = pin!(alice.store().export_room_keys_stream(|_| true).await.unwrap());
2183
2184 let mut collected = vec![];
2186 while let Some(key) = keys.next().await {
2187 collected.push(key);
2188 }
2189
2190 assert_eq!(collected.len(), 2);
2192 assert_eq!(collected[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2193 assert_eq!(collected[1].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2194 assert_eq!(collected[0].room_id, "!room1:localhost");
2195 assert_eq!(collected[1].room_id, "!room2:localhost");
2196 assert_eq!(collected[0].session_key.to_base64().len(), 220);
2197 assert_eq!(collected[1].session_key.to_base64().len(), 220);
2198 }
2199
2200 #[async_test]
2201 async fn test_export_room_keys_stream_can_provide_a_subset_of_keys() {
2202 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2204 let room1_id = room_id!("!room1:localhost");
2205 let room2_id = room_id!("!room2:localhost");
2206 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2207 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2208
2209 let mut keys =
2211 pin!(alice.store().export_room_keys_stream(|s| s.room_id() == room1_id).await.unwrap());
2212
2213 let mut collected = vec![];
2215 while let Some(key) = keys.next().await {
2216 collected.push(key);
2217 }
2218
2219 assert_eq!(collected.len(), 1);
2221 assert_eq!(collected[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2222 assert_eq!(collected[0].room_id, "!room1:localhost");
2223 assert_eq!(collected[0].session_key.to_base64().len(), 220);
2224 }
2225
2226 #[async_test]
2227 async fn test_export_secrets_bundle() {
2228 let user_id = user_id!("@alice:example.com");
2229 let (first, second, _) = get_machine_pair(user_id, user_id, false).await;
2230
2231 let _ = first
2232 .bootstrap_cross_signing(false)
2233 .await
2234 .expect("We should be able to bootstrap cross-signing");
2235
2236 let bundle = first.store().export_secrets_bundle().await.expect(
2237 "We should be able to export the secrets bundle, now that we \
2238 have the cross-signing keys",
2239 );
2240
2241 assert!(bundle.backup.is_none(), "The bundle should not contain a backup key");
2242
2243 second
2244 .store()
2245 .import_secrets_bundle(&bundle)
2246 .await
2247 .expect("We should be able to import the secrets bundle");
2248
2249 let status = second.cross_signing_status().await;
2250 let identity = second.get_identity(user_id, None).await.unwrap().unwrap().own().unwrap();
2251
2252 assert!(identity.is_verified(), "The public identity should be marked as verified.");
2253
2254 assert!(status.is_complete(), "We should have imported all the cross-signing keys");
2255 }
2256
2257 #[async_test]
2258 async fn test_create_dehydrated_device_key() {
2259 let pickle_key = DehydratedDeviceKey::new();
2260
2261 let to_vec = pickle_key.inner.to_vec();
2262 let pickle_key_from_slice = DehydratedDeviceKey::from_slice(to_vec.as_slice())
2263 .expect("Should be able to create a dehydrated device key from slice");
2264
2265 assert_eq!(pickle_key_from_slice.to_base64(), pickle_key.to_base64());
2266 }
2267
2268 #[async_test]
2269 async fn test_create_dehydrated_errors() {
2270 let too_small = [0u8; 22];
2271 let pickle_key = DehydratedDeviceKey::from_slice(&too_small);
2272
2273 assert!(pickle_key.is_err());
2274
2275 let too_big = [0u8; 40];
2276 let pickle_key = DehydratedDeviceKey::from_slice(&too_big);
2277
2278 assert!(pickle_key.is_err());
2279 }
2280
2281 #[async_test]
2282 async fn test_build_room_key_bundle() {
2283 let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2286 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2287
2288 let room1_id = room_id!("!room1:localhost");
2289 let room2_id = room_id!("!room2:localhost");
2290
2291 let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2296 let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2297 let session_key3 = "AgAAAAAM9KFsliaUUhGSXgwOzM5UemjkNH4n8NHgvC/y8hhw13zTF+ooGD4uIYEXYX630oNvQm/EvgZo+dkoc0re+vsqsx4sQeNODdSjcBsWOa0oDF+irQn9oYoLUDPI1IBtY1rX+FV99Zm/xnG7uFOX7aTVlko2GSdejy1w9mfobmfxu5aUc04A9zaKJP1pOthZvRAlhpymGYHgsDtWPrrjyc/yypMflE4kIUEEEtu1kT6mrAmcl615XYRAHYK9G2+fZsGvokwzbkl4nulGwcZMpQEoM0nD2o3GWgX81HW3nGfKBg";
2298 let session_key4 = "AgAAAAA4Kkesxq2h4v9PLD6Sm3Smxspz1PXTqytQPCMQMkkrHNmzV2bHlJ+6/Al9cu8vh1Oj69AK0WUAeJOJuaiskEeg/PI3P03+UYLeC379RzgqwSHdBgdQ41G2vD6zpgmE/8vYToe+qpCZACtPOswZxyqxHH+T/Iq0nv13JmlFGIeA6fEPfr5Y28B49viG74Fs9rxV9EH5PfjbuPM/p+Sz5obShuaBPKQBX1jT913nEXPoIJ06exNZGr0285nw/LgVvNlmWmbqNnbzO2cNZjQWA+xZYz5FSfyCxwqEBbEdUCuRCQ";
2299
2300 let sessions = [
2301 create_inbound_group_session_with_visibility(
2302 &alice,
2303 room1_id,
2304 &SessionKey::from_base64(session_key1).unwrap(),
2305 true,
2306 ),
2307 create_inbound_group_session_with_visibility(
2308 &alice,
2309 room1_id,
2310 &SessionKey::from_base64(session_key2).unwrap(),
2311 true,
2312 ),
2313 create_inbound_group_session_with_visibility(
2314 &alice,
2315 room1_id,
2316 &SessionKey::from_base64(session_key3).unwrap(),
2317 false,
2318 ),
2319 create_inbound_group_session_with_visibility(
2320 &alice,
2321 room2_id,
2322 &SessionKey::from_base64(session_key4).unwrap(),
2323 true,
2324 ),
2325 ];
2326 bob.store().save_inbound_group_sessions(&sessions).await.unwrap();
2327
2328 let mut bundle = bob.store().build_room_key_bundle(room1_id).await.unwrap();
2330
2331 bundle.room_keys.sort_by_key(|session| session.session_id.clone());
2335
2336 let algorithm = if cfg!(feature = "experimental-algorithms") {
2338 "m.megolm.v2.aes-sha2"
2339 } else {
2340 "m.megolm.v1.aes-sha2"
2341 };
2342 let map_algorithm = move |value: Content, _path: ContentPath<'_>| {
2343 assert_eq!(value.as_str().unwrap(), algorithm);
2344 "[algorithm]"
2345 };
2346
2347 let alice_curve_key = alice.identity_keys().curve25519.to_base64();
2349 let map_alice_curve_key = move |value: Content, _path: ContentPath<'_>| {
2350 assert_eq!(value.as_str().unwrap(), alice_curve_key);
2351 "[alice curve key]"
2352 };
2353 let alice_ed25519_key = alice.identity_keys().ed25519.to_base64();
2354 let map_alice_ed25519_key = move |value: Content, _path: ContentPath<'_>| {
2355 assert_eq!(value.as_str().unwrap(), alice_ed25519_key);
2356 "[alice ed25519 key]"
2357 };
2358
2359 insta::with_settings!({ sort_maps => true }, {
2360 assert_json_snapshot!(bundle, {
2361 ".withheld[].algorithm" => insta::dynamic_redaction(map_algorithm),
2362 ".room_keys[].algorithm" => insta::dynamic_redaction(map_algorithm),
2363 ".room_keys[].sender_key" => insta::dynamic_redaction(map_alice_curve_key.clone()),
2364 ".withheld[].sender_key" => insta::dynamic_redaction(map_alice_curve_key),
2365 ".room_keys[].sender_claimed_keys.ed25519" => insta::dynamic_redaction(map_alice_ed25519_key),
2366 });
2367 });
2368 }
2369
2370 #[async_test]
2371 async fn test_receive_room_key_bundle() {
2372 let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2373 let alice_key = alice.identity_keys().curve25519;
2374 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2375
2376 let room_id = room_id!("!room1:localhost");
2377
2378 let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2379 let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2380
2381 let sessions = [
2382 create_inbound_group_session_with_visibility(
2383 &alice,
2384 room_id,
2385 &SessionKey::from_base64(session_key1).unwrap(),
2386 true,
2387 ),
2388 create_inbound_group_session_with_visibility(
2389 &alice,
2390 room_id,
2391 &SessionKey::from_base64(session_key2).unwrap(),
2392 false,
2393 ),
2394 ];
2395
2396 alice.store().save_inbound_group_sessions(&sessions).await.unwrap();
2397 let bundle = alice.store().build_room_key_bundle(room_id).await.unwrap();
2398
2399 bob.store()
2400 .receive_room_key_bundle(
2401 &StoredRoomKeyBundleData {
2402 sender_user: alice.user_id().to_owned(),
2403 sender_key: alice_key,
2404 sender_data: SenderData::sender_verified(
2405 alice.user_id(),
2406 device_id!("ALICE"),
2407 alice.identity_keys().ed25519,
2408 ),
2409
2410 bundle_data: RoomKeyBundleContent {
2411 room_id: room_id.to_owned(),
2412 file: EncryptedFile::new(
2415 owned_mxc_uri!("mxc://example.com/0"),
2416 V2EncryptedFileInfo::encode([0; 32], [0; 16]).into(),
2417 EncryptedFileHashes::with_sha256([0; 32]),
2418 ),
2419 },
2420 },
2421 bundle,
2422 |_, _| {},
2423 )
2424 .await
2425 .unwrap();
2426
2427 let imported_sessions =
2429 bob.store().get_inbound_group_sessions_by_room_id(room_id).await.unwrap();
2430
2431 assert_eq!(imported_sessions.len(), 1);
2432 assert_eq!(imported_sessions[0].room_id(), room_id);
2433
2434 assert_eq!(
2436 imported_sessions[0]
2437 .forwarder_data
2438 .as_ref()
2439 .expect("Session should contain forwarder data.")
2440 .user_id(),
2441 alice.user_id()
2442 );
2443
2444 assert_matches!(
2445 bob.store()
2446 .get_withheld_info(room_id, sessions[1].session_id())
2447 .await
2448 .unwrap()
2449 .expect("Withheld info should be present in the store."),
2450 RoomKeyWithheldEntry {
2451 #[cfg(not(feature = "experimental-algorithms"))]
2452 content: RoomKeyWithheldContent::MegolmV1AesSha2(
2453 MegolmV1AesSha2WithheldContent::HistoryNotShared(_)
2454 ),
2455 #[cfg(feature = "experimental-algorithms")]
2456 content: RoomKeyWithheldContent::MegolmV2AesSha2(
2457 MegolmV1AesSha2WithheldContent::HistoryNotShared(_)
2458 ),
2459 ..
2460 }
2461 );
2462 }
2463
2464 #[async_test]
2470 async fn test_deserialize_room_key_withheld_entry_from_to_device_event() {
2471 let entry: RoomKeyWithheldEntry = serde_json::from_value(json!(
2472 {
2473 "content": {
2474 "algorithm": "m.megolm.v1.aes-sha2",
2475 "code": "m.unauthorised",
2476 "from_device": "ALICE",
2477 "reason": "You are not authorised to read the message.",
2478 "room_id": "!roomid:s.co",
2479 "sender_key": "7hIcOrEroXYdzjtCBvBjUiqvT0Me7g+ymeXqoc65RS0",
2480 "session_id": "session123"
2481 },
2482 "sender": "@alice:s.co",
2483 "type": "m.room_key.withheld"
2484 }
2485 ))
2486 .unwrap();
2487
2488 assert_matches!(
2489 entry,
2490 RoomKeyWithheldEntry {
2491 sender,
2492 content: RoomKeyWithheldContent::MegolmV1AesSha2(
2493 MegolmV1AesSha2WithheldContent::Unauthorised(withheld_content,)
2494 ),
2495 }
2496 );
2497
2498 assert_eq!(sender, "@alice:s.co");
2499 assert_eq!(withheld_content.room_id, "!roomid:s.co");
2500 assert_eq!(withheld_content.session_id, "session123");
2501 assert_eq!(
2502 withheld_content.sender_key.to_base64(),
2503 "7hIcOrEroXYdzjtCBvBjUiqvT0Me7g+ymeXqoc65RS0"
2504 );
2505 assert_eq!(withheld_content.from_device, Some(owned_device_id!("ALICE")));
2506 }
2507
2508 fn create_inbound_group_session_with_visibility(
2519 olm_machine: &OlmMachine,
2520 room_id: &RoomId,
2521 session_key: &SessionKey,
2522 shared_history: bool,
2523 ) -> InboundGroupSession {
2524 let identity_keys = &olm_machine.store().static_account().identity_keys;
2525 InboundGroupSession::new(
2526 identity_keys.curve25519,
2527 identity_keys.ed25519,
2528 room_id,
2529 session_key,
2530 SenderData::unknown(),
2531 None,
2532 #[cfg(not(feature = "experimental-algorithms"))]
2533 EventEncryptionAlgorithm::MegolmV1AesSha2,
2534 #[cfg(feature = "experimental-algorithms")]
2535 EventEncryptionAlgorithm::MegolmV2AesSha2,
2536 None,
2537 shared_history,
2538 )
2539 .unwrap()
2540 }
2541}