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, RemoveProposal, UpdateProposal, WireFormat,
11};
12#[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
13use crate::messages::group_info::GroupInfo;
14use crate::{
15 binary_tree::LeafNodeIndex,
16 ciphersuite::hash_ref::ProposalRef,
17 credentials::{Credential, NewSignerBundle},
18 error::LibraryError,
19 extensions::Extensions,
20 framing::{mls_auth_content::AuthenticatedContent, MlsMessageOut},
21 group::{errors::CreateAddProposalError, GroupContext, ValidationError},
22 key_packages::KeyPackage,
23 messages::proposals::ProposalOrRefType,
24 schedule::PreSharedKeyId,
25 storage::{OpenMlsProvider, StorageProvider},
26 treesync::{LeafNode, LeafNodeParameters},
27};
28
29#[cfg(feature = "extensions-draft")]
30use crate::{
31 component::ComponentId,
32 messages::proposals::{AppDataUpdateOperation, AppDataUpdateProposal},
33};
34
35#[derive(Debug, PartialEq, Clone)]
37pub enum Propose {
38 Add(KeyPackage),
40
41 Update(LeafNodeParameters),
46
47 Remove(u32),
49
50 RemoveCredential(Credential),
52
53 PreSharedKey(PreSharedKeyId),
55
56 GroupContextExtensions(Extensions<GroupContext>),
58
59 #[cfg(feature = "extensions-draft")]
60 UpdateAppDataComponent {
63 component_id: ComponentId,
65 update: Vec<u8>,
67 },
68 #[cfg(feature = "extensions-draft")]
69 RemoveAppDataComponent {
72 component_id: ComponentId,
74 },
75
76 Custom(CustomProposal),
78}
79
80macro_rules! impl_propose_fun {
81 ($name:ident, $impl_name:ident, $value_ty:ty, $group_fun:ident, $ref_or_value:expr, $doc:expr) => {
82 fn $impl_name<Provider: OpenMlsProvider>(
85 &mut self,
86 provider: &Provider,
87 signer: &impl Signer,
88 value: $value_ty,
89 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
90 self.is_operational()?;
91
92 let aad = self.outgoing_authenticated_data()?;
93 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
94 let proposal = self.$group_fun(framing_parameters, value, signer)?;
95
96 let queued_proposal = QueuedProposal::from_authenticated_content(
97 self.ciphersuite(),
98 provider.crypto(),
99 proposal.clone(),
100 $ref_or_value,
101 )?;
102 let proposal_ref = queued_proposal.proposal_reference();
103
104 log::trace!("Storing proposal in queue {:?}", queued_proposal);
105 provider
106 .storage()
107 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
108 .map_err(ProposalError::StorageError)?;
109 self.proposal_store_mut().add(queued_proposal);
110
111 let framing = self.content_to_mls_message(proposal, provider)?;
112
113 self.reset_aad();
114 Ok((framing, proposal_ref))
115 }
116
117 #[doc = $doc]
118 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
125 pub fn $name<Provider: OpenMlsProvider>(
126 &mut self,
127 provider: &Provider,
128 signer: &impl Signer,
129 value: $value_ty,
130 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
131 let (framing, proposal_ref) = self.$impl_name(provider, signer, value)?;
132 Ok((framing.message, proposal_ref))
133 }
134 };
135}
136
137impl MlsGroup {
138 impl_propose_fun!(
139 propose_add_member_by_value,
140 propose_add_member_by_value_impl,
141 KeyPackage,
142 create_add_proposal,
143 ProposalOrRefType::Proposal,
144 "Creates a proposal to add a member to the group, committed by value."
145 );
146
147 impl_propose_fun!(
148 propose_remove_member_by_value,
149 propose_remove_member_by_value_impl,
150 LeafNodeIndex,
151 create_remove_proposal,
152 ProposalOrRefType::Proposal,
153 "Creates a proposal to remove a member from the group, committed by value."
154 );
155
156 impl_propose_fun!(
157 propose_pre_shared_key,
158 propose_pre_shared_key_impl,
159 PreSharedKeyId,
160 create_presharedkey_proposal,
161 ProposalOrRefType::Reference,
162 "Creates a proposal to add a pre-shared key to the key schedule, committed by reference."
163 );
164
165 impl_propose_fun!(
166 propose_pre_shared_key_by_value,
167 propose_pre_shared_key_by_value_impl,
168 PreSharedKeyId,
169 create_presharedkey_proposal,
170 ProposalOrRefType::Proposal,
171 "Creates a proposal to add a pre-shared key to the key schedule, committed by value."
172 );
173
174 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
176 #[deprecated(
177 note = "Renamed to `propose_pre_shared_key`; works for any non-resumption PSK, not just external"
178 )]
179 pub fn propose_external_psk<Provider: OpenMlsProvider>(
180 &mut self,
181 provider: &Provider,
182 signer: &impl Signer,
183 value: PreSharedKeyId,
184 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
185 self.propose_pre_shared_key(provider, signer, value)
186 }
187
188 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
190 #[deprecated(
191 note = "Renamed to `propose_pre_shared_key_by_value`; works for any non-resumption PSK, not just external"
192 )]
193 pub fn propose_external_psk_by_value<Provider: OpenMlsProvider>(
194 &mut self,
195 provider: &Provider,
196 signer: &impl Signer,
197 value: PreSharedKeyId,
198 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
199 self.propose_pre_shared_key_by_value(provider, signer, value)
200 }
201
202 impl_propose_fun!(
203 propose_custom_proposal_by_value,
204 propose_custom_proposal_by_value_impl,
205 CustomProposal,
206 create_custom_proposal,
207 ProposalOrRefType::Proposal,
208 "Creates a custom proposal, committed by value."
209 );
210
211 impl_propose_fun!(
212 propose_custom_proposal_by_reference,
213 propose_custom_proposal_by_reference_impl,
214 CustomProposal,
215 create_custom_proposal,
216 ProposalOrRefType::Reference,
217 "Creates a custom proposal, committed by reference."
218 );
219
220 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
226 pub fn propose<Provider: OpenMlsProvider>(
227 &mut self,
228 provider: &Provider,
229 signer: &impl Signer,
230 propose: Propose,
231 ref_or_value: ProposalOrRefType,
232 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
233 let (framing, proposal_ref) =
234 self.propose_dispatch(provider, signer, propose, ref_or_value)?;
235 Ok((framing.message, proposal_ref))
236 }
237
238 #[cfg(feature = "virtual-clients-draft")]
247 pub fn propose_unconfirmed<Provider: OpenMlsProvider>(
248 &mut self,
249 provider: &Provider,
250 signer: &impl Signer,
251 propose: Propose,
252 ref_or_value: ProposalOrRefType,
253 ) -> Result<
254 (
255 MlsMessageOut,
256 ProposalRef,
257 Option<HandshakeConfirmationData>,
258 ),
259 ProposalError<Provider::StorageError>,
260 > {
261 let (framing, proposal_ref) =
262 self.propose_dispatch(provider, signer, propose, ref_or_value)?;
263 Ok((framing.message, proposal_ref, framing.confirmation))
264 }
265
266 fn propose_dispatch<Provider: OpenMlsProvider, S: Signer>(
270 &mut self,
271 provider: &Provider,
272 signer: &S,
273 propose: Propose,
274 ref_or_value: ProposalOrRefType,
275 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
276 match propose {
277 Propose::Add(key_package) => match ref_or_value {
278 ProposalOrRefType::Proposal => {
279 self.propose_add_member_by_value_impl(provider, signer, key_package)
280 }
281 ProposalOrRefType::Reference => self
282 .propose_add_member_impl(provider, signer, &key_package)
283 .map_err(|e| e.into()),
284 },
285
286 Propose::Update(leaf_node_parameters) => self
287 .propose_self_update_internal(
288 provider,
289 signer,
290 None::<NewSignerBundle<'_, S>>,
291 leaf_node_parameters,
292 )
293 .map_err(|e| e.into()),
294
295 Propose::Remove(leaf_index) => match ref_or_value {
296 ProposalOrRefType::Proposal => self.propose_remove_member_by_value_impl(
297 provider,
298 signer,
299 LeafNodeIndex::new(leaf_index),
300 ),
301 ProposalOrRefType::Reference => self
302 .propose_remove_member_impl(provider, signer, LeafNodeIndex::new(leaf_index))
303 .map_err(|e| e.into()),
304 },
305
306 Propose::RemoveCredential(credential) => match ref_or_value {
307 ProposalOrRefType::Proposal => self
308 .propose_remove_member_by_credential_by_value_impl(
309 provider,
310 signer,
311 &credential,
312 ),
313 ProposalOrRefType::Reference => self
314 .propose_remove_member_by_credential_impl(provider, signer, &credential)
315 .map_err(|e| e.into()),
316 },
317 Propose::PreSharedKey(psk_id) => {
318 match psk_id.psk() {
319 crate::schedule::Psk::External(_) => {}
320 #[cfg(feature = "extensions-draft")]
321 crate::schedule::Psk::Application(_) => {}
322 crate::schedule::Psk::Resumption(_) => {
323 return Err(ProposalError::LibraryError(LibraryError::custom(
324 "Invalid PSk argument",
325 )))
326 }
327 };
328 match ref_or_value {
329 ProposalOrRefType::Proposal => {
330 self.propose_pre_shared_key_by_value_impl(provider, signer, psk_id)
331 }
332 ProposalOrRefType::Reference => {
333 self.propose_pre_shared_key_impl(provider, signer, psk_id)
334 }
335 }
336 }
337 Propose::GroupContextExtensions(extensions) => self
338 .propose_group_context_extensions_impl(provider, extensions, signer, ref_or_value),
339 #[cfg(feature = "extensions-draft")]
341 Propose::UpdateAppDataComponent {
342 component_id,
343 update,
344 } => self.propose_app_data_update_impl(
345 provider,
346 signer,
347 component_id,
348 AppDataUpdateOperation::Update(update.into()),
349 ref_or_value,
350 ),
351 #[cfg(feature = "extensions-draft")]
352 Propose::RemoveAppDataComponent { component_id } => self.propose_app_data_update_impl(
353 provider,
354 signer,
355 component_id,
356 AppDataUpdateOperation::Remove,
357 ref_or_value,
358 ),
359
360 Propose::Custom(custom_proposal) => match ref_or_value {
362 ProposalOrRefType::Proposal => {
363 self.propose_custom_proposal_by_value_impl(provider, signer, custom_proposal)
364 }
365 ProposalOrRefType::Reference => self.propose_custom_proposal_by_reference_impl(
366 provider,
367 signer,
368 custom_proposal,
369 ),
370 },
371 }
372 }
373
374 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
382 pub fn propose_add_member<Provider: OpenMlsProvider>(
383 &mut self,
384 provider: &Provider,
385 signer: &impl Signer,
386 key_package: &KeyPackage,
387 ) -> Result<(MlsMessageOut, ProposalRef), ProposeAddMemberError<Provider::StorageError>> {
388 let (framing, proposal_ref) =
389 self.propose_add_member_impl(provider, signer, key_package)?;
390 Ok((framing.message, proposal_ref))
391 }
392
393 fn propose_add_member_impl<Provider: OpenMlsProvider>(
394 &mut self,
395 provider: &Provider,
396 signer: &impl Signer,
397 key_package: &KeyPackage,
398 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposeAddMemberError<Provider::StorageError>>
399 {
400 self.is_operational()?;
401
402 let aad = self.outgoing_authenticated_data()?;
403 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
404 let add_proposal = self
405 .create_add_proposal(framing_parameters, key_package.clone(), signer)
406 .map_err(|e| match e {
407 CreateAddProposalError::LibraryError(e) => e.into(),
408 CreateAddProposalError::LeafNodeValidation(error) => {
409 ProposeAddMemberError::LeafNodeValidation(error)
410 }
411 })?;
412
413 let proposal = QueuedProposal::from_authenticated_content_by_ref(
414 self.ciphersuite(),
415 provider.crypto(),
416 add_proposal.clone(),
417 )?;
418 let proposal_ref = proposal.proposal_reference();
419 provider
420 .storage()
421 .queue_proposal(self.group_id(), &proposal_ref, &proposal)
422 .map_err(ProposeAddMemberError::StorageError)?;
423 self.proposal_store_mut().add(proposal);
424
425 let framing = self.content_to_mls_message(add_proposal, provider)?;
426
427 self.reset_aad();
428 Ok((framing, proposal_ref))
429 }
430
431 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
440 pub fn propose_remove_member<Provider: OpenMlsProvider>(
441 &mut self,
442 provider: &Provider,
443 signer: &impl Signer,
444 member: LeafNodeIndex,
445 ) -> Result<(MlsMessageOut, ProposalRef), ProposeRemoveMemberError<Provider::StorageError>>
446 {
447 let (framing, proposal_ref) = self.propose_remove_member_impl(provider, signer, member)?;
448 Ok((framing.message, proposal_ref))
449 }
450
451 fn propose_remove_member_impl<Provider: OpenMlsProvider>(
452 &mut self,
453 provider: &Provider,
454 signer: &impl Signer,
455 member: LeafNodeIndex,
456 ) -> Result<
457 (HandshakeFramingOutput, ProposalRef),
458 ProposeRemoveMemberError<Provider::StorageError>,
459 > {
460 self.is_operational()?;
461
462 let aad = self.outgoing_authenticated_data()?;
463 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
464 let remove_proposal = self
465 .create_remove_proposal(framing_parameters, member, signer)
466 .map_err(|_| ProposeRemoveMemberError::UnknownMember)?;
467
468 let proposal = QueuedProposal::from_authenticated_content_by_ref(
469 self.ciphersuite(),
470 provider.crypto(),
471 remove_proposal.clone(),
472 )?;
473 let proposal_ref = proposal.proposal_reference();
474 provider
475 .storage()
476 .queue_proposal(self.group_id(), &proposal_ref, &proposal)
477 .map_err(ProposeRemoveMemberError::StorageError)?;
478 self.proposal_store_mut().add(proposal);
479
480 let framing = self.content_to_mls_message(remove_proposal, provider)?;
481
482 self.reset_aad();
483 Ok((framing, proposal_ref))
484 }
485
486 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
495 pub fn propose_remove_member_by_credential<Provider: OpenMlsProvider>(
496 &mut self,
497 provider: &Provider,
498 signer: &impl Signer,
499 member: &Credential,
500 ) -> Result<(MlsMessageOut, ProposalRef), ProposeRemoveMemberError<Provider::StorageError>>
501 {
502 let (framing, proposal_ref) =
503 self.propose_remove_member_by_credential_impl(provider, signer, member)?;
504 Ok((framing.message, proposal_ref))
505 }
506
507 fn propose_remove_member_by_credential_impl<Provider: OpenMlsProvider>(
508 &mut self,
509 provider: &Provider,
510 signer: &impl Signer,
511 member: &Credential,
512 ) -> Result<
513 (HandshakeFramingOutput, ProposalRef),
514 ProposeRemoveMemberError<Provider::StorageError>,
515 > {
516 let member_index = self
518 .public_group()
519 .members()
520 .find(|m| &m.credential == member)
521 .map(|m| m.index);
522
523 if let Some(member_index) = member_index {
524 self.propose_remove_member_impl(provider, signer, member_index)
525 } else {
526 Err(ProposeRemoveMemberError::UnknownMember)
527 }
528 }
529
530 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
539 pub fn propose_remove_member_by_credential_by_value<Provider: OpenMlsProvider>(
540 &mut self,
541 provider: &Provider,
542 signer: &impl Signer,
543 member: &Credential,
544 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
545 let (framing, proposal_ref) =
546 self.propose_remove_member_by_credential_by_value_impl(provider, signer, member)?;
547 Ok((framing.message, proposal_ref))
548 }
549
550 fn propose_remove_member_by_credential_by_value_impl<Provider: OpenMlsProvider>(
551 &mut self,
552 provider: &Provider,
553 signer: &impl Signer,
554 member: &Credential,
555 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
556 let member_index = self
558 .public_group()
559 .members()
560 .find(|m| &m.credential == member)
561 .map(|m| m.index);
562
563 if let Some(member_index) = member_index {
564 self.propose_remove_member_by_value_impl(provider, signer, member_index)
565 } else {
566 Err(ProposalError::ProposeRemoveMemberError(
567 ProposeRemoveMemberError::UnknownMember,
568 ))
569 }
570 }
571
572 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
582 pub fn propose_group_context_extensions<Provider: OpenMlsProvider>(
583 &mut self,
584 provider: &Provider,
585 extensions: Extensions<GroupContext>,
586 signer: &impl Signer,
587 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
588 let (framing, proposal_ref) = self.propose_group_context_extensions_impl(
589 provider,
590 extensions,
591 signer,
592 ProposalOrRefType::Reference,
593 )?;
594 Ok((framing.message, proposal_ref))
595 }
596
597 fn propose_group_context_extensions_impl<Provider: OpenMlsProvider>(
598 &mut self,
599 provider: &Provider,
600 extensions: Extensions<GroupContext>,
601 signer: &impl Signer,
602 ref_or_value: ProposalOrRefType,
603 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
604 self.is_operational()?;
605
606 let aad = self.outgoing_authenticated_data()?;
607 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
608 let proposal = self.create_group_context_ext_proposal::<Provider>(
609 framing_parameters,
610 extensions,
611 signer,
612 )?;
613
614 let queued_proposal = QueuedProposal::from_authenticated_content(
615 self.ciphersuite(),
616 provider.crypto(),
617 proposal.clone(),
618 ref_or_value,
619 )?;
620
621 let proposal_ref = queued_proposal.proposal_reference();
622 provider
623 .storage()
624 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
625 .map_err(ProposalError::StorageError)?;
626 self.proposal_store_mut().add(queued_proposal);
627
628 let framing = self.content_to_mls_message(proposal, provider)?;
629
630 self.reset_aad();
631 Ok((framing, proposal_ref))
632 }
633
634 #[cfg(any(not(feature = "virtual-clients-draft"), feature = "test-utils", test))]
647 #[allow(clippy::type_complexity)]
648 pub fn update_group_context_extensions<Provider: OpenMlsProvider>(
649 &mut self,
650 provider: &Provider,
651 extensions: Extensions<GroupContext>,
652 signer: &impl Signer,
653 ) -> Result<
654 (MlsMessageOut, Option<MlsMessageOut>, Option<GroupInfo>),
655 CreateGroupContextExtProposalError<Provider::StorageError>,
656 > {
657 self.is_operational()?;
658
659 let bundle = self
661 .commit_builder()
662 .propose_group_context_extensions(extensions)?
663 .load_psks(provider.storage())?
664 .build(provider.rand(), provider.crypto(), signer, |_| true)?
665 .stage_commit(provider)?;
666
667 let (commit, welcome, group_info) = bundle.into_contents();
669 let welcome = welcome.map(|welcome| MlsMessageOut::from_welcome(welcome, self.version()));
670
671 Ok((commit, welcome, group_info))
672 }
673
674 #[cfg(all(
680 feature = "extensions-draft",
681 any(not(feature = "virtual-clients-draft"), feature = "test-utils", test)
682 ))]
683 pub fn propose_app_data_update<Provider: OpenMlsProvider>(
684 &mut self,
685 provider: &Provider,
686 signer: &impl Signer,
687 component_id: ComponentId,
688 operation: AppDataUpdateOperation,
689 ) -> Result<(MlsMessageOut, ProposalRef), ProposalError<Provider::StorageError>> {
690 let (framing, proposal_ref) = self.propose_app_data_update_impl(
691 provider,
692 signer,
693 component_id,
694 operation,
695 ProposalOrRefType::Proposal,
696 )?;
697 Ok((framing.message, proposal_ref))
698 }
699
700 #[cfg(feature = "extensions-draft")]
701 fn propose_app_data_update_impl<Provider: OpenMlsProvider>(
702 &mut self,
703 provider: &Provider,
704 signer: &impl Signer,
705 component_id: ComponentId,
706 operation: AppDataUpdateOperation,
707 ref_or_value: ProposalOrRefType,
708 ) -> Result<(HandshakeFramingOutput, ProposalRef), ProposalError<Provider::StorageError>> {
709 self.is_operational()?;
710
711 let aad = self.outgoing_authenticated_data()?;
712 let framing_parameters = FramingParameters::new(&aad, self.outgoing_wire_format());
713 let proposal = self.create_app_data_update_proposal(
714 framing_parameters,
715 component_id,
716 operation,
717 signer,
718 )?;
719
720 let queued_proposal = QueuedProposal::from_authenticated_content(
721 self.ciphersuite(),
722 provider.crypto(),
723 proposal.clone(),
724 ref_or_value,
725 )?;
726 let proposal_ref = queued_proposal.proposal_reference();
727
728 log::trace!("Storing proposal in queue {:?}", queued_proposal);
729 provider
730 .storage()
731 .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal)
732 .map_err(ProposalError::StorageError)?;
733 self.proposal_store_mut().add(queued_proposal);
734
735 let framing = self.content_to_mls_message(proposal, provider)?;
736
737 self.reset_aad();
738 Ok((framing, proposal_ref))
739 }
740
741 pub fn remove_pending_proposal<Storage: StorageProvider>(
743 &mut self,
744 storage: &Storage,
745 proposal_ref: &ProposalRef,
746 ) -> Result<(), RemoveProposalError<Storage::Error>> {
747 storage
748 .remove_proposal(self.group_id(), proposal_ref)
749 .map_err(RemoveProposalError::Storage)?;
750 self.proposal_store_mut()
751 .remove(proposal_ref)
752 .ok_or(RemoveProposalError::ProposalNotFound)
753 }
754
755 pub(crate) fn create_add_proposal(
762 &self,
763 framing_parameters: FramingParameters,
764 joiner_key_package: KeyPackage,
765 signer: &impl Signer,
766 ) -> Result<AuthenticatedContent, CreateAddProposalError> {
767 if let Some(required_capabilities) = self.required_capabilities() {
768 joiner_key_package
769 .leaf_node()
770 .capabilities()
771 .supports_required_capabilities(required_capabilities)?;
772 }
773 let add_proposal = AddProposal {
774 key_package: joiner_key_package,
775 };
776 let proposal = Proposal::add(add_proposal);
777 AuthenticatedContent::member_proposal(
778 framing_parameters,
779 self.own_leaf_index(),
780 proposal,
781 self.context(),
782 signer,
783 )
784 .map_err(|e| e.into())
785 }
786
787 pub(crate) fn create_update_proposal(
792 &self,
793 framing_parameters: FramingParameters,
794 leaf_node: LeafNode,
797 signer: &impl Signer,
798 ) -> Result<AuthenticatedContent, LibraryError> {
799 let update_proposal = UpdateProposal { leaf_node };
800 let proposal = Proposal::update(update_proposal);
801 AuthenticatedContent::member_proposal(
802 framing_parameters,
803 self.own_leaf_index(),
804 proposal,
805 self.context(),
806 signer,
807 )
808 }
809
810 pub(crate) fn create_remove_proposal(
815 &self,
816 framing_parameters: FramingParameters,
817 removed: LeafNodeIndex,
818 signer: &impl Signer,
819 ) -> Result<AuthenticatedContent, ValidationError> {
820 if self.public_group().leaf(removed).is_none() {
821 return Err(ValidationError::UnknownMember);
822 }
823 let remove_proposal = RemoveProposal { removed };
824 let proposal = Proposal::remove(remove_proposal);
825 AuthenticatedContent::member_proposal(
826 framing_parameters,
827 self.own_leaf_index(),
828 proposal,
829 self.context(),
830 signer,
831 )
832 .map_err(ValidationError::LibraryError)
833 }
834
835 pub(crate) fn create_self_remove_proposal(
838 &self,
839 aad: &[u8],
840 signer: &impl Signer,
841 ) -> Result<AuthenticatedContent, LibraryError> {
842 let proposal = Proposal::SelfRemove;
843 let framing_parameters = FramingParameters::new(aad, WireFormat::PublicMessage);
844 AuthenticatedContent::member_proposal(
845 framing_parameters,
846 self.own_leaf_index(),
847 proposal,
848 self.context(),
849 signer,
850 )
851 }
852
853 pub(crate) fn create_presharedkey_proposal(
859 &self,
860 framing_parameters: FramingParameters,
861 psk: PreSharedKeyId,
862 signer: &impl Signer,
863 ) -> Result<AuthenticatedContent, LibraryError> {
864 let presharedkey_proposal = PreSharedKeyProposal::new(psk);
865 let proposal = Proposal::psk(presharedkey_proposal);
866 AuthenticatedContent::member_proposal(
867 framing_parameters,
868 self.own_leaf_index(),
869 proposal,
870 self.context(),
871 signer,
872 )
873 }
874
875 #[cfg(feature = "extensions-draft")]
876 pub(crate) fn create_app_data_update_proposal(
877 &self,
878 framing_parameters: FramingParameters,
879 component_id: ComponentId,
880 operation: AppDataUpdateOperation,
881 signer: &impl Signer,
882 ) -> Result<AuthenticatedContent, LibraryError> {
883 let proposal = Proposal::AppDataUpdate(Box::new(AppDataUpdateProposal::new(
884 component_id,
885 operation,
886 )));
887 AuthenticatedContent::member_proposal(
888 framing_parameters,
889 self.own_leaf_index(),
890 proposal,
891 self.context(),
892 signer,
893 )
894 }
895
896 pub(crate) fn create_custom_proposal(
897 &self,
898 framing_parameters: FramingParameters,
899 custom_proposal: CustomProposal,
900 signer: &impl Signer,
901 ) -> Result<AuthenticatedContent, LibraryError> {
902 let proposal = Proposal::custom(custom_proposal);
903 AuthenticatedContent::member_proposal(
904 framing_parameters,
905 self.own_leaf_index(),
906 proposal,
907 self.context(),
908 signer,
909 )
910 }
911}