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                MessageDecryptionError::AeadError
137            })?;
138        log_content!(
139            trace,
140            "  Successfully decrypted PublicMessage bytes: {:x?}",
141            private_message_content_bytes
142        );
143        deserialize_ciphertext_content(
144            &mut private_message_content_bytes.as_slice(),
145            self.content_type(),
146        )
147        .map_err(|_| MessageDecryptionError::MalformedContent)
148    }
149
150    /// This function decrypts a [`PrivateMessage`] into a
151    /// [`VerifiableAuthenticatedContent`]. In order to get an
152    /// [`FramedContent`] the result must be verified.
153    pub(crate) fn to_verifiable_content(
154        &self,
155        ciphersuite: Ciphersuite,
156        crypto: &impl OpenMlsCrypto,
157        message_secrets: &mut MessageSecrets,
158        sender_index: LeafNodeIndex,
159        sender_ratchet_configuration: &SenderRatchetConfiguration,
160        sender_data: MlsSenderData,
161    ) -> Result<VerifiableAuthenticatedContentIn, MessageDecryptionError> {
162        let secret_type = SecretType::from(&self.content_type);
163        // Extract generation and key material for encryption
164        let (ratchet_key, ratchet_nonce) = message_secrets
165            .secret_tree_mut()
166            .secret_for_decryption(
167                ciphersuite,
168                crypto,
169                sender_index,
170                secret_type,
171                sender_data.generation,
172                sender_ratchet_configuration,
173            )
174            .map_err(|e| {
175                log::error!(
176                    "  Ciphertext generation out of bounds {}\n\t{e:?}",
177                    sender_data.generation
178                );
179                MessageDecryptionError::SecretTreeError(e)
180            })?;
181        // Prepare the nonce by xoring with the reuse guard.
182        let prepared_nonce = ratchet_nonce.xor_with_reuse_guard(&sender_data.reuse_guard);
183        let private_message_content = self.decrypt(crypto, ratchet_key, &prepared_nonce)?;
184
185        // Extract sender. The sender type is always of type Member for PrivateMessage.
186        let sender = Sender::from_sender_data(sender_data);
187        log_content!(
188            trace,
189            "  Successfully decoded PublicMessage with: {:x?}",
190            private_message_content.content
191        );
192
193        let verifiable = VerifiableAuthenticatedContentIn::new(
194            WireFormat::PrivateMessage,
195            FramedContentIn {
196                group_id: self.group_id.clone(),
197                epoch: self.epoch,
198                sender,
199                authenticated_data: self.authenticated_data.clone(),
200                body: private_message_content.content,
201            },
202            Some(message_secrets.serialized_context().to_vec()),
203            private_message_content.auth,
204        );
205        Ok(verifiable)
206    }
207
208    /// Get the `group_id` in the `PrivateMessage`.
209    pub(crate) fn group_id(&self) -> &GroupId {
210        &self.group_id
211    }
212
213    /// Get the `epoch` in the `PrivateMessage`.
214    pub(crate) fn epoch(&self) -> GroupEpoch {
215        self.epoch
216    }
217
218    /// Get the `content_type` in the `PrivateMessage`.
219    pub(crate) fn content_type(&self) -> ContentType {
220        self.content_type
221    }
222
223    /// Set the ciphertext.
224    #[cfg(test)]
225    pub(crate) fn set_ciphertext(&mut self, ciphertext: Vec<u8>) {
226        self.ciphertext = ciphertext.into();
227    }
228}
229
230// === Helper structs ===
231
232/// PrivateMessageContent
233///
234/// ```c
235/// // draft-ietf-mls-protocol-17
236/// struct {
237///     select (PrivateMessage.content_type) {
238///         case application:
239///           opaque application_data<V>;
240///
241///         case proposal:
242///           Proposal proposal;
243///
244///         case commit:
245///           Commit commit;
246///     }
247///
248///     FramedContentAuthData auth;
249///     opaque padding[length_of_padding];
250/// } PrivateMessageContent;
251/// ```
252#[derive(Debug, Clone)]
253pub(crate) struct PrivateMessageContentIn {
254    // The `content` field is serialized and deserialized manually without the
255    // `content_type`, which is not part of the struct as per MLS spec. See the
256    // implementation of `TlsSerialize` for `PrivateMessageContentIn`, as well
257    // as `deserialize_ciphertext_content`.
258    pub(crate) content: FramedContentBodyIn,
259    pub(crate) auth: FramedContentAuthData,
260}
261
262// The following `From` implementation( breaks abstraction layers and MUST
263// NOT be made available outside of tests or "test-utils".
264#[cfg(any(feature = "test-utils", test))]
265impl From<PrivateMessageIn> for PrivateMessage {
266    fn from(value: PrivateMessageIn) -> Self {
267        Self {
268            group_id: value.group_id,
269            epoch: value.epoch,
270            content_type: value.content_type,
271            authenticated_data: value.authenticated_data,
272            encrypted_sender_data: value.encrypted_sender_data,
273            ciphertext: value.ciphertext,
274        }
275    }
276}
277
278#[cfg(any(feature = "test-utils", test))]
279impl From<PrivateMessage> for PrivateMessageIn {
280    fn from(value: PrivateMessage) -> Self {
281        Self {
282            group_id: value.group_id,
283            epoch: value.epoch,
284            content_type: value.content_type,
285            authenticated_data: value.authenticated_data,
286            encrypted_sender_data: value.encrypted_sender_data,
287            ciphertext: value.ciphertext,
288        }
289    }
290}