core_crypto/mls/conversation/immutable/
mod.rs1mod clients;
2mod commit_delay;
3mod credential;
4mod duplicate;
5mod e2ei;
6mod history_sharing;
7mod persistence;
8
9use async_lock::{RwLock, RwLockReadGuard};
10use core_crypto_keystore::{Transaction, entities::PersistedMlsGroup, traits::EntityDatabaseMutation as _};
11use openmls::group::{InnerState, MlsGroup};
12
13use super::{ConversationIdRef, Error, Result, SecretKey};
14use crate::{
15 CipherSuite, ConversationConfiguration, ConversationId, CredentialRef, ExternalSender, KeystoreError, OpenMlsError,
16 Session, mls::TntMessageCounter,
17};
18
19#[derive(derive_more::Constructor, derive_more::Deref, derive_more::DerefMut, derive_more::Debug)]
20pub(crate) struct MlsGroupState {
21 #[deref]
22 #[deref_mut]
23 group: MlsGroup,
24 #[debug(skip)]
25 tnt_message_counter: TntMessageCounter,
26}
27
28impl MlsGroupState {
29 pub(in crate::mls::conversation) fn mls_group(&self) -> &MlsGroup {
30 &self.group
31 }
32
33 pub(in crate::mls::conversation) fn mls_group_mut(&mut self) -> &mut MlsGroup {
34 &mut self.group
35 }
36
37 #[expect(unused)]
38 pub(in crate::mls::conversation) fn tnt_message_counter(&self) -> TntMessageCounter {
39 self.tnt_message_counter
40 }
41
42 pub(in crate::mls::conversation) fn obtain_tnt_message_counter(&mut self) -> Result<TntMessageCounter> {
44 self.tnt_message_counter.increment()?;
45 self.group.set_state(InnerState::Changed);
46 Ok(self.tnt_message_counter)
47 }
48
49 pub(in crate::mls::conversation) fn reset_tnt_message_counter(&mut self) {
50 self.tnt_message_counter = Default::default()
51 }
52
53 pub(crate) async fn persist(&mut self, tx: &Transaction) -> Result<()> {
54 self.mls_group_mut().set_state(InnerState::Persisted);
56 let id = self.group.group_id();
57
58 PersistedMlsGroup {
59 id: id.to_vec(),
60 state: core_crypto_keystore::ser(self.mls_group())
61 .map_err(KeystoreError::wrap("serializing group state"))?,
62 }
63 .save(tx)
64 .map_err(KeystoreError::wrap("persisting mls group"))?;
65
66 Ok(())
67 }
68}
69
70#[derive(Debug, derive_more::Constructor)]
72pub struct Conversation {
73 pub(in crate::mls::conversation) id: ConversationId,
74 pub(in crate::mls::conversation) group: RwLock<MlsGroupState>,
75 pub(in crate::mls::conversation) configuration: ConversationConfiguration,
76 session: Session,
77}
78
79impl Conversation {
80 pub fn id(&self) -> &ConversationIdRef {
82 ConversationIdRef::new(&self.id)
83 }
84
85 pub(crate) async fn group(&self) -> RwLockReadGuard<'_, MlsGroupState> {
87 self.group.read().await
88 }
89
90 pub fn configuration(&self) -> &ConversationConfiguration {
92 &self.configuration
93 }
94
95 pub async fn epoch(&self) -> u64 {
97 self.group().await.epoch().as_u64()
98 }
99
100 pub fn cipher_suite(&self) -> CipherSuite {
102 self.configuration.cipher_suite
103 }
104
105 pub async fn credential_ref(&self) -> Result<CredentialRef> {
107 let credential = self
108 .find_current_credential()
109 .await
110 .map_err(|_| Error::IdentityInitializationError)?;
111 Ok(CredentialRef::from_credential(&credential))
112 }
113
114 pub async fn export_secret_key(&self, key_length: usize) -> Result<SecretKey> {
123 const EXPORTER_LABEL: &str = "exporter";
124 const EXPORTER_CONTEXT: &[u8] = &[];
125 self.group()
126 .await
127 .export_secret(
128 &self.session.crypto_provider,
129 EXPORTER_LABEL,
130 EXPORTER_CONTEXT,
131 key_length,
132 )
133 .map(Into::into)
134 .map_err(OpenMlsError::wrap("exporting secret key"))
135 .map_err(Into::into)
136 }
137
138 pub async fn get_external_sender(&self) -> Result<ExternalSender> {
142 let group = self.group().await;
143 let ext_senders = group
144 .group_context_extensions()
145 .external_senders()
146 .ok_or(Error::MissingExternalSenderExtension)?;
147 let ext_sender = ext_senders.first().ok_or(Error::MissingExternalSenderExtension)?;
148 Ok(ext_sender.clone().into())
149 }
150}
151
152#[cfg(test)]
153mod test_utils {
154 use openmls::prelude::SignaturePublicKey;
155
156 use super::*;
157
158 impl Conversation {
159 pub async fn signature_keys(&self) -> Vec<SignaturePublicKey> {
160 let group = self.group().await;
161 group
162 .members()
163 .map(|m| m.signature_key)
164 .map(|mpk| SignaturePublicKey::from(mpk.as_slice()))
165 .collect()
166 }
167
168 pub async fn encryption_keys(&self) -> Vec<Vec<u8>> {
169 let group = self.group().await;
170 group.members().map(|m| m.encryption_key).collect()
171 }
172
173 pub async fn extensions(&self) -> openmls::prelude::Extensions {
174 let group = self.group().await;
175 group.export_group_context().extensions().to_owned()
176 }
177 }
178}