Skip to main content

openmls/group/mls_group/
targeted_messages.rs

1use openmls_traits::signatures::Signer;
2
3use crate::{
4    binary_tree::array_representation::LeafNodeIndex,
5    framing::MlsMessageOut,
6    storage::OpenMlsProvider,
7    targeted_messages::{
8        self, CreateTargetedMessageError, ProcessTargetedMessageError, ProcessedTargetedMessage,
9        TargetedMessageGroupContext, TargetedMessageIn,
10    },
11};
12
13use super::*;
14
15impl MlsGroup {
16    /// Creates a targeted message for a specific group member. The
17    /// `application_data` payload is encrypted to the recipient's leaf
18    /// encryption key. The sender is authenticated via signature. The number of
19    /// zero bytes appended to the plaintext before encryption to obscure the
20    /// application data length is taken from the group's configured padding
21    /// size.
22    pub fn create_targeted_message<Provider: OpenMlsProvider>(
23        &mut self,
24        provider: &Provider,
25        signer: &impl Signer,
26        recipient_leaf_index: LeafNodeIndex,
27        application_data: &[u8],
28    ) -> Result<MlsMessageOut, CreateTargetedMessageError> {
29        if !self.is_active() {
30            return Err(CreateTargetedMessageError::GroupNotActive);
31        }
32
33        let targeted_msg = {
34            let recipient_leaf = self
35                .public_group()
36                .leaf(recipient_leaf_index)
37                .ok_or(CreateTargetedMessageError::RecipientNotFound)?;
38            let recipient_encryption_key = recipient_leaf.encryption_key();
39
40            let ctx = TargetedMessageGroupContext {
41                ciphersuite: self.ciphersuite(),
42                group_id: self.group_id(),
43                epoch: self.context().epoch(),
44                exporter_secret: self.group_epoch_secrets().exporter_secret(),
45            };
46
47            targeted_messages::create_targeted_message(
48                provider.crypto(),
49                signer,
50                &ctx,
51                self.own_leaf_index(),
52                recipient_leaf_index,
53                recipient_encryption_key,
54                &self.aad,
55                application_data,
56                self.configuration().padding_size(),
57            )?
58        };
59        self.reset_aad();
60        Ok(targeted_msg.into())
61    }
62
63    /// Processes a received targeted message. Decrypts the message content and
64    /// verifies the sender's signature. Returns the sender's leaf index and the
65    /// decrypted application data.
66    pub fn process_targeted_message<Provider: OpenMlsProvider>(
67        &self,
68        provider: &Provider,
69        message: &TargetedMessageIn,
70    ) -> Result<ProcessedTargetedMessage, ProcessTargetedMessageError<Provider::StorageError>> {
71        if !self.is_active() {
72            return Err(ProcessTargetedMessageError::GroupNotActive);
73        }
74
75        let own_leaf = self
76            .public_group()
77            .leaf(self.own_leaf_index())
78            .ok_or_else(|| LibraryError::custom("Own leaf node not found in tree"))?;
79
80        let own_encryption_key = own_leaf.encryption_key();
81
82        let epoch_keypairs = self
83            .read_epoch_keypairs(provider.storage())
84            .map_err(ProcessTargetedMessageError::StorageError)?;
85
86        let own_keypair = epoch_keypairs
87            .iter()
88            .find(|kp| kp.public_key() == own_encryption_key)
89            .ok_or_else(|| LibraryError::custom("Own encryption private key not found"))?;
90
91        let leaves = self.public_group().treesync().leaves();
92
93        let ctx = TargetedMessageGroupContext {
94            ciphersuite: self.ciphersuite(),
95            group_id: self.group_id(),
96            epoch: self.context().epoch(),
97            exporter_secret: self.group_epoch_secrets().exporter_secret(),
98        };
99
100        targeted_messages::process_targeted_message(
101            provider.crypto(),
102            &ctx,
103            self.own_leaf_index(),
104            own_keypair.private_key(),
105            message,
106            &leaves,
107        )
108    }
109}