1mod errors;
9
10#[cfg(test)]
11pub mod kat;
12
13#[cfg(test)]
14mod tests;
15
16pub use errors::*;
17
18use openmls_traits::{
19 crypto::OpenMlsCrypto,
20 signatures::Signer,
21 types::{Ciphersuite, HpkeCiphertext},
22};
23use serde::{Deserialize, Serialize};
24use tls_codec::{
25 DeserializeBytes, Serialize as TlsSerializeTrait, Size, TlsDeserialize, TlsDeserializeBytes,
26 TlsSerialize, TlsSize, VLByteSlice, VLBytes,
27};
28
29use crate::{
30 binary_tree::array_representation::LeafNodeIndex,
31 ciphersuite::{
32 signable::{Signable, SignedStruct, Verifiable, VerifiedStruct},
33 AeadKey, AeadNonce, OpenMlsSignaturePublicKey, Secret, Signature,
34 },
35 error::LibraryError,
36 framing::WireFormat,
37 group::{GroupEpoch, GroupId},
38 treesync::node::{
39 encryption_keys::{EncryptionKey, EncryptionPrivateKey},
40 leaf_node::LeafNode,
41 },
42 versions::ProtocolVersion,
43};
44
45const TARGETED_MESSAGE_EXPORTER_LABEL: &str = "targeted message";
46const PSK_SUBLABEL: &str = "psk";
47const SENDER_AUTH_DATA_SECRET_SUBLABEL: &str = "sender auth data secret";
48const TARGETED_MESSAGE_TBS_LABEL: &str = "TargetedMessageTBS";
49const TARGETED_MESSAGE_DATA_LABEL: &str = "MLS 1.0 TargetedMessageData";
50const PSK_LABEL: &str = "MLS 1.0 targeted message psk";
51
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TlsSerialize, TlsSize)]
65pub struct TargetedMessage {
66 pub(crate) group_id: GroupId,
67 pub(crate) epoch: GroupEpoch,
68 pub(crate) recipient_leaf_index: u32,
69 pub(crate) authenticated_data: VLBytes,
70 pub(crate) encrypted_sender_auth_data: VLBytes,
71 pub(crate) ciphertext: VLBytes,
72}
73
74impl TargetedMessage {
75 pub fn group_id(&self) -> &GroupId {
77 &self.group_id
78 }
79
80 pub fn epoch(&self) -> GroupEpoch {
82 self.epoch
83 }
84
85 pub fn recipient_leaf_index(&self) -> u32 {
87 self.recipient_leaf_index
88 }
89
90 pub fn authenticated_data(&self) -> &[u8] {
92 self.authenticated_data.as_slice()
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize)]
98pub struct TargetedMessageIn {
99 pub(crate) group_id: GroupId,
100 pub(crate) epoch: GroupEpoch,
101 pub(crate) recipient_leaf_index: u32,
102 pub(crate) authenticated_data: VLBytes,
103 pub(crate) encrypted_sender_auth_data: VLBytes,
104 pub(crate) ciphertext: VLBytes,
105}
106
107impl TargetedMessageIn {
108 pub fn group_id(&self) -> &GroupId {
110 &self.group_id
111 }
112
113 pub fn epoch(&self) -> GroupEpoch {
115 self.epoch
116 }
117
118 pub fn recipient_leaf_index(&self) -> u32 {
120 self.recipient_leaf_index
121 }
122
123 pub fn authenticated_data(&self) -> &[u8] {
125 self.authenticated_data.as_slice()
126 }
127}
128
129#[cfg(any(feature = "test-utils", test))]
130impl From<TargetedMessageIn> for TargetedMessage {
131 fn from(msg: TargetedMessageIn) -> Self {
132 Self {
133 group_id: msg.group_id,
134 epoch: msg.epoch,
135 recipient_leaf_index: msg.recipient_leaf_index,
136 authenticated_data: msg.authenticated_data,
137 encrypted_sender_auth_data: msg.encrypted_sender_auth_data,
138 ciphertext: msg.ciphertext,
139 }
140 }
141}
142
143impl From<TargetedMessage> for TargetedMessageIn {
144 fn from(msg: TargetedMessage) -> Self {
145 Self {
146 group_id: msg.group_id,
147 epoch: msg.epoch,
148 recipient_leaf_index: msg.recipient_leaf_index,
149 authenticated_data: msg.authenticated_data,
150 encrypted_sender_auth_data: msg.encrypted_sender_auth_data,
151 ciphertext: msg.ciphertext,
152 }
153 }
154}
155
156#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
165pub(crate) struct TargetedMessageContent {
166 application_data: VLBytes,
167 padding_length: usize,
168}
169
170impl TargetedMessageContent {
171 fn new(data: &[u8], padding_length: usize) -> Self {
172 Self {
173 application_data: data.into(),
174 padding_length,
175 }
176 }
177
178 pub(crate) fn application_data(&self) -> &[u8] {
179 self.application_data.as_slice()
180 }
181
182 fn serialize_detached(&self) -> Result<Vec<u8>, tls_codec::Error> {
185 use std::io::Write;
186 let app_data_len = self.application_data.tls_serialized_len();
187 let total_len = app_data_len
190 .checked_add(self.padding_length)
191 .filter(|&len| len <= isize::MAX as usize)
192 .ok_or_else(|| {
193 tls_codec::Error::EncodingError(
194 "Targeted message content exceeds the maximum size.".into(),
195 )
196 })?;
197 let mut buffer = Vec::with_capacity(total_len);
198 self.application_data.tls_serialize(&mut buffer)?;
199 buffer
200 .write_all(&vec![0u8; self.padding_length])
201 .map_err(|e| {
202 tls_codec::Error::EncodingError(format!("Failed to write padding: {e}"))
203 })?;
204 Ok(buffer)
205 }
206
207 fn deserialize_detached(bytes: &[u8]) -> Result<Self, tls_codec::Error> {
210 let (application_data, rest) = VLBytes::tls_deserialize_bytes(bytes)?;
211 if !rest.iter().all(|&b| b == 0x00) {
212 return Err(tls_codec::Error::DecodingError(
213 "Non-zero padding in TargetedMessageContent".into(),
214 ));
215 }
216 Ok(Self {
217 application_data,
218 padding_length: rest.len(),
219 })
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize)]
233struct TargetedMessageSenderAuthData {
234 sender_leaf_index: u32,
235 signature: Signature,
236 kem_output: VLBytes,
237}
238
239#[derive(TlsSerialize, TlsSize)]
249struct SenderAuthDataAAD<'a> {
250 group_id: &'a GroupId,
251 epoch: GroupEpoch,
252 recipient_leaf_index: u32,
253}
254
255#[derive(TlsSerialize, TlsSize)]
273struct TargetedMessageTBS<'a> {
274 version: ProtocolVersion,
275 wire_format: WireFormat,
276 group_id: &'a GroupId,
277 epoch: GroupEpoch,
278 recipient_leaf_index: u32,
279 authenticated_data: VLByteSlice<'a>,
280 sender_leaf_index: u32,
281 kem_output: VLByteSlice<'a>,
282 ciphertext_hash: VLByteSlice<'a>,
283}
284
285#[derive(TlsSerialize, TlsSize)]
294struct TargetedMessageContextData {
295 recipient_leaf_index: u32,
296 sender_leaf_index: u32,
297}
298
299#[derive(TlsSerialize, TlsSize)]
308struct TargetedMessageContext<'a> {
309 label: VLByteSlice<'a>,
310 context: TargetedMessageContextData,
311}
312
313fn targeted_message_hpke_info(
315 recipient_leaf_index: u32,
316 sender_leaf_index: u32,
317) -> Result<Vec<u8>, LibraryError> {
318 TargetedMessageContext {
319 label: VLByteSlice(TARGETED_MESSAGE_DATA_LABEL.as_bytes()),
320 context: TargetedMessageContextData {
321 recipient_leaf_index,
322 sender_leaf_index,
323 },
324 }
325 .tls_serialize_detached()
326 .map_err(LibraryError::missing_bound_check)
327}
328
329#[derive(TlsSerialize, TlsSize)]
339struct TargetedMessagePskId<'a> {
340 group_id: &'a GroupId,
341 epoch: GroupEpoch,
342 label: VLByteSlice<'a>,
343}
344
345impl<'a> TargetedMessagePskId<'a> {
346 fn new(group_id: &'a GroupId, epoch: GroupEpoch) -> Self {
347 Self {
348 group_id,
349 epoch,
350 label: VLByteSlice(PSK_LABEL.as_bytes()),
351 }
352 }
353}
354
355pub(crate) struct TargetedMessageGroupContext<'a> {
359 pub ciphersuite: Ciphersuite,
360 pub group_id: &'a GroupId,
361 pub epoch: GroupEpoch,
362 pub exporter_secret: &'a crate::schedule::ExporterSecret,
363}
364
365#[derive(Debug, Clone, PartialEq)]
367pub struct ProcessedTargetedMessage {
368 sender_leaf_index: LeafNodeIndex,
369 application_data: Vec<u8>,
370 authenticated_data: Vec<u8>,
371}
372
373impl ProcessedTargetedMessage {
374 pub fn sender_leaf_index(&self) -> LeafNodeIndex {
376 self.sender_leaf_index
377 }
378
379 pub fn application_data(&self) -> &[u8] {
381 &self.application_data
382 }
383
384 pub fn authenticated_data(&self) -> &[u8] {
386 &self.authenticated_data
387 }
388
389 pub fn into_data(self) -> (Vec<u8>, Vec<u8>) {
391 (self.application_data, self.authenticated_data)
392 }
393}
394
395fn derive_targeted_message_psk(
397 crypto: &impl OpenMlsCrypto,
398 ciphersuite: Ciphersuite,
399 exporter_secret: &crate::schedule::ExporterSecret,
400) -> Result<Secret, LibraryError> {
401 exporter_secret
402 .derive_exported_secret(
403 ciphersuite,
404 crypto,
405 TARGETED_MESSAGE_EXPORTER_LABEL,
406 PSK_SUBLABEL.as_bytes(),
407 ciphersuite.hash_length(),
408 )
409 .map_err(LibraryError::unexpected_crypto_error)
410}
411
412fn derive_sender_auth_data_secret(
414 crypto: &impl OpenMlsCrypto,
415 ciphersuite: Ciphersuite,
416 exporter_secret: &crate::schedule::ExporterSecret,
417) -> Result<Secret, LibraryError> {
418 exporter_secret
419 .derive_exported_secret(
420 ciphersuite,
421 crypto,
422 TARGETED_MESSAGE_EXPORTER_LABEL,
423 SENDER_AUTH_DATA_SECRET_SUBLABEL.as_bytes(),
424 ciphersuite.hash_length(),
425 )
426 .map_err(LibraryError::unexpected_crypto_error)
427}
428
429fn derive_sender_auth_data_key_nonce(
431 crypto: &impl OpenMlsCrypto,
432 ciphersuite: Ciphersuite,
433 sender_auth_data_secret: &Secret,
434 ciphertext: &[u8],
435) -> Result<(AeadKey, AeadNonce), LibraryError> {
436 let sample_len = ciphersuite.hash_length().min(ciphertext.len());
437 let ciphertext_sample = &ciphertext[..sample_len];
438
439 let key_secret = sender_auth_data_secret
440 .kdf_expand_label(
441 crypto,
442 ciphersuite,
443 "key",
444 ciphertext_sample,
445 ciphersuite.aead_key_length(),
446 )
447 .map_err(LibraryError::unexpected_crypto_error)?;
448
449 let nonce_secret = sender_auth_data_secret
450 .kdf_expand_label(
451 crypto,
452 ciphersuite,
453 "nonce",
454 ciphertext_sample,
455 ciphersuite.aead_nonce_length(),
456 )
457 .map_err(LibraryError::unexpected_crypto_error)?;
458
459 Ok((
460 AeadKey::from_secret(key_secret, ciphersuite),
461 AeadNonce::from_secret(nonce_secret),
462 ))
463}
464
465struct TargetedMessageTBSPayload {
467 serialized: Vec<u8>,
468}
469
470impl Signable for TargetedMessageTBSPayload {
471 type SignedOutput = TargetedMessageSignature;
472
473 fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
474 Ok(self.serialized.clone())
475 }
476
477 fn label(&self) -> &str {
478 TARGETED_MESSAGE_TBS_LABEL
479 }
480}
481
482struct TargetedMessageSignature(pub(crate) Signature);
483
484impl SignedStruct<TargetedMessageTBSPayload> for TargetedMessageSignature {
485 fn from_payload(
486 _payload: TargetedMessageTBSPayload,
487 signature: Signature,
488 _serialized_payload: Vec<u8>,
489 ) -> Self {
490 Self(signature)
491 }
492}
493
494struct VerifiableTargetedMessageTBS {
496 serialized: Vec<u8>,
497 signature: Signature,
498}
499
500impl Verifiable for VerifiableTargetedMessageTBS {
501 type VerifiedStruct = VerifiedTargetedMessage;
502
503 fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
504 Ok(self.serialized.clone())
505 }
506
507 fn signature(&self) -> &Signature {
508 &self.signature
509 }
510
511 fn label(&self) -> &str {
512 TARGETED_MESSAGE_TBS_LABEL
513 }
514
515 fn verify(
516 self,
517 crypto: &impl OpenMlsCrypto,
518 pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
519 ) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
520 self.verify_no_out(crypto, pk)?;
521 Ok(VerifiedTargetedMessage)
522 }
523}
524
525struct VerifiedTargetedMessage;
526impl VerifiedStruct for VerifiedTargetedMessage {}
527
528#[allow(clippy::too_many_arguments)]
530pub(crate) fn create_targeted_message(
531 crypto: &impl OpenMlsCrypto,
532 signer: &impl Signer,
533 ctx: &TargetedMessageGroupContext<'_>,
534 sender_leaf_index: LeafNodeIndex,
535 recipient_leaf_index: LeafNodeIndex,
536 recipient_encryption_key: &EncryptionKey,
537 authenticated_data: &[u8],
538 application_data: &[u8],
539 padding_length: usize,
540) -> Result<TargetedMessage, CreateTargetedMessageError> {
541 let psk = derive_targeted_message_psk(crypto, ctx.ciphersuite, ctx.exporter_secret)?;
542 let sender_auth_data_secret =
543 derive_sender_auth_data_secret(crypto, ctx.ciphersuite, ctx.exporter_secret)?;
544
545 let psk_id = TargetedMessagePskId::new(ctx.group_id, ctx.epoch);
546 let psk_id_bytes = psk_id
547 .tls_serialize_detached()
548 .map_err(LibraryError::missing_bound_check)?;
549
550 let content = TargetedMessageContent::new(application_data, padding_length);
551 let content_bytes = content
552 .serialize_detached()
553 .map_err(LibraryError::missing_bound_check)?;
554
555 let info = targeted_message_hpke_info(recipient_leaf_index.u32(), sender_leaf_index.u32())?;
556
557 let hpke_ct = recipient_encryption_key.seal_psk(
558 crate::ciphersuite::hpke::PskEncryptParams {
559 info: &info,
560 psk: psk.as_slice(),
561 psk_id: &psk_id_bytes,
562 ciphersuite: ctx.ciphersuite,
563 },
564 authenticated_data,
565 &content_bytes,
566 crypto,
567 )?;
568
569 let ciphertext_hash = crypto
572 .hash(
573 ctx.ciphersuite.hash_algorithm(),
574 hpke_ct.ciphertext.as_slice(),
575 )
576 .map_err(LibraryError::unexpected_crypto_error)?;
577
578 let tbs = TargetedMessageTBS {
579 version: ProtocolVersion::default(),
580 wire_format: WireFormat::TargetedMessage,
581 group_id: ctx.group_id,
582 epoch: ctx.epoch,
583 recipient_leaf_index: recipient_leaf_index.u32(),
584 authenticated_data: VLByteSlice(authenticated_data),
585 sender_leaf_index: sender_leaf_index.u32(),
586 kem_output: VLByteSlice(hpke_ct.kem_output.as_slice()),
587 ciphertext_hash: VLByteSlice(&ciphertext_hash),
588 };
589 let tbs_bytes = tbs
590 .tls_serialize_detached()
591 .map_err(LibraryError::missing_bound_check)?;
592
593 let tbs_payload = TargetedMessageTBSPayload {
594 serialized: tbs_bytes,
595 };
596 let signature = tbs_payload.sign(signer).map_err(|e| {
597 log::error!("Signing targeted message failed: {e:?}");
598 LibraryError::custom("Signing targeted message failed")
599 })?;
600
601 let sender_auth_data = TargetedMessageSenderAuthData {
602 sender_leaf_index: sender_leaf_index.u32(),
603 signature: signature.0,
604 kem_output: hpke_ct.kem_output.as_slice().to_vec().into(),
605 };
606 let sender_auth_data_bytes = sender_auth_data
607 .tls_serialize_detached()
608 .map_err(LibraryError::missing_bound_check)?;
609
610 let (key, nonce) = derive_sender_auth_data_key_nonce(
612 crypto,
613 ctx.ciphersuite,
614 &sender_auth_data_secret,
615 hpke_ct.ciphertext.as_slice(),
616 )?;
617
618 let sender_auth_aad = SenderAuthDataAAD {
619 group_id: ctx.group_id,
620 epoch: ctx.epoch,
621 recipient_leaf_index: recipient_leaf_index.u32(),
622 };
623 let sender_auth_aad_bytes = sender_auth_aad
624 .tls_serialize_detached()
625 .map_err(LibraryError::missing_bound_check)?;
626
627 let encrypted_sender_auth_data = key
628 .aead_seal(
629 crypto,
630 &sender_auth_data_bytes,
631 &sender_auth_aad_bytes,
632 &nonce,
633 )
634 .map_err(LibraryError::unexpected_crypto_error)?;
635
636 Ok(TargetedMessage {
637 group_id: ctx.group_id.clone(),
638 epoch: ctx.epoch,
639 recipient_leaf_index: recipient_leaf_index.u32(),
640 authenticated_data: authenticated_data.to_vec().into(),
641 encrypted_sender_auth_data: encrypted_sender_auth_data.into(),
642 ciphertext: hpke_ct.ciphertext,
643 })
644}
645
646pub(crate) fn process_targeted_message<StorageError>(
648 crypto: &impl OpenMlsCrypto,
649 ctx: &TargetedMessageGroupContext<'_>,
650 own_leaf_index: LeafNodeIndex,
651 own_encryption_private_key: &EncryptionPrivateKey,
652 message: &TargetedMessageIn,
653 leaves: &[Option<&LeafNode>],
654) -> Result<ProcessedTargetedMessage, ProcessTargetedMessageError<StorageError>> {
655 if &message.group_id != ctx.group_id {
657 return Err(ProcessTargetedMessageError::GroupIdMismatch);
658 }
659
660 if message.epoch != ctx.epoch {
662 return Err(ProcessTargetedMessageError::EpochMismatch);
663 }
664
665 if message.recipient_leaf_index != own_leaf_index.u32() {
667 return Err(ProcessTargetedMessageError::NotIntendedRecipient);
668 }
669
670 let sender_auth_data_secret =
671 derive_sender_auth_data_secret(crypto, ctx.ciphersuite, ctx.exporter_secret)?;
672
673 let (key, nonce) = derive_sender_auth_data_key_nonce(
675 crypto,
676 ctx.ciphersuite,
677 &sender_auth_data_secret,
678 message.ciphertext.as_slice(),
679 )?;
680
681 let sender_auth_aad = SenderAuthDataAAD {
682 group_id: ctx.group_id,
683 epoch: ctx.epoch,
684 recipient_leaf_index: own_leaf_index.u32(),
685 };
686 let sender_auth_aad_bytes = sender_auth_aad
687 .tls_serialize_detached()
688 .map_err(LibraryError::missing_bound_check)?;
689
690 let sender_auth_data_bytes = key
692 .aead_open(
693 crypto,
694 message.encrypted_sender_auth_data.as_slice(),
695 &sender_auth_aad_bytes,
696 &nonce,
697 )
698 .map_err(|e| {
699 log::error!("Targeted message sender auth data decryption failed: {e:?}");
700 ProcessTargetedMessageError::SenderAuthDataDecryptionFailed
701 })?;
702
703 let sender_auth_data =
704 TargetedMessageSenderAuthData::tls_deserialize_exact_bytes(&sender_auth_data_bytes)
705 .map_err(|e| {
706 log::error!("Targeted message sender auth data is malformed: {e:?}");
707 ProcessTargetedMessageError::MalformedSenderAuthData
708 })?;
709
710 let sender_leaf_index = LeafNodeIndex::new(sender_auth_data.sender_leaf_index);
711
712 let sender_leaf = leaves
713 .get(sender_leaf_index.usize())
714 .and_then(|opt| opt.as_ref())
715 .ok_or(ProcessTargetedMessageError::SenderNotFound)?;
716 let sender_signature_key = OpenMlsSignaturePublicKey::from_signature_key(
717 sender_leaf.signature_key().clone(),
718 ctx.ciphersuite.signature_algorithm(),
719 );
720
721 let ciphertext_hash = crypto
724 .hash(
725 ctx.ciphersuite.hash_algorithm(),
726 message.ciphertext.as_slice(),
727 )
728 .map_err(LibraryError::unexpected_crypto_error)?;
729
730 let tbs = TargetedMessageTBS {
731 version: ProtocolVersion::default(),
732 wire_format: WireFormat::TargetedMessage,
733 group_id: ctx.group_id,
734 epoch: ctx.epoch,
735 recipient_leaf_index: own_leaf_index.u32(),
736 authenticated_data: VLByteSlice(message.authenticated_data.as_slice()),
737 sender_leaf_index: sender_auth_data.sender_leaf_index,
738 kem_output: VLByteSlice(sender_auth_data.kem_output.as_slice()),
739 ciphertext_hash: VLByteSlice(&ciphertext_hash),
740 };
741 let tbs_bytes = tbs
742 .tls_serialize_detached()
743 .map_err(LibraryError::missing_bound_check)?;
744
745 let verifiable = VerifiableTargetedMessageTBS {
746 serialized: tbs_bytes,
747 signature: sender_auth_data.signature.clone(),
748 };
749
750 verifiable
751 .verify(crypto, &sender_signature_key)
752 .map_err(|e| {
753 log::error!("Targeted message signature verification failed: {e:?}");
754 ProcessTargetedMessageError::SignatureVerificationFailed
755 })?;
756
757 let psk = derive_targeted_message_psk(crypto, ctx.ciphersuite, ctx.exporter_secret)?;
759
760 let psk_id = TargetedMessagePskId::new(ctx.group_id, ctx.epoch);
761 let psk_id_bytes = psk_id
762 .tls_serialize_detached()
763 .map_err(LibraryError::missing_bound_check)?;
764
765 let info =
766 targeted_message_hpke_info(own_leaf_index.u32(), sender_auth_data.sender_leaf_index)?;
767
768 let hpke_ciphertext = HpkeCiphertext {
769 kem_output: sender_auth_data.kem_output.as_slice().to_vec().into(),
770 ciphertext: message.ciphertext.as_slice().to_vec().into(),
771 };
772
773 let content_bytes = own_encryption_private_key
774 .open_psk(
775 crate::ciphersuite::hpke::PskEncryptParams {
776 info: &info,
777 psk: psk.as_slice(),
778 psk_id: &psk_id_bytes,
779 ciphersuite: ctx.ciphersuite,
780 },
781 message.authenticated_data.as_slice(),
782 &hpke_ciphertext,
783 crypto,
784 )
785 .map_err(|e| {
786 log::error!("Targeted message content decryption failed: {e:?}");
787 ProcessTargetedMessageError::ContentDecryptionFailed
788 })?;
789
790 let content = TargetedMessageContent::deserialize_detached(&content_bytes).map_err(|e| {
791 log::error!("Targeted message content is malformed: {e:?}");
792 ProcessTargetedMessageError::MalformedContent
793 })?;
794
795 Ok(ProcessedTargetedMessage {
796 sender_leaf_index,
797 application_data: content.application_data().to_vec(),
798 authenticated_data: message.authenticated_data.as_slice().to_vec(),
799 })
800}