// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT)
use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::{self, Debug}; use mls_rs_core::{
identity::{IdentityProvider, MemberValidationContext},
protocol_version::ProtocolVersion,
psk::PreSharedKeyStorage,
};
//By default, the path field of a Commit MUST be populated. The path field MAY be omitted if //(a) it covers at least one proposal and (b) none of the proposals covered by the Commit are //of "path required" types. A proposal type requires a path if it cannot change the group //membership in a way that requires the forward secrecy and post-compromise security guarantees //that an UpdatePath provides. The only proposal types defined in this document that do not //require a path are:
// add // psk // reinit pub(crate) fn path_update_required(proposals: &ProposalBundle) -> bool { let res = !proposals.external_init_proposals().is_empty();
#[cfg(feature = "by_ref_proposal")] let res = res || !proposals.update_proposals().is_empty();
#[cfg(all(
feature = "by_ref_proposal",
feature = "custom_proposal",
feature = "self_remove_proposal"
))] let res = res || !proposals.self_removes.is_empty();
res || proposals.length() == 0
|| proposals.group_context_extensions_proposal().is_some()
|| !proposals.remove_proposals().is_empty()
}
#[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] /// An event generated as a result of processing a message for a group with /// [`Group::process_incoming_message`](crate::group::Group::process_incoming_message). pubenum ReceivedMessage { /// An application message was decrypted.
ApplicationMessage(ApplicationMessageDescription), /// A new commit was processed creating a new group state.
Commit(CommitMessageDescription), /// A proposal was received.
Proposal(ProposalMessageDescription), /// Validated GroupInfo object
GroupInfo(GroupInfo), /// Validated welcome message
Welcome, /// Validated key package
KeyPackage(KeyPackage),
}
impl TryFrom<ApplicationMessageDescription> for ReceivedMessage { type Error = MlsError;
#[derive(Clone, PartialEq, Eq)] /// Description of a MLS application message. pubstruct ApplicationMessageDescription { /// Index of this user in the group state. pub sender_index: u32, /// Received application data.
data: ApplicationData, /// Plaintext authenticated data in the received MLS packet. pub authenticated_data: Vec<u8>, /// Unauthenticated key generation used to decrypt the message. See documentation for /// [`Group::peek_next_key_generation`] for usage. #[cfg(all(feature = "export_key_generation", feature = "private_message"))] pub unauthenticated_key_generation: Option<u32>,
}
#[derive(Clone, PartialEq, MlsSize, MlsEncode, MlsDecode)] #[non_exhaustive] /// Description of a processed MLS commit message. pubstruct CommitMessageDescription { /// True if this is the result of an external commit. pub is_external: bool, /// The index in the group state of the member who performed this commit. pub committer: u32, /// A full description of group state changes as a result of this commit. pub effect: CommitEffect, /// Plaintext authenticated data in the received MLS packet. #[mls_codec(with = "mls_rs_codec::byte_vec")] pub authenticated_data: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, MlsEncode, MlsDecode, MlsSize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(u8)] /// Proposal sender type. pubenum ProposalSender { /// A current member of the group by index in the group state.
Member(u32) = 1u8, /// An external entity by index within an /// [`ExternalSendersExt`](crate::extension::built_in::ExternalSendersExt).
External(u32) = 2u8, /// A new member proposing their addition to the group.
NewMember = 3u8,
}
impl TryFrom<Sender> for ProposalSender { type Error = MlsError;
type MlsRules: MlsRules; type IdentityProvider: IdentityProvider; type CipherSuiteProvider: CipherSuiteProvider; type PreSharedKeyStorage: PreSharedKeyStorage;
asyncfn process_incoming_message_with_time(
&mutself,
message: MlsMessage, #[cfg(feature = "by_ref_proposal")] cache_proposal: bool,
time_sent: Option<MlsTime>,
) -> Result<Self::OutputType, MlsError> { #[cfg(all(feature = "export_key_generation", feature = "private_message"))] // For encrypted application messages, retrieve the unauthenticated key // generation used to decrypt the message and return it with the plaintext. Does // not return an error on failure, allowing `get_event_from_incoming_message` to // continue owning that task. // Note that this decrypts the SenderData twice, which is not ideal. let unauthn_key_gen_in_app_msg: Option<u32> = match message.payload {
MlsMessagePayload::Cipher(ref cipher_text) => self
.get_unauthenticated_key_generation_from_sender_data(cipher_text)
.unwrap_or_default(),
_ => None,
};
let event_or_content = self
.get_event_from_incoming_message(message, time_sent)
.await?;
// Update the new GroupContext's confirmed and interim transcript hashes using the new Commit. let (interim_transcript_hash, confirmed_transcript_hash) = transcript_hashes( self.cipher_suite_provider(),
&self.group_state().interim_transcript_hash,
&auth_content,
)
.await?;
#[cfg(any(feature = "private_message", feature = "by_ref_proposal"))] let commit = match auth_content.content.content {
Content::Commit(commit) => Ok(commit),
_ => Err(MlsError::UnexpectedMessageType),
}?;
#[cfg(not(any(feature = "private_message", feature = "by_ref_proposal")))] let Content::Commit(commit) = auth_content.content.content;
let group_state = self.group_state(); let id_provider = self.identity_provider();
#[cfg(feature = "by_ref_proposal")] let proposals = group_state
.proposals
.resolve_for_commit(auth_content.content.sender, commit.proposals)?;
#[cfg(not(feature = "by_ref_proposal"))] let proposals = resolve_for_commit(auth_content.content.sender, commit.proposals)?;
let sender = commit_sender(&auth_content.content.sender, &provisional_state)?;
//Verify that the path value is populated if the proposals vector contains any Update // or Remove proposals, or if it's empty. Otherwise, the path value MAY be omitted. if path_update_required(&provisional_state.applied_proposals) && commit.path.is_none() { return Err(MlsError::CommitMissingPath);
}
let self_removed = self.removal_proposal(&provisional_state); #[cfg(all(
feature = "by_ref_proposal",
feature = "custom_proposal",
feature = "self_remove_proposal"
))] let self_removed_by_self = self.self_removal_proposal(&provisional_state);
let new_secrets = match update_path {
Some(update_path) if !is_self_removed => { self.apply_update_path(sender, &update_path, &mut provisional_state)
.await
}
_ => Ok(None),
}?;
// Update the transcript hash to get the new context.
provisional_state.group_context.confirmed_transcript_hash = confirmed_transcript_hash;
// Update the parent hashes in the new context
provisional_state
.public_tree
.update_hashes(&[sender], self.cipher_suite_provider())
.await?;
// Update the tree hash in the new context
provisional_state.group_context.tree_hash = provisional_state
.public_tree
.tree_hash(self.cipher_suite_provider())
.await?;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.