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?;
212 drop(cache);
213 }
214
215 let mut users_for_key_query = self.users_for_key_query.lock().await;
216 let Some(waiter) = users_for_key_query.maybe_register_waiting_task(user) else {
217 return Ok(UserKeyQueryResult::WasNotPending);
218 };
219
220 let wait_for_completion = async {
221 while !waiter.completed.load(Ordering::Relaxed) {
222 let mut notified = pin!(self.users_for_key_query_notify.notified());
226 notified.as_mut().enable();
227 drop(users_for_key_query);
228
229 notified.await;
231
232 users_for_key_query = self.users_for_key_query.lock().await;
236 }
237 };
238
239 match timeout(Box::pin(wait_for_completion), timeout_duration).await {
240 Err(_) => {
241 warn!(
242 user_id = ?user,
243 "The user has a pending `/keys/query` request which did \
244 not finish yet, some devices might be missing."
245 );
246
247 Ok(UserKeyQueryResult::TimeoutExpired)
248 }
249 _ => Ok(UserKeyQueryResult::WasPending),
250 }
251 }
252}
253
254pub(crate) struct SyncedKeyQueryManager<'a> {
255 cache: &'a StoreCache,
256 manager: &'a KeyQueryManager,
257}
258
259impl SyncedKeyQueryManager<'_> {
260 pub async fn update_tracked_users(&self, users: impl Iterator<Item = &UserId>) -> Result<()> {
265 let mut store_updates = Vec::new();
266 let mut key_query_lock = self.manager.users_for_key_query.lock().await;
267
268 {
269 let mut tracked_users = self.cache.tracked_users.write();
270 for user_id in users {
271 if tracked_users.insert(user_id.to_owned()) {
272 key_query_lock.insert_user(user_id);
273 store_updates.push((user_id, true))
274 }
275 }
276 }
277
278 self.cache.store.save_tracked_users(&store_updates).await
279 }
280
281 pub async fn mark_tracked_users_as_changed(
288 &self,
289 users: impl Iterator<Item = &UserId>,
290 ) -> Result<()> {
291 let mut store_updates: Vec<(&UserId, bool)> = Vec::new();
292 let mut key_query_lock = self.manager.users_for_key_query.lock().await;
293
294 {
295 let tracked_users = &self.cache.tracked_users.read();
296 for user_id in users {
297 if tracked_users.contains(user_id) {
298 key_query_lock.insert_user(user_id);
299 store_updates.push((user_id, true));
300 }
301 }
302 }
303
304 self.cache.store.save_tracked_users(&store_updates).await
305 }
306
307 pub async fn mark_tracked_users_as_up_to_date(
313 &self,
314 users: impl Iterator<Item = &UserId>,
315 sequence_number: SequenceNumber,
316 ) -> Result<()> {
317 let mut store_updates: Vec<(&UserId, bool)> = Vec::new();
318 let mut key_query_lock = self.manager.users_for_key_query.lock().await;
319
320 {
321 let tracked_users = self.cache.tracked_users.read();
322 for user_id in users {
323 if tracked_users.contains(user_id) {
324 let clean = key_query_lock.maybe_remove_user(user_id, sequence_number);
325 store_updates.push((user_id, !clean));
326 }
327 }
328 }
329
330 self.cache.store.save_tracked_users(&store_updates).await?;
331 self.manager.users_for_key_query_notify.notify_waiters();
333
334 Ok(())
335 }
336
337 pub async fn users_for_key_query(&self) -> (HashSet<OwnedUserId>, SequenceNumber) {
349 self.manager.users_for_key_query.lock().await.users_for_key_query()
350 }
351
352 pub fn tracked_users(&self) -> HashSet<OwnedUserId> {
354 self.cache.tracked_users.read().iter().cloned().collect()
355 }
356
357 pub async fn mark_user_as_changed(&self, user: &UserId) -> Result<()> {
363 self.manager.users_for_key_query.lock().await.insert_user(user);
364 self.cache.tracked_users.write().insert(user.to_owned());
365
366 self.cache.store.save_tracked_users(&[(user, true)]).await
367 }
368}
369
370fn collect_device_updates(
376 verification_machine: VerificationMachine,
377 own_identity: Option<OwnUserIdentityData>,
378 identities: IdentityChanges,
379 devices: DeviceChanges,
380 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<X509Verifier>,
381) -> DeviceUpdates {
382 let mut new: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
383 let mut changed: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::new();
384
385 let (new_identities, changed_identities, unchanged_identities) = identities.into_maps();
386
387 let map_device = |device: DeviceData| {
388 let device_owner_identity = new_identities
389 .get(device.user_id())
390 .or_else(|| changed_identities.get(device.user_id()))
391 .or_else(|| unchanged_identities.get(device.user_id()))
392 .cloned();
393
394 Device {
395 inner: device,
396 verification_machine: verification_machine.to_owned(),
397 own_identity: own_identity.to_owned(),
398 device_owner_identity,
399 #[cfg(feature = "experimental-x509-identity-verification")]
400 x509_verifier: x509_verifier.to_owned(),
401 }
402 };
403
404 for device in devices.new {
405 let device = map_device(device);
406
407 new.entry(device.user_id().to_owned())
408 .or_default()
409 .insert(device.device_id().to_owned(), device);
410 }
411
412 for device in devices.changed {
413 let device = map_device(device);
414
415 changed
416 .entry(device.user_id().to_owned())
417 .or_default()
418 .insert(device.device_id().to_owned(), device.to_owned());
419 }
420
421 DeviceUpdates { new, changed }
422}
423
424#[allow(missing_debug_implementations)]
426pub struct StoreTransaction {
427 store: Store,
428 changes: PendingChanges,
429 cache: OwnedRwLockWriteGuard<StoreCache>,
431}
432
433impl StoreTransaction {
434 async fn new(store: Store) -> Self {
436 let cache = store.inner.cache.clone();
437
438 Self { store, changes: PendingChanges::default(), cache: cache.clone().write_owned().await }
439 }
440
441 pub(crate) fn cache(&self) -> &StoreCache {
442 &self.cache
443 }
444
445 pub fn store(&self) -> &Store {
447 &self.store
448 }
449
450 pub async fn account(&mut self) -> Result<&mut Account> {
457 if self.changes.account.is_none() {
458 let _ = self.cache.account().await?;
460 self.changes.account = self.cache.account.lock().await.take();
461 }
462 Ok(self.changes.account.as_mut().unwrap())
463 }
464
465 pub async fn commit(self) -> Result<()> {
468 if self.changes.is_empty() {
469 return Ok(());
470 }
471
472 let account = self.changes.account.as_ref().map(|acc| acc.deep_clone());
474
475 self.store.save_pending_changes(self.changes).await?;
476
477 if let Some(account) = account {
479 *self.cache.account.lock().await = Some(account);
480 }
481
482 Ok(())
483 }
484}
485
486#[derive(Debug)]
487struct StoreInner {
488 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
489 store: Arc<CryptoStoreWrapper>,
490
491 cache: Arc<RwLock<StoreCache>>,
495
496 verification_machine: VerificationMachine,
497
498 #[cfg(feature = "experimental-x509-identity-verification")]
501 x509_verifier: Option<X509Verifier>,
502
503 #[cfg(feature = "experimental-x509-identity-verification")]
506 x509_signer: Option<X509Signer>,
507
508 static_account: StaticAccountData,
511}
512
513#[derive(Debug, Error)]
516pub enum SecretImportError {
517 #[error("Error while importing {name}: {error}")]
519 Key {
520 name: SecretName,
522 error: vodozemac::KeyError,
524 },
525 #[error(
528 "Error while importing {name}: The public key of the imported private \
529 key doesn't match the public key that was uploaded to the server"
530 )]
531 MismatchedPublicKeys {
532 name: SecretName,
534 },
535 #[error(transparent)]
537 Store(#[from] CryptoStoreError),
538}
539
540#[derive(Debug, Error)]
545pub enum SecretsBundleExportError {
546 #[error(transparent)]
548 Store(#[from] CryptoStoreError),
549 #[error("The store is missing one or multiple cross-signing keys")]
551 MissingCrossSigningKey(KeyUsage),
552 #[error("The store doesn't contain any cross-signing keys")]
554 MissingCrossSigningKeys,
555 #[error("The store contains a backup key, but no backup version")]
558 MissingBackupVersion,
559}
560
561impl Store {
562 pub(crate) fn new(
564 account: StaticAccountData,
565 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
566 store: Arc<CryptoStoreWrapper>,
567 verification_machine: VerificationMachine,
568 ) -> Self {
569 Self::new_with_x509(
570 account,
571 identity,
572 store,
573 verification_machine,
574 #[cfg(feature = "experimental-x509-identity-verification")]
575 None,
576 #[cfg(feature = "experimental-x509-identity-verification")]
577 None,
578 )
579 }
580
581 pub(crate) fn new_with_x509(
583 account: StaticAccountData,
584 identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
585 store: Arc<CryptoStoreWrapper>,
586 verification_machine: VerificationMachine,
587 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
588 X509Verifier,
589 >,
590 #[cfg(feature = "experimental-x509-identity-verification")] x509_signer: Option<X509Signer>,
591 ) -> Self {
592 Self {
593 inner: Arc::new(StoreInner {
594 static_account: account,
595 identity,
596 store: store.clone(),
597 verification_machine,
598 #[cfg(feature = "experimental-x509-identity-verification")]
599 x509_verifier,
600 #[cfg(feature = "experimental-x509-identity-verification")]
601 x509_signer,
602 cache: Arc::new(RwLock::new(StoreCache {
603 store,
604 tracked_users: Default::default(),
605 loaded_tracked_users: Default::default(),
606 account: Default::default(),
607 })),
608 }),
609 }
610 }
611
612 pub(crate) fn user_id(&self) -> &UserId {
614 &self.inner.static_account.user_id
615 }
616
617 pub(crate) fn device_id(&self) -> &DeviceId {
619 self.inner.verification_machine.own_device_id()
620 }
621
622 pub(crate) fn static_account(&self) -> &StaticAccountData {
624 &self.inner.static_account
625 }
626
627 #[cfg(feature = "experimental-x509-identity-verification")]
630 pub(crate) fn x509_signer(&self) -> Option<&X509Signer> {
631 self.inner.x509_signer.as_ref()
632 }
633
634 #[cfg(feature = "experimental-x509-identity-verification")]
637 pub(crate) fn x509_verifier(&self) -> Option<&X509Verifier> {
638 self.inner.x509_verifier.as_ref()
639 }
640
641 pub(crate) async fn cache(&self) -> Result<StoreCacheGuard> {
642 Ok(StoreCacheGuard { cache: self.inner.cache.clone().read_owned().await })
643 }
644
645 pub(crate) async fn transaction(&self) -> StoreTransaction {
646 StoreTransaction::new(self.clone()).await
647 }
648
649 pub(crate) async fn with_transaction<T>(
650 &self,
651 func: impl AsyncFnOnce(&mut StoreTransaction) -> Result<T, crate::OlmError>,
652 ) -> Result<T, crate::OlmError> {
653 let mut tr = self.transaction().await;
654 let res = func(&mut tr).await?;
655 tr.commit().await?;
656 Ok(res)
657 }
658
659 #[cfg(test)]
660 pub(crate) async fn reset_cross_signing_identity(&self) {
662 self.inner.identity.lock().await.reset();
663 }
664
665 pub(crate) fn private_identity(&self) -> Arc<Mutex<PrivateCrossSigningIdentity>> {
667 self.inner.identity.clone()
668 }
669
670 pub(crate) async fn save_sessions(&self, sessions: &[Session]) -> Result<()> {
672 let changes = Changes { sessions: sessions.to_vec(), ..Default::default() };
673
674 self.save_changes(changes).await
675 }
676
677 pub(crate) async fn get_sessions(
678 &self,
679 sender_key: &str,
680 ) -> Result<Option<Arc<Mutex<Vec<Session>>>>> {
681 self.inner.store.get_sessions(sender_key).await
682 }
683
684 pub(crate) async fn save_changes(&self, changes: Changes) -> Result<()> {
685 self.inner.store.save_changes(changes).await
686 }
687
688 pub(crate) async fn merge_received_group_session(
696 &self,
697 session: InboundGroupSession,
698 ) -> Result<Option<InboundGroupSession>> {
699 let old_session = self
700 .inner
701 .store
702 .get_inbound_group_session(session.room_id(), session.session_id())
703 .await?;
704
705 let Some(old_session) = old_session else {
707 info!("Received a new megolm room key");
708 return Ok(Some(session));
709 };
710
711 let index_comparison = session.compare_ratchet(&old_session).await;
712 let trust_level_comparison =
713 session.sender_data.compare_trust_level(&old_session.sender_data);
714
715 let result = match (index_comparison, trust_level_comparison) {
716 (SessionOrdering::Unconnected, _) => {
717 warn!(
722 "Received a group session with an ratchet that does not connect to the one in the store, discarding"
723 );
724 None
725 }
726
727 (SessionOrdering::Better, std::cmp::Ordering::Greater)
728 | (SessionOrdering::Better, std::cmp::Ordering::Equal)
729 | (SessionOrdering::Equal, std::cmp::Ordering::Greater) => {
730 info!(
733 ?index_comparison,
734 ?trust_level_comparison,
735 "Received a megolm room key that we have a worse version of, merging"
736 );
737 Some(session)
738 }
739
740 (SessionOrdering::Worse, std::cmp::Ordering::Less)
741 | (SessionOrdering::Worse, std::cmp::Ordering::Equal)
742 | (SessionOrdering::Equal, std::cmp::Ordering::Less) => {
743 warn!(
746 ?index_comparison,
747 ?trust_level_comparison,
748 "Received a megolm room key that we already have a better version \
749 of, discarding"
750 );
751 None
752 }
753
754 (SessionOrdering::Equal, std::cmp::Ordering::Equal) => {
755 info!("Received a megolm room key that we already have, discarding");
757 None
758 }
759
760 (SessionOrdering::Better, std::cmp::Ordering::Less) => {
761 info!("Upgrading a previously-received megolm session with new ratchet");
764 let result = old_session.with_ratchet(&session);
765 result.reset_backup_state();
767 Some(result)
768 }
769
770 (SessionOrdering::Worse, std::cmp::Ordering::Greater) => {
771 info!("Upgrading a previously-received megolm session with new sender data");
774 Some(session.with_ratchet(&old_session))
775 }
776 };
777
778 Ok(result)
779 }
780
781 #[cfg(test)]
782 pub(crate) async fn save_device_data(&self, devices: &[DeviceData]) -> Result<()> {
784 use types::DeviceChanges;
785
786 let changes = Changes {
787 devices: DeviceChanges { changed: devices.to_vec(), ..Default::default() },
788 ..Default::default()
789 };
790
791 self.save_changes(changes).await
792 }
793
794 pub(crate) async fn save_inbound_group_sessions(
796 &self,
797 sessions: &[InboundGroupSession],
798 ) -> Result<()> {
799 let changes = Changes { inbound_group_sessions: sessions.to_vec(), ..Default::default() };
800
801 self.save_changes(changes).await
802 }
803
804 pub(crate) async fn device_display_name(&self) -> Result<Option<String>, CryptoStoreError> {
806 Ok(self
807 .inner
808 .store
809 .get_device(self.user_id(), self.device_id())
810 .await?
811 .and_then(|d| d.display_name().map(|d| d.to_owned())))
812 }
813
814 pub(crate) async fn get_device_data(
819 &self,
820 user_id: &UserId,
821 device_id: &DeviceId,
822 ) -> Result<Option<DeviceData>> {
823 self.inner.store.get_device(user_id, device_id).await
824 }
825
826 pub(crate) async fn get_device_data_for_user_filtered(
834 &self,
835 user_id: &UserId,
836 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
837 self.inner.store.get_user_devices(user_id).await.map(|mut d| {
838 if user_id == self.user_id() {
839 d.remove(self.device_id());
840 }
841 d
842 })
843 }
844
845 pub(crate) async fn get_device_data_for_user(
854 &self,
855 user_id: &UserId,
856 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
857 self.inner.store.get_user_devices(user_id).await
858 }
859
860 pub(crate) async fn get_device_from_curve_key(
866 &self,
867 user_id: &UserId,
868 curve_key: Curve25519PublicKey,
869 ) -> Result<Option<Device>> {
870 self.get_user_devices(user_id)
871 .await
872 .map(|d| d.devices().find(|d| d.curve25519_key() == Some(curve_key)))
873 }
874
875 pub(crate) async fn get_user_devices(&self, user_id: &UserId) -> Result<UserDevices> {
885 let devices = self.get_device_data_for_user(user_id).await?;
886
887 let own_identity = self
888 .inner
889 .store
890 .get_user_identity(self.user_id())
891 .await?
892 .and_then(|i| i.own().cloned());
893 let device_owner_identity = self.inner.store.get_user_identity(user_id).await?;
894
895 Ok(UserDevices {
896 inner: devices,
897 verification_machine: self.inner.verification_machine.clone(),
898 own_identity,
899 device_owner_identity,
900 #[cfg(feature = "experimental-x509-identity-verification")]
901 x509_verifier: self.x509_verifier().cloned(),
902 })
903 }
904
905 pub(crate) async fn get_device(
915 &self,
916 user_id: &UserId,
917 device_id: &DeviceId,
918 ) -> Result<Option<Device>> {
919 if let Some(device_data) = self.inner.store.get_device(user_id, device_id).await? {
920 Ok(Some(self.wrap_device_data(device_data).await?))
921 } else {
922 Ok(None)
923 }
924 }
925
926 pub(crate) async fn wrap_device_data(&self, device_data: DeviceData) -> Result<Device> {
931 let own_identity = self
932 .inner
933 .store
934 .get_user_identity(self.user_id())
935 .await?
936 .and_then(|i| i.own().cloned());
937
938 let device_owner_identity =
939 self.inner.store.get_user_identity(device_data.user_id()).await?;
940
941 Ok(Device {
942 inner: device_data,
943 verification_machine: self.inner.verification_machine.clone(),
944 own_identity,
945 device_owner_identity,
946 #[cfg(feature = "experimental-x509-identity-verification")]
947 x509_verifier: self.x509_verifier().cloned(),
948 })
949 }
950
951 pub(crate) async fn get_identity(&self, user_id: &UserId) -> Result<Option<UserIdentity>> {
953 let own_identity = self
954 .inner
955 .store
956 .get_user_identity(self.user_id())
957 .await?
958 .and_then(as_variant!(UserIdentityData::Own));
959
960 Ok(self.inner.store.get_user_identity(user_id).await?.map(|i| {
961 UserIdentity::new(
962 self.clone(),
963 i,
964 self.inner.verification_machine.to_owned(),
965 own_identity,
966 )
967 }))
968 }
969
970 pub async fn export_secret(
979 &self,
980 secret_name: &SecretName,
981 ) -> Result<Option<String>, CryptoStoreError> {
982 Ok(match secret_name {
983 SecretName::CrossSigningMasterKey
984 | SecretName::CrossSigningUserSigningKey
985 | SecretName::CrossSigningSelfSigningKey => {
986 self.inner.identity.lock().await.export_secret(secret_name).await
987 }
988 SecretName::RecoveryKey => {
989 if let Some(key) = self.load_backup_keys().await?.decryption_key {
990 let exported = key.to_base64();
991 Some(exported)
992 } else {
993 None
994 }
995 }
996 name => {
997 warn!(secret = ?name, "Unknown secret was requested");
998 None
999 }
1000 })
1001 }
1002
1003 pub async fn export_cross_signing_keys(
1011 &self,
1012 ) -> Result<Option<CrossSigningKeyExport>, CryptoStoreError> {
1013 let master_key = self.export_secret(&SecretName::CrossSigningMasterKey).await?;
1014 let self_signing_key = self.export_secret(&SecretName::CrossSigningSelfSigningKey).await?;
1015 let user_signing_key = self.export_secret(&SecretName::CrossSigningUserSigningKey).await?;
1016
1017 Ok(if master_key.is_none() && self_signing_key.is_none() && user_signing_key.is_none() {
1018 None
1019 } else {
1020 Some(CrossSigningKeyExport { master_key, self_signing_key, user_signing_key })
1021 })
1022 }
1023
1024 pub async fn import_cross_signing_keys(
1029 &self,
1030 export: CrossSigningKeyExport,
1031 ) -> Result<CrossSigningStatus, SecretImportError> {
1032 if let Some(public_identity) =
1033 self.get_identity(self.user_id()).await?.and_then(|i| i.own())
1034 {
1035 let identity = self.inner.identity.lock().await;
1036
1037 identity
1038 .import_secrets(
1039 public_identity.to_owned(),
1040 export.master_key.as_deref(),
1041 export.self_signing_key.as_deref(),
1042 export.user_signing_key.as_deref(),
1043 )
1044 .await?;
1045
1046 let status = identity.status().await;
1047
1048 let diff = identity.get_public_identity_diff(&public_identity.inner).await;
1049
1050 let mut changes =
1051 Changes { private_identity: Some(identity.clone()), ..Default::default() };
1052
1053 if diff.none_differ() {
1054 public_identity.mark_as_verified();
1055 changes.identities.changed.push(UserIdentityData::Own(public_identity.inner));
1056 }
1057
1058 info!(?status, "Successfully imported the private cross-signing keys");
1059
1060 self.save_changes(changes).await?;
1061 } else {
1062 warn!(
1063 "No public identity found while importing cross-signing keys, \
1064 a /keys/query needs to be done"
1065 );
1066 }
1067
1068 Ok(self.inner.identity.lock().await.status().await)
1069 }
1070
1071 pub async fn export_secrets_bundle(&self) -> Result<SecretsBundle, SecretsBundleExportError> {
1083 let Some(cross_signing) = self.export_cross_signing_keys().await? else {
1084 return Err(SecretsBundleExportError::MissingCrossSigningKeys);
1085 };
1086
1087 let Some(master_key) = cross_signing.master_key.clone() else {
1088 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::Master));
1089 };
1090
1091 let Some(user_signing_key) = cross_signing.user_signing_key.clone() else {
1092 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::UserSigning));
1093 };
1094
1095 let Some(self_signing_key) = cross_signing.self_signing_key.clone() else {
1096 return Err(SecretsBundleExportError::MissingCrossSigningKey(KeyUsage::SelfSigning));
1097 };
1098
1099 let backup_keys = self.load_backup_keys().await?;
1100
1101 let backup = if let Some(key) = backup_keys.decryption_key {
1102 if let Some(backup_version) = backup_keys.backup_version {
1103 Some(BackupSecrets::MegolmBackupV1Curve25519AesSha2(
1104 MegolmBackupV1Curve25519AesSha2Secrets { key, backup_version },
1105 ))
1106 } else {
1107 return Err(SecretsBundleExportError::MissingBackupVersion);
1108 }
1109 } else {
1110 None
1111 };
1112
1113 Ok(SecretsBundle {
1114 cross_signing: CrossSigningSecrets { master_key, user_signing_key, self_signing_key },
1115 backup,
1116 })
1117 }
1118
1119 pub async fn import_secrets_bundle(
1132 &self,
1133 bundle: &SecretsBundle,
1134 ) -> Result<(), SecretImportError> {
1135 let mut changes = Changes::default();
1136
1137 if let Some(backup_bundle) = &bundle.backup {
1138 match backup_bundle {
1139 BackupSecrets::MegolmBackupV1Curve25519AesSha2(bundle) => {
1140 changes.backup_decryption_key = Some(bundle.key.clone());
1141 changes.backup_version = Some(bundle.backup_version.clone());
1142 }
1143 }
1144 }
1145
1146 let identity = self.inner.identity.lock().await;
1147
1148 identity
1149 .import_secrets_unchecked(
1150 Some(&bundle.cross_signing.master_key),
1151 Some(&bundle.cross_signing.self_signing_key),
1152 Some(&bundle.cross_signing.user_signing_key),
1153 )
1154 .await?;
1155
1156 let public_identity = identity.to_public_identity().await.expect(
1157 "We should be able to create a new public identity since we just imported \
1158 all the private cross-signing keys",
1159 );
1160
1161 changes.private_identity = Some(identity.clone());
1162 changes.identities.new.push(UserIdentityData::Own(public_identity));
1163
1164 Ok(self.save_changes(changes).await?)
1165 }
1166
1167 pub async fn import_secret(&self, secret: &GossippedSecret) -> Result<(), SecretImportError> {
1169 match &secret.secret_name {
1170 SecretName::CrossSigningMasterKey
1171 | SecretName::CrossSigningUserSigningKey
1172 | SecretName::CrossSigningSelfSigningKey => {
1173 if let Some(public_identity) =
1174 self.get_identity(self.user_id()).await?.and_then(|i| i.own())
1175 {
1176 let identity = self.inner.identity.lock().await;
1177
1178 identity
1179 .import_secret(
1180 public_identity,
1181 &secret.secret_name,
1182 &secret.event.content.secret,
1183 )
1184 .await?;
1185 info!(
1186 secret_name = ?secret.secret_name,
1187 "Successfully imported a private cross signing key"
1188 );
1189
1190 let changes =
1191 Changes { private_identity: Some(identity.clone()), ..Default::default() };
1192
1193 self.save_changes(changes).await?;
1194 }
1195 }
1196 SecretName::RecoveryKey => {
1197 }
1203 name => {
1204 warn!(secret = ?name, "Tried to import an unknown secret");
1205 }
1206 }
1207
1208 Ok(())
1209 }
1210
1211 pub async fn get_only_allow_trusted_devices(&self) -> Result<bool> {
1214 let value = self.get_value("only_allow_trusted_devices").await?.unwrap_or_default();
1215 Ok(value)
1216 }
1217
1218 pub async fn set_only_allow_trusted_devices(
1221 &self,
1222 block_untrusted_devices: bool,
1223 ) -> Result<()> {
1224 self.set_value("only_allow_trusted_devices", &block_untrusted_devices).await
1225 }
1226
1227 pub async fn get_value<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>> {
1229 let Some(value) = self.get_custom_value(key).await? else {
1230 return Ok(None);
1231 };
1232 let deserialized = self.deserialize_value(&value)?;
1233 Ok(Some(deserialized))
1234 }
1235
1236 pub async fn set_value(&self, key: &str, value: &impl Serialize) -> Result<()> {
1238 let serialized = self.serialize_value(value)?;
1239 self.set_custom_value(key, serialized).await?;
1240 Ok(())
1241 }
1242
1243 fn serialize_value(&self, value: &impl Serialize) -> Result<Vec<u8>> {
1244 let serialized =
1245 rmp_serde::to_vec_named(value).map_err(|x| CryptoStoreError::Backend(x.into()))?;
1246 Ok(serialized)
1247 }
1248
1249 fn deserialize_value<T: DeserializeOwned>(&self, value: &[u8]) -> Result<T> {
1250 let deserialized =
1251 rmp_serde::from_slice(value).map_err(|e| CryptoStoreError::Backend(e.into()))?;
1252 Ok(deserialized)
1253 }
1254
1255 pub fn room_keys_received_stream(
1267 &self,
1268 ) -> impl Stream<Item = Result<Vec<RoomKeyInfo>, BroadcastStreamRecvError>> + use<> {
1269 self.inner.store.room_keys_received_stream()
1270 }
1271
1272 pub fn room_keys_withheld_received_stream(
1281 &self,
1282 ) -> impl Stream<Item = Vec<RoomKeyWithheldInfo>> + use<> {
1283 self.inner.store.room_keys_withheld_received_stream()
1284 }
1285
1286 pub fn user_identities_stream(&self) -> impl Stream<Item = IdentityUpdates> + use<> {
1318 let verification_machine = self.inner.verification_machine.to_owned();
1319
1320 let this = self.clone();
1321 self.inner.store.identities_stream().map(move |(own_identity, identities, _)| {
1322 let (new_identities, changed_identities, unchanged_identities) = identities.into_maps();
1323
1324 let map_identity = |(user_id, identity)| {
1325 (
1326 user_id,
1327 UserIdentity::new(
1328 this.clone(),
1329 identity,
1330 verification_machine.to_owned(),
1331 own_identity.to_owned(),
1332 ),
1333 )
1334 };
1335
1336 let new = new_identities.into_iter().map(map_identity).collect();
1337 let changed = changed_identities.into_iter().map(map_identity).collect();
1338 let unchanged = unchanged_identities.into_iter().map(map_identity).collect();
1339
1340 IdentityUpdates { new, changed, unchanged }
1341 })
1342 }
1343
1344 pub fn devices_stream(&self) -> impl Stream<Item = DeviceUpdates> + use<> {
1377 let verification_machine = self.inner.verification_machine.to_owned();
1378 #[cfg(feature = "experimental-x509-identity-verification")]
1379 let x509_verifier = self.x509_verifier().cloned();
1380
1381 self.inner.store.identities_stream().map(move |(own_identity, identities, devices)| {
1382 collect_device_updates(
1383 verification_machine.to_owned(),
1384 own_identity,
1385 identities,
1386 devices,
1387 #[cfg(feature = "experimental-x509-identity-verification")]
1388 x509_verifier.to_owned(),
1389 )
1390 })
1391 }
1392
1393 pub fn identities_stream_raw(
1403 &self,
1404 ) -> impl Stream<Item = (IdentityChanges, DeviceChanges)> + use<> {
1405 self.inner.store.identities_stream().map(|(_, identities, devices)| (identities, devices))
1406 }
1407
1408 pub fn create_store_lock(
1411 &self,
1412 lock_key: String,
1413 config: CrossProcessLockConfig,
1414 ) -> CrossProcessLock<LockableCryptoStore> {
1415 self.inner.store.create_store_lock(lock_key, config)
1416 }
1417
1418 pub fn secrets_stream(&self) -> impl Stream<Item = SecretsInboxItem> + use<> {
1459 self.inner.store.secrets_stream()
1460 }
1461
1462 pub fn historic_room_key_stream(&self) -> impl Stream<Item = RoomKeyBundleInfo> + use<> {
1504 self.inner.store.historic_room_key_stream()
1505 }
1506
1507 pub async fn import_room_keys(
1520 &self,
1521 exported_keys: Vec<ExportedRoomKey>,
1522 from_backup_version: Option<&str>,
1523 progress_listener: impl Fn(usize, usize),
1524 ) -> Result<RoomKeyImportResult> {
1525 let exported_keys = exported_keys.iter().filter_map(|key| {
1526 key.try_into()
1527 .map_err(|e| {
1528 warn!(
1529 sender_key = key.sender_key().to_base64(),
1530 room_id = ?key.room_id(),
1531 session_id = key.session_id(),
1532 error = ?e,
1533 "Couldn't import a room key from a file export."
1534 );
1535 })
1536 .ok()
1537 });
1538 self.import_sessions_impl(exported_keys, from_backup_version, progress_listener).await
1539 }
1540
1541 pub async fn import_exported_room_keys(
1568 &self,
1569 exported_keys: Vec<ExportedRoomKey>,
1570 progress_listener: impl Fn(usize, usize),
1571 ) -> Result<RoomKeyImportResult> {
1572 self.import_room_keys(exported_keys, None, progress_listener).await
1573 }
1574
1575 async fn import_sessions_impl(
1576 &self,
1577 sessions: impl Iterator<Item = InboundGroupSession>,
1578 from_backup_version: Option<&str>,
1579 progress_listener: impl Fn(usize, usize),
1580 ) -> Result<RoomKeyImportResult> {
1581 let sessions: Vec<_> = sessions.collect();
1582 let mut imported_sessions = Vec::new();
1583
1584 let total_count = sessions.len();
1585 let mut keys = BTreeMap::new();
1586
1587 for (i, session) in sessions.into_iter().enumerate() {
1588 if let Some(merged) = self.merge_received_group_session(session).await? {
1591 if from_backup_version.is_some() {
1592 merged.mark_as_backed_up();
1593 }
1594
1595 keys.entry(merged.room_id().to_owned())
1596 .or_insert_with(BTreeMap::new)
1597 .entry(merged.sender_key().to_base64())
1598 .or_insert_with(BTreeSet::new)
1599 .insert(merged.session_id().to_owned());
1600
1601 imported_sessions.push(merged);
1602 }
1603
1604 progress_listener(i, total_count);
1605 }
1606
1607 let imported_count = imported_sessions.len();
1608
1609 self.inner
1610 .store
1611 .save_inbound_group_sessions(imported_sessions, from_backup_version)
1612 .await?;
1613
1614 info!(total_count, imported_count, room_keys = ?keys, "Successfully imported room keys");
1615
1616 Ok(RoomKeyImportResult::new(imported_count, total_count, keys))
1617 }
1618
1619 pub(crate) fn crypto_store(&self) -> Arc<CryptoStoreWrapper> {
1620 self.inner.store.clone()
1621 }
1622
1623 pub async fn export_room_keys(
1646 &self,
1647 predicate: impl FnMut(&InboundGroupSession) -> bool,
1648 ) -> Result<Vec<ExportedRoomKey>> {
1649 let mut exported = Vec::new();
1650
1651 let mut sessions = self.get_inbound_group_sessions().await?;
1652 sessions.retain(predicate);
1653
1654 for session in sessions {
1655 let export = session.export().await;
1656 exported.push(export);
1657 }
1658
1659 Ok(exported)
1660 }
1661
1662 pub async fn export_room_keys_stream(
1697 &self,
1698 predicate: impl FnMut(&InboundGroupSession) -> bool,
1699 ) -> Result<impl Stream<Item = ExportedRoomKey>> {
1700 let sessions = self.get_inbound_group_sessions().await?;
1703 Ok(futures_util::stream::iter(sessions.into_iter().filter(predicate))
1704 .then(|session| async move { session.export().await }))
1705 }
1706
1707 pub async fn build_room_key_bundle(
1712 &self,
1713 room_id: &RoomId,
1714 ) -> std::result::Result<RoomKeyBundle, CryptoStoreError> {
1715 let sessions = self.get_inbound_group_sessions_by_room_id(room_id).await?;
1716
1717 let mut bundle = RoomKeyBundle::default();
1718 for session in sessions {
1719 if session.shared_history() {
1720 bundle.room_keys.push(session.export().await.into());
1721 } else {
1722 bundle.withheld.push(RoomKeyWithheldContent::new(
1723 session.algorithm().to_owned(),
1724 WithheldCode::HistoryNotShared,
1725 session.room_id().to_owned(),
1726 session.session_id().to_owned(),
1727 session.sender_key().to_owned(),
1728 self.device_id().to_owned(),
1729 ));
1730 }
1731 }
1732
1733 let withhelds = self.get_withheld_sessions_by_room_id(room_id).await?;
1736 for withheld in withhelds {
1737 if withheld.content.withheld_code() == WithheldCode::HistoryNotShared {
1738 bundle.withheld.push(withheld.content);
1739 }
1740 }
1741
1742 Ok(bundle)
1743 }
1744
1745 #[instrument(skip(self, bundle, progress_listener), fields(bundle_size = bundle.room_keys.len(), sender_data))]
1755 pub async fn receive_room_key_bundle(
1756 &self,
1757 bundle_info: &StoredRoomKeyBundleData,
1758 bundle: RoomKeyBundle,
1759 progress_listener: impl Fn(usize, usize),
1760 ) -> Result<(), CryptoStoreError> {
1761 let sender_data = if bundle_info.sender_data.should_recalculate() {
1762 let device = self
1763 .get_device_from_curve_key(&bundle_info.sender_user, bundle_info.sender_key)
1764 .await?;
1765
1766 device
1767 .as_ref()
1768 .map(SenderData::from_device)
1769 .unwrap_or_else(|| bundle_info.sender_data.clone())
1770 } else {
1771 bundle_info.sender_data.clone()
1772 };
1773
1774 tracing::Span::current().record("sender_data", tracing::field::debug(&sender_data));
1775
1776 let Ok(forwarder_data) = (&sender_data).try_into() else {
1780 warn!(
1781 "Not accepting a historic room key bundle due to insufficient trust in the sender"
1782 );
1783 return Ok(());
1784 };
1785
1786 self.import_room_key_bundle_sessions(
1787 bundle_info,
1788 &bundle,
1789 &forwarder_data,
1790 progress_listener,
1791 )
1792 .await?;
1793 self.import_room_key_bundle_withheld_info(bundle_info, &bundle).await?;
1794
1795 Ok(())
1796 }
1797
1798 async fn import_room_key_bundle_sessions(
1799 &self,
1800 bundle_info: &StoredRoomKeyBundleData,
1801 bundle: &RoomKeyBundle,
1802 forwarder_data: &ForwarderData,
1803 progress_listener: impl Fn(usize, usize),
1804 ) -> Result<(), CryptoStoreError> {
1805 let (good, bad): (Vec<_>, Vec<_>) = bundle.room_keys.iter().partition_map(|key| {
1806 if key.room_id != bundle_info.bundle_data.room_id {
1807 trace!("Ignoring key for incorrect room {} in bundle", key.room_id);
1808 Either::Right(key)
1809 } else {
1810 Either::Left(key)
1811 }
1812 });
1813
1814 match (bad.is_empty(), good.is_empty()) {
1815 (true, true) => {
1817 warn!("Received a completely empty room key bundle");
1818 }
1819
1820 (false, true) => {
1822 let bad_keys: Vec<_> =
1823 bad.iter().map(|&key| (&key.room_id, &key.session_id)).collect();
1824
1825 warn!(
1826 ?bad_keys,
1827 "Received a room key bundle for the wrong room, ignoring all room keys from the bundle"
1828 );
1829 }
1830
1831 (_, false) => {
1833 if !bad.is_empty() {
1836 warn!(
1837 bad_key_count = bad.len(),
1838 "The room key bundle contained some room keys \
1839 that were meant for a different room"
1840 );
1841 }
1842
1843 let keys = good.iter().filter_map(|key| {
1844 key.try_into_inbound_group_session(forwarder_data)
1845 .map_err(|e| {
1846 warn!(
1847 sender_key = ?key.sender_key().to_base64(),
1848 room_id = ?key.room_id(),
1849 session_id = key.session_id(),
1850 error = ?e,
1851 "Couldn't import a room key from a key bundle."
1852 );
1853 })
1854 .ok()
1855 });
1856
1857 self.import_sessions_impl(keys, None, progress_listener).await?;
1858 }
1859 }
1860
1861 Ok(())
1862 }
1863
1864 async fn import_room_key_bundle_withheld_info(
1865 &self,
1866 bundle_info: &StoredRoomKeyBundleData,
1867 bundle: &RoomKeyBundle,
1868 ) -> Result<(), CryptoStoreError> {
1869 let mut session_id_to_withheld_code_map = BTreeMap::new();
1870
1871 let mut changes = Changes::default();
1872 for withheld in &bundle.withheld {
1873 let (room_id, session_id) = match withheld {
1874 RoomKeyWithheldContent::MegolmV1AesSha2(c) => match (c.room_id(), c.session_id()) {
1875 (Some(room_id), Some(session_id)) => (room_id, session_id),
1876 _ => continue,
1877 },
1878 #[cfg(feature = "experimental-algorithms")]
1879 RoomKeyWithheldContent::MegolmV2AesSha2(c) => match (c.room_id(), c.session_id()) {
1880 (Some(room_id), Some(session_id)) => (room_id, session_id),
1881 _ => continue,
1882 },
1883 RoomKeyWithheldContent::Unknown(_) => continue,
1884 };
1885
1886 if room_id != bundle_info.bundle_data.room_id {
1887 trace!("Ignoring withheld info for incorrect room {} in bundle", room_id);
1888 continue;
1889 }
1890
1891 changes.withheld_session_info.entry(room_id.to_owned()).or_default().insert(
1892 session_id.to_owned(),
1893 RoomKeyWithheldEntry {
1894 sender: bundle_info.sender_user.clone(),
1895 content: withheld.to_owned(),
1896 },
1897 );
1898 session_id_to_withheld_code_map.insert(session_id, withheld.withheld_code());
1899 }
1900
1901 self.save_changes(changes).await?;
1902
1903 info!(
1904 room_id = ?bundle_info.bundle_data.room_id,
1905 ?session_id_to_withheld_code_map,
1906 "Successfully imported withheld info from room key bundle",
1907 );
1908
1909 Ok(())
1910 }
1911
1912 pub async fn store_room_pending_key_bundle(
1917 &self,
1918 room_id: &RoomId,
1919 inviter: &UserId,
1920 ) -> Result<(), CryptoStoreError> {
1921 let invite_accepted_at = MilliSecondsSinceUnixEpoch::now();
1922 self.save_changes(Changes {
1923 rooms_pending_key_bundle: HashMap::from([(
1924 room_id.to_owned(),
1925 Some(RoomPendingKeyBundleDetails {
1926 room_id: room_id.to_owned(),
1927 invite_accepted_at,
1928 inviter: inviter.to_owned(),
1929 }),
1930 )]),
1931 ..Default::default()
1932 })
1933 .await?;
1934
1935 Ok(())
1936 }
1937
1938 pub async fn clear_room_pending_key_bundle(&self, room_id: &RoomId) -> Result<()> {
1942 self.save_changes(Changes {
1943 rooms_pending_key_bundle: HashMap::from([(room_id.to_owned(), None)]),
1944 ..Default::default()
1945 })
1946 .await
1947 }
1948}
1949
1950impl Deref for Store {
1951 type Target = DynCryptoStore;
1952
1953 fn deref(&self) -> &Self::Target {
1954 self.inner.store.deref().deref()
1955 }
1956}
1957
1958#[derive(Clone, Debug)]
1960pub struct LockableCryptoStore(Arc<dyn CryptoStore<Error = CryptoStoreError>>);
1961
1962impl matrix_sdk_common::cross_process_lock::TryLock for LockableCryptoStore {
1963 type LockError = CryptoStoreError;
1964
1965 async fn try_lock(
1966 &self,
1967 lease_duration_ms: u32,
1968 key: &str,
1969 holder: &str,
1970 ) -> std::result::Result<Option<CrossProcessLockGeneration>, Self::LockError> {
1971 self.0.try_take_leased_lock(lease_duration_ms, key, holder).await
1972 }
1973}
1974
1975#[cfg(test)]
1976mod tests {
1977 use std::{assert_matches, collections::BTreeMap, pin::pin};
1978
1979 use futures_util::StreamExt;
1980 use insta::{_macro_support::Content, assert_json_snapshot, internals::ContentPath};
1981 use matrix_sdk_test::async_test;
1982 use rand::RngExt;
1983 use ruma::{
1984 RoomId, device_id,
1985 events::room::{EncryptedFile, EncryptedFileHashes, V2EncryptedFileInfo},
1986 owned_device_id, owned_mxc_uri, room_id, user_id,
1987 };
1988 use serde_json::json;
1989 use strass::assert_let;
1990 use vodozemac::{Ed25519Keypair, megolm::SessionKey};
1991
1992 use crate::{
1993 Account, OlmMachine,
1994 machine::test_helpers::get_machine_pair,
1995 olm::{InboundGroupSession, SenderData},
1996 store::types::{DehydratedDeviceKey, RoomKeyWithheldEntry, StoredRoomKeyBundleData},
1997 types::{
1998 EventEncryptionAlgorithm,
1999 events::{
2000 room_key_bundle::RoomKeyBundleContent,
2001 room_key_withheld::{MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent},
2002 },
2003 },
2004 };
2005
2006 #[async_test]
2007 async fn test_merge_received_group_session() {
2008 let alice_account = Account::with_device_id(user_id!("@a:s.co"), device_id!("ABC"));
2009 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("DEF")).await;
2010
2011 let room_id = room_id!("!test:localhost");
2012
2013 let megolm_signing_key = Ed25519Keypair::new();
2014 let inbound = make_inbound_group_session(&alice_account, &megolm_signing_key, room_id);
2015
2016 let mut inbound_at_index_5 =
2019 InboundGroupSession::from_export(&inbound.export_at_index(5).await).unwrap();
2020 inbound_at_index_5.sender_data = inbound.sender_data.clone();
2021 bob.store().save_inbound_group_sessions(&[inbound_at_index_5.clone()]).await.unwrap();
2022
2023 let disconnected = make_inbound_group_session(&alice_account, &megolm_signing_key, room_id);
2025 assert_eq!(bob.store().merge_received_group_session(disconnected).await.unwrap(), None);
2026
2027 let mut worse =
2029 InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
2030 worse.sender_data = inbound.sender_data.clone();
2031 assert_eq!(bob.store().merge_received_group_session(worse).await.unwrap(), None);
2032
2033 let mut copy = InboundGroupSession::from_pickle(inbound_at_index_5.pickle().await).unwrap();
2035 copy.sender_data = inbound.sender_data.clone();
2036 assert_eq!(bob.store().merge_received_group_session(copy).await.unwrap(), None);
2037
2038 let mut better =
2041 InboundGroupSession::from_export(&inbound.export_at_index(0).await).unwrap();
2042 better.sender_data = inbound.sender_data.clone();
2043 assert_let!(Some(update) = bob.store().merge_received_group_session(better).await.unwrap());
2044 assert_eq!(update.first_known_index(), 0);
2045
2046 {
2048 let mut worse_ratchet_better_trust =
2049 InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
2050 let updated_sender_data = SenderData::sender_verified(
2051 alice_account.user_id(),
2052 alice_account.device_id(),
2053 Ed25519Keypair::new().public_key(),
2054 );
2055 worse_ratchet_better_trust.sender_data = updated_sender_data.clone();
2056 assert_let!(
2057 Some(update) = bob
2058 .store()
2059 .merge_received_group_session(worse_ratchet_better_trust)
2060 .await
2061 .unwrap()
2062 );
2063 assert_eq!(update.sender_data, updated_sender_data);
2064 assert_eq!(update.first_known_index(), 5);
2065 assert_eq!(
2066 update.export_at_index(0).await.session_key.to_bytes(),
2067 inbound.export_at_index(5).await.session_key.to_bytes()
2068 );
2069 }
2070
2071 {
2073 let mut better_ratchet_worse_trust =
2074 InboundGroupSession::from_export(&inbound.export_at_index(0).await).unwrap();
2075 let updated_sender_data = SenderData::unknown();
2076 better_ratchet_worse_trust.sender_data = updated_sender_data.clone();
2077 assert_let!(
2078 Some(update) = bob
2079 .store()
2080 .merge_received_group_session(better_ratchet_worse_trust)
2081 .await
2082 .unwrap()
2083 );
2084 assert_eq!(update.sender_data, inbound.sender_data);
2085 assert_eq!(update.first_known_index(), 0);
2086 assert_eq!(
2087 update.export_at_index(0).await.session_key.to_bytes(),
2088 inbound.export_at_index(0).await.session_key.to_bytes()
2089 );
2090 }
2091 }
2092
2093 fn make_inbound_group_session(
2096 sender_account: &Account,
2097 signing_key: &Ed25519Keypair,
2098 room_id: &RoomId,
2099 ) -> InboundGroupSession {
2100 InboundGroupSession::new(
2101 sender_account.identity_keys.curve25519,
2102 sender_account.identity_keys.ed25519,
2103 room_id,
2104 &make_session_key(signing_key),
2105 SenderData::device_info(crate::types::DeviceKeys::new(
2106 sender_account.user_id().to_owned(),
2107 sender_account.device_id().to_owned(),
2108 vec![],
2109 BTreeMap::new(),
2110 crate::types::Signatures::new(),
2111 )),
2112 None,
2113 EventEncryptionAlgorithm::MegolmV1AesSha2,
2114 Some(ruma::events::room::history_visibility::HistoryVisibility::Shared),
2115 true,
2116 )
2117 .unwrap()
2118 }
2119
2120 fn make_session_key(signing_key: &Ed25519Keypair) -> SessionKey {
2123 let mut session_key_bytes = vec![0u8; 229];
2128 session_key_bytes[0] = 2;
2130 rand::rng().fill(&mut session_key_bytes[5..133]);
2132 session_key_bytes[133..165].copy_from_slice(signing_key.public_key().as_bytes());
2134 let sig = signing_key.sign(&session_key_bytes[0..165]);
2136 session_key_bytes[165..229].copy_from_slice(&sig.to_bytes());
2137
2138 SessionKey::from_bytes(&session_key_bytes).unwrap()
2139 }
2140
2141 #[async_test]
2142 async fn test_import_room_keys_notifies_stream() {
2143 use futures_util::FutureExt;
2144
2145 let (alice, bob, _) =
2146 get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2147
2148 let room1_id = room_id!("!room1:localhost");
2149 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2150 let exported_sessions = alice.store().export_room_keys(|_| true).await.unwrap();
2151
2152 let mut room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream());
2153 bob.store().import_room_keys(exported_sessions, None, |_, _| {}).await.unwrap();
2154
2155 let room_keys = room_keys_received_stream
2156 .next()
2157 .now_or_never()
2158 .flatten()
2159 .expect("We should have received an update of room key infos")
2160 .unwrap();
2161 assert_eq!(room_keys.len(), 1);
2162 assert_eq!(room_keys[0].room_id, "!room1:localhost");
2163 }
2164
2165 #[async_test]
2166 async fn test_export_room_keys_provides_selected_keys() {
2167 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2169 let room1_id = room_id!("!room1:localhost");
2170 let room2_id = room_id!("!room2:localhost");
2171 let room3_id = room_id!("!room3:localhost");
2172 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2173 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2174 alice.create_outbound_group_session_with_defaults_test_helper(room3_id).await.unwrap();
2175
2176 let keys = alice
2178 .store()
2179 .export_room_keys(|s| s.room_id() == room2_id || s.room_id() == room3_id)
2180 .await
2181 .unwrap();
2182
2183 assert_eq!(keys.len(), 2);
2185 assert_eq!(keys[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2186 assert_eq!(keys[1].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2187 assert_eq!(keys[0].room_id, "!room2:localhost");
2188 assert_eq!(keys[1].room_id, "!room3:localhost");
2189 assert_eq!(keys[0].session_key.to_base64().len(), 220);
2190 assert_eq!(keys[1].session_key.to_base64().len(), 220);
2191 }
2192
2193 #[async_test]
2194 async fn test_export_room_keys_stream_can_provide_all_keys() {
2195 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2197 let room1_id = room_id!("!room1:localhost");
2198 let room2_id = room_id!("!room2:localhost");
2199 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2200 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2201
2202 let mut keys = pin!(alice.store().export_room_keys_stream(|_| true).await.unwrap());
2204
2205 let mut collected = vec![];
2207 while let Some(key) = keys.next().await {
2208 collected.push(key);
2209 }
2210
2211 assert_eq!(collected.len(), 2);
2213 assert_eq!(collected[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2214 assert_eq!(collected[1].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2215 assert_eq!(collected[0].room_id, "!room1:localhost");
2216 assert_eq!(collected[1].room_id, "!room2:localhost");
2217 assert_eq!(collected[0].session_key.to_base64().len(), 220);
2218 assert_eq!(collected[1].session_key.to_base64().len(), 220);
2219 }
2220
2221 #[async_test]
2222 async fn test_export_room_keys_stream_can_provide_a_subset_of_keys() {
2223 let (alice, _, _) = get_machine_pair(user_id!("@a:s.co"), user_id!("@b:s.co"), false).await;
2225 let room1_id = room_id!("!room1:localhost");
2226 let room2_id = room_id!("!room2:localhost");
2227 alice.create_outbound_group_session_with_defaults_test_helper(room1_id).await.unwrap();
2228 alice.create_outbound_group_session_with_defaults_test_helper(room2_id).await.unwrap();
2229
2230 let mut keys =
2232 pin!(alice.store().export_room_keys_stream(|s| s.room_id() == room1_id).await.unwrap());
2233
2234 let mut collected = vec![];
2236 while let Some(key) = keys.next().await {
2237 collected.push(key);
2238 }
2239
2240 assert_eq!(collected.len(), 1);
2242 assert_eq!(collected[0].algorithm, EventEncryptionAlgorithm::MegolmV1AesSha2);
2243 assert_eq!(collected[0].room_id, "!room1:localhost");
2244 assert_eq!(collected[0].session_key.to_base64().len(), 220);
2245 }
2246
2247 #[async_test]
2248 async fn test_export_secrets_bundle() {
2249 let user_id = user_id!("@alice:example.com");
2250 let (first, second, _) = get_machine_pair(user_id, user_id, false).await;
2251
2252 let _ = first
2253 .bootstrap_cross_signing(false)
2254 .await
2255 .expect("We should be able to bootstrap cross-signing");
2256
2257 let bundle = first.store().export_secrets_bundle().await.expect(
2258 "We should be able to export the secrets bundle, now that we \
2259 have the cross-signing keys",
2260 );
2261
2262 assert!(bundle.backup.is_none(), "The bundle should not contain a backup key");
2263
2264 second
2265 .store()
2266 .import_secrets_bundle(&bundle)
2267 .await
2268 .expect("We should be able to import the secrets bundle");
2269
2270 let status = second.cross_signing_status().await;
2271 let identity = second.get_identity(user_id, None).await.unwrap().unwrap().own().unwrap();
2272
2273 assert!(identity.is_verified(), "The public identity should be marked as verified.");
2274
2275 assert!(status.is_complete(), "We should have imported all the cross-signing keys");
2276 }
2277
2278 #[async_test]
2279 async fn test_create_dehydrated_device_key() {
2280 let pickle_key = DehydratedDeviceKey::new();
2281
2282 let to_vec = pickle_key.inner.to_vec();
2283 let pickle_key_from_slice = DehydratedDeviceKey::from_slice(to_vec.as_slice())
2284 .expect("Should be able to create a dehydrated device key from slice");
2285
2286 assert_eq!(pickle_key_from_slice.to_base64(), pickle_key.to_base64());
2287 }
2288
2289 #[async_test]
2290 async fn test_create_dehydrated_errors() {
2291 let too_small = [0u8; 22];
2292 let pickle_key = DehydratedDeviceKey::from_slice(&too_small);
2293
2294 assert!(pickle_key.is_err());
2295
2296 let too_big = [0u8; 40];
2297 let pickle_key = DehydratedDeviceKey::from_slice(&too_big);
2298
2299 assert!(pickle_key.is_err());
2300 }
2301
2302 #[async_test]
2303 async fn test_build_room_key_bundle() {
2304 let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2307 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2308
2309 let room1_id = room_id!("!room1:localhost");
2310 let room2_id = room_id!("!room2:localhost");
2311
2312 let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2317 let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2318 let session_key3 = "AgAAAAAM9KFsliaUUhGSXgwOzM5UemjkNH4n8NHgvC/y8hhw13zTF+ooGD4uIYEXYX630oNvQm/EvgZo+dkoc0re+vsqsx4sQeNODdSjcBsWOa0oDF+irQn9oYoLUDPI1IBtY1rX+FV99Zm/xnG7uFOX7aTVlko2GSdejy1w9mfobmfxu5aUc04A9zaKJP1pOthZvRAlhpymGYHgsDtWPrrjyc/yypMflE4kIUEEEtu1kT6mrAmcl615XYRAHYK9G2+fZsGvokwzbkl4nulGwcZMpQEoM0nD2o3GWgX81HW3nGfKBg";
2319 let session_key4 = "AgAAAAA4Kkesxq2h4v9PLD6Sm3Smxspz1PXTqytQPCMQMkkrHNmzV2bHlJ+6/Al9cu8vh1Oj69AK0WUAeJOJuaiskEeg/PI3P03+UYLeC379RzgqwSHdBgdQ41G2vD6zpgmE/8vYToe+qpCZACtPOswZxyqxHH+T/Iq0nv13JmlFGIeA6fEPfr5Y28B49viG74Fs9rxV9EH5PfjbuPM/p+Sz5obShuaBPKQBX1jT913nEXPoIJ06exNZGr0285nw/LgVvNlmWmbqNnbzO2cNZjQWA+xZYz5FSfyCxwqEBbEdUCuRCQ";
2320
2321 let sessions = [
2322 create_inbound_group_session_with_visibility(
2323 &alice,
2324 room1_id,
2325 &SessionKey::from_base64(session_key1).unwrap(),
2326 true,
2327 ),
2328 create_inbound_group_session_with_visibility(
2329 &alice,
2330 room1_id,
2331 &SessionKey::from_base64(session_key2).unwrap(),
2332 true,
2333 ),
2334 create_inbound_group_session_with_visibility(
2335 &alice,
2336 room1_id,
2337 &SessionKey::from_base64(session_key3).unwrap(),
2338 false,
2339 ),
2340 create_inbound_group_session_with_visibility(
2341 &alice,
2342 room2_id,
2343 &SessionKey::from_base64(session_key4).unwrap(),
2344 true,
2345 ),
2346 ];
2347 bob.store().save_inbound_group_sessions(&sessions).await.unwrap();
2348
2349 let mut bundle = bob.store().build_room_key_bundle(room1_id).await.unwrap();
2351
2352 bundle.room_keys.sort_by_key(|session| session.session_id.clone());
2356
2357 let algorithm = if cfg!(feature = "experimental-algorithms") {
2360 "m.megolm.v2.aes-sha2"
2361 } else {
2362 "m.megolm.v1.aes-sha2"
2363 };
2364 let map_algorithm = move |value: Content, _path: ContentPath<'_>| {
2365 assert_eq!(value.as_str().unwrap(), algorithm);
2366 "[algorithm]"
2367 };
2368
2369 let alice_curve_key = alice.identity_keys().curve25519.to_base64();
2371 let map_alice_curve_key = move |value: Content, _path: ContentPath<'_>| {
2372 assert_eq!(value.as_str().unwrap(), alice_curve_key);
2373 "[alice curve key]"
2374 };
2375 let alice_ed25519_key = alice.identity_keys().ed25519.to_base64();
2376 let map_alice_ed25519_key = move |value: Content, _path: ContentPath<'_>| {
2377 assert_eq!(value.as_str().unwrap(), alice_ed25519_key);
2378 "[alice ed25519 key]"
2379 };
2380
2381 insta::with_settings!({ sort_maps => true }, {
2382 assert_json_snapshot!(bundle, {
2383 ".withheld[].algorithm" => insta::dynamic_redaction(map_algorithm),
2384 ".room_keys[].algorithm" => insta::dynamic_redaction(map_algorithm),
2385 ".room_keys[].sender_key" => insta::dynamic_redaction(map_alice_curve_key.clone()),
2386 ".withheld[].sender_key" => insta::dynamic_redaction(map_alice_curve_key),
2387 ".room_keys[].sender_claimed_keys.ed25519" => insta::dynamic_redaction(map_alice_ed25519_key),
2388 });
2389 });
2390 }
2391
2392 #[async_test]
2393 async fn test_receive_room_key_bundle() {
2394 let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2395 let alice_key = alice.identity_keys().curve25519;
2396 let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2397
2398 let room_id = room_id!("!room1:localhost");
2399
2400 let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2401 let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2402
2403 let sessions = [
2404 create_inbound_group_session_with_visibility(
2405 &alice,
2406 room_id,
2407 &SessionKey::from_base64(session_key1).unwrap(),
2408 true,
2409 ),
2410 create_inbound_group_session_with_visibility(
2411 &alice,
2412 room_id,
2413 &SessionKey::from_base64(session_key2).unwrap(),
2414 false,
2415 ),
2416 ];
2417
2418 alice.store().save_inbound_group_sessions(&sessions).await.unwrap();
2419 let bundle = alice.store().build_room_key_bundle(room_id).await.unwrap();
2420
2421 bob.store()
2422 .receive_room_key_bundle(
2423 &StoredRoomKeyBundleData {
2424 sender_user: alice.user_id().to_owned(),
2425 sender_key: alice_key,
2426 sender_data: SenderData::sender_verified(
2427 alice.user_id(),
2428 device_id!("ALICE"),
2429 alice.identity_keys().ed25519,
2430 ),
2431
2432 bundle_data: RoomKeyBundleContent {
2433 room_id: room_id.to_owned(),
2434 file: EncryptedFile::new(
2437 owned_mxc_uri!("mxc://example.com/0"),
2438 V2EncryptedFileInfo::encode([0; 32], [0; 16]).into(),
2439 EncryptedFileHashes::with_sha256([0; 32]),
2440 ),
2441 },
2442 },
2443 bundle,
2444 |_, _| {},
2445 )
2446 .await
2447 .unwrap();
2448
2449 let imported_sessions =
2451 bob.store().get_inbound_group_sessions_by_room_id(room_id).await.unwrap();
2452
2453 assert_eq!(imported_sessions.len(), 1);
2454 assert_eq!(imported_sessions[0].room_id(), room_id);
2455
2456 assert_eq!(
2458 imported_sessions[0]
2459 .forwarder_data
2460 .as_ref()
2461 .expect("Session should contain forwarder data.")
2462 .user_id(),
2463 alice.user_id()
2464 );
2465
2466 assert_matches!(
2467 bob.store()
2468 .get_withheld_info(room_id, sessions[1].session_id())
2469 .await
2470 .unwrap()
2471 .expect("Withheld info should be present in the store."),
2472 RoomKeyWithheldEntry {
2473 #[cfg(not(feature = "experimental-algorithms"))]
2474 content: RoomKeyWithheldContent::MegolmV1AesSha2(
2475 MegolmV1AesSha2WithheldContent::HistoryNotShared(_)
2476 ),
2477 #[cfg(feature = "experimental-algorithms")]
2478 content: RoomKeyWithheldContent::MegolmV2AesSha2(
2479 MegolmV1AesSha2WithheldContent::HistoryNotShared(_)
2480 ),
2481 ..
2482 }
2483 );
2484 }
2485
2486 #[async_test]
2492 async fn test_deserialize_room_key_withheld_entry_from_to_device_event() {
2493 let entry: RoomKeyWithheldEntry = serde_json::from_value(json!(
2494 {
2495 "content": {
2496 "algorithm": "m.megolm.v1.aes-sha2",
2497 "code": "m.unauthorised",
2498 "from_device": "ALICE",
2499 "reason": "You are not authorised to read the message.",
2500 "room_id": "!roomid:s.co",
2501 "sender_key": "7hIcOrEroXYdzjtCBvBjUiqvT0Me7g+ymeXqoc65RS0",
2502 "session_id": "session123"
2503 },
2504 "sender": "@alice:s.co",
2505 "type": "m.room_key.withheld"
2506 }
2507 ))
2508 .unwrap();
2509
2510 assert_let!(
2511 RoomKeyWithheldEntry {
2512 sender,
2513 content: RoomKeyWithheldContent::MegolmV1AesSha2(
2514 MegolmV1AesSha2WithheldContent::Unauthorised(withheld_content)
2515 ),
2516 } = entry
2517 );
2518
2519 assert_eq!(sender, "@alice:s.co");
2520 assert_eq!(withheld_content.room_id, "!roomid:s.co");
2521 assert_eq!(withheld_content.session_id, "session123");
2522 assert_eq!(
2523 withheld_content.sender_key.to_base64(),
2524 "7hIcOrEroXYdzjtCBvBjUiqvT0Me7g+ymeXqoc65RS0"
2525 );
2526 assert_eq!(withheld_content.from_device, Some(owned_device_id!("ALICE")));
2527 }
2528
2529 fn create_inbound_group_session_with_visibility(
2540 olm_machine: &OlmMachine,
2541 room_id: &RoomId,
2542 session_key: &SessionKey,
2543 shared_history: bool,
2544 ) -> InboundGroupSession {
2545 let identity_keys = &olm_machine.store().static_account().identity_keys;
2546 InboundGroupSession::new(
2547 identity_keys.curve25519,
2548 identity_keys.ed25519,
2549 room_id,
2550 session_key,
2551 SenderData::unknown(),
2552 None,
2553 #[cfg(not(feature = "experimental-algorithms"))]
2554 EventEncryptionAlgorithm::MegolmV1AesSha2,
2555 #[cfg(feature = "experimental-algorithms")]
2556 EventEncryptionAlgorithm::MegolmV2AesSha2,
2557 None,
2558 shared_history,
2559 )
2560 .unwrap()
2561 }
2562}