openmls/framing/
message_out.rs

1//! MLS Message (Output)
2//!
3//! This module defines the [`MlsMessageOut`] structs which implements the
4//! `MLSMessage` struct as defined by the MLS specification, but is used
5//! exclusively as output of the [`MlsGroup`] API. [`MlsMessageIn`] also
6//! implements `MLSMessage`, but for inputs.
7//!
8//! The [`MlsMessageOut`] struct is meant to be serialized upon its return from
9//! a function of the `MlsGroup` API so that it can be sent to the DS.
10use tls_codec::Serialize;
11
12use super::*;
13
14use crate::{
15    key_packages::KeyPackage, messages::group_info::GroupInfo, prelude::KeyPackageBundle,
16    versions::ProtocolVersion,
17};
18
19#[cfg(any(feature = "test-utils", test))]
20use crate::messages::group_info::VerifiableGroupInfo;
21
22/// An [`MlsMessageOut`] is typically returned from an [`MlsGroup`] function and
23/// meant to be serialized and sent to the DS.
24#[derive(Debug, Clone, PartialEq, TlsSerialize, TlsSize)]
25pub struct MlsMessageOut {
26    pub(crate) version: ProtocolVersion,
27    pub(crate) body: MlsMessageBodyOut,
28}
29
30/// MLSMessage (Body)
31///
32/// Note: Because [MlsMessageBodyOut] already discriminates between
33/// `public_message`, `private_message`, etc., we don't use the
34/// `wire_format` field. This prevents inconsistent assignments
35/// where `wire_format` contradicts the variant given in `body`.
36///
37/// ```c
38/// // draft-ietf-mls-protocol-17
39/// struct {
40///     // ... continued from [MlsMessage] ...
41///
42///     WireFormat wire_format;
43///     select (MLSMessage.wire_format) {
44///         case mls_plaintext:
45///             PublicMessage plaintext;
46///         case mls_ciphertext:
47///             PrivateMessage ciphertext;
48///         case mls_welcome:
49///             Welcome welcome;
50///         case mls_group_info:
51///             GroupInfo group_info;
52///         case mls_key_package:
53///             KeyPackage key_package;
54///     }
55/// } MLSMessage;
56/// ```
57#[derive(Debug, PartialEq, Clone, TlsSerialize, TlsSize)]
58#[repr(u16)]
59pub enum MlsMessageBodyOut {
60    /// Plaintext message
61    #[tls_codec(discriminant = 1)]
62    PublicMessage(PublicMessage),
63
64    /// Ciphertext message
65    #[tls_codec(discriminant = 2)]
66    PrivateMessage(PrivateMessage),
67
68    /// Welcome message
69    #[tls_codec(discriminant = 3)]
70    Welcome(Welcome),
71
72    /// Group information
73    #[tls_codec(discriminant = 4)]
74    GroupInfo(GroupInfo),
75
76    /// KeyPackage
77    #[tls_codec(discriminant = 5)]
78    #[allow(dead_code)]
79    KeyPackage(KeyPackage),
80}
81
82impl From<PublicMessage> for MlsMessageOut {
83    fn from(public_message: PublicMessage) -> Self {
84        Self {
85            version: ProtocolVersion::default(),
86            body: MlsMessageBodyOut::PublicMessage(public_message),
87        }
88    }
89}
90
91impl From<PrivateMessage> for MlsMessageOut {
92    fn from(private_message: PrivateMessage) -> Self {
93        Self {
94            version: ProtocolVersion::default(),
95            body: MlsMessageBodyOut::PrivateMessage(private_message),
96        }
97    }
98}
99
100impl From<GroupInfo> for MlsMessageOut {
101    fn from(group_info: GroupInfo) -> Self {
102        Self {
103            version: group_info.group_context().protocol_version(),
104            body: MlsMessageBodyOut::GroupInfo(group_info),
105        }
106    }
107}
108
109impl From<KeyPackage> for MlsMessageOut {
110    fn from(key_package: KeyPackage) -> Self {
111        Self {
112            version: key_package.protocol_version(),
113            body: MlsMessageBodyOut::KeyPackage(key_package),
114        }
115    }
116}
117
118impl From<KeyPackageBundle> for MlsMessageOut {
119    fn from(key_package: KeyPackageBundle) -> Self {
120        Self {
121            version: key_package.key_package().protocol_version(),
122            body: MlsMessageBodyOut::KeyPackage(key_package.key_package),
123        }
124    }
125}
126
127impl MlsMessageOut {
128    /// Create an [`MlsMessageOut`] from a [`PrivateMessage`], as well as the
129    /// currently used [`ProtocolVersion`].
130    pub(crate) fn from_private_message(
131        private_message: PrivateMessage,
132        version: ProtocolVersion,
133    ) -> Self {
134        Self {
135            version,
136            body: MlsMessageBodyOut::PrivateMessage(private_message),
137        }
138    }
139
140    /// Create an [`MlsMessageOut`] from a [`Welcome`] message and the currently
141    /// used [`ProtocolVersion`].
142    pub fn from_welcome(welcome: Welcome, version: ProtocolVersion) -> Self {
143        MlsMessageOut {
144            version,
145            body: MlsMessageBodyOut::Welcome(welcome),
146        }
147    }
148
149    /// Serializes the message to a byte vector. Returns [`MlsMessageError::UnableToEncode`] on failure.
150    pub fn to_bytes(&self) -> Result<Vec<u8>, MlsMessageError> {
151        self.tls_serialize_detached()
152            .map_err(|_| MlsMessageError::UnableToEncode)
153    }
154
155    /// Returns a reference to the contents of this [`MlsMessageOut`].
156    pub fn body(&self) -> &MlsMessageBodyOut {
157        &self.body
158    }
159}
160
161// Convenience functions for tests and test-utils
162
163#[cfg(any(feature = "test-utils", test))]
164impl MlsMessageOut {
165    /// Turn an [`MlsMessageOut`] into a [`Welcome`].
166    #[cfg(any(feature = "test-utils", test))]
167    pub fn into_welcome(self) -> Option<Welcome> {
168        match self.body {
169            MlsMessageBodyOut::Welcome(w) => Some(w),
170            _ => None,
171        }
172    }
173
174    #[cfg(any(feature = "test-utils", test))]
175    pub fn into_protocol_message(self) -> Option<ProtocolMessage> {
176        let mls_message_in: MlsMessageIn = self.into();
177
178        match mls_message_in.extract() {
179            MlsMessageBodyIn::PublicMessage(pm) => Some(pm.into()),
180            MlsMessageBodyIn::PrivateMessage(pm) => Some(pm.into()),
181            _ => None,
182        }
183    }
184
185    #[cfg(any(feature = "test-utils", test))]
186    pub fn into_verifiable_group_info(self) -> Option<VerifiableGroupInfo> {
187        match self.body {
188            MlsMessageBodyOut::GroupInfo(group_info) => {
189                Some(group_info.into_verifiable_group_info())
190            }
191            _ => None,
192        }
193    }
194}
195
196// The following two `From` implementations break abstraction layers and MUST
197// NOT be made available outside of tests or "test-utils".
198
199#[cfg(any(feature = "test-utils", test))]
200impl From<MlsMessageIn> for MlsMessageOut {
201    fn from(mls_message: MlsMessageIn) -> Self {
202        let version = mls_message.version;
203        let body = match mls_message.body {
204            MlsMessageBodyIn::Welcome(w) => MlsMessageBodyOut::Welcome(w),
205            MlsMessageBodyIn::GroupInfo(gi) => MlsMessageBodyOut::GroupInfo(gi.into()),
206            MlsMessageBodyIn::KeyPackage(kp) => MlsMessageBodyOut::KeyPackage(kp.into()),
207            MlsMessageBodyIn::PublicMessage(pm) => MlsMessageBodyOut::PublicMessage(pm.into()),
208            MlsMessageBodyIn::PrivateMessage(pm) => MlsMessageBodyOut::PrivateMessage(pm.into()),
209        };
210        Self { version, body }
211    }
212}
213
214#[cfg(any(feature = "test-utils", test))]
215impl From<MlsMessageOut> for MlsMessageIn {
216    fn from(mls_message_out: MlsMessageOut) -> Self {
217        let version = mls_message_out.version;
218        let body = match mls_message_out.body {
219            MlsMessageBodyOut::PublicMessage(pm) => MlsMessageBodyIn::PublicMessage(pm.into()),
220            MlsMessageBodyOut::PrivateMessage(pm) => MlsMessageBodyIn::PrivateMessage(pm.into()),
221            MlsMessageBodyOut::Welcome(w) => MlsMessageBodyIn::Welcome(w),
222            MlsMessageBodyOut::GroupInfo(gi) => {
223                MlsMessageBodyIn::GroupInfo(gi.into_verifiable_group_info())
224            }
225            MlsMessageBodyOut::KeyPackage(kp) => MlsMessageBodyIn::KeyPackage(kp.into()),
226        };
227        Self { version, body }
228    }
229}