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 openmls::group::MlsGroup;
11
12use super::{ConversationIdRef, Error, Result, SecretKey};
13use crate::{
14 CipherSuite, ConversationConfiguration, ConversationId, CredentialRef, ExternalSender, OpenMlsError, Session,
15 mls::TntMessageCounter,
16};
17
18#[derive(derive_more::Constructor, derive_more::Deref, derive_more::DerefMut, derive_more::Debug)]
19pub(crate) struct MlsGroupState {
20 #[deref]
21 #[deref_mut]
22 group: MlsGroup,
23 #[debug(skip)]
24 tnt_message_counter: TntMessageCounter,
25}
26
27impl MlsGroupState {
28 pub(in crate::mls::conversation) fn mls_group(&self) -> &MlsGroup {
29 &self.group
30 }
31
32 pub(in crate::mls::conversation) fn mls_group_mut(&mut self) -> &mut MlsGroup {
33 &mut self.group
34 }
35
36 #[expect(unused)]
37 pub(in crate::mls::conversation) fn tnt_message_counter(&self) -> TntMessageCounter {
38 self.tnt_message_counter
39 }
40
41 pub(in crate::mls::conversation) fn obtain_tnt_message_counter(&mut self) -> Result<TntMessageCounter> {
43 self.tnt_message_counter.increment()?;
44 self.group.set_state(openmls::group::InnerState::Changed);
45 Ok(self.tnt_message_counter)
46 }
47
48 pub(in crate::mls::conversation) fn reset_tnt_message_counter(&mut self) {
49 self.tnt_message_counter = Default::default()
50 }
51}
52
53#[derive(Debug, derive_more::Constructor)]
55pub struct Conversation {
56 pub(in crate::mls::conversation) id: ConversationId,
57 pub(in crate::mls::conversation) group: RwLock<MlsGroupState>,
58 pub(in crate::mls::conversation) configuration: ConversationConfiguration,
59 session: Session,
60}
61
62impl Conversation {
63 pub fn id(&self) -> &ConversationIdRef {
65 ConversationIdRef::new(&self.id)
66 }
67
68 pub(crate) async fn group(&self) -> RwLockReadGuard<'_, MlsGroupState> {
70 self.group.read().await
71 }
72
73 pub fn configuration(&self) -> &ConversationConfiguration {
75 &self.configuration
76 }
77
78 pub async fn epoch(&self) -> u64 {
80 self.group().await.epoch().as_u64()
81 }
82
83 pub fn cipher_suite(&self) -> CipherSuite {
85 self.configuration.cipher_suite
86 }
87
88 pub async fn credential_ref(&self) -> Result<CredentialRef> {
90 let credential = self
91 .find_current_credential()
92 .await
93 .map_err(|_| Error::IdentityInitializationError)?;
94 Ok(CredentialRef::from_credential(&credential))
95 }
96
97 pub async fn export_secret_key(&self, key_length: usize) -> Result<SecretKey> {
106 const EXPORTER_LABEL: &str = "exporter";
107 const EXPORTER_CONTEXT: &[u8] = &[];
108 self.group()
109 .await
110 .export_secret(
111 &self.session.crypto_provider,
112 EXPORTER_LABEL,
113 EXPORTER_CONTEXT,
114 key_length,
115 )
116 .map(Into::into)
117 .map_err(OpenMlsError::wrap("exporting secret key"))
118 .map_err(Into::into)
119 }
120
121 pub async fn get_external_sender(&self) -> Result<ExternalSender> {
125 let group = self.group().await;
126 let ext_senders = group
127 .group_context_extensions()
128 .external_senders()
129 .ok_or(Error::MissingExternalSenderExtension)?;
130 let ext_sender = ext_senders.first().ok_or(Error::MissingExternalSenderExtension)?;
131 Ok(ext_sender.clone().into())
132 }
133}
134
135#[cfg(test)]
136mod test_utils {
137 use openmls::prelude::SignaturePublicKey;
138
139 use super::*;
140
141 impl Conversation {
142 pub async fn signature_keys(&self) -> Vec<SignaturePublicKey> {
143 let group = self.group().await;
144 group
145 .members()
146 .map(|m| m.signature_key)
147 .map(|mpk| SignaturePublicKey::from(mpk.as_slice()))
148 .collect()
149 }
150
151 pub async fn encryption_keys(&self) -> Vec<Vec<u8>> {
152 let group = self.group().await;
153 group.members().map(|m| m.encryption_key).collect()
154 }
155
156 pub async fn extensions(&self) -> openmls::prelude::Extensions {
157 let group = self.group().await;
158 group.export_group_context().extensions().to_owned()
159 }
160 }
161}