core_crypto/mls/conversation/conversation_guard/
mod.rs1mod commit;
2pub(crate) mod decrypt;
3mod encrypt;
4mod merge;
5
6use super::{ConversationWithMls, Error, MlsConversation, Result};
7use crate::mls::credential::CredentialBundle;
8use crate::prelude::ConversationId;
9use crate::{
10 KeystoreError, LeafError, RecursiveError, group_store::GroupStoreValue, prelude::MlsGroupInfoBundle,
11 transaction_context::TransactionContext,
12};
13use async_lock::{RwLockReadGuard, RwLockWriteGuard};
14use core_crypto_keystore::CryptoKeystoreMls;
15use openmls::prelude::group_info::GroupInfo;
16use openmls_traits::OpenMlsCryptoProvider;
17use std::sync::Arc;
18
19#[derive(Debug)]
25pub struct ConversationGuard {
26 inner: GroupStoreValue<MlsConversation>,
27 central_context: TransactionContext,
28}
29
30#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
31#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
32impl<'inner> ConversationWithMls<'inner> for ConversationGuard {
33 type Context = TransactionContext;
34 type Conversation = RwLockReadGuard<'inner, MlsConversation>;
35
36 async fn context(&self) -> Result<TransactionContext> {
37 Ok(self.central_context.clone())
38 }
39
40 async fn conversation(&'inner self) -> RwLockReadGuard<'inner, MlsConversation> {
41 self.inner.read().await
42 }
43}
44
45impl ConversationGuard {
46 pub(crate) fn new(inner: GroupStoreValue<MlsConversation>, central_context: TransactionContext) -> Self {
47 Self { inner, central_context }
48 }
49
50 pub(crate) async fn conversation_mut(&mut self) -> RwLockWriteGuard<MlsConversation> {
51 self.inner.write().await
52 }
53
54 pub async fn wipe(&mut self) -> Result<()> {
59 let provider = self.crypto_provider().await?;
60 let mut group_store = self
61 .central_context
62 .mls_groups()
63 .await
64 .map_err(RecursiveError::transaction("getting mls groups"))?;
65 let mut conversation = self.conversation_mut().await;
66 conversation.wipe_associated_entities(&provider).await?;
67 provider
68 .key_store()
69 .mls_group_delete(conversation.id())
70 .await
71 .map_err(KeystoreError::wrap("deleting mls group"))?;
72 let _ = group_store.remove(conversation.id());
73 Ok(())
74 }
75
76 #[expect(dead_code)]
80 pub(crate) async fn get_parent(&self) -> Result<Option<Self>> {
81 let conversation_lock = self.conversation().await;
82 let Some(parent_id) = conversation_lock.parent_id.as_ref() else {
83 return Ok(None);
84 };
85 self.central_context
86 .conversation(parent_id)
87 .await
88 .map(Some)
89 .map_err(|_| Error::ParentGroupNotFound)
90 }
91
92 pub async fn mark_as_child_of(&mut self, parent_id: &ConversationId) -> Result<()> {
95 let backend = self.crypto_provider().await?;
96 let keystore = &backend.keystore();
97 let mut conversation = self.conversation_mut().await;
98 if keystore.mls_group_exists(parent_id).await {
99 conversation.parent_id = Some(parent_id.clone());
100 conversation.persist_group_when_changed(keystore, true).await?;
101 Ok(())
102 } else {
103 Err(Error::ParentGroupNotFound)
104 }
105 }
106
107 async fn credential_bundle(&self) -> Result<Arc<CredentialBundle>> {
108 let client = self.session().await?;
109 let inner = self.conversation().await;
110 inner
111 .find_current_credential_bundle(&client)
112 .await
113 .map_err(|_| Error::IdentityInitializationError)
114 }
115
116 fn group_info(group_info: Option<GroupInfo>) -> Result<MlsGroupInfoBundle> {
117 let group_info = group_info.ok_or(LeafError::MissingGroupInfo)?;
118 MlsGroupInfoBundle::try_new_full_plaintext(group_info)
119 }
120}
121
122#[cfg(test)]
123pub mod test_utils {
124 use super::ConversationGuard;
125 use crate::{mls::conversation::ConversationWithMls as _, prelude::MlsConversation};
126
127 impl ConversationGuard {
128 pub async fn drop_and_restore(&mut self) {
130 use core_crypto_keystore::CryptoKeystoreMls as _;
131 let context = self.context().await.unwrap();
132 let inner = self.conversation().await;
133 let id = inner.id();
134
135 let (parent_id, group) = context
136 .keystore()
137 .await
138 .unwrap()
139 .mls_groups_restore()
140 .await
141 .map(|mut groups| groups.remove(id.as_slice()).unwrap())
142 .unwrap();
143 let group = MlsConversation::from_serialized_state(group, parent_id).unwrap();
144 context.mls_groups().await.unwrap().insert(id.clone(), group);
145 }
146 }
147}