matrix_sdk_sqlite/
crypto_store.rs

1// Copyright 2022 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::HashMap,
17    fmt,
18    path::Path,
19    sync::{Arc, RwLock},
20};
21
22use async_trait::async_trait;
23use matrix_sdk_base::cross_process_lock::CrossProcessLockGeneration;
24use matrix_sdk_crypto::{
25    Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, TrackedUser, UserIdentityData,
26    olm::{
27        InboundGroupSession, OutboundGroupSession, PickledInboundGroupSession,
28        PrivateCrossSigningIdentity, SenderDataType, Session, StaticAccountData,
29    },
30    store::{
31        CryptoStore,
32        types::{
33            BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts,
34            RoomKeyWithheldEntry, RoomSettings, StoredRoomKeyBundleData,
35        },
36    },
37};
38use matrix_sdk_store_encryption::StoreCipher;
39use ruma::{
40    DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, RoomId, TransactionId, UserId,
41    events::secret::request::SecretName,
42};
43use rusqlite::{OptionalExtension, named_params, params_from_iter};
44use tokio::{fs, sync::Mutex};
45use tracing::{debug, instrument, warn};
46use vodozemac::Curve25519PublicKey;
47
48use crate::{
49    OpenStoreError, Secret, SqliteStoreConfig,
50    connection::{Connection as SqliteAsyncConn, Pool as SqlitePool},
51    error::{Error, Result},
52    utils::{
53        EncryptableStore, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
54        SqliteKeyValueStoreConnExt, repeat_vars,
55    },
56};
57
58/// The database name.
59const DATABASE_NAME: &str = "matrix-sdk-crypto.sqlite3";
60
61/// An SQLite-based crypto store.
62#[derive(Clone)]
63pub struct SqliteCryptoStore {
64    store_cipher: Option<Arc<StoreCipher>>,
65    pool: SqlitePool,
66
67    // DB values cached in memory
68    static_account: Arc<RwLock<Option<StaticAccountData>>>,
69    save_changes_lock: Arc<Mutex<()>>,
70}
71
72#[cfg(not(tarpaulin_include))]
73impl fmt::Debug for SqliteCryptoStore {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.debug_struct("SqliteCryptoStore").finish_non_exhaustive()
76    }
77}
78
79impl EncryptableStore for SqliteCryptoStore {
80    fn get_cypher(&self) -> Option<&StoreCipher> {
81        self.store_cipher.as_deref()
82    }
83}
84
85impl SqliteCryptoStore {
86    /// Open the SQLite-based crypto store at the given path using the given
87    /// passphrase to encrypt private data.
88    pub async fn open(
89        path: impl AsRef<Path>,
90        passphrase: Option<&str>,
91    ) -> Result<Self, OpenStoreError> {
92        Self::open_with_config(SqliteStoreConfig::new(path).passphrase(passphrase)).await
93    }
94
95    /// Open the SQLite-based crypto store at the given path using the given
96    /// key to encrypt private data.
97    pub async fn open_with_key(
98        path: impl AsRef<Path>,
99        key: Option<&[u8; 32]>,
100    ) -> Result<Self, OpenStoreError> {
101        Self::open_with_config(SqliteStoreConfig::new(path).key(key)).await
102    }
103
104    /// Open the SQLite-based crypto store with the config open config.
105    pub async fn open_with_config(config: SqliteStoreConfig) -> Result<Self, OpenStoreError> {
106        fs::create_dir_all(&config.path).await.map_err(OpenStoreError::CreateDir)?;
107
108        let pool = config.build_pool_of_connections(DATABASE_NAME)?;
109
110        let this = Self::open_with_pool(pool, config.secret).await?;
111        this.pool.get().await?.apply_runtime_config(config.runtime_config).await?;
112
113        Ok(this)
114    }
115
116    /// Create an SQLite-based crypto store using the given SQLite database
117    /// pool. The given secret will be used to encrypt private data.
118    async fn open_with_pool(
119        pool: SqlitePool,
120        secret: Option<Secret>,
121    ) -> Result<Self, OpenStoreError> {
122        let conn = pool.get().await?;
123
124        let version = conn.db_version().await?;
125        debug!("Opened sqlite store with version {}", version);
126        run_migrations(&conn, version).await?;
127
128        conn.wal_checkpoint().await;
129
130        let store_cipher = match secret {
131            Some(s) => Some(Arc::new(conn.get_or_create_store_cipher(s).await?)),
132            None => None,
133        };
134
135        Ok(SqliteCryptoStore {
136            store_cipher,
137            pool,
138            static_account: Arc::new(RwLock::new(None)),
139            save_changes_lock: Default::default(),
140        })
141    }
142
143    fn deserialize_and_unpickle_inbound_group_session(
144        &self,
145        value: Vec<u8>,
146        backed_up: bool,
147    ) -> Result<InboundGroupSession> {
148        let mut pickle: PickledInboundGroupSession = self.deserialize_value(&value)?;
149
150        // The `backed_up` SQL column is the source of truth, because we update it
151        // inside `mark_inbound_group_sessions_as_backed_up` and don't update
152        // the pickled value inside the `data` column (until now, when we are puling it
153        // out of the DB).
154        pickle.backed_up = backed_up;
155
156        Ok(InboundGroupSession::from_pickle(pickle)?)
157    }
158
159    fn deserialize_key_request(&self, value: &[u8], sent_out: bool) -> Result<GossipRequest> {
160        let mut request: GossipRequest = self.deserialize_value(value)?;
161        // sent_out SQL column is source of truth, sent_out field in serialized value
162        // needed for other stores though
163        request.sent_out = sent_out;
164        Ok(request)
165    }
166
167    fn get_static_account(&self) -> Option<StaticAccountData> {
168        self.static_account.read().unwrap().clone()
169    }
170
171    async fn acquire(&self) -> Result<SqliteAsyncConn> {
172        Ok(self.pool.get().await?)
173    }
174}
175
176const DATABASE_VERSION: u8 = 14;
177
178/// key for the dehydrated device pickle key in the key/value table.
179const DEHYDRATED_DEVICE_PICKLE_KEY: &str = "dehydrated_device_pickle_key";
180
181/// Run migrations for the given version of the database.
182async fn run_migrations(conn: &SqliteAsyncConn, version: u8) -> Result<()> {
183    if version == 0 {
184        debug!("Creating database");
185    } else if version < DATABASE_VERSION {
186        debug!(version, new_version = DATABASE_VERSION, "Upgrading database");
187    } else {
188        return Ok(());
189    }
190
191    if version < 1 {
192        // First turn on WAL mode, this can't be done in the transaction, it fails with
193        // the error message: "cannot change into wal mode from within a transaction".
194        conn.execute_batch("PRAGMA journal_mode = wal;").await?;
195        conn.with_transaction(|txn| {
196            txn.execute_batch(include_str!("../migrations/crypto_store/001_init.sql"))?;
197            txn.set_db_version(1)
198        })
199        .await?;
200    }
201
202    if version < 2 {
203        conn.with_transaction(|txn| {
204            txn.execute_batch(include_str!("../migrations/crypto_store/002_reset_olm_hash.sql"))?;
205            txn.set_db_version(2)
206        })
207        .await?;
208    }
209
210    if version < 3 {
211        conn.with_transaction(|txn| {
212            txn.execute_batch(include_str!("../migrations/crypto_store/003_room_settings.sql"))?;
213            txn.set_db_version(3)
214        })
215        .await?;
216    }
217
218    if version < 4 {
219        conn.with_transaction(|txn| {
220            txn.execute_batch(include_str!(
221                "../migrations/crypto_store/004_drop_outbound_group_sessions.sql"
222            ))?;
223            txn.set_db_version(4)
224        })
225        .await?;
226    }
227
228    if version < 5 {
229        conn.with_transaction(|txn| {
230            txn.execute_batch(include_str!("../migrations/crypto_store/005_withheld_code.sql"))?;
231            txn.set_db_version(5)
232        })
233        .await?;
234    }
235
236    if version < 6 {
237        conn.with_transaction(|txn| {
238            txn.execute_batch(include_str!(
239                "../migrations/crypto_store/006_drop_outbound_group_sessions.sql"
240            ))?;
241            txn.set_db_version(6)
242        })
243        .await?;
244    }
245
246    if version < 7 {
247        conn.with_transaction(|txn| {
248            txn.execute_batch(include_str!("../migrations/crypto_store/007_lock_leases.sql"))?;
249            txn.set_db_version(7)
250        })
251        .await?;
252    }
253
254    if version < 8 {
255        conn.with_transaction(|txn| {
256            txn.execute_batch(include_str!("../migrations/crypto_store/008_secret_inbox.sql"))?;
257            txn.set_db_version(8)
258        })
259        .await?;
260    }
261
262    if version < 9 {
263        conn.with_transaction(|txn| {
264            txn.execute_batch(include_str!(
265                "../migrations/crypto_store/009_inbound_group_session_sender_key_sender_data_type.sql"
266            ))?;
267            txn.set_db_version(9)
268        })
269        .await?;
270    }
271
272    if version < 10 {
273        conn.with_transaction(|txn| {
274            txn.execute_batch(include_str!(
275                "../migrations/crypto_store/010_received_room_key_bundles.sql"
276            ))?;
277            txn.set_db_version(10)
278        })
279        .await?;
280    }
281
282    if version < 11 {
283        conn.with_transaction(|txn| {
284            txn.execute_batch(include_str!(
285                "../migrations/crypto_store/011_received_room_key_bundles_with_curve_key.sql"
286            ))?;
287            txn.set_db_version(11)
288        })
289        .await?;
290    }
291
292    if version < 12 {
293        conn.with_transaction(|txn| {
294            txn.execute_batch(include_str!(
295                "../migrations/crypto_store/012_withheld_code_by_room.sql"
296            ))?;
297            txn.set_db_version(12)
298        })
299        .await?;
300    }
301
302    if version < 13 {
303        conn.with_transaction(|txn| {
304            txn.execute_batch(include_str!(
305                "../migrations/crypto_store/013_lease_locks_with_generation.sql"
306            ))?;
307            txn.set_db_version(13)
308        })
309        .await?;
310    }
311
312    if version < 14 {
313        conn.with_transaction(|txn| {
314            txn.execute_batch(include_str!(
315                "../migrations/crypto_store/014_room_key_backups_fully_downloaded.sql"
316            ))?;
317            txn.set_db_version(14)
318        })
319        .await?;
320    }
321
322    Ok(())
323}
324
325trait SqliteConnectionExt {
326    fn set_session(
327        &self,
328        session_id: &[u8],
329        sender_key: &[u8],
330        data: &[u8],
331    ) -> rusqlite::Result<()>;
332
333    fn set_inbound_group_session(
334        &self,
335        room_id: &[u8],
336        session_id: &[u8],
337        data: &[u8],
338        backed_up: bool,
339        sender_key: Option<&[u8]>,
340        sender_data_type: Option<u8>,
341    ) -> rusqlite::Result<()>;
342
343    fn set_outbound_group_session(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
344
345    fn set_device(&self, user_id: &[u8], device_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
346    fn delete_device(&self, user_id: &[u8], device_id: &[u8]) -> rusqlite::Result<()>;
347
348    fn set_identity(&self, user_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
349
350    fn add_olm_hash(&self, data: &[u8]) -> rusqlite::Result<()>;
351
352    fn set_key_request(
353        &self,
354        request_id: &[u8],
355        sent_out: bool,
356        data: &[u8],
357    ) -> rusqlite::Result<()>;
358
359    fn set_direct_withheld(
360        &self,
361        session_id: &[u8],
362        room_id: &[u8],
363        data: &[u8],
364    ) -> rusqlite::Result<()>;
365
366    fn set_room_settings(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
367
368    fn set_secret(&self, request_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
369
370    fn set_received_room_key_bundle(
371        &self,
372        room_id: &[u8],
373        user_id: &[u8],
374        data: &[u8],
375    ) -> rusqlite::Result<()>;
376
377    fn set_has_downloaded_all_room_keys(&self, room_id: &[u8]) -> rusqlite::Result<()>;
378}
379
380impl SqliteConnectionExt for rusqlite::Connection {
381    fn set_session(
382        &self,
383        session_id: &[u8],
384        sender_key: &[u8],
385        data: &[u8],
386    ) -> rusqlite::Result<()> {
387        self.execute(
388            "INSERT INTO session (session_id, sender_key, data)
389             VALUES (?1, ?2, ?3)
390             ON CONFLICT (session_id) DO UPDATE SET data = ?3",
391            (session_id, sender_key, data),
392        )?;
393        Ok(())
394    }
395
396    fn set_inbound_group_session(
397        &self,
398        room_id: &[u8],
399        session_id: &[u8],
400        data: &[u8],
401        backed_up: bool,
402        sender_key: Option<&[u8]>,
403        sender_data_type: Option<u8>,
404    ) -> rusqlite::Result<()> {
405        self.execute(
406            "INSERT INTO inbound_group_session (session_id, room_id, data, backed_up, sender_key, sender_data_type) \
407             VALUES (?1, ?2, ?3, ?4, ?5, ?6)
408             ON CONFLICT (session_id) DO UPDATE SET data = ?3, backed_up = ?4, sender_key = ?5, sender_data_type = ?6",
409            (session_id, room_id, data, backed_up, sender_key, sender_data_type),
410        )?;
411        Ok(())
412    }
413
414    fn set_outbound_group_session(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
415        self.execute(
416            "INSERT INTO outbound_group_session (room_id, data) \
417             VALUES (?1, ?2)
418             ON CONFLICT (room_id) DO UPDATE SET data = ?2",
419            (room_id, data),
420        )?;
421        Ok(())
422    }
423
424    fn set_device(&self, user_id: &[u8], device_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
425        self.execute(
426            "INSERT INTO device (user_id, device_id, data) \
427             VALUES (?1, ?2, ?3)
428             ON CONFLICT (user_id, device_id) DO UPDATE SET data = ?3",
429            (user_id, device_id, data),
430        )?;
431        Ok(())
432    }
433
434    fn delete_device(&self, user_id: &[u8], device_id: &[u8]) -> rusqlite::Result<()> {
435        self.execute(
436            "DELETE FROM device WHERE user_id = ? AND device_id = ?",
437            (user_id, device_id),
438        )?;
439        Ok(())
440    }
441
442    fn set_identity(&self, user_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
443        self.execute(
444            "INSERT INTO identity (user_id, data) \
445             VALUES (?1, ?2)
446             ON CONFLICT (user_id) DO UPDATE SET data = ?2",
447            (user_id, data),
448        )?;
449        Ok(())
450    }
451
452    fn add_olm_hash(&self, data: &[u8]) -> rusqlite::Result<()> {
453        self.execute("INSERT INTO olm_hash (data) VALUES (?) ON CONFLICT DO NOTHING", (data,))?;
454        Ok(())
455    }
456
457    fn set_key_request(
458        &self,
459        request_id: &[u8],
460        sent_out: bool,
461        data: &[u8],
462    ) -> rusqlite::Result<()> {
463        self.execute(
464            "INSERT INTO key_requests (request_id, sent_out, data)
465            VALUES (?1, ?2, ?3)
466            ON CONFLICT (request_id) DO UPDATE SET sent_out = ?2, data = ?3",
467            (request_id, sent_out, data),
468        )?;
469        Ok(())
470    }
471
472    fn set_direct_withheld(
473        &self,
474        session_id: &[u8],
475        room_id: &[u8],
476        data: &[u8],
477    ) -> rusqlite::Result<()> {
478        self.execute(
479            "INSERT INTO direct_withheld_info (session_id, room_id, data)
480            VALUES (?1, ?2, ?3)
481            ON CONFLICT (session_id) DO UPDATE SET room_id = ?2, data = ?3",
482            (session_id, room_id, data),
483        )?;
484        Ok(())
485    }
486
487    fn set_room_settings(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
488        self.execute(
489            "INSERT INTO room_settings (room_id, data)
490            VALUES (?1, ?2)
491            ON CONFLICT (room_id) DO UPDATE SET data = ?2",
492            (room_id, data),
493        )?;
494        Ok(())
495    }
496
497    fn set_secret(&self, secret_name: &[u8], data: &[u8]) -> rusqlite::Result<()> {
498        self.execute(
499            "INSERT INTO secrets (secret_name, data)
500            VALUES (?1, ?2)",
501            (secret_name, data),
502        )?;
503
504        Ok(())
505    }
506
507    fn set_received_room_key_bundle(
508        &self,
509        room_id: &[u8],
510        sender_user_id: &[u8],
511        data: &[u8],
512    ) -> rusqlite::Result<()> {
513        self.execute(
514            "INSERT INTO received_room_key_bundle(room_id, sender_user_id, bundle_data)
515            VALUES (?1, ?2, ?3)
516            ON CONFLICT (room_id, sender_user_id) DO UPDATE SET bundle_data = ?3",
517            (room_id, sender_user_id, data),
518        )?;
519        Ok(())
520    }
521
522    fn set_has_downloaded_all_room_keys(&self, room_id: &[u8]) -> rusqlite::Result<()> {
523        self.execute(
524            "INSERT INTO room_key_backups_fully_downloaded(room_id)
525             VALUES (?1)
526             ON CONFLICT(room_id) DO NOTHING",
527            (room_id,),
528        )?;
529        Ok(())
530    }
531}
532
533#[async_trait]
534trait SqliteObjectCryptoStoreExt: SqliteAsyncConnExt {
535    async fn get_sessions_for_sender_key(&self, sender_key: Key) -> Result<Vec<Vec<u8>>> {
536        Ok(self
537            .prepare("SELECT data FROM session WHERE sender_key = ?", |mut stmt| {
538                stmt.query((sender_key,))?.mapped(|row| row.get(0)).collect()
539            })
540            .await?)
541    }
542
543    async fn get_inbound_group_session(
544        &self,
545        session_id: Key,
546    ) -> Result<Option<(Vec<u8>, Vec<u8>, bool)>> {
547        Ok(self
548            .query_row(
549                "SELECT room_id, data, backed_up FROM inbound_group_session WHERE session_id = ?",
550                (session_id,),
551                |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
552            )
553            .await
554            .optional()?)
555    }
556
557    async fn get_inbound_group_sessions(&self) -> Result<Vec<(Vec<u8>, bool)>> {
558        Ok(self
559            .prepare("SELECT data, backed_up FROM inbound_group_session", |mut stmt| {
560                stmt.query(())?.mapped(|row| Ok((row.get(0)?, row.get(1)?))).collect()
561            })
562            .await?)
563    }
564
565    async fn get_inbound_group_session_counts(
566        &self,
567        _backup_version: Option<&str>,
568    ) -> Result<RoomKeyCounts> {
569        let total = self
570            .query_row("SELECT count(*) FROM inbound_group_session", (), |row| row.get(0))
571            .await?;
572        let backed_up = self
573            .query_row(
574                "SELECT count(*) FROM inbound_group_session WHERE backed_up = TRUE",
575                (),
576                |row| row.get(0),
577            )
578            .await?;
579        Ok(RoomKeyCounts { total, backed_up })
580    }
581
582    async fn get_inbound_group_sessions_by_room_id(
583        &self,
584        room_id: Key,
585    ) -> Result<Vec<(Vec<u8>, bool)>> {
586        Ok(self
587            .prepare(
588                "SELECT data, backed_up FROM inbound_group_session WHERE room_id = :room_id",
589                move |mut stmt| {
590                    stmt.query(named_params! {
591                        ":room_id": room_id,
592                    })?
593                    .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
594                    .collect()
595                },
596            )
597            .await?)
598    }
599
600    async fn get_inbound_group_sessions_for_device_batch(
601        &self,
602        sender_key: Key,
603        sender_data_type: SenderDataType,
604        after_session_id: Option<Key>,
605        limit: usize,
606    ) -> Result<Vec<(Vec<u8>, bool)>> {
607        Ok(self
608            .prepare(
609                "
610                SELECT data, backed_up
611                FROM inbound_group_session
612                WHERE sender_key = :sender_key
613                    AND sender_data_type = :sender_data_type
614                    AND session_id > :after_session_id
615                ORDER BY session_id
616                LIMIT :limit
617                ",
618                move |mut stmt| {
619                    let sender_data_type = sender_data_type as u8;
620
621                    // If we are not provided with an `after_session_id`, use a key which will sort
622                    // before all real keys: the empty string.
623                    let after_session_id = after_session_id.unwrap_or(Key::Plain(Vec::new()));
624
625                    stmt.query(named_params! {
626                        ":sender_key": sender_key,
627                        ":sender_data_type": sender_data_type,
628                        ":after_session_id": after_session_id,
629                        ":limit": limit,
630                    })?
631                    .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
632                    .collect()
633                },
634            )
635            .await?)
636    }
637
638    async fn get_inbound_group_sessions_for_backup(&self, limit: usize) -> Result<Vec<Vec<u8>>> {
639        Ok(self
640            .prepare(
641                "SELECT data FROM inbound_group_session WHERE backed_up = FALSE LIMIT ?",
642                move |mut stmt| stmt.query((limit,))?.mapped(|row| row.get(0)).collect(),
643            )
644            .await?)
645    }
646
647    async fn mark_inbound_group_sessions_as_backed_up(&self, session_ids: Vec<Key>) -> Result<()> {
648        if session_ids.is_empty() {
649            // We are not expecting to be called with an empty list of sessions
650            warn!("No sessions to mark as backed up!");
651            return Ok(());
652        }
653
654        let session_ids_len = session_ids.len();
655
656        self.chunk_large_query_over(session_ids, None, move |txn, session_ids| {
657            // Safety: placeholders is not generated using any user input except the number
658            // of session IDs, so it is safe from injection.
659            let sql_params = repeat_vars(session_ids_len);
660            let query = format!("UPDATE inbound_group_session SET backed_up = TRUE where session_id IN ({sql_params})");
661            txn.prepare(&query)?.execute(params_from_iter(session_ids.iter()))?;
662            Ok(Vec::<()>::new())
663        }).await?;
664
665        Ok(())
666    }
667
668    async fn reset_inbound_group_session_backup_state(&self) -> Result<()> {
669        self.execute("UPDATE inbound_group_session SET backed_up = FALSE", ()).await?;
670        Ok(())
671    }
672
673    async fn get_outbound_group_session(&self, room_id: Key) -> Result<Option<Vec<u8>>> {
674        Ok(self
675            .query_row(
676                "SELECT data FROM outbound_group_session WHERE room_id = ?",
677                (room_id,),
678                |row| row.get(0),
679            )
680            .await
681            .optional()?)
682    }
683
684    async fn get_device(&self, user_id: Key, device_id: Key) -> Result<Option<Vec<u8>>> {
685        Ok(self
686            .query_row(
687                "SELECT data FROM device WHERE user_id = ? AND device_id = ?",
688                (user_id, device_id),
689                |row| row.get(0),
690            )
691            .await
692            .optional()?)
693    }
694
695    async fn get_user_devices(&self, user_id: Key) -> Result<Vec<Vec<u8>>> {
696        Ok(self
697            .prepare("SELECT data FROM device WHERE user_id = ?", |mut stmt| {
698                stmt.query((user_id,))?.mapped(|row| row.get(0)).collect()
699            })
700            .await?)
701    }
702
703    async fn get_user_identity(&self, user_id: Key) -> Result<Option<Vec<u8>>> {
704        Ok(self
705            .query_row("SELECT data FROM identity WHERE user_id = ?", (user_id,), |row| row.get(0))
706            .await
707            .optional()?)
708    }
709
710    async fn has_olm_hash(&self, data: Vec<u8>) -> Result<bool> {
711        Ok(self
712            .query_row("SELECT count(*) FROM olm_hash WHERE data = ?", (data,), |row| {
713                row.get::<_, i32>(0)
714            })
715            .await?
716            > 0)
717    }
718
719    async fn get_tracked_users(&self) -> Result<Vec<Vec<u8>>> {
720        Ok(self
721            .prepare("SELECT data FROM tracked_user", |mut stmt| {
722                stmt.query(())?.mapped(|row| row.get(0)).collect()
723            })
724            .await?)
725    }
726
727    async fn add_tracked_users(&self, users: Vec<(Key, Vec<u8>)>) -> Result<()> {
728        Ok(self
729            .prepare(
730                "INSERT INTO tracked_user (user_id, data) \
731                 VALUES (?1, ?2) \
732                 ON CONFLICT (user_id) DO UPDATE SET data = ?2",
733                |mut stmt| {
734                    for (user_id, data) in users {
735                        stmt.execute((user_id, data))?;
736                    }
737
738                    Ok(())
739                },
740            )
741            .await?)
742    }
743
744    async fn get_outgoing_secret_request(
745        &self,
746        request_id: Key,
747    ) -> Result<Option<(Vec<u8>, bool)>> {
748        Ok(self
749            .query_row(
750                "SELECT data, sent_out FROM key_requests WHERE request_id = ?",
751                (request_id,),
752                |row| Ok((row.get(0)?, row.get(1)?)),
753            )
754            .await
755            .optional()?)
756    }
757
758    async fn get_outgoing_secret_requests(&self) -> Result<Vec<(Vec<u8>, bool)>> {
759        Ok(self
760            .prepare("SELECT data, sent_out FROM key_requests", |mut stmt| {
761                stmt.query(())?.mapped(|row| Ok((row.get(0)?, row.get(1)?))).collect()
762            })
763            .await?)
764    }
765
766    async fn get_unsent_secret_requests(&self) -> Result<Vec<Vec<u8>>> {
767        Ok(self
768            .prepare("SELECT data FROM key_requests WHERE sent_out = FALSE", |mut stmt| {
769                stmt.query(())?.mapped(|row| row.get(0)).collect()
770            })
771            .await?)
772    }
773
774    async fn delete_key_request(&self, request_id: Key) -> Result<()> {
775        self.execute("DELETE FROM key_requests WHERE request_id = ?", (request_id,)).await?;
776        Ok(())
777    }
778
779    async fn get_secrets_from_inbox(&self, secret_name: Key) -> Result<Vec<Vec<u8>>> {
780        Ok(self
781            .prepare("SELECT data FROM secrets WHERE secret_name = ?", |mut stmt| {
782                stmt.query((secret_name,))?.mapped(|row| row.get(0)).collect()
783            })
784            .await?)
785    }
786
787    async fn delete_secrets_from_inbox(&self, secret_name: Key) -> Result<()> {
788        self.execute("DELETE FROM secrets WHERE secret_name = ?", (secret_name,)).await?;
789        Ok(())
790    }
791
792    async fn get_direct_withheld_info(
793        &self,
794        session_id: Key,
795        room_id: Key,
796    ) -> Result<Option<Vec<u8>>> {
797        Ok(self
798            .query_row(
799                "SELECT data FROM direct_withheld_info WHERE session_id = ?1 AND room_id = ?2",
800                (session_id, room_id),
801                |row| row.get(0),
802            )
803            .await
804            .optional()?)
805    }
806
807    async fn get_withheld_sessions_by_room_id(&self, room_id: Key) -> Result<Vec<Vec<u8>>> {
808        Ok(self
809            .prepare("SELECT data FROM direct_withheld_info WHERE room_id = ?1", |mut stmt| {
810                stmt.query((room_id,))?.mapped(|row| row.get(0)).collect()
811            })
812            .await?)
813    }
814
815    async fn get_room_settings(&self, room_id: Key) -> Result<Option<Vec<u8>>> {
816        Ok(self
817            .query_row("SELECT data FROM room_settings WHERE room_id = ?", (room_id,), |row| {
818                row.get(0)
819            })
820            .await
821            .optional()?)
822    }
823
824    async fn get_received_room_key_bundle(
825        &self,
826        room_id: Key,
827        sender_user: Key,
828    ) -> Result<Option<Vec<u8>>> {
829        Ok(self
830            .query_row(
831                "SELECT bundle_data FROM received_room_key_bundle WHERE room_id = ? AND sender_user_id = ?",
832                (room_id, sender_user),
833                |row| { row.get(0) },
834            )
835            .await
836            .optional()?)
837    }
838
839    async fn has_downloaded_all_room_keys(&self, room_id: Key) -> Result<bool> {
840        Ok(self
841            .query_row(
842                "SELECT EXISTS (SELECT 1 FROM room_key_backups_fully_downloaded WHERE room_id = ?)",
843                (room_id,),
844                |row| row.get(0),
845            )
846            .await?)
847    }
848}
849
850#[async_trait]
851impl SqliteObjectCryptoStoreExt for SqliteAsyncConn {}
852
853#[async_trait]
854impl CryptoStore for SqliteCryptoStore {
855    type Error = Error;
856
857    async fn load_account(&self) -> Result<Option<Account>> {
858        let conn = self.acquire().await?;
859        if let Some(pickle) = conn.get_kv("account").await? {
860            let pickle = self.deserialize_value(&pickle)?;
861
862            let account = Account::from_pickle(pickle).map_err(|_| Error::Unpickle)?;
863
864            *self.static_account.write().unwrap() = Some(account.static_data().clone());
865
866            Ok(Some(account))
867        } else {
868            Ok(None)
869        }
870    }
871
872    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>> {
873        let conn = self.acquire().await?;
874        if let Some(i) = conn.get_kv("identity").await? {
875            let pickle = self.deserialize_value(&i)?;
876            Ok(Some(PrivateCrossSigningIdentity::from_pickle(pickle).map_err(|_| Error::Unpickle)?))
877        } else {
878            Ok(None)
879        }
880    }
881
882    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<()> {
883        // Serialize calls to `save_pending_changes`; there are multiple await points
884        // below, and we're pickling data as we go, so we don't want to
885        // invalidate data we've previously read and overwrite it in the store.
886        // TODO: #2000 should make this lock go away, or change its shape.
887        let _guard = self.save_changes_lock.lock().await;
888
889        let pickled_account = if let Some(account) = changes.account {
890            *self.static_account.write().unwrap() = Some(account.static_data().clone());
891            Some(account.pickle())
892        } else {
893            None
894        };
895
896        let this = self.clone();
897        self.acquire()
898            .await?
899            .with_transaction(move |txn| {
900                if let Some(pickled_account) = pickled_account {
901                    let serialized_account = this.serialize_value(&pickled_account)?;
902                    txn.set_kv("account", &serialized_account)?;
903                }
904
905                Ok::<_, Error>(())
906            })
907            .await?;
908
909        Ok(())
910    }
911
912    async fn save_changes(&self, changes: Changes) -> Result<()> {
913        // Serialize calls to `save_changes`; there are multiple await points below, and
914        // we're pickling data as we go, so we don't want to invalidate data
915        // we've previously read and overwrite it in the store.
916        // TODO: #2000 should make this lock go away, or change its shape.
917        let _guard = self.save_changes_lock.lock().await;
918
919        let pickled_private_identity =
920            if let Some(i) = changes.private_identity { Some(i.pickle().await) } else { None };
921
922        let mut session_changes = Vec::new();
923
924        for session in changes.sessions {
925            let session_id = self.encode_key("session", session.session_id());
926            let sender_key = self.encode_key("session", session.sender_key().to_base64());
927            let pickle = session.pickle().await;
928            session_changes.push((session_id, sender_key, pickle));
929        }
930
931        let mut inbound_session_changes = Vec::new();
932        for session in changes.inbound_group_sessions {
933            let room_id = self.encode_key("inbound_group_session", session.room_id().as_bytes());
934            let session_id = self.encode_key("inbound_group_session", session.session_id());
935            let pickle = session.pickle().await;
936            let sender_key =
937                self.encode_key("inbound_group_session", session.sender_key().to_base64());
938            inbound_session_changes.push((room_id, session_id, pickle, sender_key));
939        }
940
941        let mut outbound_session_changes = Vec::new();
942        for session in changes.outbound_group_sessions {
943            let room_id = self.encode_key("outbound_group_session", session.room_id().as_bytes());
944            let pickle = session.pickle().await;
945            outbound_session_changes.push((room_id, pickle));
946        }
947
948        let this = self.clone();
949        self.acquire()
950            .await?
951            .with_transaction(move |txn| {
952                if let Some(pickled_private_identity) = &pickled_private_identity {
953                    let serialized_private_identity =
954                        this.serialize_value(pickled_private_identity)?;
955                    txn.set_kv("identity", &serialized_private_identity)?;
956                }
957
958                if let Some(token) = &changes.next_batch_token {
959                    let serialized_token = this.serialize_value(token)?;
960                    txn.set_kv("next_batch_token", &serialized_token)?;
961                }
962
963                if let Some(decryption_key) = &changes.backup_decryption_key {
964                    let serialized_decryption_key = this.serialize_value(decryption_key)?;
965                    txn.set_kv("recovery_key_v1", &serialized_decryption_key)?;
966                }
967
968                if let Some(backup_version) = &changes.backup_version {
969                    let serialized_backup_version = this.serialize_value(backup_version)?;
970                    txn.set_kv("backup_version_v1", &serialized_backup_version)?;
971                }
972
973                if let Some(pickle_key) = &changes.dehydrated_device_pickle_key {
974                    let serialized_pickle_key = this.serialize_value(pickle_key)?;
975                    txn.set_kv(DEHYDRATED_DEVICE_PICKLE_KEY, &serialized_pickle_key)?;
976                }
977
978                for device in changes.devices.new.iter().chain(&changes.devices.changed) {
979                    let user_id = this.encode_key("device", device.user_id().as_bytes());
980                    let device_id = this.encode_key("device", device.device_id().as_bytes());
981                    let data = this.serialize_value(&device)?;
982                    txn.set_device(&user_id, &device_id, &data)?;
983                }
984
985                for device in &changes.devices.deleted {
986                    let user_id = this.encode_key("device", device.user_id().as_bytes());
987                    let device_id = this.encode_key("device", device.device_id().as_bytes());
988                    txn.delete_device(&user_id, &device_id)?;
989                }
990
991                for identity in changes.identities.changed.iter().chain(&changes.identities.new) {
992                    let user_id = this.encode_key("identity", identity.user_id().as_bytes());
993                    let data = this.serialize_value(&identity)?;
994                    txn.set_identity(&user_id, &data)?;
995                }
996
997                for (session_id, sender_key, pickle) in &session_changes {
998                    let serialized_session = this.serialize_value(&pickle)?;
999                    txn.set_session(session_id, sender_key, &serialized_session)?;
1000                }
1001
1002                for (room_id, session_id, pickle, sender_key) in &inbound_session_changes {
1003                    let serialized_session = this.serialize_value(&pickle)?;
1004                    txn.set_inbound_group_session(
1005                        room_id,
1006                        session_id,
1007                        &serialized_session,
1008                        pickle.backed_up,
1009                        Some(sender_key),
1010                        Some(pickle.sender_data.to_type() as u8),
1011                    )?;
1012                }
1013
1014                for (room_id, pickle) in &outbound_session_changes {
1015                    let serialized_session = this.serialize_json(&pickle)?;
1016                    txn.set_outbound_group_session(room_id, &serialized_session)?;
1017                }
1018
1019                for hash in &changes.message_hashes {
1020                    let hash = rmp_serde::to_vec(hash)?;
1021                    txn.add_olm_hash(&hash)?;
1022                }
1023
1024                for request in changes.key_requests {
1025                    let request_id = this.encode_key("key_requests", request.request_id.as_bytes());
1026                    let serialized_request = this.serialize_value(&request)?;
1027                    txn.set_key_request(&request_id, request.sent_out, &serialized_request)?;
1028                }
1029
1030                for (room_id, data) in changes.withheld_session_info {
1031                    for (session_id, event) in data {
1032                        let session_id = this.encode_key("direct_withheld_info", session_id);
1033                        let room_id = this.encode_key("direct_withheld_info", &room_id);
1034                        let serialized_info = this.serialize_json(&event)?;
1035                        txn.set_direct_withheld(&session_id, &room_id, &serialized_info)?;
1036                    }
1037                }
1038
1039                for (room_id, settings) in changes.room_settings {
1040                    let room_id = this.encode_key("room_settings", room_id.as_bytes());
1041                    let value = this.serialize_value(&settings)?;
1042                    txn.set_room_settings(&room_id, &value)?;
1043                }
1044
1045                for secret in changes.secrets {
1046                    let secret_name = this.encode_key("secrets", secret.secret_name.to_string());
1047                    let value = this.serialize_json(&secret)?;
1048                    txn.set_secret(&secret_name, &value)?;
1049                }
1050
1051                for bundle in changes.received_room_key_bundles {
1052                    let room_id =
1053                        this.encode_key("received_room_key_bundle", &bundle.bundle_data.room_id);
1054                    let user_id = this.encode_key("received_room_key_bundle", &bundle.sender_user);
1055                    let value = this.serialize_value(&bundle)?;
1056                    txn.set_received_room_key_bundle(&room_id, &user_id, &value)?;
1057                }
1058
1059                for room in changes.room_key_backups_fully_downloaded {
1060                    let room_id = this.encode_key("room_key_backups_fully_downloaded", &room);
1061                    txn.set_has_downloaded_all_room_keys(&room_id)?;
1062                }
1063
1064                Ok::<_, Error>(())
1065            })
1066            .await?;
1067
1068        Ok(())
1069    }
1070
1071    async fn save_inbound_group_sessions(
1072        &self,
1073        sessions: Vec<InboundGroupSession>,
1074        backed_up_to_version: Option<&str>,
1075    ) -> matrix_sdk_crypto::store::Result<(), Self::Error> {
1076        // Sanity-check that the data in the sessions corresponds to backed_up_version
1077        sessions.iter().for_each(|s| {
1078            let backed_up = s.backed_up();
1079            if backed_up != backed_up_to_version.is_some() {
1080                warn!(
1081                    backed_up,
1082                    backed_up_to_version,
1083                    "Session backed-up flag does not correspond to backup version setting",
1084                );
1085            }
1086        });
1087
1088        // Currently, this store doesn't save the backup version separately, so this
1089        // just delegates to save_changes.
1090        self.save_changes(Changes { inbound_group_sessions: sessions, ..Changes::default() }).await
1091    }
1092
1093    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>> {
1094        let device_keys = self.get_own_device().await?.as_device_keys().clone();
1095
1096        let sessions: Vec<_> = self
1097            .acquire()
1098            .await?
1099            .get_sessions_for_sender_key(self.encode_key("session", sender_key.as_bytes()))
1100            .await?
1101            .into_iter()
1102            .map(|bytes| {
1103                let pickle = self.deserialize_value(&bytes)?;
1104                Session::from_pickle(device_keys.clone(), pickle).map_err(|_| Error::AccountUnset)
1105            })
1106            .collect::<Result<_>>()?;
1107
1108        if sessions.is_empty() { Ok(None) } else { Ok(Some(sessions)) }
1109    }
1110
1111    #[instrument(skip(self))]
1112    async fn get_inbound_group_session(
1113        &self,
1114        room_id: &RoomId,
1115        session_id: &str,
1116    ) -> Result<Option<InboundGroupSession>> {
1117        let session_id = self.encode_key("inbound_group_session", session_id);
1118        let Some((room_id_from_db, value, backed_up)) =
1119            self.acquire().await?.get_inbound_group_session(session_id).await?
1120        else {
1121            return Ok(None);
1122        };
1123
1124        let room_id = self.encode_key("inbound_group_session", room_id.as_bytes());
1125        if *room_id != room_id_from_db {
1126            warn!("expected room_id for session_id doesn't match what's in the DB");
1127            return Ok(None);
1128        }
1129
1130        Ok(Some(self.deserialize_and_unpickle_inbound_group_session(value, backed_up)?))
1131    }
1132
1133    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
1134        self.acquire()
1135            .await?
1136            .get_inbound_group_sessions()
1137            .await?
1138            .into_iter()
1139            .map(|(value, backed_up)| {
1140                self.deserialize_and_unpickle_inbound_group_session(value, backed_up)
1141            })
1142            .collect()
1143    }
1144
1145    async fn get_inbound_group_sessions_by_room_id(
1146        &self,
1147        room_id: &RoomId,
1148    ) -> Result<Vec<InboundGroupSession>> {
1149        let room_id = self.encode_key("inbound_group_session", room_id.as_bytes());
1150        self.acquire()
1151            .await?
1152            .get_inbound_group_sessions_by_room_id(room_id)
1153            .await?
1154            .into_iter()
1155            .map(|(value, backed_up)| {
1156                self.deserialize_and_unpickle_inbound_group_session(value, backed_up)
1157            })
1158            .collect()
1159    }
1160
1161    async fn get_inbound_group_sessions_for_device_batch(
1162        &self,
1163        sender_key: Curve25519PublicKey,
1164        sender_data_type: SenderDataType,
1165        after_session_id: Option<String>,
1166        limit: usize,
1167    ) -> Result<Vec<InboundGroupSession>, Self::Error> {
1168        let after_session_id =
1169            after_session_id.map(|session_id| self.encode_key("inbound_group_session", session_id));
1170        let sender_key = self.encode_key("inbound_group_session", sender_key.to_base64());
1171
1172        self.acquire()
1173            .await?
1174            .get_inbound_group_sessions_for_device_batch(
1175                sender_key,
1176                sender_data_type,
1177                after_session_id,
1178                limit,
1179            )
1180            .await?
1181            .into_iter()
1182            .map(|(value, backed_up)| {
1183                self.deserialize_and_unpickle_inbound_group_session(value, backed_up)
1184            })
1185            .collect()
1186    }
1187
1188    async fn inbound_group_session_counts(
1189        &self,
1190        backup_version: Option<&str>,
1191    ) -> Result<RoomKeyCounts> {
1192        Ok(self.acquire().await?.get_inbound_group_session_counts(backup_version).await?)
1193    }
1194
1195    async fn inbound_group_sessions_for_backup(
1196        &self,
1197        _backup_version: &str,
1198        limit: usize,
1199    ) -> Result<Vec<InboundGroupSession>> {
1200        self.acquire()
1201            .await?
1202            .get_inbound_group_sessions_for_backup(limit)
1203            .await?
1204            .into_iter()
1205            .map(|value| self.deserialize_and_unpickle_inbound_group_session(value, false))
1206            .collect()
1207    }
1208
1209    async fn mark_inbound_group_sessions_as_backed_up(
1210        &self,
1211        _backup_version: &str,
1212        session_ids: &[(&RoomId, &str)],
1213    ) -> Result<()> {
1214        Ok(self
1215            .acquire()
1216            .await?
1217            .mark_inbound_group_sessions_as_backed_up(
1218                session_ids
1219                    .iter()
1220                    .map(|(_, s)| self.encode_key("inbound_group_session", s))
1221                    .collect(),
1222            )
1223            .await?)
1224    }
1225
1226    async fn reset_backup_state(&self) -> Result<()> {
1227        Ok(self.acquire().await?.reset_inbound_group_session_backup_state().await?)
1228    }
1229
1230    async fn load_backup_keys(&self) -> Result<BackupKeys> {
1231        let conn = self.acquire().await?;
1232
1233        let backup_version = conn
1234            .get_kv("backup_version_v1")
1235            .await?
1236            .map(|value| self.deserialize_value(&value))
1237            .transpose()?;
1238
1239        let decryption_key = conn
1240            .get_kv("recovery_key_v1")
1241            .await?
1242            .map(|value| self.deserialize_value(&value))
1243            .transpose()?;
1244
1245        Ok(BackupKeys { backup_version, decryption_key })
1246    }
1247
1248    async fn load_dehydrated_device_pickle_key(&self) -> Result<Option<DehydratedDeviceKey>> {
1249        let conn = self.acquire().await?;
1250
1251        conn.get_kv(DEHYDRATED_DEVICE_PICKLE_KEY)
1252            .await?
1253            .map(|value| self.deserialize_value(&value))
1254            .transpose()
1255    }
1256
1257    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error> {
1258        let conn = self.acquire().await?;
1259        conn.clear_kv(DEHYDRATED_DEVICE_PICKLE_KEY).await?;
1260
1261        Ok(())
1262    }
1263    async fn get_outbound_group_session(
1264        &self,
1265        room_id: &RoomId,
1266    ) -> Result<Option<OutboundGroupSession>> {
1267        let room_id = self.encode_key("outbound_group_session", room_id.as_bytes());
1268        let Some(value) = self.acquire().await?.get_outbound_group_session(room_id).await? else {
1269            return Ok(None);
1270        };
1271
1272        let account_info = self.get_static_account().ok_or(Error::AccountUnset)?;
1273
1274        let pickle = self.deserialize_json(&value)?;
1275        let session = OutboundGroupSession::from_pickle(
1276            account_info.device_id,
1277            account_info.identity_keys,
1278            pickle,
1279        )
1280        .map_err(|_| Error::Unpickle)?;
1281
1282        return Ok(Some(session));
1283    }
1284
1285    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>> {
1286        self.acquire()
1287            .await?
1288            .get_tracked_users()
1289            .await?
1290            .iter()
1291            .map(|value| self.deserialize_value(value))
1292            .collect()
1293    }
1294
1295    async fn save_tracked_users(&self, tracked_users: &[(&UserId, bool)]) -> Result<()> {
1296        let users: Vec<(Key, Vec<u8>)> = tracked_users
1297            .iter()
1298            .map(|(u, d)| {
1299                let user_id = self.encode_key("tracked_users", u.as_bytes());
1300                let data =
1301                    self.serialize_value(&TrackedUser { user_id: (*u).into(), dirty: *d })?;
1302                Ok((user_id, data))
1303            })
1304            .collect::<Result<_>>()?;
1305
1306        Ok(self.acquire().await?.add_tracked_users(users).await?)
1307    }
1308
1309    async fn get_device(
1310        &self,
1311        user_id: &UserId,
1312        device_id: &DeviceId,
1313    ) -> Result<Option<DeviceData>> {
1314        let user_id = self.encode_key("device", user_id.as_bytes());
1315        let device_id = self.encode_key("device", device_id.as_bytes());
1316        Ok(self
1317            .acquire()
1318            .await?
1319            .get_device(user_id, device_id)
1320            .await?
1321            .map(|value| self.deserialize_value(&value))
1322            .transpose()?)
1323    }
1324
1325    async fn get_user_devices(
1326        &self,
1327        user_id: &UserId,
1328    ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
1329        let user_id = self.encode_key("device", user_id.as_bytes());
1330        self.acquire()
1331            .await?
1332            .get_user_devices(user_id)
1333            .await?
1334            .into_iter()
1335            .map(|value| {
1336                let device: DeviceData = self.deserialize_value(&value)?;
1337                Ok((device.device_id().to_owned(), device))
1338            })
1339            .collect()
1340    }
1341
1342    async fn get_own_device(&self) -> Result<DeviceData> {
1343        let account_info = self.get_static_account().ok_or(Error::AccountUnset)?;
1344
1345        Ok(self
1346            .get_device(&account_info.user_id, &account_info.device_id)
1347            .await?
1348            .expect("We should be able to find our own device."))
1349    }
1350
1351    async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentityData>> {
1352        let user_id = self.encode_key("identity", user_id.as_bytes());
1353        Ok(self
1354            .acquire()
1355            .await?
1356            .get_user_identity(user_id)
1357            .await?
1358            .map(|value| self.deserialize_value(&value))
1359            .transpose()?)
1360    }
1361
1362    async fn is_message_known(
1363        &self,
1364        message_hash: &matrix_sdk_crypto::olm::OlmMessageHash,
1365    ) -> Result<bool> {
1366        let value = rmp_serde::to_vec(message_hash)?;
1367        Ok(self.acquire().await?.has_olm_hash(value).await?)
1368    }
1369
1370    async fn get_outgoing_secret_requests(
1371        &self,
1372        request_id: &TransactionId,
1373    ) -> Result<Option<GossipRequest>> {
1374        let request_id = self.encode_key("key_requests", request_id.as_bytes());
1375        Ok(self
1376            .acquire()
1377            .await?
1378            .get_outgoing_secret_request(request_id)
1379            .await?
1380            .map(|(value, sent_out)| self.deserialize_key_request(&value, sent_out))
1381            .transpose()?)
1382    }
1383
1384    async fn get_secret_request_by_info(
1385        &self,
1386        key_info: &SecretInfo,
1387    ) -> Result<Option<GossipRequest>> {
1388        let requests = self.acquire().await?.get_outgoing_secret_requests().await?;
1389        for (request, sent_out) in requests {
1390            let request = self.deserialize_key_request(&request, sent_out)?;
1391            if request.info == *key_info {
1392                return Ok(Some(request));
1393            }
1394        }
1395        Ok(None)
1396    }
1397
1398    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
1399        self.acquire()
1400            .await?
1401            .get_unsent_secret_requests()
1402            .await?
1403            .iter()
1404            .map(|value| {
1405                let request = self.deserialize_key_request(value, false)?;
1406                Ok(request)
1407            })
1408            .collect()
1409    }
1410
1411    async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> {
1412        let request_id = self.encode_key("key_requests", request_id.as_bytes());
1413        Ok(self.acquire().await?.delete_key_request(request_id).await?)
1414    }
1415
1416    async fn get_secrets_from_inbox(
1417        &self,
1418        secret_name: &SecretName,
1419    ) -> Result<Vec<GossippedSecret>> {
1420        let secret_name = self.encode_key("secrets", secret_name.to_string());
1421
1422        self.acquire()
1423            .await?
1424            .get_secrets_from_inbox(secret_name)
1425            .await?
1426            .into_iter()
1427            .map(|value| self.deserialize_json(value.as_ref()))
1428            .collect()
1429    }
1430
1431    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<()> {
1432        let secret_name = self.encode_key("secrets", secret_name.to_string());
1433        self.acquire().await?.delete_secrets_from_inbox(secret_name).await
1434    }
1435
1436    async fn get_withheld_info(
1437        &self,
1438        room_id: &RoomId,
1439        session_id: &str,
1440    ) -> Result<Option<RoomKeyWithheldEntry>> {
1441        let room_id = self.encode_key("direct_withheld_info", room_id);
1442        let session_id = self.encode_key("direct_withheld_info", session_id);
1443
1444        self.acquire()
1445            .await?
1446            .get_direct_withheld_info(session_id, room_id)
1447            .await?
1448            .map(|value| {
1449                let info = self.deserialize_json::<RoomKeyWithheldEntry>(&value)?;
1450                Ok(info)
1451            })
1452            .transpose()
1453    }
1454
1455    async fn get_withheld_sessions_by_room_id(
1456        &self,
1457        room_id: &RoomId,
1458    ) -> matrix_sdk_crypto::store::Result<Vec<RoomKeyWithheldEntry>, Self::Error> {
1459        let room_id = self.encode_key("direct_withheld_info", room_id);
1460
1461        self.acquire()
1462            .await?
1463            .get_withheld_sessions_by_room_id(room_id)
1464            .await?
1465            .into_iter()
1466            .map(|value| self.deserialize_json(&value))
1467            .collect()
1468    }
1469
1470    async fn get_room_settings(&self, room_id: &RoomId) -> Result<Option<RoomSettings>> {
1471        let room_id = self.encode_key("room_settings", room_id.as_bytes());
1472        let Some(value) = self.acquire().await?.get_room_settings(room_id).await? else {
1473            return Ok(None);
1474        };
1475
1476        let settings = self.deserialize_value(&value)?;
1477
1478        return Ok(Some(settings));
1479    }
1480
1481    async fn get_received_room_key_bundle_data(
1482        &self,
1483        room_id: &RoomId,
1484        user_id: &UserId,
1485    ) -> Result<Option<StoredRoomKeyBundleData>> {
1486        let room_id = self.encode_key("received_room_key_bundle", room_id);
1487        let user_id = self.encode_key("received_room_key_bundle", user_id);
1488        self.acquire()
1489            .await?
1490            .get_received_room_key_bundle(room_id, user_id)
1491            .await?
1492            .map(|value| self.deserialize_value(&value))
1493            .transpose()
1494    }
1495
1496    async fn has_downloaded_all_room_keys(&self, room_id: &RoomId) -> Result<bool> {
1497        let room_id = self.encode_key("room_key_backups_fully_downloaded", room_id);
1498        self.acquire().await?.has_downloaded_all_room_keys(room_id).await
1499    }
1500
1501    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>> {
1502        let Some(serialized) = self.acquire().await?.get_kv(key).await? else {
1503            return Ok(None);
1504        };
1505        let value = if let Some(cipher) = &self.store_cipher {
1506            let encrypted = rmp_serde::from_slice(&serialized)?;
1507            cipher.decrypt_value_data(encrypted)?
1508        } else {
1509            serialized
1510        };
1511
1512        Ok(Some(value))
1513    }
1514
1515    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<()> {
1516        let serialized = if let Some(cipher) = &self.store_cipher {
1517            let encrypted = cipher.encrypt_value_data(value)?;
1518            rmp_serde::to_vec_named(&encrypted)?
1519        } else {
1520            value
1521        };
1522
1523        self.acquire().await?.set_kv(key, serialized).await?;
1524        Ok(())
1525    }
1526
1527    async fn remove_custom_value(&self, key: &str) -> Result<()> {
1528        let key = key.to_owned();
1529        self.acquire()
1530            .await?
1531            .interact(move |conn| conn.execute("DELETE FROM kv WHERE key = ?1", (&key,)))
1532            .await
1533            .unwrap()?;
1534        Ok(())
1535    }
1536
1537    #[instrument(skip(self))]
1538    async fn try_take_leased_lock(
1539        &self,
1540        lease_duration_ms: u32,
1541        key: &str,
1542        holder: &str,
1543    ) -> Result<Option<CrossProcessLockGeneration>> {
1544        let key = key.to_owned();
1545        let holder = holder.to_owned();
1546
1547        let now: u64 = MilliSecondsSinceUnixEpoch::now().get().into();
1548        let expiration = now + lease_duration_ms as u64;
1549
1550        // Learn about the `excluded` keyword in https://sqlite.org/lang_upsert.html.
1551        let generation = self
1552            .acquire()
1553            .await?
1554            .with_transaction(move |txn| {
1555                txn.query_row(
1556                    "INSERT INTO lease_locks (key, holder, expiration)
1557                    VALUES (?1, ?2, ?3)
1558                    ON CONFLICT (key)
1559                    DO
1560                        UPDATE SET
1561                            holder = excluded.holder,
1562                            expiration = excluded.expiration,
1563                            generation =
1564                                CASE holder
1565                                    WHEN excluded.holder THEN generation
1566                                    ELSE generation + 1
1567                                END
1568                        WHERE
1569                            holder = excluded.holder
1570                            OR expiration < ?4
1571                    RETURNING generation
1572                    ",
1573                    (key, holder, expiration, now),
1574                    |row| row.get(0),
1575                )
1576                .optional()
1577            })
1578            .await?;
1579
1580        Ok(generation)
1581    }
1582
1583    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error> {
1584        let conn = self.acquire().await?;
1585        if let Some(token) = conn.get_kv("next_batch_token").await? {
1586            let maybe_token: Option<String> = self.deserialize_value(&token)?;
1587            Ok(maybe_token)
1588        } else {
1589            Ok(None)
1590        }
1591    }
1592
1593    async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
1594        Ok(Some(self.pool.get().await?.get_db_size().await?))
1595    }
1596}
1597
1598#[cfg(test)]
1599mod tests {
1600    use std::path::Path;
1601
1602    use matrix_sdk_common::deserialized_responses::WithheldCode;
1603    use matrix_sdk_crypto::{
1604        cryptostore_integration_tests, cryptostore_integration_tests_time, olm::SenderDataType,
1605        store::CryptoStore,
1606    };
1607    use matrix_sdk_test::async_test;
1608    use once_cell::sync::Lazy;
1609    use ruma::{device_id, room_id, user_id};
1610    use similar_asserts::assert_eq;
1611    use tempfile::{TempDir, tempdir};
1612    use tokio::fs;
1613
1614    use super::SqliteCryptoStore;
1615    use crate::SqliteStoreConfig;
1616
1617    static TMP_DIR: Lazy<TempDir> = Lazy::new(|| tempdir().unwrap());
1618
1619    struct TestDb {
1620        // Needs to be kept alive because the Drop implementation for TempDir deletes the
1621        // directory.
1622        _dir: TempDir,
1623        database: SqliteCryptoStore,
1624    }
1625
1626    fn copy_db(data_path: &str) -> TempDir {
1627        let db_name = super::DATABASE_NAME;
1628
1629        let manifest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
1630        let database_path = manifest_path.join(data_path).join(db_name);
1631
1632        let tmpdir = tempdir().unwrap();
1633        let destination = tmpdir.path().join(db_name);
1634
1635        // Copy the test database to the tempdir so our test runs are idempotent.
1636        std::fs::copy(&database_path, destination).unwrap();
1637
1638        tmpdir
1639    }
1640
1641    async fn get_test_db(data_path: &str, passphrase: Option<&str>) -> TestDb {
1642        let tmpdir = copy_db(data_path);
1643
1644        let database = SqliteCryptoStore::open(tmpdir.path(), passphrase)
1645            .await
1646            .expect("Can't open the test store");
1647
1648        TestDb { _dir: tmpdir, database }
1649    }
1650
1651    #[async_test]
1652    async fn test_pool_size() {
1653        let store_open_config =
1654            SqliteStoreConfig::new(TMP_DIR.path().join("test_pool_size")).pool_max_size(42);
1655
1656        let store = SqliteCryptoStore::open_with_config(store_open_config).await.unwrap();
1657
1658        assert_eq!(store.pool.status().max_size, 42);
1659    }
1660
1661    /// Test that we didn't regress in our storage layer by loading data from a
1662    /// pre-filled database, or in other words use a test vector for this.
1663    #[async_test]
1664    async fn test_open_test_vector_store() {
1665        let TestDb { _dir: _, database } = get_test_db("testing/data/storage", None).await;
1666
1667        let account = database
1668            .load_account()
1669            .await
1670            .unwrap()
1671            .expect("The test database is prefilled with data, we should find an account");
1672
1673        let user_id = account.user_id();
1674        let device_id = account.device_id();
1675
1676        assert_eq!(
1677            user_id.as_str(),
1678            "@pjtest:synapse-oidc.element.dev",
1679            "The user ID should match to the one we expect."
1680        );
1681
1682        assert_eq!(
1683            device_id.as_str(),
1684            "v4TqgcuIH6",
1685            "The device ID should match to the one we expect."
1686        );
1687
1688        let device = database
1689            .get_device(user_id, device_id)
1690            .await
1691            .unwrap()
1692            .expect("Our own device should be found in the store.");
1693
1694        assert_eq!(device.device_id(), device_id);
1695        assert_eq!(device.user_id(), user_id);
1696
1697        assert_eq!(
1698            device.ed25519_key().expect("The device should have a Ed25519 key.").to_base64(),
1699            "+cxl1Gl3du5i7UJwfWnoRDdnafFF+xYdAiTYYhYLr8s"
1700        );
1701
1702        assert_eq!(
1703            device.curve25519_key().expect("The device should have a Curve25519 key.").to_base64(),
1704            "4SL9eEUlpyWSUvjljC5oMjknHQQJY7WZKo5S1KL/5VU"
1705        );
1706
1707        let identity = database
1708            .get_user_identity(user_id)
1709            .await
1710            .unwrap()
1711            .expect("The store should contain an identity.");
1712
1713        assert_eq!(identity.user_id(), user_id);
1714
1715        let identity = identity
1716            .own()
1717            .expect("The identity should be of the correct type, it should be our own identity.");
1718
1719        let master_key = identity
1720            .master_key()
1721            .get_first_key()
1722            .expect("Our own identity should have a master key");
1723
1724        assert_eq!(master_key.to_base64(), "iCUEtB1RwANeqRa5epDrblLk4mer/36sylwQ5hYY3oE");
1725    }
1726
1727    /// Test that we didn't regress in our storage layer by loading data from a
1728    /// pre-filled database, or in other words use a test vector for this.
1729    #[async_test]
1730    async fn test_open_test_vector_encrypted_store() {
1731        let TestDb { _dir: _, database } = get_test_db(
1732            "testing/data/storage/alice",
1733            Some(concat!(
1734                "/rCia2fYAJ+twCZ1Xm2mxFCYcmJdyzkdJjwtgXsziWpYS/UeNxnixuSieuwZXm+x1VsJHmWpl",
1735                "H+QIQBZpEGZtC9/S/l8xK+WOCesmET0o6yJ/KP73ofDtjBlnNpPwuHLKFpyTbyicpCgQ4UT+5E",
1736                "UBuJ08TY9Ujdf1D13k5kr5tSZUefDKKCuG1fCRqlU8ByRas1PMQsZxT2W8t7QgBrQiiGmhpo/O",
1737                "Ti4hfx97GOxncKcxTzppiYQNoHs/f15+XXQD7/oiCcqRIuUlXNsU6hRpFGmbYx2Pi1eyQViQCt",
1738                "B5dAEiSD0N8U81wXYnpynuTPtnL+hfnOJIn7Sy7mkERQeKg"
1739            )),
1740        )
1741        .await;
1742
1743        let account = database
1744            .load_account()
1745            .await
1746            .unwrap()
1747            .expect("The test database is prefilled with data, we should find an account");
1748
1749        let user_id = account.user_id();
1750        let device_id = account.device_id();
1751
1752        assert_eq!(
1753            user_id.as_str(),
1754            "@alice:localhost",
1755            "The user ID should match to the one we expect."
1756        );
1757
1758        assert_eq!(
1759            device_id.as_str(),
1760            "JVVORTHFXY",
1761            "The device ID should match to the one we expect."
1762        );
1763
1764        let tracked_users =
1765            database.load_tracked_users().await.expect("Should be tracking some users");
1766
1767        assert_eq!(tracked_users.len(), 6);
1768
1769        let known_users = vec![
1770            user_id!("@alice:localhost"),
1771            user_id!("@dehydration3:localhost"),
1772            user_id!("@eve:localhost"),
1773            user_id!("@bob:localhost"),
1774            user_id!("@malo:localhost"),
1775            user_id!("@carl:localhost"),
1776        ];
1777
1778        // load the identities
1779        for user_id in known_users {
1780            database.get_user_identity(user_id).await.expect("Should load this identity").unwrap();
1781        }
1782
1783        let carl_identity =
1784            database.get_user_identity(user_id!("@carl:localhost")).await.unwrap().unwrap();
1785
1786        assert_eq!(
1787            carl_identity.master_key().get_first_key().unwrap().to_base64(),
1788            "CdhKYYDeBDQveOioXEGWhTPCyzc63Irpar3CNyfun2Q"
1789        );
1790        assert!(!carl_identity.was_previously_verified());
1791
1792        let bob_identity =
1793            database.get_user_identity(user_id!("@bob:localhost")).await.unwrap().unwrap();
1794
1795        assert_eq!(
1796            bob_identity.master_key().get_first_key().unwrap().to_base64(),
1797            "COh2GYOJWSjem5QPRCaGp9iWV83IELG1IzLKW2S3pFY"
1798        );
1799        // Bob is verified so this flag should be set
1800        assert!(bob_identity.was_previously_verified());
1801
1802        let known_devices = vec![
1803            (device_id!("OPXQHCZSKW"), user_id!("@alice:localhost")),
1804            // a dehydrated one
1805            (
1806                device_id!("EvW+9IrGR10KVgVeZP25/KaPfx4R86FofVMcaz7VOho"),
1807                user_id!("@alice:localhost"),
1808            ),
1809            (device_id!("HEEFRFQENV"), user_id!("@alice:localhost")),
1810            (device_id!("JVVORTHFXY"), user_id!("@alice:localhost")),
1811            (device_id!("NQUWWSKKHS"), user_id!("@alice:localhost")),
1812            (device_id!("ORBLPFYCPG"), user_id!("@alice:localhost")),
1813            (device_id!("YXOWENSEGM"), user_id!("@dehydration3:localhost")),
1814            (device_id!("VXLFMYCHXC"), user_id!("@bob:localhost")),
1815            (device_id!("FDGDQAEWOW"), user_id!("@bob:localhost")),
1816            (device_id!("VXLFMYCHXC"), user_id!("@bob:localhost")),
1817            (device_id!("FDGDQAEWOW"), user_id!("@bob:localhost")),
1818            (device_id!("QKUKWJTTQC"), user_id!("@malo:localhost")),
1819            (device_id!("LOUXJECTFG"), user_id!("@malo:localhost")),
1820            (device_id!("MKKMAEVLPB"), user_id!("@carl:localhost")),
1821        ];
1822
1823        for (device_id, user_id) in known_devices {
1824            database.get_device(user_id, device_id).await.expect("Should load the device").unwrap();
1825        }
1826
1827        let known_sender_key_to_session_count = vec![
1828            ("FfYcYfDF4nWy+LHdK6CEpIMlFAQDORc30WUkghL06kM", 1),
1829            ("EvW+9IrGR10KVgVeZP25/KaPfx4R86FofVMcaz7VOho", 1),
1830            ("hAGsoA4a9M6wwEUX5Q1jux1i+tUngLi01n5AmhDoHTY", 1),
1831            ("aKqtSJymLzuoglWFwPGk1r/Vm2LE2hFESzXxn4RNjRM", 0),
1832            ("zHK1psCrgeMn0kaz8hcdvA3INyar9jg1yfrSp0p1pHo", 1),
1833            ("1QmBA316Wj5jIFRwNOti6N6Xh/vW0bsYCcR4uPfy8VQ", 1),
1834            ("g5ef2vZF3VXgSPyODIeXpyHIRkuthvLhGvd6uwYggWU", 1),
1835            ("o7hfupPd1VsNkRIvdlH6ujrEJFSKjFCGbxhAd31XxjI", 1),
1836            ("Z3RxKQLxY7xpP+ZdOGR2SiNE37SrvmRhW7GPu1UGdm8", 1),
1837            ("GDomaav8NiY3J+dNEeApJm+O0FooJ3IpVaIyJzCN4w4", 1),
1838            ("7m7fqkHyEr47V5s/KjaxtJMOr3pSHrrns2q2lWpAQi8", 0),
1839            ("9psAkPUIF8vNbWbnviX3PlwRcaeO53EHJdNtKpTY1X0", 0),
1840            ("mqanh+ztw5oRtpqYQgLGW864i6NY2zpoKMIlrcyC+Aw", 0),
1841            ("fJU/TJdbsv7tVbbpHw1Ke73ziElnM32cNhP2WIg4T10", 0),
1842            ("sUIeFeFcCZoa5IC6nJ6Vrbvztcyx09m8BBg57XKRClg", 1),
1843        ];
1844
1845        for (id, count) in known_sender_key_to_session_count {
1846            let olm_sessions =
1847                database.get_sessions(id).await.expect("Should have some olm sessions");
1848
1849            println!("### Session id: {id:?}");
1850            assert_eq!(olm_sessions.map_or(0, |v| v.len()), count);
1851        }
1852
1853        let inbound_group_sessions = database.get_inbound_group_sessions().await.unwrap();
1854        assert_eq!(inbound_group_sessions.len(), 15);
1855        let known_inbound_group_sessions = vec![
1856            (
1857                "5hNAxrLai3VI0LKBwfh3wLfksfBFWds0W1a5X5/vSXA",
1858                room_id!("!SRstFdydzrGwJYtVfm:localhost"),
1859            ),
1860            (
1861                "M6d2eU3y54gaYTbvGSlqa/xc1Az35l56Cp9sxzHWO4g",
1862                room_id!("!SRstFdydzrGwJYtVfm:localhost"),
1863            ),
1864            (
1865                "IrydwXkRk2N2AqUMIVmLL3oJgMq14R9KId0P/uSD100",
1866                room_id!("!SRstFdydzrGwJYtVfm:localhost"),
1867            ),
1868            (
1869                "Y74+l9jTo7N5UF+GQwdpgJGe4sn1+QtWITq7BxulHIE",
1870                room_id!("!SRstFdydzrGwJYtVfm:localhost"),
1871            ),
1872            (
1873                "HpJxQR57WbQGdY6w2Q+C16znVvbXGa+JvQdRoMpWbXg",
1874                room_id!("!SRstFdydzrGwJYtVfm:localhost"),
1875            ),
1876            (
1877                "Xetvi+ydFkZt8dpONGFbEusQb/Chc2V0XlLByZhsbgE",
1878                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1879            ),
1880            (
1881                "wv/WN/39akyerIXczTaIpjAuLnwgXKRtbXFSEHiJqxo",
1882                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1883            ),
1884            (
1885                "nA4gQwL//Cm8OdlyjABl/jChbPT/cP5V4Sd8iuE6H0s",
1886                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1887            ),
1888            (
1889                "bAAgqFeRDTjfEqL6Qf/c9mk55zoNDCSlboAIRd6b0hw",
1890                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1891            ),
1892            (
1893                "exPbsMMdGfAG2qmDdFtpAn+koVprfzS0Zip/RA9QRCE",
1894                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1895            ),
1896            (
1897                "h+om7oSw/ZV94fcKaoe8FGXJwQXWOfKQfzbGgNWQILI",
1898                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1899            ),
1900            (
1901                "ul3VXonpgk4lO2L3fEWubP/nxsTmLHqu5v8ZM9vHEcw",
1902                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1903            ),
1904            (
1905                "JXY15UxC3az2mwg8uX4qwgxfvCM4aygiIWMcdNiVQoc",
1906                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1907            ),
1908            (
1909                "OGB9lObr9kWUvha9tB5sMfOF/Mztk24JwQz/nwg3iFQ",
1910                room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
1911            ),
1912            (
1913                "SFkHcbxjUOYF7mUAYI/oEMDZFaXszQbCN6Jza7iemj0",
1914                room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
1915            ),
1916        ];
1917
1918        // ensure we can load them all
1919        for (session_id, room_id) in &known_inbound_group_sessions {
1920            database
1921                .get_inbound_group_session(room_id, session_id)
1922                .await
1923                .expect("Should be able to load inbound group session")
1924                .unwrap();
1925        }
1926
1927        let bob_sender_verified = database
1928            .get_inbound_group_session(
1929                room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
1930                "exPbsMMdGfAG2qmDdFtpAn+koVprfzS0Zip/RA9QRCE",
1931            )
1932            .await
1933            .unwrap()
1934            .unwrap();
1935
1936        assert_eq!(bob_sender_verified.sender_data.to_type(), SenderDataType::SenderVerified);
1937        assert!(bob_sender_verified.backed_up());
1938        assert!(!bob_sender_verified.has_been_imported());
1939
1940        let alice_unknown_device = database
1941            .get_inbound_group_session(
1942                room_id!("!SRstFdydzrGwJYtVfm:localhost"),
1943                "IrydwXkRk2N2AqUMIVmLL3oJgMq14R9KId0P/uSD100",
1944            )
1945            .await
1946            .unwrap()
1947            .unwrap();
1948
1949        assert_eq!(alice_unknown_device.sender_data.to_type(), SenderDataType::UnknownDevice);
1950        assert!(alice_unknown_device.backed_up());
1951        assert!(alice_unknown_device.has_been_imported());
1952
1953        let carl_tofu_session = database
1954            .get_inbound_group_session(
1955                room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
1956                "OGB9lObr9kWUvha9tB5sMfOF/Mztk24JwQz/nwg3iFQ",
1957            )
1958            .await
1959            .unwrap()
1960            .unwrap();
1961
1962        assert_eq!(carl_tofu_session.sender_data.to_type(), SenderDataType::SenderUnverified);
1963        assert!(carl_tofu_session.backed_up());
1964        assert!(!carl_tofu_session.has_been_imported());
1965
1966        // Load outbound sessions
1967        database
1968            .get_outbound_group_session(room_id!("!OgRiTRMaUzLdpCeDBM:localhost"))
1969            .await
1970            .unwrap()
1971            .unwrap();
1972        database
1973            .get_outbound_group_session(room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"))
1974            .await
1975            .unwrap()
1976            .unwrap();
1977        database
1978            .get_outbound_group_session(room_id!("!SRstFdydzrGwJYtVfm:localhost"))
1979            .await
1980            .unwrap()
1981            .unwrap();
1982
1983        let withheld_info = database
1984            .get_withheld_info(
1985                room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
1986                "SASgZ+EklvAF4QxJclMlDRlmL0fAMjAJJIKFMdb4Ht0",
1987            )
1988            .await
1989            .expect("This session should be withheld")
1990            .unwrap();
1991
1992        assert_eq!(withheld_info.content.withheld_code(), WithheldCode::Unverified);
1993
1994        let backup_keys = database.load_backup_keys().await.expect("backup key should be cached");
1995        assert_eq!(backup_keys.backup_version.unwrap(), "6");
1996        assert!(backup_keys.decryption_key.is_some());
1997    }
1998
1999    async fn get_store(
2000        name: &str,
2001        passphrase: Option<&str>,
2002        clear_data: bool,
2003    ) -> SqliteCryptoStore {
2004        let tmpdir_path = TMP_DIR.path().join(name);
2005
2006        if clear_data {
2007            let _ = fs::remove_dir_all(&tmpdir_path).await;
2008        }
2009
2010        SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), passphrase)
2011            .await
2012            .expect("Can't create a secret protected store")
2013    }
2014
2015    cryptostore_integration_tests!();
2016    cryptostore_integration_tests_time!();
2017}
2018
2019#[cfg(test)]
2020mod encrypted_tests {
2021    use matrix_sdk_crypto::{cryptostore_integration_tests, cryptostore_integration_tests_time};
2022    use once_cell::sync::Lazy;
2023    use tempfile::{TempDir, tempdir};
2024    use tokio::fs;
2025
2026    use super::SqliteCryptoStore;
2027
2028    static TMP_DIR: Lazy<TempDir> = Lazy::new(|| tempdir().unwrap());
2029
2030    async fn get_store(
2031        name: &str,
2032        passphrase: Option<&str>,
2033        clear_data: bool,
2034    ) -> SqliteCryptoStore {
2035        let tmpdir_path = TMP_DIR.path().join(name);
2036        let pass = passphrase.unwrap_or("default_test_password");
2037
2038        if clear_data {
2039            let _ = fs::remove_dir_all(&tmpdir_path).await;
2040        }
2041
2042        SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), Some(pass))
2043            .await
2044            .expect("Can't create a secret protected store")
2045    }
2046
2047    cryptostore_integration_tests!();
2048    cryptostore_integration_tests_time!();
2049}