core_crypto/mls/conversation/conversation_guard/
mod.rsmod commit;
pub(crate) mod decrypt;
mod encrypt;
mod merge;
use super::{ConversationWithMls, Error, MlsConversation, Result};
use crate::mls::credential::CredentialBundle;
use crate::prelude::ConversationId;
use crate::{
KeystoreError, LeafError, RecursiveError, context::CentralContext, group_store::GroupStoreValue,
prelude::MlsGroupInfoBundle,
};
use async_lock::{RwLockReadGuard, RwLockWriteGuard};
use core_crypto_keystore::CryptoKeystoreMls;
use openmls::prelude::group_info::GroupInfo;
use openmls_traits::OpenMlsCryptoProvider;
use std::sync::Arc;
#[derive(Debug)]
pub struct ConversationGuard {
inner: GroupStoreValue<MlsConversation>,
central_context: CentralContext,
}
#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
impl<'inner> ConversationWithMls<'inner> for ConversationGuard {
type Central = CentralContext;
type Conversation = RwLockReadGuard<'inner, MlsConversation>;
async fn central(&self) -> Result<CentralContext> {
Ok(self.central_context.clone())
}
async fn conversation(&'inner self) -> RwLockReadGuard<'inner, MlsConversation> {
self.inner.read().await
}
}
impl ConversationGuard {
pub(crate) fn new(inner: GroupStoreValue<MlsConversation>, central_context: CentralContext) -> Self {
Self { inner, central_context }
}
pub(crate) async fn conversation_mut(&mut self) -> RwLockWriteGuard<MlsConversation> {
self.inner.write().await
}
pub async fn wipe(&mut self) -> Result<()> {
let provider = self.mls_provider().await?;
let mut group_store = self
.central_context
.mls_groups()
.await
.map_err(RecursiveError::root("getting mls groups"))?;
let mut conversation = self.conversation_mut().await;
conversation.wipe_associated_entities(&provider).await?;
provider
.key_store()
.mls_group_delete(conversation.id())
.await
.map_err(KeystoreError::wrap("deleting mls group"))?;
let _ = group_store.remove(conversation.id());
Ok(())
}
#[expect(dead_code)]
pub(crate) async fn get_parent(&self) -> Result<Option<Self>> {
let conversation_lock = self.conversation().await;
let Some(parent_id) = conversation_lock.parent_id.as_ref() else {
return Ok(None);
};
self.central_context
.conversation(parent_id)
.await
.map(Some)
.map_err(|_| Error::ParentGroupNotFound)
}
pub async fn mark_as_child_of(&mut self, parent_id: &ConversationId) -> Result<()> {
let backend = self.mls_provider().await?;
let keystore = &backend.keystore();
let mut conversation = self.conversation_mut().await;
if keystore.mls_group_exists(parent_id).await {
conversation.parent_id = Some(parent_id.clone());
conversation.persist_group_when_changed(keystore, true).await?;
Ok(())
} else {
Err(Error::ParentGroupNotFound)
}
}
async fn credential_bundle(&self) -> Result<Arc<CredentialBundle>> {
let client = self.mls_client().await?;
let inner = self.conversation().await;
inner
.find_current_credential_bundle(&client)
.await
.map_err(|_| Error::IdentityInitializationError)
}
fn group_info(group_info: Option<GroupInfo>) -> Result<MlsGroupInfoBundle> {
let group_info = group_info.ok_or(LeafError::MissingGroupInfo)?;
MlsGroupInfoBundle::try_new_full_plaintext(group_info)
}
}