1use zeroize::{Zeroize, ZeroizeOnDrop};
2
3use crate::{
4 CryptoKeystoreError, CryptoKeystoreResult,
5 connection::TransactionWrapper,
6 traits::{BorrowPrimaryKey, Entity, EntityBase, KeyType, OwnedKeyType, PrimaryKey},
7};
8
9#[derive(
11 core_crypto_macros::Debug,
12 Clone,
13 PartialEq,
14 Eq,
15 Zeroize,
16 core_crypto_macros::Entity,
17 core_crypto_macros::EntityNew,
18 serde::Serialize,
19 serde::Deserialize,
20)]
21#[zeroize(drop)]
22#[entity(collection_name = "mls_groups")]
23#[sensitive]
24pub struct PersistedMlsGroup {
25 #[entity(id, hex, column = "id_hex")]
26 #[id(hex, column = "id_hex")]
27 pub id: Vec<u8>,
28 pub state: Vec<u8>,
29 pub parent_id: Option<Vec<u8>>,
30}
31
32#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
33#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
34pub trait PersistedMlsGroupExt: Entity + BorrowPrimaryKey
35where
36 for<'a> &'a <Self as BorrowPrimaryKey>::BorrowedPrimaryKey: KeyType,
37{
38 fn parent_id(&self) -> Option<&[u8]>;
39
40 async fn parent_group(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
41 let Some(parent_id) = self.parent_id() else {
42 return Ok(None);
43 };
44
45 let parent_id = OwnedKeyType::from_bytes(parent_id)
46 .ok_or(CryptoKeystoreError::InvalidPrimaryKeyBytes(Self::COLLECTION_NAME))?;
47 Self::get(conn, &parent_id).await
48 }
49
50 async fn child_groups(&self, conn: &mut <Self as EntityBase>::ConnectionType) -> CryptoKeystoreResult<Vec<Self>> {
51 let entities = Self::load_all(conn).await?;
55
56 let id = self.borrow_primary_key();
59 let id = id.bytes();
60 let id = id.as_ref();
61
62 Ok(entities
63 .into_iter()
64 .filter(|entity| entity.parent_id().map(|parent_id| parent_id == id).unwrap_or_default())
65 .collect())
66 }
67}
68
69#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
71#[zeroize(drop)]
72pub struct PersistedMlsPendingGroup {
73 #[sensitive]
74 pub id: Vec<u8>,
75 #[sensitive]
76 pub state: Vec<u8>,
77 #[sensitive]
78 pub parent_id: Option<Vec<u8>>,
79 pub custom_configuration: Vec<u8>,
80}
81
82#[derive(ZeroizeOnDrop)]
92pub struct MlsPendingMessagePrimaryKey {
93 pub(crate) foreign_id: Vec<u8>,
94 message: Vec<u8>,
95}
96
97impl MlsPendingMessagePrimaryKey {
98 pub(crate) fn from_conversation_id(conversation_id: impl AsRef<[u8]>) -> Self {
104 Self {
105 foreign_id: conversation_id.as_ref().to_owned(),
106 message: Vec::new(),
107 }
108 }
109}
110
111impl From<&MlsPendingMessage> for MlsPendingMessagePrimaryKey {
112 fn from(value: &MlsPendingMessage) -> Self {
113 Self {
114 foreign_id: value.foreign_id.clone(),
115 message: value.message.clone(),
116 }
117 }
118}
119
120impl KeyType for MlsPendingMessagePrimaryKey {
121 fn bytes(&self) -> std::borrow::Cow<'_, [u8]> {
122 let fields = [&self.foreign_id, &self.message];
124 let mut key = Vec::with_capacity(
125 ((u32::BITS / u8::BITS) as usize * fields.len()) + self.foreign_id.len() + self.message.len(),
126 );
127 for field in fields {
128 key.extend((field.len() as u32).to_le_bytes());
129 key.extend(field.as_slice());
130 }
131 key.into()
132 }
133}
134
135impl OwnedKeyType for MlsPendingMessagePrimaryKey {
136 fn from_bytes(bytes: &[u8]) -> Option<Self> {
137 let (len, bytes) = bytes.split_at_checked(4)?;
139 let len = u32::from_le_bytes(len.try_into().ok()?);
140 let (foreign_id, bytes) = bytes.split_at_checked(len as _)?;
141
142 let (len, bytes) = bytes.split_at_checked(4)?;
143 let len = u32::from_le_bytes(len.try_into().ok()?);
144 let (message, bytes) = bytes.split_at_checked(len as _)?;
145
146 bytes.is_empty().then(|| Self {
147 foreign_id: foreign_id.to_owned(),
148 message: message.to_owned(),
149 })
150 }
151}
152
153#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
155#[zeroize(drop)]
156pub struct MlsPendingMessage {
157 #[sensitive]
158 pub foreign_id: Vec<u8>,
159 pub message: Vec<u8>,
160}
161
162impl PrimaryKey for MlsPendingMessage {
163 type PrimaryKey = MlsPendingMessagePrimaryKey;
164 fn primary_key(&self) -> Self::PrimaryKey {
165 self.into()
166 }
167}
168
169#[derive(
177 core_crypto_macros::Debug,
178 Clone,
179 PartialEq,
180 Eq,
181 Zeroize,
182 core_crypto_macros::Entity,
183 core_crypto_macros::EntityNew,
184 serde::Serialize,
185 serde::Deserialize,
186)]
187#[entity(collection_name = "mls_buffered_commits")]
188pub struct StoredBufferedCommit {
189 #[entity(id, hex, column = "conversation_id_hex")]
190 #[id(hex, column = "conversation_id_hex")]
191 #[sensitive]
192 conversation_id: Vec<u8>,
193 commit_data: Vec<u8>,
194}
195
196impl StoredBufferedCommit {
197 pub fn new(conversation_id: Vec<u8>, commit_data: Vec<u8>) -> Self {
199 Self {
200 conversation_id,
201 commit_data,
202 }
203 }
204
205 pub fn conversation_id(&self) -> &[u8] {
206 &self.conversation_id
207 }
208
209 pub fn commit_data(&self) -> &[u8] {
210 &self.commit_data
211 }
212
213 pub fn into_commit_data(self) -> Vec<u8> {
214 self.commit_data
215 }
216}
217
218#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
220#[zeroize(drop)]
221pub struct StoredCredential {
222 #[sensitive]
224 pub session_id: Vec<u8>,
225 #[sensitive]
226 pub credential: Vec<u8>,
227 pub created_at: u64,
228 pub ciphersuite: u16,
229 #[sensitive]
230 pub public_key: Vec<u8>,
231 #[sensitive]
232 pub private_key: Vec<u8>,
233}
234
235#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
237#[zeroize(drop)]
238#[sensitive]
239pub struct StoredHpkePrivateKey {
240 pub sk: Vec<u8>,
241 pub pk: Vec<u8>,
242}
243
244#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
246#[zeroize(drop)]
247#[sensitive]
248pub struct StoredEncryptionKeyPair {
249 pub sk: Vec<u8>,
250 pub pk: Vec<u8>,
251}
252
253#[derive(
255 core_crypto_macros::Debug,
256 Clone,
257 PartialEq,
258 Eq,
259 Zeroize,
260 core_crypto_macros::Entity,
261 core_crypto_macros::EntityNew,
262 serde::Serialize,
263 serde::Deserialize,
264)]
265#[zeroize(drop)]
266#[entity(collection_name = "mls_epoch_encryption_keypairs")]
267pub struct StoredEpochEncryptionKeypair {
268 #[entity(hex, column = "id_hex")]
269 #[id(hex, column = "id_hex")]
270 pub id: Vec<u8>,
271 #[sensitive]
272 pub keypairs: Vec<u8>,
273}
274
275#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
277#[zeroize(drop)]
278#[sensitive]
279pub struct StoredPskBundle {
280 pub psk_id: Vec<u8>,
281 pub psk: Vec<u8>,
282}
283
284#[derive(
286 core_crypto_macros::Debug,
287 Clone,
288 PartialEq,
289 Eq,
290 Zeroize,
291 core_crypto_macros::Entity,
292 core_crypto_macros::EntityNew,
293 serde::Serialize,
294 serde::Deserialize,
295)]
296#[zeroize(drop)]
297#[entity(collection_name = "mls_keypackages")]
298pub struct StoredKeypackage {
299 #[entity(id, hex, column = "keypackage_ref_hex")]
300 #[id(hex, column = "keypackage_ref_hex")]
301 pub keypackage_ref: Vec<u8>,
302 #[sensitive]
303 pub keypackage: Vec<u8>,
304}
305
306#[derive(
309 core_crypto_macros::Debug,
310 Clone,
311 PartialEq,
312 Eq,
313 Zeroize,
314 core_crypto_macros::Entity,
315 core_crypto_macros::EntityNew,
316 serde::Serialize,
317 serde::Deserialize,
318)]
319#[zeroize(drop)]
320#[entity(collection_name = "e2ei_enrollment", no_upsert)]
321pub struct StoredE2eiEnrollment {
322 pub id: Vec<u8>,
323 pub content: Vec<u8>,
324}
325
326#[cfg(target_family = "wasm")]
327#[async_trait::async_trait(?Send)]
328pub trait UniqueEntity:
329 crate::entities::EntityBase<ConnectionType = crate::connection::KeystoreDatabaseConnection>
330 + serde::Serialize
331 + serde::de::DeserializeOwned
332where
333 Self: 'static,
334{
335 const ID: [u8; 1] = [0];
336
337 fn content(&self) -> &[u8];
338
339 fn set_content(&mut self, content: Vec<u8>);
340
341 async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Self> {
342 Ok(conn
343 .storage()
344 .get(Self::COLLECTION_NAME, &Self::ID)
345 .await?
346 .ok_or(CryptoKeystoreError::NotFound(Self::COLLECTION_NAME, "".to_string()))?)
347 }
348
349 async fn find_all(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Vec<Self>> {
350 match Self::find_unique(conn).await {
351 Ok(record) => Ok(vec![record]),
352 Err(CryptoKeystoreError::NotFound(..)) => Ok(vec![]),
353 Err(err) => Err(err),
354 }
355 }
356
357 async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
358 match Self::find_unique(conn).await {
359 Ok(record) => Ok(Some(record)),
360 Err(CryptoKeystoreError::NotFound(..)) => Ok(None),
361 Err(err) => Err(err),
362 }
363 }
364
365 async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
366 conn.storage().count(Self::COLLECTION_NAME).await
367 }
368
369 async fn replace<'a>(&'a self, transaction: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> {
370 transaction.save(self.clone()).await?;
371 Ok(())
372 }
373}
374
375#[cfg(not(target_family = "wasm"))]
376#[async_trait::async_trait]
377pub trait UniqueEntity: EntityBase<ConnectionType = crate::connection::KeystoreDatabaseConnection> {
378 const ID: usize = 0;
379
380 fn new(content: Vec<u8>) -> Self;
381
382 async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Self> {
383 let mut conn = conn.conn().await;
384 let transaction = conn.transaction()?;
385 use rusqlite::OptionalExtension as _;
386
387 let maybe_content = transaction
388 .query_row(
389 &format!("SELECT content FROM {} WHERE id = ?", Self::COLLECTION_NAME),
390 [Self::ID],
391 |r| r.get::<_, Vec<u8>>(0),
392 )
393 .optional()?;
394
395 if let Some(content) = maybe_content {
396 Ok(Self::new(content))
397 } else {
398 Err(CryptoKeystoreError::NotFound(Self::COLLECTION_NAME, "".to_string()))
399 }
400 }
401
402 async fn find_all(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Vec<Self>> {
403 match Self::find_unique(conn).await {
404 Ok(record) => Ok(vec![record]),
405 Err(CryptoKeystoreError::NotFound(..)) => Ok(vec![]),
406 Err(err) => Err(err),
407 }
408 }
409
410 async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Option<Self>> {
411 match Self::find_unique(conn).await {
412 Ok(record) => Ok(Some(record)),
413 Err(CryptoKeystoreError::NotFound(..)) => Ok(None),
414 Err(err) => Err(err),
415 }
416 }
417
418 async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<usize> {
419 let conn = conn.conn().await;
420 conn.query_row(&format!("SELECT COUNT(*) FROM {}", Self::COLLECTION_NAME), [], |r| {
421 r.get(0)
422 })
423 .map_err(Into::into)
424 }
425
426 fn content(&self) -> &[u8];
427
428 async fn replace(&self, transaction: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> {
429 use crate::connection::DatabaseConnection;
430 Self::ConnectionType::check_buffer_size(self.content().len())?;
431 let zb_content = rusqlite::blob::ZeroBlob(self.content().len() as i32);
432
433 use rusqlite::ToSql;
434 let params: [rusqlite::types::ToSqlOutput; 2] = [Self::ID.to_sql()?, zb_content.to_sql()?];
435
436 transaction.execute(
437 &format!(
438 "INSERT OR REPLACE INTO {} (id, content) VALUES (?, ?)",
439 Self::COLLECTION_NAME
440 ),
441 params,
442 )?;
443 let row_id = transaction.last_insert_rowid();
444
445 let mut blob = transaction.blob_open(rusqlite::MAIN_DB, Self::COLLECTION_NAME, "content", row_id, false)?;
446 use std::io::Write;
447 blob.write_all(self.content())?;
448 blob.close()?;
449
450 Ok(())
451 }
452}
453
454#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
455#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
456impl<T> crate::entities::EntityTransactionExt for T
457where
458 T: crate::entities::Entity<ConnectionType = crate::connection::KeystoreDatabaseConnection>
459 + UniqueEntity
460 + Send
461 + Sync,
462{
463 #[cfg(not(target_family = "wasm"))]
464 async fn save(&self, tx: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> {
465 self.replace(tx).await
466 }
467
468 #[cfg(target_family = "wasm")]
469 async fn save<'a>(&'a self, tx: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> {
470 self.replace(tx).await
471 }
472
473 #[cfg(not(target_family = "wasm"))]
474 async fn delete_fail_on_missing_id(
475 _: &TransactionWrapper<'_>,
476 _id: crate::entities::StringEntityId<'_>,
477 ) -> CryptoKeystoreResult<()> {
478 Err(CryptoKeystoreError::NotImplemented)
479 }
480
481 #[cfg(target_family = "wasm")]
482 async fn delete_fail_on_missing_id<'a>(
483 _: &TransactionWrapper<'a>,
484 _id: crate::entities::StringEntityId<'a>,
485 ) -> CryptoKeystoreResult<()> {
486 Err(CryptoKeystoreError::NotImplemented)
487 }
488}
489
490#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
492#[zeroize(drop)]
493pub struct E2eiRefreshToken {
494 pub content: Vec<u8>,
495}
496
497#[derive(core_crypto_macros::Debug, Clone, PartialEq, Eq, Zeroize, serde::Serialize, serde::Deserialize)]
498#[zeroize(drop)]
499pub struct E2eiAcmeCA {
500 pub content: Vec<u8>,
501}
502
503#[derive(
504 core_crypto_macros::Debug,
505 Clone,
506 PartialEq,
507 Eq,
508 Zeroize,
509 core_crypto_macros::Entity,
510 core_crypto_macros::EntityNew,
511 serde::Serialize,
512 serde::Deserialize,
513)]
514#[zeroize(drop)]
515pub struct E2eiIntermediateCert {
516 #[id]
519 #[entity(id)]
520 pub ski_aki_pair: String,
521 pub content: Vec<u8>,
522}
523
524#[derive(
525 core_crypto_macros::Debug,
526 Clone,
527 PartialEq,
528 Eq,
529 Zeroize,
530 core_crypto_macros::Entity,
531 core_crypto_macros::EntityNew,
532 serde::Serialize,
533 serde::Deserialize,
534)]
535#[zeroize(drop)]
536pub struct E2eiCrl {
537 #[id]
538 #[entity(id)]
539 pub distribution_point: String,
540 pub content: Vec<u8>,
541}