core_crypto_keystore/entities/
mod.rs

1pub(crate) mod general;
2pub(crate) mod mls;
3
4pub use self::{general::*, mls::*};
5
6cfg_if::cfg_if! {
7    if #[cfg(feature = "proteus-keystore")] {
8        pub(crate) mod proteus;
9        pub use self::proteus::*;
10    }
11}
12
13mod platform {
14    cfg_if::cfg_if! {
15        if #[cfg(target_family = "wasm")] {
16            mod wasm;
17            pub use self::wasm::*;
18        } else {
19            mod generic;
20            pub use self::generic::*;
21        }
22    }
23}
24
25#[cfg(target_family = "wasm")]
26use aes_gcm::Aes256Gcm;
27use serde::{Deserialize, Serialize};
28
29pub use self::platform::*;
30#[cfg(not(target_family = "wasm"))]
31use crate::sha256;
32use crate::{CryptoKeystoreError, CryptoKeystoreResult, MissingKeyErrorKind, connection::DatabaseConnection};
33
34#[derive(Debug, Clone, PartialEq, Eq, Default)]
35#[cfg_attr(target_family = "wasm", derive(serde::Serialize, serde::Deserialize))]
36#[repr(transparent)]
37pub struct StringEntityId<'a>(&'a [u8]);
38
39impl<'a> StringEntityId<'a> {
40    pub fn new(bytes: &'a [u8]) -> Self {
41        Self(bytes)
42    }
43
44    pub fn as_hex_string(&self) -> String {
45        hex::encode(self.0)
46    }
47
48    #[cfg(not(target_family = "wasm"))]
49    pub(crate) fn sha256(&self) -> String {
50        sha256(self.0)
51    }
52
53    pub fn to_bytes(&self) -> Vec<u8> {
54        self.0.into()
55    }
56
57    pub fn as_slice(&self) -> &[u8] {
58        self.0
59    }
60
61    pub fn try_as_str(&self) -> Result<&str, ::core::str::Utf8Error> {
62        std::str::from_utf8(self.0)
63    }
64}
65
66impl TryInto<String> for &StringEntityId<'_> {
67    type Error = CryptoKeystoreError;
68
69    fn try_into(self) -> CryptoKeystoreResult<String> {
70        Ok(String::from_utf8(self.0.into())?)
71    }
72}
73
74impl std::fmt::Display for StringEntityId<'_> {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "{}", self.as_hex_string())
77    }
78}
79
80impl<'a> From<&'a [u8]> for StringEntityId<'a> {
81    fn from(bytes: &'a [u8]) -> Self {
82        Self::new(bytes)
83    }
84}
85
86#[derive(Debug, Clone, Default)]
87pub struct EntityFindParams {
88    pub limit: Option<u32>,
89    pub offset: Option<u32>,
90    pub reverse: bool,
91}
92
93#[cfg(not(target_family = "wasm"))]
94impl EntityFindParams {
95    pub fn to_sql(&self) -> String {
96        use std::fmt::Write as _;
97        let mut query: String = "".into();
98        if let Some(offset) = self.offset {
99            let _ = write!(query, " OFFSET {offset}");
100        }
101        let _ = write!(query, " ORDER BY rowid");
102        if self.reverse {
103            let _ = write!(query, " DESC");
104        }
105        if let Some(limit) = self.limit {
106            let _ = write!(query, " LIMIT {limit}");
107        }
108
109        query
110    }
111}
112
113#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
114#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
115pub trait EntityBase:
116    'static + Send + Sized + Clone + PartialEq + Eq + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>
117{
118    type ConnectionType: for<'a> DatabaseConnection<'a>;
119    type AutoGeneratedFields: Default;
120    /// Beware: if you change the value of this constant on any WASM entity, you'll need to do a data migration
121    ///     not only because it is used as reference to the object store names but also for the value of the aad.
122    const COLLECTION_NAME: &'static str;
123
124    fn to_missing_key_err_kind() -> MissingKeyErrorKind;
125
126    fn downcast<T: EntityBase>(&self) -> Option<&T> {
127        let as_dyn_any: &dyn std::any::Any = self;
128        as_dyn_any.downcast_ref()
129    }
130
131    fn to_transaction_entity(self) -> crate::transaction::dynamic_dispatch::Entity;
132}
133
134cfg_if::cfg_if! {
135    if #[cfg(target_family = "wasm")] {
136        const AES_GCM_256_NONCE_SIZE: usize = 12;
137
138        #[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
139        struct Aad {
140            type_name: Vec<u8>,
141            id: Vec<u8>,
142        }
143
144        #[async_trait::async_trait(?Send)]
145        pub trait EntityTransactionExt: Entity<ConnectionType = crate::connection::KeystoreDatabaseConnection> {
146            async fn save<'a>(&'a self, tx: &crate::connection::storage::WasmStorageTransaction<'a>) -> CryptoKeystoreResult<()> {
147                tx.save(self.clone()).await
148            }
149            async fn pre_save<'a>(&'a mut self) -> CryptoKeystoreResult<Self::AutoGeneratedFields> {
150                Ok(Default::default())
151            }
152            async fn delete_fail_on_missing_id<'a>(tx: &crate::connection::storage::WasmStorageTransaction<'a>, id: StringEntityId<'a>) -> CryptoKeystoreResult<()> {
153                tx.delete(Self::COLLECTION_NAME, id.as_slice()).await
154            }
155
156            async fn delete<'a>(tx: &crate::connection::storage::WasmStorageTransaction<'a>, id: StringEntityId<'a>) -> CryptoKeystoreResult<()> {
157                match Self::delete_fail_on_missing_id(tx, id).await{
158                    Ok(_) => Ok(()),
159                    Err(CryptoKeystoreError::IdbError(idb::Error::DeleteFailed(_))) => Ok(()),
160                    Err(e) => Err(e),
161                }
162            }
163        }
164
165        #[async_trait::async_trait(?Send)]
166        pub trait Entity: EntityBase + serde::Serialize + serde::de::DeserializeOwned {
167            fn id(&self) -> CryptoKeystoreResult<wasm_bindgen::JsValue> {
168                Ok(js_sys::Uint8Array::from(self.id_raw()).into())
169            }
170
171            fn id_raw(&self) -> &[u8];
172
173            /// The query results that are obtained during a transaction
174            /// from the transaction cache and the database are merged by this key.
175            fn merge_key(&self) -> Vec<u8> {
176                self.id_raw().into()
177            }
178
179            fn aad(&self) -> CryptoKeystoreResult<Vec<u8>> {
180                let aad = Aad {
181                    type_name: Self::COLLECTION_NAME.as_bytes().to_vec(),
182                    id: self.id_raw().into(),
183                };
184                serde_json::to_vec(&aad).map_err(Into::into)
185            }
186
187            async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>>;
188            async fn find_one(conn: &mut Self::ConnectionType, id: &StringEntityId) -> CryptoKeystoreResult<Option<Self>>;
189            async fn find_many(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> CryptoKeystoreResult<Vec<Self>> {
190                // Default, inefficient & naive method
191                let mut ret = Vec::with_capacity(ids.len());
192                for id in ids {
193                    if let Some(entity) = Self::find_one(conn, id).await? {
194                        ret.push(entity);
195                    }
196                }
197                Ok(ret)
198            }
199            async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize>;
200
201            // About WASM Encryption:
202            // The store key (i.e. passphrase) is hashed using SHA256 to obtain 32 bytes
203            // The AES256-GCM cipher is then initialized and is used to encrypt individual values
204            // Entities shall decide which fields need to be encrypted
205            // Internal layout:
206            // - Cleartext: [u8] bytes
207            // - Ciphertext: [12 bytes of nonce..., ...encrypted data]
208            fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()>;
209            fn encrypt_with_nonce_and_aad(cipher: &aes_gcm::Aes256Gcm, data: &[u8], nonce: &[u8], aad: &[u8]) -> CryptoKeystoreResult<Vec<u8>> {
210                use aes_gcm::aead::Aead as _;
211                let nonce = aes_gcm::Nonce::from_slice(nonce);
212                let msg = data;
213                let payload = aes_gcm::aead::Payload {
214                    msg,
215                    aad,
216                };
217
218                let mut encrypted = cipher.encrypt(nonce, payload).map_err(|_| CryptoKeystoreError::AesGcmError)?;
219                let mut message = Vec::with_capacity(nonce.len() + encrypted.len());
220                message.extend_from_slice(nonce);
221                message.append(&mut encrypted);
222                Ok(message)
223            }
224
225            fn encrypt_data(&self, cipher: &aes_gcm::Aes256Gcm, data: &[u8]) -> CryptoKeystoreResult<Vec<u8>> {
226                let nonce_bytes: [u8; AES_GCM_256_NONCE_SIZE] = rand::random();
227                Self::encrypt_with_nonce_and_aad(cipher, data, &nonce_bytes, &self.aad()?)
228            }
229
230            fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()>;
231            fn decrypt_data(&self, cipher: &aes_gcm::Aes256Gcm, data: &[u8]) -> CryptoKeystoreResult<Vec<u8>> {
232                use aes_gcm::aead::Aead as _;
233
234                if data.is_empty() {
235                    return Err(CryptoKeystoreError::MissingKeyInStore(Self::to_missing_key_err_kind()));
236                }
237                if data.len() < AES_GCM_256_NONCE_SIZE {
238                    return Err(CryptoKeystoreError::AesGcmError);
239                }
240
241                let nonce_bytes = &data[..AES_GCM_256_NONCE_SIZE];
242                let nonce = aes_gcm::Nonce::from_slice(nonce_bytes);
243                let msg = &data[AES_GCM_256_NONCE_SIZE..];
244                let aad = &self.aad()?;
245                let payload = aes_gcm::aead::Payload {
246                    msg,
247                    aad,
248                };
249                let cleartext = cipher.decrypt(nonce, payload).map_err(|_| CryptoKeystoreError::AesGcmError)?;
250                Ok(cleartext)
251            }
252        }
253
254        #[async_trait::async_trait(?Send)]
255        impl<T: UniqueEntity + serde::Serialize + serde::de::DeserializeOwned> Entity for T {
256            fn id_raw(&self) -> &[u8] {
257                &Self::ID
258            }
259
260            async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>> {
261                    <Self as UniqueEntity>::find_all(conn, params).await
262                }
263
264            async fn find_one(conn: &mut Self::ConnectionType, _id: &StringEntityId) -> CryptoKeystoreResult<Option<Self>> {
265                    <Self as UniqueEntity>::find_one(conn).await
266                }
267
268            async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
269                    <Self as UniqueEntity>::count(conn).await
270                }
271
272            fn encrypt(&mut self, cipher: &Aes256Gcm) -> CryptoKeystoreResult<()> {
273                self.set_content(self.encrypt_data(cipher, self.content())?);
274                Self::ConnectionType::check_buffer_size(self.content().len())?;
275                Ok(())
276            }
277
278            fn decrypt(&mut self, cipher: &Aes256Gcm) -> CryptoKeystoreResult<()> {
279                self.set_content(self.decrypt_data(cipher, self.content())?);
280                Self::ConnectionType::check_buffer_size(self.content().len())?;
281                Ok(())
282            }
283        }
284    } else {
285        #[async_trait::async_trait]
286        pub trait EntityTransactionExt: Entity {
287            async fn save(&self, tx: &crate::connection::TransactionWrapper<'_>) -> CryptoKeystoreResult<()>;
288            async fn pre_save<'a>(&'a mut self) -> CryptoKeystoreResult<Self::AutoGeneratedFields> {
289                Ok(Default::default())
290            }
291            async fn delete_fail_on_missing_id(
292                tx: &crate::connection::TransactionWrapper<'_>,
293                id: StringEntityId<'_>,
294            ) -> CryptoKeystoreResult<()>;
295
296            async fn delete(
297                tx: &crate::connection::TransactionWrapper<'_>,
298                id: StringEntityId<'_>,
299            ) -> CryptoKeystoreResult<()> {
300                match Self::delete_fail_on_missing_id(tx, id).await{
301                    Ok(_) => Ok(()),
302                    Err(CryptoKeystoreError::MissingKeyInStore(_)) => Ok(()),
303                    Err(e) => Err(e),
304                }
305            }
306        }
307
308        #[async_trait::async_trait]
309        pub trait Entity: EntityBase {
310            fn id_raw(&self) -> &[u8];
311
312            /// The query results that are obtained during a transaction
313            /// from the transaction cache and the database are merged by this key.
314            fn merge_key(&self) -> Vec<u8> {
315                self.id_raw().into()
316            }
317
318            async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>>;
319            async fn find_one(conn: &mut Self::ConnectionType, id: &StringEntityId) -> CryptoKeystoreResult<Option<Self>>;
320            async fn find_many(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> CryptoKeystoreResult<Vec<Self>> {
321                // Default, inefficient & naive method
322                let mut ret = Vec::with_capacity(ids.len());
323                for id in ids {
324                    if let Some(entity) = Self::find_one(conn, id).await? {
325                        ret.push(entity);
326                    }
327                }
328                Ok(ret)
329            }
330            async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize>;
331        }
332
333        #[async_trait::async_trait]
334        impl<T: UniqueEntity> Entity for T {
335            fn id_raw(&self) -> &[u8] {
336                &[Self::ID as u8]
337            }
338
339            async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>> {
340                <Self as UniqueEntity>::find_all(conn, params).await
341            }
342
343            async fn find_one(conn: &mut Self::ConnectionType, _id: &StringEntityId) -> CryptoKeystoreResult<Option<Self>> {
344                <Self as UniqueEntity>::find_one(conn).await
345            }
346
347            async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
348               <Self as UniqueEntity>::count(conn).await
349            }
350        }
351
352        pub trait EntityIdStringExt: Entity {
353            fn id_hex(&self) -> String {
354                hex::encode(self.id_raw())
355            }
356
357            fn id_sha256(&self) -> String {
358                sha256(self.id_raw())
359            }
360
361            fn id_from_hex(id_hex: &str) -> CryptoKeystoreResult<Vec<u8>> {
362                hex::decode(id_hex).map_err(Into::into)
363            }
364        }
365
366        impl<T: Entity> EntityIdStringExt for T {}
367    }
368}