1use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite};
26use proposal_store::QueuedProposal;
27
28use crate::{
29 binary_tree::LeafNodeIndex,
30 ciphersuite::signable::Verifiable,
31 error::LibraryError,
32 extensions::ExternalSendersExtension,
33 group::{errors::ValidationError, mls_group::staged_commit::StagedCommit},
34 tree::sender_ratchet::SenderRatchetConfiguration,
35 versions::ProtocolVersion,
36};
37
38#[cfg(feature = "extensions-draft-08")]
39use crate::{
40 component::ComponentId, framing::safe_aad::SafeAad, messages::proposals_in::ProposalOrRefIn,
41};
42
43use super::{
44 mls_auth_content::AuthenticatedContent,
45 mls_auth_content_in::{AuthenticatedContentIn, VerifiableAuthenticatedContentIn},
46 private_message_in::PrivateMessageIn,
47 public_message_in::PublicMessageIn,
48 *,
49};
50
51#[derive(Debug)]
58pub(crate) struct DecryptedMessage {
59 verifiable_content: VerifiableAuthenticatedContentIn,
60 #[cfg(feature = "virtual-clients-draft")]
63 emulator_sender_leaf_index: Option<LeafNodeIndex>,
64}
65
66impl DecryptedMessage {
67 pub(crate) fn from_inbound_public_message<'a>(
69 public_message: PublicMessageIn,
70 message_secrets_option: impl Into<Option<&'a MessageSecrets>>,
71 serialized_context: Vec<u8>,
72 crypto: &impl OpenMlsCrypto,
73 ciphersuite: Ciphersuite,
74 ) -> Result<Self, ValidationError> {
75 if public_message.sender().is_member() {
76 if public_message.membership_tag().is_none() {
78 return Err(ValidationError::MissingMembershipTag);
79 }
80
81 if let Some(message_secrets) = message_secrets_option.into() {
82 public_message.verify_membership(
87 crypto,
88 ciphersuite,
89 message_secrets.membership_key(),
90 message_secrets.serialized_context(),
91 )?;
92 }
93 }
94
95 let verifiable_content = public_message.into_verifiable_content(serialized_context);
96
97 Self::from_verifiable_content(
100 verifiable_content,
101 #[cfg(feature = "virtual-clients-draft")]
102 None,
103 )
104 }
105
106 pub(crate) fn from_inbound_ciphertext(
109 ciphertext: PrivateMessageIn,
110 crypto: &impl OpenMlsCrypto,
111 group: &mut MlsGroup,
112 sender_ratchet_configuration: &SenderRatchetConfiguration,
113 #[cfg(feature = "virtual-clients-draft")] emulator_ctx: Option<
114 &crate::framing::private_message::EmulatorReuseGuardCtx<'_>,
115 >,
116 ) -> Result<Self, ValidationError> {
117 let ciphersuite = group.ciphersuite();
119 let (message_secrets, _old_leaves) = group
122 .message_secrets_and_leaves(ciphertext.epoch())
123 .map_err(MessageDecryptionError::SecretTreeError)?;
124 let sender_data = ciphertext.sender_data(message_secrets, crypto, ciphersuite)?;
125 #[cfg(not(feature = "virtual-clients-draft"))]
128 if sender_data.leaf_index == group.own_leaf_index() {
129 return Err(ValidationError::CannotDecryptOwnMessage);
130 }
131 #[cfg(feature = "virtual-clients-draft")]
132 let effective_emulator_ctx = match emulator_ctx {
133 Some(ctx) if sender_data.leaf_index == group.own_leaf_index() => Some(ctx),
134 _ => None,
135 };
136 let message_secrets = group
137 .message_secrets_for_epoch_mut(ciphertext.epoch())
138 .map_err(|_| MessageDecryptionError::AeadError)?;
139 let decrypted = ciphertext.to_verifiable_content(
140 ciphersuite,
141 crypto,
142 message_secrets,
143 sender_data.leaf_index,
144 sender_ratchet_configuration,
145 sender_data,
146 #[cfg(feature = "virtual-clients-draft")]
147 effective_emulator_ctx,
148 )?;
149 Self::from_verifiable_content(
150 decrypted.verifiable,
151 #[cfg(feature = "virtual-clients-draft")]
152 decrypted.emulator_sender_leaf_index,
153 )
154 }
155
156 fn from_verifiable_content(
161 verifiable_content: VerifiableAuthenticatedContentIn,
162 #[cfg(feature = "virtual-clients-draft")] emulator_sender_leaf_index: Option<LeafNodeIndex>,
163 ) -> Result<Self, ValidationError> {
164 if verifiable_content.content_type() == ContentType::Commit
166 && verifiable_content.confirmation_tag().is_none()
167 {
168 return Err(ValidationError::MissingConfirmationTag);
169 }
170 if verifiable_content.content_type() == ContentType::Application {
172 if verifiable_content.wire_format() != WireFormat::PrivateMessage {
173 return Err(ValidationError::UnencryptedApplicationMessage);
174 } else if !verifiable_content.sender().is_member() {
175 return Err(LibraryError::custom("Expected sender to be member.").into());
177 }
178 }
179 Ok(DecryptedMessage {
180 verifiable_content,
181 #[cfg(feature = "virtual-clients-draft")]
182 emulator_sender_leaf_index,
183 })
184 }
185
186 pub(crate) fn credential(
201 &self,
202 look_up_credential_with_key: impl Fn(LeafNodeIndex) -> Option<CredentialWithKey>,
203 external_senders: Option<&ExternalSendersExtension>,
204 ) -> Result<CredentialWithKey, ValidationError> {
205 let sender = self.sender();
206 match sender {
207 Sender::Member(leaf_index) => {
208 look_up_credential_with_key(*leaf_index).ok_or(ValidationError::UnknownMember)
210 }
211 Sender::External(index) => {
212 let sender = external_senders
213 .ok_or(ValidationError::NoExternalSendersExtension)?
214 .get(index.index())
215 .ok_or(ValidationError::UnauthorizedExternalSender)?;
216 Ok(CredentialWithKey {
217 credential: sender.credential().clone(),
218 signature_key: sender.signature_key().clone(),
219 })
220 }
221 Sender::NewMemberCommit | Sender::NewMemberProposal => {
222 self.verifiable_content.new_member_credential()
225 }
226 }
227 }
228
229 pub fn sender(&self) -> &Sender {
231 self.verifiable_content.sender()
232 }
233
234 pub(crate) fn verifiable_content(&self) -> &VerifiableAuthenticatedContentIn {
236 &self.verifiable_content
237 }
238}
239
240pub(crate) struct VerifiedMessage {
242 pub(crate) content: AuthenticatedContent,
243 pub(crate) credential: Credential,
244 #[cfg(feature = "virtual-clients-draft")]
245 pub(crate) emulator_sender_leaf_index: Option<LeafNodeIndex>,
246}
247
248#[derive(Debug, Clone)]
251pub(crate) enum SenderContext {
252 Member((GroupId, LeafNodeIndex)),
253 ExternalCommit {
254 group_id: GroupId,
255 leftmost_blank_index: LeafNodeIndex,
256 self_removes_in_store: Vec<SelfRemoveInStore>,
257 },
258}
259
260#[derive(Debug, Clone)]
266pub struct UnverifiedMessage {
267 verifiable_content: VerifiableAuthenticatedContentIn,
268 credential: Credential,
269 sender_pk: OpenMlsSignaturePublicKey,
270 sender_context: Option<SenderContext>,
271 #[cfg(feature = "virtual-clients-draft")]
273 emulator_sender_leaf_index: Option<LeafNodeIndex>,
274}
275
276impl UnverifiedMessage {
277 pub(crate) fn from_decrypted_message(
279 decrypted_message: DecryptedMessage,
280 credential: Credential,
281 sender_pk: OpenMlsSignaturePublicKey,
282 sender_context: Option<SenderContext>,
283 ) -> Self {
284 #[cfg(feature = "virtual-clients-draft")]
285 let emulator_sender_leaf_index = decrypted_message.emulator_sender_leaf_index;
286 UnverifiedMessage {
287 verifiable_content: decrypted_message.verifiable_content,
288 credential,
289 sender_pk,
290 sender_context,
291 #[cfg(feature = "virtual-clients-draft")]
292 emulator_sender_leaf_index,
293 }
294 }
295
296 pub(crate) fn verify(
298 self,
299 ciphersuite: Ciphersuite,
300 crypto: &impl OpenMlsCrypto,
301 protocol_version: ProtocolVersion,
302 ) -> Result<VerifiedMessage, ValidationError> {
303 let content: AuthenticatedContentIn = self
304 .verifiable_content
305 .verify(crypto, &self.sender_pk)
306 .map_err(|_| ValidationError::InvalidSignature)?;
307 let content =
310 content.validate(ciphersuite, crypto, self.sender_context, protocol_version)?;
311 Ok(VerifiedMessage {
312 content,
313 credential: self.credential,
314 #[cfg(feature = "virtual-clients-draft")]
315 emulator_sender_leaf_index: self.emulator_sender_leaf_index,
316 })
317 }
318
319 #[cfg(feature = "extensions-draft-08")]
321 pub fn committed_proposals(&self) -> Option<&[ProposalOrRefIn]> {
322 self.verifiable_content.committed_proposals()
323 }
324}
325
326#[derive(Debug)]
328pub struct ProcessedMessage {
329 group_id: GroupId,
330 epoch: GroupEpoch,
331 sender: Sender,
332 authenticated_data: Vec<u8>,
333 content: ProcessedMessageContent,
334 credential: Credential,
335 #[cfg(feature = "virtual-clients-draft")]
337 emulator_sender_leaf_index: Option<LeafNodeIndex>,
338 #[cfg(feature = "extensions-draft-08")]
341 safe_aad: Option<SafeAad>,
342 #[cfg(feature = "extensions-draft-08")]
345 safe_aad_prefix_len: usize,
346}
347
348impl ProcessedMessage {
349 pub(crate) fn new(
351 group_id: GroupId,
352 epoch: GroupEpoch,
353 sender: Sender,
354 authenticated_data: Vec<u8>,
355 content: ProcessedMessageContent,
356 credential: Credential,
357 #[cfg(feature = "virtual-clients-draft")] emulator_sender_leaf_index: Option<LeafNodeIndex>,
358 ) -> Self {
359 Self {
360 group_id,
361 epoch,
362 sender,
363 authenticated_data,
364 content,
365 credential,
366 #[cfg(feature = "virtual-clients-draft")]
367 emulator_sender_leaf_index,
368 #[cfg(feature = "extensions-draft-08")]
369 safe_aad: None,
370 #[cfg(feature = "extensions-draft-08")]
371 safe_aad_prefix_len: 0,
372 }
373 }
374
375 #[cfg(feature = "extensions-draft-08")]
381 pub(crate) fn try_attach_safe_aad(&mut self) -> Result<(), crate::framing::SafeAadError> {
382 let (safe_aad, prefix_len) =
383 crate::framing::safe_aad::parse_authenticated_data_prefix(&self.authenticated_data)?;
384 self.safe_aad = Some(safe_aad);
385 self.safe_aad_prefix_len = prefix_len;
386 Ok(())
387 }
388
389 #[cfg(feature = "extensions-draft-08")]
392 pub fn safe_aad(&self) -> Option<&SafeAad> {
393 self.safe_aad.as_ref()
394 }
395
396 #[cfg(feature = "extensions-draft-08")]
398 pub fn safe_aad_item(&self, component_id: crate::component::ComponentId) -> Option<&[u8]> {
399 self.safe_aad
400 .as_ref()
401 .and_then(|safe_aad| safe_aad.get(component_id))
402 }
403
404 #[cfg(feature = "extensions-draft-08")]
407 pub fn tail_aad(&self) -> &[u8] {
408 &self.authenticated_data[self.safe_aad_prefix_len..]
409 }
410
411 #[cfg(feature = "virtual-clients-draft")]
414 pub fn emulator_sender_leaf_index(&self) -> Option<LeafNodeIndex> {
415 self.emulator_sender_leaf_index
416 }
417
418 pub fn group_id(&self) -> &GroupId {
420 &self.group_id
421 }
422
423 pub fn epoch(&self) -> GroupEpoch {
425 self.epoch
426 }
427
428 pub fn sender(&self) -> &Sender {
430 &self.sender
431 }
432
433 pub fn aad(&self) -> &[u8] {
435 &self.authenticated_data
436 }
437
438 pub fn content(&self) -> &ProcessedMessageContent {
440 &self.content
441 }
442
443 pub fn into_content(self) -> ProcessedMessageContent {
445 self.content
446 }
447
448 pub fn credential(&self) -> &Credential {
450 &self.credential
451 }
452
453 #[cfg(feature = "extensions-draft-08")]
456 pub fn safe_export_secret<Crypto: OpenMlsCrypto>(
457 &mut self,
458 crypto: &Crypto,
459 component_id: ComponentId,
460 ) -> Result<Vec<u8>, ProcessedMessageSafeExportSecretError> {
461 if let ProcessedMessageContent::StagedCommitMessage(ref mut staged_commit) =
462 &mut self.content
463 {
464 let secret = staged_commit.safe_export_secret(crypto, component_id)?;
465 Ok(secret)
466 } else {
467 Err(ProcessedMessageSafeExportSecretError::NotACommit)
468 }
469 }
470}
471
472#[derive(Debug)]
477pub enum ProcessedMessageContent {
478 ApplicationMessage(ApplicationMessage),
482 ProposalMessage(Box<QueuedProposal>),
488 ExternalJoinProposalMessage(Box<QueuedProposal>),
496 StagedCommitMessage(Box<StagedCommit>),
503}
504
505#[derive(Debug, PartialEq, Eq)]
507pub struct ApplicationMessage {
508 bytes: Vec<u8>,
509}
510
511impl ApplicationMessage {
512 pub(crate) fn new(bytes: Vec<u8>) -> Self {
514 Self { bytes }
515 }
516
517 pub fn into_bytes(self) -> Vec<u8> {
519 self.bytes
520 }
521}