Skip to main content

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