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}