Skip to main content

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