Skip to main content

matrix_sdk_crypto/store/
integration_tests.rs

1/// A macro which will run the CryptoStore integration test suite.
2///
3/// You need to provide a `async fn get_store() -> StoreResult<impl StateStore>`
4/// providing a fresh store on the same level you invoke the macro.
5///
6/// ## Usage Example:
7/// ```no_run
8/// # use matrix_sdk_crypto::store::{
9/// #    MemoryStore as MyCryptoStore,
10/// # };
11///
12/// #[cfg(test)]
13/// mod tests {
14///     use super::MyCryptoStore;
15///
16///     async fn get_store(
17///         name: &str,
18///         passphrase: Option<&str>,
19///         clear_data: bool,
20///     ) -> MyCryptoStore {
21///         let store = MyCryptoStore::new();
22///         if clear_data {
23///             store.clear();
24///         }
25///         store
26///     }
27///
28///     cryptostore_integration_tests!();
29/// }
30/// ```
31#[allow(unused_macros)]
32#[macro_export]
33macro_rules! cryptostore_integration_tests {
34    () => {
35        mod cryptostore_integration_tests {
36            use std::collections::{BTreeMap, HashMap, HashSet};
37            use std::ops::Deref;
38            use std::time::Duration;
39
40            use assert_matches::assert_matches;
41            use matrix_sdk_test::async_test;
42            use ruma::{
43                device_id, events::secret::request::SecretName, room_id, serde::Raw, owned_room_id,
44                to_device::DeviceIdOrAllDevices, user_id, DeviceId, RoomId, TransactionId, UserId,
45            };
46            use serde_json::value::to_raw_value;
47            use serde_json::json;
48            use matrix_sdk_common::deserialized_responses::WithheldCode;
49            use $crate::{
50                olm::{
51                    Account, Curve25519PublicKey, InboundGroupSession, OlmMessageHash,
52                    PrivateCrossSigningIdentity, SenderData, SenderDataType, Session
53                },
54                store::{
55                    types::{
56                        BackupDecryptionKey, Changes, DehydratedDeviceKey, DeviceChanges,
57                        IdentityChanges, PendingChanges, StoredRoomKeyBundleData, RoomKeyWithheldEntry,
58                        RoomSettings
59                    },
60                    CryptoStore, GossipRequest,
61                },
62                testing::{get_device, get_other_identity, get_own_identity},
63                types::{
64                    events::{
65                        dummy::DummyEventContent,
66                        olm_v1::{DecryptedSecretSendEvent, OlmV1Keys},
67                        room_key_request::MegolmV1AesSha2Content,
68                        room_key_withheld::{
69                            CommonWithheldCodeContent, MegolmV1AesSha2WithheldContent,
70                            RoomKeyWithheldContent,
71                        },
72                        room_key_bundle::RoomKeyBundleContent,
73                        secret_send::SecretSendContent,
74                        ToDeviceEvent,
75                    },
76                    requests::ToDeviceRequest,
77                    DeviceKeys,
78                    EventEncryptionAlgorithm,
79                },
80                vodozemac::megolm::{GroupSession, SessionConfig}, DeviceData, GossippedSecret, LocalTrust,  SecretInfo,
81                TrackedUser,
82            };
83            #[cfg(feature = "experimental-push-secrets")]
84            use $crate::types::events::secret_push::SecretPushContent;
85
86            use super::get_store;
87
88            fn alice_id() -> &'static UserId {
89                user_id!("@alice:example.org")
90            }
91
92            fn alice_device_id() -> &'static DeviceId {
93                device_id!("ALICEDEVICE")
94            }
95
96            fn bob_id() -> &'static UserId {
97                user_id!("@bob:example.org")
98            }
99
100            fn bob_device_id() -> &'static DeviceId {
101                device_id!("BOBDEVICE")
102            }
103
104            pub async fn get_loaded_store(name: &str) -> (Account, impl CryptoStore + use<>) {
105                let store = get_store(name, None, true).await;
106                let account = get_account();
107
108                store.save_pending_changes(PendingChanges { account: Some(account.deep_clone()), }).await.expect("Can't save account");
109
110                (account, store)
111            }
112
113            fn get_account() -> Account {
114                Account::with_device_id(alice_id(), alice_device_id())
115            }
116
117            pub(crate) async fn get_account_and_session() -> (Account, Session) {
118                let alice = Account::with_device_id(alice_id(), alice_device_id());
119                let mut bob = Account::with_device_id(bob_id(), bob_device_id());
120
121                bob.generate_one_time_keys(1);
122                let one_time_key = *bob.one_time_keys().values().next().unwrap();
123                let sender_key = bob.identity_keys().curve25519;
124                let session = alice.create_outbound_session_helper(
125                    Default::default(),
126                    sender_key,
127                    one_time_key,
128                    false,
129                    alice.device_keys(),
130                );
131
132                (alice, session)
133            }
134
135            #[async_test]
136            async fn test_save_account_via_generic_save() {
137                let store = get_store("save_account_via_generic", None, true).await;
138                assert!(store.get_static_account().is_none());
139                assert!(store.load_account().await.unwrap().is_none());
140                let account = get_account();
141
142                store
143                    .save_pending_changes(PendingChanges { account: Some(account) })
144                    .await
145                    .expect("Can't save account");
146                assert!(store.get_static_account().is_some());
147            }
148
149            #[async_test]
150            async fn test_save_account() {
151                let store = get_store("save_account", None, true).await;
152                assert!(store.get_static_account().is_none());
153                assert!(store.load_account().await.unwrap().is_none());
154                let account = get_account();
155
156                store
157                    .save_pending_changes(PendingChanges { account: Some(account) })
158                    .await
159                    .expect("Can't save account");
160                assert!(store.get_static_account().is_some());
161            }
162
163            #[async_test]
164            async fn test_load_account() {
165                let store = get_store("load_account", None, true).await;
166                let account = get_account();
167
168                store
169                    .save_pending_changes(PendingChanges { account: Some(account.deep_clone()) })
170                    .await
171                    .expect("Can't save account");
172
173                let loaded_account = store.load_account().await.expect("Can't load account");
174                let loaded_account = loaded_account.unwrap();
175
176                assert_eq!(account, loaded_account);
177            }
178
179            #[async_test]
180            async fn test_load_account_with_passphrase() {
181                let passphrase = Some("secret_passphrase");
182                let store = get_store("load_account_with_passphrase", passphrase, true).await;
183                let account = get_account();
184
185                store
186                    .save_pending_changes(PendingChanges { account: Some(account.deep_clone()) })
187                    .await
188                    .expect("Can't save account");
189
190                let loaded_account = store.load_account().await.expect("Can't load account");
191                let loaded_account = loaded_account.unwrap();
192
193                assert_eq!(account, loaded_account);
194            }
195
196            #[async_test]
197            async fn test_save_and_share_account() {
198                let store = get_store("save_and_share_account", None, true).await;
199                let mut account = get_account();
200
201                store
202                    .save_pending_changes(PendingChanges { account: Some(account.deep_clone()) })
203                    .await
204                    .expect("Can't save account");
205
206                account.mark_as_shared();
207                account.update_uploaded_key_count(50);
208
209                store
210                    .save_pending_changes(PendingChanges { account: Some(account.deep_clone()) })
211                    .await
212                    .expect("Can't save account");
213
214                let loaded_account = store.load_account().await.expect("Can't load account");
215                let loaded_account = loaded_account.unwrap();
216
217                assert_eq!(account, loaded_account);
218                assert_eq!(account.uploaded_key_count(), loaded_account.uploaded_key_count());
219            }
220
221            #[async_test]
222            async fn test_load_sessions() {
223                let store = get_store("load_sessions", None, true).await;
224                let (account, session) = get_account_and_session().await;
225                store
226                    .save_pending_changes(PendingChanges { account: Some(account.deep_clone()) })
227                    .await
228                    .expect("Can't save account");
229
230                let changes = Changes {
231                    sessions: vec![session.clone()],
232                    devices: DeviceChanges { new: vec![DeviceData::from_account(&account)], ..Default::default() },
233                    ..Default::default()
234                };
235
236                store.save_changes(changes).await.unwrap();
237
238                let sessions = store
239                    .get_sessions(&session.sender_key.to_base64())
240                    .await
241                    .expect("Can't load sessions")
242                    .unwrap();
243                let loaded_session = sessions.get(0).cloned().expect("We should find the session in the store.");
244
245                assert_eq!(&session, &loaded_session, "The loaded session should be the same one we put into the store.");
246            }
247
248            #[async_test]
249            async fn test_add_and_save_session() {
250                let store_name = "add_and_save_session";
251
252                // Given we created a session and saved it in the store
253                let (session_id, account, sender_key) = {
254                    let store = get_store(store_name, None, true).await;
255                    let (account, session) = get_account_and_session().await;
256                    let sender_key = session.sender_key.to_base64();
257                    let session_id = session.session_id().to_owned();
258
259                    store
260                        .save_pending_changes(PendingChanges {
261                            account: Some(account.deep_clone()),
262                        })
263                        .await
264                        .expect("Can't save account");
265                    store
266                        .save_changes(Changes {
267                            devices: DeviceChanges {
268                                new: vec![DeviceData::from_account(&account)],
269                                ..Default::default()
270                            },
271                            ..Default::default()
272                        })
273                        .await
274                        .unwrap();
275
276                    let changes = Changes { sessions: vec![session.clone()], ..Default::default() };
277                    store.save_changes(changes).await.unwrap();
278
279                    let sessions = store.get_sessions(&sender_key).await.unwrap().unwrap();
280                    let session = &sessions[0];
281
282                    assert_eq!(session_id, session.session_id());
283
284                    (session_id, account, sender_key)
285                };
286
287                // When we reload the store
288                let store = get_store(store_name, None, false).await;
289
290                // Then the same account and session info was reloaded
291                let loaded_account = store.load_account().await.unwrap().unwrap();
292                assert_eq!(account, loaded_account);
293
294                let sessions = store.get_sessions(&sender_key).await.unwrap().unwrap();
295                let session = &sessions[0];
296
297                assert_eq!(session_id, session.session_id());
298            }
299
300            #[async_test]
301            async fn test_load_outbound_group_session() {
302                let dir = "load_outbound_group_session";
303                let room_id = room_id!("!test:localhost");
304
305                // Given we saved an outbound group session
306                {
307                    let (account, store) = get_loaded_store(dir.clone()).await;
308                    assert!(
309                        store.get_outbound_group_session(&room_id).await.unwrap().is_none(),
310                        "Initially there should be no outbound group session"
311                    );
312
313                    let (session, _) =
314                        account.create_group_session_pair_with_defaults(&room_id).await;
315
316                    let user_id = user_id!("@example:localhost");
317                    let request = ToDeviceRequest::new(
318                        user_id,
319                        DeviceIdOrAllDevices::AllDevices,
320                        "m.dummy",
321                        Raw::from_json(to_raw_value(&DummyEventContent::new()).unwrap()),
322                    );
323
324                    session.add_request(TransactionId::new(), request.into(), Default::default());
325
326                    let changes = Changes {
327                        outbound_group_sessions: vec![session.clone()],
328                        ..Default::default()
329                    };
330
331                    store.save_changes(changes).await.expect("Can't save group session");
332                    assert!(
333                        store.get_outbound_group_session(&room_id).await.unwrap().is_some(),
334                        "Sanity: after we've saved one, there should be an outbound_group_session"
335                    );
336                }
337
338                // When we reload the account
339                let store = get_store(dir, None, false).await;
340                store.load_account().await.unwrap();
341
342                // Then the saved session is restored
343                assert!(
344                    store.get_outbound_group_session(&room_id).await.unwrap().is_some(),
345                    "The outbound_group_session should have been loaded"
346                );
347            }
348
349            /// Test that we can import an inbound group session via [`CryptoStore::save_changes`]
350            #[async_test]
351            async fn test_save_changes_save_inbound_group_session() {
352                let (account, store) = get_loaded_store("save_inbound_group_session").await;
353
354                let room_id = &room_id!("!test:localhost");
355                let (_, session) = account.create_group_session_pair_with_defaults(room_id).await;
356
357                let changes =
358                    Changes { inbound_group_sessions: vec![session], ..Default::default() };
359
360                store.save_changes(changes).await.expect("Can't save group session");
361            }
362
363            /// Test that we can import a backed-up group session via
364            /// [`CryptoStore::save_inbound_group_sessions`]
365            #[async_test]
366            async fn test_save_inbound_group_session_from_backup() {
367                let (account, store) =
368                    get_loaded_store("save_inbound_group_session_from_backup").await;
369
370                let room_id = &room_id!("!test:localhost");
371                let (_, session) = account.create_group_session_pair_with_defaults(room_id).await;
372
373                session.mark_as_backed_up();
374                store
375                    .save_inbound_group_sessions(vec![session.clone()], Some(&"bkpver1"))
376                    .await
377                    .expect("could not save sessions");
378
379                let loaded_session = store
380                    .get_inbound_group_session(&session.room_id, session.session_id())
381                    .await
382                    .expect("error when loading session")
383                    .expect("session not found in store");
384                assert_eq!(session, loaded_session);
385                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 1);
386                assert_eq!(store.inbound_group_session_counts(None).await.unwrap().total, 1);
387
388                // It should *not* be returned by a request for backup for the same backup version
389                let to_back_up = store.inbound_group_sessions_for_backup("bkpver1", 1).await.unwrap();
390                assert_eq!(to_back_up.len(), 0, "backup was returned by backup query");
391                assert_eq!(
392                    store.inbound_group_session_counts(Some(&"bkpver1")).await.unwrap().backed_up, 1,
393                    "backed_up count",
394                );
395            }
396
397            /// Test that the behaviour of a key imported from an *old* backup is correct
398            ///
399            /// This currently only works on the MemoryStore, so is ignored. The other stores
400            /// are waiting for more work on https://github.com/element-hq/element-web/issues/26892.
401            #[ignore]
402            #[async_test]
403            async fn test_save_inbound_group_session_from_old_backup() {
404                let (account, store) =
405                    get_loaded_store("save_inbound_group_session_from_old_backup").await;
406
407                let room_id = &room_id!("!test:localhost");
408                let (_, session) = account.create_group_session_pair_with_defaults(room_id).await;
409
410                session.mark_as_backed_up();
411                store
412                    .save_inbound_group_sessions(vec![session.clone()], Some(&"bkpver1"))
413                    .await
414                    .expect("could not save sessions");
415
416                // The session should be returned by a request for backup from a different backup version.
417                let to_back_up = store.inbound_group_sessions_for_backup("bkpver2", 1).await.unwrap();
418                assert_eq!(to_back_up, vec![session]);
419                assert_eq!(
420                    store.inbound_group_session_counts(Some(&"bkpver2")).await.unwrap().backed_up, 0,
421                    "backed_up count for backup version 2",
422                );
423            }
424
425            /// Test that we can import a not-backed-up group session via
426            /// [`CryptoStore::save_inbound_group_sessions`]
427            #[async_test]
428            async fn test_save_inbound_group_session_from_import() {
429                let (account, store) =
430                    get_loaded_store("save_inbound_group_session_from_import").await;
431
432                let room_id = &room_id!("!test:localhost");
433                let (_, session) = account.create_group_session_pair_with_defaults(room_id).await;
434
435                store
436                    .save_inbound_group_sessions(vec![session.clone()], None)
437                    .await
438                    .expect("could not save sessions");
439
440                let loaded_session = store
441                    .get_inbound_group_session(&session.room_id, session.session_id())
442                    .await
443                    .expect("error when loading session")
444                    .expect("session not found in store");
445                assert_eq!(session, loaded_session);
446                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 1);
447                assert_eq!(store.inbound_group_session_counts(None).await.unwrap().total, 1);
448                assert_eq!(store.inbound_group_session_counts(None).await.unwrap().backed_up, 0);
449
450                // It should be returned by a request for backup
451                let to_back_up = store.inbound_group_sessions_for_backup("bkpver1", 1).await.unwrap();
452                assert_eq!(to_back_up, vec![session]);
453            }
454
455            #[async_test]
456            async fn test_mark_inbound_group_sessions_as_backed_up() {
457                // Given a store exists with multiple unbacked-up sessions
458                let (account, store) =
459                    get_loaded_store("mark_inbound_group_sessions_as_backed_up").await;
460                let room_id = &room_id!("!test:localhost");
461                let mut sessions: Vec<InboundGroupSession> = Vec::with_capacity(10);
462                for _i in 0..10 {
463                    sessions.push(account.create_group_session_pair_with_defaults(room_id).await.1);
464                }
465                let changes = Changes { inbound_group_sessions: sessions.clone(), ..Default::default() };
466                store.save_changes(changes).await.expect("Can't save group session");
467                assert_eq!(store.inbound_group_sessions_for_backup("bkpver", 100).await.unwrap().len(), 10);
468
469                // When I mark some as backed up
470                store.mark_inbound_group_sessions_as_backed_up("bkpver", &[
471                    session_info(&sessions[1]),
472                    session_info(&sessions[3]),
473                    session_info(&sessions[5]),
474                    session_info(&sessions[7]),
475                    session_info(&sessions[9]),
476                ]).await.expect("Failed to mark sessions as backed up");
477
478                // And ask which still need backing up
479                let to_back_up = store.inbound_group_sessions_for_backup("bkpver", 10).await.unwrap();
480                let needs_backing_up = |i: usize| to_back_up.iter().any(|s| s.session_id() == sessions[i].session_id());
481
482                // Then the sessions we said were backed up no longer need backing up
483                assert!(!needs_backing_up(1));
484                assert!(!needs_backing_up(3));
485                assert!(!needs_backing_up(5));
486                assert!(!needs_backing_up(7));
487                assert!(!needs_backing_up(9));
488
489                // And the sessions we didn't mention still need backing up
490                assert!(needs_backing_up(0));
491                assert!(needs_backing_up(2));
492                assert!(needs_backing_up(4));
493                assert!(needs_backing_up(6));
494                assert!(needs_backing_up(8));
495                assert_eq!(to_back_up.len(), 5);
496            }
497
498            #[async_test]
499            async fn test_reset_inbound_group_session_for_backup() {
500                // Given a store exists where all sessions are backed up to backup_1
501                let (account, store) =
502                    get_loaded_store("reset_inbound_group_session_for_backup").await;
503                let room_id = &room_id!("!test:localhost");
504                let mut sessions: Vec<InboundGroupSession> = Vec::with_capacity(10);
505                for _ in 0..10 {
506                    sessions.push(account.create_group_session_pair_with_defaults(room_id).await.1);
507                }
508                let changes = Changes { inbound_group_sessions: sessions.clone(), ..Default::default() };
509                store.save_changes(changes).await.expect("Can't save group session");
510                assert_eq!(store.inbound_group_sessions_for_backup("backup_1", 100).await.unwrap().len(), 10);
511                store.mark_inbound_group_sessions_as_backed_up(
512                    "backup_1",
513                    &(0..10).map(|i| session_info(&sessions[i])).collect::<Vec<_>>(),
514                ).await.expect("Failed to mark sessions as backed up");
515
516                // Sanity: none need backing up to the same backup
517                {
518                    let to_back_up_old = store.inbound_group_sessions_for_backup("backup_1", 10).await.unwrap();
519                    assert_eq!(to_back_up_old.len(), 0);
520                }
521
522                // Some stores ignore backup_version and just reset when you tell them to. Tell
523                // them here.
524                store.reset_backup_state().await.expect("reset failed");
525
526                // When we ask what needs backing up to a different backup version
527                let to_back_up = store.inbound_group_sessions_for_backup("backup_02", 10).await.unwrap();
528
529                // Then the answer is everything
530                let needs_backing_up = |i: usize| to_back_up.iter().any(|s| s.session_id() == sessions[i].session_id());
531                assert!(needs_backing_up(0));
532                assert!(needs_backing_up(1));
533                assert!(needs_backing_up(8));
534                assert!(needs_backing_up(9));
535                assert_eq!(to_back_up.len(), 10);
536            }
537
538            #[async_test]
539            async fn test_load_inbound_group_session() {
540                let dir = "load_inbound_group_session";
541                let (account, store) = get_loaded_store(dir).await;
542                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 0);
543
544                let room_id = &room_id!("!test:localhost");
545                let (_, session) = account.create_group_session_pair_with_defaults(room_id).await;
546
547                let export = session.export().await;
548
549                let session = InboundGroupSession::from_export(&export).unwrap();
550
551                let changes =
552                    Changes { inbound_group_sessions: vec![session.clone()], ..Default::default() };
553
554                store.save_changes(changes).await.expect("Can't save group session");
555
556                drop(store);
557
558                let store = get_store(dir, None, false).await;
559
560                store.load_account().await.unwrap();
561
562                let loaded_session = store
563                    .get_inbound_group_session(&session.room_id, session.session_id())
564                    .await
565                    .unwrap()
566                    .unwrap();
567                assert_eq!(session, loaded_session);
568                loaded_session.export().await;
569
570                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 1);
571                assert_eq!(store.inbound_group_session_counts(None).await.unwrap().total, 1);
572            }
573
574            #[async_test]
575            async fn test_get_inbound_group_sessions_by_room_id_empty() {
576                let dir = "get_inbound_group_session_by_room_id_empty";
577                let (_, store) = get_loaded_store(dir).await;
578                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 0);
579
580                let room_id = &room_id!("!testing:localhost");
581                assert_eq!(store.get_inbound_group_sessions_by_room_id(room_id).await.unwrap().len(), 0);
582            }
583
584            #[async_test]
585            async fn test_get_inbound_group_sessions_by_room_id() {
586                let dir = "get_inbound_group_session_by_room_id";
587                let (account, store) = get_loaded_store(dir).await;
588                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 0);
589
590                let room_id = &room_id!("!testing:localhost");
591                let (_, session_1) = account.create_group_session_pair_with_defaults(room_id).await;
592                let (_, session_2) = account.create_group_session_pair_with_defaults(room_id).await;
593
594                let second_room_id = &room_id!("!other_room_testing:localhost");
595                let (_, session_3) = account.create_group_session_pair_with_defaults(second_room_id).await;
596
597                let mut sessions = vec![
598                    session_1,
599                    session_2,
600                    session_3
601                ];
602
603                let changes = Changes {
604                    inbound_group_sessions: sessions.clone(),
605                    ..Default::default()
606                };
607                store.save_changes(changes).await.expect("Can't save group session");
608
609                drop(store);
610
611                // The last session is in a different room, so should not be returned by
612                // get_inbound_group_sessions_by_room_id. Remove it from the list.
613                sessions.pop();
614
615                let store = get_store(dir, None, false).await;
616                // Make sure all the sessions are in the store
617                assert_eq!(store.get_inbound_group_sessions().await.unwrap().len(), 3);
618
619                store.load_account().await.unwrap();
620
621                let loaded_sessions = store
622                    .get_inbound_group_sessions_by_room_id(room_id)
623                    .await
624                    .unwrap();
625
626                assert_eq!(loaded_sessions.len(), 2);
627                assert_session_lists_eq(sessions, loaded_sessions, "room by id sessions");
628            }
629
630            #[async_test]
631            async fn test_fetch_inbound_group_sessions_for_device() {
632                // Given a store exists, containing inbound group sessions from different devices
633                let (account, store) =
634                    get_loaded_store("fetch_inbound_group_sessions_for_device").await;
635
636                let dev1 = Curve25519PublicKey::from_base64(
637                    "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4"
638                ).unwrap();
639                let dev2 = Curve25519PublicKey::from_base64(
640                    "LTpv2DGMhggPAXO02+7f68CNEp6A40F0Yl8B094Y8gc"
641                ).unwrap();
642
643                let dev_1_unknown_a = create_session(&account, &dev1, SenderDataType::UnknownDevice).await;
644                let dev_1_unknown_b = create_session(&account, &dev1, SenderDataType::UnknownDevice).await;
645
646                let dev_1_keys_a = create_session(&account, &dev1, SenderDataType::DeviceInfo).await;
647                let dev_1_keys_b = create_session(&account, &dev1, SenderDataType::DeviceInfo).await;
648                let dev_1_keys_c = create_session(&account, &dev1, SenderDataType::DeviceInfo).await;
649                let dev_1_keys_d = create_session(&account, &dev1, SenderDataType::DeviceInfo).await;
650
651                let dev_2_unknown = create_session(
652                    &account, &dev2, SenderDataType::UnknownDevice).await;
653
654                let dev_2_keys = create_session(
655                    &account, &dev2, SenderDataType::DeviceInfo).await;
656
657                let sessions = vec![
658                    dev_1_unknown_a.clone(),
659                    dev_1_unknown_b.clone(),
660                    dev_1_keys_a.clone(),
661                    dev_1_keys_b.clone(),
662                    dev_1_keys_c.clone(),
663                    dev_1_keys_d.clone(),
664                    dev_2_unknown.clone(),
665                    dev_2_keys.clone(),
666                ];
667
668                let changes = Changes {
669                    inbound_group_sessions: sessions,
670                    ..Default::default()
671                };
672                store.save_changes(changes).await.expect("Can't save group session");
673
674                // When we fetch the list of sessions for device 1, unknown
675                let sessions_1_u = store.get_inbound_group_sessions_for_device_batch(
676                    dev1,
677                    SenderDataType::UnknownDevice,
678                    None,
679                    10
680                ).await.expect("Failed to get sessions for dev1");
681
682                // Then the expected sessions are returned
683                assert_session_lists_eq(sessions_1_u, [dev_1_unknown_a, dev_1_unknown_b], "device 1 sessions");
684
685                // And when we ask for the list of sessions for device 2, with device keys
686                let sessions_2_d = store
687                    .get_inbound_group_sessions_for_device_batch(dev2, SenderDataType::DeviceInfo, None, 10)
688                    .await
689                    .expect("Failed to get sessions for dev2");
690
691                // Then the matching session is returned
692                assert_eq!(sessions_2_d, vec![dev_2_keys], "device 2 sessions");
693
694                // And we can fetch device 1, keys in batches.
695                // We call the batch function repeatedly, to ensure it terminates correctly.
696                let mut sessions_1_k = Vec::new();
697                let mut previous_last_session_id: Option<String> = None;
698                loop {
699                    let mut sessions_1_k_batch = store.get_inbound_group_sessions_for_device_batch(
700                        dev1,
701                        SenderDataType::DeviceInfo,
702                        previous_last_session_id,
703                        2
704                    ).await.expect("Failed to get batch 1");
705
706                    // If there are no results in the batch, we have reached the end of the results.
707                    let Some(last_session) = sessions_1_k_batch.last() else {
708                        break;
709                    };
710
711                    // Check that there are exactly two results in the batch
712                    assert_eq!(sessions_1_k_batch.len(), 2);
713
714                    previous_last_session_id = Some(last_session.session_id().to_owned());
715
716                    // Modify one of the results, to check that that doesn't break iteration
717                    let mut last_session = last_session.clone();
718                    last_session.sender_data = SenderData::unknown();
719                    store.save_inbound_group_sessions(vec![last_session], None).await.unwrap();
720
721                    sessions_1_k.append(&mut sessions_1_k_batch);
722                }
723
724                assert_session_lists_eq(
725                    sessions_1_k,
726                    [dev_1_keys_a, dev_1_keys_b, dev_1_keys_c, dev_1_keys_d],
727                    "device 1 batched results"
728                );
729            }
730
731            /// Assert that two lists of sessions are the same, modulo ordering.
732            ///
733            /// There is no requirement for `get_inbound_group_sessions_for_device_batch` to
734            /// return the results in a specific order. This helper ensures that the two lists
735            /// of inbound group sessions are equivalent, without worrying about the ordering.
736            fn assert_session_lists_eq<I, J>(actual: I, expected: J, message: &str)
737                where I: IntoIterator<Item = InboundGroupSession>, J: IntoIterator<Item = InboundGroupSession>
738            {
739                let sorter = |a: &InboundGroupSession, b: &InboundGroupSession| Ord::cmp(a.session_id(), b.session_id());
740
741                let mut actual = Vec::from_iter(actual);
742                actual.sort_unstable_by(sorter);
743                let mut expected = Vec::from_iter(expected);
744                expected.sort_unstable_by(sorter);
745                assert_eq!(actual, expected, "{}", message);
746            }
747
748            #[async_test]
749            async fn test_tracked_users() {
750                let dir = "test_tracked_users";
751                let (_account, store) = get_loaded_store(dir.clone()).await;
752
753                let alice = user_id!("@alice:example.org");
754                let bob = user_id!("@bob:example.org");
755                let candy = user_id!("@candy:example.org");
756
757                let loaded = store.load_tracked_users().await.unwrap();
758                assert!(loaded.is_empty(), "Initially there are no tracked users");
759
760                let users = vec![(alice, true), (bob, false)];
761                store.save_tracked_users(&users).await.unwrap();
762
763                let check_loaded_users = |loaded: Vec<TrackedUser>| {
764                    let loaded: HashMap<_, _> =
765                        loaded.into_iter().map(|u| (u.user_id.to_owned(), u)).collect();
766
767                    let loaded_alice =
768                        loaded.get(alice).expect("Alice should be in the store as a tracked user");
769                    let loaded_bob =
770                        loaded.get(bob).expect("Bob should be in the store as as tracked user");
771
772                    assert!(!loaded.contains_key(candy), "Candy shouldn't be part of the store");
773                    assert_eq!(loaded.len(), 2, "Candy shouldn't be part of the store");
774
775                    assert!(loaded_alice.dirty, "Alice should be considered to be dirty");
776                    assert!(!loaded_bob.dirty, "Bob should not be considered to be dirty");
777                };
778
779                let loaded = store.load_tracked_users().await.unwrap();
780                check_loaded_users(loaded);
781
782                drop(store);
783
784                let name = dir.clone();let store = get_store(name, None, false).await;
785                let loaded = store.load_tracked_users().await.unwrap();
786                check_loaded_users(loaded);
787            }
788
789            #[async_test]
790            async fn test_device_saving() {
791                let dir = "device_saving";
792                let (_account, store) = get_loaded_store(dir.clone()).await;
793
794                let alice_device_1 = DeviceData::from_account(&Account::with_device_id(
795                    "@alice:localhost".try_into().unwrap(),
796                    "FIRSTDEVICE".into(),
797                ));
798
799                let alice_device_2 = DeviceData::from_account(&Account::with_device_id(
800                    "@alice:localhost".try_into().unwrap(),
801                    "SECONDDEVICE".into(),
802                ));
803
804                let json = json!({
805                    "algorithms": ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
806                    "user_id": "@bob:localhost",
807                    "device_id": "BOBDEVICE",
808                    "extra_property": "somevalue",
809                    "keys": {
810                        "curve25519:BOBDEVICE": "n0zs7qnaPLLf/OTL+dDLcI5kaPexbUeQ8jLQ2q6sO0E",
811                        "ed25519:BOBDEVICE": "RrKiu4+5EHRBWY6Qj6OtQGC0txpmEeanOz2irEZ/IN4",
812                    },
813                    "signatures": {
814                        "@bob:localhost": {
815                            "ed25519:BOBDEVICE": "9NjPewVHfB7Ah32mJ+CBx64mVoiQ8gbh+/2pc9WfAgut/H0Kqd/bbpgJq9Pn518szaXcGqEq0DxDP6CABBX8CQ",
816                        },
817                    },
818                });
819
820                let bob_device_1_keys: DeviceKeys = serde_json::from_value(json).unwrap();
821                let bob_device_1 = DeviceData::new(bob_device_1_keys, LocalTrust::Unset);
822
823                let changes = Changes {
824                    devices: DeviceChanges {
825                        new: vec![alice_device_1.clone(), alice_device_2.clone(), bob_device_1.clone()],
826                        ..Default::default()
827                    },
828                    ..Default::default()
829                };
830
831                store.save_changes(changes).await.unwrap();
832
833                drop(store);
834
835                let store = get_store(dir, None, false).await;
836
837                store.load_account().await.unwrap();
838
839                let loaded_device = store
840                    .get_device(alice_device_1.user_id(), alice_device_1.device_id())
841                    .await
842                    .unwrap()
843                    .unwrap();
844
845                assert_eq!(alice_device_1, loaded_device);
846
847                for algorithm in loaded_device.algorithms() {
848                    assert!(alice_device_1.algorithms().contains(algorithm));
849                }
850                assert_eq!(alice_device_1.algorithms().len(), loaded_device.algorithms().len());
851                assert_eq!(alice_device_1.keys(), loaded_device.keys());
852
853                let user_devices = store.get_user_devices(alice_device_1.user_id()).await.unwrap();
854                assert_eq!(user_devices.len(), 2);
855
856                let bob_device = store
857                    .get_device(bob_device_1.user_id(), bob_device_1.device_id())
858                    .await
859                    .unwrap();
860
861                let bob_device_json = serde_json::to_value(bob_device).unwrap();
862                assert_eq!(bob_device_json["device_keys"]["extra_property"], json!("somevalue"));
863            }
864
865            #[async_test]
866            async fn test_device_deleting() {
867                let dir = "device_deleting";
868                let (_account, store) = get_loaded_store(dir.clone()).await;
869                let device = get_device();
870
871                let changes = Changes {
872                    devices: DeviceChanges { changed: vec![device.clone()], ..Default::default() },
873                    ..Default::default()
874                };
875
876                store.save_changes(changes).await.unwrap();
877
878                let changes = Changes {
879                    devices: DeviceChanges { deleted: vec![device.clone()], ..Default::default() },
880                    ..Default::default()
881                };
882
883                store.save_changes(changes).await.unwrap();
884                drop(store);
885
886                let store = get_store(dir, None, false).await;
887
888                store.load_account().await.unwrap();
889
890                let loaded_device =
891                    store.get_device(device.user_id(), device.device_id()).await.unwrap();
892
893                assert!(loaded_device.is_none());
894            }
895
896            #[async_test]
897            async fn test_user_saving() {
898                let dir = "user_saving";
899
900                let user_id = user_id!("@example:localhost");
901                let device_id: &DeviceId = "WSKKLTJZCL".into();
902
903                let store = get_store(dir, None, true).await;
904
905                let account = Account::with_device_id(&user_id, device_id);
906
907                store.save_pending_changes(PendingChanges { account: Some(account), })
908                    .await
909                    .expect("Can't save account");
910
911                let own_identity = get_own_identity();
912
913                let changes = Changes {
914                    identities: IdentityChanges {
915                        changed: vec![own_identity.clone().into()],
916                        ..Default::default()
917                    },
918                    ..Default::default()
919                };
920
921                store.save_changes(changes).await.expect("Can't save identity");
922
923                drop(store);
924
925                let store = get_store(dir, None, false).await;
926
927                store.load_account().await.unwrap();
928
929                let loaded_user =
930                    store.get_user_identity(own_identity.user_id()).await.unwrap().unwrap();
931
932                assert_eq!(loaded_user.master_key(), own_identity.master_key());
933                assert_eq!(loaded_user.self_signing_key(), own_identity.self_signing_key());
934                assert_eq!(loaded_user.own().unwrap().clone(), own_identity.clone());
935
936                let other_identity = get_other_identity();
937
938                let changes = Changes {
939                    identities: IdentityChanges {
940                        changed: vec![other_identity.clone().into()],
941                        ..Default::default()
942                    },
943                    ..Default::default()
944                };
945
946                store.save_changes(changes).await.unwrap();
947
948                let loaded_user =
949                    store.get_user_identity(other_identity.user_id()).await.unwrap().unwrap();
950
951                assert_eq!(loaded_user.master_key(), other_identity.master_key());
952                assert_eq!(loaded_user.self_signing_key(), other_identity.self_signing_key());
953                assert_eq!(loaded_user.user_id(), other_identity.user_id());
954                assert_eq!(loaded_user.other().unwrap().clone(), other_identity);
955
956                own_identity.mark_as_verified();
957
958                let changes = Changes {
959                    identities: IdentityChanges {
960                        changed: vec![own_identity.into()],
961                        ..Default::default()
962                    },
963                    ..Default::default()
964                };
965
966                store.save_changes(changes).await.unwrap();
967                let loaded_user = store.get_user_identity(&user_id).await.unwrap().unwrap();
968                assert!(loaded_user.own().unwrap().is_verified())
969            }
970
971            #[async_test]
972            async fn test_private_identity_saving() {
973                let (_, store) = get_loaded_store("private_identity_saving").await;
974                assert!(store.load_identity().await.unwrap().is_none());
975                let identity = PrivateCrossSigningIdentity::new(alice_id().to_owned());
976
977                let changes =
978                    Changes { private_identity: Some(identity.clone()), ..Default::default() };
979
980                store.save_changes(changes).await.unwrap();
981                let loaded_identity = store.load_identity().await.unwrap().unwrap();
982                assert_eq!(identity.user_id(), loaded_identity.user_id());
983            }
984
985            #[async_test]
986            async fn test_olm_hash_saving() {
987                let (_, store) = get_loaded_store("olm_hash_saving").await;
988
989                let hash = OlmMessageHash {
990                    sender_key: "test_sender".to_owned(),
991                    hash: "test_hash".to_owned(),
992                };
993
994                let mut changes = Changes::default();
995                changes.message_hashes.push(hash.clone());
996
997                assert!(!store.is_message_known(&hash).await.unwrap());
998                store.save_changes(changes).await.unwrap();
999                assert!(store.is_message_known(&hash).await.unwrap());
1000            }
1001
1002            #[async_test]
1003            async fn test_key_request_saving() {
1004                let (account, store) = get_loaded_store("key_request_saving").await;
1005                let sender_key =
1006                    Curve25519PublicKey::from_base64("Nn0L2hkcCMFKqynTjyGsJbth7QrVmX3lbrksMkrGOAw")
1007                        .unwrap();
1008
1009                let id = TransactionId::new();
1010                let info: SecretInfo = MegolmV1AesSha2Content {
1011                    room_id: owned_room_id!("!test:localhost"),
1012                    sender_key: Some(sender_key),
1013                    session_id: "test_session_id".to_owned(),
1014                }
1015                .into();
1016
1017                let request = GossipRequest {
1018                    request_recipient: account.user_id().to_owned(),
1019                    request_id: id.clone(),
1020                    info: info.clone(),
1021                    sent_out: false,
1022                };
1023
1024                assert!(store.get_outgoing_secret_requests(&id).await.unwrap().is_none());
1025
1026                let mut changes = Changes::default();
1027                changes.key_requests.push(request.clone());
1028                store.save_changes(changes).await.unwrap();
1029
1030                let request = Some(request);
1031
1032                let stored_request = store.get_outgoing_secret_requests(&id).await.unwrap();
1033                assert_eq!(request, stored_request);
1034
1035                let stored_request = store.get_secret_request_by_info(&info).await.unwrap();
1036                assert_eq!(request, stored_request);
1037                assert!(!store.get_unsent_secret_requests().await.unwrap().is_empty());
1038
1039                let request = GossipRequest {
1040                    request_recipient: account.user_id().to_owned(),
1041                    request_id: id.clone(),
1042                    info: info.clone(),
1043                    sent_out: true,
1044                };
1045
1046                let mut changes = Changes::default();
1047                changes.key_requests.push(request.clone());
1048                store.save_changes(changes).await.unwrap();
1049
1050                assert!(store.get_unsent_secret_requests().await.unwrap().is_empty());
1051                let stored_request = store.get_outgoing_secret_requests(&id).await.unwrap();
1052                assert_eq!(Some(request), stored_request);
1053
1054                store.delete_outgoing_secret_requests(&id).await.unwrap();
1055
1056                let stored_request = store.get_outgoing_secret_requests(&id).await.unwrap();
1057                assert_eq!(None, stored_request);
1058
1059                let stored_request = store.get_secret_request_by_info(&info).await.unwrap();
1060                assert_eq!(None, stored_request);
1061                assert!(store.get_unsent_secret_requests().await.unwrap().is_empty());
1062            }
1063
1064            #[async_test]
1065            async fn test_gossipped_secret_saving() {
1066                let (account, store) = get_loaded_store("gossipped_secret_saving").await;
1067
1068                let secret = "It is a secret to everybody";
1069
1070                let id = TransactionId::new();
1071                let info: SecretInfo = MegolmV1AesSha2Content {
1072                    room_id: owned_room_id!("!test:localhost"),
1073                    sender_key: Some(account.identity_keys().curve25519),
1074                    session_id: "test_session_id".to_owned(),
1075                }
1076                .into();
1077
1078                let gossip_request = GossipRequest {
1079                    request_recipient: account.user_id().to_owned(),
1080                    request_id: id.clone(),
1081                    info: info.clone(),
1082                    sent_out: true,
1083                };
1084
1085                let mut event = DecryptedSecretSendEvent {
1086                    sender: account.user_id().to_owned(),
1087                    recipient: account.user_id().to_owned(),
1088                    keys: OlmV1Keys {
1089                        ed25519: account.identity_keys().ed25519,
1090                    },
1091                    recipient_keys: OlmV1Keys {
1092                        ed25519: account.identity_keys().ed25519,
1093                    },
1094                    sender_device_keys: None,
1095                    content: SecretSendContent::new(id.to_owned(), secret.to_owned()),
1096                };
1097
1098                let value = GossippedSecret {
1099                    secret_name: SecretName::RecoveryKey,
1100                    gossip_request: gossip_request.to_owned(),
1101                    event: event.to_owned(),
1102                };
1103
1104                assert!(
1105                    store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap().is_empty(),
1106                    "No secret should initially be found in the store"
1107                );
1108
1109                let mut changes = Changes::default();
1110                changes.secrets.push(value.into());
1111                store.save_changes(changes).await.unwrap();
1112
1113                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1114                let first_secret = restored.first().expect("We should have restored a secret now");
1115                assert_eq!(first_secret.deref(), secret);
1116                assert_eq!(restored.len(), 1, "We should only have one secret stored for now");
1117
1118                let secret2 = "It is another secret to everybody";
1119                event.content.request_id = TransactionId::new();
1120                event.content.secret = secret2.to_string();
1121                let another_secret = GossippedSecret {
1122                    secret_name: SecretName::RecoveryKey,
1123                    gossip_request,
1124                    event,
1125                };
1126
1127                let mut changes = Changes::default();
1128                changes.secrets.push(another_secret.clone().into());
1129                store.save_changes(changes).await.unwrap();
1130
1131                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1132                assert_eq!(restored.len(), 2, "We should only have two secrets stored");
1133
1134                let restored = store.get_secrets_from_inbox(&SecretName::CrossSigningMasterKey).await.unwrap();
1135                assert!(restored.is_empty(), "We should not have secrets of a different type stored");
1136
1137                // if we push a secret with the same name and secret, it should
1138                // succeed
1139                let mut changes = Changes::default();
1140                changes.secrets.push(another_secret.into());
1141                store.save_changes(changes).await.unwrap();
1142
1143                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1144                // the store may store the secrets separately, or combine them
1145                // if they have the same name and secret
1146                assert!(restored.len() == 2 || restored.len() == 3, "We should only have two or three secrets stored");
1147
1148                store.delete_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1149
1150                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1151                assert!(restored.is_empty(), "We should not have any secrets after we have deleted them");
1152            }
1153
1154            #[async_test]
1155            #[cfg(feature = "experimental-push-secrets")]
1156            async fn test_pushed_secret_saving() {
1157                let (_account, store) = get_loaded_store("pushed_secret_saving").await;
1158
1159                let secret = "It is a secret to everybody";
1160
1161                let value = SecretPushContent::new(
1162                    SecretName::RecoveryKey,
1163                    secret.to_owned(),
1164                );
1165
1166                assert!(
1167                    store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap().is_empty(),
1168                    "No secret should initially be found in the store"
1169                );
1170
1171                let mut changes = Changes::default();
1172                changes.secrets.push(value.clone().into());
1173                store.save_changes(changes).await.unwrap();
1174
1175                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1176                let first_secret = restored.first().expect("We should have restored a secret now");
1177                assert_eq!(first_secret.deref(), secret);
1178                assert_eq!(restored.len(), 1, "We should only have one secret stored for now");
1179
1180                let secret2 = "It is another secret to everybody";
1181                let another_secret = SecretPushContent::new(
1182                    SecretName::RecoveryKey,
1183                    secret2.to_owned(),
1184                );
1185
1186                let mut changes = Changes::default();
1187                changes.secrets.push(another_secret.into());
1188                store.save_changes(changes).await.unwrap();
1189
1190                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1191                assert_eq!(restored.len(), 2, "We should only have two secrets stored");
1192
1193                let restored = store.get_secrets_from_inbox(&SecretName::CrossSigningMasterKey).await.unwrap();
1194                assert!(restored.is_empty(), "We should not have secrets of a different type stored");
1195
1196                store.delete_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1197
1198                let restored = store.get_secrets_from_inbox(&SecretName::RecoveryKey).await.unwrap();
1199                assert!(restored.is_empty(), "We should not have any secrets after we have deleted them");
1200            }
1201
1202            #[async_test]
1203            async fn test_withheld_info_storage() {
1204                let (account, store) = get_loaded_store("withheld_info_storage").await;
1205
1206                let user_id = account.user_id().to_owned();
1207                let room_id = room_id!("!DwLygpkclUAfQNnfva:example.com");
1208                let session_id_1 = "GBnDxGP9i3IkPsz3/ihNr6P7qjIXxSRVWZ1MYmSn09w";
1209                let session_id_2 = "IDLtnNCH2kIr3xIf1B7JFkGpQmTjyMca2jww+X6zeOE";
1210
1211                {
1212                    let mut info_list: BTreeMap<_, BTreeMap<_, RoomKeyWithheldEntry>> = BTreeMap::new();
1213
1214                    let content = RoomKeyWithheldContent::MegolmV1AesSha2(
1215                        MegolmV1AesSha2WithheldContent::Unverified(
1216                            CommonWithheldCodeContent::new(
1217                                room_id.to_owned(),
1218                                session_id_1.into(),
1219                                Curve25519PublicKey::from_base64(
1220                                    "9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA",
1221                                )
1222                                .unwrap(),
1223                                "DEVICEID".into(),
1224                            )
1225                            .into(),
1226                        ),
1227                    );
1228                    let event = ToDeviceEvent::new(user_id.to_owned(), content);
1229                    info_list
1230                        .entry(room_id.to_owned())
1231                        .or_default()
1232                        .insert(session_id_1.to_owned(), event.into());
1233
1234                    let content = RoomKeyWithheldContent::MegolmV1AesSha2(
1235                        MegolmV1AesSha2WithheldContent::BlackListed(
1236                            CommonWithheldCodeContent::new(
1237                                room_id.to_owned(),
1238                                session_id_2.into(),
1239                                Curve25519PublicKey::from_base64(
1240                                    "9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA",
1241                                )
1242                                .unwrap(),
1243                                "DEVICEID".into(),
1244                            )
1245                            .into(),
1246                        ),
1247                    );
1248                    let event = ToDeviceEvent::new(user_id.to_owned(), content);
1249                    info_list
1250                        .entry(room_id.to_owned())
1251                        .or_default()
1252                        .insert(session_id_2.to_owned(), event.into());
1253
1254                    let changes = Changes { withheld_session_info: info_list, ..Default::default() };
1255                    store.save_changes(changes).await.unwrap();
1256                }
1257
1258                // Test `get_withheld_info`
1259
1260                let is_withheld = store.get_withheld_info(room_id, session_id_1).await.unwrap();
1261
1262                assert_matches!(
1263                    is_withheld, Some(event)
1264                    if event.content.algorithm() == EventEncryptionAlgorithm::MegolmV1AesSha2 &&
1265                    event.content.withheld_code() == WithheldCode::Unverified
1266                );
1267
1268                let is_withheld = store.get_withheld_info(room_id, session_id_2).await.unwrap();
1269
1270                assert_matches!(
1271                    is_withheld, Some(event)
1272                    if event.content.algorithm() == EventEncryptionAlgorithm::MegolmV1AesSha2 &&
1273                    event.content.withheld_code() == WithheldCode::Blacklisted
1274                );
1275
1276                let other_room_id = room_id!("!nQRyiRFuyUhXeaQfiR:example.com");
1277
1278                let is_withheld =
1279                    store.get_withheld_info(other_room_id, session_id_2).await.unwrap();
1280
1281                assert!(is_withheld.is_none());
1282
1283                // Test `get_withheld_sessions_by_room_id`
1284                let withhelds = store.get_withheld_sessions_by_room_id(room_id).await.expect("Error getting withheld sessions by room ID");
1285                assert_eq!(withhelds.len(), 2);
1286                let withheld1 = withhelds.iter().find(|entry| entry.content.megolm_session_id() == Some(session_id_1)).expect("Did not find session 1 in withhelds list");
1287                assert_eq!(withheld1.content.withheld_code(), WithheldCode::Unverified)
1288            }
1289
1290            #[async_test]
1291            async fn test_room_settings_saving() {
1292                let (_, store) = get_loaded_store("room_settings_saving").await;
1293
1294                let room_1 = room_id!("!test_1:localhost");
1295                let settings_1 = RoomSettings {
1296                    algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
1297                    #[cfg(feature = "experimental-encrypted-state-events")]
1298                    encrypt_state_events: false,
1299                    only_allow_trusted_devices: true,
1300                    session_rotation_period: Some(Duration::from_secs(10)),
1301                    session_rotation_period_messages: Some(123),
1302                };
1303
1304                let room_2 = room_id!("!test_2:localhost");
1305                let settings_2 = RoomSettings {
1306                    algorithm: EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
1307                    only_allow_trusted_devices: false,
1308                    ..Default::default()
1309                };
1310
1311                let room_3 = room_id!("!test_3:localhost");
1312
1313                let changes = Changes {
1314                    room_settings: HashMap::from([
1315                        (room_1.into(), settings_1.clone()),
1316                        (room_2.into(), settings_2.clone()),
1317                    ]),
1318                    ..Default::default()
1319                };
1320
1321                store.save_changes(changes).await.unwrap();
1322
1323                let loaded_settings_1 = store.get_room_settings(room_1).await.unwrap();
1324                assert_eq!(Some(settings_1), loaded_settings_1);
1325
1326                let loaded_settings_2 = store.get_room_settings(room_2).await.unwrap();
1327                assert_eq!(Some(settings_2), loaded_settings_2);
1328
1329                let loaded_settings_3 = store.get_room_settings(room_3).await.unwrap();
1330                assert_eq!(None, loaded_settings_3);
1331            }
1332
1333            #[async_test]
1334            async fn test_backup_keys_saving() {
1335                let (_account, store) = get_loaded_store("backup_keys_saving").await;
1336
1337                let restored = store.load_backup_keys().await.unwrap();
1338                assert!(restored.decryption_key.is_none(), "Initially no backup decryption key should be present");
1339
1340                let backup_decryption_key = Some(BackupDecryptionKey::new());
1341
1342                let changes = Changes { backup_decryption_key, ..Default::default() };
1343                store.save_changes(changes).await.unwrap();
1344
1345                let restored = store.load_backup_keys().await.unwrap();
1346                assert!(restored.decryption_key.is_some(), "We should be able to restore a backup decryption key");
1347                assert!(restored.backup_version.is_none(), "The backup version should still be None");
1348
1349                let changes = Changes { backup_version: Some("some_version".to_owned()), ..Default::default() };
1350                store.save_changes(changes).await.unwrap();
1351
1352                let restored = store.load_backup_keys().await.unwrap();
1353                assert!(restored.decryption_key.is_some(), "The backup decryption key should still be known");
1354                assert!(restored.backup_version.is_some(), "The backup version should now be Some as well");
1355            }
1356
1357            #[async_test]
1358            async fn test_dehydration_pickle_key_saving() {
1359                let (_account, store) = get_loaded_store("dehydration_pickle_key_saving").await;
1360
1361                let restored = store.load_dehydrated_device_pickle_key().await.unwrap();
1362                assert!(restored.is_none(), "Initially no pickle key should be present");
1363
1364                let dehydrated_device_pickle_key = Some(DehydratedDeviceKey::new());
1365                let exported_base64 = dehydrated_device_pickle_key.clone().unwrap().to_base64();
1366
1367                let changes = Changes { dehydrated_device_pickle_key, ..Default::default() };
1368                store.save_changes(changes).await.unwrap();
1369
1370                let restored = store.load_dehydrated_device_pickle_key().await.unwrap();
1371                assert!(restored.is_some(), "We should be able to restore a pickle key");
1372                assert_eq!(restored.unwrap().to_base64(), exported_base64);
1373
1374                // If None, should not clear the existing saved key
1375                let changes = Changes { dehydrated_device_pickle_key: None, ..Default::default() };
1376                store.save_changes(changes).await.unwrap();
1377
1378                let restored = store.load_dehydrated_device_pickle_key().await.unwrap();
1379                assert!(restored.is_some(), "We should be able to restore a pickle key");
1380                assert_eq!(restored.unwrap().to_base64(), exported_base64);
1381
1382            }
1383
1384             #[async_test]
1385            async fn test_delete_dehydration_pickle_key() {
1386                let (_account, store) = get_loaded_store("delete_dehydration_pickle_key").await;
1387
1388                let dehydrated_device_pickle_key = DehydratedDeviceKey::new();
1389
1390                let changes = Changes { dehydrated_device_pickle_key: Some(dehydrated_device_pickle_key), ..Default::default() };
1391                store.save_changes(changes).await.unwrap();
1392
1393                let restored = store.load_dehydrated_device_pickle_key().await.unwrap();
1394                assert!(restored.is_some(), "We should be able to restore a pickle key");
1395
1396                store.delete_dehydrated_device_pickle_key().await.unwrap();
1397
1398                let restored = store.load_dehydrated_device_pickle_key().await.unwrap();
1399                assert!(restored.is_none(), "The previously saved key should be deleted");
1400
1401            }
1402
1403
1404            #[async_test]
1405            async fn test_custom_value_saving() {
1406                let (_, store) = get_loaded_store("custom_value_saving").await;
1407                store.set_custom_value("A", "Hello".as_bytes().to_vec()).await.unwrap();
1408
1409                let loaded_1 = store.get_custom_value("A").await.unwrap();
1410                assert_eq!(Some("Hello".as_bytes().to_vec()), loaded_1);
1411
1412                let loaded_2 = store.get_custom_value("B").await.unwrap();
1413                assert_eq!(None, loaded_2);
1414            }
1415
1416            #[async_test]
1417            async fn test_received_room_key_bundle() {
1418                let store = get_store("received_room_key_bundle", None, true).await;
1419                let test_room = room_id!("!room:example.org");
1420
1421                fn make_bundle_data(sender_user: &UserId, bundle_uri: &str) -> StoredRoomKeyBundleData {
1422                    let info = ruma::events::room::V2EncryptedFileInfo::encode([0; 32], [0;16]).into();
1423
1424                    let file = ruma::events::room::EncryptedFile::new(
1425                        ruma::OwnedMxcUri::from(bundle_uri),
1426                        info,
1427                        Default::default()
1428                    );
1429
1430                    StoredRoomKeyBundleData {
1431                        sender_user: sender_user.to_owned(),
1432                        sender_key: Curve25519PublicKey::from_bytes([0u8; 32]),
1433                        sender_data: SenderData::unknown(),
1434                        bundle_data: RoomKeyBundleContent {
1435                            room_id: owned_room_id!("!room:example.org"),
1436                            file,
1437                        },
1438                    }
1439                }
1440
1441                // Add three entries
1442                let changes = Changes {
1443                    received_room_key_bundles: vec![
1444                        make_bundle_data(user_id!("@alice:example.com"), "alice1"),
1445                        make_bundle_data(user_id!("@bob:example.com"), "bob1"),
1446                        make_bundle_data(user_id!("@alice:example.com"), "alice2"),
1447                    ],
1448                    ..Default::default()
1449                };
1450                store.save_changes(changes).await.unwrap();
1451
1452                // Check we get the right one
1453                let bundle = store.get_received_room_key_bundle_data(
1454                    test_room, user_id!("@alice:example.com")
1455                ).await.unwrap().expect("Did not get any bundle data");
1456                assert_eq!(bundle.bundle_data.file.url.to_string(), "alice2");
1457            }
1458
1459            #[async_test]
1460            async fn test_room_pending_key_bundle() {
1461                use $crate::store::types::RoomPendingKeyBundleDetails;
1462                let store = get_store("room_pending_key_bundle", None, true).await;
1463                let test_room = room_id!("!room:example.org");
1464                let test_user = user_id!("@user:example.com");
1465                let timestamp = ruma::MilliSecondsSinceUnixEpoch::now();
1466
1467                // Empty to start with
1468                assert!(store.get_pending_key_bundle_details_for_room(test_room).await.unwrap().is_none());
1469
1470                // Now add an entry, and check it comes back correctly
1471                store.save_changes(Changes {
1472                    rooms_pending_key_bundle: HashMap::from([(
1473                        test_room.to_owned(),
1474                        Some(RoomPendingKeyBundleDetails {
1475                            room_id: test_room.to_owned(),
1476                            invite_accepted_at: timestamp,
1477                            inviter: test_user.to_owned(),
1478                        }),
1479                    )]),
1480                    ..Default::default()
1481                }).await.unwrap();
1482
1483                let details = store.get_pending_key_bundle_details_for_room(test_room).await.unwrap();
1484                assert_matches!(details, Some(details) => {
1485                    assert_eq!(details.room_id, test_room);
1486                    assert_eq!(details.inviter, test_user);
1487                    assert_eq!(details.invite_accepted_at, timestamp);
1488                });
1489
1490                let all_rooms = store.get_all_rooms_pending_key_bundles().await.unwrap();
1491                assert_eq!(all_rooms.len(), 1);
1492                assert_eq!(all_rooms[0].room_id, test_room);
1493                assert_eq!(all_rooms[0].inviter, test_user);
1494                assert_eq!(all_rooms[0].invite_accepted_at, timestamp);
1495
1496                // Clear the entry, and check it is blank again
1497                store.save_changes(Changes {
1498                    rooms_pending_key_bundle: HashMap::from([(test_room.to_owned(), None)]),
1499                    ..Default::default()
1500                }).await.unwrap();
1501                assert!(
1502                    store.get_pending_key_bundle_details_for_room(test_room).await.unwrap().is_none(),
1503                    "Pending key bundle details were present after being cleared"
1504                );
1505            }
1506
1507            #[async_test]
1508            async fn test_set_has_downloaded_all_room_keys() {
1509                let store = get_store("room_key_backups_fully_downloaded", None, true).await;
1510                let test_room = room_id!("!room:example.org");
1511
1512                let changes = Changes {
1513                    room_key_backups_fully_downloaded: HashSet::from_iter([test_room.to_owned()]),
1514                    ..Default::default()
1515                };
1516                store
1517                    .save_changes(changes)
1518                    .await
1519                    .expect("We should be able to save the changes to the store");
1520
1521                assert!(
1522                    store
1523                    .has_downloaded_all_room_keys(test_room)
1524                    .await
1525                    .expect("We should be able to check if we have downloaded room keys")
1526                );
1527            }
1528
1529            fn session_info(session: &InboundGroupSession) -> (&RoomId, &str) {
1530                (&session.room_id(), &session.session_id())
1531            }
1532
1533            async fn create_session(
1534                account: &Account,
1535                device_curve_key: &Curve25519PublicKey,
1536                sender_data_type: SenderDataType,
1537            ) -> InboundGroupSession {
1538                let sender_data = match sender_data_type {
1539                    SenderDataType::UnknownDevice => {
1540                        SenderData::UnknownDevice { legacy_session: false, owner_check_failed: false }
1541                    }
1542                    SenderDataType::DeviceInfo => SenderData::DeviceInfo {
1543                        device_keys: account.device_keys().clone(),
1544                        legacy_session: false,
1545                    },
1546                    SenderDataType::VerificationViolation => panic!("VerificationViolation not supported"),
1547                    SenderDataType::SenderUnverified=> panic!("SenderUnverified not supported"),
1548                    SenderDataType::SenderVerified => panic!("SenderVerified not supported"),
1549                };
1550
1551                let session_key = GroupSession::new(SessionConfig::default()).session_key();
1552
1553                InboundGroupSession::new(
1554                    device_curve_key.clone(),
1555                    account.device_keys().ed25519_key().unwrap(),
1556                    room_id!("!r:s.co"),
1557                    &session_key,
1558                    sender_data,
1559                    None,
1560                    EventEncryptionAlgorithm::MegolmV1AesSha2,
1561                    None,
1562                    false,
1563                )
1564                .unwrap()
1565            }
1566        }
1567    };
1568}
1569
1570#[allow(unused_macros)]
1571#[macro_export]
1572macro_rules! cryptostore_integration_tests_time {
1573    () => {
1574        mod cryptostore_integration_tests_time {
1575            use std::time::Duration;
1576
1577            use matrix_sdk_test::async_test;
1578            use $crate::store::CryptoStore as _;
1579
1580            use super::cryptostore_integration_tests::*;
1581
1582            #[async_test]
1583            async fn test_lease_locks() {
1584                let (_account, store) = get_loaded_store("lease_locks").await;
1585
1586                let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1587                assert_eq!(acquired0, Some(1)); // first generation
1588
1589                // Should extend the lease automatically (same holder).
1590                let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1591                assert_eq!(acquired2, Some(1)); // same lock generation
1592
1593                // Should extend the lease automatically (same holder + time is ok).
1594                let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1595                assert_eq!(acquired3, Some(1)); // same lock generation
1596
1597                // Another attempt at taking the lock should fail, because it's taken.
1598                let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1599                assert!(acquired4.is_none()); // not acquired
1600
1601                // Even if we insist.
1602                let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1603                assert!(acquired5.is_none());
1604
1605                // That's a nice test we got here, go take a little nap.
1606                tokio::time::sleep(Duration::from_millis(50)).await;
1607
1608                // Still too early.
1609                let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1610                assert!(acquired55.is_none()); // not acquired
1611
1612                // Ok you can take another nap then.
1613                tokio::time::sleep(Duration::from_millis(250)).await;
1614
1615                // At some point, we do get the lock.
1616                let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
1617                assert_eq!(acquired6, Some(2)); // new lock generation!
1618
1619                tokio::time::sleep(Duration::from_millis(1)).await;
1620
1621                // The other gets it almost immediately too.
1622                let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1623                assert_eq!(acquired7, Some(3)); // new lock generation!
1624
1625                tokio::time::sleep(Duration::from_millis(1)).await;
1626
1627                // But when we take a longer lease…
1628                let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1629                assert_eq!(acquired8, Some(4)); // new lock generation!
1630
1631                // It blocks the other user.
1632                let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1633                assert!(acquired9.is_none()); // not acquired
1634
1635                // We can hold onto our lease.
1636                let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1637                assert_eq!(acquired10, Some(4)); // same lock generation
1638            }
1639        }
1640    };
1641}