1use super::{Entity, EntityBase, EntityFindParams, EntityTransactionExt, StringEntityId};
2use crate::{CryptoKeystoreError, CryptoKeystoreResult, connection::TransactionWrapper};
3use openmls_traits::types::SignatureScheme;
4use zeroize::Zeroize;
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)]
97pub struct MlsBufferedCommit {
98 #[id(hex, column = "conversation_id_hex")]
101 #[sensitive]
102 conversation_id: Vec<u8>,
103 commit_data: Vec<u8>,
104}
105
106impl MlsBufferedCommit {
107 pub fn new(conversation_id: Vec<u8>, commit_data: Vec<u8>) -> Self {
109 Self {
110 conversation_id,
111 commit_data,
112 }
113 }
114
115 pub fn conversation_id(&self) -> &[u8] {
116 &self.conversation_id
117 }
118
119 pub fn commit_data(&self) -> &[u8] {
120 &self.commit_data
121 }
122
123 pub fn into_commit_data(self) -> Vec<u8> {
124 self.commit_data
125 }
126}
127
128#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
130#[zeroize(drop)]
131pub struct MlsCredential {
132 #[sensitive]
133 pub id: Vec<u8>,
134 #[sensitive]
135 pub credential: Vec<u8>,
136 pub created_at: u64,
137}
138
139#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
140#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
141pub trait MlsCredentialExt: Entity {
142 async fn delete_by_credential(tx: &TransactionWrapper<'_>, credential: Vec<u8>) -> CryptoKeystoreResult<()>;
143}
144
145#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
147#[zeroize(drop)]
148pub struct MlsSignatureKeyPair {
149 pub signature_scheme: u16,
150 #[sensitive]
151 pub pk: Vec<u8>,
152 #[sensitive]
153 pub keypair: Vec<u8>,
154 #[sensitive]
155 pub credential_id: Vec<u8>,
156}
157
158impl MlsSignatureKeyPair {
159 pub fn new(signature_scheme: SignatureScheme, pk: Vec<u8>, keypair: Vec<u8>, credential_id: Vec<u8>) -> Self {
160 Self {
161 signature_scheme: signature_scheme as u16,
162 pk,
163 keypair,
164 credential_id,
165 }
166 }
167}
168
169#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
171#[zeroize(drop)]
172#[sensitive]
173pub struct MlsHpkePrivateKey {
174 pub sk: Vec<u8>,
175 pub pk: Vec<u8>,
176}
177
178#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
180#[zeroize(drop)]
181#[sensitive]
182pub struct MlsEncryptionKeyPair {
183 pub sk: Vec<u8>,
184 pub pk: Vec<u8>,
185}
186
187#[derive(
189 core_crypto_macros::Debug,
190 Clone,
191 PartialEq,
192 Eq,
193 Zeroize,
194 core_crypto_macros::Entity,
195 serde::Serialize,
196 serde::Deserialize,
197)]
198#[zeroize(drop)]
199#[entity(collection_name = "mls_epoch_encryption_keypairs")]
200pub struct MlsEpochEncryptionKeyPair {
201 #[id(hex, column = "id_hex")]
202 pub id: Vec<u8>,
203 #[sensitive]
204 pub keypairs: Vec<u8>,
205}
206
207#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
209#[zeroize(drop)]
210#[sensitive]
211pub struct MlsPskBundle {
212 pub psk_id: Vec<u8>,
213 pub psk: Vec<u8>,
214}
215
216#[derive(
218 core_crypto_macros::Debug,
219 Clone,
220 PartialEq,
221 Eq,
222 Zeroize,
223 core_crypto_macros::Entity,
224 serde::Serialize,
225 serde::Deserialize,
226)]
227#[zeroize(drop)]
228#[entity(collection_name = "mls_keypackages")]
229pub struct MlsKeyPackage {
230 #[id(hex, column = "keypackage_ref_hex")]
231 pub keypackage_ref: Vec<u8>,
232 #[sensitive]
233 pub keypackage: Vec<u8>,
234}
235
236#[derive(
239 core_crypto_macros::Debug,
240 Clone,
241 PartialEq,
242 Eq,
243 Zeroize,
244 core_crypto_macros::Entity,
245 serde::Serialize,
246 serde::Deserialize,
247)]
248#[zeroize(drop)]
249#[entity(collection_name = "e2ei_enrollment", no_upsert)]
250pub struct E2eiEnrollment {
251 pub id: Vec<u8>,
252 pub content: Vec<u8>,
253}
254
255#[cfg(target_family = "wasm")]
256#[async_trait::async_trait(?Send)]
257pub trait UniqueEntity:
258 EntityBase<ConnectionType = crate::connection::KeystoreDatabaseConnection>
259 + serde::Serialize
260 + serde::de::DeserializeOwned
261where
262 Self: 'static,
263{
264 const ID: [u8; 1] = [0];
265
266 fn content(&self) -> &[u8];
267
268 fn set_content(&mut self, content: Vec<u8>);
269
270 async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Self> {
271 Ok(conn
272 .storage()
273 .get(Self::COLLECTION_NAME, &Self::ID)
274 .await?
275 .ok_or(CryptoKeystoreError::NotFound(Self::COLLECTION_NAME, "".to_string()))?)
276 }
277
278 async fn find_all(conn: &mut Self::ConnectionType, _params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>> {
279 match Self::find_unique(conn).await {
280 Ok(record) => Ok(vec![record]),
281 Err(CryptoKeystoreError::NotFound(_, _)) => Ok(vec![]),
282 Err(err) => Err(err),
283 }
284 }
285
286 async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
287 match Self::find_unique(conn).await {
288 Ok(record) => Ok(Some(record)),
289 Err(CryptoKeystoreError::NotFound(_, _)) => Ok(None),
290 Err(err) => Err(err),
291 }
292 }
293
294 async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
295 conn.storage().count(Self::COLLECTION_NAME).await
296 }
297
298 async fn replace<'a>(&'a self, transaction: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> {
299 transaction.save(self.clone()).await?;
300 Ok(())
301 }
302}
303
304#[cfg(not(target_family = "wasm"))]
305#[async_trait::async_trait]
306pub trait UniqueEntity: EntityBase<ConnectionType = crate::connection::KeystoreDatabaseConnection> {
307 const ID: usize = 0;
308
309 fn new(content: Vec<u8>) -> Self;
310
311 async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Self> {
312 let mut conn = conn.conn().await;
313 let transaction = conn.transaction()?;
314 use rusqlite::OptionalExtension as _;
315
316 let maybe_content = transaction
317 .query_row(
318 &format!("SELECT content FROM {} WHERE id = ?", Self::COLLECTION_NAME),
319 [Self::ID],
320 |r| r.get::<_, Vec<u8>>(0),
321 )
322 .optional()?;
323
324 if let Some(content) = maybe_content {
325 Ok(Self::new(content))
326 } else {
327 Err(CryptoKeystoreError::NotFound(Self::COLLECTION_NAME, "".to_string()))
328 }
329 }
330
331 async fn find_all(conn: &mut Self::ConnectionType, _params: EntityFindParams) -> CryptoKeystoreResult<Vec<Self>> {
332 match Self::find_unique(conn).await {
333 Ok(record) => Ok(vec![record]),
334 Err(CryptoKeystoreError::NotFound(_, _)) => Ok(vec![]),
335 Err(err) => Err(err),
336 }
337 }
338
339 async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
340 match Self::find_unique(conn).await {
341 Ok(record) => Ok(Some(record)),
342 Err(CryptoKeystoreError::NotFound(_, _)) => Ok(None),
343 Err(err) => Err(err),
344 }
345 }
346
347 async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
348 let conn = conn.conn().await;
349 conn.query_row(&format!("SELECT COUNT(*) FROM {}", Self::COLLECTION_NAME), [], |r| {
350 r.get(0)
351 })
352 .map_err(Into::into)
353 }
354
355 fn content(&self) -> &[u8];
356
357 async fn replace(&self, transaction: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> {
358 use crate::connection::DatabaseConnection;
359 Self::ConnectionType::check_buffer_size(self.content().len())?;
360 let zb_content = rusqlite::blob::ZeroBlob(self.content().len() as i32);
361
362 use rusqlite::ToSql;
363 let params: [rusqlite::types::ToSqlOutput; 2] = [Self::ID.to_sql()?, zb_content.to_sql()?];
364
365 transaction.execute(
366 &format!(
367 "INSERT OR REPLACE INTO {} (id, content) VALUES (?, ?)",
368 Self::COLLECTION_NAME
369 ),
370 params,
371 )?;
372 let row_id = transaction.last_insert_rowid();
373
374 let mut blob = transaction.blob_open(
375 rusqlite::DatabaseName::Main,
376 Self::COLLECTION_NAME,
377 "content",
378 row_id,
379 false,
380 )?;
381 use std::io::Write;
382 blob.write_all(self.content())?;
383 blob.close()?;
384
385 Ok(())
386 }
387}
388
389#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
390#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
391impl<T: UniqueEntity + Send + Sync> EntityTransactionExt for T {
392 #[cfg(not(target_family = "wasm"))]
393 async fn save(&self, tx: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> {
394 self.replace(tx).await
395 }
396
397 #[cfg(target_family = "wasm")]
398 async fn save<'a>(&'a self, tx: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> {
399 self.replace(tx).await
400 }
401
402 #[cfg(not(target_family = "wasm"))]
403 async fn delete_fail_on_missing_id(
404 _: &TransactionWrapper<'_>,
405 _id: StringEntityId<'_>,
406 ) -> CryptoKeystoreResult<()> {
407 Err(CryptoKeystoreError::NotImplemented)
408 }
409
410 #[cfg(target_family = "wasm")]
411 async fn delete_fail_on_missing_id<'a>(
412 _: &TransactionWrapper<'a>,
413 _id: StringEntityId<'a>,
414 ) -> CryptoKeystoreResult<()> {
415 Err(CryptoKeystoreError::NotImplemented)
416 }
417}
418
419#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
421#[zeroize(drop)]
422pub struct E2eiRefreshToken {
423 pub content: Vec<u8>,
424}
425
426#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
427#[zeroize(drop)]
428pub struct E2eiAcmeCA {
429 pub content: Vec<u8>,
430}
431
432#[derive(
433 core_crypto_macros::Debug,
434 Clone,
435 PartialEq,
436 Eq,
437 Zeroize,
438 core_crypto_macros::Entity,
439 serde::Serialize,
440 serde::Deserialize,
441)]
442#[zeroize(drop)]
443pub struct E2eiIntermediateCert {
444 #[id]
446 pub ski_aki_pair: String,
447 pub content: Vec<u8>,
448}
449
450#[derive(
451 core_crypto_macros::Debug,
452 Clone,
453 PartialEq,
454 Eq,
455 Zeroize,
456 core_crypto_macros::Entity,
457 serde::Serialize,
458 serde::Deserialize,
459)]
460#[zeroize(drop)]
461pub struct E2eiCrl {
462 #[id]
463 pub distribution_point: String,
464 pub content: Vec<u8>,
465}