diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index 37a9a72ad..50e2e8317 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -500,7 +500,7 @@ pub(crate) enum CoreGroupParseMessageError { /// Create group context ext proposal error #[derive(Error, Debug, PartialEq, Clone)] -pub enum CreateGroupContextExtProposalError { +pub enum CreateGroupContextExtProposalError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -513,6 +513,12 @@ pub enum CreateGroupContextExtProposalError { /// See [`LeafNodeValidationError`] for more details. #[error(transparent)] LeafNodeValidation(#[from] LeafNodeValidationError), + /// See [`MlsGroupStateError`] for more details. + #[error(transparent)] + MlsGroupStateError(#[from] MlsGroupStateError), + /// See [`CreateCommitError`] for more details. + #[error(transparent)] + CreateCommitError(#[from] CreateCommitError), } /// Error merging a commit. @@ -537,6 +543,15 @@ pub enum GroupContextExtensionsProposalValidationError { #[error(transparent)] LibraryError(#[from] LibraryError), + /// The new extension types in required capabilties contails extensions that are not supported by all group members. + #[error( + "The new required capabilties contain extension types that are not supported by all group members." + )] + ExtensionNotSupportedByAllMembers, + /// Proposal changes the immutable metadata extension, which is not allowed. + #[error("Proposal changes the immutable metadata extension, which is not allowed.")] + ChangedImmutableMetadata, + /// The new extension types in required capabilties contails extensions that are not supported by all group members. #[error( "The new required capabilties contain extension types that are not supported by all group members." diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 0416b4bfd..b22c723ed 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -214,6 +214,23 @@ impl MlsGroup { .leaf(leaf_index) .map(|leaf| leaf.credential()) } + + /// Returns the [`Member`] corresponding to the given + /// leaf index. Returns `None` if the member can not be found in this group. + pub fn member_at(&self, leaf_index: LeafNodeIndex) -> Option { + self.group + .public_group() + // This will return an error if the member can't be found. + .leaf(leaf_index) + .map(|leaf_node| { + Member::new( + leaf_index, + leaf_node.encryption_key().as_slice().to_vec(), + leaf_node.signature_key().as_slice().to_vec(), + leaf_node.credential().clone(), + ) + }) + } } /// Helper `enum` that classifies the kind of remove operation. This can be used to diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 19bfd78d5..a747ac141 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -1,8 +1,10 @@ use openmls_traits::{signatures::Signer, storage::StorageProvider, types::Ciphersuite}; use super::{ + core_group::create_commit_params::CreateCommitParams, errors::{ProposalError, ProposeAddMemberError, ProposeRemoveMemberError}, - CustomProposal, MlsGroup, + CreateGroupContextExtProposalError, CustomProposal, GroupContextExtensionProposal, MlsGroup, + MlsGroupState, PendingCommitState, Proposal, }; use crate::{ binary_tree::LeafNodeIndex, @@ -12,7 +14,7 @@ use crate::{ framing::MlsMessageOut, group::{errors::CreateAddProposalError, GroupId, QueuedProposal}, key_packages::KeyPackage, - messages::proposals::ProposalOrRefType, + messages::{group_info::GroupInfo, proposals::ProposalOrRefType}, prelude::LibraryError, schedule::PreSharedKeyId, storage::OpenMlsProvider, @@ -384,4 +386,46 @@ impl MlsGroup { Ok((mls_message, proposal_ref)) } + + /// Updates group context extensions + /// + /// Returns an error when the group does not support all the required capabilities + /// in the new `extensions`. + #[allow(clippy::type_complexity)] + pub fn update_group_context_extensions( + &mut self, + provider: &Provider, + extensions: Extensions, + signer: &impl Signer, + ) -> Result< + (MlsMessageOut, Option, Option), + CreateGroupContextExtProposalError, + > { + self.is_operational()?; + + // Create group context extension proposals + let inline_proposals = vec![Proposal::GroupContextExtensions( + GroupContextExtensionProposal { extensions }, + )]; + + let params = CreateCommitParams::builder() + .framing_parameters(self.framing_parameters()) + .proposal_store(&self.proposal_store) + .inline_proposals(inline_proposals) + .build(); + let create_commit_result = self.group.create_commit(params, provider, signer)?; + + let mls_messages = self.content_to_mls_message(create_commit_result.commit, provider)?; + self.group_state = MlsGroupState::PendingCommit(Box::new(PendingCommitState::Member( + create_commit_result.staged_commit, + ))); + + Ok(( + mls_messages, + create_commit_result + .welcome_option + .map(|w| MlsMessageOut::from_welcome(w, self.group.version())), + create_commit_result.group_info, + )) + } } diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 558b73efa..cf5369500 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -401,6 +401,11 @@ impl KeyPackage { pub fn last_resort(&self) -> bool { self.payload.extensions.contains(ExtensionType::LastResort) } + + /// Get the lifetime of the KeyPackage + pub fn life_time(&self) -> &Lifetime { + self.payload.leaf_node.life_time().unwrap() + } } /// Crate visible `KeyPackage` functions. diff --git a/openmls/src/messages/proposals.rs b/openmls/src/messages/proposals.rs index 181fa7cb9..c13a236c0 100644 --- a/openmls/src/messages/proposals.rs +++ b/openmls/src/messages/proposals.rs @@ -468,7 +468,7 @@ pub struct AppAckProposal { TlsSize, )] pub struct GroupContextExtensionProposal { - extensions: Extensions, + pub(crate) extensions: Extensions, } impl GroupContextExtensionProposal { @@ -478,7 +478,7 @@ impl GroupContextExtensionProposal { } /// Get the extensions of the proposal - pub(crate) fn extensions(&self) -> &Extensions { + pub fn extensions(&self) -> &Extensions { &self.extensions } }