From c286ae8291bdc93b47768e6d150611be02d28554 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Thu, 15 Feb 2024 13:17:49 +0100 Subject: [PATCH] fix: Full GCE proposal support --- openmls/src/group/mls_group/errors.rs | 3 ++ openmls/src/group/mls_group/extension.rs | 38 ++++------------- openmls/src/group/mls_group/proposal.rs | 41 +++++++++++++++++-- openmls/src/group/tests/test_gce_proposals.rs | 24 +++++++---- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/openmls/src/group/mls_group/errors.rs b/openmls/src/group/mls_group/errors.rs index b5a9b63404..057be443f2 100644 --- a/openmls/src/group/mls_group/errors.rs +++ b/openmls/src/group/mls_group/errors.rs @@ -402,6 +402,9 @@ pub enum ProposalError { /// See [`ProposeRemoveMemberError`] for more details. #[error(transparent)] ProposeRemoveMemberError(#[from] ProposeRemoveMemberError), + /// See [`ProposeGroupContextExtensionError`] for more details. + #[error(transparent)] + ProposeGroupContextExtensionError(#[from] ProposeGroupContextExtensionError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] GroupStateError(#[from] MlsGroupStateError), diff --git a/openmls/src/group/mls_group/extension.rs b/openmls/src/group/mls_group/extension.rs index 541bb170b4..695e71fb35 100644 --- a/openmls/src/group/mls_group/extension.rs +++ b/openmls/src/group/mls_group/extension.rs @@ -6,10 +6,7 @@ use openmls_traits::signatures::Signer; use crate::{ messages::group_info::GroupInfo, - prelude::{ - create_commit_params::CreateCommitParams, hash_ref::ProposalRef, - ProposeGroupContextExtensionError, - }, + prelude::{create_commit_params::CreateCommitParams, hash_ref::ProposalRef}, }; use super::*; @@ -19,36 +16,19 @@ impl MlsGroup { /// of the group but does not merge them yet. /// /// Returns an error if there is a pending commit. - pub fn propose_extensions( + #[inline] + pub fn propose_extensions( &mut self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, extensions: Extensions, - ) -> Result<(MlsMessageOut, ProposalRef), ProposeGroupContextExtensionError> { - self.is_operational()?; - - let gce_proposal = self.group.create_group_context_ext_proposal( - self.framing_parameters(), - extensions, - self.pending_proposals(), - signer, - )?; - let proposal = QueuedProposal::from_authenticated_content( - self.ciphersuite(), + ) -> Result<(MlsMessageOut, ProposalRef), ProposalError> { + self.propose_group_context_extensions( backend, - gce_proposal.clone(), + signer, + extensions, ProposalOrRefType::Proposal, - )?; - let reference = proposal.proposal_reference().clone(); - - self.proposal_store.add(proposal); - - let mls_message = self.content_to_mls_message(gce_proposal, backend)?; - - // Since the state of the group might be changed, arm the state flag - self.flag_state_change(); - - Ok((mls_message, reference)) + ) } /// Updates the extensions of the group diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index da9a4a88bf..cde44993f5 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -229,9 +229,9 @@ impl MlsGroup { Propose::ExternalInit(_) => Err(ProposalError::LibraryError(LibraryError::custom( "Unsupported proposal type ExternalInit", ))), - Propose::GroupContextExtensions(_) => Err(ProposalError::LibraryError( - LibraryError::custom("Unsupported proposal type GroupContextExtensions"), - )), + Propose::GroupContextExtensions(new_extensions) => { + self.propose_group_context_extensions(backend, signer, new_extensions, ref_or_value) + } } } @@ -353,4 +353,39 @@ impl MlsGroup { )) } } + + /// Creates a proposal to wholesale replace extensions in a group + /// Returns an error if it contains a RequiredCapabilities extension and any group member doesn't support any of the new extensions required + pub fn propose_group_context_extensions( + &mut self, + backend: &impl OpenMlsCryptoProvider, + signer: &impl Signer, + new_extensions: Extensions, + proposal_type: ProposalOrRefType, + ) -> Result<(MlsMessageOut, ProposalRef), ProposalError> { + self.is_operational()?; + + let gce_proposal = self.group.create_group_context_ext_proposal( + self.framing_parameters(), + new_extensions, + self.pending_proposals(), + signer, + )?; + + let queued_proposal = QueuedProposal::from_authenticated_content( + self.ciphersuite(), + backend, + gce_proposal.clone(), + proposal_type, + )?; + + let proposal_ref = queued_proposal.proposal_reference().clone(); + self.proposal_store.add(queued_proposal); + + let mls_message = self.content_to_mls_message(gce_proposal, backend)?; + + self.flag_state_change(); + + Ok((mls_message, proposal_ref)) + } } diff --git a/openmls/src/group/tests/test_gce_proposals.rs b/openmls/src/group/tests/test_gce_proposals.rs index 60685767b1..a907a3b335 100644 --- a/openmls/src/group/tests/test_gce_proposals.rs +++ b/openmls/src/group/tests/test_gce_proposals.rs @@ -98,23 +98,31 @@ async fn gce_fails_when_it_contains_unsupported_extensions( )); let e = alice_group.propose_extensions(backend, &alice_signer, Extensions::single(required_key_id.clone())) .expect_err("Alice was able to create a gce proposal with a required extensions she doesn't support."); - assert_eq!( + assert!(matches!( e, - ProposeGroupContextExtensionError::MemberExtensionValidationError( - MemberExtensionValidationError::ExtensionError(ExtensionError::UnsupportedProposalType) + ProposalError::ProposeGroupContextExtensionError( + ProposeGroupContextExtensionError::MemberExtensionValidationError( + MemberExtensionValidationError::ExtensionError( + ExtensionError::UnsupportedProposalType + ) + ) ) - ); + )); // Now Bob wants the ExternalSenders extension to be required. // This should fail because Alice doesn't support it. let e = bob_group .propose_extensions(backend, &bob_signer, Extensions::single(required_key_id)) .expect_err("Bob was able to create a gce proposal for an extension not supported by all other parties."); - assert_eq!( + assert!(matches!( e, - ProposeGroupContextExtensionError::MemberExtensionValidationError( - MemberExtensionValidationError::ExtensionError(ExtensionError::UnsupportedProposalType) + ProposalError::ProposeGroupContextExtensionError( + ProposeGroupContextExtensionError::MemberExtensionValidationError( + MemberExtensionValidationError::ExtensionError( + ExtensionError::UnsupportedProposalType + ) + ) ) - ); + )); } #[apply(ciphersuites_and_backends)]