Skip to main content

matrix_sdk_crypto/store/
memorystore.rs

1// Copyright 2020, 2026 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    collections::{BTreeMap, HashMap, HashSet},
17    convert::Infallible,
18    sync::Arc,
19};
20
21use async_trait::async_trait;
22use matrix_sdk_common::{
23    cross_process_lock::{
24        CrossProcessLockGeneration,
25        memory_store_helper::{Lease, try_take_leased_lock},
26    },
27    locks::RwLock as StdRwLock,
28};
29use ruma::{
30    DeviceId, OwnedDeviceId, OwnedRoomId, OwnedTransactionId, OwnedUserId, RoomId, TransactionId,
31    UserId, events::secret::request::SecretName,
32};
33use tokio::sync::{Mutex, RwLock};
34use tracing::warn;
35use vodozemac::Curve25519PublicKey;
36use zeroize::Zeroizing;
37
38use super::{
39    Account, CryptoStore, InboundGroupSession, Session,
40    caches::DeviceStore,
41    types::{
42        BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts, RoomSettings,
43        StoredRoomKeyBundleData, TrackedUser,
44    },
45};
46use crate::{
47    gossiping::{GossipRequest, SecretInfo},
48    identities::{DeviceData, UserIdentityData},
49    olm::{
50        OutboundGroupSession, PickledAccount, PickledInboundGroupSession, PickledSession,
51        PrivateCrossSigningIdentity, SenderDataType, StaticAccountData,
52    },
53    store::types::{RoomKeyWithheldEntry, RoomPendingKeyBundleDetails},
54};
55
56fn encode_key_info(info: &SecretInfo) -> String {
57    match info {
58        SecretInfo::KeyRequest(info) => {
59            format!("{}{}{}", info.room_id(), info.algorithm(), info.session_id())
60        }
61        SecretInfo::SecretRequest(i) => i.as_ref().to_owned(),
62    }
63}
64
65type SessionId = String;
66
67/// The "version" of a backup - newtype wrapper around a String.
68#[derive(Clone, Debug, PartialEq)]
69struct BackupVersion(String);
70
71impl BackupVersion {
72    fn from(s: &str) -> Self {
73        Self(s.to_owned())
74    }
75
76    fn as_str(&self) -> &str {
77        &self.0
78    }
79}
80
81/// An in-memory only store that will forget all the E2EE key once it's dropped.
82#[derive(Default, Debug)]
83pub struct MemoryStore {
84    static_account: Arc<StdRwLock<Option<StaticAccountData>>>,
85
86    account: StdRwLock<Option<String>>,
87    // Map of sender_key to map of session_id to serialized pickle
88    sessions: StdRwLock<BTreeMap<String, BTreeMap<String, String>>>,
89    inbound_group_sessions: StdRwLock<BTreeMap<OwnedRoomId, HashMap<String, String>>>,
90
91    /// Map room id -> session id -> backup order number The latest backup in
92    /// which this session is stored. Equivalent to `backed_up_to` in
93    /// [`IndexedDbCryptoStore`]
94    inbound_group_sessions_backed_up_to:
95        StdRwLock<HashMap<OwnedRoomId, HashMap<SessionId, BackupVersion>>>,
96
97    outbound_group_sessions: StdRwLock<BTreeMap<OwnedRoomId, OutboundGroupSession>>,
98    private_identity: StdRwLock<Option<PrivateCrossSigningIdentity>>,
99    tracked_users: StdRwLock<HashMap<OwnedUserId, TrackedUser>>,
100    olm_hashes: StdRwLock<HashMap<String, HashSet<String>>>,
101    devices: DeviceStore,
102    identities: StdRwLock<HashMap<OwnedUserId, String>>,
103    outgoing_key_requests: StdRwLock<HashMap<OwnedTransactionId, GossipRequest>>,
104    key_requests_by_info: StdRwLock<HashMap<String, OwnedTransactionId>>,
105    direct_withheld_info: StdRwLock<HashMap<OwnedRoomId, HashMap<String, RoomKeyWithheldEntry>>>,
106    custom_values: StdRwLock<HashMap<String, Vec<u8>>>,
107    leases: StdRwLock<HashMap<String, Lease>>,
108    secret_inbox: StdRwLock<HashMap<String, Vec<Zeroizing<String>>>>,
109    backup_keys: RwLock<BackupKeys>,
110    dehydrated_device_pickle_key: RwLock<Option<DehydratedDeviceKey>>,
111    next_batch_token: RwLock<Option<String>>,
112    room_settings: StdRwLock<HashMap<OwnedRoomId, RoomSettings>>,
113    room_key_bundles:
114        StdRwLock<HashMap<OwnedRoomId, HashMap<OwnedUserId, StoredRoomKeyBundleData>>>,
115    room_key_backups_fully_downloaded: StdRwLock<HashSet<OwnedRoomId>>,
116    rooms_pending_key_bundle: StdRwLock<HashMap<OwnedRoomId, RoomPendingKeyBundleDetails>>,
117
118    save_changes_lock: Arc<Mutex<()>>,
119}
120
121impl MemoryStore {
122    /// Create a new empty `MemoryStore`.
123    pub fn new() -> Self {
124        Self::default()
125    }
126
127    fn get_static_account(&self) -> Option<StaticAccountData> {
128        self.static_account.read().clone()
129    }
130
131    pub(crate) fn save_devices(&self, devices: Vec<DeviceData>) {
132        for device in devices {
133            let _ = self.devices.add(device);
134        }
135    }
136
137    fn delete_devices(&self, devices: Vec<DeviceData>) {
138        for device in devices {
139            let _ = self.devices.remove(device.user_id(), device.device_id());
140        }
141    }
142
143    fn save_sessions(&self, sessions: Vec<(String, PickledSession)>) {
144        let mut session_store = self.sessions.write();
145
146        for (session_id, pickle) in sessions {
147            let entry = session_store.entry(pickle.sender_key.to_base64()).or_default();
148
149            // insert or replace if exists
150            entry.insert(
151                session_id,
152                serde_json::to_string(&pickle).expect("Failed to serialize olm session"),
153            );
154        }
155    }
156
157    fn save_outbound_group_sessions(&self, sessions: Vec<OutboundGroupSession>) {
158        self.outbound_group_sessions
159            .write()
160            .extend(sessions.into_iter().map(|s| (s.room_id().to_owned(), s)));
161    }
162
163    fn save_private_identity(&self, private_identity: Option<PrivateCrossSigningIdentity>) {
164        *self.private_identity.write() = private_identity;
165    }
166
167    /// Return all the [`InboundGroupSession`]s we have, paired with the
168    /// `backed_up_to` value for each one (or "" where it is missing, which
169    /// should never happen).
170    async fn get_inbound_group_sessions_and_backed_up_to(
171        &self,
172    ) -> Result<Vec<(InboundGroupSession, Option<BackupVersion>)>> {
173        let lookup = |s: &InboundGroupSession| {
174            self.inbound_group_sessions_backed_up_to
175                .read()
176                .get(&s.room_id)?
177                .get(s.session_id())
178                .cloned()
179        };
180
181        Ok(self
182            .get_inbound_group_sessions()
183            .await?
184            .into_iter()
185            .map(|s| {
186                let v = lookup(&s);
187                (s, v)
188            })
189            .collect())
190    }
191}
192
193type Result<T> = std::result::Result<T, Infallible>;
194
195#[cfg_attr(target_family = "wasm", async_trait(?Send))]
196#[cfg_attr(not(target_family = "wasm"), async_trait)]
197impl CryptoStore for MemoryStore {
198    type Error = Infallible;
199
200    async fn close(&self) -> Result<()> {
201        Ok(())
202    }
203
204    async fn reopen(&self) -> Result<()> {
205        Ok(())
206    }
207
208    async fn load_account(&self) -> Result<Option<Account>> {
209        let pickled_account: Option<PickledAccount> = self.account.read().as_ref().map(|acc| {
210            serde_json::from_str(acc)
211                .expect("Deserialization failed: invalid pickled account JSON format")
212        });
213
214        if let Some(pickle) = pickled_account {
215            let account =
216                Account::from_pickle(pickle).expect("From pickle failed: invalid pickle format");
217
218            *self.static_account.write() = Some(account.static_data().clone());
219
220            Ok(Some(account))
221        } else {
222            Ok(None)
223        }
224    }
225
226    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>> {
227        Ok(self.private_identity.read().clone())
228    }
229
230    async fn next_batch_token(&self) -> Result<Option<String>> {
231        Ok(self.next_batch_token.read().await.clone())
232    }
233
234    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<()> {
235        let _guard = self.save_changes_lock.lock().await;
236
237        let pickled_account = if let Some(account) = changes.account {
238            *self.static_account.write() = Some(account.static_data().clone());
239            Some(account.pickle())
240        } else {
241            None
242        };
243
244        *self.account.write() = pickled_account.map(|pickle| {
245            serde_json::to_string(&pickle)
246                .expect("Serialization failed: invalid pickled account JSON format")
247        });
248
249        Ok(())
250    }
251
252    async fn save_changes(&self, changes: Changes) -> Result<()> {
253        let _guard = self.save_changes_lock.lock().await;
254
255        let mut pickled_session: Vec<(String, PickledSession)> = Vec::new();
256        for session in changes.sessions {
257            let session_id = session.session_id().to_owned();
258            let pickle = session.pickle().await;
259            pickled_session.push((session_id.clone(), pickle));
260        }
261        self.save_sessions(pickled_session);
262
263        self.save_inbound_group_sessions(changes.inbound_group_sessions, None).await?;
264        self.save_outbound_group_sessions(changes.outbound_group_sessions);
265        self.save_private_identity(changes.private_identity);
266
267        self.save_devices(changes.devices.new);
268        self.save_devices(changes.devices.changed);
269        self.delete_devices(changes.devices.deleted);
270
271        {
272            let mut identities = self.identities.write();
273            for identity in changes.identities.new.into_iter().chain(changes.identities.changed) {
274                identities.insert(
275                    identity.user_id().to_owned(),
276                    serde_json::to_string(&identity)
277                        .expect("UserIdentityData should always serialize to json"),
278                );
279            }
280        }
281
282        {
283            let mut olm_hashes = self.olm_hashes.write();
284            for hash in changes.message_hashes {
285                olm_hashes.entry(hash.sender_key.to_owned()).or_default().insert(hash.hash);
286            }
287        }
288
289        {
290            let mut outgoing_key_requests = self.outgoing_key_requests.write();
291            let mut key_requests_by_info = self.key_requests_by_info.write();
292
293            for key_request in changes.key_requests {
294                let id = key_request.request_id.clone();
295                let info_string = encode_key_info(&key_request.info);
296
297                // If we have an old request for the same key/secret, remove it.
298                if let Some(old_id) = key_requests_by_info.get(&info_string) {
299                    outgoing_key_requests.remove(old_id);
300                    key_requests_by_info.remove(&info_string);
301                }
302
303                outgoing_key_requests.insert(id.clone(), key_request);
304                key_requests_by_info.insert(info_string, id);
305            }
306        }
307
308        if let Some(key) = changes.backup_decryption_key {
309            self.backup_keys.write().await.decryption_key = Some(key);
310        }
311
312        if let Some(version) = changes.backup_version {
313            self.backup_keys.write().await.backup_version = Some(version);
314        }
315
316        if let Some(pickle_key) = changes.dehydrated_device_pickle_key {
317            let mut lock = self.dehydrated_device_pickle_key.write().await;
318            *lock = Some(pickle_key);
319        }
320
321        {
322            let mut secret_inbox = self.secret_inbox.write();
323            for secret in changes.secrets {
324                secret_inbox.entry(secret.secret_name.to_string()).or_default().push(secret.secret);
325            }
326        }
327
328        {
329            let mut direct_withheld_info = self.direct_withheld_info.write();
330            for (room_id, data) in changes.withheld_session_info {
331                for (session_id, event) in data {
332                    direct_withheld_info
333                        .entry(room_id.to_owned())
334                        .or_default()
335                        .insert(session_id, event);
336                }
337            }
338        }
339
340        if let Some(next_batch_token) = changes.next_batch_token {
341            *self.next_batch_token.write().await = Some(next_batch_token);
342        }
343
344        if !changes.room_settings.is_empty() {
345            let mut settings = self.room_settings.write();
346            settings.extend(changes.room_settings);
347        }
348
349        if !changes.received_room_key_bundles.is_empty() {
350            let mut room_key_bundles = self.room_key_bundles.write();
351            for bundle in changes.received_room_key_bundles {
352                room_key_bundles
353                    .entry(bundle.bundle_data.room_id.clone())
354                    .or_default()
355                    .insert(bundle.sender_user.clone(), bundle);
356            }
357        }
358
359        if !changes.room_key_backups_fully_downloaded.is_empty() {
360            let mut room_key_backups_fully_downloaded =
361                self.room_key_backups_fully_downloaded.write();
362            for room in changes.room_key_backups_fully_downloaded {
363                room_key_backups_fully_downloaded.insert(room);
364            }
365        }
366
367        if !changes.rooms_pending_key_bundle.is_empty() {
368            let mut lock = self.rooms_pending_key_bundle.write();
369            for (room, details) in changes.rooms_pending_key_bundle {
370                if let Some(details) = details {
371                    lock.insert(room, details);
372                } else {
373                    lock.remove(&room);
374                }
375            }
376        }
377
378        Ok(())
379    }
380
381    async fn save_inbound_group_sessions(
382        &self,
383        sessions: Vec<InboundGroupSession>,
384        backed_up_to_version: Option<&str>,
385    ) -> Result<()> {
386        for session in sessions {
387            let room_id = session.room_id();
388            let session_id = session.session_id();
389
390            // Sanity-check that the data in the sessions corresponds to
391            // backed_up_version
392            let backed_up = session.backed_up();
393            if backed_up != backed_up_to_version.is_some() {
394                warn!(
395                    backed_up,
396                    backed_up_to_version,
397                    "Session backed-up flag does not correspond to backup version setting",
398                );
399            }
400
401            if let Some(backup_version) = backed_up_to_version {
402                self.inbound_group_sessions_backed_up_to
403                    .write()
404                    .entry(room_id.to_owned())
405                    .or_default()
406                    .insert(session_id.to_owned(), BackupVersion::from(backup_version));
407            }
408
409            let pickle = session.pickle().await;
410            self.inbound_group_sessions
411                .write()
412                .entry(session.room_id().to_owned())
413                .or_default()
414                .insert(
415                    session.session_id().to_owned(),
416                    serde_json::to_string(&pickle)
417                        .expect("Pickle pickle data should serialize to json"),
418                );
419        }
420        Ok(())
421    }
422
423    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>> {
424        let device_keys = self.get_own_device().await?.as_device_keys().clone();
425
426        if let Some(pickles) = self.sessions.read().get(sender_key) {
427            let mut sessions: Vec<Session> = Vec::new();
428            for serialized_pickle in pickles.values() {
429                let pickle: PickledSession = serde_json::from_str(serialized_pickle.as_str())
430                    .expect("Pickle pickle deserialization should work");
431                let session = Session::from_pickle(device_keys.clone(), pickle)
432                    .expect("Expect from pickle to always work");
433                sessions.push(session);
434            }
435            Ok(Some(sessions))
436        } else {
437            Ok(None)
438        }
439    }
440
441    async fn get_inbound_group_session(
442        &self,
443        room_id: &RoomId,
444        session_id: &str,
445    ) -> Result<Option<InboundGroupSession>> {
446        let pickle: Option<PickledInboundGroupSession> = self
447            .inbound_group_sessions
448            .read()
449            .get(room_id)
450            .and_then(|m| m.get(session_id))
451            .and_then(|ser| {
452                serde_json::from_str(ser).expect("Pickle pickle deserialization should work")
453            });
454
455        Ok(pickle.map(|p| {
456            InboundGroupSession::from_pickle(p).expect("Expect from pickle to always work")
457        }))
458    }
459
460    async fn get_withheld_info(
461        &self,
462        room_id: &RoomId,
463        session_id: &str,
464    ) -> Result<Option<RoomKeyWithheldEntry>> {
465        Ok(self
466            .direct_withheld_info
467            .read()
468            .get(room_id)
469            .and_then(|e| Some(e.get(session_id)?.to_owned())))
470    }
471
472    async fn get_withheld_sessions_by_room_id(
473        &self,
474        room_id: &RoomId,
475    ) -> crate::store::Result<Vec<RoomKeyWithheldEntry>, Self::Error> {
476        Ok(self
477            .direct_withheld_info
478            .read()
479            .get(room_id)
480            .map(|e| e.values().cloned().collect())
481            .unwrap_or_default())
482    }
483
484    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
485        let inbounds = self
486            .inbound_group_sessions
487            .read()
488            .values()
489            .flat_map(HashMap::values)
490            .map(|ser| {
491                let pickle: PickledInboundGroupSession =
492                    serde_json::from_str(ser).expect("Pickle deserialization should work");
493                InboundGroupSession::from_pickle(pickle).expect("Expect from pickle to always work")
494            })
495            .collect();
496        Ok(inbounds)
497    }
498
499    async fn inbound_group_session_counts(
500        &self,
501        backup_version: Option<&str>,
502    ) -> Result<RoomKeyCounts> {
503        let backed_up = if let Some(backup_version) = backup_version {
504            self.get_inbound_group_sessions_and_backed_up_to()
505                .await?
506                .into_iter()
507                // Count the sessions backed up in the required backup
508                .filter(|(_, o)| o.as_ref().is_some_and(|o| o.as_str() == backup_version))
509                .count()
510        } else {
511            // We asked about a nonexistent backup version - this doesn't make
512            // much sense, but we can easily answer that nothing is backed up in
513            // this nonexistent backup.
514            0
515        };
516
517        let total = self.inbound_group_sessions.read().values().map(HashMap::len).sum();
518        Ok(RoomKeyCounts { total, backed_up })
519    }
520
521    async fn get_inbound_group_sessions_by_room_id(
522        &self,
523        room_id: &RoomId,
524    ) -> Result<Vec<InboundGroupSession>> {
525        let inbounds = match self.inbound_group_sessions.read().get(room_id) {
526            None => Vec::new(),
527            Some(v) => v
528                .values()
529                .map(|ser| {
530                    let pickle: PickledInboundGroupSession =
531                        serde_json::from_str(ser).expect("Pickle deserialization should work");
532                    InboundGroupSession::from_pickle(pickle)
533                        .expect("Expect from pickle to always work")
534                })
535                .collect(),
536        };
537        Ok(inbounds)
538    }
539
540    async fn get_inbound_group_sessions_for_device_batch(
541        &self,
542        sender_key: Curve25519PublicKey,
543        sender_data_type: SenderDataType,
544        after_session_id: Option<String>,
545        limit: usize,
546    ) -> Result<Vec<InboundGroupSession>> {
547        // First, find all InboundGroupSessions, filtering for those that match
548        // the device and sender_data type.
549        let mut sessions: Vec<_> = self
550            .get_inbound_group_sessions()
551            .await?
552            .into_iter()
553            .filter(|session: &InboundGroupSession| {
554                session.creator_info.curve25519_key == sender_key
555                    && session.sender_data.to_type() == sender_data_type
556            })
557            .collect();
558
559        // Then, sort the sessions in order of ascending session ID...
560        sessions.sort_by_key(|s| s.session_id().to_owned());
561
562        // Figure out where in the array to start returning results from
563        let start_index = {
564            match after_session_id {
565                None => 0,
566                Some(id) => {
567                    // We're looking for the first session with a session ID
568                    // strictly after `id`; if there are none, the end of the
569                    // array.
570                    sessions
571                        .iter()
572                        .position(|session| session.session_id() > id.as_str())
573                        .unwrap_or(sessions.len())
574                }
575            }
576        };
577
578        // Return up to `limit` items from the array, starting from
579        // `start_index`
580        Ok(sessions.drain(start_index..).take(limit).collect())
581    }
582
583    async fn inbound_group_sessions_for_backup(
584        &self,
585        backup_version: &str,
586        limit: usize,
587    ) -> Result<Vec<InboundGroupSession>> {
588        Ok(self
589            .get_inbound_group_sessions_and_backed_up_to()
590            .await?
591            .into_iter()
592            .filter_map(|(session, backed_up_to)| {
593                if let Some(ref existing_version) = backed_up_to
594                    && existing_version.as_str() == backup_version
595                {
596                    // This session is already backed up in the required backup
597                    None
598                } else {
599                    // It's not backed up, or it's backed up in a different
600                    // backup
601                    Some(session)
602                }
603            })
604            .take(limit)
605            .collect())
606    }
607
608    async fn mark_inbound_group_sessions_as_backed_up(
609        &self,
610        backup_version: &str,
611        room_and_session_ids: &[(&RoomId, &str)],
612    ) -> Result<()> {
613        for &(room_id, session_id) in room_and_session_ids {
614            let session = self.get_inbound_group_session(room_id, session_id).await?;
615
616            if let Some(session) = session {
617                session.mark_as_backed_up();
618
619                self.inbound_group_sessions_backed_up_to
620                    .write()
621                    .entry(room_id.to_owned())
622                    .or_default()
623                    .insert(session_id.to_owned(), BackupVersion::from(backup_version));
624
625                // Save it back
626                let updated_pickle = session.pickle().await;
627
628                self.inbound_group_sessions.write().entry(room_id.to_owned()).or_default().insert(
629                    session_id.to_owned(),
630                    serde_json::to_string(&updated_pickle)
631                        .expect("Pickle serialization should work"),
632                );
633            }
634        }
635
636        Ok(())
637    }
638
639    async fn reset_backup_state(&self) -> Result<()> {
640        // Nothing to do here, because we remember which backup versions we
641        // backed up to in `mark_inbound_group_sessions_as_backed_up`, so we
642        // don't need to reset anything here because the required version is
643        // passed in to `inbound_group_sessions_for_backup`, and we can compare
644        // against the version we stored.
645
646        Ok(())
647    }
648
649    async fn load_backup_keys(&self) -> Result<BackupKeys> {
650        Ok(self.backup_keys.read().await.to_owned())
651    }
652
653    async fn load_dehydrated_device_pickle_key(&self) -> Result<Option<DehydratedDeviceKey>> {
654        Ok(self.dehydrated_device_pickle_key.read().await.to_owned())
655    }
656
657    async fn delete_dehydrated_device_pickle_key(&self) -> Result<()> {
658        let mut lock = self.dehydrated_device_pickle_key.write().await;
659        *lock = None;
660        Ok(())
661    }
662
663    async fn get_outbound_group_session(
664        &self,
665        room_id: &RoomId,
666    ) -> Result<Option<OutboundGroupSession>> {
667        Ok(self.outbound_group_sessions.read().get(room_id).cloned())
668    }
669
670    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>> {
671        Ok(self.tracked_users.read().values().cloned().collect())
672    }
673
674    async fn save_tracked_users(&self, tracked_users: &[(&UserId, bool)]) -> Result<()> {
675        self.tracked_users.write().extend(tracked_users.iter().map(|(user_id, dirty)| {
676            let user_id: OwnedUserId = user_id.to_owned().into();
677            (user_id.clone(), TrackedUser { user_id, dirty: *dirty })
678        }));
679        Ok(())
680    }
681
682    async fn get_device(
683        &self,
684        user_id: &UserId,
685        device_id: &DeviceId,
686    ) -> Result<Option<DeviceData>> {
687        Ok(self.devices.get(user_id, device_id))
688    }
689
690    async fn get_user_devices(
691        &self,
692        user_id: &UserId,
693    ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
694        Ok(self.devices.user_devices(user_id))
695    }
696
697    async fn get_own_device(&self) -> Result<DeviceData> {
698        let account =
699            self.get_static_account().expect("Expect account to exist when getting own device");
700
701        Ok(self
702            .devices
703            .get(&account.user_id, &account.device_id)
704            .expect("Invalid state: Should always have a own device"))
705    }
706
707    async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentityData>> {
708        let serialized = self.identities.read().get(user_id).cloned();
709        match serialized {
710            None => Ok(None),
711            Some(serialized) => {
712                let id: UserIdentityData = serde_json::from_str(serialized.as_str())
713                    .expect("Only valid serialized identity are saved");
714                Ok(Some(id))
715            }
716        }
717    }
718
719    async fn is_message_known(&self, message_hash: &crate::olm::OlmMessageHash) -> Result<bool> {
720        Ok(self
721            .olm_hashes
722            .write()
723            .entry(message_hash.sender_key.to_owned())
724            .or_default()
725            .contains(&message_hash.hash))
726    }
727
728    async fn get_outgoing_secret_requests(
729        &self,
730        request_id: &TransactionId,
731    ) -> Result<Option<GossipRequest>> {
732        Ok(self.outgoing_key_requests.read().get(request_id).cloned())
733    }
734
735    async fn get_secret_request_by_info(
736        &self,
737        key_info: &SecretInfo,
738    ) -> Result<Option<GossipRequest>> {
739        let key_info_string = encode_key_info(key_info);
740
741        Ok(self
742            .key_requests_by_info
743            .read()
744            .get(&key_info_string)
745            .and_then(|i| self.outgoing_key_requests.read().get(i).cloned()))
746    }
747
748    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
749        Ok(self
750            .outgoing_key_requests
751            .read()
752            .values()
753            .filter(|req| !req.sent_out)
754            .cloned()
755            .collect())
756    }
757
758    async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> {
759        let req = self.outgoing_key_requests.write().remove(request_id);
760        if let Some(i) = req {
761            let key_info_string = encode_key_info(&i.info);
762            self.key_requests_by_info.write().remove(&key_info_string);
763        }
764
765        Ok(())
766    }
767
768    async fn get_secrets_from_inbox(
769        &self,
770        secret_name: &SecretName,
771    ) -> Result<Vec<Zeroizing<String>>> {
772        Ok(self.secret_inbox.write().entry(secret_name.to_string()).or_default().to_owned())
773    }
774
775    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<()> {
776        self.secret_inbox.write().remove(secret_name.as_str());
777
778        Ok(())
779    }
780
781    async fn get_room_settings(&self, room_id: &RoomId) -> Result<Option<RoomSettings>> {
782        Ok(self.room_settings.read().get(room_id).cloned())
783    }
784
785    async fn get_received_room_key_bundle_data(
786        &self,
787        room_id: &RoomId,
788        user_id: &UserId,
789    ) -> Result<Option<StoredRoomKeyBundleData>> {
790        let guard = self.room_key_bundles.read();
791
792        let result = guard.get(room_id).and_then(|bundles| bundles.get(user_id).cloned());
793
794        Ok(result)
795    }
796
797    async fn get_pending_key_bundle_details_for_room(
798        &self,
799        room_id: &RoomId,
800    ) -> Result<Option<RoomPendingKeyBundleDetails>> {
801        Ok(self.rooms_pending_key_bundle.read().get(room_id).cloned())
802    }
803
804    async fn get_all_rooms_pending_key_bundles(&self) -> Result<Vec<RoomPendingKeyBundleDetails>> {
805        Ok(self.rooms_pending_key_bundle.read().values().cloned().collect())
806    }
807
808    async fn has_downloaded_all_room_keys(&self, room_id: &RoomId) -> Result<bool> {
809        let guard = self.room_key_backups_fully_downloaded.read();
810        Ok(guard.contains(room_id))
811    }
812
813    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>> {
814        Ok(self.custom_values.read().get(key).cloned())
815    }
816
817    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<()> {
818        self.custom_values.write().insert(key.to_owned(), value);
819        Ok(())
820    }
821
822    async fn remove_custom_value(&self, key: &str) -> Result<()> {
823        self.custom_values.write().remove(key);
824        Ok(())
825    }
826
827    async fn try_take_leased_lock(
828        &self,
829        lease_duration_ms: u32,
830        key: &str,
831        holder: &str,
832    ) -> Result<Option<CrossProcessLockGeneration>> {
833        Ok(try_take_leased_lock(&mut self.leases.write(), lease_duration_ms, key, holder))
834    }
835
836    async fn get_size(&self) -> Result<Option<usize>> {
837        Ok(None)
838    }
839}
840
841#[cfg(test)]
842mod tests {
843    use std::collections::HashMap;
844
845    use matrix_sdk_test::async_test;
846    use ruma::{RoomId, room_id, user_id};
847    use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
848
849    use super::SessionId;
850    use crate::{
851        DeviceData,
852        identities::device::testing::get_device,
853        olm::{
854            Account, InboundGroupSession, OlmMessageHash, PrivateCrossSigningIdentity, SenderData,
855            tests::get_account_and_session_test_helper,
856        },
857        store::{
858            CryptoStore,
859            memorystore::MemoryStore,
860            types::{Changes, DeviceChanges, PendingChanges},
861        },
862    };
863
864    #[async_test]
865    async fn test_session_store() {
866        let (account, session) = get_account_and_session_test_helper();
867        let own_device = DeviceData::from_account(&account);
868        let store = MemoryStore::new();
869
870        assert!(store.load_account().await.unwrap().is_none());
871
872        store
873            .save_changes(Changes {
874                devices: DeviceChanges { new: vec![own_device], ..Default::default() },
875                ..Default::default()
876            })
877            .await
878            .unwrap();
879        store.save_pending_changes(PendingChanges { account: Some(account) }).await.unwrap();
880
881        store
882            .save_changes(Changes { sessions: (vec![session.clone()]), ..Default::default() })
883            .await
884            .unwrap();
885
886        let sessions = store.get_sessions(&session.sender_key.to_base64()).await.unwrap().unwrap();
887
888        let loaded_session = &sessions[0];
889
890        assert_eq!(&session, loaded_session);
891    }
892
893    #[async_test]
894    async fn test_inbound_group_session_store() {
895        let (account, _) = get_account_and_session_test_helper();
896        let room_id = room_id!("!test:localhost");
897        let curve_key = "Nn0L2hkcCMFKqynTjyGsJbth7QrVmX3lbrksMkrGOAw";
898
899        let (outbound, _) = account.create_group_session_pair_with_defaults(room_id).await;
900        let inbound = InboundGroupSession::new(
901            Curve25519PublicKey::from_base64(curve_key).unwrap(),
902            Ed25519PublicKey::from_base64("ee3Ek+J2LkkPmjGPGLhMxiKnhiX//xcqaVL4RP6EypE").unwrap(),
903            room_id,
904            &outbound.session_key().await,
905            SenderData::unknown(),
906            None,
907            outbound.settings().algorithm.to_owned(),
908            None,
909            false,
910        )
911        .unwrap();
912
913        let store = MemoryStore::new();
914        store.save_inbound_group_sessions(vec![inbound.clone()], None).await.unwrap();
915
916        let loaded_session =
917            store.get_inbound_group_session(room_id, outbound.session_id()).await.unwrap().unwrap();
918        assert_eq!(inbound, loaded_session);
919    }
920
921    #[async_test]
922    async fn test_backing_up_marks_sessions_as_backed_up() {
923        // Given there are 2 sessions
924        let room_id = room_id!("!test:localhost");
925        let (store, sessions) = store_with_sessions(2, room_id).await;
926
927        // When I mark them as backed up
928        mark_backed_up(&store, room_id, "bkp1", &sessions).await;
929
930        // Then their backed_up_to field is set
931        let but = backed_up_tos(&store).await;
932        assert_eq!(but[sessions[0].session_id()], "bkp1");
933        assert_eq!(but[sessions[1].session_id()], "bkp1");
934    }
935
936    #[async_test]
937    async fn test_backing_up_a_second_set_of_sessions_updates_their_backup_order() {
938        // Given there are 3 sessions
939        let room_id = room_id!("!test:localhost");
940        let (store, sessions) = store_with_sessions(3, room_id).await;
941
942        // When I mark 0 and 1 as backed up in bkp1
943        mark_backed_up(&store, room_id, "bkp1", &sessions[..2]).await;
944
945        // And 1 and 2 as backed up in bkp2
946        mark_backed_up(&store, room_id, "bkp2", &sessions[1..]).await;
947
948        // Then 0 is backed up in bkp1 and the 1 and 2 are backed up in bkp2
949        let but = backed_up_tos(&store).await;
950        assert_eq!(but[sessions[0].session_id()], "bkp1");
951        assert_eq!(but[sessions[1].session_id()], "bkp2");
952        assert_eq!(but[sessions[2].session_id()], "bkp2");
953    }
954
955    #[async_test]
956    async fn test_backing_up_again_to_the_same_version_has_no_effect() {
957        // Given there are 3 sessions
958        let room_id = room_id!("!test:localhost");
959        let (store, sessions) = store_with_sessions(3, room_id).await;
960
961        // When I mark the first two as backed up in the first backup
962        mark_backed_up(&store, room_id, "bkp1", &sessions[..2]).await;
963
964        // And the last 2 as backed up in the same backup version
965        mark_backed_up(&store, room_id, "bkp1", &sessions[1..]).await;
966
967        // Then they all get the same backed_up_to value
968        let but = backed_up_tos(&store).await;
969        assert_eq!(but[sessions[0].session_id()], "bkp1");
970        assert_eq!(but[sessions[1].session_id()], "bkp1");
971        assert_eq!(but[sessions[2].session_id()], "bkp1");
972    }
973
974    #[async_test]
975    async fn test_backing_up_to_an_old_backup_version_can_increase_backed_up_to() {
976        // Given we have backed up some sessions to 2 backup versions, an older
977        // and a newer
978        let room_id = room_id!("!test:localhost");
979        let (store, sessions) = store_with_sessions(4, room_id).await;
980        mark_backed_up(&store, room_id, "older_bkp", &sessions[..2]).await;
981        mark_backed_up(&store, room_id, "newer_bkp", &sessions[1..2]).await;
982
983        // When I ask to back up the un-backed-up ones to the older backup
984        mark_backed_up(&store, room_id, "older_bkp", &sessions[2..]).await;
985
986        // Then each session lists the backup it was most recently included in
987        let but = backed_up_tos(&store).await;
988        assert_eq!(but[sessions[0].session_id()], "older_bkp");
989        assert_eq!(but[sessions[1].session_id()], "newer_bkp");
990        assert_eq!(but[sessions[2].session_id()], "older_bkp");
991        assert_eq!(but[sessions[3].session_id()], "older_bkp");
992    }
993
994    #[async_test]
995    async fn test_backing_up_to_an_old_backup_version_overwrites_a_newer_one() {
996        // Given we have backed up to 2 backup versions, an older and a newer
997        let room_id = room_id!("!test:localhost");
998        let (store, sessions) = store_with_sessions(4, room_id).await;
999        mark_backed_up(&store, room_id, "older_bkp", &sessions).await;
1000        // Sanity: they are backed up in order number 1
1001        assert_eq!(backed_up_tos(&store).await[sessions[0].session_id()], "older_bkp");
1002        mark_backed_up(&store, room_id, "newer_bkp", &sessions).await;
1003        // Sanity: they are backed up in order number 2
1004        assert_eq!(backed_up_tos(&store).await[sessions[0].session_id()], "newer_bkp");
1005
1006        // When I ask to back up some to the older version
1007        mark_backed_up(&store, room_id, "older_bkp", &sessions[..2]).await;
1008
1009        // Then older backup overwrites: we don't consider the order here at all
1010        let but = backed_up_tos(&store).await;
1011        assert_eq!(but[sessions[0].session_id()], "older_bkp");
1012        assert_eq!(but[sessions[1].session_id()], "older_bkp");
1013        assert_eq!(but[sessions[2].session_id()], "newer_bkp");
1014        assert_eq!(but[sessions[3].session_id()], "newer_bkp");
1015    }
1016
1017    #[async_test]
1018    async fn test_not_backed_up_sessions_are_eligible_for_backup() {
1019        // Given there are 4 sessions, 2 of which are already backed up
1020        let room_id = room_id!("!test:localhost");
1021        let (store, sessions) = store_with_sessions(4, room_id).await;
1022        mark_backed_up(&store, room_id, "bkp1", &sessions[..2]).await;
1023
1024        // When I ask which to back up
1025        let mut to_backup = store
1026            .inbound_group_sessions_for_backup("bkp1", 10)
1027            .await
1028            .expect("Failed to ask for sessions to backup");
1029        to_backup.sort_by_key(|s| s.session_id().to_owned());
1030
1031        // Then I am told the last 2 only
1032        assert_eq!(to_backup, &[sessions[2].clone(), sessions[3].clone()]);
1033    }
1034
1035    #[async_test]
1036    async fn test_all_sessions_are_eligible_for_backup_if_version_is_unknown() {
1037        // Given there are 4 sessions, 2 of which are already backed up in bkp1
1038        let room_id = room_id!("!test:localhost");
1039        let (store, sessions) = store_with_sessions(4, room_id).await;
1040        mark_backed_up(&store, room_id, "bkp1", &sessions[..2]).await;
1041
1042        // When I ask which to back up in an unknown version
1043        let mut to_backup = store
1044            .inbound_group_sessions_for_backup("unknown_bkp", 10)
1045            .await
1046            .expect("Failed to ask for sessions to backup");
1047        to_backup.sort_by_key(|s| s.session_id().to_owned());
1048
1049        // Then I am told to back up all of them
1050        assert_eq!(
1051            to_backup,
1052            &[sessions[0].clone(), sessions[1].clone(), sessions[2].clone(), sessions[3].clone()]
1053        );
1054    }
1055
1056    #[async_test]
1057    async fn test_sessions_backed_up_to_a_later_version_are_eligible_for_backup() {
1058        // Given there are 4 sessions, some backed up to three different
1059        // versions
1060        let room_id = room_id!("!test:localhost");
1061        let (store, sessions) = store_with_sessions(4, room_id).await;
1062        mark_backed_up(&store, room_id, "bkp0", &sessions[..1]).await;
1063        mark_backed_up(&store, room_id, "bkp1", &sessions[1..2]).await;
1064        mark_backed_up(&store, room_id, "bkp2", &sessions[2..3]).await;
1065
1066        // When I ask which to back up in the middle version
1067        let mut to_backup = store
1068            .inbound_group_sessions_for_backup("bkp1", 10)
1069            .await
1070            .expect("Failed to ask for sessions to backup");
1071        to_backup.sort_by_key(|s| s.session_id().to_owned());
1072
1073        // Then I am told to back up everything not in the version I asked about
1074        assert_eq!(
1075            to_backup,
1076            &[
1077                sessions[0].clone(), // Backed up in bkp0
1078                // sessions[1] is backed up in bkp1 already, which we asked
1079                // about
1080                sessions[2].clone(), // Backed up in bkp2
1081                sessions[3].clone(), // Not backed up
1082            ]
1083        );
1084    }
1085
1086    #[async_test]
1087    async fn test_outbound_group_session_store() {
1088        // Given an outbound session
1089        let (account, _) = get_account_and_session_test_helper();
1090        let room_id = room_id!("!test:localhost");
1091        let (outbound, _) = account.create_group_session_pair_with_defaults(room_id).await;
1092
1093        // When we save it to the store
1094        let store = MemoryStore::new();
1095        store.save_outbound_group_sessions(vec![outbound.clone()]);
1096
1097        // Then we can get it out again
1098        let loaded_session = store.get_outbound_group_session(room_id).await.unwrap().unwrap();
1099        assert_eq!(
1100            serde_json::to_string(&outbound.pickle().await).unwrap(),
1101            serde_json::to_string(&loaded_session.pickle().await).unwrap()
1102        );
1103    }
1104
1105    #[async_test]
1106    async fn test_tracked_users_are_stored_once_per_user_id() {
1107        // Given a store containing 2 tracked users, both dirty
1108        let user1 = user_id!("@user1:s");
1109        let user2 = user_id!("@user2:s");
1110        let user3 = user_id!("@user3:s");
1111        let store = MemoryStore::new();
1112        store.save_tracked_users(&[(user1, true), (user2, true)]).await.unwrap();
1113
1114        // When we mark one as clean and add another
1115        store.save_tracked_users(&[(user2, false), (user3, false)]).await.unwrap();
1116
1117        // Then we can get them out again and their dirty flags are correct
1118        let loaded_tracked_users =
1119            store.load_tracked_users().await.expect("failed to load tracked users");
1120
1121        let tracked_contains = |user_id, dirty| {
1122            loaded_tracked_users.iter().any(|u| u.user_id == user_id && u.dirty == dirty)
1123        };
1124
1125        assert!(tracked_contains(user1, true));
1126        assert!(tracked_contains(user2, false));
1127        assert!(tracked_contains(user3, false));
1128        assert_eq!(loaded_tracked_users.len(), 3);
1129    }
1130
1131    #[async_test]
1132    async fn test_private_identity_store() {
1133        // Given a private identity
1134        let private_identity = PrivateCrossSigningIdentity::empty(user_id!("@u:s"));
1135
1136        // When we save it to the store
1137        let store = MemoryStore::new();
1138        store.save_private_identity(Some(private_identity.clone()));
1139
1140        // Then we can get it out again
1141        let loaded_identity =
1142            store.load_identity().await.expect("failed to load private identity").unwrap();
1143
1144        assert_eq!(loaded_identity.user_id(), "@u:s");
1145    }
1146
1147    #[async_test]
1148    async fn test_device_store() {
1149        let device = get_device();
1150        let store = MemoryStore::new();
1151
1152        store.save_devices(vec![device.clone()]);
1153
1154        let loaded_device =
1155            store.get_device(device.user_id(), device.device_id()).await.unwrap().unwrap();
1156
1157        assert_eq!(device, loaded_device);
1158
1159        let user_devices = store.get_user_devices(device.user_id()).await.unwrap();
1160
1161        assert_eq!(&**user_devices.keys().next().unwrap(), device.device_id());
1162        assert_eq!(user_devices.values().next().unwrap(), &device);
1163
1164        let loaded_device = user_devices.get(device.device_id()).unwrap();
1165
1166        assert_eq!(&device, loaded_device);
1167
1168        store.delete_devices(vec![device.clone()]);
1169        assert!(store.get_device(device.user_id(), device.device_id()).await.unwrap().is_none());
1170    }
1171
1172    #[async_test]
1173    async fn test_message_hash() {
1174        let store = MemoryStore::new();
1175
1176        let hash =
1177            OlmMessageHash { sender_key: "test_sender".to_owned(), hash: "test_hash".to_owned() };
1178
1179        let mut changes = Changes::default();
1180        changes.message_hashes.push(hash.clone());
1181
1182        assert!(!store.is_message_known(&hash).await.unwrap());
1183        store.save_changes(changes).await.unwrap();
1184        assert!(store.is_message_known(&hash).await.unwrap());
1185    }
1186
1187    #[async_test]
1188    async fn test_key_counts_of_empty_store_are_zero() {
1189        // Given an empty store
1190        let store = MemoryStore::new();
1191
1192        // When we count keys
1193        let key_counts = store.inbound_group_session_counts(Some("")).await.unwrap();
1194
1195        // Then the answer is zero
1196        assert_eq!(key_counts.total, 0);
1197        assert_eq!(key_counts.backed_up, 0);
1198    }
1199
1200    #[async_test]
1201    async fn test_counting_sessions_reports_the_number_of_sessions() {
1202        // Given a store with sessions
1203        let room_id = room_id!("!test:localhost");
1204        let (store, _) = store_with_sessions(4, room_id).await;
1205
1206        // When we count keys
1207        let key_counts = store.inbound_group_session_counts(Some("bkp")).await.unwrap();
1208
1209        // Then the answer equals the number of sessions we created
1210        assert_eq!(key_counts.total, 4);
1211        // And none are backed up
1212        assert_eq!(key_counts.backed_up, 0);
1213    }
1214
1215    #[async_test]
1216    async fn test_counting_backed_up_sessions_reports_the_number_backed_up_in_this_backup() {
1217        // Given a store with sessions, some backed up
1218        let room_id = room_id!("!test:localhost");
1219        let (store, sessions) = store_with_sessions(5, room_id).await;
1220        mark_backed_up(&store, room_id, "bkp", &sessions[..2]).await;
1221
1222        // When we count keys
1223        let key_counts = store.inbound_group_session_counts(Some("bkp")).await.unwrap();
1224
1225        // Then the answer equals the number of sessions we created
1226        assert_eq!(key_counts.total, 5);
1227        // And the backed_up count matches how many were backed up
1228        assert_eq!(key_counts.backed_up, 2);
1229    }
1230
1231    #[async_test]
1232    async fn test_counting_backed_up_sessions_for_null_backup_reports_zero() {
1233        // Given a store with sessions, some backed up
1234        let room_id = room_id!("!test:localhost");
1235        let (store, sessions) = store_with_sessions(4, room_id).await;
1236        mark_backed_up(&store, room_id, "bkp", &sessions[..2]).await;
1237
1238        // When we count keys, providing None as the backup version
1239        let key_counts = store.inbound_group_session_counts(None).await.unwrap();
1240
1241        // Then we ignore everything and just say zero
1242        assert_eq!(key_counts.backed_up, 0);
1243    }
1244
1245    #[async_test]
1246    async fn test_counting_backed_up_sessions_only_reports_sessions_in_the_version_specified() {
1247        // Given a store with sessions, backed up in several versions
1248        let room_id = room_id!("!test:localhost");
1249        let (store, sessions) = store_with_sessions(4, room_id).await;
1250        mark_backed_up(&store, room_id, "bkp1", &sessions[..2]).await;
1251        mark_backed_up(&store, room_id, "bkp2", &sessions[3..]).await;
1252
1253        // When we count keys for bkp2
1254        let key_counts = store.inbound_group_session_counts(Some("bkp2")).await.unwrap();
1255
1256        // Then the backed_up count reflects how many were backed up in bkp2
1257        // only
1258        assert_eq!(key_counts.backed_up, 1);
1259    }
1260
1261    /// Mark the supplied sessions as backed up in the supplied backup version
1262    async fn mark_backed_up(
1263        store: &MemoryStore,
1264        room_id: &RoomId,
1265        backup_version: &str,
1266        sessions: &[InboundGroupSession],
1267    ) {
1268        let rooms_and_ids: Vec<_> = sessions.iter().map(|s| (room_id, s.session_id())).collect();
1269
1270        store
1271            .mark_inbound_group_sessions_as_backed_up(backup_version, &rooms_and_ids)
1272            .await
1273            .expect("Failed to mark sessions as backed up");
1274    }
1275
1276    // Create a MemoryStore containing the supplied number of sessions.
1277    //
1278    // Sessions are returned in alphabetical order of session id.
1279    async fn store_with_sessions(
1280        num_sessions: usize,
1281        room_id: &RoomId,
1282    ) -> (MemoryStore, Vec<InboundGroupSession>) {
1283        let (account, _) = get_account_and_session_test_helper();
1284
1285        let mut sessions = Vec::with_capacity(num_sessions);
1286        for _ in 0..num_sessions {
1287            sessions.push(new_session(&account, room_id).await);
1288        }
1289        sessions.sort_by_key(|s| s.session_id().to_owned());
1290
1291        let store = MemoryStore::new();
1292        store.save_inbound_group_sessions(sessions.clone(), None).await.unwrap();
1293
1294        (store, sessions)
1295    }
1296
1297    // Create a new InboundGroupSession
1298    async fn new_session(account: &Account, room_id: &RoomId) -> InboundGroupSession {
1299        let curve_key = "Nn0L2hkcCMFKqynTjyGsJbth7QrVmX3lbrksMkrGOAw";
1300        let (outbound, _) = account.create_group_session_pair_with_defaults(room_id).await;
1301
1302        InboundGroupSession::new(
1303            Curve25519PublicKey::from_base64(curve_key).unwrap(),
1304            Ed25519PublicKey::from_base64("ee3Ek+J2LkkPmjGPGLhMxiKnhiX//xcqaVL4RP6EypE").unwrap(),
1305            room_id,
1306            &outbound.session_key().await,
1307            SenderData::unknown(),
1308            None,
1309            outbound.settings().algorithm.to_owned(),
1310            None,
1311            false,
1312        )
1313        .unwrap()
1314    }
1315
1316    /// Find the session_id and backed_up_to value for each of the sessions in
1317    /// the store.
1318    async fn backed_up_tos(store: &MemoryStore) -> HashMap<SessionId, String> {
1319        store
1320            .get_inbound_group_sessions_and_backed_up_to()
1321            .await
1322            .expect("Unable to get inbound group sessions and backup order")
1323            .iter()
1324            .map(|(s, o)| {
1325                (
1326                    s.session_id().to_owned(),
1327                    o.as_ref().map(|v| v.as_str().to_owned()).unwrap_or("".to_owned()),
1328                )
1329            })
1330            .collect()
1331    }
1332}
1333
1334#[cfg(test)]
1335mod integration_tests {
1336    use std::{
1337        collections::HashMap,
1338        sync::{Arc, Mutex, OnceLock},
1339    };
1340
1341    use async_trait::async_trait;
1342    use matrix_sdk_common::cross_process_lock::CrossProcessLockGeneration;
1343    use ruma::{
1344        DeviceId, OwnedDeviceId, RoomId, TransactionId, UserId, events::secret::request::SecretName,
1345    };
1346    use vodozemac::Curve25519PublicKey;
1347    use zeroize::Zeroizing;
1348
1349    use super::MemoryStore;
1350    use crate::{
1351        Account, DeviceData, GossipRequest, SecretInfo, Session, UserIdentityData,
1352        cryptostore_integration_tests, cryptostore_integration_tests_time,
1353        olm::{
1354            InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
1355            SenderDataType, StaticAccountData,
1356        },
1357        store::{
1358            CryptoStore,
1359            types::{
1360                BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts,
1361                RoomKeyWithheldEntry, RoomPendingKeyBundleDetails, RoomSettings,
1362                StoredRoomKeyBundleData, TrackedUser,
1363            },
1364        },
1365    };
1366
1367    /// Holds on to a MemoryStore during a test, and moves it back into STORES
1368    /// when this is dropped
1369    #[derive(Clone, Debug)]
1370    struct PersistentMemoryStore(Arc<MemoryStore>);
1371
1372    impl PersistentMemoryStore {
1373        fn new() -> Self {
1374            Self(Arc::new(MemoryStore::new()))
1375        }
1376
1377        fn get_static_account(&self) -> Option<StaticAccountData> {
1378            self.0.get_static_account()
1379        }
1380    }
1381
1382    /// Return a clone of the store for the test with the supplied name. Note:
1383    /// dropping this store won't destroy its data, since
1384    /// [PersistentMemoryStore] is a reference-counted smart pointer to an
1385    /// underlying [MemoryStore].
1386    async fn get_store(
1387        name: &str,
1388        _passphrase: Option<&str>,
1389        clear_data: bool,
1390    ) -> PersistentMemoryStore {
1391        // Holds on to one [PersistentMemoryStore] per test, so even if the test
1392        // drops the store, we keep its data alive. This simulates the behaviour
1393        // of the other stores, which keep their data in a real DB, allowing us
1394        // to test MemoryStore using the same code.
1395        static STORES: OnceLock<Mutex<HashMap<String, PersistentMemoryStore>>> = OnceLock::new();
1396        let stores = STORES.get_or_init(|| Mutex::new(HashMap::new()));
1397
1398        let mut stores = stores.lock().unwrap();
1399
1400        if clear_data {
1401            // Create a new PersistentMemoryStore
1402            let new_store = PersistentMemoryStore::new();
1403            stores.insert(name.to_owned(), new_store.clone());
1404            new_store
1405        } else {
1406            stores.entry(name.to_owned()).or_insert_with(PersistentMemoryStore::new).clone()
1407        }
1408    }
1409
1410    /// Forwards all methods to the underlying [MemoryStore].
1411    #[cfg_attr(target_family = "wasm", async_trait(?Send))]
1412    #[cfg_attr(not(target_family = "wasm"), async_trait)]
1413    impl CryptoStore for PersistentMemoryStore {
1414        type Error = <MemoryStore as CryptoStore>::Error;
1415
1416        async fn close(&self) -> Result<(), Self::Error> {
1417            self.0.close().await
1418        }
1419
1420        async fn reopen(&self) -> Result<(), Self::Error> {
1421            self.0.reopen().await
1422        }
1423
1424        async fn load_account(&self) -> Result<Option<Account>, Self::Error> {
1425            self.0.load_account().await
1426        }
1427
1428        async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>, Self::Error> {
1429            self.0.load_identity().await
1430        }
1431
1432        async fn save_changes(&self, changes: Changes) -> Result<(), Self::Error> {
1433            self.0.save_changes(changes).await
1434        }
1435
1436        async fn save_pending_changes(&self, changes: PendingChanges) -> Result<(), Self::Error> {
1437            self.0.save_pending_changes(changes).await
1438        }
1439
1440        async fn save_inbound_group_sessions(
1441            &self,
1442            sessions: Vec<InboundGroupSession>,
1443            backed_up_to_version: Option<&str>,
1444        ) -> Result<(), Self::Error> {
1445            self.0.save_inbound_group_sessions(sessions, backed_up_to_version).await
1446        }
1447
1448        async fn get_sessions(
1449            &self,
1450            sender_key: &str,
1451        ) -> Result<Option<Vec<Session>>, Self::Error> {
1452            self.0.get_sessions(sender_key).await
1453        }
1454
1455        async fn get_inbound_group_session(
1456            &self,
1457            room_id: &RoomId,
1458            session_id: &str,
1459        ) -> Result<Option<InboundGroupSession>, Self::Error> {
1460            self.0.get_inbound_group_session(room_id, session_id).await
1461        }
1462
1463        async fn get_withheld_info(
1464            &self,
1465            room_id: &RoomId,
1466            session_id: &str,
1467        ) -> Result<Option<RoomKeyWithheldEntry>, Self::Error> {
1468            self.0.get_withheld_info(room_id, session_id).await
1469        }
1470
1471        async fn get_withheld_sessions_by_room_id(
1472            &self,
1473            room_id: &RoomId,
1474        ) -> Result<Vec<RoomKeyWithheldEntry>, Self::Error> {
1475            self.0.get_withheld_sessions_by_room_id(room_id).await
1476        }
1477
1478        async fn get_inbound_group_sessions(
1479            &self,
1480        ) -> Result<Vec<InboundGroupSession>, Self::Error> {
1481            self.0.get_inbound_group_sessions().await
1482        }
1483
1484        async fn inbound_group_session_counts(
1485            &self,
1486            backup_version: Option<&str>,
1487        ) -> Result<RoomKeyCounts, Self::Error> {
1488            self.0.inbound_group_session_counts(backup_version).await
1489        }
1490
1491        async fn get_inbound_group_sessions_by_room_id(
1492            &self,
1493            room_id: &RoomId,
1494        ) -> Result<Vec<InboundGroupSession>, Self::Error> {
1495            self.0.get_inbound_group_sessions_by_room_id(room_id).await
1496        }
1497
1498        async fn get_inbound_group_sessions_for_device_batch(
1499            &self,
1500            sender_key: Curve25519PublicKey,
1501            sender_data_type: SenderDataType,
1502            after_session_id: Option<String>,
1503            limit: usize,
1504        ) -> Result<Vec<InboundGroupSession>, Self::Error> {
1505            self.0
1506                .get_inbound_group_sessions_for_device_batch(
1507                    sender_key,
1508                    sender_data_type,
1509                    after_session_id,
1510                    limit,
1511                )
1512                .await
1513        }
1514
1515        async fn inbound_group_sessions_for_backup(
1516            &self,
1517            backup_version: &str,
1518            limit: usize,
1519        ) -> Result<Vec<InboundGroupSession>, Self::Error> {
1520            self.0.inbound_group_sessions_for_backup(backup_version, limit).await
1521        }
1522
1523        async fn mark_inbound_group_sessions_as_backed_up(
1524            &self,
1525            backup_version: &str,
1526            room_and_session_ids: &[(&RoomId, &str)],
1527        ) -> Result<(), Self::Error> {
1528            self.0
1529                .mark_inbound_group_sessions_as_backed_up(backup_version, room_and_session_ids)
1530                .await
1531        }
1532
1533        async fn reset_backup_state(&self) -> Result<(), Self::Error> {
1534            self.0.reset_backup_state().await
1535        }
1536
1537        async fn load_backup_keys(&self) -> Result<BackupKeys, Self::Error> {
1538            self.0.load_backup_keys().await
1539        }
1540
1541        async fn load_dehydrated_device_pickle_key(
1542            &self,
1543        ) -> Result<Option<DehydratedDeviceKey>, Self::Error> {
1544            self.0.load_dehydrated_device_pickle_key().await
1545        }
1546
1547        async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error> {
1548            self.0.delete_dehydrated_device_pickle_key().await
1549        }
1550
1551        async fn get_outbound_group_session(
1552            &self,
1553            room_id: &RoomId,
1554        ) -> Result<Option<OutboundGroupSession>, Self::Error> {
1555            self.0.get_outbound_group_session(room_id).await
1556        }
1557
1558        async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>, Self::Error> {
1559            self.0.load_tracked_users().await
1560        }
1561
1562        async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<(), Self::Error> {
1563            self.0.save_tracked_users(users).await
1564        }
1565
1566        async fn get_device(
1567            &self,
1568            user_id: &UserId,
1569            device_id: &DeviceId,
1570        ) -> Result<Option<DeviceData>, Self::Error> {
1571            self.0.get_device(user_id, device_id).await
1572        }
1573
1574        async fn get_user_devices(
1575            &self,
1576            user_id: &UserId,
1577        ) -> Result<HashMap<OwnedDeviceId, DeviceData>, Self::Error> {
1578            self.0.get_user_devices(user_id).await
1579        }
1580
1581        async fn get_own_device(&self) -> Result<DeviceData, Self::Error> {
1582            self.0.get_own_device().await
1583        }
1584
1585        async fn get_user_identity(
1586            &self,
1587            user_id: &UserId,
1588        ) -> Result<Option<UserIdentityData>, Self::Error> {
1589            self.0.get_user_identity(user_id).await
1590        }
1591
1592        async fn is_message_known(
1593            &self,
1594            message_hash: &OlmMessageHash,
1595        ) -> Result<bool, Self::Error> {
1596            self.0.is_message_known(message_hash).await
1597        }
1598
1599        async fn get_outgoing_secret_requests(
1600            &self,
1601            request_id: &TransactionId,
1602        ) -> Result<Option<GossipRequest>, Self::Error> {
1603            self.0.get_outgoing_secret_requests(request_id).await
1604        }
1605
1606        async fn get_secret_request_by_info(
1607            &self,
1608            secret_info: &SecretInfo,
1609        ) -> Result<Option<GossipRequest>, Self::Error> {
1610            self.0.get_secret_request_by_info(secret_info).await
1611        }
1612
1613        async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>, Self::Error> {
1614            self.0.get_unsent_secret_requests().await
1615        }
1616
1617        async fn delete_outgoing_secret_requests(
1618            &self,
1619            request_id: &TransactionId,
1620        ) -> Result<(), Self::Error> {
1621            self.0.delete_outgoing_secret_requests(request_id).await
1622        }
1623
1624        async fn get_secrets_from_inbox(
1625            &self,
1626            secret_name: &SecretName,
1627        ) -> Result<Vec<Zeroizing<String>>, Self::Error> {
1628            self.0.get_secrets_from_inbox(secret_name).await
1629        }
1630
1631        async fn delete_secrets_from_inbox(
1632            &self,
1633            secret_name: &SecretName,
1634        ) -> Result<(), Self::Error> {
1635            self.0.delete_secrets_from_inbox(secret_name).await
1636        }
1637
1638        async fn get_room_settings(
1639            &self,
1640            room_id: &RoomId,
1641        ) -> Result<Option<RoomSettings>, Self::Error> {
1642            self.0.get_room_settings(room_id).await
1643        }
1644
1645        async fn get_received_room_key_bundle_data(
1646            &self,
1647            room_id: &RoomId,
1648            user_id: &UserId,
1649        ) -> crate::store::Result<Option<StoredRoomKeyBundleData>, Self::Error> {
1650            self.0.get_received_room_key_bundle_data(room_id, user_id).await
1651        }
1652
1653        async fn has_downloaded_all_room_keys(
1654            &self,
1655            room_id: &RoomId,
1656        ) -> Result<bool, Self::Error> {
1657            self.0.has_downloaded_all_room_keys(room_id).await
1658        }
1659
1660        async fn get_pending_key_bundle_details_for_room(
1661            &self,
1662            room_id: &RoomId,
1663        ) -> Result<Option<RoomPendingKeyBundleDetails>, Self::Error> {
1664            self.0.get_pending_key_bundle_details_for_room(room_id).await
1665        }
1666
1667        async fn get_all_rooms_pending_key_bundles(
1668            &self,
1669        ) -> Result<Vec<RoomPendingKeyBundleDetails>, Self::Error> {
1670            self.0.get_all_rooms_pending_key_bundles().await
1671        }
1672
1673        async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
1674            self.0.get_custom_value(key).await
1675        }
1676
1677        async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error> {
1678            self.0.set_custom_value(key, value).await
1679        }
1680
1681        async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error> {
1682            self.0.remove_custom_value(key).await
1683        }
1684
1685        async fn try_take_leased_lock(
1686            &self,
1687            lease_duration_ms: u32,
1688            key: &str,
1689            holder: &str,
1690        ) -> Result<Option<CrossProcessLockGeneration>, Self::Error> {
1691            self.0.try_take_leased_lock(lease_duration_ms, key, holder).await
1692        }
1693
1694        async fn next_batch_token(&self) -> Result<Option<String>, Self::Error> {
1695            self.0.next_batch_token().await
1696        }
1697
1698        async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
1699            self.0.get_size().await
1700        }
1701    }
1702
1703    cryptostore_integration_tests!();
1704    cryptostore_integration_tests_time!();
1705}