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