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
72 .leaves_for_epoch(message_epoch)
73 .get(&leaf_node_index)
74 .map(|&member| CredentialWithKey::from(member))
75 } else {
76 None
77 }
78 };
79
80 let CredentialWithKey {
88 credential,
89 signature_key,
90 } = decrypted_message.credential(
91 look_up_credential_with_key,
92 self.group_context().extensions().external_senders(),
93 )?;
94 let signature_public_key = OpenMlsSignaturePublicKey::from_signature_key(
95 signature_key,
96 self.ciphersuite().signature_algorithm(),
97 );
98
99 let sender_context = match decrypted_message.sender() {
102 Sender::Member(leaf_index) => Some(SenderContext::Member((
103 self.group_id().clone(),
104 *leaf_index,
105 ))),
106 Sender::NewMemberCommit => Some(SenderContext::ExternalCommit {
107 group_id: self.group_id().clone(),
108 leftmost_blank_index: self.treesync().free_leaf_index(),
109 self_removes_in_store: self.proposal_store.self_removes(),
110 }),
111 Sender::External(_) | Sender::NewMemberProposal => None,
112 };
113
114 Ok(UnverifiedMessage::from_decrypted_message(
115 decrypted_message,
116 credential,
117 signature_public_key,
118 sender_context,
119 ))
120 }
121
122 pub fn process_message(
157 &self,
158 crypto: &impl OpenMlsCrypto,
159 message: impl Into<ProtocolMessage>,
160 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
161 let protocol_message = message.into();
162 self.validate_framing(&protocol_message)?;
166
167 let decrypted_message = match protocol_message {
168 ProtocolMessage::PrivateMessage(_) => {
169 return Err(PublicProcessMessageError::IncompatibleWireFormat)
170 }
171 ProtocolMessage::PublicMessage(public_message) => {
172 DecryptedMessage::from_inbound_public_message(
173 *public_message,
174 None,
175 self.group_context()
176 .tls_serialize_detached()
177 .map_err(LibraryError::missing_bound_check)?,
178 crypto,
179 self.ciphersuite(),
180 )?
181 }
182 };
183
184 let unverified_message = self
185 .parse_message(decrypted_message, None)
186 .map_err(PublicProcessMessageError::from)?;
187 self.process_unverified_message(crypto, unverified_message)
188 }
189}
190
191impl PublicGroup {
192 pub(crate) fn process_unverified_message(
219 &self,
220 crypto: &impl OpenMlsCrypto,
221 unverified_message: UnverifiedMessage,
222 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
223 let (content, credential) =
228 unverified_message.verify(self.ciphersuite(), crypto, self.version())?;
229
230 match content.sender() {
231 Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => {
232 self.process_internal_authenticated_content(crypto, content, credential)
233 }
234 Sender::External(_) => {
235 self.process_external_authenticated_content(crypto, content, credential)
236 }
237 }
238 }
239
240 #[cfg(feature = "extensions-draft-08")]
267 pub fn process_unverified_message_with_app_data_updates(
268 &self,
269 crypto: &impl OpenMlsCrypto,
270 unverified_message: UnverifiedMessage,
271 app_data_dict_updates: Option<AppDataUpdates>,
272 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
273 let (content, credential) =
278 unverified_message.verify(self.ciphersuite(), crypto, self.version())?;
279
280 match content.sender() {
281 Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => self
282 .process_internal_authenticated_content_with_app_data_updates(
283 crypto,
284 content,
285 credential,
286 app_data_dict_updates,
287 ),
288 Sender::External(_) => {
289 self.process_external_authenticated_content(crypto, content, credential)
290 }
291 }
292 }
293
294 fn process_internal_authenticated_content(
295 &self,
296 crypto: &impl OpenMlsCrypto,
297 content: AuthenticatedContent,
298 credential: Credential,
299 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
300 let sender = content.sender().clone();
301 let authenticated_data = content.authenticated_data().to_owned();
302
303 let content = match content.content() {
304 FramedContentBody::Application(application_message) => {
305 ProcessedMessageContent::ApplicationMessage(ApplicationMessage::new(
306 application_message.as_slice().to_owned(),
307 ))
308 }
309 FramedContentBody::Proposal(_) => {
310 let proposal = Box::new(QueuedProposal::from_authenticated_content_by_ref(
311 self.ciphersuite(),
312 crypto,
313 content,
314 )?);
315 if matches!(sender, Sender::NewMemberProposal) {
316 ProcessedMessageContent::ExternalJoinProposalMessage(proposal)
317 } else {
318 ProcessedMessageContent::ProposalMessage(proposal)
319 }
320 }
321 FramedContentBody::Commit(_) => {
322 let staged_commit = self.stage_commit(&content, crypto)?;
323 ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit))
324 }
325 };
326
327 Ok(ProcessedMessage::new(
328 self.group_id().clone(),
329 self.group_context().epoch(),
330 sender,
331 authenticated_data,
332 content,
333 credential,
334 ))
335 }
336
337 #[cfg(feature = "extensions-draft-08")]
338 fn process_internal_authenticated_content_with_app_data_updates(
339 &self,
340 crypto: &impl OpenMlsCrypto,
341 content: AuthenticatedContent,
342 credential: Credential,
343 app_data_dict_updates: Option<AppDataUpdates>,
344 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
345 let sender = content.sender().clone();
346 let authenticated_data = content.authenticated_data().to_owned();
347
348 debug_assert!(matches!(
349 sender,
350 Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal
351 ));
352
353 let content = match content.content() {
354 FramedContentBody::Application(application_message) => {
355 ProcessedMessageContent::ApplicationMessage(ApplicationMessage::new(
356 application_message.as_slice().to_owned(),
357 ))
358 }
359 FramedContentBody::Proposal(_) => {
360 let proposal = Box::new(QueuedProposal::from_authenticated_content_by_ref(
361 self.ciphersuite(),
362 crypto,
363 content,
364 )?);
365 if matches!(sender, Sender::NewMemberProposal) {
366 ProcessedMessageContent::ExternalJoinProposalMessage(proposal)
367 } else {
368 ProcessedMessageContent::ProposalMessage(proposal)
369 }
370 }
371 FramedContentBody::Commit(_) => {
372 let staged_commit = self.stage_commit_with_app_data_updates(
373 &content,
374 crypto,
375 app_data_dict_updates,
376 )?;
377
378 ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit))
379 }
380 };
381
382 Ok(ProcessedMessage::new(
383 self.group_id().clone(),
384 self.group_context().epoch(),
385 sender,
386 authenticated_data,
387 content,
388 credential,
389 ))
390 }
391
392 fn process_external_authenticated_content(
393 &self,
394 crypto: &impl OpenMlsCrypto,
395 content: AuthenticatedContent,
396 credential: Credential,
397 ) -> Result<ProcessedMessage, PublicProcessMessageError> {
398 let sender = content.sender().clone();
399 let data = content.authenticated_data().to_owned();
400
401 debug_assert!(matches!(sender, Sender::External(_)));
402
403 match content.content() {
405 FramedContentBody::Application(_) => {
406 Err(PublicProcessMessageError::UnauthorizedExternalApplicationMessage)
407 }
408 FramedContentBody::Proposal(Proposal::GroupContextExtensions(_)) => {
410 let content = ProcessedMessageContent::ProposalMessage(Box::new(
411 QueuedProposal::from_authenticated_content_by_ref(
412 self.ciphersuite(),
413 crypto,
414 content,
415 )?,
416 ));
417 Ok(ProcessedMessage::new(
418 self.group_id().clone(),
419 self.group_context().epoch(),
420 sender,
421 data,
422 content,
423 credential,
424 ))
425 }
426
427 FramedContentBody::Proposal(Proposal::Remove(_)) => {
428 let content = ProcessedMessageContent::ProposalMessage(Box::new(
429 QueuedProposal::from_authenticated_content_by_ref(
430 self.ciphersuite(),
431 crypto,
432 content,
433 )?,
434 ));
435 Ok(ProcessedMessage::new(
436 self.group_id().clone(),
437 self.group_context().epoch(),
438 sender,
439 data,
440 content,
441 credential,
442 ))
443 }
444 FramedContentBody::Proposal(Proposal::Add(_)) => {
445 let content = ProcessedMessageContent::ProposalMessage(Box::new(
446 QueuedProposal::from_authenticated_content_by_ref(
447 self.ciphersuite(),
448 crypto,
449 content,
450 )?,
451 ));
452 Ok(ProcessedMessage::new(
453 self.group_id().clone(),
454 self.group_context().epoch(),
455 sender,
456 data,
457 content,
458 credential,
459 ))
460 }
461 FramedContentBody::Proposal(_) => {
463 Err(PublicProcessMessageError::UnsupportedProposalType)
464 }
465 FramedContentBody::Commit(_) => {
466 Err(PublicProcessMessageError::UnauthorizedExternalCommitMessage)
467 }
468 }
469 }
470}