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