openmls/framing/
private_message_in.rs

1use openmls_traits::crypto::OpenMlsCrypto;
2use openmls_traits::types::Ciphersuite;
3use tls_codec::{
4    Deserialize, Serialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
5};
6
7use super::{
8    codec::deserialize_ciphertext_content, mls_auth_content::FramedContentAuthData,
9    mls_auth_content_in::VerifiableAuthenticatedContentIn, mls_content_in::FramedContentBodyIn,
10};
11
12use crate::{
13    binary_tree::array_representation::LeafNodeIndex,
14    error::LibraryError,
15    framing::mls_content_in::FramedContentIn,
16    tree::{secret_tree::SecretType, sender_ratchet::SenderRatchetConfiguration},
17};
18
19use super::*;
20
21/// `PrivateMessage` is the framing struct for an encrypted `PublicMessage`.
22/// This message format is meant to be sent to and received from the Delivery
23/// Service.
24///
25/// ```c
26/// // draft-ietf-mls-protocol-17
27/// struct {
28///     opaque group_id<V>;
29///     uint64 epoch;
30///     ContentType content_type;
31///     opaque authenticated_data<V>;
32///     opaque encrypted_sender_data<V>;
33///     opaque ciphertext<V>;
34/// } PrivateMessage;
35/// ```
36#[derive(
37    Debug, PartialEq, Eq, Clone, TlsSerialize, TlsSize, TlsDeserialize, TlsDeserializeBytes,
38)]
39pub struct PrivateMessageIn {
40    group_id: GroupId,
41    epoch: GroupEpoch,
42    content_type: ContentType,
43    authenticated_data: VLBytes,
44    encrypted_sender_data: VLBytes,
45    ciphertext: VLBytes,
46}
47
48impl PrivateMessageIn {
49    /// Decrypt the sender data from this [`PrivateMessageIn`].
50    pub(crate) fn sender_data(
51        &self,
52        message_secrets: &MessageSecrets,
53        crypto: &impl OpenMlsCrypto,
54        ciphersuite: Ciphersuite,
55    ) -> Result<MlsSenderData, MessageDecryptionError> {
56        log::debug!("Decrypting PrivateMessage");
57        // Derive key from the key schedule using the ciphertext.
58        let sender_data_key = message_secrets
59            .sender_data_secret()
60            .derive_aead_key(crypto, ciphersuite, self.ciphertext.as_slice())
61            .map_err(LibraryError::unexpected_crypto_error)?;
62        // Derive initial nonce from the key schedule using the ciphertext.
63        let sender_data_nonce = message_secrets
64            .sender_data_secret()
65            .derive_aead_nonce(ciphersuite, crypto, self.ciphertext.as_slice())
66            .map_err(LibraryError::unexpected_crypto_error)?;
67        // Serialize sender data AAD
68        let mls_sender_data_aad =
69            MlsSenderDataAad::new(self.group_id.clone(), self.epoch, self.content_type);
70        let mls_sender_data_aad_bytes = mls_sender_data_aad
71            .tls_serialize_detached()
72            .map_err(LibraryError::missing_bound_check)?;
73        // Decrypt sender data
74        log_crypto!(
75            trace,
76            "Decryption key for sender data: {sender_data_key:x?}"
77        );
78        log_crypto!(trace, "Decryption of sender data mls_sender_data_aad_bytes: {mls_sender_data_aad_bytes:x?} - sender_data_nonce: {sender_data_nonce:x?}");
79        let sender_data_bytes = sender_data_key
80            .aead_open(
81                crypto,
82                self.encrypted_sender_data.as_slice(),
83                &mls_sender_data_aad_bytes,
84                &sender_data_nonce,
85            )
86            .map_err(|_| {
87                log::error!("Sender data decryption error");
88                MessageDecryptionError::AeadError
89            })?;
90        log::trace!("  Successfully decrypted sender data.");
91        MlsSenderData::tls_deserialize(&mut sender_data_bytes.as_slice())
92            .map_err(|_| MessageDecryptionError::MalformedContent)
93    }
94
95    /// Decrypt this [`PrivateMessage`] and return the
96    /// [`PrivateMessageContentIn`].
97    #[inline]
98    fn decrypt(
99        &self,
100        crypto: &impl OpenMlsCrypto,
101        ratchet_key: AeadKey,
102        ratchet_nonce: &AeadNonce,
103    ) -> Result<PrivateMessageContentIn, MessageDecryptionError> {
104        // Serialize content AAD
105        let private_message_content_aad_bytes = PrivateContentAad {
106            group_id: self.group_id.clone(),
107            epoch: self.epoch,
108            content_type: self.content_type,
109            authenticated_data: VLByteSlice(self.authenticated_data.as_slice()),
110        }
111        .tls_serialize_detached()
112        .map_err(LibraryError::missing_bound_check)?;
113        // Decrypt payload
114        log_crypto!(
115            trace,
116            "Decryption key for private message: {ratchet_key:x?}"
117        );
118        log_crypto!(trace, "Decryption of private message private_message_content_aad_bytes: {private_message_content_aad_bytes:x?} - ratchet_nonce: {ratchet_nonce:x?}");
119        log::trace!("Decrypting ciphertext {:x?}", self.ciphertext);
120        let private_message_content_bytes = ratchet_key
121            .aead_open(
122                crypto,
123                self.ciphertext.as_slice(),
124                &private_message_content_aad_bytes,
125                ratchet_nonce,
126            )
127            .map_err(|_| {
128                log::error!("  Ciphertext decryption error");
129                debug_assert!(false, "Ciphertext decryption failed");
130                MessageDecryptionError::AeadError
131            })?;
132        log_content!(
133            trace,
134            "  Successfully decrypted PublicMessage bytes: {:x?}",
135            private_message_content_bytes
136        );
137        deserialize_ciphertext_content(
138            &mut private_message_content_bytes.as_slice(),
139            self.content_type(),
140        )
141        .map_err(|_| MessageDecryptionError::MalformedContent)
142    }
143
144    /// This function decrypts a [`PrivateMessage`] into a
145    /// [`VerifiableAuthenticatedContent`]. In order to get an
146    /// [`FramedContent`] the result must be verified.
147    pub(crate) fn to_verifiable_content(
148        &self,
149        ciphersuite: Ciphersuite,
150        crypto: &impl OpenMlsCrypto,
151        message_secrets: &mut MessageSecrets,
152        sender_index: LeafNodeIndex,
153        sender_ratchet_configuration: &SenderRatchetConfiguration,
154        sender_data: MlsSenderData,
155    ) -> Result<VerifiableAuthenticatedContentIn, MessageDecryptionError> {
156        let secret_type = SecretType::from(&self.content_type);
157        // Extract generation and key material for encryption
158        let (ratchet_key, ratchet_nonce) = message_secrets
159            .secret_tree_mut()
160            .secret_for_decryption(
161                ciphersuite,
162                crypto,
163                sender_index,
164                secret_type,
165                sender_data.generation,
166                sender_ratchet_configuration,
167            )
168            .map_err(|e| {
169                log::error!(
170                    "  Ciphertext generation out of bounds {}\n\t{e:?}",
171                    sender_data.generation
172                );
173                MessageDecryptionError::SecretTreeError(e)
174            })?;
175        // Prepare the nonce by xoring with the reuse guard.
176        let prepared_nonce = ratchet_nonce.xor_with_reuse_guard(&sender_data.reuse_guard);
177        let private_message_content = self.decrypt(crypto, ratchet_key, &prepared_nonce)?;
178
179        // Extract sender. The sender type is always of type Member for PrivateMessage.
180        let sender = Sender::from_sender_data(sender_data);
181        log_content!(
182            trace,
183            "  Successfully decoded PublicMessage with: {:x?}",
184            private_message_content.content
185        );
186
187        let verifiable = VerifiableAuthenticatedContentIn::new(
188            WireFormat::PrivateMessage,
189            FramedContentIn {
190                group_id: self.group_id.clone(),
191                epoch: self.epoch,
192                sender,
193                authenticated_data: self.authenticated_data.clone(),
194                body: private_message_content.content,
195            },
196            Some(message_secrets.serialized_context().to_vec()),
197            private_message_content.auth,
198        );
199        Ok(verifiable)
200    }
201
202    /// Get the `group_id` in the `PrivateMessage`.
203    pub(crate) fn group_id(&self) -> &GroupId {
204        &self.group_id
205    }
206
207    /// Get the `epoch` in the `PrivateMessage`.
208    pub(crate) fn epoch(&self) -> GroupEpoch {
209        self.epoch
210    }
211
212    /// Get the `content_type` in the `PrivateMessage`.
213    pub(crate) fn content_type(&self) -> ContentType {
214        self.content_type
215    }
216
217    /// Set the ciphertext.
218    #[cfg(test)]
219    pub(crate) fn set_ciphertext(&mut self, ciphertext: Vec<u8>) {
220        self.ciphertext = ciphertext.into();
221    }
222}
223
224// === Helper structs ===
225
226/// PrivateMessageContent
227///
228/// ```c
229/// // draft-ietf-mls-protocol-17
230/// struct {
231///     select (PrivateMessage.content_type) {
232///         case application:
233///           opaque application_data<V>;
234///
235///         case proposal:
236///           Proposal proposal;
237///
238///         case commit:
239///           Commit commit;
240///     }
241///
242///     FramedContentAuthData auth;
243///     opaque padding[length_of_padding];
244/// } PrivateMessageContent;
245/// ```
246#[derive(Debug, Clone)]
247pub(crate) struct PrivateMessageContentIn {
248    // The `content` field is serialized and deserialized manually without the
249    // `content_type`, which is not part of the struct as per MLS spec. See the
250    // implementation of `TlsSerialize` for `PrivateMessageContentIn`, as well
251    // as `deserialize_ciphertext_content`.
252    pub(crate) content: FramedContentBodyIn,
253    pub(crate) auth: FramedContentAuthData,
254}
255
256// The following `From` implementation( breaks abstraction layers and MUST
257// NOT be made available outside of tests or "test-utils".
258#[cfg(any(feature = "test-utils", test))]
259impl From<PrivateMessageIn> for PrivateMessage {
260    fn from(value: PrivateMessageIn) -> Self {
261        Self {
262            group_id: value.group_id,
263            epoch: value.epoch,
264            content_type: value.content_type,
265            authenticated_data: value.authenticated_data,
266            encrypted_sender_data: value.encrypted_sender_data,
267            ciphertext: value.ciphertext,
268        }
269    }
270}
271
272#[cfg(any(feature = "test-utils", test))]
273impl From<PrivateMessage> for PrivateMessageIn {
274    fn from(value: PrivateMessage) -> Self {
275        Self {
276            group_id: value.group_id,
277            epoch: value.epoch,
278            content_type: value.content_type,
279            authenticated_data: value.authenticated_data,
280            encrypted_sender_data: value.encrypted_sender_data,
281            ciphertext: value.ciphertext,
282        }
283    }
284}