core_crypto_keystore/entities/
mod.rs

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