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, key_packages::KeyPackage,
15 messages::group_info::GroupInfo, storage::OpenMlsProvider, treesync::LeafNode,
16};
17
18impl MlsGroup {
19 #[allow(clippy::type_complexity)]
37 pub fn add_members<Provider: OpenMlsProvider>(
38 &mut self,
39 provider: &Provider,
40 signer: &impl Signer,
41 key_packages: &[KeyPackage],
42 ) -> Result<
43 (MlsMessageOut, MlsMessageOut, Option<GroupInfo>),
44 AddMembersError<Provider::StorageError>,
45 > {
46 self.add_members_internal(provider, signer, key_packages, true)
47 }
48
49 #[allow(clippy::type_complexity)]
70 pub fn add_members_without_update<Provider: OpenMlsProvider>(
71 &mut self,
72 provider: &Provider,
73 signer: &impl Signer,
74 key_packages: &[KeyPackage],
75 ) -> Result<
76 (MlsMessageOut, MlsMessageOut, Option<GroupInfo>),
77 AddMembersError<Provider::StorageError>,
78 > {
79 self.add_members_internal(provider, signer, key_packages, false)
80 }
81
82 #[allow(clippy::type_complexity)]
83 fn add_members_internal<Provider: OpenMlsProvider>(
84 &mut self,
85 provider: &Provider,
86 signer: &impl Signer,
87 key_packages: &[KeyPackage],
88 force_self_update: bool,
89 ) -> Result<
90 (MlsMessageOut, MlsMessageOut, Option<GroupInfo>),
91 AddMembersError<Provider::StorageError>,
92 > {
93 self.is_operational()?;
94
95 if key_packages.is_empty() {
96 return Err(AddMembersError::EmptyInput(EmptyInputError::AddMembers));
97 }
98
99 let bundle = self
100 .commit_builder()
101 .propose_adds(key_packages.iter().cloned())
102 .force_self_update(force_self_update)
103 .load_psks(provider.storage())?
104 .build(provider.rand(), provider.crypto(), signer, |_| true)?
105 .stage_commit(provider)?;
106
107 let welcome: MlsMessageOut = bundle.to_welcome_msg().ok_or(LibraryError::custom(
108 "No secrets to generate commit message.",
109 ))?;
110 let (commit, _, group_info) = bundle.into_contents();
111
112 self.reset_aad();
113
114 Ok((commit, welcome, group_info))
115 }
116
117 pub fn own_leaf(&self) -> Option<&LeafNode> {
119 self.public_group().leaf(self.own_leaf_index())
120 }
121
122 #[allow(clippy::type_complexity)]
138 pub fn remove_members<Provider: OpenMlsProvider>(
139 &mut self,
140 provider: &Provider,
141 signer: &impl Signer,
142 members: &[LeafNodeIndex],
143 ) -> Result<
144 (MlsMessageOut, Option<MlsMessageOut>, Option<GroupInfo>),
145 RemoveMembersError<Provider::StorageError>,
146 > {
147 self.is_operational()?;
148
149 if members.is_empty() {
150 return Err(RemoveMembersError::EmptyInput(
151 EmptyInputError::RemoveMembers,
152 ));
153 }
154
155 let bundle = self
156 .commit_builder()
157 .propose_removals(members.iter().cloned())
158 .load_psks(provider.storage())?
159 .build(provider.rand(), provider.crypto(), signer, |_| true)?
160 .stage_commit(provider)?;
161
162 let welcome = bundle.to_welcome_msg();
163 let (commit, _, group_info) = bundle.into_contents();
164
165 provider
166 .storage()
167 .write_group_state(self.group_id(), &self.group_state)
168 .map_err(RemoveMembersError::StorageError)?;
169
170 self.reset_aad();
171 Ok((commit, welcome, group_info))
172 }
173
174 pub fn leave_group<Provider: OpenMlsProvider>(
181 &mut self,
182 provider: &Provider,
183 signer: &impl Signer,
184 ) -> Result<MlsMessageOut, LeaveGroupError<Provider::StorageError>> {
185 self.is_operational()?;
186
187 let removed = self.own_leaf_index();
188 let remove_proposal = self
189 .create_remove_proposal(self.framing_parameters(), removed, signer)
190 .map_err(|_| LibraryError::custom("Creating a self removal should not fail"))?;
191
192 let ciphersuite = self.ciphersuite();
193 let queued_remove_proposal = QueuedProposal::from_authenticated_content_by_ref(
194 ciphersuite,
195 provider.crypto(),
196 remove_proposal.clone(),
197 )?;
198
199 provider
200 .storage()
201 .queue_proposal(
202 self.group_id(),
203 &queued_remove_proposal.proposal_reference(),
204 &queued_remove_proposal,
205 )
206 .map_err(LeaveGroupError::StorageError)?;
207
208 self.proposal_store_mut().add(queued_remove_proposal);
209
210 self.reset_aad();
211 Ok(self.content_to_mls_message(remove_proposal, provider)?)
212 }
213
214 pub fn leave_group_via_self_remove<Provider: OpenMlsProvider>(
226 &mut self,
227 provider: &Provider,
228 signer: &impl Signer,
229 ) -> Result<MlsMessageOut, LeaveGroupError<Provider::StorageError>> {
230 self.is_operational()?;
231
232 if matches!(
233 self.configuration().wire_format_policy().outgoing(),
234 OutgoingWireFormatPolicy::AlwaysCiphertext
235 ) {
236 return Err(LeaveGroupError::CannotSelfRemoveWithPureCiphertext);
237 }
238 let self_remove_proposal =
239 self.create_self_remove_proposal(self.framing_parameters().aad(), signer)?;
240
241 let ciphersuite = self.ciphersuite();
242 let queued_self_remove_proposal = QueuedProposal::from_authenticated_content_by_ref(
243 ciphersuite,
244 provider.crypto(),
245 self_remove_proposal.clone(),
246 )?;
247
248 provider
249 .storage()
250 .queue_proposal(
251 self.group_id(),
252 &queued_self_remove_proposal.proposal_reference(),
253 &queued_self_remove_proposal,
254 )
255 .map_err(LeaveGroupError::StorageError)?;
256
257 self.proposal_store_mut().add(queued_self_remove_proposal);
258
259 self.reset_aad();
260 Ok(self.content_to_mls_message(self_remove_proposal, provider)?)
261 }
262
263 pub fn members(&self) -> impl Iterator<Item = Member> + '_ {
265 self.public_group().members()
266 }
267
268 pub fn member(&self, leaf_index: LeafNodeIndex) -> Option<&Credential> {
271 self.public_group()
272 .leaf(leaf_index)
274 .map(|leaf| leaf.credential())
275 }
276
277 pub fn member_at(&self, leaf_index: LeafNodeIndex) -> Option<Member> {
280 self.public_group()
281 .leaf(leaf_index)
283 .map(|leaf_node| {
284 Member::new(
285 leaf_index,
286 leaf_node.encryption_key().as_slice().to_vec(),
287 leaf_node.signature_key().as_slice().to_vec(),
288 leaf_node.credential().clone(),
289 )
290 })
291 }
292}
293
294#[derive(Debug)]
298pub enum RemoveOperation {
299 WeLeft,
302 WeWereRemovedBy(Sender),
304 TheyLeft(LeafNodeIndex),
308 TheyWereRemovedBy((LeafNodeIndex, Sender)),
310 WeRemovedThem(LeafNodeIndex),
312}
313
314impl RemoveOperation {
315 pub fn new(
318 queued_remove_proposal: QueuedRemoveProposal,
319 group: &MlsGroup,
320 ) -> Result<Self, LibraryError> {
321 let own_index = group.own_leaf_index();
322 let sender = queued_remove_proposal.sender();
323 let removed = queued_remove_proposal.remove_proposal().removed();
324
325 if let Sender::Member(leaf_index) = sender {
327 if *leaf_index == own_index {
329 if removed == own_index {
330 return Ok(Self::WeLeft);
332 } else {
333 return Ok(Self::WeRemovedThem(removed));
335 }
336 }
337
338 if removed == *leaf_index {
340 return Ok(Self::TheyLeft(removed));
341 }
342 }
343
344 if removed == own_index {
348 Ok(Self::WeWereRemovedBy(sender.clone()))
350 } else {
351 Ok(Self::TheyWereRemovedBy((removed, sender.clone())))
353 }
354 }
355}