1use crate::group::{proposal_filter::ProposalBundle, Roster};
6
7#[cfg(feature = "private_message")]
8use crate::{
9 group::{padding::PaddingMode, Sender},
10 WireFormat,
11};
12
13use alloc::boxed::Box;
14use core::convert::Infallible;
15use mls_rs_core::{error::IntoAnyError, group::Member, identity::SigningIdentity};
16
17use super::GroupContext;
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub enum CommitDirection {
21 Send,
22 Receive,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
28pub enum CommitSource {
29 ExistingMember(Member),
30 NewMember(SigningIdentity),
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35#[non_exhaustive]
36pub struct CommitOptions {
37 pub path_required: bool,
38 pub ratchet_tree_extension: bool,
39 pub single_welcome_message: bool,
40 pub allow_external_commit: bool,
41 pub always_out_of_band_ratchet_tree: bool,
44}
45
46impl Default for CommitOptions {
47 fn default() -> Self {
48 CommitOptions {
49 path_required: false,
50 ratchet_tree_extension: true,
51 single_welcome_message: true,
52 allow_external_commit: false,
53 always_out_of_band_ratchet_tree: false,
54 }
55 }
56}
57
58impl CommitOptions {
59 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn with_path_required(self, path_required: bool) -> Self {
64 Self {
65 path_required,
66 ..self
67 }
68 }
69
70 pub fn with_ratchet_tree_extension(self, ratchet_tree_extension: bool) -> Self {
71 Self {
72 ratchet_tree_extension,
73 ..self
74 }
75 }
76
77 pub fn with_single_welcome_message(self, single_welcome_message: bool) -> Self {
78 Self {
79 single_welcome_message,
80 ..self
81 }
82 }
83
84 pub fn with_allow_external_commit(self, allow_external_commit: bool) -> Self {
85 Self {
86 allow_external_commit,
87 ..self
88 }
89 }
90
91 pub fn with_always_out_of_band_ratchet_tree(
92 self,
93 always_out_of_band_ratchet_tree: bool,
94 ) -> Self {
95 Self {
96 always_out_of_band_ratchet_tree,
97 ..self
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
104#[non_exhaustive]
105pub struct EncryptionOptions {
106 #[cfg(feature = "private_message")]
107 pub encrypt_control_messages: bool,
108 #[cfg(feature = "private_message")]
109 pub padding_mode: PaddingMode,
110}
111
112#[cfg(feature = "private_message")]
113impl EncryptionOptions {
114 pub fn new(encrypt_control_messages: bool, padding_mode: PaddingMode) -> Self {
115 Self {
116 encrypt_control_messages,
117 padding_mode,
118 }
119 }
120
121 pub(crate) fn control_wire_format(&self, sender: Sender) -> WireFormat {
122 match sender {
123 Sender::Member(_) if self.encrypt_control_messages => WireFormat::PrivateMessage,
124 _ => WireFormat::PublicMessage,
125 }
126 }
127}
128
129#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
131#[cfg_attr(mls_build_async, maybe_async::must_be_async)]
132pub trait MlsRules: Send + Sync {
133 type Error: IntoAnyError;
134
135 async fn filter_proposals(
156 &self,
157 direction: CommitDirection,
158 source: CommitSource,
159 current_roster: &Roster,
160 current_context: &GroupContext,
161 proposals: ProposalBundle,
162 ) -> Result<ProposalBundle, Self::Error>;
163
164 fn commit_options(
171 &self,
172 new_roster: &Roster,
173 new_context: &GroupContext,
174 proposals: &ProposalBundle,
175 ) -> Result<CommitOptions, Self::Error>;
176
177 fn encryption_options(
183 &self,
184 current_roster: &Roster,
185 current_context: &GroupContext,
186 ) -> Result<EncryptionOptions, Self::Error>;
187}
188
189macro_rules! delegate_mls_rules {
190 ($implementer:ty) => {
191 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
192 #[cfg_attr(mls_build_async, maybe_async::must_be_async)]
193 impl<T: MlsRules + ?Sized> MlsRules for $implementer {
194 type Error = T::Error;
195
196 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
197 async fn filter_proposals(
198 &self,
199 direction: CommitDirection,
200 source: CommitSource,
201 current_roster: &Roster,
202 context: &GroupContext,
203 proposals: ProposalBundle,
204 ) -> Result<ProposalBundle, Self::Error> {
205 (**self)
206 .filter_proposals(direction, source, current_roster, context, proposals)
207 .await
208 }
209
210 fn commit_options(
211 &self,
212 roster: &Roster,
213 context: &GroupContext,
214 proposals: &ProposalBundle,
215 ) -> Result<CommitOptions, Self::Error> {
216 (**self).commit_options(roster, context, proposals)
217 }
218
219 fn encryption_options(
220 &self,
221 roster: &Roster,
222 context: &GroupContext,
223 ) -> Result<EncryptionOptions, Self::Error> {
224 (**self).encryption_options(roster, context)
225 }
226 }
227 };
228}
229
230delegate_mls_rules!(Box<T>);
231delegate_mls_rules!(&T);
232
233#[derive(Clone, Debug, Default)]
234#[non_exhaustive]
235pub struct DefaultMlsRules {
237 pub commit_options: CommitOptions,
238 pub encryption_options: EncryptionOptions,
239}
240
241impl DefaultMlsRules {
242 pub fn new() -> Self {
245 Default::default()
246 }
247
248 pub fn with_commit_options(self, commit_options: CommitOptions) -> Self {
250 Self {
251 commit_options,
252 encryption_options: self.encryption_options,
253 }
254 }
255
256 pub fn with_encryption_options(self, encryption_options: EncryptionOptions) -> Self {
258 Self {
259 commit_options: self.commit_options,
260 encryption_options,
261 }
262 }
263}
264
265#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
266#[cfg_attr(mls_build_async, maybe_async::must_be_async)]
267impl MlsRules for DefaultMlsRules {
268 type Error = Infallible;
269
270 async fn filter_proposals(
271 &self,
272 _direction: CommitDirection,
273 _source: CommitSource,
274 _current_roster: &Roster,
275 _: &GroupContext,
276 proposals: ProposalBundle,
277 ) -> Result<ProposalBundle, Self::Error> {
278 Ok(proposals)
279 }
280
281 fn commit_options(
282 &self,
283 _: &Roster,
284 _: &GroupContext,
285 _: &ProposalBundle,
286 ) -> Result<CommitOptions, Self::Error> {
287 Ok(self.commit_options)
288 }
289
290 fn encryption_options(
291 &self,
292 _: &Roster,
293 _: &GroupContext,
294 ) -> Result<EncryptionOptions, Self::Error> {
295 Ok(self.encryption_options)
296 }
297}