core_crypto_keystore/entities/
mod.rs

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