core_crypto_keystore/entities/
mod.rs

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