Skip to main content

openmls/messages/
external_proposals.rs

1//! External Proposals
2//!
3//! Contains the types and methods to build external proposal to add/remove a client from a MLS group
4//!
5//! `ReInit` is not yet implemented
6
7use crate::{
8    binary_tree::LeafNodeIndex,
9    error::LibraryError,
10    extensions::{Extensions, SenderExtensionIndex},
11    framing::{mls_auth_content::AuthenticatedContent, MlsMessageOut, PublicMessage},
12    group::{
13        errors::{CreateGroupContextExtProposalError, ProposeRemoveMemberError},
14        mls_group::errors::ProposeAddMemberError,
15        GroupContext, GroupEpoch, GroupId,
16    },
17    key_packages::KeyPackage,
18    messages::{AddProposal, Proposal},
19    schedule::psk::PreSharedKeyId,
20    storage::{OpenMlsProvider, StorageProvider},
21};
22use openmls_traits::signatures::Signer;
23
24use super::proposals::{GroupContextExtensionProposal, PreSharedKeyProposal, RemoveProposal};
25
26/// External Add Proposal where sender is [NewMemberProposal](crate::prelude::Sender::NewMemberProposal). A client
27/// outside the group can request joining the group. This proposal should then be committed by a
28/// group member. Note that this is unconstrained i.e. it works for any [MLS group](crate::group::MlsGroup).
29/// This is not the case for the same external proposal with a [Preconfigured sender](crate::prelude::Sender::External)
30pub struct JoinProposal;
31
32/// External Proposal where sender is [External](crate::prelude::Sender::External). A party
33/// outside the group can request to add or remove a member to the group. This proposal should then
34/// be committed by a group member. The sender must be pre configured within the group through the [crate::extensions::ExternalSendersExtension]
35pub struct ExternalProposal;
36
37impl JoinProposal {
38    /// Creates an external Add proposal. For clients requesting to be added to a group. This
39    /// proposal will have to be committed later by a group member.
40    ///
41    /// # Arguments
42    /// * `key_package` - of the joiner
43    /// * `group_id` - unique group identifier of the group to join
44    /// * `epoch` - group's epoch
45    /// * `signer` - of the sender to sign the message
46    #[allow(clippy::new_ret_no_self)]
47    pub fn new<Storage: StorageProvider>(
48        key_package: KeyPackage,
49        group_id: GroupId,
50        epoch: GroupEpoch,
51        signer: &impl Signer,
52    ) -> Result<MlsMessageOut, ProposeAddMemberError<Storage::Error>> {
53        AuthenticatedContent::new_join_proposal(
54            Proposal::add(AddProposal { key_package }),
55            group_id,
56            epoch,
57            signer,
58        )
59        .map(PublicMessage::from)
60        .map(MlsMessageOut::from)
61        .map_err(ProposeAddMemberError::from)
62    }
63}
64
65impl ExternalProposal {
66    /// Creates an external GroupContextExtensions proposal.
67    /// For example, for delivery services requesting to update the group context
68    /// extension.
69    /// This proposal will have to be committed later by a group member.
70    ///
71    /// # Arguments
72    /// * `extensions` - a new set of extensions for the group context
73    /// * `group_id` - unique group identifier of the group to join
74    /// * `epoch` - group's epoch
75    /// * `signer` - of the sender to sign the message
76    /// * `sender_index` - index of the sender of the proposal
77    ///   (in the [ExternalSendersExtension](crate::extensions::ExternalSendersExtension)
78    ///   array from the Group Context)
79    pub fn new_group_context_extensions<Provider: OpenMlsProvider>(
80        extensions: Extensions<GroupContext>,
81        group_id: GroupId,
82        epoch: GroupEpoch,
83        signer: &impl Signer,
84        sender_index: SenderExtensionIndex,
85    ) -> Result<MlsMessageOut, CreateGroupContextExtProposalError<Provider::StorageError>> {
86        let proposal = GroupContextExtensionProposal::new(extensions);
87
88        AuthenticatedContent::new_external_proposal(
89            Proposal::GroupContextExtensions(Box::new(proposal)),
90            group_id,
91            epoch,
92            signer,
93            sender_index,
94        )
95        .map(PublicMessage::from)
96        .map(MlsMessageOut::from)
97        .map_err(CreateGroupContextExtProposalError::from)
98    }
99
100    /// Creates an external Remove proposal.
101    /// For example, for delivery services requesting to remove a client.
102    /// This proposal will have to be committed later by a group member.
103    ///
104    /// # Arguments
105    /// * `removed` - index of the client to remove
106    /// * `group_id` - unique group identifier of the group to join
107    /// * `epoch` - group's epoch
108    /// * `signer` - of the sender to sign the message
109    /// * `sender_index` - index of the sender of the proposal
110    ///   (in the [ExternalSendersExtension](crate::extensions::ExternalSendersExtension)
111    ///   array from the Group Context)
112    pub fn new_remove<Provider: OpenMlsProvider>(
113        removed: LeafNodeIndex,
114        group_id: GroupId,
115        epoch: GroupEpoch,
116        signer: &impl Signer,
117        sender_index: SenderExtensionIndex,
118    ) -> Result<MlsMessageOut, ProposeRemoveMemberError<Provider::StorageError>> {
119        AuthenticatedContent::new_external_proposal(
120            Proposal::remove(RemoveProposal { removed }),
121            group_id,
122            epoch,
123            signer,
124            sender_index,
125        )
126        .map(PublicMessage::from)
127        .map(MlsMessageOut::from)
128        .map_err(ProposeRemoveMemberError::from)
129    }
130
131    /// Creates an external Add proposal. For delivery services requesting to
132    /// add a client.
133    /// This proposal will have to be committed later by a group member.
134    ///
135    /// # Arguments
136    /// * `key_package` - key package of the client to add
137    /// * `group_id` - unique group identifier of the group to join
138    /// * `epoch` - group's epoch
139    /// * `signer` - of the sender to sign the message
140    /// * `sender_index` - index of the sender of the proposal
141    ///   (in the [ExternalSendersExtension](crate::extensions::ExternalSendersExtension)
142    ///   array from the Group Context)
143    pub fn new_add<Provider: OpenMlsProvider>(
144        key_package: KeyPackage,
145        group_id: GroupId,
146        epoch: GroupEpoch,
147        signer: &impl Signer,
148        sender_index: SenderExtensionIndex,
149    ) -> Result<MlsMessageOut, ProposeAddMemberError<Provider::StorageError>> {
150        AuthenticatedContent::new_external_proposal(
151            Proposal::add(AddProposal { key_package }),
152            group_id,
153            epoch,
154            signer,
155            sender_index,
156        )
157        .map(PublicMessage::from)
158        .map(MlsMessageOut::from)
159        .map_err(ProposeAddMemberError::from)
160    }
161
162    /// Creates an external PreSharedKey proposal.
163    /// For example for delivery services requesting to inject a pre-shared key
164    /// into the group's key schedule (RFC 9420 ยง12.1.8 permits external senders
165    /// to send PSK proposals).
166    /// This proposal will have to be committed later by a group member.
167    /// The same constructor is used for both external and resumption PSKs;
168    /// the distinction is carried by the [`PreSharedKeyId`].
169    ///
170    /// # Arguments
171    /// * `psk_id` - identifier of the pre-shared key to inject
172    /// * `group_id` - unique group identifier of the group
173    /// * `epoch` - group's epoch
174    /// * `signer` - of the sender to sign the message
175    /// * `sender_index` - index of the sender of the proposal
176    ///   (in the [ExternalSendersExtension](crate::extensions::ExternalSendersExtension)
177    ///   array from the Group Context)
178    pub fn new_pre_shared_key(
179        psk_id: PreSharedKeyId,
180        group_id: GroupId,
181        epoch: GroupEpoch,
182        signer: &impl Signer,
183        sender_index: SenderExtensionIndex,
184    ) -> Result<MlsMessageOut, LibraryError> {
185        AuthenticatedContent::new_external_proposal(
186            Proposal::psk(PreSharedKeyProposal::new(psk_id)),
187            group_id,
188            epoch,
189            signer,
190            sender_index,
191        )
192        .map(PublicMessage::from)
193        .map(MlsMessageOut::from)
194    }
195}