mls_spec/messages/
message_kinds.rs

1use crate::{
2    SensitiveBytes,
3    crypto::Mac,
4    defs::WireFormat,
5    group::GroupId,
6    messages::{ContentType, FramedContent, FramedContentAuthData, FramedContentTBS, Sender},
7};
8
9use super::{AuthenticatedContent, AuthenticatedContentRef};
10
11/// MLS Public Message (authenticated only)
12///
13/// <https://www.rfc-editor.org/rfc/rfc9420.html#name-encoding-and-decoding-a-pub>
14///
15/// ## TLS Presentation Language
16///
17/// ```notrust,ignore
18/// struct {
19///     FramedContent content;
20///     FramedContentAuthData auth;
21///     select (PublicMessage.content.sender.sender_type) {
22///         case member:
23///             MAC membership_tag;
24///         case external:
25///         case new_member_commit:
26///         case new_member_proposal:
27///             struct{};
28///     };
29/// } PublicMessage;
30/// ```
31///
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct PublicMessage {
35    pub content: FramedContent,
36    pub auth: FramedContentAuthData,
37    pub membership_tag: Option<Mac>,
38}
39
40impl PublicMessage {
41    const AUTH_CONTENT_REF_WF: WireFormat =
42        WireFormat::new_unchecked(WireFormat::MLS_PUBLIC_MESSAGE);
43
44    pub fn into_authenticated_content(self) -> AuthenticatedContent {
45        AuthenticatedContent {
46            wire_format: Self::AUTH_CONTENT_REF_WF,
47            content: self.content,
48            auth: self.auth,
49        }
50    }
51
52    pub fn as_authenticated_content(&self) -> AuthenticatedContentRef {
53        AuthenticatedContentRef {
54            wire_format: &Self::AUTH_CONTENT_REF_WF,
55            content: &self.content,
56            auth: &self.auth,
57        }
58    }
59}
60
61impl tls_codec::Serialize for PublicMessage {
62    fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
63        let mut written = self.content.tls_serialize(writer)?;
64        written += self.auth.tls_serialize(writer)?;
65        if matches!(self.content.sender, Sender::Member(_)) {
66            let Some(mac) = &self.membership_tag else {
67                return Err(tls_codec::Error::EncodingError(
68                    "PublicMessage.content.sender is Member but `membership_tag` is missing".into(),
69                ));
70            };
71            written += mac.tls_serialize(writer)?;
72        }
73
74        Ok(written)
75    }
76}
77
78impl tls_codec::Deserialize for PublicMessage {
79    fn tls_deserialize<R: std::io::Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
80    where
81        Self: Sized,
82    {
83        let content = FramedContent::tls_deserialize(bytes)?;
84        let auth = FramedContentAuthData::tls_deserialize_with_content_type(
85            bytes,
86            (&content.content).into(),
87        )?;
88
89        let membership_tag = if matches!(content.sender, Sender::Member(_)) {
90            Some(Mac::tls_deserialize(bytes)?)
91        } else {
92            None
93        };
94
95        Ok(Self {
96            content,
97            auth,
98            membership_tag,
99        })
100    }
101}
102
103impl tls_codec::Size for PublicMessage {
104    fn tls_serialized_len(&self) -> usize {
105        let membership_tag_len = if matches!(self.content.sender, Sender::Member(_)) {
106            self.membership_tag
107                .as_ref()
108                .map(tls_codec::Size::tls_serialized_len)
109                .unwrap_or_default()
110        } else {
111            debug_assert!(
112                self.membership_tag.is_none(),
113                "PublicMessage contains a membership_tag while it shouldn't. There's a bug somewhere"
114            );
115
116            0
117        };
118
119        self.content.tls_serialized_len() + self.auth.tls_serialized_len() + membership_tag_len
120    }
121}
122
123/// Struct to be HMAC'd to calculate a [PublicMessage]'s `membership_tag`
124///
125/// <https://www.rfc-editor.org/rfc/rfc9420.html#section-6.2-4>
126///
127/// ## TLS Presentation Language
128///
129/// ```notrust,ignore
130/// struct {
131///   FramedContentTBS content_tbs;
132///   FramedContentAuthData auth;
133/// } AuthenticatedContentTBM;
134/// ```
135#[derive(Debug, Clone, PartialEq, Eq, tls_codec::TlsSerialize, tls_codec::TlsSize)]
136pub struct AuthenticatedContentTBM<'a> {
137    pub content_tbs: FramedContentTBS<'a>,
138    pub auth: &'a FramedContentAuthData,
139}
140
141/// MLS Private Message (authenticated & encrypted)
142///
143/// <https://www.rfc-editor.org/rfc/rfc9420.html#name-encoding-and-decoding-a-pri>
144///
145/// ## TLS Presentation Language
146///
147/// ```notrust,ignore
148/// struct {
149///     opaque group_id<V>;
150///     uint64 epoch;
151///     ContentType content_type;
152///     opaque authenticated_data<V>;
153///     opaque encrypted_sender_data<V>;
154///     opaque ciphertext<V>;
155/// } PrivateMessage;
156/// ```
157#[derive(
158    Debug,
159    Clone,
160    PartialEq,
161    Eq,
162    tls_codec::TlsSerialize,
163    tls_codec::TlsDeserialize,
164    tls_codec::TlsSize,
165)]
166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
167pub struct PrivateMessage {
168    pub group_id: GroupId,
169    pub epoch: u64,
170    pub content_type: ContentType,
171    pub authenticated_data: SensitiveBytes,
172    pub encrypted_sender_data: SensitiveBytes,
173    pub ciphertext: SensitiveBytes,
174}