1use std::{
16 collections::HashMap,
17 fmt,
18 ops::Deref,
19 path::{Path, PathBuf},
20 sync::{Arc, RwLock},
21};
22
23use async_trait::async_trait;
24use deadpool::managed::PoolConfig;
25use matrix_sdk_base::cross_process_lock::CrossProcessLockGeneration;
26use matrix_sdk_crypto::{
27 Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, TrackedUser, UserIdentityData,
28 olm::{
29 InboundGroupSession, OutboundGroupSession, PickledInboundGroupSession,
30 PrivateCrossSigningIdentity, SenderDataType, Session, StaticAccountData,
31 },
32 store::{
33 CryptoStore,
34 types::{
35 BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts,
36 RoomKeyWithheldEntry, RoomPendingKeyBundleDetails, RoomSettings,
37 StoredRoomKeyBundleData,
38 },
39 },
40};
41use matrix_sdk_store_encryption::StoreCipher;
42use ruma::{
43 DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, RoomId, TransactionId, UserId,
44 events::secret::request::SecretName,
45};
46use rusqlite::{OptionalExtension, named_params, params_from_iter};
47use tokio::{
48 fs,
49 sync::{Mutex, OwnedMutexGuard},
50};
51use tracing::{debug, instrument, warn};
52use vodozemac::Curve25519PublicKey;
53use zeroize::Zeroizing;
54
55use crate::{
56 OpenStoreError, RuntimeConfig, Secret, SqliteStoreConfig,
57 connection::{self, Connection as SqliteAsyncConn, Pool as SqlitePool, SqliteConnections},
58 error::{Error, Result},
59 utils::{
60 EncryptableStore, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
61 SqliteKeyValueStoreConnExt,
62 },
63};
64
65const DATABASE_NAME: &str = "matrix-sdk-crypto.sqlite3";
67
68#[derive(Clone)]
70pub struct SqliteCryptoStore {
71 store_cipher: Option<Arc<StoreCipher>>,
72
73 connections: Arc<Mutex<Option<SqliteConnections>>>,
76
77 db_path: PathBuf,
79
80 pool_config: PoolConfig,
82
83 runtime_config: RuntimeConfig,
85
86 static_account: Arc<RwLock<Option<StaticAccountData>>>,
88 save_changes_lock: Arc<Mutex<()>>,
89}
90
91#[cfg(not(tarpaulin_include))]
92impl fmt::Debug for SqliteCryptoStore {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_struct("SqliteCryptoStore").finish_non_exhaustive()
95 }
96}
97
98impl EncryptableStore for SqliteCryptoStore {
99 fn get_cypher(&self) -> Option<&StoreCipher> {
100 self.store_cipher.as_deref()
101 }
102}
103
104impl SqliteCryptoStore {
105 pub(crate) async fn create_raw(
117 secret: Option<Secret>,
118 pool: SqlitePool,
119 conn: SqliteAsyncConn,
120 pool_config: PoolConfig,
121 runtime_config: RuntimeConfig,
122 ) -> Result<Self, OpenStoreError> {
123 let store_cipher = match secret {
124 Some(s) => Some(Arc::new(conn.get_or_create_store_cipher(s).await?)),
125 None => None,
126 };
127
128 let db_path = pool.manager().database_path.clone();
129
130 Ok(Self {
131 store_cipher,
132 connections: Arc::new(Mutex::new(Some(SqliteConnections {
133 pool,
134 write_connection: Arc::new(Mutex::new(conn)),
135 }))),
136 db_path,
137 pool_config,
138 runtime_config,
139 static_account: Arc::new(RwLock::new(None)),
140 save_changes_lock: Default::default(),
141 })
142 }
143
144 pub async fn open(
147 path: impl AsRef<Path>,
148 passphrase: Option<&str>,
149 ) -> Result<Self, OpenStoreError> {
150 Self::open_with_config(&SqliteStoreConfig::new(path).passphrase(passphrase)).await
151 }
152
153 pub async fn open_with_key(
156 path: impl AsRef<Path>,
157 key: Option<&[u8; 32]>,
158 ) -> Result<Self, OpenStoreError> {
159 Self::open_with_config(&SqliteStoreConfig::new(path).key(key)).await
160 }
161
162 pub async fn open_with_config(config: &SqliteStoreConfig) -> Result<Self, OpenStoreError> {
164 fs::create_dir_all(&config.path).await.map_err(OpenStoreError::CreateDir)?;
165
166 let pool = config.build_pool_of_connections(DATABASE_NAME)?;
167 let pool_config = config.pool_config();
168 let runtime_config = config.runtime_config();
169
170 let this =
171 Self::open_with_pool(pool, config.secret.clone(), pool_config, runtime_config).await?;
172 this.read().await?.apply_runtime_config(runtime_config).await?;
173
174 Ok(this)
175 }
176
177 async fn open_with_pool(
180 pool: SqlitePool,
181 secret: Option<Secret>,
182 pool_config: PoolConfig,
183 runtime_config: RuntimeConfig,
184 ) -> Result<Self, OpenStoreError> {
185 let conn = pool.get().await?;
186
187 let version = conn.db_version().await?;
188 debug!("Opened sqlite store with version {}", version);
189
190 let version = initialize_store(&conn, version).await?;
191
192 let store = Self::create_raw(secret, pool, conn, pool_config, runtime_config).await?;
193
194 run_migrations(&store, version, None).await?;
195
196 store.write().await?.wal_checkpoint().await;
197
198 Ok(store)
199 }
200
201 fn deserialize_and_unpickle_inbound_group_session(
202 &self,
203 value: Vec<u8>,
204 backed_up: bool,
205 ) -> Result<InboundGroupSession> {
206 let mut pickle: PickledInboundGroupSession = self.deserialize_value(&value)?;
207
208 pickle.backed_up = backed_up;
213
214 Ok(InboundGroupSession::from_pickle(pickle)?)
215 }
216
217 fn deserialize_key_request(&self, value: &[u8], sent_out: bool) -> Result<GossipRequest> {
218 let mut request: GossipRequest = self.deserialize_value(value)?;
219 request.sent_out = sent_out;
222 Ok(request)
223 }
224
225 fn get_static_account(&self) -> Option<StaticAccountData> {
226 self.static_account.read().unwrap().clone()
227 }
228
229 #[instrument(skip_all)]
231 async fn read(&self) -> Result<SqliteAsyncConn> {
232 let pool = {
233 let guard = self.connections.lock().await;
234 let conns = guard.as_ref().ok_or(Error::StoreClosed)?;
235 conns.pool.clone()
236 };
237 Ok(pool.get().await?)
238 }
239
240 #[instrument(skip_all)]
242 pub(crate) async fn write(&self) -> Result<OwnedMutexGuard<SqliteAsyncConn>> {
243 let write_connection = {
244 let guard = self.connections.lock().await;
245 let conns = guard.as_ref().ok_or(Error::StoreClosed)?;
246 conns.write_connection.clone()
247 };
248 Ok(write_connection.lock_owned().await)
249 }
250}
251
252const DATABASE_VERSION: u8 = 19;
253
254const DEHYDRATED_DEVICE_PICKLE_KEY: &str = "dehydrated_device_pickle_key";
256
257pub(crate) async fn initialize_store(conn: &SqliteAsyncConn, version: u8) -> Result<u8> {
268 if version == 0 {
269 debug!("Creating database");
270 } else if version < DATABASE_VERSION {
271 debug!(version, new_version = DATABASE_VERSION, "Upgrading database");
272 } else {
273 return Ok(version);
274 }
275
276 if version < 1 {
277 debug!("Creating database");
278 conn.execute_batch("PRAGMA journal_mode = wal;").await?;
281 conn.with_transaction(|txn| {
282 txn.execute_batch(include_str!("../migrations/crypto_store/001_init.sql"))?;
283 txn.set_db_version(1)
284 })
285 .await?;
286 return Ok(1);
287 }
288
289 Ok(version)
290}
291
292pub(crate) async fn run_migrations(
304 store: &SqliteCryptoStore,
305 version: u8,
306 max_version: Option<u8>,
307) -> Result<()> {
308 let conn = store.write().await?;
309
310 if version < 2 {
311 debug!("Upgrading database to version 2");
312 conn.with_transaction(|txn| {
313 txn.execute_batch(include_str!("../migrations/crypto_store/002_reset_olm_hash.sql"))?;
314 txn.set_db_version(2)
315 })
316 .await?;
317 }
318
319 if version < 3 {
320 debug!("Upgrading database to version 3");
321 conn.with_transaction(|txn| {
322 txn.execute_batch(include_str!("../migrations/crypto_store/003_room_settings.sql"))?;
323 txn.set_db_version(3)
324 })
325 .await?;
326 }
327
328 if version < 4 {
329 debug!("Upgrading database to version 4");
330 conn.with_transaction(|txn| {
331 txn.execute_batch(include_str!(
332 "../migrations/crypto_store/004_drop_outbound_group_sessions.sql"
333 ))?;
334 txn.set_db_version(4)
335 })
336 .await?;
337 }
338
339 if version < 5 {
340 debug!("Upgrading database to version 5");
341 conn.with_transaction(|txn| {
342 txn.execute_batch(include_str!("../migrations/crypto_store/005_withheld_code.sql"))?;
343 txn.set_db_version(5)
344 })
345 .await?;
346 }
347
348 if version < 6 {
349 debug!("Upgrading database to version 6");
350 conn.with_transaction(|txn| {
351 txn.execute_batch(include_str!(
352 "../migrations/crypto_store/006_drop_outbound_group_sessions.sql"
353 ))?;
354 txn.set_db_version(6)
355 })
356 .await?;
357 }
358
359 if version < 7 {
360 debug!("Upgrading database to version 7");
361 conn.with_transaction(|txn| {
362 txn.execute_batch(include_str!("../migrations/crypto_store/007_lock_leases.sql"))?;
363 txn.set_db_version(7)
364 })
365 .await?;
366 }
367
368 if version < 8 {
369 debug!("Upgrading database to version 8");
370 conn.with_transaction(|txn| {
371 txn.execute_batch(include_str!("../migrations/crypto_store/008_secret_inbox.sql"))?;
372 txn.set_db_version(8)
373 })
374 .await?;
375 }
376
377 if version < 9 {
378 debug!("Upgrading database to version 9");
379 conn.with_transaction(|txn| {
380 txn.execute_batch(include_str!(
381 "../migrations/crypto_store/009_inbound_group_session_sender_key_sender_data_type.sql"
382 ))?;
383 txn.set_db_version(9)
384 })
385 .await?;
386 }
387
388 if version < 10 {
389 debug!("Upgrading database to version 10");
390 conn.with_transaction(|txn| {
391 txn.execute_batch(include_str!(
392 "../migrations/crypto_store/010_received_room_key_bundles.sql"
393 ))?;
394 txn.set_db_version(10)
395 })
396 .await?;
397 }
398
399 if version < 11 {
400 debug!("Upgrading database to version 11");
401 conn.with_transaction(|txn| {
402 txn.execute_batch(include_str!(
403 "../migrations/crypto_store/011_received_room_key_bundles_with_curve_key.sql"
404 ))?;
405 txn.set_db_version(11)
406 })
407 .await?;
408 }
409
410 if version < 12 {
411 debug!("Upgrading database to version 12");
412 conn.with_transaction(|txn| {
413 txn.execute_batch(include_str!(
414 "../migrations/crypto_store/012_withheld_code_by_room.sql"
415 ))?;
416 txn.set_db_version(12)
417 })
418 .await?;
419 }
420
421 if version < 13 {
422 debug!("Upgrading database to version 13");
423 conn.with_transaction(|txn| {
424 txn.execute_batch(include_str!(
425 "../migrations/crypto_store/013_lease_locks_with_generation.sql"
426 ))?;
427 txn.set_db_version(13)
428 })
429 .await?;
430 }
431
432 if version < 14 {
433 debug!("Upgrading database to version 14");
434 conn.with_transaction(|txn| {
435 txn.execute_batch(include_str!(
436 "../migrations/crypto_store/014_room_key_backups_fully_downloaded.sql"
437 ))?;
438 txn.set_db_version(14)
439 })
440 .await?;
441 }
442
443 if version < 15 {
444 debug!("Upgrading database to version 15");
445 conn.with_transaction(|txn| {
446 txn.execute_batch(include_str!(
447 "../migrations/crypto_store/015_rooms_pending_key_bundle.sql"
448 ))?;
449 txn.set_db_version(15)
450 })
451 .await?;
452 }
453
454 if version < 16 {
455 debug!("Upgrading database to version 16");
456 conn.with_transaction(|txn| {
457 txn.execute_batch(include_str!(
458 "../migrations/crypto_store/016_remove_old_generation_counter.sql"
459 ))?;
460 txn.set_db_version(16)
461 })
462 .await?;
463 }
464
465 if max_version.is_some_and(|max_version| max_version < 17) {
466 return Ok(());
467 }
468
469 if version < 17 {
470 debug!("Upgrading database to version 17");
471 let store = store.clone();
472 conn.with_transaction(move |txn| {
473 txn.execute_batch(include_str!(
474 "../migrations/crypto_store/017_add_new_secrets_inbox.sql"
475 ))?;
476 let mut select_query = txn.prepare("SELECT data FROM secrets")?;
477 let mut secrets = select_query.query([])?;
478 let mut insert_query = txn.prepare(
479 "INSERT OR IGNORE INTO secrets_inbox (secret_name, secret)
480 VALUES (?1, ?2)",
481 )?;
482 while let Some(row) = secrets.next()? {
483 let Ok(secret) =
484 store.deserialize_json::<GossippedSecret>(row.get::<_, Vec<u8>>(0)?.as_ref())
485 else {
486 continue;
487 };
488 let Ok(encoded_secret) = store.serialize_json(&secret.event.content.secret) else {
489 continue;
490 };
491 insert_query.execute((
492 store.encode_key("secrets_inbox", secret.secret_name.to_string()),
493 &encoded_secret,
494 ))?;
495 }
496 txn.execute_batch(include_str!(
497 "../migrations/crypto_store/017_drop_old_secrets_inbox.sql"
498 ))?;
499 txn.set_db_version(17)
500 })
501 .await?;
502 }
503
504 if version < 18 {
505 debug!("Upgrading database to version 18");
506 let store = store.clone();
507 conn.with_transaction(move |txn| {
508 txn.execute_batch(include_str!(
509 "../migrations/crypto_store/018_add_gossip_request_info.sql"
510 ))?;
511 let mut select_query =
512 txn.prepare("SELECT request_id, sent_out, data FROM key_requests")?;
513 let mut requests = select_query.query([])?;
514 let mut update_query =
515 txn.prepare("UPDATE OR REPLACE key_requests SET info = ?1 WHERE request_id = ?2")?;
516 while let Some(row) = requests.next()? {
517 let Ok(request) = store.deserialize_key_request(
518 row.get::<_, Vec<u8>>(2)?.as_ref(),
519 row.get::<_, bool>(1)?,
520 ) else {
521 continue;
522 };
523 let info = store.encode_key("key_requests", request.info.as_key());
524 update_query.execute((info, row.get::<_, Vec<u8>>(0)?))?;
525 }
526 txn.set_db_version(18)
527 })
528 .await?;
529 }
530
531 if version < 19 {
532 debug!("Upgrading database to version 19");
533 let user_id = store.load_account().await?.map(|account| account.user_id.clone());
537
538 conn.with_transaction(move |txn| {
539 if let Some(user_id) = user_id {
540 txn.clear_kv(&format!("sliding_sync_store::room-list::{user_id}::instance"))?;
541 }
542 txn.set_db_version(19)
543 })
544 .await?;
545 }
546
547 Ok(())
548}
549
550trait SqliteConnectionExt {
551 fn set_session(
552 &self,
553 session_id: &[u8],
554 sender_key: &[u8],
555 data: &[u8],
556 ) -> rusqlite::Result<()>;
557
558 fn set_inbound_group_session(
559 &self,
560 room_id: &[u8],
561 session_id: &[u8],
562 data: &[u8],
563 backed_up: bool,
564 sender_key: Option<&[u8]>,
565 sender_data_type: Option<u8>,
566 ) -> rusqlite::Result<()>;
567
568 fn set_outbound_group_session(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
569
570 fn set_device(&self, user_id: &[u8], device_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
571 fn delete_device(&self, user_id: &[u8], device_id: &[u8]) -> rusqlite::Result<()>;
572
573 fn set_identity(&self, user_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
574
575 fn add_olm_hash(&self, data: &[u8]) -> rusqlite::Result<()>;
576
577 fn set_key_request(
578 &self,
579 request_id: &[u8],
580 sent_out: bool,
581 data: &[u8],
582 info: &[u8],
583 ) -> rusqlite::Result<()>;
584
585 fn set_direct_withheld(
586 &self,
587 session_id: &[u8],
588 room_id: &[u8],
589 data: &[u8],
590 ) -> rusqlite::Result<()>;
591
592 fn set_room_settings(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
593
594 fn set_secret(&self, request_id: &[u8], data: &[u8]) -> rusqlite::Result<()>;
595
596 fn set_received_room_key_bundle(
597 &self,
598 room_id: &[u8],
599 user_id: &[u8],
600 data: &[u8],
601 ) -> rusqlite::Result<()>;
602
603 fn set_has_downloaded_all_room_keys(&self, room_id: &[u8]) -> rusqlite::Result<()>;
604
605 fn set_room_pending_key_bundle(
606 &self,
607 room_id: &[u8],
608 details: Option<&[u8]>,
609 ) -> rusqlite::Result<()>;
610}
611
612impl SqliteConnectionExt for rusqlite::Connection {
613 fn set_session(
614 &self,
615 session_id: &[u8],
616 sender_key: &[u8],
617 data: &[u8],
618 ) -> rusqlite::Result<()> {
619 self.execute(
620 "INSERT INTO session (session_id, sender_key, data)
621 VALUES (?1, ?2, ?3)
622 ON CONFLICT (session_id) DO UPDATE SET data = ?3",
623 (session_id, sender_key, data),
624 )?;
625 Ok(())
626 }
627
628 fn set_inbound_group_session(
629 &self,
630 room_id: &[u8],
631 session_id: &[u8],
632 data: &[u8],
633 backed_up: bool,
634 sender_key: Option<&[u8]>,
635 sender_data_type: Option<u8>,
636 ) -> rusqlite::Result<()> {
637 self.execute(
638 "INSERT INTO inbound_group_session (session_id, room_id, data, backed_up, sender_key, sender_data_type) \
639 VALUES (?1, ?2, ?3, ?4, ?5, ?6)
640 ON CONFLICT (session_id) DO UPDATE SET data = ?3, backed_up = ?4, sender_key = ?5, sender_data_type = ?6",
641 (session_id, room_id, data, backed_up, sender_key, sender_data_type),
642 )?;
643 Ok(())
644 }
645
646 fn set_outbound_group_session(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
647 self.execute(
648 "INSERT INTO outbound_group_session (room_id, data) \
649 VALUES (?1, ?2)
650 ON CONFLICT (room_id) DO UPDATE SET data = ?2",
651 (room_id, data),
652 )?;
653 Ok(())
654 }
655
656 fn set_device(&self, user_id: &[u8], device_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
657 self.execute(
658 "INSERT INTO device (user_id, device_id, data) \
659 VALUES (?1, ?2, ?3)
660 ON CONFLICT (user_id, device_id) DO UPDATE SET data = ?3",
661 (user_id, device_id, data),
662 )?;
663 Ok(())
664 }
665
666 fn delete_device(&self, user_id: &[u8], device_id: &[u8]) -> rusqlite::Result<()> {
667 self.execute(
668 "DELETE FROM device WHERE user_id = ? AND device_id = ?",
669 (user_id, device_id),
670 )?;
671 Ok(())
672 }
673
674 fn set_identity(&self, user_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
675 self.execute(
676 "INSERT INTO identity (user_id, data) \
677 VALUES (?1, ?2)
678 ON CONFLICT (user_id) DO UPDATE SET data = ?2",
679 (user_id, data),
680 )?;
681 Ok(())
682 }
683
684 fn add_olm_hash(&self, data: &[u8]) -> rusqlite::Result<()> {
685 self.execute("INSERT INTO olm_hash (data) VALUES (?) ON CONFLICT DO NOTHING", (data,))?;
686 Ok(())
687 }
688
689 fn set_key_request(
690 &self,
691 request_id: &[u8],
692 sent_out: bool,
693 data: &[u8],
694 info: &[u8],
695 ) -> rusqlite::Result<()> {
696 self.execute(
700 "INSERT INTO key_requests (request_id, sent_out, data, info)
701 VALUES (?1, ?2, ?3, ?4)
702 ON CONFLICT (request_id) DO UPDATE SET sent_out = ?2, data = ?3, info = ?4
703 ON CONFLICT (info) DO UPDATE SET request_id = ?1, sent_out = ?2, data = ?3",
704 (request_id, sent_out, data, info),
705 )?;
706 Ok(())
707 }
708
709 fn set_direct_withheld(
710 &self,
711 session_id: &[u8],
712 room_id: &[u8],
713 data: &[u8],
714 ) -> rusqlite::Result<()> {
715 self.execute(
716 "INSERT INTO direct_withheld_info (session_id, room_id, data)
717 VALUES (?1, ?2, ?3)
718 ON CONFLICT (session_id) DO UPDATE SET room_id = ?2, data = ?3",
719 (session_id, room_id, data),
720 )?;
721 Ok(())
722 }
723
724 fn set_room_settings(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()> {
725 self.execute(
726 "INSERT INTO room_settings (room_id, data)
727 VALUES (?1, ?2)
728 ON CONFLICT (room_id) DO UPDATE SET data = ?2",
729 (room_id, data),
730 )?;
731 Ok(())
732 }
733
734 fn set_secret(&self, secret_name: &[u8], secret: &[u8]) -> rusqlite::Result<()> {
735 self.execute(
738 "INSERT OR IGNORE INTO secrets_inbox (secret_name, secret)
739 VALUES (?1, ?2)",
740 (secret_name, secret),
741 )?;
742
743 Ok(())
744 }
745
746 fn set_received_room_key_bundle(
747 &self,
748 room_id: &[u8],
749 sender_user_id: &[u8],
750 data: &[u8],
751 ) -> rusqlite::Result<()> {
752 self.execute(
753 "INSERT INTO received_room_key_bundle(room_id, sender_user_id, bundle_data)
754 VALUES (?1, ?2, ?3)
755 ON CONFLICT (room_id, sender_user_id) DO UPDATE SET bundle_data = ?3",
756 (room_id, sender_user_id, data),
757 )?;
758 Ok(())
759 }
760
761 fn set_room_pending_key_bundle(
762 &self,
763 room_id: &[u8],
764 data: Option<&[u8]>,
765 ) -> rusqlite::Result<()> {
766 if let Some(data) = data {
767 self.execute(
768 "INSERT INTO rooms_pending_key_bundle (room_id, data)
769 VALUES (?1, ?2)
770 ON CONFLICT (room_id) DO UPDATE SET data = ?2",
771 (room_id, data),
772 )?;
773 } else {
774 self.execute("DELETE FROM rooms_pending_key_bundle WHERE room_id = ?1", (room_id,))?;
775 }
776 Ok(())
777 }
778
779 fn set_has_downloaded_all_room_keys(&self, room_id: &[u8]) -> rusqlite::Result<()> {
780 self.execute(
781 "INSERT INTO room_key_backups_fully_downloaded(room_id)
782 VALUES (?1)
783 ON CONFLICT(room_id) DO NOTHING",
784 (room_id,),
785 )?;
786 Ok(())
787 }
788}
789
790#[async_trait]
791trait SqliteObjectCryptoStoreExt: SqliteAsyncConnExt {
792 async fn get_sessions_for_sender_key(&self, sender_key: Key) -> Result<Vec<Vec<u8>>> {
793 Ok(self
794 .prepare("SELECT data FROM session WHERE sender_key = ?", |mut stmt| {
795 stmt.query((sender_key,))?.mapped(|row| row.get(0)).collect()
796 })
797 .await?)
798 }
799
800 async fn get_inbound_group_session(
801 &self,
802 session_id: Key,
803 ) -> Result<Option<(Vec<u8>, Vec<u8>, bool)>> {
804 Ok(self
805 .query_one(
806 "SELECT room_id, data, backed_up FROM inbound_group_session WHERE session_id = ?",
807 (session_id,),
808 |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
809 )
810 .await
811 .optional()?)
812 }
813
814 async fn get_inbound_group_sessions(&self) -> Result<Vec<(Vec<u8>, bool)>> {
815 Ok(self
816 .prepare("SELECT data, backed_up FROM inbound_group_session", |mut stmt| {
817 stmt.query(())?.mapped(|row| Ok((row.get(0)?, row.get(1)?))).collect()
818 })
819 .await?)
820 }
821
822 async fn get_inbound_group_session_counts(
823 &self,
824 _backup_version: Option<&str>,
825 ) -> Result<RoomKeyCounts> {
826 let total = self
827 .query_one("SELECT count(*) FROM inbound_group_session", (), |row| row.get(0))
828 .await?;
829 let backed_up = self
830 .query_one(
831 "SELECT count(*) FROM inbound_group_session WHERE backed_up = TRUE",
832 (),
833 |row| row.get(0),
834 )
835 .await?;
836 Ok(RoomKeyCounts { total, backed_up })
837 }
838
839 async fn get_inbound_group_sessions_by_room_id(
840 &self,
841 room_id: Key,
842 ) -> Result<Vec<(Vec<u8>, bool)>> {
843 Ok(self
844 .prepare(
845 "SELECT data, backed_up FROM inbound_group_session WHERE room_id = :room_id",
846 move |mut stmt| {
847 stmt.query(named_params! {
848 ":room_id": room_id,
849 })?
850 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
851 .collect()
852 },
853 )
854 .await?)
855 }
856
857 async fn get_inbound_group_sessions_for_device_batch(
858 &self,
859 sender_key: Key,
860 sender_data_type: SenderDataType,
861 after_session_id: Option<Key>,
862 limit: usize,
863 ) -> Result<Vec<(Vec<u8>, bool)>> {
864 Ok(self
865 .prepare(
866 "
867 SELECT data, backed_up
868 FROM inbound_group_session
869 WHERE sender_key = :sender_key
870 AND sender_data_type = :sender_data_type
871 AND session_id > :after_session_id
872 ORDER BY session_id
873 LIMIT :limit
874 ",
875 move |mut stmt| {
876 let sender_data_type = sender_data_type as u8;
877
878 let after_session_id = after_session_id.unwrap_or(Key::Plain(Vec::new()));
881
882 stmt.query(named_params! {
883 ":sender_key": sender_key,
884 ":sender_data_type": sender_data_type,
885 ":after_session_id": after_session_id,
886 ":limit": limit,
887 })?
888 .mapped(|row| Ok((row.get(0)?, row.get(1)?)))
889 .collect()
890 },
891 )
892 .await?)
893 }
894
895 async fn get_inbound_group_sessions_for_backup(&self, limit: usize) -> Result<Vec<Vec<u8>>> {
896 Ok(self
897 .prepare(
898 "SELECT data FROM inbound_group_session WHERE backed_up = FALSE LIMIT ?",
899 move |mut stmt| stmt.query((limit,))?.mapped(|row| row.get(0)).collect(),
900 )
901 .await?)
902 }
903
904 async fn mark_inbound_group_sessions_as_backed_up(&self, session_ids: Vec<Key>) -> Result<()> {
905 if session_ids.is_empty() {
906 warn!("No sessions to mark as backed up!");
908 return Ok(());
909 }
910
911 self.chunk_large_query_over(session_ids, None, move |txn, session_ids| {
912 let query = format!(
915 "UPDATE inbound_group_session SET backed_up = TRUE where session_id IN ({})",
916 session_ids.host_parameters()
917 );
918 txn.prepare(&query)?.execute(params_from_iter(session_ids))?;
919 Ok(Vec::<()>::new())
920 })
921 .await?;
922
923 Ok(())
924 }
925
926 async fn reset_inbound_group_session_backup_state(&self) -> Result<()> {
927 self.execute("UPDATE inbound_group_session SET backed_up = FALSE", ()).await?;
928 Ok(())
929 }
930
931 async fn get_outbound_group_session(&self, room_id: Key) -> Result<Option<Vec<u8>>> {
932 Ok(self
933 .query_one(
934 "SELECT data FROM outbound_group_session WHERE room_id = ?",
935 (room_id,),
936 |row| row.get(0),
937 )
938 .await
939 .optional()?)
940 }
941
942 async fn get_device(&self, user_id: Key, device_id: Key) -> Result<Option<Vec<u8>>> {
943 Ok(self
944 .query_one(
945 "SELECT data FROM device WHERE user_id = ? AND device_id = ?",
946 (user_id, device_id),
947 |row| row.get(0),
948 )
949 .await
950 .optional()?)
951 }
952
953 async fn get_user_devices(&self, user_id: Key) -> Result<Vec<Vec<u8>>> {
954 Ok(self
955 .prepare("SELECT data FROM device WHERE user_id = ?", |mut stmt| {
956 stmt.query((user_id,))?.mapped(|row| row.get(0)).collect()
957 })
958 .await?)
959 }
960
961 async fn get_user_identity(&self, user_id: Key) -> Result<Option<Vec<u8>>> {
962 Ok(self
963 .query_one("SELECT data FROM identity WHERE user_id = ?", (user_id,), |row| row.get(0))
964 .await
965 .optional()?)
966 }
967
968 async fn has_olm_hash(&self, data: Vec<u8>) -> Result<bool> {
969 Ok(self
970 .query_one("SELECT count(*) FROM olm_hash WHERE data = ?", (data,), |row| {
971 row.get::<_, i32>(0)
972 })
973 .await?
974 > 0)
975 }
976
977 async fn get_tracked_users(&self) -> Result<Vec<Vec<u8>>> {
978 Ok(self
979 .prepare("SELECT data FROM tracked_user", |mut stmt| {
980 stmt.query(())?.mapped(|row| row.get(0)).collect()
981 })
982 .await?)
983 }
984
985 async fn add_tracked_users(&self, users: Vec<(Key, Vec<u8>)>) -> Result<()> {
986 Ok(self
987 .prepare(
988 "INSERT INTO tracked_user (user_id, data) \
989 VALUES (?1, ?2) \
990 ON CONFLICT (user_id) DO UPDATE SET data = ?2",
991 |mut stmt| {
992 for (user_id, data) in users {
993 stmt.execute((user_id, data))?;
994 }
995
996 Ok(())
997 },
998 )
999 .await?)
1000 }
1001
1002 async fn get_outgoing_secret_request(
1003 &self,
1004 request_id: Key,
1005 ) -> Result<Option<(Vec<u8>, bool)>> {
1006 Ok(self
1007 .query_one(
1008 "SELECT data, sent_out FROM key_requests WHERE request_id = ?",
1009 (request_id,),
1010 |row| Ok((row.get(0)?, row.get(1)?)),
1011 )
1012 .await
1013 .optional()?)
1014 }
1015
1016 async fn get_secret_request_by_info(&self, info: Key) -> Result<Option<(Vec<u8>, bool)>> {
1017 Ok(self
1018 .query_one("SELECT data, sent_out FROM key_requests WHERE info = ?", (info,), |row| {
1019 Ok((row.get(0)?, row.get(1)?))
1020 })
1021 .await
1022 .optional()?)
1023 }
1024
1025 async fn get_unsent_secret_requests(&self) -> Result<Vec<Vec<u8>>> {
1026 Ok(self
1027 .prepare("SELECT data FROM key_requests WHERE sent_out = FALSE", |mut stmt| {
1028 stmt.query(())?.mapped(|row| row.get(0)).collect()
1029 })
1030 .await?)
1031 }
1032
1033 async fn delete_key_request(&self, request_id: Key) -> Result<()> {
1034 self.execute("DELETE FROM key_requests WHERE request_id = ?", (request_id,)).await?;
1035 Ok(())
1036 }
1037
1038 async fn get_secrets_from_inbox(&self, secret_name: Key) -> Result<Vec<Vec<u8>>> {
1039 Ok(self
1040 .prepare("SELECT secret FROM secrets_inbox WHERE secret_name = ?", |mut stmt| {
1041 stmt.query((secret_name,))?.mapped(|row| row.get(0)).collect()
1042 })
1043 .await?)
1044 }
1045
1046 async fn delete_secrets_from_inbox(&self, secret_name: Key) -> Result<()> {
1047 self.execute("DELETE FROM secrets_inbox WHERE secret_name = ?", (secret_name,)).await?;
1048 Ok(())
1049 }
1050
1051 async fn get_direct_withheld_info(
1052 &self,
1053 session_id: Key,
1054 room_id: Key,
1055 ) -> Result<Option<Vec<u8>>> {
1056 Ok(self
1057 .query_one(
1058 "SELECT data FROM direct_withheld_info WHERE session_id = ?1 AND room_id = ?2",
1059 (session_id, room_id),
1060 |row| row.get(0),
1061 )
1062 .await
1063 .optional()?)
1064 }
1065
1066 async fn get_withheld_sessions_by_room_id(&self, room_id: Key) -> Result<Vec<Vec<u8>>> {
1067 Ok(self
1068 .prepare("SELECT data FROM direct_withheld_info WHERE room_id = ?1", |mut stmt| {
1069 stmt.query((room_id,))?.mapped(|row| row.get(0)).collect()
1070 })
1071 .await?)
1072 }
1073
1074 async fn get_room_settings(&self, room_id: Key) -> Result<Option<Vec<u8>>> {
1075 Ok(self
1076 .query_one("SELECT data FROM room_settings WHERE room_id = ?", (room_id,), |row| {
1077 row.get(0)
1078 })
1079 .await
1080 .optional()?)
1081 }
1082
1083 async fn get_received_room_key_bundle(
1084 &self,
1085 room_id: Key,
1086 sender_user: Key,
1087 ) -> Result<Option<Vec<u8>>> {
1088 Ok(self
1089 .query_one(
1090 "SELECT bundle_data FROM received_room_key_bundle WHERE room_id = ? AND sender_user_id = ?",
1091 (room_id, sender_user),
1092 |row| { row.get(0) },
1093 )
1094 .await
1095 .optional()?)
1096 }
1097
1098 async fn get_room_pending_key_bundle(&self, room_id: Key) -> Result<Option<Vec<u8>>> {
1099 Ok(self
1100 .query_one(
1101 "SELECT data FROM rooms_pending_key_bundle WHERE room_id = ?",
1102 (room_id,),
1103 |row| row.get(0),
1104 )
1105 .await
1106 .optional()?)
1107 }
1108
1109 async fn get_all_rooms_pending_key_bundle(&self) -> Result<Vec<Vec<u8>>> {
1110 Ok(self
1111 .query_many("SELECT data FROM rooms_pending_key_bundle", (), |row| row.get(0))
1112 .await?)
1113 }
1114
1115 async fn has_downloaded_all_room_keys(&self, room_id: Key) -> Result<bool> {
1116 Ok(self
1117 .query_row(
1118 "SELECT EXISTS (SELECT 1 FROM room_key_backups_fully_downloaded WHERE room_id = ?)",
1119 (room_id,),
1120 |row| row.get(0),
1121 )
1122 .await?)
1123 }
1124}
1125
1126#[async_trait]
1127impl SqliteObjectCryptoStoreExt for SqliteAsyncConn {}
1128
1129#[async_trait]
1130impl CryptoStore for SqliteCryptoStore {
1131 type Error = Error;
1132
1133 async fn load_account(&self) -> Result<Option<Account>> {
1134 let conn = self.read().await?;
1135 if let Some(pickle) = conn.get_kv("account").await? {
1136 let pickle = self.deserialize_value(&pickle)?;
1137
1138 let account = Account::from_pickle(pickle).map_err(|_| Error::Unpickle)?;
1139
1140 *self.static_account.write().unwrap() = Some(account.static_data().clone());
1141
1142 Ok(Some(account))
1143 } else {
1144 Ok(None)
1145 }
1146 }
1147
1148 async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>> {
1149 let conn = self.read().await?;
1150 if let Some(i) = conn.get_kv("identity").await? {
1151 let pickle = self.deserialize_value(&i)?;
1152 Ok(Some(PrivateCrossSigningIdentity::from_pickle(pickle).map_err(|_| Error::Unpickle)?))
1153 } else {
1154 Ok(None)
1155 }
1156 }
1157
1158 async fn save_pending_changes(&self, changes: PendingChanges) -> Result<()> {
1159 let _guard = self.save_changes_lock.lock().await;
1164
1165 let pickled_account = if let Some(account) = changes.account {
1166 *self.static_account.write().unwrap() = Some(account.static_data().clone());
1167 Some(account.pickle())
1168 } else {
1169 None
1170 };
1171
1172 let this = self.clone();
1173 self.write()
1174 .await?
1175 .with_transaction(move |txn| {
1176 if let Some(pickled_account) = pickled_account {
1177 let serialized_account = this.serialize_value(&pickled_account)?;
1178 txn.set_kv("account", &serialized_account)?;
1179 }
1180
1181 Ok::<_, Error>(())
1182 })
1183 .await?;
1184
1185 Ok(())
1186 }
1187
1188 async fn save_changes(&self, changes: Changes) -> Result<()> {
1189 let _guard = self.save_changes_lock.lock().await;
1194
1195 let pickled_private_identity =
1196 if let Some(i) = changes.private_identity { Some(i.pickle().await) } else { None };
1197
1198 let mut session_changes = Vec::new();
1199
1200 for session in changes.sessions {
1201 let session_id = self.encode_key("session", session.session_id());
1202 let sender_key = self.encode_key("session", session.sender_key().to_base64());
1203 let pickle = session.pickle().await;
1204 session_changes.push((session_id, sender_key, pickle));
1205 }
1206
1207 let mut inbound_session_changes = Vec::new();
1208 for session in changes.inbound_group_sessions {
1209 let room_id = self.encode_key("inbound_group_session", session.room_id().as_bytes());
1210 let session_id = self.encode_key("inbound_group_session", session.session_id());
1211 let pickle = session.pickle().await;
1212 let sender_key =
1213 self.encode_key("inbound_group_session", session.sender_key().to_base64());
1214 inbound_session_changes.push((room_id, session_id, pickle, sender_key));
1215 }
1216
1217 let mut outbound_session_changes = Vec::new();
1218 for session in changes.outbound_group_sessions {
1219 let room_id = self.encode_key("outbound_group_session", session.room_id().as_bytes());
1220 let pickle = session.pickle().await;
1221 outbound_session_changes.push((room_id, pickle));
1222 }
1223
1224 let this = self.clone();
1225 self.write()
1226 .await?
1227 .with_transaction(move |txn| {
1228 if let Some(pickled_private_identity) = &pickled_private_identity {
1229 let serialized_private_identity =
1230 this.serialize_value(pickled_private_identity)?;
1231 txn.set_kv("identity", &serialized_private_identity)?;
1232 }
1233
1234 if let Some(token) = &changes.next_batch_token {
1235 let serialized_token = this.serialize_value(token)?;
1236 txn.set_kv("next_batch_token", &serialized_token)?;
1237 }
1238
1239 if let Some(decryption_key) = &changes.backup_decryption_key {
1240 let serialized_decryption_key = this.serialize_value(decryption_key)?;
1241 txn.set_kv("recovery_key_v1", &serialized_decryption_key)?;
1242 }
1243
1244 if let Some(backup_version) = &changes.backup_version {
1245 let serialized_backup_version = this.serialize_value(backup_version)?;
1246 txn.set_kv("backup_version_v1", &serialized_backup_version)?;
1247 }
1248
1249 if let Some(pickle_key) = &changes.dehydrated_device_pickle_key {
1250 let serialized_pickle_key = this.serialize_value(pickle_key)?;
1251 txn.set_kv(DEHYDRATED_DEVICE_PICKLE_KEY, &serialized_pickle_key)?;
1252 }
1253
1254 for device in changes.devices.new.iter().chain(&changes.devices.changed) {
1255 let user_id = this.encode_key("device", device.user_id().as_bytes());
1256 let device_id = this.encode_key("device", device.device_id().as_bytes());
1257 let data = this.serialize_value(&device)?;
1258 txn.set_device(&user_id, &device_id, &data)?;
1259 }
1260
1261 for device in &changes.devices.deleted {
1262 let user_id = this.encode_key("device", device.user_id().as_bytes());
1263 let device_id = this.encode_key("device", device.device_id().as_bytes());
1264 txn.delete_device(&user_id, &device_id)?;
1265 }
1266
1267 for identity in changes.identities.changed.iter().chain(&changes.identities.new) {
1268 let user_id = this.encode_key("identity", identity.user_id().as_bytes());
1269 let data = this.serialize_value(&identity)?;
1270 txn.set_identity(&user_id, &data)?;
1271 }
1272
1273 for (session_id, sender_key, pickle) in &session_changes {
1274 let serialized_session = this.serialize_value(&pickle)?;
1275 txn.set_session(session_id, sender_key, &serialized_session)?;
1276 }
1277
1278 for (room_id, session_id, pickle, sender_key) in &inbound_session_changes {
1279 let serialized_session = this.serialize_value(&pickle)?;
1280 txn.set_inbound_group_session(
1281 room_id,
1282 session_id,
1283 &serialized_session,
1284 pickle.backed_up,
1285 Some(sender_key),
1286 Some(pickle.sender_data.to_type() as u8),
1287 )?;
1288 }
1289
1290 for (room_id, pickle) in &outbound_session_changes {
1291 let serialized_session = this.serialize_json(&pickle)?;
1292 txn.set_outbound_group_session(room_id, &serialized_session)?;
1293 }
1294
1295 for hash in &changes.message_hashes {
1296 let hash = rmp_serde::to_vec(hash)?;
1297 txn.add_olm_hash(&hash)?;
1298 }
1299
1300 for request in changes.key_requests {
1301 let request_id = this.encode_key("key_requests", request.request_id.as_bytes());
1302 let serialized_request = this.serialize_value(&request)?;
1303 let serialized_info = this.encode_key("key_requests", request.info.as_key());
1304 txn.set_key_request(
1305 &request_id,
1306 request.sent_out,
1307 &serialized_request,
1308 &serialized_info,
1309 )?;
1310 }
1311
1312 for (room_id, data) in changes.withheld_session_info {
1313 for (session_id, event) in data {
1314 let session_id = this.encode_key("direct_withheld_info", session_id);
1315 let room_id = this.encode_key("direct_withheld_info", &room_id);
1316 let serialized_info = this.serialize_json(&event)?;
1317 txn.set_direct_withheld(&session_id, &room_id, &serialized_info)?;
1318 }
1319 }
1320
1321 for (room_id, settings) in changes.room_settings {
1322 let room_id = this.encode_key("room_settings", room_id.as_bytes());
1323 let value = this.serialize_value(&settings)?;
1324 txn.set_room_settings(&room_id, &value)?;
1325 }
1326
1327 for secret in changes.secrets {
1328 let secret_name =
1329 this.encode_key("secrets_inbox", secret.secret_name.to_string());
1330 let value = this.serialize_json(secret.secret.deref())?;
1331 txn.set_secret(&secret_name, &value)?;
1332 }
1333
1334 for bundle in changes.received_room_key_bundles {
1335 let room_id =
1336 this.encode_key("received_room_key_bundle", &bundle.bundle_data.room_id);
1337 let user_id = this.encode_key("received_room_key_bundle", &bundle.sender_user);
1338 let value = this.serialize_value(&bundle)?;
1339 txn.set_received_room_key_bundle(&room_id, &user_id, &value)?;
1340 }
1341
1342 for room in changes.room_key_backups_fully_downloaded {
1343 let room_id = this.encode_key("room_key_backups_fully_downloaded", &room);
1344 txn.set_has_downloaded_all_room_keys(&room_id)?;
1345 }
1346
1347 for (room, details) in changes.rooms_pending_key_bundle {
1348 let room_id = this.encode_key("rooms_pending_key_bundle", &room);
1349 let value = details.as_ref().map(|d| this.serialize_value(d)).transpose()?;
1350 txn.set_room_pending_key_bundle(&room_id, value.as_deref())?;
1351 }
1352
1353 Ok::<_, Error>(())
1354 })
1355 .await?;
1356
1357 Ok(())
1358 }
1359
1360 async fn save_inbound_group_sessions(
1361 &self,
1362 sessions: Vec<InboundGroupSession>,
1363 backed_up_to_version: Option<&str>,
1364 ) -> matrix_sdk_crypto::store::Result<(), Self::Error> {
1365 sessions.iter().for_each(|s| {
1367 let backed_up = s.backed_up();
1368 if backed_up != backed_up_to_version.is_some() {
1369 warn!(
1370 backed_up,
1371 backed_up_to_version,
1372 "Session backed-up flag does not correspond to backup version setting",
1373 );
1374 }
1375 });
1376
1377 self.save_changes(Changes { inbound_group_sessions: sessions, ..Changes::default() }).await
1380 }
1381
1382 async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>> {
1383 let device_keys = self.get_own_device().await?.as_device_keys().clone();
1384
1385 let sessions: Vec<_> = self
1386 .read()
1387 .await?
1388 .get_sessions_for_sender_key(self.encode_key("session", sender_key.as_bytes()))
1389 .await?
1390 .into_iter()
1391 .map(|bytes| {
1392 let pickle = self.deserialize_value(&bytes)?;
1393 Session::from_pickle(device_keys.clone(), pickle).map_err(|_| Error::AccountUnset)
1394 })
1395 .collect::<Result<_>>()?;
1396
1397 if sessions.is_empty() { Ok(None) } else { Ok(Some(sessions)) }
1398 }
1399
1400 #[instrument(skip(self))]
1401 async fn get_inbound_group_session(
1402 &self,
1403 room_id: &RoomId,
1404 session_id: &str,
1405 ) -> Result<Option<InboundGroupSession>> {
1406 let session_id = self.encode_key("inbound_group_session", session_id);
1407 let Some((room_id_from_db, value, backed_up)) =
1408 self.read().await?.get_inbound_group_session(session_id).await?
1409 else {
1410 return Ok(None);
1411 };
1412
1413 let room_id = self.encode_key("inbound_group_session", room_id.as_bytes());
1414 if *room_id != room_id_from_db {
1415 warn!("expected room_id for session_id doesn't match what's in the DB");
1416 return Ok(None);
1417 }
1418
1419 Ok(Some(self.deserialize_and_unpickle_inbound_group_session(value, backed_up)?))
1420 }
1421
1422 async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
1423 self.read()
1424 .await?
1425 .get_inbound_group_sessions()
1426 .await?
1427 .into_iter()
1428 .map(|(value, backed_up)| {
1429 self.deserialize_and_unpickle_inbound_group_session(value, backed_up)
1430 })
1431 .collect()
1432 }
1433
1434 async fn get_inbound_group_sessions_by_room_id(
1435 &self,
1436 room_id: &RoomId,
1437 ) -> Result<Vec<InboundGroupSession>> {
1438 let room_id = self.encode_key("inbound_group_session", room_id.as_bytes());
1439 self.read()
1440 .await?
1441 .get_inbound_group_sessions_by_room_id(room_id)
1442 .await?
1443 .into_iter()
1444 .map(|(value, backed_up)| {
1445 self.deserialize_and_unpickle_inbound_group_session(value, backed_up)
1446 })
1447 .collect()
1448 }
1449
1450 async fn get_inbound_group_sessions_for_device_batch(
1451 &self,
1452 sender_key: Curve25519PublicKey,
1453 sender_data_type: SenderDataType,
1454 after_session_id: Option<String>,
1455 limit: usize,
1456 ) -> Result<Vec<InboundGroupSession>, Self::Error> {
1457 let after_session_id =
1458 after_session_id.map(|session_id| self.encode_key("inbound_group_session", session_id));
1459 let sender_key = self.encode_key("inbound_group_session", sender_key.to_base64());
1460
1461 self.read()
1462 .await?
1463 .get_inbound_group_sessions_for_device_batch(
1464 sender_key,
1465 sender_data_type,
1466 after_session_id,
1467 limit,
1468 )
1469 .await?
1470 .into_iter()
1471 .map(|(value, backed_up)| {
1472 self.deserialize_and_unpickle_inbound_group_session(value, backed_up)
1473 })
1474 .collect()
1475 }
1476
1477 async fn inbound_group_session_counts(
1478 &self,
1479 backup_version: Option<&str>,
1480 ) -> Result<RoomKeyCounts> {
1481 Ok(self.read().await?.get_inbound_group_session_counts(backup_version).await?)
1482 }
1483
1484 async fn inbound_group_sessions_for_backup(
1485 &self,
1486 _backup_version: &str,
1487 limit: usize,
1488 ) -> Result<Vec<InboundGroupSession>> {
1489 self.read()
1490 .await?
1491 .get_inbound_group_sessions_for_backup(limit)
1492 .await?
1493 .into_iter()
1494 .map(|value| self.deserialize_and_unpickle_inbound_group_session(value, false))
1495 .collect()
1496 }
1497
1498 async fn mark_inbound_group_sessions_as_backed_up(
1499 &self,
1500 _backup_version: &str,
1501 session_ids: &[(&RoomId, &str)],
1502 ) -> Result<()> {
1503 Ok(self
1504 .write()
1505 .await?
1506 .mark_inbound_group_sessions_as_backed_up(
1507 session_ids
1508 .iter()
1509 .map(|(_, s)| self.encode_key("inbound_group_session", s))
1510 .collect(),
1511 )
1512 .await?)
1513 }
1514
1515 async fn reset_backup_state(&self) -> Result<()> {
1516 Ok(self.write().await?.reset_inbound_group_session_backup_state().await?)
1517 }
1518
1519 async fn load_backup_keys(&self) -> Result<BackupKeys> {
1520 let conn = self.read().await?;
1521
1522 let backup_version = conn
1523 .get_kv("backup_version_v1")
1524 .await?
1525 .map(|value| self.deserialize_value(&value))
1526 .transpose()?;
1527
1528 let decryption_key = conn
1529 .get_kv("recovery_key_v1")
1530 .await?
1531 .map(|value| self.deserialize_value(&value))
1532 .transpose()?;
1533
1534 Ok(BackupKeys { backup_version, decryption_key })
1535 }
1536
1537 async fn load_dehydrated_device_pickle_key(&self) -> Result<Option<DehydratedDeviceKey>> {
1538 let conn = self.read().await?;
1539
1540 conn.get_kv(DEHYDRATED_DEVICE_PICKLE_KEY)
1541 .await?
1542 .map(|value| self.deserialize_value(&value))
1543 .transpose()
1544 }
1545
1546 async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error> {
1547 Ok(self.write().await?.clear_kv(DEHYDRATED_DEVICE_PICKLE_KEY).await?)
1548 }
1549 async fn get_outbound_group_session(
1550 &self,
1551 room_id: &RoomId,
1552 ) -> Result<Option<OutboundGroupSession>> {
1553 let room_id = self.encode_key("outbound_group_session", room_id.as_bytes());
1554 let Some(value) = self.read().await?.get_outbound_group_session(room_id).await? else {
1555 return Ok(None);
1556 };
1557
1558 let account_info = self.get_static_account().ok_or(Error::AccountUnset)?;
1559
1560 let pickle = self.deserialize_json(&value)?;
1561 let session = OutboundGroupSession::from_pickle(
1562 account_info.device_id,
1563 account_info.identity_keys,
1564 pickle,
1565 )
1566 .map_err(|_| Error::Unpickle)?;
1567
1568 return Ok(Some(session));
1569 }
1570
1571 async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>> {
1572 self.read()
1573 .await?
1574 .get_tracked_users()
1575 .await?
1576 .iter()
1577 .map(|value| self.deserialize_value(value))
1578 .collect()
1579 }
1580
1581 async fn save_tracked_users(&self, tracked_users: &[(&UserId, bool)]) -> Result<()> {
1582 let users: Vec<(Key, Vec<u8>)> = tracked_users
1583 .iter()
1584 .map(|(u, d)| {
1585 let user_id = self.encode_key("tracked_users", u.as_bytes());
1586 let data =
1587 self.serialize_value(&TrackedUser { user_id: (*u).into(), dirty: *d })?;
1588 Ok((user_id, data))
1589 })
1590 .collect::<Result<_>>()?;
1591
1592 Ok(self.write().await?.add_tracked_users(users).await?)
1593 }
1594
1595 async fn get_device(
1596 &self,
1597 user_id: &UserId,
1598 device_id: &DeviceId,
1599 ) -> Result<Option<DeviceData>> {
1600 let user_id = self.encode_key("device", user_id.as_bytes());
1601 let device_id = self.encode_key("device", device_id.as_bytes());
1602 Ok(self
1603 .read()
1604 .await?
1605 .get_device(user_id, device_id)
1606 .await?
1607 .map(|value| self.deserialize_value(&value))
1608 .transpose()?)
1609 }
1610
1611 async fn get_user_devices(
1612 &self,
1613 user_id: &UserId,
1614 ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
1615 let user_id = self.encode_key("device", user_id.as_bytes());
1616 self.read()
1617 .await?
1618 .get_user_devices(user_id)
1619 .await?
1620 .into_iter()
1621 .map(|value| {
1622 let device: DeviceData = self.deserialize_value(&value)?;
1623 Ok((device.device_id().to_owned(), device))
1624 })
1625 .collect()
1626 }
1627
1628 async fn get_own_device(&self) -> Result<DeviceData> {
1629 let account_info = self.get_static_account().ok_or(Error::AccountUnset)?;
1630
1631 Ok(self
1632 .get_device(&account_info.user_id, &account_info.device_id)
1633 .await?
1634 .expect("We should be able to find our own device."))
1635 }
1636
1637 async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentityData>> {
1638 let user_id = self.encode_key("identity", user_id.as_bytes());
1639 Ok(self
1640 .read()
1641 .await?
1642 .get_user_identity(user_id)
1643 .await?
1644 .map(|value| self.deserialize_value(&value))
1645 .transpose()?)
1646 }
1647
1648 async fn is_message_known(
1649 &self,
1650 message_hash: &matrix_sdk_crypto::olm::OlmMessageHash,
1651 ) -> Result<bool> {
1652 let value = rmp_serde::to_vec(message_hash)?;
1653 Ok(self.read().await?.has_olm_hash(value).await?)
1654 }
1655
1656 async fn get_outgoing_secret_requests(
1657 &self,
1658 request_id: &TransactionId,
1659 ) -> Result<Option<GossipRequest>> {
1660 let request_id = self.encode_key("key_requests", request_id.as_bytes());
1661 Ok(self
1662 .read()
1663 .await?
1664 .get_outgoing_secret_request(request_id)
1665 .await?
1666 .map(|(value, sent_out)| self.deserialize_key_request(&value, sent_out))
1667 .transpose()?)
1668 }
1669
1670 async fn get_secret_request_by_info(
1671 &self,
1672 key_info: &SecretInfo,
1673 ) -> Result<Option<GossipRequest>> {
1674 let key_info = self.encode_key("key_requests", key_info.as_key());
1675 Ok(self
1676 .read()
1677 .await?
1678 .get_secret_request_by_info(key_info)
1679 .await?
1680 .map(|(value, sent_out)| self.deserialize_key_request(&value, sent_out))
1681 .transpose()?)
1682 }
1683
1684 async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
1685 self.read()
1686 .await?
1687 .get_unsent_secret_requests()
1688 .await?
1689 .iter()
1690 .map(|value| {
1691 let request = self.deserialize_key_request(value, false)?;
1692 Ok(request)
1693 })
1694 .collect()
1695 }
1696
1697 async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> {
1698 let request_id = self.encode_key("key_requests", request_id.as_bytes());
1699 Ok(self.write().await?.delete_key_request(request_id).await?)
1700 }
1701
1702 async fn get_secrets_from_inbox(
1703 &self,
1704 secret_name: &SecretName,
1705 ) -> Result<Vec<Zeroizing<String>>> {
1706 let secret_name = self.encode_key("secrets_inbox", secret_name.to_string());
1707
1708 self.read()
1709 .await?
1710 .get_secrets_from_inbox(secret_name)
1711 .await?
1712 .into_iter()
1713 .map(|value| self.deserialize_json(value.as_ref()).map(|value: String| value.into()))
1714 .collect()
1715 }
1716
1717 async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<()> {
1718 let secret_name = self.encode_key("secrets_inbox", secret_name.to_string());
1719 self.write().await?.delete_secrets_from_inbox(secret_name).await
1720 }
1721
1722 async fn get_withheld_info(
1723 &self,
1724 room_id: &RoomId,
1725 session_id: &str,
1726 ) -> Result<Option<RoomKeyWithheldEntry>> {
1727 let room_id = self.encode_key("direct_withheld_info", room_id);
1728 let session_id = self.encode_key("direct_withheld_info", session_id);
1729
1730 self.read()
1731 .await?
1732 .get_direct_withheld_info(session_id, room_id)
1733 .await?
1734 .map(|value| {
1735 let info = self.deserialize_json::<RoomKeyWithheldEntry>(&value)?;
1736 Ok(info)
1737 })
1738 .transpose()
1739 }
1740
1741 async fn get_withheld_sessions_by_room_id(
1742 &self,
1743 room_id: &RoomId,
1744 ) -> matrix_sdk_crypto::store::Result<Vec<RoomKeyWithheldEntry>, Self::Error> {
1745 let room_id = self.encode_key("direct_withheld_info", room_id);
1746
1747 self.read()
1748 .await?
1749 .get_withheld_sessions_by_room_id(room_id)
1750 .await?
1751 .into_iter()
1752 .map(|value| self.deserialize_json(&value))
1753 .collect()
1754 }
1755
1756 async fn get_room_settings(&self, room_id: &RoomId) -> Result<Option<RoomSettings>> {
1757 let room_id = self.encode_key("room_settings", room_id.as_bytes());
1758 let Some(value) = self.read().await?.get_room_settings(room_id).await? else {
1759 return Ok(None);
1760 };
1761
1762 let settings = self.deserialize_value(&value)?;
1763
1764 return Ok(Some(settings));
1765 }
1766
1767 async fn get_received_room_key_bundle_data(
1768 &self,
1769 room_id: &RoomId,
1770 user_id: &UserId,
1771 ) -> Result<Option<StoredRoomKeyBundleData>> {
1772 let room_id = self.encode_key("received_room_key_bundle", room_id);
1773 let user_id = self.encode_key("received_room_key_bundle", user_id);
1774 self.read()
1775 .await?
1776 .get_received_room_key_bundle(room_id, user_id)
1777 .await?
1778 .map(|value| self.deserialize_value(&value))
1779 .transpose()
1780 }
1781
1782 async fn has_downloaded_all_room_keys(&self, room_id: &RoomId) -> Result<bool> {
1783 let room_id = self.encode_key("room_key_backups_fully_downloaded", room_id);
1784 self.read().await?.has_downloaded_all_room_keys(room_id).await
1785 }
1786
1787 async fn get_pending_key_bundle_details_for_room(
1788 &self,
1789 room_id: &RoomId,
1790 ) -> Result<Option<RoomPendingKeyBundleDetails>> {
1791 let room_id = self.encode_key("rooms_pending_key_bundle", room_id.as_bytes());
1792 let Some(value) = self.read().await?.get_room_pending_key_bundle(room_id).await? else {
1793 return Ok(None);
1794 };
1795
1796 let details = self.deserialize_value(&value)?;
1797 Ok(Some(details))
1798 }
1799
1800 async fn get_all_rooms_pending_key_bundles(&self) -> Result<Vec<RoomPendingKeyBundleDetails>> {
1801 let details = self.read().await?.get_all_rooms_pending_key_bundle().await?;
1802 let room_ids = details
1803 .into_iter()
1804 .map(|value| self.deserialize_value(&value))
1805 .collect::<Result<_, _>>()?;
1806 Ok(room_ids)
1807 }
1808
1809 async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>> {
1810 let Some(serialized) = self.read().await?.get_kv(key).await? else {
1811 return Ok(None);
1812 };
1813 let value = if let Some(cipher) = &self.store_cipher {
1814 let encrypted = rmp_serde::from_slice(&serialized)?;
1815 cipher.decrypt_value_data(encrypted)?
1816 } else {
1817 serialized
1818 };
1819
1820 Ok(Some(value))
1821 }
1822
1823 async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<()> {
1824 let serialized = if let Some(cipher) = &self.store_cipher {
1825 let encrypted = cipher.encrypt_value_data(value)?;
1826 rmp_serde::to_vec_named(&encrypted)?
1827 } else {
1828 value
1829 };
1830
1831 self.write().await?.set_kv(key, serialized).await?;
1832 Ok(())
1833 }
1834
1835 async fn remove_custom_value(&self, key: &str) -> Result<()> {
1836 let key = key.to_owned();
1837 self.write()
1838 .await?
1839 .interact(move |conn| conn.execute("DELETE FROM kv WHERE key = ?1", (&key,)))
1840 .await
1841 .unwrap()?;
1842 Ok(())
1843 }
1844
1845 #[instrument(skip(self))]
1846 async fn try_take_leased_lock(
1847 &self,
1848 lease_duration_ms: u32,
1849 key: &str,
1850 holder: &str,
1851 ) -> Result<Option<CrossProcessLockGeneration>> {
1852 let key = key.to_owned();
1853 let holder = holder.to_owned();
1854
1855 let now: u64 = MilliSecondsSinceUnixEpoch::now().get().into();
1856 let expiration = now + lease_duration_ms as u64;
1857
1858 let generation = self
1860 .write()
1861 .await?
1862 .with_transaction(move |txn| {
1863 txn.query_one(
1864 "INSERT INTO lease_locks (key, holder, expiration)
1865 VALUES (?1, ?2, ?3)
1866 ON CONFLICT (key)
1867 DO
1868 UPDATE SET
1869 holder = excluded.holder,
1870 expiration = excluded.expiration,
1871 generation =
1872 CASE holder
1873 WHEN excluded.holder THEN generation
1874 ELSE generation + 1
1875 END
1876 WHERE
1877 holder = excluded.holder
1878 OR expiration < ?4
1879 RETURNING generation
1880 ",
1881 (key, holder, expiration, now),
1882 |row| row.get(0),
1883 )
1884 .optional()
1885 })
1886 .await?;
1887
1888 Ok(generation)
1889 }
1890
1891 async fn next_batch_token(&self) -> Result<Option<String>, Self::Error> {
1892 let conn = self.read().await?;
1893 if let Some(token) = conn.get_kv("next_batch_token").await? {
1894 let maybe_token: Option<String> = self.deserialize_value(&token)?;
1895 Ok(maybe_token)
1896 } else {
1897 Ok(None)
1898 }
1899 }
1900
1901 async fn close(&self) -> Result<()> {
1902 connection::close_connections(&self.connections, "Crypto store").await;
1903 Ok(())
1904 }
1905
1906 async fn reopen(&self) -> Result<()> {
1907 connection::reopen_connections(
1908 &self.connections,
1909 self.db_path.clone(),
1910 self.pool_config,
1911 self.runtime_config,
1912 )
1913 .await?;
1914 Ok(())
1915 }
1916
1917 async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
1918 Ok(Some(self.read().await?.get_db_size().await?))
1919 }
1920}
1921
1922#[cfg(test)]
1923mod tests {
1924 use std::{path::Path, sync::LazyLock};
1925
1926 use matrix_sdk_common::deserialized_responses::WithheldCode;
1927 use matrix_sdk_crypto::{
1928 cryptostore_integration_tests, cryptostore_integration_tests_time, olm::SenderDataType,
1929 store::CryptoStore,
1930 };
1931 use matrix_sdk_test::async_test;
1932 use ruma::{device_id, room_id, user_id};
1933 use similar_asserts::assert_eq;
1934 use tempfile::{TempDir, tempdir};
1935 use tokio::fs;
1936
1937 use super::SqliteCryptoStore;
1938 use crate::SqliteStoreConfig;
1939
1940 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
1941
1942 struct TestDb {
1943 _dir: TempDir,
1946 database: SqliteCryptoStore,
1947 }
1948
1949 fn copy_db(data_path: &str) -> TempDir {
1950 let db_name = super::DATABASE_NAME;
1951
1952 let manifest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
1953 let database_path = manifest_path.join(data_path).join(db_name);
1954
1955 let tmpdir = tempdir().unwrap();
1956 let destination = tmpdir.path().join(db_name);
1957
1958 std::fs::copy(&database_path, destination).unwrap();
1960
1961 tmpdir
1962 }
1963
1964 async fn get_test_db(data_path: &str, passphrase: Option<&str>) -> TestDb {
1965 let tmpdir = copy_db(data_path);
1966
1967 let database = SqliteCryptoStore::open(tmpdir.path(), passphrase)
1968 .await
1969 .expect("Can't open the test store");
1970
1971 TestDb { _dir: tmpdir, database }
1972 }
1973
1974 #[async_test]
1975 async fn test_pool_size() {
1976 let store_open_config =
1977 SqliteStoreConfig::new(TMP_DIR.path().join("test_pool_size")).pool_max_size(42);
1978
1979 let store = SqliteCryptoStore::open_with_config(&store_open_config).await.unwrap();
1980
1981 let guard = store.connections.lock().await;
1982 let conns = guard.as_ref().unwrap();
1983 assert_eq!(conns.pool.status().max_size, 42);
1984 }
1985
1986 #[async_test]
1989 async fn test_open_test_vector_store() {
1990 let TestDb { _dir: _, database } = get_test_db("testing/data/storage", None).await;
1991
1992 let account = database
1993 .load_account()
1994 .await
1995 .unwrap()
1996 .expect("The test database is prefilled with data, we should find an account");
1997
1998 let user_id = account.user_id();
1999 let device_id = account.device_id();
2000
2001 assert_eq!(
2002 user_id.as_str(),
2003 "@pjtest:synapse-oidc.element.dev",
2004 "The user ID should match to the one we expect."
2005 );
2006
2007 assert_eq!(
2008 device_id.as_str(),
2009 "v4TqgcuIH6",
2010 "The device ID should match to the one we expect."
2011 );
2012
2013 let device = database
2014 .get_device(user_id, device_id)
2015 .await
2016 .unwrap()
2017 .expect("Our own device should be found in the store.");
2018
2019 assert_eq!(device.device_id(), device_id);
2020 assert_eq!(device.user_id(), user_id);
2021
2022 assert_eq!(
2023 device.ed25519_key().expect("The device should have a Ed25519 key.").to_base64(),
2024 "+cxl1Gl3du5i7UJwfWnoRDdnafFF+xYdAiTYYhYLr8s"
2025 );
2026
2027 assert_eq!(
2028 device.curve25519_key().expect("The device should have a Curve25519 key.").to_base64(),
2029 "4SL9eEUlpyWSUvjljC5oMjknHQQJY7WZKo5S1KL/5VU"
2030 );
2031
2032 let identity = database
2033 .get_user_identity(user_id)
2034 .await
2035 .unwrap()
2036 .expect("The store should contain an identity.");
2037
2038 assert_eq!(identity.user_id(), user_id);
2039
2040 let identity = identity
2041 .own()
2042 .expect("The identity should be of the correct type, it should be our own identity.");
2043
2044 let master_key = identity
2045 .master_key()
2046 .get_first_key()
2047 .expect("Our own identity should have a master key");
2048
2049 assert_eq!(master_key.to_base64(), "iCUEtB1RwANeqRa5epDrblLk4mer/36sylwQ5hYY3oE");
2050 }
2051
2052 #[async_test]
2055 async fn test_open_test_vector_encrypted_store() {
2056 let TestDb { _dir: _, database } = get_test_db(
2057 "testing/data/storage/alice",
2058 Some(concat!(
2059 "/rCia2fYAJ+twCZ1Xm2mxFCYcmJdyzkdJjwtgXsziWpYS/UeNxnixuSieuwZXm+x1VsJHmWpl",
2060 "H+QIQBZpEGZtC9/S/l8xK+WOCesmET0o6yJ/KP73ofDtjBlnNpPwuHLKFpyTbyicpCgQ4UT+5E",
2061 "UBuJ08TY9Ujdf1D13k5kr5tSZUefDKKCuG1fCRqlU8ByRas1PMQsZxT2W8t7QgBrQiiGmhpo/O",
2062 "Ti4hfx97GOxncKcxTzppiYQNoHs/f15+XXQD7/oiCcqRIuUlXNsU6hRpFGmbYx2Pi1eyQViQCt",
2063 "B5dAEiSD0N8U81wXYnpynuTPtnL+hfnOJIn7Sy7mkERQeKg"
2064 )),
2065 )
2066 .await;
2067
2068 let account = database
2069 .load_account()
2070 .await
2071 .unwrap()
2072 .expect("The test database is prefilled with data, we should find an account");
2073
2074 let user_id = account.user_id();
2075 let device_id = account.device_id();
2076
2077 assert_eq!(
2078 user_id.as_str(),
2079 "@alice:localhost",
2080 "The user ID should match to the one we expect."
2081 );
2082
2083 assert_eq!(
2084 device_id.as_str(),
2085 "JVVORTHFXY",
2086 "The device ID should match to the one we expect."
2087 );
2088
2089 let tracked_users =
2090 database.load_tracked_users().await.expect("Should be tracking some users");
2091
2092 assert_eq!(tracked_users.len(), 6);
2093
2094 let known_users = vec![
2095 user_id!("@alice:localhost"),
2096 user_id!("@dehydration3:localhost"),
2097 user_id!("@eve:localhost"),
2098 user_id!("@bob:localhost"),
2099 user_id!("@malo:localhost"),
2100 user_id!("@carl:localhost"),
2101 ];
2102
2103 for user_id in known_users {
2105 database.get_user_identity(user_id).await.expect("Should load this identity").unwrap();
2106 }
2107
2108 let carl_identity =
2109 database.get_user_identity(user_id!("@carl:localhost")).await.unwrap().unwrap();
2110
2111 assert_eq!(
2112 carl_identity.master_key().get_first_key().unwrap().to_base64(),
2113 "CdhKYYDeBDQveOioXEGWhTPCyzc63Irpar3CNyfun2Q"
2114 );
2115 assert!(!carl_identity.was_previously_verified());
2116
2117 let bob_identity =
2118 database.get_user_identity(user_id!("@bob:localhost")).await.unwrap().unwrap();
2119
2120 assert_eq!(
2121 bob_identity.master_key().get_first_key().unwrap().to_base64(),
2122 "COh2GYOJWSjem5QPRCaGp9iWV83IELG1IzLKW2S3pFY"
2123 );
2124 assert!(bob_identity.was_previously_verified());
2126
2127 let known_devices = vec![
2128 (device_id!("OPXQHCZSKW"), user_id!("@alice:localhost")),
2129 (
2131 device_id!("EvW+9IrGR10KVgVeZP25/KaPfx4R86FofVMcaz7VOho"),
2132 user_id!("@alice:localhost"),
2133 ),
2134 (device_id!("HEEFRFQENV"), user_id!("@alice:localhost")),
2135 (device_id!("JVVORTHFXY"), user_id!("@alice:localhost")),
2136 (device_id!("NQUWWSKKHS"), user_id!("@alice:localhost")),
2137 (device_id!("ORBLPFYCPG"), user_id!("@alice:localhost")),
2138 (device_id!("YXOWENSEGM"), user_id!("@dehydration3:localhost")),
2139 (device_id!("VXLFMYCHXC"), user_id!("@bob:localhost")),
2140 (device_id!("FDGDQAEWOW"), user_id!("@bob:localhost")),
2141 (device_id!("VXLFMYCHXC"), user_id!("@bob:localhost")),
2142 (device_id!("FDGDQAEWOW"), user_id!("@bob:localhost")),
2143 (device_id!("QKUKWJTTQC"), user_id!("@malo:localhost")),
2144 (device_id!("LOUXJECTFG"), user_id!("@malo:localhost")),
2145 (device_id!("MKKMAEVLPB"), user_id!("@carl:localhost")),
2146 ];
2147
2148 for (device_id, user_id) in known_devices {
2149 database.get_device(user_id, device_id).await.expect("Should load the device").unwrap();
2150 }
2151
2152 let known_sender_key_to_session_count = vec![
2153 ("FfYcYfDF4nWy+LHdK6CEpIMlFAQDORc30WUkghL06kM", 1),
2154 ("EvW+9IrGR10KVgVeZP25/KaPfx4R86FofVMcaz7VOho", 1),
2155 ("hAGsoA4a9M6wwEUX5Q1jux1i+tUngLi01n5AmhDoHTY", 1),
2156 ("aKqtSJymLzuoglWFwPGk1r/Vm2LE2hFESzXxn4RNjRM", 0),
2157 ("zHK1psCrgeMn0kaz8hcdvA3INyar9jg1yfrSp0p1pHo", 1),
2158 ("1QmBA316Wj5jIFRwNOti6N6Xh/vW0bsYCcR4uPfy8VQ", 1),
2159 ("g5ef2vZF3VXgSPyODIeXpyHIRkuthvLhGvd6uwYggWU", 1),
2160 ("o7hfupPd1VsNkRIvdlH6ujrEJFSKjFCGbxhAd31XxjI", 1),
2161 ("Z3RxKQLxY7xpP+ZdOGR2SiNE37SrvmRhW7GPu1UGdm8", 1),
2162 ("GDomaav8NiY3J+dNEeApJm+O0FooJ3IpVaIyJzCN4w4", 1),
2163 ("7m7fqkHyEr47V5s/KjaxtJMOr3pSHrrns2q2lWpAQi8", 0),
2164 ("9psAkPUIF8vNbWbnviX3PlwRcaeO53EHJdNtKpTY1X0", 0),
2165 ("mqanh+ztw5oRtpqYQgLGW864i6NY2zpoKMIlrcyC+Aw", 0),
2166 ("fJU/TJdbsv7tVbbpHw1Ke73ziElnM32cNhP2WIg4T10", 0),
2167 ("sUIeFeFcCZoa5IC6nJ6Vrbvztcyx09m8BBg57XKRClg", 1),
2168 ];
2169
2170 for (id, count) in known_sender_key_to_session_count {
2171 let olm_sessions =
2172 database.get_sessions(id).await.expect("Should have some olm sessions");
2173
2174 println!("### Session id: {id:?}");
2175 assert_eq!(olm_sessions.map_or(0, |v| v.len()), count);
2176 }
2177
2178 let inbound_group_sessions = database.get_inbound_group_sessions().await.unwrap();
2179 assert_eq!(inbound_group_sessions.len(), 15);
2180 let known_inbound_group_sessions = vec![
2181 (
2182 "5hNAxrLai3VI0LKBwfh3wLfksfBFWds0W1a5X5/vSXA",
2183 room_id!("!SRstFdydzrGwJYtVfm:localhost"),
2184 ),
2185 (
2186 "M6d2eU3y54gaYTbvGSlqa/xc1Az35l56Cp9sxzHWO4g",
2187 room_id!("!SRstFdydzrGwJYtVfm:localhost"),
2188 ),
2189 (
2190 "IrydwXkRk2N2AqUMIVmLL3oJgMq14R9KId0P/uSD100",
2191 room_id!("!SRstFdydzrGwJYtVfm:localhost"),
2192 ),
2193 (
2194 "Y74+l9jTo7N5UF+GQwdpgJGe4sn1+QtWITq7BxulHIE",
2195 room_id!("!SRstFdydzrGwJYtVfm:localhost"),
2196 ),
2197 (
2198 "HpJxQR57WbQGdY6w2Q+C16znVvbXGa+JvQdRoMpWbXg",
2199 room_id!("!SRstFdydzrGwJYtVfm:localhost"),
2200 ),
2201 (
2202 "Xetvi+ydFkZt8dpONGFbEusQb/Chc2V0XlLByZhsbgE",
2203 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2204 ),
2205 (
2206 "wv/WN/39akyerIXczTaIpjAuLnwgXKRtbXFSEHiJqxo",
2207 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2208 ),
2209 (
2210 "nA4gQwL//Cm8OdlyjABl/jChbPT/cP5V4Sd8iuE6H0s",
2211 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2212 ),
2213 (
2214 "bAAgqFeRDTjfEqL6Qf/c9mk55zoNDCSlboAIRd6b0hw",
2215 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2216 ),
2217 (
2218 "exPbsMMdGfAG2qmDdFtpAn+koVprfzS0Zip/RA9QRCE",
2219 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2220 ),
2221 (
2222 "h+om7oSw/ZV94fcKaoe8FGXJwQXWOfKQfzbGgNWQILI",
2223 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2224 ),
2225 (
2226 "ul3VXonpgk4lO2L3fEWubP/nxsTmLHqu5v8ZM9vHEcw",
2227 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2228 ),
2229 (
2230 "JXY15UxC3az2mwg8uX4qwgxfvCM4aygiIWMcdNiVQoc",
2231 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2232 ),
2233 (
2234 "OGB9lObr9kWUvha9tB5sMfOF/Mztk24JwQz/nwg3iFQ",
2235 room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
2236 ),
2237 (
2238 "SFkHcbxjUOYF7mUAYI/oEMDZFaXszQbCN6Jza7iemj0",
2239 room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
2240 ),
2241 ];
2242
2243 for (session_id, room_id) in &known_inbound_group_sessions {
2245 database
2246 .get_inbound_group_session(room_id, session_id)
2247 .await
2248 .expect("Should be able to load inbound group session")
2249 .unwrap();
2250 }
2251
2252 let bob_sender_verified = database
2253 .get_inbound_group_session(
2254 room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"),
2255 "exPbsMMdGfAG2qmDdFtpAn+koVprfzS0Zip/RA9QRCE",
2256 )
2257 .await
2258 .unwrap()
2259 .unwrap();
2260
2261 assert_eq!(bob_sender_verified.sender_data.to_type(), SenderDataType::SenderVerified);
2262 assert!(bob_sender_verified.backed_up());
2263 assert!(!bob_sender_verified.has_been_imported());
2264
2265 let alice_unknown_device = database
2266 .get_inbound_group_session(
2267 room_id!("!SRstFdydzrGwJYtVfm:localhost"),
2268 "IrydwXkRk2N2AqUMIVmLL3oJgMq14R9KId0P/uSD100",
2269 )
2270 .await
2271 .unwrap()
2272 .unwrap();
2273
2274 assert_eq!(alice_unknown_device.sender_data.to_type(), SenderDataType::UnknownDevice);
2275 assert!(alice_unknown_device.backed_up());
2276 assert!(alice_unknown_device.has_been_imported());
2277
2278 let carl_tofu_session = database
2279 .get_inbound_group_session(
2280 room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
2281 "OGB9lObr9kWUvha9tB5sMfOF/Mztk24JwQz/nwg3iFQ",
2282 )
2283 .await
2284 .unwrap()
2285 .unwrap();
2286
2287 assert_eq!(carl_tofu_session.sender_data.to_type(), SenderDataType::SenderUnverified);
2288 assert!(carl_tofu_session.backed_up());
2289 assert!(!carl_tofu_session.has_been_imported());
2290
2291 database
2293 .get_outbound_group_session(room_id!("!OgRiTRMaUzLdpCeDBM:localhost"))
2294 .await
2295 .unwrap()
2296 .unwrap();
2297 database
2298 .get_outbound_group_session(room_id!("!ZIwZcFqZVAYLAqVjfV:localhost"))
2299 .await
2300 .unwrap()
2301 .unwrap();
2302 database
2303 .get_outbound_group_session(room_id!("!SRstFdydzrGwJYtVfm:localhost"))
2304 .await
2305 .unwrap()
2306 .unwrap();
2307
2308 let withheld_info = database
2309 .get_withheld_info(
2310 room_id!("!OgRiTRMaUzLdpCeDBM:localhost"),
2311 "SASgZ+EklvAF4QxJclMlDRlmL0fAMjAJJIKFMdb4Ht0",
2312 )
2313 .await
2314 .expect("This session should be withheld")
2315 .unwrap();
2316
2317 assert_eq!(withheld_info.content.withheld_code(), WithheldCode::Unverified);
2318
2319 let backup_keys = database.load_backup_keys().await.expect("backup key should be cached");
2320 assert_eq!(backup_keys.backup_version.unwrap(), "6");
2321 assert!(backup_keys.decryption_key.is_some());
2322 }
2323
2324 #[async_test]
2330 async fn test_secrets_inbox_migration() {
2331 use std::ops::Deref;
2332
2333 use matrix_sdk_crypto::{
2334 GossipRequest, GossippedSecret, SecretInfo,
2335 types::events::{
2336 olm_v1::{DecryptedSecretSendEvent, OlmV1Keys},
2337 secret_send::SecretSendContent,
2338 },
2339 vodozemac::Ed25519SecretKey,
2340 };
2341 use ruma::{TransactionId, events::secret::request::SecretName, owned_user_id};
2342
2343 use crate::utils::{EncryptableStore, SqliteAsyncConnExt};
2344
2345 let tmpdir = tempdir().unwrap();
2347 let config = SqliteStoreConfig::new(tmpdir.path());
2348 let pool = config.build_pool_of_connections(super::DATABASE_NAME).unwrap();
2349 let conn = pool.get().await.unwrap();
2350 let version = super::initialize_store(&conn, 0).await.unwrap();
2351 let old_data_store = SqliteCryptoStore::create_raw(
2352 config.secret.clone(),
2353 pool,
2354 conn,
2355 config.pool_config(),
2356 config.runtime_config(),
2357 )
2358 .await
2359 .unwrap();
2360 super::run_migrations(&old_data_store, version, Some(16)).await.unwrap();
2361 old_data_store.write().await.unwrap().wal_checkpoint().await;
2362
2363 let secret = GossippedSecret {
2365 secret_name: SecretName::CrossSigningMasterKey,
2366 gossip_request: GossipRequest {
2367 request_recipient: owned_user_id!("@alice:example.com"),
2368 request_id: TransactionId::new(),
2369 info: SecretInfo::SecretRequest(SecretName::CrossSigningMasterKey),
2370 sent_out: true,
2371 },
2372 event: DecryptedSecretSendEvent {
2373 sender: owned_user_id!("@alice:example.com"),
2374 recipient: owned_user_id!("@alice:example.com"),
2375 keys: OlmV1Keys { ed25519: Ed25519SecretKey::new().public_key() },
2376 recipient_keys: OlmV1Keys { ed25519: Ed25519SecretKey::new().public_key() },
2377 sender_device_keys: None,
2378 content: SecretSendContent::new(
2379 "abc".into(),
2380 "It is a secret to everybody".to_owned(),
2381 ),
2382 },
2383 };
2384 let value = old_data_store.serialize_json(&secret).unwrap();
2385 old_data_store
2386 .write()
2387 .await
2388 .unwrap()
2389 .prepare("INSERT INTO secrets (secret_name, data) VALUES (?1, ?2)", |mut stmt| {
2390 stmt.execute((SecretName::CrossSigningMasterKey.to_string(), value))
2391 })
2392 .await
2393 .unwrap();
2394
2395 let store = SqliteCryptoStore::open_with_config(&config).await.unwrap();
2397
2398 let secrets =
2400 store.get_secrets_from_inbox(&SecretName::CrossSigningMasterKey).await.unwrap();
2401 assert_eq!(secrets.len(), 1);
2402 assert_eq!(secrets[0].deref(), "It is a secret to everybody");
2403 }
2404
2405 #[async_test]
2412 async fn test_key_requests_migration() {
2413 use matrix_sdk_crypto::{GossipRequest, SecretInfo};
2414 use ruma::{TransactionId, events::secret::request::SecretName, owned_user_id};
2415
2416 use crate::utils::{EncryptableStore, SqliteAsyncConnExt};
2417
2418 let tmpdir = tempdir().unwrap();
2420 let config = SqliteStoreConfig::new(tmpdir.path());
2421 let pool = config.build_pool_of_connections(super::DATABASE_NAME).unwrap();
2422 let conn = pool.get().await.unwrap();
2423 let version = super::initialize_store(&conn, 0).await.unwrap();
2424 let old_data_store = SqliteCryptoStore::create_raw(
2425 config.secret.clone(),
2426 pool,
2427 conn,
2428 config.pool_config(),
2429 config.runtime_config(),
2430 )
2431 .await
2432 .unwrap();
2433 super::run_migrations(&old_data_store, version, Some(16)).await.unwrap();
2434 old_data_store.write().await.unwrap().wal_checkpoint().await;
2435
2436 let recovery_request1 = GossipRequest {
2438 request_recipient: owned_user_id!("@alice:example.com"),
2439 request_id: TransactionId::new(),
2440 info: SecretInfo::SecretRequest(SecretName::RecoveryKey),
2441 sent_out: true,
2442 };
2443 let serialized_recovery_request1 =
2444 old_data_store.serialize_value(&recovery_request1).unwrap();
2445 let recovery_request2 = GossipRequest {
2446 request_recipient: owned_user_id!("@alice:example.com"),
2447 request_id: TransactionId::new(),
2448 info: SecretInfo::SecretRequest(SecretName::RecoveryKey),
2449 sent_out: true,
2450 };
2451 let serialized_recovery_request2 =
2452 old_data_store.serialize_value(&recovery_request2).unwrap();
2453 let msk_request = GossipRequest {
2454 request_recipient: owned_user_id!("@alice:example.com"),
2455 request_id: TransactionId::new(),
2456 info: SecretInfo::SecretRequest(SecretName::CrossSigningMasterKey),
2457 sent_out: true,
2458 };
2459 let serialized_msk_request = old_data_store.serialize_value(&msk_request).unwrap();
2460 let recovery_request1_clone = recovery_request1.clone();
2461 let recovery_request2_clone = recovery_request2.clone();
2462 let msk_request_clone = msk_request.clone();
2463 old_data_store
2464 .write()
2465 .await
2466 .unwrap()
2467 .prepare(
2468 "INSERT INTO key_requests (request_id, sent_out, data) VALUES (?1, ?2, ?3)",
2469 move |mut stmt| {
2470 stmt.execute((
2471 old_data_store.encode_key(
2472 "key_requests",
2473 recovery_request1_clone.request_id.as_bytes(),
2474 ),
2475 recovery_request1_clone.sent_out,
2476 serialized_recovery_request1,
2477 ))?;
2478 stmt.execute((
2479 old_data_store.encode_key(
2480 "key_requests",
2481 recovery_request2_clone.request_id.as_bytes(),
2482 ),
2483 recovery_request2_clone.sent_out,
2484 serialized_recovery_request2,
2485 ))?;
2486 stmt.execute((
2487 old_data_store
2488 .encode_key("key_requests", msk_request_clone.request_id.as_bytes()),
2489 msk_request_clone.sent_out,
2490 serialized_msk_request,
2491 ))
2492 },
2493 )
2494 .await
2495 .unwrap();
2496
2497 let store = SqliteCryptoStore::open_with_config(&config).await.unwrap();
2499
2500 if let Some(GossipRequest {
2503 request_id,
2504 info: SecretInfo::SecretRequest(SecretName::RecoveryKey),
2505 ..
2506 }) = store
2507 .get_secret_request_by_info(&SecretInfo::SecretRequest(SecretName::RecoveryKey))
2508 .await
2509 .unwrap()
2510 {
2511 if request_id == recovery_request1.request_id {
2512 assert!(
2513 store
2514 .get_outgoing_secret_requests(&recovery_request2.request_id)
2515 .await
2516 .unwrap()
2517 .is_none()
2518 );
2519 } else if request_id == recovery_request2.request_id {
2520 assert!(
2521 store
2522 .get_outgoing_secret_requests(&recovery_request1.request_id)
2523 .await
2524 .unwrap()
2525 .is_none()
2526 );
2527 } else {
2528 panic!("unexpected record found");
2529 }
2530 } else {
2531 panic!("expected to get a secret request");
2532 }
2533 if let Some(GossipRequest {
2534 request_id,
2535 info: SecretInfo::SecretRequest(SecretName::CrossSigningMasterKey),
2536 ..
2537 }) = store
2538 .get_secret_request_by_info(&SecretInfo::SecretRequest(
2539 SecretName::CrossSigningMasterKey,
2540 ))
2541 .await
2542 .unwrap()
2543 {
2544 assert_eq!(request_id, msk_request.request_id);
2545 } else {
2546 panic!("expected to get a secret request");
2547 }
2548 }
2549
2550 async fn get_store(
2551 name: &str,
2552 passphrase: Option<&str>,
2553 clear_data: bool,
2554 ) -> SqliteCryptoStore {
2555 let tmpdir_path = TMP_DIR.path().join(name);
2556
2557 if clear_data {
2558 let _ = fs::remove_dir_all(&tmpdir_path).await;
2559 }
2560
2561 SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), passphrase)
2562 .await
2563 .expect("Can't create a secret protected store")
2564 }
2565
2566 cryptostore_integration_tests!();
2567 cryptostore_integration_tests_time!();
2568}
2569
2570#[cfg(test)]
2571mod encrypted_tests {
2572 use std::sync::LazyLock;
2573
2574 use matrix_sdk_crypto::{cryptostore_integration_tests, cryptostore_integration_tests_time};
2575 use tempfile::{TempDir, tempdir};
2576 use tokio::fs;
2577
2578 use super::SqliteCryptoStore;
2579
2580 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2581
2582 async fn get_store(
2583 name: &str,
2584 passphrase: Option<&str>,
2585 clear_data: bool,
2586 ) -> SqliteCryptoStore {
2587 let tmpdir_path = TMP_DIR.path().join(name);
2588 let pass = passphrase.unwrap_or("default_test_password");
2589
2590 if clear_data {
2591 let _ = fs::remove_dir_all(&tmpdir_path).await;
2592 }
2593
2594 SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), Some(pass))
2595 .await
2596 .expect("Can't create a secret protected store")
2597 }
2598
2599 cryptostore_integration_tests!();
2600 cryptostore_integration_tests_time!();
2601}
2602
2603#[cfg(test)]
2604mod close_reopen_tests {
2605 use std::sync::LazyLock;
2606
2607 use matrix_sdk_crypto::store::CryptoStore;
2608 use matrix_sdk_test::async_test;
2609 use tempfile::{TempDir, tempdir};
2610
2611 use super::SqliteCryptoStore;
2612
2613 static TMP_DIR: LazyLock<TempDir> = LazyLock::new(|| tempdir().unwrap());
2614
2615 async fn new_store(name: &str) -> SqliteCryptoStore {
2616 let tmpdir_path = TMP_DIR.path().join(name);
2617 SqliteCryptoStore::open(tmpdir_path, None).await.unwrap()
2618 }
2619
2620 #[async_test]
2621 async fn test_close_completes_without_timeout() {
2622 let store = new_store("close_no_timeout").await;
2623
2624 let start = std::time::Instant::now();
2626 store.close().await.unwrap();
2627 let elapsed = start.elapsed();
2628
2629 assert!(
2630 elapsed < std::time::Duration::from_secs(2),
2631 "close() took {elapsed:?}, expected < 2s (no timeout)"
2632 );
2633
2634 let guard = store.connections.lock().await;
2636 assert!(guard.is_none(), "connections should be None after close");
2637 }
2638
2639 #[async_test]
2640 async fn test_reopen_restores_connections() {
2641 let store = new_store("reopen_restores").await;
2642
2643 store.close().await.unwrap();
2644
2645 {
2646 let guard = store.connections.lock().await;
2647 assert!(guard.is_none());
2648 }
2649
2650 store.reopen().await.unwrap();
2651
2652 {
2653 let guard = store.connections.lock().await;
2654 assert!(guard.is_some(), "connections should be Some after reopen");
2655 }
2656 }
2657
2658 #[async_test]
2659 async fn test_close_is_idempotent() {
2660 let store = new_store("close_idempotent").await;
2661
2662 store.close().await.unwrap();
2663 store.close().await.unwrap();
2665
2666 let guard = store.connections.lock().await;
2667 assert!(guard.is_none());
2668 }
2669
2670 #[async_test]
2671 async fn test_reopen_is_idempotent() {
2672 let store = new_store("reopen_idempotent").await;
2673
2674 store.reopen().await.unwrap();
2676
2677 let guard = store.connections.lock().await;
2678 assert!(guard.is_some());
2679 }
2680
2681 #[async_test]
2682 async fn test_read_fails_when_closed() {
2683 let store = new_store("read_fails_closed").await;
2684 store.close().await.unwrap();
2685
2686 let err = store.load_account().await;
2687 assert!(err.is_err(), "read should fail when closed");
2688
2689 let err_msg = err.unwrap_err().to_string();
2690 assert!(err_msg.contains("closed"), "error should mention 'closed', got: {err_msg}");
2691 }
2692
2693 #[async_test]
2694 async fn test_operations_work_after_reopen() {
2695 let store = new_store("ops_after_reopen").await;
2696
2697 store.close().await.unwrap();
2698 store.reopen().await.unwrap();
2699
2700 let account = store.load_account().await;
2702 assert!(account.is_ok(), "load_account should succeed after reopen");
2703 assert!(account.unwrap().is_none());
2705 }
2706
2707 #[async_test]
2708 async fn test_multiple_close_reopen_cycles() {
2709 let store = new_store("multi_cycles").await;
2710
2711 for _ in 0..5 {
2712 store.close().await.unwrap();
2713 store.reopen().await.unwrap();
2714
2715 let account = store.load_account().await;
2717 assert!(account.is_ok(), "store should work after close/reopen cycle");
2718 }
2719 }
2720
2721 #[async_test]
2722 async fn test_pool_is_fully_drained_after_close() {
2723 let store = new_store("pool_drained").await;
2724
2725 let _ = store.load_account().await;
2727 let _ = store.load_account().await;
2728
2729 store.close().await.unwrap();
2730
2731 let guard = store.connections.lock().await;
2733 assert!(guard.is_none(), "all connections should be released after close");
2734 }
2735
2736 #[async_test]
2737 async fn test_close_waits_for_held_read_connection_to_drain() {
2738 let store = new_store("held_read_drain").await;
2739
2740 let held_conn = store.read().await.unwrap();
2742
2743 let store_clone = store.clone();
2746 let close_handle = tokio::spawn(async move {
2747 store_clone.close().await.unwrap();
2748 });
2749
2750 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
2752
2753 assert!(!close_handle.is_finished(), "close should be waiting for the held connection");
2755
2756 drop(held_conn);
2758
2759 let timeout = tokio::time::timeout(std::time::Duration::from_secs(3), close_handle).await;
2761 assert!(timeout.is_ok(), "close should complete after the held connection is released");
2762 timeout.unwrap().unwrap();
2763
2764 let guard = store.connections.lock().await;
2766 assert!(guard.is_none(), "connections should be None after close");
2767 }
2768}