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 update_key(&mut self, new_key: &DatabaseKey) -> CryptoKeystoreResult<()>;
114
115 async fn close(self) -> CryptoKeystoreResult<()>;
116
117 async fn wipe(self) -> CryptoKeystoreResult<()> {
119 self.close().await
120 }
121
122 fn check_buffer_size(size: usize) -> CryptoKeystoreResult<()> {
123 #[cfg(not(target_family = "wasm"))]
124 if size > i32::MAX as usize {
125 return Err(CryptoKeystoreError::BlobTooBig);
126 }
127
128 if size >= MAX_BLOB_LEN {
129 return Err(CryptoKeystoreError::BlobTooBig);
130 }
131
132 Ok(())
133 }
134}
135
136#[derive(Debug, Clone)]
137pub struct Connection {
138 pub(crate) conn: Arc<Mutex<KeystoreDatabaseConnection>>,
139 pub(crate) transaction: Arc<Mutex<Option<KeystoreTransaction>>>,
140 transaction_semaphore: Arc<Semaphore>,
141}
142
143const ALLOWED_CONCURRENT_TRANSACTIONS_COUNT: usize = 1;
144
145#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
148#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
149pub trait FetchFromDatabase: Send + Sync {
150 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
151 &self,
152 id: &[u8],
153 ) -> CryptoKeystoreResult<Option<E>>;
154
155 async fn find_unique<U: UniqueEntity<ConnectionType = KeystoreDatabaseConnection>>(
156 &self,
157 ) -> CryptoKeystoreResult<U>;
158
159 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
160 &self,
161 params: EntityFindParams,
162 ) -> CryptoKeystoreResult<Vec<E>>;
163
164 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
165 &self,
166 ids: &[Vec<u8>],
167 ) -> CryptoKeystoreResult<Vec<E>>;
168 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize>;
169}
170
171unsafe impl Send for Connection {}
173unsafe impl Sync for Connection {}
175
176#[derive(Debug, Clone)]
178pub enum ConnectionType<'a> {
179 Persistent(&'a str),
181 InMemory,
183}
184
185impl Connection {
186 pub async fn open(location: ConnectionType<'_>, key: &DatabaseKey) -> CryptoKeystoreResult<Self> {
187 let conn = match location {
188 ConnectionType::Persistent(name) => KeystoreDatabaseConnection::open(name, key).await?.into(),
189 ConnectionType::InMemory => KeystoreDatabaseConnection::open_in_memory(key).await?.into(),
190 };
191 #[allow(clippy::arc_with_non_send_sync)] let conn = Arc::new(conn);
193 Ok(Self {
194 conn,
195 transaction: Default::default(),
196 transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
197 })
198 }
199
200 pub async fn borrow_conn(&self) -> CryptoKeystoreResult<MutexGuard<'_, KeystoreDatabaseConnection>> {
201 Ok(self.conn.lock().await)
202 }
203
204 pub async fn migrate_db_key_type_to_bytes(
205 name: &str,
206 old_key: &str,
207 new_key: &DatabaseKey,
208 ) -> CryptoKeystoreResult<()> {
209 KeystoreDatabaseConnection::migrate_db_key_type_to_bytes(name, old_key, new_key).await
210 }
211
212 pub async fn update_key(&mut self, new_key: &DatabaseKey) -> CryptoKeystoreResult<()> {
213 self.conn.lock().await.update_key(new_key).await
214 }
215
216 pub async fn wipe(self) -> CryptoKeystoreResult<()> {
217 if self.transaction.lock().await.is_some() {
218 return Err(CryptoKeystoreError::TransactionInProgress {
219 attempted_operation: "wipe()".to_string(),
220 });
221 }
222 let conn: KeystoreDatabaseConnection = Arc::into_inner(self.conn).unwrap().into_inner();
223 conn.wipe().await?;
224 Ok(())
225 }
226
227 pub async fn close(self) -> CryptoKeystoreResult<()> {
229 let _semaphore = self.transaction_semaphore.acquire_arc().await;
231 let Some(conn) = Arc::into_inner(self.conn) else {
233 return Err(CryptoKeystoreError::CannotClose);
234 };
235 let conn = conn.into_inner();
236 conn.close().await?;
237 Ok(())
238 }
239
240 pub async fn new_transaction(&self) -> CryptoKeystoreResult<()> {
242 let semaphore = self.transaction_semaphore.acquire_arc().await;
243 let mut transaction_guard = self.transaction.lock().await;
244 *transaction_guard = Some(KeystoreTransaction::new(semaphore).await?);
245 Ok(())
246 }
247
248 pub async fn commit_transaction(&self) -> CryptoKeystoreResult<()> {
249 let mut transaction_guard = self.transaction.lock().await;
250 let Some(transaction) = transaction_guard.as_ref() else {
251 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
252 };
253 transaction.commit(self).await?;
254 *transaction_guard = None;
255 Ok(())
256 }
257
258 pub async fn rollback_transaction(&self) -> CryptoKeystoreResult<()> {
259 let mut transaction_guard = self.transaction.lock().await;
260 if transaction_guard.is_none() {
261 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
262 };
263 *transaction_guard = None;
264 Ok(())
265 }
266
267 pub async fn child_groups<
268 E: Entity<ConnectionType = KeystoreDatabaseConnection> + crate::entities::PersistedMlsGroupExt + Sync,
269 >(
270 &self,
271 entity: E,
272 ) -> CryptoKeystoreResult<Vec<E>> {
273 let mut conn = self.conn.lock().await;
274 let persisted_records = entity.child_groups(conn.deref_mut()).await?;
275
276 let transaction_guard = self.transaction.lock().await;
277 let Some(transaction) = transaction_guard.as_ref() else {
278 return Ok(persisted_records);
279 };
280 transaction.child_groups(entity, persisted_records).await
281 }
282
283 pub async fn save<E: Entity<ConnectionType = KeystoreDatabaseConnection> + Sync + EntityTransactionExt>(
284 &self,
285 entity: E,
286 ) -> CryptoKeystoreResult<E> {
287 let transaction_guard = self.transaction.lock().await;
288 let Some(transaction) = transaction_guard.as_ref() else {
289 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
290 };
291 transaction.save_mut(entity).await
292 }
293
294 pub async fn remove<
295 E: Entity<ConnectionType = KeystoreDatabaseConnection> + EntityTransactionExt,
296 S: AsRef<[u8]>,
297 >(
298 &self,
299 id: S,
300 ) -> CryptoKeystoreResult<()> {
301 let transaction_guard = self.transaction.lock().await;
302 let Some(transaction) = transaction_guard.as_ref() else {
303 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
304 };
305 transaction.remove::<E, S>(id).await
306 }
307
308 pub async fn cred_delete_by_credential(&self, cred: Vec<u8>) -> CryptoKeystoreResult<()> {
309 let transaction_guard = self.transaction.lock().await;
310 let Some(transaction) = transaction_guard.as_ref() else {
311 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
312 };
313 transaction.cred_delete_by_credential(cred).await
314 }
315}
316
317#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
318#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
319impl FetchFromDatabase for Connection {
320 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
321 &self,
322 id: &[u8],
323 ) -> CryptoKeystoreResult<Option<E>> {
324 if let Some(transaction) = self.transaction.lock().await.as_ref() {
326 if let Some(result) = transaction.find::<E>(id).await? {
328 return Ok(result);
330 }
331 }
332
333 let mut conn = self.conn.lock().await;
335 E::find_one(&mut conn, &id.into()).await
336 }
337
338 async fn find_unique<U: UniqueEntity>(&self) -> CryptoKeystoreResult<U> {
339 if let Some(transaction) = self.transaction.lock().await.as_ref() {
341 if let Some(result) = transaction.find_unique::<U>().await? {
343 return Ok(result);
345 }
346 }
347 let mut conn = self.conn.lock().await;
349 U::find_unique(&mut conn).await
350 }
351
352 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
353 &self,
354 params: EntityFindParams,
355 ) -> CryptoKeystoreResult<Vec<E>> {
356 let mut conn = self.conn.lock().await;
357 let persisted_records = E::find_all(&mut conn, params.clone()).await?;
358
359 let transaction_guard = self.transaction.lock().await;
360 let Some(transaction) = transaction_guard.as_ref() else {
361 return Ok(persisted_records);
362 };
363 transaction.find_all(persisted_records, params).await
364 }
365
366 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
367 &self,
368 ids: &[Vec<u8>],
369 ) -> CryptoKeystoreResult<Vec<E>> {
370 let entity_ids: Vec<StringEntityId> = ids.iter().map(|id| id.as_slice().into()).collect();
371 let mut conn = self.conn.lock().await;
372 let persisted_records = E::find_many(&mut conn, &entity_ids).await?;
373
374 let transaction_guard = self.transaction.lock().await;
375 let Some(transaction) = transaction_guard.as_ref() else {
376 return Ok(persisted_records);
377 };
378 transaction.find_many(persisted_records, ids).await
379 }
380
381 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize> {
382 if self.transaction.lock().await.is_some() {
383 return Ok(self.find_all::<E>(Default::default()).await?.len());
386 };
387 let mut conn = self.conn.lock().await;
388 E::count(&mut conn).await
389 }
390}