1use zeroize::Zeroize;
2
3use super::{Entity, EntityBase, EntityFindParams, EntityTransactionExt, StringEntityId};
4use crate::{CryptoKeystoreError, CryptoKeystoreResult, connection::TransactionWrapper};
5
6#[derive(
8 core_crypto_macros::Debug,
9 Clone,
10 PartialEq,
11 Eq,
12 Zeroize,
13 core_crypto_macros::Entity,
14 serde::Serialize,
15 serde::Deserialize,
16)]
17#[zeroize(drop)]
18#[entity(collection_name = "mls_groups")]
19#[sensitive]
20pub struct PersistedMlsGroup {
21 #[id(hex, column = "id_hex")]
22 pub id: Vec<u8>,
23 pub state: Vec<u8>,
24 pub parent_id: Option<Vec<u8>>,
25}
26
27#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
28#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
29pub trait PersistedMlsGroupExt: Entity {
30 fn parent_id(&self) -> Option<&[u8]>;
31
32 async fn parent_group(
33 &self,
34 conn: &mut <Self as super::EntityBase>::ConnectionType,
35 ) -> CryptoKeystoreResult<Option<Self>> {
36 let Some(parent_id) = self.parent_id() else {
37 return Ok(None);
38 };
39
40 <Self as super::Entity>::find_one(conn, &parent_id.into()).await
41 }
42
43 async fn child_groups(
44 &self,
45 conn: &mut <Self as super::EntityBase>::ConnectionType,
46 ) -> CryptoKeystoreResult<Vec<Self>> {
47 let entities = <Self as super::Entity>::find_all(conn, super::EntityFindParams::default()).await?;
48
49 let id = self.id_raw();
50
51 Ok(entities
52 .into_iter()
53 .filter(|entity| entity.parent_id().map(|parent_id| parent_id == id).unwrap_or_default())
54 .collect())
55 }
56}
57
58#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
60#[zeroize(drop)]
61pub struct PersistedMlsPendingGroup {
62 #[sensitive]
63 pub id: Vec<u8>,
64 #[sensitive]
65 pub state: Vec<u8>,
66 #[sensitive]
67 pub parent_id: Option<Vec<u8>>,
68 pub custom_configuration: Vec<u8>,
69}
70
71#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
73#[zeroize(drop)]
74pub struct MlsPendingMessage {
75 #[sensitive]
76 pub foreign_id: Vec<u8>,
77 pub message: Vec<u8>,
78}
79
80#[derive(
88 core_crypto_macros::Debug,
89 Clone,
90 PartialEq,
91 Eq,
92 Zeroize,
93 core_crypto_macros::Entity,
94 serde::Serialize,
95 serde::Deserialize,
96)]
97#[entity(collection_name = "mls_buffered_commits")]
98pub struct StoredBufferedCommit {
99 #[id(hex, column = "conversation_id_hex")]
102 #[sensitive]
103 conversation_id: Vec<u8>,
104 commit_data: Vec<u8>,
105}
106
107impl StoredBufferedCommit {
108 pub fn new(conversation_id: Vec<u8>, commit_data: Vec<u8>) -> Self {
110 Self {
111 conversation_id,
112 commit_data,
113 }
114 }
115
116 pub fn conversation_id(&self) -> &[u8] {
117 &self.conversation_id
118 }
119
120 pub fn commit_data(&self) -> &[u8] {
121 &self.commit_data
122 }
123
124 pub fn into_commit_data(self) -> Vec<u8> {
125 self.commit_data
126 }
127}
128
129#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
131#[zeroize(drop)]
132pub struct StoredCredential {
133 #[sensitive]
135 pub id: Vec<u8>,
136 #[sensitive]
137 pub credential: Vec<u8>,
138 pub created_at: u64,
139 pub signature_scheme: u16,
140 #[sensitive]
141 pub public_key: Vec<u8>,
142 #[sensitive]
143 pub secret_key: Vec<u8>,
144}
145
146#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
147#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
148pub trait MlsCredentialExt: Entity {
149 async fn delete_by_credential(tx: &TransactionWrapper<'_>, credential: Vec<u8>) -> CryptoKeystoreResult<()>;
150}
151
152#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
154#[zeroize(drop)]
155#[sensitive]
156pub struct StoredHpkePrivateKey {
157 pub sk: Vec<u8>,
158 pub pk: Vec<u8>,
159}
160
161#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
163#[zeroize(drop)]
164#[sensitive]
165pub struct StoredEncryptionKeyPair {
166 pub sk: Vec<u8>,
167 pub pk: Vec<u8>,
168}
169
170#[derive(
172 core_crypto_macros::Debug,
173 Clone,
174 PartialEq,
175 Eq,
176 Zeroize,
177 core_crypto_macros::Entity,
178 serde::Serialize,
179 serde::Deserialize,
180)]
181#[zeroize(drop)]
182#[entity(collection_name = "mls_epoch_encryption_keypairs")]
183pub struct StoredEpochEncryptionKeypair {
184 #[id(hex, column = "id_hex")]
185 pub id: Vec<u8>,
186 #[sensitive]
187 pub keypairs: Vec<u8>,
188}
189
190#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
192#[zeroize(drop)]
193#[sensitive]
194pub struct StoredPskBundle {
195 pub psk_id: Vec<u8>,
196 pub psk: Vec<u8>,
197}
198
199#[derive(
201 core_crypto_macros::Debug,
202 Clone,
203 PartialEq,
204 Eq,
205 Zeroize,
206 core_crypto_macros::Entity,
207 serde::Serialize,
208 serde::Deserialize,
209)]
210#[zeroize(drop)]
211#[entity(collection_name = "mls_keypackages")]
212pub struct StoredKeypackage {
213 #[id(hex, column = "keypackage_ref_hex")]
214 pub keypackage_ref: Vec<u8>,
215 #[sensitive]
216 pub keypackage: Vec<u8>,
217}
218
219#[derive(
222 core_crypto_macros::Debug,
223 Clone,
224 PartialEq,
225 Eq,
226 Zeroize,
227 core_crypto_macros::Entity,
228 serde::Serialize,
229 serde::Deserialize,
230)]
231#[zeroize(drop)]
232#[entity(collection_name = "e2ei_enrollment", no_upsert)]
233pub struct StoredE2eiEnrollment {
234 pub id: Vec<u8>,
235 pub content: Vec<u8>,
236}
237
238#[cfg(target_family = "wasm")]
239#[async_trait::async_trait(?Send)]
240pub trait UniqueEntity:
241 EntityBase<ConnectionType = crate::connection::KeystoreDatabaseConnection>
242 + serde::Serialize
243 + serde::de::DeserializeOwned
244where
245 Self: 'static,
246{
247 const ID: [u8; 1] = [0];
248
249 fn content(&self) -> &[u8];
250
251 fn set_content(&mut self, content: Vec<u8>);
252
253 async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Self> {
254 Ok(conn
255 .storage()
256 .get(Self::COLLECTION_NAME, &Self::ID)
257 .await?
258 .ok_or(CryptoKeystoreError::NotFound(Self::COLLECTION_NAME, "".to_string()))?)
259 }
260
261 async fn find_all(conn: &mut Self::ConnectionType, _params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>> {
262 match Self::find_unique(conn).await {
263 Ok(record) => Ok(vec![record]),
264 Err(CryptoKeystoreError::NotFound(..)) => Ok(vec![]),
265 Err(err) => Err(err),
266 }
267 }
268
269 async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
270 match Self::find_unique(conn).await {
271 Ok(record) => Ok(Some(record)),
272 Err(CryptoKeystoreError::NotFound(..)) => Ok(None),
273 Err(err) => Err(err),
274 }
275 }
276
277 async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
278 conn.storage().count(Self::COLLECTION_NAME).await
279 }
280
281 async fn replace<'a>(&'a self, transaction: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> {
282 transaction.save(self.clone()).await?;
283 Ok(())
284 }
285}
286
287#[cfg(not(target_family = "wasm"))]
288#[async_trait::async_trait]
289pub trait UniqueEntity: EntityBase<ConnectionType = crate::connection::KeystoreDatabaseConnection> {
290 const ID: usize = 0;
291
292 fn new(content: Vec<u8>) -> Self;
293
294 async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Self> {
295 let mut conn = conn.conn().await;
296 let transaction = conn.transaction()?;
297 use rusqlite::OptionalExtension as _;
298
299 let maybe_content = transaction
300 .query_row(
301 &format!("SELECT content FROM {} WHERE id = ?", Self::COLLECTION_NAME),
302 [Self::ID],
303 |r| r.get::<_, Vec<u8>>(0),
304 )
305 .optional()?;
306
307 if let Some(content) = maybe_content {
308 Ok(Self::new(content))
309 } else {
310 Err(CryptoKeystoreError::NotFound(Self::COLLECTION_NAME, "".to_string()))
311 }
312 }
313
314 async fn find_all(conn: &mut Self::ConnectionType, _params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>> {
315 match Self::find_unique(conn).await {
316 Ok(record) => Ok(vec![record]),
317 Err(CryptoKeystoreError::NotFound(..)) => Ok(vec![]),
318 Err(err) => Err(err),
319 }
320 }
321
322 async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
323 match Self::find_unique(conn).await {
324 Ok(record) => Ok(Some(record)),
325 Err(CryptoKeystoreError::NotFound(..)) => Ok(None),
326 Err(err) => Err(err),
327 }
328 }
329
330 async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
331 let conn = conn.conn().await;
332 conn.query_row(&format!("SELECT COUNT(*) FROM {}", Self::COLLECTION_NAME), [], |r| {
333 r.get(0)
334 })
335 .map_err(Into::into)
336 }
337
338 fn content(&self) -> &[u8];
339
340 async fn replace(&self, transaction: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> {
341 use crate::connection::DatabaseConnection;
342 Self::ConnectionType::check_buffer_size(self.content().len())?;
343 let zb_content = rusqlite::blob::ZeroBlob(self.content().len() as i32);
344
345 use rusqlite::ToSql;
346 let params: [rusqlite::types::ToSqlOutput; 2] = [Self::ID.to_sql()?, zb_content.to_sql()?];
347
348 transaction.execute(
349 &format!(
350 "INSERT OR REPLACE INTO {} (id, content) VALUES (?, ?)",
351 Self::COLLECTION_NAME
352 ),
353 params,
354 )?;
355 let row_id = transaction.last_insert_rowid();
356
357 let mut blob = transaction.blob_open(rusqlite::MAIN_DB, Self::COLLECTION_NAME, "content", row_id, false)?;
358 use std::io::Write;
359 blob.write_all(self.content())?;
360 blob.close()?;
361
362 Ok(())
363 }
364}
365
366#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
367#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
368impl<T: UniqueEntity + Send + Sync> EntityTransactionExt for T {
369 #[cfg(not(target_family = "wasm"))]
370 async fn save(&self, tx: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> {
371 self.replace(tx).await
372 }
373
374 #[cfg(target_family = "wasm")]
375 async fn save<'a>(&'a self, tx: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> {
376 self.replace(tx).await
377 }
378
379 #[cfg(not(target_family = "wasm"))]
380 async fn delete_fail_on_missing_id(
381 _: &TransactionWrapper<'_>,
382 _id: StringEntityId<'_>,
383 ) -> CryptoKeystoreResult<()> {
384 Err(CryptoKeystoreError::NotImplemented)
385 }
386
387 #[cfg(target_family = "wasm")]
388 async fn delete_fail_on_missing_id<'a>(
389 _: &TransactionWrapper<'a>,
390 _id: StringEntityId<'a>,
391 ) -> CryptoKeystoreResult<()> {
392 Err(CryptoKeystoreError::NotImplemented)
393 }
394}
395
396#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
398#[zeroize(drop)]
399pub struct E2eiRefreshToken {
400 pub content: Vec<u8>,
401}
402
403#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
404#[zeroize(drop)]
405pub struct E2eiAcmeCA {
406 pub content: Vec<u8>,
407}
408
409#[derive(
410 core_crypto_macros::Debug,
411 Clone,
412 PartialEq,
413 Eq,
414 Zeroize,
415 core_crypto_macros::Entity,
416 serde::Serialize,
417 serde::Deserialize,
418)]
419#[zeroize(drop)]
420pub struct E2eiIntermediateCert {
421 #[id]
424 pub ski_aki_pair: String,
425 pub content: Vec<u8>,
426}
427
428#[derive(
429 core_crypto_macros::Debug,
430 Clone,
431 PartialEq,
432 Eq,
433 Zeroize,
434 core_crypto_macros::Entity,
435 serde::Serialize,
436 serde::Deserialize,
437)]
438#[zeroize(drop)]
439pub struct E2eiCrl {
440 #[id]
441 pub distribution_point: String,
442 pub content: Vec<u8>,
443}