core_crypto_keystore/connection/
mod.rs1use std::fmt;
2use std::ops::Deref;
3
4use sha2::{Digest as _, Sha256};
5use zeroize::{Zeroize, ZeroizeOnDrop};
6
7pub mod platform {
8 cfg_if::cfg_if! {
9 if #[cfg(target_family = "wasm")] {
10 mod wasm;
11 pub use wasm::keystore_v_1_0_0;
12 pub use self::wasm::WasmConnection as KeystoreDatabaseConnection;
13 pub use wasm::storage;
14 pub use self::wasm::storage::WasmStorageTransaction as TransactionWrapper;
15
16 pub use wasm::open_and_migrate_pre_v4;
19 } else {
20 mod generic;
21 pub use self::generic::SqlCipherConnection as KeystoreDatabaseConnection;
22 pub use self::generic::TransactionWrapper;
23 }
24 }
25}
26
27pub use self::platform::*;
28use crate::entities::{Entity, EntityFindParams, StringEntityId};
29use std::ops::DerefMut;
30
31use crate::entities::{EntityTransactionExt, UniqueEntity};
32use crate::transaction::KeystoreTransaction;
33use crate::{CryptoKeystoreError, CryptoKeystoreResult};
34use async_lock::{Mutex, MutexGuard, Semaphore};
35use std::sync::Arc;
36
37pub const MAX_BLOB_LEN: usize = 1_000_000_000;
45
46#[cfg(not(target_family = "wasm"))]
47pub trait DatabaseConnectionRequirements: Sized + Send {}
49#[cfg(target_family = "wasm")]
50pub trait DatabaseConnectionRequirements: Sized {}
52
53#[derive(Clone, Zeroize, ZeroizeOnDrop, derive_more::From)]
55pub struct DatabaseKey([u8; Self::LEN]);
56
57impl DatabaseKey {
58 pub const LEN: usize = 32;
59
60 pub fn generate() -> DatabaseKey {
61 DatabaseKey(rand::random::<[u8; Self::LEN]>())
62 }
63}
64
65impl fmt::Debug for DatabaseKey {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
67 f.write_str("DatabaseKey(hash=")?;
68 for x in Sha256::digest(self).as_slice().iter().take(10) {
69 fmt::LowerHex::fmt(x, f)?
70 }
71 f.write_str("...)")
72 }
73}
74
75impl AsRef<[u8]> for DatabaseKey {
76 fn as_ref(&self) -> &[u8] {
77 &self.0
78 }
79}
80
81impl Deref for DatabaseKey {
82 type Target = [u8];
83
84 fn deref(&self) -> &Self::Target {
85 &self.0
86 }
87}
88
89impl TryFrom<&[u8]> for DatabaseKey {
90 type Error = CryptoKeystoreError;
91
92 fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
93 if buf.len() != Self::LEN {
94 Err(CryptoKeystoreError::InvalidDbKeySize {
95 expected: Self::LEN,
96 actual: buf.len(),
97 })
98 } else {
99 Ok(Self(buf.try_into().unwrap()))
100 }
101 }
102}
103
104#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
105#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
106pub trait DatabaseConnection<'a>: DatabaseConnectionRequirements {
107 type Connection: 'a;
108
109 async fn open(name: &str, key: &DatabaseKey) -> CryptoKeystoreResult<Self>;
110
111 async fn open_in_memory(name: &str, key: &DatabaseKey) -> CryptoKeystoreResult<Self>;
112
113 async fn close(self) -> CryptoKeystoreResult<()>;
114
115 async fn wipe(self) -> CryptoKeystoreResult<()> {
117 self.close().await
118 }
119
120 fn check_buffer_size(size: usize) -> CryptoKeystoreResult<()> {
121 #[cfg(not(target_family = "wasm"))]
122 if size > i32::MAX as usize {
123 return Err(CryptoKeystoreError::BlobTooBig);
124 }
125
126 if size >= MAX_BLOB_LEN {
127 return Err(CryptoKeystoreError::BlobTooBig);
128 }
129
130 Ok(())
131 }
132}
133
134#[derive(Debug, Clone)]
135pub struct Connection {
136 pub(crate) conn: Arc<Mutex<KeystoreDatabaseConnection>>,
137 pub(crate) transaction: Arc<Mutex<Option<KeystoreTransaction>>>,
138 transaction_semaphore: Arc<Semaphore>,
139}
140
141const ALLOWED_CONCURRENT_TRANSACTIONS_COUNT: usize = 1;
142
143#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
146#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
147pub trait FetchFromDatabase: Send + Sync {
148 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
149 &self,
150 id: &[u8],
151 ) -> CryptoKeystoreResult<Option<E>>;
152
153 async fn find_unique<U: UniqueEntity<ConnectionType = KeystoreDatabaseConnection>>(
154 &self,
155 ) -> CryptoKeystoreResult<U>;
156
157 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
158 &self,
159 params: EntityFindParams,
160 ) -> CryptoKeystoreResult<Vec<E>>;
161
162 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
163 &self,
164 ids: &[Vec<u8>],
165 ) -> CryptoKeystoreResult<Vec<E>>;
166 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize>;
167}
168
169unsafe impl Send for Connection {}
171unsafe impl Sync for Connection {}
173
174impl Connection {
175 pub async fn open_with_key(name: impl AsRef<str>, key: &DatabaseKey) -> CryptoKeystoreResult<Self> {
176 let conn = KeystoreDatabaseConnection::open(name.as_ref(), key).await?.into();
177 #[allow(clippy::arc_with_non_send_sync)] let conn = Arc::new(conn);
179 Ok(Self {
180 conn,
181 transaction: Default::default(),
182 transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
183 })
184 }
185
186 pub async fn open_in_memory_with_key(name: impl AsRef<str>, key: &DatabaseKey) -> CryptoKeystoreResult<Self> {
187 let conn = KeystoreDatabaseConnection::open_in_memory(name.as_ref(), key)
188 .await?
189 .into();
190 #[allow(clippy::arc_with_non_send_sync)] let conn = Arc::new(conn);
192 Ok(Self {
193 conn,
194 transaction: Default::default(),
195 transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
196 })
197 }
198
199 pub async fn borrow_conn(&self) -> CryptoKeystoreResult<MutexGuard<'_, KeystoreDatabaseConnection>> {
200 Ok(self.conn.lock().await)
201 }
202
203 pub async fn migrate_db_key_type_to_bytes(
204 name: &str,
205 old_key: &str,
206 new_key: &DatabaseKey,
207 ) -> CryptoKeystoreResult<()> {
208 KeystoreDatabaseConnection::migrate_db_key_type_to_bytes(name, old_key, new_key).await
209 }
210
211 pub async fn wipe(self) -> CryptoKeystoreResult<()> {
212 if self.transaction.lock().await.is_some() {
213 return Err(CryptoKeystoreError::TransactionInProgress {
214 attempted_operation: "wipe()".to_string(),
215 });
216 }
217 let conn: KeystoreDatabaseConnection = Arc::into_inner(self.conn).unwrap().into_inner();
218 conn.wipe().await?;
219 Ok(())
220 }
221
222 pub async fn can_close(&self) -> bool {
223 if self.transaction.lock().await.is_some() {
225 return false;
226 }
227 Arc::strong_count(&self.conn) <= 1
228 }
229
230 pub async fn close(self) -> CryptoKeystoreResult<()> {
231 if self.transaction.lock().await.is_some() {
232 return Err(CryptoKeystoreError::TransactionInProgress {
233 attempted_operation: "close()".to_string(),
234 });
235 }
236 let Some(conn) = Arc::into_inner(self.conn) else {
237 return Err(CryptoKeystoreError::CannotClose);
238 };
239 let conn = conn.into_inner();
240 conn.close().await?;
241 Ok(())
242 }
243
244 pub async fn new_transaction(&self) -> CryptoKeystoreResult<()> {
246 let semaphore = self.transaction_semaphore.acquire_arc().await;
247 let mut transaction_guard = self.transaction.lock().await;
248 *transaction_guard = Some(KeystoreTransaction::new(semaphore).await?);
249 Ok(())
250 }
251
252 pub async fn commit_transaction(&self) -> CryptoKeystoreResult<()> {
253 let mut transaction_guard = self.transaction.lock().await;
254 let Some(transaction) = transaction_guard.as_ref() else {
255 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
256 };
257 transaction.commit(self).await?;
258 *transaction_guard = None;
259 Ok(())
260 }
261
262 pub async fn rollback_transaction(&self) -> CryptoKeystoreResult<()> {
263 let mut transaction_guard = self.transaction.lock().await;
264 if transaction_guard.is_none() {
265 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
266 };
267 *transaction_guard = None;
268 Ok(())
269 }
270
271 pub async fn child_groups<
272 E: Entity<ConnectionType = KeystoreDatabaseConnection> + crate::entities::PersistedMlsGroupExt + Sync,
273 >(
274 &self,
275 entity: E,
276 ) -> CryptoKeystoreResult<Vec<E>> {
277 let mut conn = self.conn.lock().await;
278 let persisted_records = entity.child_groups(conn.deref_mut()).await?;
279
280 let transaction_guard = self.transaction.lock().await;
281 let Some(transaction) = transaction_guard.as_ref() else {
282 return Ok(persisted_records);
283 };
284 transaction.child_groups(entity, persisted_records).await
285 }
286
287 pub async fn save<E: Entity<ConnectionType = KeystoreDatabaseConnection> + Sync + EntityTransactionExt>(
288 &self,
289 entity: E,
290 ) -> CryptoKeystoreResult<E> {
291 let transaction_guard = self.transaction.lock().await;
292 let Some(transaction) = transaction_guard.as_ref() else {
293 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
294 };
295 transaction.save_mut(entity).await
296 }
297
298 pub async fn remove<
299 E: Entity<ConnectionType = KeystoreDatabaseConnection> + EntityTransactionExt,
300 S: AsRef<[u8]>,
301 >(
302 &self,
303 id: S,
304 ) -> CryptoKeystoreResult<()> {
305 let transaction_guard = self.transaction.lock().await;
306 let Some(transaction) = transaction_guard.as_ref() else {
307 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
308 };
309 transaction.remove::<E, S>(id).await
310 }
311
312 pub async fn cred_delete_by_credential(&self, cred: Vec<u8>) -> CryptoKeystoreResult<()> {
313 let transaction_guard = self.transaction.lock().await;
314 let Some(transaction) = transaction_guard.as_ref() else {
315 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
316 };
317 transaction.cred_delete_by_credential(cred).await
318 }
319}
320
321#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
322#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
323impl FetchFromDatabase for Connection {
324 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
325 &self,
326 id: &[u8],
327 ) -> CryptoKeystoreResult<Option<E>> {
328 if let Some(transaction) = self.transaction.lock().await.as_ref() {
330 if let Some(result) = transaction.find::<E>(id).await? {
332 return Ok(result);
334 }
335 }
336
337 let mut conn = self.conn.lock().await;
339 E::find_one(&mut conn, &id.into()).await
340 }
341
342 async fn find_unique<U: UniqueEntity>(&self) -> CryptoKeystoreResult<U> {
343 if let Some(transaction) = self.transaction.lock().await.as_ref() {
345 if let Some(result) = transaction.find_unique::<U>().await? {
347 return Ok(result);
349 }
350 }
351 let mut conn = self.conn.lock().await;
353 U::find_unique(&mut conn).await
354 }
355
356 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
357 &self,
358 params: EntityFindParams,
359 ) -> CryptoKeystoreResult<Vec<E>> {
360 let mut conn = self.conn.lock().await;
361 let persisted_records = E::find_all(&mut conn, params.clone()).await?;
362
363 let transaction_guard = self.transaction.lock().await;
364 let Some(transaction) = transaction_guard.as_ref() else {
365 return Ok(persisted_records);
366 };
367 transaction.find_all(persisted_records, params).await
368 }
369
370 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
371 &self,
372 ids: &[Vec<u8>],
373 ) -> CryptoKeystoreResult<Vec<E>> {
374 let entity_ids: Vec<StringEntityId> = ids.iter().map(|id| id.as_slice().into()).collect();
375 let mut conn = self.conn.lock().await;
376 let persisted_records = E::find_many(&mut conn, &entity_ids).await?;
377
378 let transaction_guard = self.transaction.lock().await;
379 let Some(transaction) = transaction_guard.as_ref() else {
380 return Ok(persisted_records);
381 };
382 transaction.find_many(persisted_records, ids).await
383 }
384
385 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize> {
386 if self.transaction.lock().await.is_some() {
387 return Ok(self.find_all::<E>(Default::default()).await?.len());
390 };
391 let mut conn = self.conn.lock().await;
392 E::count(&mut conn).await
393 }
394}