// 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 super ::framing::Content;
use crate ::client::MlsError;
use crate ::crypto::SignatureSecretKey;
use crate ::group::framing::{ContentType, FramedContent, PublicMessage, Sender, WireFormat};
use crate ::group::{ConfirmationTag, GroupContext};
use crate ::signer::Signable;
use crate ::CipherSuiteProvider;
use alloc::vec;
use alloc::vec::Vec;
use core::{
fmt::{self , Debug},
ops::Deref,
};
use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize};
use mls_rs_core::protocol_version::ProtocolVersion;
#[ derive(Clone, Debug, PartialEq)]
#[ cfg_attr(feature = "arbitrary" , derive(arbitrary::Arbitrary))]
#[ cfg_attr(feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
pub struct FramedContentAuthData {
pub signature: MessageSignature,
pub confirmation_tag: Option<ConfirmationTag>,
}
impl MlsSize for FramedContentAuthData {
fn mls_encoded_len(&self ) -> usize {
self .signature.mls_encoded_len()
+ self
.confirmation_tag
.as_ref()
.map_or(0 , |tag| tag.mls_encoded_len())
}
}
impl MlsEncode for FramedContentAuthData {
fn mls_encode(&self , writer: &mut Vec<u8>) -> Result<(), mls_rs_codec::Error> {
self .signature.mls_encode(writer)?;
if let Some(ref tag) = self .confirmation_tag {
tag.mls_encode(writer)?;
}
Ok(())
}
}
impl FramedContentAuthData {
pub (crate ) fn mls_decode(
reader: &mut &[u8],
content_type: ContentType,
) -> Result<Self , mls_rs_codec::Error> {
Ok(FramedContentAuthData {
signature: MessageSignature::mls_decode(reader)?,
confirmation_tag: match content_type {
ContentType::Commit => Some(ConfirmationTag::mls_decode(reader)?),
#[ cfg(feature = "private_message" )]
ContentType::Application => None,
#[ cfg(feature = "by_ref_proposal" )]
ContentType::Proposal => None,
},
})
}
}
#[ derive(Clone, Debug, PartialEq, MlsSize, MlsEncode)]
#[ cfg_attr(feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
pub struct AuthenticatedContent {
pub (crate ) wire_format: WireFormat,
pub (crate ) content: FramedContent,
pub (crate ) auth: FramedContentAuthData,
}
impl From<PublicMessage> for AuthenticatedContent {
fn from(p: PublicMessage) -> Self {
Self {
wire_format: WireFormat::PublicMessage,
content: p.content,
auth: p.auth,
}
}
}
impl AuthenticatedContent {
pub (crate ) fn new(
context: &GroupContext,
sender: Sender,
content: Content,
authenticated_data: Vec<u8>,
wire_format: WireFormat,
) -> AuthenticatedContent {
AuthenticatedContent {
wire_format,
content: FramedContent {
group_id: context.group_id.clone(),
epoch: context.epoch,
sender,
authenticated_data,
content,
},
auth: FramedContentAuthData {
signature: MessageSignature::empty(),
confirmation_tag: None,
},
}
}
#[ inline(never)]
#[ cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
pub (crate ) async fn new_signed<P: CipherSuiteProvider>(
signature_provider: &P,
context: &GroupContext,
sender: Sender,
content: Content,
signer: &SignatureSecretKey,
wire_format: WireFormat,
authenticated_data: Vec<u8>,
) -> Result<AuthenticatedContent, MlsError> {
// Construct an MlsPlaintext object containing the content
let mut plaintext =
AuthenticatedContent::new(context, sender, content, authenticated_data, wire_format);
let signing_context = MessageSigningContext {
group_context: Some(context),
protocol_version: context.protocol_version,
};
// Sign the MlsPlaintext using the current epoch's GroupContext as context.
plaintext
.sign(signature_provider, signer, &signing_context)
.await ?;
Ok(plaintext)
}
}
impl MlsDecode for AuthenticatedContent {
fn mls_decode(reader: &mut &[u8]) -> Result<Self , mls_rs_codec::Error> {
let wire_format = WireFormat::mls_decode(reader)?;
let content = FramedContent::mls_decode(reader)?;
let auth_data = FramedContentAuthData::mls_decode(reader, content.content_type())?;
Ok(AuthenticatedContent {
wire_format,
content,
auth: auth_data,
})
}
}
#[ derive(Clone, Debug, PartialEq)]
pub (crate ) struct AuthenticatedContentTBS<'a> {
pub (crate ) protocol_version: ProtocolVersion,
pub (crate ) wire_format: WireFormat,
pub (crate ) content: &'a FramedContent,
pub (crate ) context: Option<&'a GroupContext>,
}
impl <'a> MlsSize for AuthenticatedContentTBS<' a> {
fn mls_encoded_len(&self ) -> usize {
self .protocol_version.mls_encoded_len()
+ self .wire_format.mls_encoded_len()
+ self .content.mls_encoded_len()
+ self .context.as_ref().map_or(0 , |ctx| ctx.mls_encoded_len())
}
}
impl <'a> MlsEncode for AuthenticatedContentTBS<' a> {
fn mls_encode(&self , writer: &mut Vec<u8>) -> Result<(), mls_rs_codec::Error> {
self .protocol_version.mls_encode(writer)?;
self .wire_format.mls_encode(writer)?;
self .content.mls_encode(writer)?;
if let Some(context) = self .context {
context.mls_encode(writer)?;
}
Ok(())
}
}
impl <'a> AuthenticatedContentTBS<' a> {
/// The group context must not be `None` when the sender is `Member` or `NewMember`.
pub (crate ) fn from_authenticated_content(
auth_content: &'a AuthenticatedContent,
group_context: Option<&'a GroupContext>,
protocol_version: ProtocolVersion,
) -> Self {
AuthenticatedContentTBS {
protocol_version,
wire_format: auth_content.wire_format,
content: &auth_content.content,
context: match auth_content.content.sender {
Sender::Member(_) | Sender::NewMemberCommit => group_context,
#[ cfg(feature = "by_ref_proposal" )]
Sender::External(_) => None,
#[ cfg(feature = "by_ref_proposal" )]
Sender::NewMemberProposal => None,
},
}
}
}
#[ derive(Debug)]
pub (crate ) struct MessageSigningContext<'a> {
pub group_context: Option<&'a GroupContext>,
pub protocol_version: ProtocolVersion,
}
impl <'a> Signable<' a> for AuthenticatedContent {
const SIGN_LABEL: &'static str = "FramedContentTBS";
type SigningContext = MessageSigningContext<'a>;
fn signature(&self ) -> &[u8] {
&self .auth.signature
}
fn signable_content(
&self ,
context: &MessageSigningContext,
) -> Result<Vec<u8>, mls_rs_codec::Error> {
AuthenticatedContentTBS::from_authenticated_content(
self ,
context.group_context,
context.protocol_version,
)
.mls_encode_to_vec()
}
fn write_signature(&mut self , signature: Vec<u8>) {
self .auth.signature = MessageSignature::from(signature)
}
}
#[ derive(Clone, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode)]
#[ cfg_attr(feature = "arbitrary" , derive(arbitrary::Arbitrary))]
#[ cfg_attr(feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
pub struct MessageSignature(
#[ mls_codec(with = "mls_rs_codec::byte_vec" )]
#[ cfg_attr(feature = "serde" , serde(with = "mls_rs_core::vec_serde" ))]
Vec<u8>,
);
impl Debug for MessageSignature {
fn fmt(&self , f: &mut fmt::Formatter<'_>) -> fmt::Result {
mls_rs_core::debug::pretty_bytes(&self .0 )
.named("MessageSignature" )
.fmt(f)
}
}
impl MessageSignature {
pub (crate ) fn empty() -> Self {
MessageSignature(vec![])
}
}
impl Deref for MessageSignature {
type Target = Vec<u8>;
fn deref(&self ) -> &Self ::Target {
&self .0
}
}
impl From<Vec<u8>> for MessageSignature {
fn from(v: Vec<u8>) -> Self {
MessageSignature(v)
}
}
Messung V0.5 in Prozent C=76 H=96 G=86
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-23)
¤
*© Formatika GbR, Deutschland