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