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