use crate::{
CRATE_NAME, SensitiveBytes,
crypto::Mac,
defs::WireFormat,
group::GroupId,
messages::{ContentType, FramedContent, FramedContentAuthData, FramedContentTBS, Sender},
};
use super::{AuthenticatedContent, AuthenticatedContentRef};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PublicMessage<'a> {
pub content: FramedContent<'a>,
pub auth: FramedContentAuthData<'a>,
pub membership_tag: Option<Mac<'a>>,
}
impl<'a> PublicMessage<'a> {
const AUTH_CONTENT_REF_WF: WireFormat =
WireFormat::new_unchecked(WireFormat::MLS_PUBLIC_MESSAGE);
pub fn into_authenticated_content(self) -> AuthenticatedContent<'a> {
AuthenticatedContent {
wire_format: Self::AUTH_CONTENT_REF_WF,
content: self.content,
auth: self.auth,
}
}
pub fn as_authenticated_content(&'a self) -> AuthenticatedContentRef<'a> {
AuthenticatedContentRef {
wire_format: &Self::AUTH_CONTENT_REF_WF,
content: &self.content,
auth: &self.auth,
}
}
}
impl thalassa::TlsplSize for PublicMessage<'_> {
fn tlspl_serialized_len(&self) -> usize {
let membership_tag_len = if matches!(self.content.sender, Sender::Member(_)) {
self.membership_tag
.as_ref()
.map(thalassa::TlsplSize::tlspl_serialized_len)
.unwrap_or_default()
} else {
debug_assert!(
self.membership_tag.is_none(),
"PublicMessage contains a membership_tag while it shouldn't. There's a bug somewhere"
);
0
};
self.content.tlspl_serialized_len() + self.auth.tlspl_serialized_len() + membership_tag_len
}
}
impl thalassa::TlsplSerialize for PublicMessage<'_> {
fn tlspl_serialize_to<W: thalassa::io::Write>(
&self,
writer: &mut W,
) -> thalassa::error::TlsplWriteResult<usize> {
let mut written =
self.content.tlspl_serialize_to(writer)? + self.auth.tlspl_serialize_to(writer)?;
if matches!(self.content.sender, Sender::Member(_)) {
let Some(mac) = &self.membership_tag else {
return Err(thalassa::error::TlsplWriteError::custom(
CRATE_NAME,
"PublicMessage.content.sender is Member but `membership_tag` is missing",
));
};
written += mac.tlspl_serialize_to(writer)?;
}
Ok(written)
}
}
impl<'a> thalassa::TlsplDeserialize<'a> for PublicMessage<'a> {
fn tlspl_deserialize_from<R: thalassa::io::Read<'a>>(
reader: &mut R,
) -> thalassa::error::TlsplReadResult<Self>
where
Self: Sized + 'a,
{
let content = FramedContent::tlspl_deserialize_from(reader)?;
let auth = FramedContentAuthData::tlspl_deserialize_from_with_content_type(
reader,
(&content.content).into(),
)?;
let membership_tag = if matches!(content.sender, Sender::Member(_)) {
Some(Mac::tlspl_deserialize_from(reader)?)
} else {
None
};
Ok(Self {
content,
auth,
membership_tag,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, thalassa::TlsplSerialize, thalassa::TlsplSize)]
pub struct AuthenticatedContentTBM<'a> {
pub content_tbs: FramedContentTBS<'a>,
pub auth: &'a FramedContentAuthData<'a>,
}
#[derive(Debug, Clone, PartialEq, Eq, thalassa::TlsplAll)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PrivateMessage<'a> {
pub group_id: GroupId<'a>,
pub epoch: u64,
pub content_type: ContentType,
pub authenticated_data: SensitiveBytes<'a>,
pub encrypted_sender_data: SensitiveBytes<'a>,
pub ciphertext: SensitiveBytes<'a>,
}