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