1use openmls_traits::{signatures::Signer, storage::StorageProvider as _};
2
3#[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
4use super::CreateGroupContextExtProposalError;
5#[cfg(feature = "virtual-clients-draft")]
6use super::HandshakeConfirmationData;
7use super::{
8 errors::{ProposalError, ProposeAddMemberError, ProposeRemoveMemberError, RemoveProposalError},
9 AddProposal, CustomProposal, FramingParameters, HandshakeFramingOutput, MlsGroup,
10 PreSharedKeyProposal, Proposal, QueuedProposal, ReInitProposal, RemoveProposal, UpdateProposal,
11 WireFormat,
12};
13#[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
14use crate::messages::group_info::GroupInfo;
15use crate::{
16 binary_tree::LeafNodeIndex,
17 ciphersuite::hash_ref::ProposalRef,
18 credentials::{Credential, NewSignerBundle},
19 error::LibraryError,
20 extensions::Extensions,
21 framing::{mls_auth_content::AuthenticatedContent, MlsMessageOut},
22 group::{errors::CreateAddProposalError, GroupContext, ValidationError},
23 key_packages::KeyPackage,
24 messages::proposals::ProposalOrRefType,
25 schedule::PreSharedKeyId,
26 storage::{OpenMlsProvider, StorageProvider},
27 treesync::{LeafNode, LeafNodeParameters},
28};
29
30#[cfg(feature = "extensions-draft")]
31use crate::{
32 component::ComponentId,
33 messages::proposals::{AppDataUpdateOperation, AppDataUpdateProposal},
34};
35
36#[derive(Debug, PartialEq, Clone)]
38pub enum Propose {
39 Add(KeyPackage),
41
42 Update(LeafNodeParameters),
47
48 Remove(u32),
50
51 RemoveCredential(Credential),
53
54 PreSharedKey(PreSharedKeyId),
56
57 GroupContextExtensions(Extensions<GroupContext>),
59
60 #[cfg(feature = "extensions-draft")]
61 UpdateAppDataComponent {
64 component_id: ComponentId,
66 update: Vec<u8>,
68 },
69 #[cfg(feature = "extensions-draft")]
70 RemoveAppDataComponent {
73 component_id: ComponentId,
75 },
76
77 Custom(CustomProposal),
79}
80
81macro_rules! impl_propose_fun {
82 ($name:ident, $impl_name:ident, $value_ty:ty, $group_fun:ident, $ref_or_value:expr, $doc:expr) => {
83 #[openmls_traits::maybe_async]
86 async fn $impl_name<Provider: OpenMlsProvider>(
87 &mut self,
88 provider: &Provider,
89 signer: &impl Signer,
90 value: $value_ty,
91 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
92 self.is_operational()?;
93
94 let aad = self.outgoing_authenticated_data()?;
95 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
96 let proposal = self.$group_fun(framing_parameters, value, signer)?;
97
98 let queued_proposal = QueuedProposal::from_authenticated_content(
99 self.ciphersuite(),
100 provider.crypto(),
101 proposal.clone(),
102 $ref_or_value,
103 )?;
104 let proposal_ref = queued_proposal.proposal_reference();
105
106 log::trace!("Storing proposal in queue {:?}", queued_proposal);
107 provider
108 .storage()
109 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
110 .await
111 .map_err(ProposalError::StorageError)?;
112 self.proposal_store_mut().add(queued_proposal);
113
114 let framing = self.content_to_mls_message(proposal, provider).await?;
115
116 self.reset_aad();
117 Ok((framing, proposal_ref))
118 }
119
120 #[doc = $doc]
121 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
128 #[openmls_traits::maybe_async]
129 pub async fn $name<Provider: OpenMlsProvider>(
130 &mut self,
131 provider: &Provider,
132 signer: &impl Signer,
133 value: $value_ty,
134 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
135 let (framing, proposal_ref) = self.$impl_name(provider, signer, value).await?;
136 Ok((framing.message, proposal_ref))
137 }
138 };
139}
140
141impl MlsGroup {
142 impl_propose_fun!(
143 propose_add_member_by_value,
144 propose_add_member_by_value_impl,
145 KeyPackage,
146 create_add_proposal,
147 ProposalOrRefType::Proposal,
148 "Creates a proposal to add a member to the group, committed by value."
149 );
150
151 impl_propose_fun!(
152 propose_remove_member_by_value,
153 propose_remove_member_by_value_impl,
154 LeafNodeIndex,
155 create_remove_proposal,
156 ProposalOrRefType::Proposal,
157 "Creates a proposal to remove a member from the group, committed by value."
158 );
159
160 impl_propose_fun!(
161 propose_pre_shared_key,
162 propose_pre_shared_key_impl,
163 PreSharedKeyId,
164 create_presharedkey_proposal,
165 ProposalOrRefType::Reference,
166 "Creates a proposal to add a pre-shared key to the key schedule, committed by reference."
167 );
168
169 impl_propose_fun!(
170 propose_pre_shared_key_by_value,
171 propose_pre_shared_key_by_value_impl,
172 PreSharedKeyId,
173 create_presharedkey_proposal,
174 ProposalOrRefType::Proposal,
175 "Creates a proposal to add a pre-shared key to the key schedule, committed by value."
176 );
177
178 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
180 #[deprecated(
181 note = "Renamed to `propose_pre_shared_key`; works for any non-resumption PSK, not just external"
182 )]
183 #[openmls_traits::maybe_async]
184 pub async fn propose_external_psk<Provider: OpenMlsProvider>(
185 &mut self,
186 provider: &Provider,
187 signer: &impl Signer,
188 value: PreSharedKeyId,
189 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
190 self.propose_pre_shared_key(provider, signer, value).await
191 }
192
193 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
195 #[deprecated(
196 note = "Renamed to `propose_pre_shared_key_by_value`; works for any non-resumption PSK, not just external"
197 )]
198 #[openmls_traits::maybe_async]
199 pub async fn propose_external_psk_by_value<Provider: OpenMlsProvider>(
200 &mut self,
201 provider: &Provider,
202 signer: &impl Signer,
203 value: PreSharedKeyId,
204 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
205 self.propose_pre_shared_key_by_value(provider, signer, value)
206 .await
207 }
208
209 impl_propose_fun!(
210 propose_custom_proposal_by_value,
211 propose_custom_proposal_by_value_impl,
212 CustomProposal,
213 create_custom_proposal,
214 ProposalOrRefType::Proposal,
215 "Creates a custom proposal, committed by value."
216 );
217
218 impl_propose_fun!(
219 propose_custom_proposal_by_reference,
220 propose_custom_proposal_by_reference_impl,
221 CustomProposal,
222 create_custom_proposal,
223 ProposalOrRefType::Reference,
224 "Creates a custom proposal, committed by reference."
225 );
226
227 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
233 #[openmls_traits::maybe_async]
234 pub async fn propose<Provider: OpenMlsProvider>(
235 &mut self,
236 provider: &Provider,
237 signer: &impl Signer,
238 propose: Propose,
239 ref_or_value: ProposalOrRefType,
240 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
241 let (framing, proposal_ref) = self
242 .propose_dispatch(provider, signer, propose, ref_or_value)
243 .await?;
244 Ok((framing.message, proposal_ref))
245 }
246
247 #[cfg(feature = "virtual-clients-draft")]
256 #[openmls_traits::maybe_async]
257 pub async fn propose_unconfirmed<Provider: OpenMlsProvider>(
258 &mut self,
259 provider: &Provider,
260 signer: &impl Signer,
261 propose: Propose,
262 ref_or_value: ProposalOrRefType,
263 ) -> Result<
264 (
265 MlsMessageOut,
266 ProposalRef,
267 Option<HandshakeConfirmationData>,
268 ),
269 ProposalError<Provider::StorageError>,
270 > {
271 let (framing, proposal_ref) = self
272 .propose_dispatch(provider, signer, propose, ref_or_value)
273 .await?;
274 Ok((framing.message, proposal_ref, framing.confirmation))
275 }
276
277 #[openmls_traits::maybe_async]
281 async fn propose_dispatch<Provider: OpenMlsProvider, S: Signer>(
282 &mut self,
283 provider: &Provider,
284 signer: &S,
285 propose: Propose,
286 ref_or_value: ProposalOrRefType,
287 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
288 match propose {
289 Propose::Add(key_package) => match ref_or_value {
290 ProposalOrRefType::Proposal => {
291 self.propose_add_member_by_value_impl(provider, signer, key_package)
292 .await
293 }
294 ProposalOrRefType::Reference => self
295 .propose_add_member_impl(provider, signer, &key_package)
296 .await
297 .map_err(|e| e.into()),
298 },
299
300 Propose::Update(leaf_node_parameters) => self
301 .propose_self_update_internal(
302 provider,
303 signer,
304 None::<NewSignerBundle<'_, S>>,
305 leaf_node_parameters,
306 )
307 .await
308 .map_err(|e| e.into()),
309
310 Propose::Remove(leaf_index) => match ref_or_value {
311 ProposalOrRefType::Proposal => {
312 self.propose_remove_member_by_value_impl(
313 provider,
314 signer,
315 LeafNodeIndex::new(leaf_index),
316 )
317 .await
318 }
319 ProposalOrRefType::Reference => self
320 .propose_remove_member_impl(provider, signer, LeafNodeIndex::new(leaf_index))
321 .await
322 .map_err(|e| e.into()),
323 },
324
325 Propose::RemoveCredential(credential) => match ref_or_value {
326 ProposalOrRefType::Proposal => {
327 self.propose_remove_member_by_credential_by_value_impl(
328 provider,
329 signer,
330 &credential,
331 )
332 .await
333 }
334 ProposalOrRefType::Reference => self
335 .propose_remove_member_by_credential_impl(provider, signer, &credential)
336 .await
337 .map_err(|e| e.into()),
338 },
339 Propose::PreSharedKey(psk_id) => {
340 match psk_id.psk() {
341 crate::schedule::Psk::External(_) => {}
342 #[cfg(feature = "extensions-draft")]
343 crate::schedule::Psk::Application(_) => {}
344 crate::schedule::Psk::Resumption(_) => {
345 return Err(ProposalError::LibraryError(LibraryError::custom(
346 "Invalid PSk argument",
347 )))
348 }
349 };
350 match ref_or_value {
351 ProposalOrRefType::Proposal => {
352 self.propose_pre_shared_key_by_value_impl(provider, signer, psk_id)
353 .await
354 }
355 ProposalOrRefType::Reference => {
356 self.propose_pre_shared_key_impl(provider, signer, psk_id)
357 .await
358 }
359 }
360 }
361 Propose::GroupContextExtensions(extensions) => {
362 self.propose_group_context_extensions_impl(
363 provider,
364 extensions,
365 signer,
366 ref_or_value,
367 )
368 .await
369 }
370 #[cfg(feature = "extensions-draft")]
372 Propose::UpdateAppDataComponent {
373 component_id,
374 update,
375 } => {
376 self.propose_app_data_update_impl(
377 provider,
378 signer,
379 component_id,
380 AppDataUpdateOperation::Update(update.into()),
381 ref_or_value,
382 )
383 .await
384 }
385 #[cfg(feature = "extensions-draft")]
386 Propose::RemoveAppDataComponent { component_id } => {
387 self.propose_app_data_update_impl(
388 provider,
389 signer,
390 component_id,
391 AppDataUpdateOperation::Remove,
392 ref_or_value,
393 )
394 .await
395 }
396
397 Propose::Custom(custom_proposal) => match ref_or_value {
399 ProposalOrRefType::Proposal => {
400 self.propose_custom_proposal_by_value_impl(provider, signer, custom_proposal)
401 .await
402 }
403 ProposalOrRefType::Reference => {
404 self.propose_custom_proposal_by_reference_impl(
405 provider,
406 signer,
407 custom_proposal,
408 )
409 .await
410 }
411 },
412 }
413 }
414
415 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
423 #[openmls_traits::maybe_async]
424 pub async fn propose_add_member<Provider: OpenMlsProvider>(
425 &mut self,
426 provider: &Provider,
427 signer: &impl Signer,
428 key_package: &KeyPackage,
429 ) -> Result<(MlsMessageOut, ProposalRef), ProposeAddMemberError<Provider::StorageError>> {
430 let (framing, proposal_ref) = self
431 .propose_add_member_impl(provider, signer, key_package)
432 .await?;
433 Ok((framing.message, proposal_ref))
434 }
435
436 #[openmls_traits::maybe_async]
437 async fn propose_add_member_impl<Provider: OpenMlsProvider>(
438 &mut self,
439 provider: &Provider,
440 signer: &impl Signer,
441 key_package: &KeyPackage,
442 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposeAddMemberError<Provider::StorageError>>
443 {
444 self.is_operational()?;
445
446 let aad = self.outgoing_authenticated_data()?;
447 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
448 let add_proposal = self
449 .create_add_proposal(framing_parameters, key_package.clone(), signer)
450 .map_err(|e| match e {
451 CreateAddProposalError::LibraryError(e) => e.into(),
452 CreateAddProposalError::LeafNodeValidation(error) => {
453 ProposeAddMemberError::LeafNodeValidation(error)
454 }
455 })?;
456
457 let proposal = QueuedProposal::from_authenticated_content_by_ref(
458 self.ciphersuite(),
459 provider.crypto(),
460 add_proposal.clone(),
461 )?;
462 let proposal_ref = proposal.proposal_reference();
463 provider
464 .storage()
465 .queue_proposal(self.group_id(), &proposal_ref, &proposal)
466 .await
467 .map_err(ProposeAddMemberError::StorageError)?;
468 self.proposal_store_mut().add(proposal);
469
470 let framing = self.content_to_mls_message(add_proposal, provider).await?;
471
472 self.reset_aad();
473 Ok((framing, proposal_ref))
474 }
475
476 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
485 #[openmls_traits::maybe_async]
486 pub async fn propose_remove_member<Provider: OpenMlsProvider>(
487 &mut self,
488 provider: &Provider,
489 signer: &impl Signer,
490 member: LeafNodeIndex,
491 ) -> Result<(MlsMessageOut, ProposalRef), ProposeRemoveMemberError<Provider::StorageError>>
492 {
493 let (framing, proposal_ref) = self
494 .propose_remove_member_impl(provider, signer, member)
495 .await?;
496 Ok((framing.message, proposal_ref))
497 }
498
499 #[openmls_traits::maybe_async]
500 async fn propose_remove_member_impl<Provider: OpenMlsProvider>(
501 &mut self,
502 provider: &Provider,
503 signer: &impl Signer,
504 member: LeafNodeIndex,
505 ) -> Result<
506 (HandshakeFramingOutput, ProposalRef),
507 ProposeRemoveMemberError<Provider::StorageError>,
508 > {
509 self.is_operational()?;
510
511 let aad = self.outgoing_authenticated_data()?;
512 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
513 let remove_proposal = self
514 .create_remove_proposal(framing_parameters, member, signer)
515 .map_err(|_| ProposeRemoveMemberError::UnknownMember)?;
516
517 let proposal = QueuedProposal::from_authenticated_content_by_ref(
518 self.ciphersuite(),
519 provider.crypto(),
520 remove_proposal.clone(),
521 )?;
522 let proposal_ref = proposal.proposal_reference();
523 provider
524 .storage()
525 .queue_proposal(self.group_id(), &proposal_ref, &proposal)
526 .await
527 .map_err(ProposeRemoveMemberError::StorageError)?;
528 self.proposal_store_mut().add(proposal);
529
530 let framing = self
531 .content_to_mls_message(remove_proposal, provider)
532 .await?;
533
534 self.reset_aad();
535 Ok((framing, proposal_ref))
536 }
537
538 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
547 #[openmls_traits::maybe_async]
548 pub async fn propose_remove_member_by_credential<Provider: OpenMlsProvider>(
549 &mut self,
550 provider: &Provider,
551 signer: &impl Signer,
552 member: &Credential,
553 ) -> Result<(MlsMessageOut, ProposalRef), ProposeRemoveMemberError<Provider::StorageError>>
554 {
555 let (framing, proposal_ref) = self
556 .propose_remove_member_by_credential_impl(provider, signer, member)
557 .await?;
558 Ok((framing.message, proposal_ref))
559 }
560
561 #[openmls_traits::maybe_async]
562 async fn propose_remove_member_by_credential_impl<Provider: OpenMlsProvider>(
563 &mut self,
564 provider: &Provider,
565 signer: &impl Signer,
566 member: &Credential,
567 ) -> Result<
568 (HandshakeFramingOutput, ProposalRef),
569 ProposeRemoveMemberError<Provider::StorageError>,
570 > {
571 let member_index = self
573 .public_group()
574 .members()
575 .find(|m| &m.credential == member)
576 .map(|m| m.index);
577
578 if let Some(member_index) = member_index {
579 self.propose_remove_member_impl(provider, signer, member_index)
580 .await
581 } else {
582 Err(ProposeRemoveMemberError::UnknownMember)
583 }
584 }
585
586 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
595 #[openmls_traits::maybe_async]
596 pub async fn propose_remove_member_by_credential_by_value<Provider: OpenMlsProvider>(
597 &mut self,
598 provider: &Provider,
599 signer: &impl Signer,
600 member: &Credential,
601 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
602 let (framing, proposal_ref) = self
603 .propose_remove_member_by_credential_by_value_impl(provider, signer, member)
604 .await?;
605 Ok((framing.message, proposal_ref))
606 }
607
608 #[openmls_traits::maybe_async]
609 async fn propose_remove_member_by_credential_by_value_impl<Provider: OpenMlsProvider>(
610 &mut self,
611 provider: &Provider,
612 signer: &impl Signer,
613 member: &Credential,
614 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
615 let member_index = self
617 .public_group()
618 .members()
619 .find(|m| &m.credential == member)
620 .map(|m| m.index);
621
622 if let Some(member_index) = member_index {
623 self.propose_remove_member_by_value_impl(provider, signer, member_index)
624 .await
625 } else {
626 Err(ProposalError::ProposeRemoveMemberError(
627 ProposeRemoveMemberError::UnknownMember,
628 ))
629 }
630 }
631
632 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
642 #[openmls_traits::maybe_async]
643 pub async fn propose_group_context_extensions<Provider: OpenMlsProvider>(
644 &mut self,
645 provider: &Provider,
646 extensions: Extensions<GroupContext>,
647 signer: &impl Signer,
648 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
649 let (framing, proposal_ref) = self
650 .propose_group_context_extensions_impl(
651 provider,
652 extensions,
653 signer,
654 ProposalOrRefType::Reference,
655 )
656 .await?;
657 Ok((framing.message, proposal_ref))
658 }
659
660 #[openmls_traits::maybe_async]
661 async fn propose_group_context_extensions_impl<Provider: OpenMlsProvider>(
662 &mut self,
663 provider: &Provider,
664 extensions: Extensions<GroupContext>,
665 signer: &impl Signer,
666 ref_or_value: ProposalOrRefType,
667 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
668 self.is_operational()?;
669
670 let aad = self.outgoing_authenticated_data()?;
671 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
672 let proposal = self.create_group_context_ext_proposal::<Provider>(
673 framing_parameters,
674 extensions,
675 signer,
676 )?;
677
678 let queued_proposal = QueuedProposal::from_authenticated_content(
679 self.ciphersuite(),
680 provider.crypto(),
681 proposal.clone(),
682 ref_or_value,
683 )?;
684
685 let proposal_ref = queued_proposal.proposal_reference();
686 provider
687 .storage()
688 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
689 .await
690 .map_err(ProposalError::StorageError)?;
691 self.proposal_store_mut().add(queued_proposal);
692
693 let framing = self.content_to_mls_message(proposal, provider).await?;
694
695 self.reset_aad();
696 Ok((framing, proposal_ref))
697 }
698
699 #[openmls_traits::maybe_async]
714 pub async fn propose_reinit<Provider: OpenMlsProvider>(
715 &mut self,
716 provider: &Provider,
717 reinit_proposal: ReInitProposal,
718 signer: &impl Signer,
719 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
720 self.is_operational()?;
721
722 let aad = self.outgoing_authenticated_data()?;
723 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
724 let proposal = self.create_reinit_proposal(framing_parameters, reinit_proposal, signer)?;
725
726 let queued_proposal = QueuedProposal::from_authenticated_content(
727 self.ciphersuite(),
728 provider.crypto(),
729 proposal.clone(),
730 ProposalOrRefType::Reference,
731 )?;
732
733 let proposal_ref = queued_proposal.proposal_reference();
734 provider
735 .storage()
736 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
737 .await
738 .map_err(ProposalError::StorageError)?;
739 self.proposal_store_mut().add(queued_proposal);
740
741 let framing = self.content_to_mls_message(proposal, provider).await?;
742
743 self.reset_aad();
744 Ok((framing.message, proposal_ref))
745 }
746
747 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
760 #[allow(clippy::type_complexity)]
761 #[openmls_traits::maybe_async]
762 pub async fn update_group_context_extensions<Provider: OpenMlsProvider>(
763 &mut self,
764 provider: &Provider,
765 extensions: Extensions<GroupContext>,
766 signer: &impl Signer,
767 ) -> Result<
768 (MlsMessageOut, Option<MlsMessageOut>, Option<GroupInfo>),
769 CreateGroupContextExtProposalError<Provider::StorageError>,
770 > {
771 self.is_operational()?;
772
773 let bundle = self
775 .commit_builder()
776 .propose_group_context_extensions(extensions)?
777 .load_psks(provider.storage())
778 .await?
779 .build(provider.rand(), provider.crypto(), signer, |_| true)?
780 .stage_commit(provider)
781 .await?;
782
783 let (commit, welcome, group_info) = bundle.into_contents();
785 let welcome = welcome.map(|welcome| MlsMessageOut::from_welcome(welcome, self.version()));
786
787 Ok((commit, welcome, group_info))
788 }
789
790 #[cfg(all(
796 feature = "extensions-draft",
797 any(not(feature = "virtual-clients-draft"), feature = "test-utils", test)
798 ))]
799 #[openmls_traits::maybe_async]
800 pub async fn propose_app_data_update<Provider: OpenMlsProvider>(
801 &mut self,
802 provider: &Provider,
803 signer: &impl Signer,
804 component_id: ComponentId,
805 operation: AppDataUpdateOperation,
806 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
807 let (framing, proposal_ref) = self
808 .propose_app_data_update_impl(
809 provider,
810 signer,
811 component_id,
812 operation,
813 ProposalOrRefType::Proposal,
814 )
815 .await?;
816 Ok((framing.message, proposal_ref))
817 }
818
819 #[cfg(feature = "extensions-draft")]
820 #[openmls_traits::maybe_async]
821 async fn propose_app_data_update_impl<Provider: OpenMlsProvider>(
822 &mut self,
823 provider: &Provider,
824 signer: &impl Signer,
825 component_id: ComponentId,
826 operation: AppDataUpdateOperation,
827 ref_or_value: ProposalOrRefType,
828 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
829 self.is_operational()?;
830
831 let aad = self.outgoing_authenticated_data()?;
832 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
833 let proposal = self.create_app_data_update_proposal(
834 framing_parameters,
835 component_id,
836 operation,
837 signer,
838 )?;
839
840 let queued_proposal = QueuedProposal::from_authenticated_content(
841 self.ciphersuite(),
842 provider.crypto(),
843 proposal.clone(),
844 ref_or_value,
845 )?;
846 let proposal_ref = queued_proposal.proposal_reference();
847
848 log::trace!("Storing proposal in queue {:?}", queued_proposal);
849 provider
850 .storage()
851 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
852 .await
853 .map_err(ProposalError::StorageError)?;
854 self.proposal_store_mut().add(queued_proposal);
855
856 let framing = self.content_to_mls_message(proposal, provider).await?;
857
858 self.reset_aad();
859 Ok((framing, proposal_ref))
860 }
861
862 #[openmls_traits::maybe_async]
864 pub async fn remove_pending_proposal<Storage: StorageProvider>(
865 &mut self,
866 storage: &Storage,
867 proposal_ref: &ProposalRef,
868 ) -> Result<(), RemoveProposalError<Storage::Error>> {
869 storage
870 .remove_proposal(self.group_id(), proposal_ref)
871 .await
872 .map_err(RemoveProposalError::Storage)?;
873 self.proposal_store_mut()
874 .remove(proposal_ref)
875 .ok_or(RemoveProposalError::ProposalNotFound)
876 }
877
878 pub(crate) fn create_add_proposal(
885 &self,
886 framing_parameters: FramingParameters,
887 joiner_key_package: KeyPackage,
888 signer: &impl Signer,
889 ) -> Result<AuthenticatedContent, CreateAddProposalError> {
890 if let Some(required_capabilities) = self.required_capabilities() {
891 joiner_key_package
892 .leaf_node()
893 .capabilities()
894 .supports_required_capabilities(required_capabilities)?;
895 }
896 let add_proposal = AddProposal {
897 key_package: joiner_key_package,
898 };
899 let proposal = Proposal::add(add_proposal);
900 AuthenticatedContent::member_proposal(
901 framing_parameters,
902 self.own_leaf_index(),
903 proposal,
904 self.context(),
905 signer,
906 )
907 .map_err(|e| e.into())
908 }
909
910 pub(crate) fn create_update_proposal(
915 &self,
916 framing_parameters: FramingParameters,
917 leaf_node: LeafNode,
920 signer: &impl Signer,
921 ) -> Result<AuthenticatedContent, LibraryError> {
922 let update_proposal = UpdateProposal { leaf_node };
923 let proposal = Proposal::update(update_proposal);
924 AuthenticatedContent::member_proposal(
925 framing_parameters,
926 self.own_leaf_index(),
927 proposal,
928 self.context(),
929 signer,
930 )
931 }
932
933 pub(crate) fn create_remove_proposal(
938 &self,
939 framing_parameters: FramingParameters,
940 removed: LeafNodeIndex,
941 signer: &impl Signer,
942 ) -> Result<AuthenticatedContent, ValidationError> {
943 if self.public_group().leaf(removed).is_none() {
944 return Err(ValidationError::UnknownMember);
945 }
946 let remove_proposal = RemoveProposal { removed };
947 let proposal = Proposal::remove(remove_proposal);
948 AuthenticatedContent::member_proposal(
949 framing_parameters,
950 self.own_leaf_index(),
951 proposal,
952 self.context(),
953 signer,
954 )
955 .map_err(ValidationError::LibraryError)
956 }
957
958 pub(crate) fn create_self_remove_proposal(
961 &self,
962 aad: &[u8],
963 signer: &impl Signer,
964 ) -> Result<AuthenticatedContent, LibraryError> {
965 let proposal = Proposal::SelfRemove;
966 let framing_parameters = FramingParameters::new(aad, WireFormat::PublicMessage);
967 AuthenticatedContent::member_proposal(
968 framing_parameters,
969 self.own_leaf_index(),
970 proposal,
971 self.context(),
972 signer,
973 )
974 }
975
976 pub(crate) fn create_presharedkey_proposal(
981 &self,
982 framing_parameters: FramingParameters,
983 psk: PreSharedKeyId,
984 signer: &impl Signer,
985 ) -> Result<AuthenticatedContent, LibraryError> {
986 let presharedkey_proposal = PreSharedKeyProposal::new(psk);
987 let proposal = Proposal::psk(presharedkey_proposal);
988 AuthenticatedContent::member_proposal(
989 framing_parameters,
990 self.own_leaf_index(),
991 proposal,
992 self.context(),
993 signer,
994 )
995 }
996
997 pub(crate) fn create_reinit_proposal(
1005 &self,
1006 framing_parameters: FramingParameters,
1007 reinit_proposal: ReInitProposal,
1008 signer: &impl Signer,
1009 ) -> Result<AuthenticatedContent, LibraryError> {
1010 let proposal = Proposal::re_init(reinit_proposal);
1011 AuthenticatedContent::member_proposal(
1012 framing_parameters,
1013 self.own_leaf_index(),
1014 proposal,
1015 self.context(),
1016 signer,
1017 )
1018 }
1019
1020 #[cfg(feature = "extensions-draft")]
1021 pub(crate) fn create_app_data_update_proposal(
1022 &self,
1023 framing_parameters: FramingParameters,
1024 component_id: ComponentId,
1025 operation: AppDataUpdateOperation,
1026 signer: &impl Signer,
1027 ) -> Result<AuthenticatedContent, LibraryError> {
1028 let proposal = Proposal::AppDataUpdate(Box::new(AppDataUpdateProposal::new(
1029 component_id,
1030 operation,
1031 )));
1032 AuthenticatedContent::member_proposal(
1033 framing_parameters,
1034 self.own_leaf_index(),
1035 proposal,
1036 self.context(),
1037 signer,
1038 )
1039 }
1040
1041 pub(crate) fn create_custom_proposal(
1042 &self,
1043 framing_parameters: FramingParameters,
1044 custom_proposal: CustomProposal,
1045 signer: &impl Signer,
1046 ) -> Result<AuthenticatedContent, LibraryError> {
1047 let proposal = Proposal::custom(custom_proposal);
1048 AuthenticatedContent::member_proposal(
1049 framing_parameters,
1050 self.own_leaf_index(),
1051 proposal,
1052 self.context(),
1053 signer,
1054 )
1055 }
1056}