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