core_crypto/mls/conversation/immutable/
mod.rs1mod clients;
2mod commit_delay;
3mod credential;
4mod duplicate;
5mod e2ei;
6mod history_sharing;
7mod persistence;
8
9use std::collections::{HashMap, hash_map::Entry};
10
11use async_lock::{RwLock, RwLockReadGuard};
12use core_crypto_keystore::{
13 Transaction,
14 entities::{PersistedMlsGroup, TargetedMessageTxCounter, TargetedMessageTxCounterPk},
15 traits::FetchFromDatabase,
16};
17use openmls::{
18 group::{InnerState, MlsGroup},
19 prelude::LeafNodeIndex,
20};
21
22use super::{ConversationIdRef, Error, Result, SecretKey};
23use crate::{
24 CipherSuite, ConversationConfiguration, ConversationId, CredentialRef, ExternalSender, KeystoreError, OpenMlsError,
25 Session, mls::TntMessageCounter,
26};
27
28#[derive(derive_more::Constructor, derive_more::Deref, derive_more::DerefMut, derive_more::Debug)]
29pub(crate) struct MlsGroupState {
30 #[deref]
31 #[deref_mut]
32 group: MlsGroup,
33 #[debug(skip)]
41 targeted_message_tx_counters: HashMap<LeafNodeIndex, TntMessageCounter>,
42}
43
44impl MlsGroupState {
45 pub(in crate::mls::conversation) fn mls_group(&self) -> &MlsGroup {
46 &self.group
47 }
48
49 pub(in crate::mls::conversation) fn mls_group_mut(&mut self) -> &mut MlsGroup {
50 &mut self.group
51 }
52
53 pub(in crate::mls::conversation) async fn obtain_targeted_message_tx_counter(
59 &mut self,
60 recipient: LeafNodeIndex,
61 database: &impl FetchFromDatabase,
62 ) -> Result<TntMessageCounter> {
63 let key = TargetedMessageTxCounterPk::new(self.group_id().to_vec(), recipient.u32());
64
65 let mut counter = match self.targeted_message_tx_counters.entry(recipient) {
67 Entry::Occupied(entry) => entry,
68 Entry::Vacant(entry) => {
69 let count = database
70 .get::<TargetedMessageTxCounter>(&key)
71 .await
72 .map_err(KeystoreError::wrap("searching for tnt message counters for group"))?
73 .map(|counter| counter.count)
74 .unwrap_or_default();
75
76 entry.insert_entry(count.into())
77 }
78 };
79
80 counter.get_mut().increment()?;
81 self.group.set_state(openmls::group::InnerState::Changed);
82
83 Ok(*counter.get())
84 }
85
86 pub(in crate::mls::conversation) async fn reset_targeted_message_tx_counters(&mut self, tx: &Transaction) {
87 self.targeted_message_tx_counters = Default::default();
88 let id = self.group.group_id().to_vec();
89 tx.bulk_remove::<TargetedMessageTxCounter, _>(id.into()).await
90 }
91
92 pub(crate) async fn persist(&mut self, tx: &Transaction) -> Result<()> {
93 self.mls_group_mut().set_state(InnerState::Persisted);
95 let id = self.group.group_id();
96
97 tx.save(PersistedMlsGroup {
98 id: id.to_vec(),
99 state: core_crypto_keystore::ser(self.mls_group())
100 .map_err(KeystoreError::wrap("serializing group state"))?,
101 })
102 .await
103 .map_err(KeystoreError::wrap("persisting mls group"))?;
104
105 for (receiver, counter) in self.targeted_message_tx_counters.iter() {
106 tx.save(TargetedMessageTxCounter {
107 conversation_id: id.to_vec(),
108 receiver: receiver.u32(),
109 count: (*counter).into(),
110 })
111 .await
112 .map_err(KeystoreError::wrap("saving tnt message counter"))?;
113 }
114
115 Ok(())
116 }
117}
118
119#[derive(Debug, derive_more::Constructor)]
121pub struct Conversation {
122 pub(in crate::mls::conversation) id: ConversationId,
123 pub(in crate::mls::conversation) group: RwLock<MlsGroupState>,
124 pub(in crate::mls::conversation) configuration: ConversationConfiguration,
125 session: Session,
126}
127
128impl Conversation {
129 pub fn id(&self) -> &ConversationIdRef {
131 ConversationIdRef::new(&self.id)
132 }
133
134 pub(crate) async fn group(&self) -> RwLockReadGuard<'_, MlsGroupState> {
136 self.group.read().await
137 }
138
139 pub fn configuration(&self) -> &ConversationConfiguration {
141 &self.configuration
142 }
143
144 pub async fn epoch(&self) -> u64 {
146 self.group().await.epoch().as_u64()
147 }
148
149 pub fn cipher_suite(&self) -> CipherSuite {
151 self.configuration.cipher_suite
152 }
153
154 pub async fn credential_ref(&self) -> Result<CredentialRef> {
156 let credential = self
157 .find_current_credential()
158 .await
159 .map_err(|_| Error::IdentityInitializationError)?;
160 Ok(CredentialRef::from_credential(&credential))
161 }
162
163 pub async fn export_secret_key(&self, key_length: usize) -> Result<SecretKey> {
172 const EXPORTER_LABEL: &str = "exporter";
173 const EXPORTER_CONTEXT: &[u8] = &[];
174 self.group()
175 .await
176 .export_secret(
177 &self.session.crypto_provider,
178 EXPORTER_LABEL,
179 EXPORTER_CONTEXT,
180 key_length,
181 )
182 .map(Into::into)
183 .map_err(OpenMlsError::wrap("exporting secret key"))
184 .map_err(Into::into)
185 }
186
187 pub async fn get_external_sender(&self) -> Result<ExternalSender> {
191 let group = self.group().await;
192 let ext_senders = group
193 .group_context_extensions()
194 .external_senders()
195 .ok_or(Error::MissingExternalSenderExtension)?;
196 let ext_sender = ext_senders.first().ok_or(Error::MissingExternalSenderExtension)?;
197 Ok(ext_sender.clone().into())
198 }
199}
200
201#[cfg(test)]
202mod test_utils {
203 use openmls::prelude::SignaturePublicKey;
204
205 use super::*;
206
207 impl Conversation {
208 pub async fn signature_keys(&self) -> Vec<SignaturePublicKey> {
209 let group = self.group().await;
210 group
211 .members()
212 .map(|m| m.signature_key)
213 .map(|mpk| SignaturePublicKey::from(mpk.as_slice()))
214 .collect()
215 }
216
217 pub async fn encryption_keys(&self) -> Vec<Vec<u8>> {
218 let group = self.group().await;
219 group.members().map(|m| m.encryption_key).collect()
220 }
221
222 pub async fn extensions(&self) -> openmls::prelude::Extensions {
223 let group = self.group().await;
224 group.export_group_context().extensions().to_owned()
225 }
226 }
227}