openmls/group/public_group/
process.rs1use openmls_traits::crypto::OpenMlsCrypto;
5use tls_codec::Serialize;
6
7use crate::{
8 ciphersuite::OpenMlsSignaturePublicKey,
9 credentials::{Credential, CredentialWithKey},
10 error::LibraryError,
11 framing::{
12 mls_auth_content::AuthenticatedContent, mls_content::FramedContentBody, ApplicationMessage,
13 DecryptedMessage, ProcessedMessage, ProcessedMessageContent, ProtocolMessage, Sender,
14 SenderContext, UnverifiedMessage,
15 },
16 group::{
17 errors::ValidationError, past_secrets::MessageSecretsStore, proposal_store::QueuedProposal,
18 PublicProcessMessageError,
19 },
20 messages::proposals::Proposal,
21};
22
23#[cfg(feature = "extensions-draft-08")]
24use crate::prelude::processing::AppDataUpdates;
25
26use super::PublicGroup;
27
28impl PublicGroup {
29 pub(crate) fn parse_message<'a>(
45 &self,
46 decrypted_message: DecryptedMessage,
47 message_secrets_store_option: impl Into<Option<&'a MessageSecretsStore>>,
48 ) -> Result<UnverifiedMessage, ValidationError> {
49 let message_secrets_store_option = message_secrets_store_option.into();
50 let verifiable_content = decrypted_message.verifiable_content();
51
52 self.validate_verifiable_content(verifiable_content, message_secrets_store_option)?;
57
58 let message_epoch = verifiable_content.epoch();
59
60 let look_up_credential_with_key = |leaf_node_index| {
63 if message_epoch == self.group_context().epoch() {
64 self.treesync()
65 .leaf(leaf_node_index)
66 .map(CredentialWithKey::from)
67 } else if let Some(store) = message_secrets_store_option {
68 store
69 .leaves_for_epoch(message_epoch)
70 .get(leaf_node_index.u32() as usize)
71 .map(CredentialWithKey::from)
72 } else {
73 None
74 }
75 };
76
77 let CredentialWithKey {
85 credential,
86 signature_key,
87 } = decrypted_message.credential(
88 look_up_credential_with_key,
89 self.group_context().extensions().external_senders(),
90 )?;
91 let signature_public_key = OpenMlsSignaturePublicKey::from_signature_key(
92 signature_key,
93 self.ciphersuite().signature_algorithm(),
94 );
95
96 let sender_context = match decrypted_message.sender() {
99 Sender::Member(leaf_index) => Some(SenderContext::Member((
100 self.group_id().clone(),
101 *leaf_index,
102 ))),
103 Sender::NewMemberCommit => Some(SenderContext::ExternalCommit {
104 group_id: self.group_id().clone(),
105 leftmost_blank_index: self.treesync().free_leaf_index(),
106 self_removes_in_store: self.proposal_store.self_removes(),
107 }),
108 Sender::External(_) | Sender::NewMemberProposal => None,
109 };
110
111 Ok(UnverifiedMessage::from_decrypted_message(
112 decrypted_message,
113 credential,
114 signature_public_key,
115 sender_context,
116 ))
117 }
118
119 pub fn process_message(
154 &self,
155 crypto: &impl OpenMlsCrypto,
156 message: impl Into<ProtocolMessage>,
157 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
158 let protocol_message = message.into();
159 self.validate_framing(&protocol_message)?;
163
164 let decrypted_message = match protocol_message {
165 ProtocolMessage::PrivateMessage(_) => {
166 return Err(PublicProcessMessageError::IncompatibleWireFormat)
167 }
168 ProtocolMessage::PublicMessage(public_message) => {
169 DecryptedMessage::from_inbound_public_message(
170 *public_message,
171 None,
172 self.group_context()
173 .tls_serialize_detached()
174 .map_err(LibraryError::missing_bound_check)?,
175 crypto,
176 self.ciphersuite(),
177 )?
178 }
179 };
180
181 let unverified_message = self
182 .parse_message(decrypted_message, None)
183 .map_err(PublicProcessMessageError::from)?;
184 self.process_unverified_message(crypto, unverified_message)
185 }
186}
187
188impl PublicGroup {
189 pub(crate) fn process_unverified_message(
216 &self,
217 crypto: &impl OpenMlsCrypto,
218 unverified_message: UnverifiedMessage,
219 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
220 let (content, credential) =
225 unverified_message.verify(self.ciphersuite(), crypto, self.version())?;
226
227 match content.sender() {
228 Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => {
229 self.process_internal_authenticated_content(crypto, content, credential)
230 }
231 Sender::External(_) => {
232 self.process_external_authenticated_content(crypto, content, credential)
233 }
234 }
235 }
236
237 #[cfg(feature = "extensions-draft-08")]
264 pub fn process_unverified_message_with_app_data_updates(
265 &self,
266 crypto: &impl OpenMlsCrypto,
267 unverified_message: UnverifiedMessage,
268 app_data_dict_updates: Option<AppDataUpdates>,
269 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
270 let (content, credential) =
275 unverified_message.verify(self.ciphersuite(), crypto, self.version())?;
276
277 match content.sender() {
278 Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => self
279 .process_internal_authenticated_content_with_app_data_updates(
280 crypto,
281 content,
282 credential,
283 app_data_dict_updates,
284 ),
285 Sender::External(_) => {
286 self.process_external_authenticated_content(crypto, content, credential)
287 }
288 }
289 }
290
291 fn process_internal_authenticated_content(
292 &self,
293 crypto: &impl OpenMlsCrypto,
294 content: AuthenticatedContent,
295 credential: Credential,
296 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
297 let sender = content.sender().clone();
298 let authenticated_data = content.authenticated_data().to_owned();
299
300 let content = match content.content() {
301 FramedContentBody::Application(application_message) => {
302 ProcessedMessageContent::ApplicationMessage(ApplicationMessage::new(
303 application_message.as_slice().to_owned(),
304 ))
305 }
306 FramedContentBody::Proposal(_) => {
307 let proposal = Box::new(QueuedProposal::from_authenticated_content_by_ref(
308 self.ciphersuite(),
309 crypto,
310 content,
311 )?);
312 if matches!(sender, Sender::NewMemberProposal) {
313 ProcessedMessageContent::ExternalJoinProposalMessage(proposal)
314 } else {
315 ProcessedMessageContent::ProposalMessage(proposal)
316 }
317 }
318 FramedContentBody::Commit(_) => {
319 let staged_commit = self.stage_commit(&content, crypto)?;
320 ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit))
321 }
322 };
323
324 Ok(ProcessedMessage::new(
325 self.group_id().clone(),
326 self.group_context().epoch(),
327 sender,
328 authenticated_data,
329 content,
330 credential,
331 ))
332 }
333
334 #[cfg(feature = "extensions-draft-08")]
335 fn process_internal_authenticated_content_with_app_data_updates(
336 &self,
337 crypto: &impl OpenMlsCrypto,
338 content: AuthenticatedContent,
339 credential: Credential,
340 app_data_dict_updates: Option<AppDataUpdates>,
341 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
342 let sender = content.sender().clone();
343 let authenticated_data = content.authenticated_data().to_owned();
344
345 debug_assert!(matches!(
346 sender,
347 Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal
348 ));
349
350 let content = match content.content() {
351 FramedContentBody::Application(application_message) => {
352 ProcessedMessageContent::ApplicationMessage(ApplicationMessage::new(
353 application_message.as_slice().to_owned(),
354 ))
355 }
356 FramedContentBody::Proposal(_) => {
357 let proposal = Box::new(QueuedProposal::from_authenticated_content_by_ref(
358 self.ciphersuite(),
359 crypto,
360 content,
361 )?);
362 if matches!(sender, Sender::NewMemberProposal) {
363 ProcessedMessageContent::ExternalJoinProposalMessage(proposal)
364 } else {
365 ProcessedMessageContent::ProposalMessage(proposal)
366 }
367 }
368 FramedContentBody::Commit(_) => {
369 let staged_commit = self.stage_commit_with_app_data_updates(
370 &content,
371 crypto,
372 app_data_dict_updates,
373 )?;
374
375 ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit))
376 }
377 };
378
379 Ok(ProcessedMessage::new(
380 self.group_id().clone(),
381 self.group_context().epoch(),
382 sender,
383 authenticated_data,
384 content,
385 credential,
386 ))
387 }
388
389 fn process_external_authenticated_content(
390 &self,
391 crypto: &impl OpenMlsCrypto,
392 content: AuthenticatedContent,
393 credential: Credential,
394 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
395 let sender = content.sender().clone();
396 let data = content.authenticated_data().to_owned();
397
398 debug_assert!(matches!(sender, Sender::External(_)));
399
400 match content.content() {
402 FramedContentBody::Application(_) => {
403 Err(PublicProcessMessageError::UnauthorizedExternalApplicationMessage)
404 }
405 FramedContentBody::Proposal(Proposal::GroupContextExtensions(_)) => {
407 let content = ProcessedMessageContent::ProposalMessage(Box::new(
408 QueuedProposal::from_authenticated_content_by_ref(
409 self.ciphersuite(),
410 crypto,
411 content,
412 )?,
413 ));
414 Ok(ProcessedMessage::new(
415 self.group_id().clone(),
416 self.group_context().epoch(),
417 sender,
418 data,
419 content,
420 credential,
421 ))
422 }
423
424 FramedContentBody::Proposal(Proposal::Remove(_)) => {
425 let content = ProcessedMessageContent::ProposalMessage(Box::new(
426 QueuedProposal::from_authenticated_content_by_ref(
427 self.ciphersuite(),
428 crypto,
429 content,
430 )?,
431 ));
432 Ok(ProcessedMessage::new(
433 self.group_id().clone(),
434 self.group_context().epoch(),
435 sender,
436 data,
437 content,
438 credential,
439 ))
440 }
441 FramedContentBody::Proposal(Proposal::Add(_)) => {
442 let content = ProcessedMessageContent::ProposalMessage(Box::new(
443 QueuedProposal::from_authenticated_content_by_ref(
444 self.ciphersuite(),
445 crypto,
446 content,
447 )?,
448 ));
449 Ok(ProcessedMessage::new(
450 self.group_id().clone(),
451 self.group_context().epoch(),
452 sender,
453 data,
454 content,
455 credential,
456 ))
457 }
458 FramedContentBody::Proposal(_) => {
460 Err(PublicProcessMessageError::UnsupportedProposalType)
461 }
462 FramedContentBody::Commit(_) => {
463 Err(PublicProcessMessageError::UnauthorizedExternalCommitMessage)
464 }
465 }
466 }
467}