1use errors::EmptyInputError;
6use openmls_traits::{signatures::Signer, storage::StorageProvider as _};
7use proposal_store::QueuedRemoveProposal;
8
9use super::{
10 errors::{AddMembersError, LeaveGroupError, RemoveMembersError},
11 *,
12};
13use crate::{
14 binary_tree::array_representation::LeafNodeIndex,
15 group::{SwapMembersError, WelcomeCommitMessages},
16 key_packages::KeyPackage,
17 messages::group_info::GroupInfo,
18 storage::OpenMlsProvider,
19 treesync::LeafNode,
20};
21
22impl MlsGroup {
23 #[allow(clippy::type_complexity)]
41 pub fn add_members<Provider: OpenMlsProvider>(
42 &mut self,
43 provider: &Provider,
44 signer: &impl Signer,
45 key_packages: &[KeyPackage],
46 ) -> Result<
47 (MlsMessageOut, MlsMessageOut, Option<GroupInfo>),
48 AddMembersError<Provider::StorageError>,
49 > {
50 self.add_members_internal(provider, signer, key_packages, true)
51 }
52
53 pub fn swap_members<Provider: OpenMlsProvider>(
64 &mut self,
65 provider: &Provider,
66 signer: &impl Signer,
67 members: &[LeafNodeIndex],
68 key_packages: &[KeyPackage],
69 ) -> Result<WelcomeCommitMessages, SwapMembersError<Provider::StorageError>> {
70 self.is_operational()?;
71
72 if members.is_empty() {
73 return Err(EmptyInputError::RemoveMembers.into());
74 }
75
76 if key_packages.is_empty() {
77 return Err(EmptyInputError::AddMembers.into());
78 }
79
80 if members.len() != key_packages.len() {
81 return Err(SwapMembersError::InvalidInput);
82 }
83
84 let bundle = self
85 .commit_builder()
86 .propose_removals(members.iter().cloned())
87 .propose_adds(key_packages.iter().cloned())
88 .load_psks(provider.storage())?
89 .build(provider.rand(), provider.crypto(), signer, |_| true)?
90 .stage_commit(provider)?;
91
92 self.reset_aad();
93
94 Ok(bundle.try_into()?)
95 }
96
97 #[allow(clippy::type_complexity)]
118 pub fn add_members_without_update<Provider: OpenMlsProvider>(
119 &mut self,
120 provider: &Provider,
121 signer: &impl Signer,
122 key_packages: &[KeyPackage],
123 ) -> Result<
124 (MlsMessageOut, MlsMessageOut, Option<GroupInfo>),
125 AddMembersError<Provider::StorageError>,
126 > {
127 self.add_members_internal(provider, signer, key_packages, false)
128 }
129
130 #[allow(clippy::type_complexity)]
131 fn add_members_internal<Provider: OpenMlsProvider>(
132 &mut self,
133 provider: &Provider,
134 signer: &impl Signer,
135 key_packages: &[KeyPackage],
136 force_self_update: bool,
137 ) -> Result<
138 (MlsMessageOut, MlsMessageOut, Option<GroupInfo>),
139 AddMembersError<Provider::StorageError>,
140 > {
141 self.is_operational()?;
142
143 if key_packages.is_empty() {
144 return Err(AddMembersError::EmptyInput(EmptyInputError::AddMembers));
145 }
146
147 let bundle = self
148 .commit_builder()
149 .propose_adds(key_packages.iter().cloned())
150 .force_self_update(force_self_update)
151 .load_psks(provider.storage())?
152 .build(provider.rand(), provider.crypto(), signer, |_| true)?
153 .stage_commit(provider)?;
154
155 let welcome: MlsMessageOut = bundle.to_welcome_msg().ok_or(LibraryError::custom(
156 "No secrets to generate commit message.",
157 ))?;
158 let (commit, _, group_info) = bundle.into_contents();
159
160 self.reset_aad();
161
162 Ok((commit, welcome, group_info))
163 }
164
165 pub fn own_leaf(&self) -> Option<&LeafNode> {
167 self.public_group().leaf(self.own_leaf_index())
168 }
169
170 #[allow(clippy::type_complexity)]
186 pub fn remove_members<Provider: OpenMlsProvider>(
187 &mut self,
188 provider: &Provider,
189 signer: &impl Signer,
190 members: &[LeafNodeIndex],
191 ) -> Result<
192 (MlsMessageOut, Option<MlsMessageOut>, Option<GroupInfo>),
193 RemoveMembersError<Provider::StorageError>,
194 > {
195 self.is_operational()?;
196
197 if members.is_empty() {
198 return Err(RemoveMembersError::EmptyInput(
199 EmptyInputError::RemoveMembers,
200 ));
201 }
202
203 let bundle = self
204 .commit_builder()
205 .propose_removals(members.iter().cloned())
206 .load_psks(provider.storage())?
207 .build(provider.rand(), provider.crypto(), signer, |_| true)?
208 .stage_commit(provider)?;
209
210 let welcome = bundle.to_welcome_msg();
211 let (commit, _, group_info) = bundle.into_contents();
212
213 provider
214 .storage()
215 .write_group_state(self.group_id(), &self.group_state)
216 .map_err(RemoveMembersError::StorageError)?;
217
218 self.reset_aad();
219 Ok((commit, welcome, group_info))
220 }
221
222 pub fn leave_group<Provider: OpenMlsProvider>(
229 &mut self,
230 provider: &Provider,
231 signer: &impl Signer,
232 ) -> Result<MlsMessageOut, LeaveGroupError<Provider::StorageError>> {
233 self.is_operational()?;
234
235 let removed = self.own_leaf_index();
236 let aad = self.outgoing_authenticated_data()?;
237 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
238 let remove_proposal = self
239 .create_remove_proposal(framing_parameters, removed, signer)
240 .map_err(|_| LibraryError::custom("Creating a self removal should not fail"))?;
241
242 let ciphersuite = self.ciphersuite();
243 let queued_remove_proposal = QueuedProposal::from_authenticated_content_by_ref(
244 ciphersuite,
245 provider.crypto(),
246 remove_proposal.clone(),
247 )?;
248
249 provider
250 .storage()
251 .queue_proposal(
252 self.group_id(),
253 &queued_remove_proposal.proposal_reference(),
254 &queued_remove_proposal,
255 )
256 .map_err(LeaveGroupError::StorageError)?;
257
258 self.proposal_store_mut().add(queued_remove_proposal);
259
260 self.reset_aad();
261 Ok(self.content_to_mls_message(remove_proposal, provider)?)
262 }
263
264 pub fn leave_group_via_self_remove<Provider: OpenMlsProvider>(
276 &mut self,
277 provider: &Provider,
278 signer: &impl Signer,
279 ) -> Result<MlsMessageOut, LeaveGroupError<Provider::StorageError>> {
280 self.is_operational()?;
281
282 if matches!(
283 self.configuration().wire_format_policy().outgoing(),
284 OutgoingWireFormatPolicy::AlwaysCiphertext
285 ) {
286 return Err(LeaveGroupError::CannotSelfRemoveWithPureCiphertext);
287 }
288 let aad = self.outgoing_authenticated_data()?;
289 let self_remove_proposal = self.create_self_remove_proposal(&aad, signer)?;
290
291 let ciphersuite = self.ciphersuite();
292 let queued_self_remove_proposal = QueuedProposal::from_authenticated_content_by_ref(
293 ciphersuite,
294 provider.crypto(),
295 self_remove_proposal.clone(),
296 )?;
297
298 provider
299 .storage()
300 .queue_proposal(
301 self.group_id(),
302 &queued_self_remove_proposal.proposal_reference(),
303 &queued_self_remove_proposal,
304 )
305 .map_err(LeaveGroupError::StorageError)?;
306
307 self.proposal_store_mut().add(queued_self_remove_proposal);
308
309 self.reset_aad();
310 Ok(self.content_to_mls_message(self_remove_proposal, provider)?)
311 }
312
313 pub fn members(&self) -> impl Iterator<Item = Member> + '_ {
315 self.public_group().members()
316 }
317
318 pub fn member_leaf_index(&self, credential: &Credential) -> Option<LeafNodeIndex> {
321 self.members()
322 .find(|m| &m.credential == credential)
323 .map(|m| m.index)
324 }
325
326 pub fn member(&self, leaf_index: LeafNodeIndex) -> Option<&Credential> {
329 self.public_group()
330 .leaf(leaf_index)
332 .map(|leaf| leaf.credential())
333 }
334
335 pub fn member_at(&self, leaf_index: LeafNodeIndex) -> Option<Member> {
338 self.public_group()
339 .leaf(leaf_index)
341 .map(|leaf_node| {
342 Member::new(
343 leaf_index,
344 leaf_node.encryption_key().as_slice().to_vec(),
345 leaf_node.signature_key().as_slice().to_vec(),
346 leaf_node.credential().clone(),
347 )
348 })
349 }
350}
351
352#[derive(Debug)]
356pub enum RemoveOperation {
357 WeLeft,
360 WeWereRemovedBy(Sender),
362 TheyLeft(LeafNodeIndex),
366 TheyWereRemovedBy((LeafNodeIndex, Sender)),
368 WeRemovedThem(LeafNodeIndex),
370}
371
372impl RemoveOperation {
373 pub fn new(
376 queued_remove_proposal: QueuedRemoveProposal,
377 group: &MlsGroup,
378 ) -> Result<Self, LibraryError> {
379 let own_index = group.own_leaf_index();
380 let sender = queued_remove_proposal.sender();
381 let removed = queued_remove_proposal.remove_proposal().removed();
382
383 if let Sender::Member(leaf_index) = sender {
385 if *leaf_index == own_index {
387 if removed == own_index {
388 return Ok(Self::WeLeft);
390 } else {
391 return Ok(Self::WeRemovedThem(removed));
393 }
394 }
395
396 if removed == *leaf_index {
398 return Ok(Self::TheyLeft(removed));
399 }
400 }
401
402 if removed == own_index {
406 Ok(Self::WeWereRemovedBy(sender.clone()))
408 } else {
409 Ok(Self::TheyWereRemovedBy((removed, sender.clone())))
411 }
412 }
413}