openmls/group/public_group/
process.rs

1//! This module contains the implementation of the processing functions for
2//! public groups.
3
4use openmls_traits::crypto::OpenMlsCrypto;
5use tls_codec::Serialize;
6
7use crate::{
8    ciphersuite::OpenMlsSignaturePublicKey,
9    credentials::CredentialWithKey,
10    error::LibraryError,
11    framing::{
12        mls_content::FramedContentBody, ApplicationMessage, DecryptedMessage, ProcessedMessage,
13        ProcessedMessageContent, ProtocolMessage, Sender, SenderContext, UnverifiedMessage,
14    },
15    group::{
16        errors::ValidationError, mls_group::errors::ProcessMessageError,
17        past_secrets::MessageSecretsStore, proposal_store::QueuedProposal,
18    },
19    messages::proposals::{Proposal, ProposalOrRefType},
20};
21
22use super::PublicGroup;
23
24impl PublicGroup {
25    /// This function is used to parse messages from the DS.
26    /// It checks for syntactic errors and makes some semantic checks as well.
27    /// If the input is a [PrivateMessage] message, it will be decrypted.
28    /// Returns an [UnverifiedMessage] that can be inspected and later processed in
29    /// [Self::process_unverified_message()].
30    /// Checks the following semantic validation:
31    ///  - ValSem002
32    ///  - ValSem003
33    ///  - ValSem004
34    ///  - ValSem005
35    ///  - ValSem006
36    ///  - ValSem007
37    ///  - ValSem009
38    ///  - ValSem112
39    ///  - ValSem245
40    pub(crate) fn parse_message<'a>(
41        &self,
42        decrypted_message: DecryptedMessage,
43        message_secrets_store_option: impl Into<Option<&'a MessageSecretsStore>>,
44    ) -> Result<UnverifiedMessage, ValidationError> {
45        let message_secrets_store_option = message_secrets_store_option.into();
46        let verifiable_content = decrypted_message.verifiable_content();
47
48        // Checks the following semantic validation:
49        //  - ValSem004
50        //  - ValSem005
51        //  - ValSem009
52        self.validate_verifiable_content(verifiable_content, message_secrets_store_option)?;
53
54        let message_epoch = verifiable_content.epoch();
55
56        // Depending on the epoch of the message, use the correct set of leaf nodes for getting the
57        // credential and signature key for the member with given index.
58        let look_up_credential_with_key = |leaf_node_index| {
59            if message_epoch == self.group_context().epoch() {
60                self.treesync()
61                    .leaf(leaf_node_index)
62                    .map(CredentialWithKey::from)
63            } else if let Some(store) = message_secrets_store_option {
64                store
65                    .leaves_for_epoch(message_epoch)
66                    .get(leaf_node_index.u32() as usize)
67                    .map(CredentialWithKey::from)
68            } else {
69                None
70            }
71        };
72
73        // Extract the credential if the sender is a member or a new member.
74        // Checks the following semantic validation:
75        //  - ValSem112
76        //  - ValSem245
77        //  - Prepares ValSem246 by setting the right credential. The remainder
78        //    of ValSem246 is validated as part of ValSem010.
79        // External senders are not supported yet #106/#151.
80        let CredentialWithKey {
81            credential,
82            signature_key,
83        } = decrypted_message.credential(
84            look_up_credential_with_key,
85            self.group_context().extensions().external_senders(),
86        )?;
87        let signature_public_key = OpenMlsSignaturePublicKey::from_signature_key(
88            signature_key,
89            self.ciphersuite().signature_algorithm(),
90        );
91
92        // For commit messages, we need to check if the sender is a member or a
93        // new member and set the tree position accordingly.
94        let sender_context = match decrypted_message.sender() {
95            Sender::Member(leaf_index) => Some(SenderContext::Member((
96                self.group_id().clone(),
97                *leaf_index,
98            ))),
99            Sender::NewMemberCommit => Some(SenderContext::ExternalCommit((
100                self.group_id().clone(),
101                self.treesync().free_leaf_index(),
102            ))),
103            Sender::External(_) | Sender::NewMemberProposal => None,
104        };
105
106        Ok(UnverifiedMessage::from_decrypted_message(
107            decrypted_message,
108            credential,
109            signature_public_key,
110            sender_context,
111        ))
112    }
113
114    /// This function is used to parse messages from the DS. It checks for
115    /// syntactic errors and does semantic validation as well. It returns a
116    /// [ProcessedMessage] enum. Checks the following semantic validation:
117    ///  - ValSem002
118    ///  - ValSem003
119    ///  - ValSem004
120    ///  - ValSem005
121    ///  - ValSem006
122    ///  - ValSem007
123    ///  - ValSem008
124    ///  - ValSem009
125    ///  - ValSem010
126    ///  - ValSem101
127    ///  - ValSem102
128    ///  - ValSem104
129    ///  - ValSem106
130    ///  - ValSem107
131    ///  - ValSem108
132    ///  - ValSem110
133    ///  - ValSem111
134    ///  - ValSem112
135    ///  - ValSem200
136    ///  - ValSem201
137    ///  - ValSem202: Path must be the right length
138    ///  - ValSem203: Path secrets must decrypt correctly
139    ///  - ValSem204: Public keys from Path must be verified and match the
140    ///    private keys from the direct path
141    ///  - ValSem205
142    ///  - ValSem240
143    ///  - ValSem241
144    ///  - ValSem242
145    ///  - ValSem244
146    ///  - ValSem245
147    ///  - ValSem246 (as part of ValSem010)
148    pub fn process_message(
149        &self,
150        crypto: &impl OpenMlsCrypto,
151        message: impl Into<ProtocolMessage>,
152    ) -> Result<ProcessedMessage, ProcessMessageError> {
153        let protocol_message = message.into();
154        // Checks the following semantic validation:
155        //  - ValSem002
156        //  - ValSem003
157        self.validate_framing(&protocol_message)?;
158
159        let decrypted_message = match protocol_message {
160            ProtocolMessage::PrivateMessage(_) => {
161                return Err(ProcessMessageError::IncompatibleWireFormat)
162            }
163            ProtocolMessage::PublicMessage(public_message) => {
164                DecryptedMessage::from_inbound_public_message(
165                    *public_message,
166                    None,
167                    self.group_context()
168                        .tls_serialize_detached()
169                        .map_err(LibraryError::missing_bound_check)?,
170                    crypto,
171                    self.ciphersuite(),
172                )?
173            }
174        };
175
176        let unverified_message = self
177            .parse_message(decrypted_message, None)
178            .map_err(ProcessMessageError::from)?;
179        self.process_unverified_message(crypto, unverified_message)
180    }
181}
182
183impl PublicGroup {
184    /// This processing function does most of the semantic verifications.
185    /// It returns a [ProcessedMessage] enum.
186    /// Checks the following semantic validation:
187    ///  - ValSem008
188    ///  - ValSem010
189    ///  - ValSem101
190    ///  - ValSem102
191    ///  - ValSem104
192    ///  - ValSem106
193    ///  - ValSem107
194    ///  - ValSem108
195    ///  - ValSem110
196    ///  - ValSem111
197    ///  - ValSem112
198    ///  - ValSem200
199    ///  - ValSem201
200    ///  - ValSem202: Path must be the right length
201    ///  - ValSem203: Path secrets must decrypt correctly
202    ///  - ValSem204: Public keys from Path must be verified and match the
203    ///    private keys from the direct path
204    ///  - ValSem205
205    ///  - ValSem240
206    ///  - ValSem241
207    ///  - ValSem242
208    ///  - ValSem244
209    ///  - ValSem246 (as part of ValSem010)
210    pub(crate) fn process_unverified_message(
211        &self,
212        crypto: &impl OpenMlsCrypto,
213        unverified_message: UnverifiedMessage,
214    ) -> Result<ProcessedMessage, ProcessMessageError> {
215        // Checks the following semantic validation:
216        //  - ValSem010
217        //  - ValSem246 (as part of ValSem010)
218        //  - https://validation.openmls.tech/#valn1203
219        let (content, credential) =
220            unverified_message.verify(self.ciphersuite(), crypto, self.version())?;
221
222        match content.sender() {
223            Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => {
224                let sender = content.sender().clone();
225                let authenticated_data = content.authenticated_data().to_owned();
226
227                let content = match content.content() {
228                    FramedContentBody::Application(application_message) => {
229                        ProcessedMessageContent::ApplicationMessage(ApplicationMessage::new(
230                            application_message.as_slice().to_owned(),
231                        ))
232                    }
233                    FramedContentBody::Proposal(_) => {
234                        let proposal = Box::new(QueuedProposal::from_authenticated_content_by_ref(
235                            self.ciphersuite(),
236                            crypto,
237                            content,
238                        )?);
239                        if matches!(sender, Sender::NewMemberProposal) {
240                            ProcessedMessageContent::ExternalJoinProposalMessage(proposal)
241                        } else {
242                            ProcessedMessageContent::ProposalMessage(proposal)
243                        }
244                    }
245                    FramedContentBody::Commit(_) => {
246                        let staged_commit = self.stage_commit(&content, crypto)?;
247                        ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit))
248                    }
249                };
250
251                Ok(ProcessedMessage::new(
252                    self.group_id().clone(),
253                    self.group_context().epoch(),
254                    sender,
255                    authenticated_data,
256                    content,
257                    credential,
258                ))
259            }
260            Sender::External(_) => {
261                let sender = content.sender().clone();
262                let data = content.authenticated_data().to_owned();
263                match content.content() {
264                    FramedContentBody::Application(_) => {
265                        Err(ProcessMessageError::UnauthorizedExternalApplicationMessage)
266                    }
267                    FramedContentBody::Proposal(Proposal::Remove(_)) => {
268                        let content = ProcessedMessageContent::ProposalMessage(Box::new(
269                            QueuedProposal::from_authenticated_content_by_ref(
270                                self.ciphersuite(),
271                                crypto,
272                                content,
273                            )?,
274                        ));
275                        Ok(ProcessedMessage::new(
276                            self.group_id().clone(),
277                            self.group_context().epoch(),
278                            sender,
279                            data,
280                            content,
281                            credential,
282                        ))
283                    }
284                    FramedContentBody::Proposal(Proposal::Add(_)) => {
285                        let content = ProcessedMessageContent::ProposalMessage(Box::new(
286                            QueuedProposal::from_authenticated_content(
287                                self.ciphersuite(),
288                                crypto,
289                                content,
290                                ProposalOrRefType::Proposal,
291                            )?,
292                        ));
293                        Ok(ProcessedMessage::new(
294                            self.group_id().clone(),
295                            self.group_context().epoch(),
296                            sender,
297                            data,
298                            content,
299                            credential,
300                        ))
301                    }
302                    // TODO #151/#106
303                    FramedContentBody::Proposal(_) => {
304                        Err(ProcessMessageError::UnsupportedProposalType)
305                    }
306                    FramedContentBody::Commit(_) => {
307                        Err(ProcessMessageError::UnauthorizedExternalCommitMessage)
308                    }
309                }
310            }
311        }
312    }
313}