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(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
174#[derive(Debug, Clone)]
176pub enum ConnectionType<'a> {
177 Persistent(&'a str),
179 InMemory,
181}
182
183impl Connection {
184 pub async fn open(location: ConnectionType<'_>, key: &DatabaseKey) -> CryptoKeystoreResult<Self> {
185 let conn = match location {
186 ConnectionType::Persistent(name) => KeystoreDatabaseConnection::open(name, key).await?.into(),
187 ConnectionType::InMemory => KeystoreDatabaseConnection::open_in_memory(key).await?.into(),
188 };
189 #[allow(clippy::arc_with_non_send_sync)] let conn = Arc::new(conn);
191 Ok(Self {
192 conn,
193 transaction: Default::default(),
194 transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
195 })
196 }
197
198 pub async fn borrow_conn(&self) -> CryptoKeystoreResult<MutexGuard<'_, KeystoreDatabaseConnection>> {
199 Ok(self.conn.lock().await)
200 }
201
202 pub async fn migrate_db_key_type_to_bytes(
203 name: &str,
204 old_key: &str,
205 new_key: &DatabaseKey,
206 ) -> CryptoKeystoreResult<()> {
207 KeystoreDatabaseConnection::migrate_db_key_type_to_bytes(name, old_key, new_key).await
208 }
209
210 pub async fn wipe(self) -> CryptoKeystoreResult<()> {
211 if self.transaction.lock().await.is_some() {
212 return Err(CryptoKeystoreError::TransactionInProgress {
213 attempted_operation: "wipe()".to_string(),
214 });
215 }
216 let conn: KeystoreDatabaseConnection = Arc::into_inner(self.conn).unwrap().into_inner();
217 conn.wipe().await?;
218 Ok(())
219 }
220
221 pub async fn can_close(&self) -> bool {
222 if self.transaction.lock().await.is_some() {
224 return false;
225 }
226 Arc::strong_count(&self.conn) <= 1
227 }
228
229 pub async fn close(self) -> CryptoKeystoreResult<()> {
230 if self.transaction.lock().await.is_some() {
231 return Err(CryptoKeystoreError::TransactionInProgress {
232 attempted_operation: "close()".to_string(),
233 });
234 }
235 let Some(conn) = Arc::into_inner(self.conn) else {
236 return Err(CryptoKeystoreError::CannotClose);
237 };
238 let conn = conn.into_inner();
239 conn.close().await?;
240 Ok(())
241 }
242
243 pub async fn new_transaction(&self) -> CryptoKeystoreResult<()> {
245 let semaphore = self.transaction_semaphore.acquire_arc().await;
246 let mut transaction_guard = self.transaction.lock().await;
247 *transaction_guard = Some(KeystoreTransaction::new(semaphore).await?);
248 Ok(())
249 }
250
251 pub async fn commit_transaction(&self) -> CryptoKeystoreResult<()> {
252 let mut transaction_guard = self.transaction.lock().await;
253 let Some(transaction) = transaction_guard.as_ref() else {
254 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
255 };
256 transaction.commit(self).await?;
257 *transaction_guard = None;
258 Ok(())
259 }
260
261 pub async fn rollback_transaction(&self) -> CryptoKeystoreResult<()> {
262 let mut transaction_guard = self.transaction.lock().await;
263 if transaction_guard.is_none() {
264 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
265 };
266 *transaction_guard = None;
267 Ok(())
268 }
269
270 pub async fn child_groups<
271 E: Entity<ConnectionType = KeystoreDatabaseConnection> + crate::entities::PersistedMlsGroupExt + Sync,
272 >(
273 &self,
274 entity: E,
275 ) -> CryptoKeystoreResult<Vec<E>> {
276 let mut conn = self.conn.lock().await;
277 let persisted_records = entity.child_groups(conn.deref_mut()).await?;
278
279 let transaction_guard = self.transaction.lock().await;
280 let Some(transaction) = transaction_guard.as_ref() else {
281 return Ok(persisted_records);
282 };
283 transaction.child_groups(entity, persisted_records).await
284 }
285
286 pub async fn save<E: Entity<ConnectionType = KeystoreDatabaseConnection> + Sync + EntityTransactionExt>(
287 &self,
288 entity: E,
289 ) -> CryptoKeystoreResult<E> {
290 let transaction_guard = self.transaction.lock().await;
291 let Some(transaction) = transaction_guard.as_ref() else {
292 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
293 };
294 transaction.save_mut(entity).await
295 }
296
297 pub async fn remove<
298 E: Entity<ConnectionType = KeystoreDatabaseConnection> + EntityTransactionExt,
299 S: AsRef<[u8]>,
300 >(
301 &self,
302 id: S,
303 ) -> CryptoKeystoreResult<()> {
304 let transaction_guard = self.transaction.lock().await;
305 let Some(transaction) = transaction_guard.as_ref() else {
306 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
307 };
308 transaction.remove::<E, S>(id).await
309 }
310
311 pub async fn cred_delete_by_credential(&self, cred: Vec<u8>) -> CryptoKeystoreResult<()> {
312 let transaction_guard = self.transaction.lock().await;
313 let Some(transaction) = transaction_guard.as_ref() else {
314 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
315 };
316 transaction.cred_delete_by_credential(cred).await
317 }
318}
319
320#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
321#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
322impl FetchFromDatabase for Connection {
323 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
324 &self,
325 id: &[u8],
326 ) -> CryptoKeystoreResult<Option<E>> {
327 if let Some(transaction) = self.transaction.lock().await.as_ref() {
329 if let Some(result) = transaction.find::<E>(id).await? {
331 return Ok(result);
333 }
334 }
335
336 let mut conn = self.conn.lock().await;
338 E::find_one(&mut conn, &id.into()).await
339 }
340
341 async fn find_unique<U: UniqueEntity>(&self) -> CryptoKeystoreResult<U> {
342 if let Some(transaction) = self.transaction.lock().await.as_ref() {
344 if let Some(result) = transaction.find_unique::<U>().await? {
346 return Ok(result);
348 }
349 }
350 let mut conn = self.conn.lock().await;
352 U::find_unique(&mut conn).await
353 }
354
355 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
356 &self,
357 params: EntityFindParams,
358 ) -> CryptoKeystoreResult<Vec<E>> {
359 let mut conn = self.conn.lock().await;
360 let persisted_records = E::find_all(&mut conn, params.clone()).await?;
361
362 let transaction_guard = self.transaction.lock().await;
363 let Some(transaction) = transaction_guard.as_ref() else {
364 return Ok(persisted_records);
365 };
366 transaction.find_all(persisted_records, params).await
367 }
368
369 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
370 &self,
371 ids: &[Vec<u8>],
372 ) -> CryptoKeystoreResult<Vec<E>> {
373 let entity_ids: Vec<StringEntityId> = ids.iter().map(|id| id.as_slice().into()).collect();
374 let mut conn = self.conn.lock().await;
375 let persisted_records = E::find_many(&mut conn, &entity_ids).await?;
376
377 let transaction_guard = self.transaction.lock().await;
378 let Some(transaction) = transaction_guard.as_ref() else {
379 return Ok(persisted_records);
380 };
381 transaction.find_many(persisted_records, ids).await
382 }
383
384 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize> {
385 if self.transaction.lock().await.is_some() {
386 return Ok(self.find_all::<E>(Default::default()).await?.len());
389 };
390 let mut conn = self.conn.lock().await;
391 E::count(&mut conn).await
392 }
393}