1use openmls_traits::{signatures::Signer, types::Ciphersuite};
2use tls_codec::Serialize;
3
4#[cfg(feature = "extensions-draft-08")]
5use crate::schedule::application_export_tree::ApplicationExportTree;
6use crate::{
7 binary_tree::{array_representation::TreeSize, LeafNodeIndex},
8 credentials::CredentialWithKey,
9 error::LibraryError,
10 extensions::Extensions,
11 group::{
12 config::PastEpochDeletionPolicy, past_secrets::MessageSecretsStore,
13 public_group::errors::PublicGroupBuildError, GroupContext, GroupId, MlsGroup,
14 MlsGroupCreateConfig, MlsGroupCreateConfigBuilder, MlsGroupState, NewGroupError,
15 PublicGroup, WireFormatPolicy,
16 },
17 key_packages::Lifetime,
18 schedule::{
19 psk::{load_psks, store::ResumptionPskStore, PskSecret},
20 EpochSecretsResult, InitSecret, JoinerSecret, KeySchedule, PreSharedKeyId,
21 },
22 storage::OpenMlsProvider,
23 tree::sender_ratchet::SenderRatchetConfiguration,
24 treesync::{
25 errors::LeafNodeValidationError,
26 node::leaf_node::{Capabilities, LeafNode},
27 },
28};
29
30#[derive(Default, Debug)]
32pub struct MlsGroupBuilder {
33 group_id: Option<GroupId>,
34 mls_group_create_config_builder: MlsGroupCreateConfigBuilder,
35 replace_old_group: bool,
36 psk_ids: Vec<PreSharedKeyId>,
37}
38
39impl MlsGroupBuilder {
40 pub(super) fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn with_group_id(mut self, group_id: GroupId) -> Self {
46 self.group_id = Some(group_id);
47 self
48 }
49
50 pub fn replace_old_group(mut self) -> Self {
52 self.replace_old_group = true;
53 self
54 }
55
56 pub fn build<Provider: OpenMlsProvider>(
58 self,
59 provider: &Provider,
60 signer: &impl Signer,
61 credential_with_key: CredentialWithKey,
62 ) -> Result<MlsGroup, NewGroupError<Provider::StorageError>> {
63 self.build_internal(provider, signer, credential_with_key, None)
64 }
65
66 pub(super) fn build_internal<Provider: OpenMlsProvider>(
75 self,
76 provider: &Provider,
77 signer: &impl Signer,
78 credential_with_key: CredentialWithKey,
79 mls_group_create_config_option: Option<MlsGroupCreateConfig>,
80 ) -> Result<MlsGroup, NewGroupError<Provider::StorageError>> {
81 let mls_group_create_config = mls_group_create_config_option
82 .unwrap_or_else(|| self.mls_group_create_config_builder.build());
83 let group_id = self
84 .group_id
85 .unwrap_or_else(|| GroupId::random(provider.rand()));
86 let ciphersuite = mls_group_create_config.ciphersuite;
87
88 if !self.replace_old_group
89 && MlsGroup::load(provider.storage(), &group_id)
90 .map_err(NewGroupError::StorageError)?
91 .is_some()
92 {
93 return Err(NewGroupError::GroupAlreadyExists);
94 }
95
96 let (public_group_builder, commit_secret, leaf_keypair) =
97 PublicGroup::builder(group_id, ciphersuite, credential_with_key)
98 .with_group_context_extensions(
99 mls_group_create_config.group_context_extensions.clone(),
100 )
101 .with_leaf_node_extensions(mls_group_create_config.leaf_node_extensions.clone())
102 .with_lifetime(*mls_group_create_config.lifetime())
103 .with_capabilities(mls_group_create_config.capabilities.clone())
104 .get_secrets(provider, signer)
105 .map_err(|e| match e {
106 PublicGroupBuildError::LibraryError(e) => NewGroupError::LibraryError(e),
107 PublicGroupBuildError::InvalidExtensions(e) => e.into(),
108 })?;
109
110 let serialized_group_context = public_group_builder
111 .group_context()
112 .tls_serialize_detached()
113 .map_err(LibraryError::missing_bound_check)?;
114
115 let joiner_secret = JoinerSecret::new(
119 provider.crypto(),
120 ciphersuite,
121 commit_secret,
122 &InitSecret::random(ciphersuite, provider.rand())
123 .map_err(LibraryError::unexpected_crypto_error)?,
124 &serialized_group_context,
125 )
126 .map_err(LibraryError::unexpected_crypto_error)?;
127
128 let mut resumption_psk_store = ResumptionPskStore::new(32);
130
131 let psk_secret = load_psks(provider.storage(), &resumption_psk_store, &self.psk_ids)
133 .and_then(|psks| PskSecret::new(provider.crypto(), ciphersuite, psks))
134 .map_err(|e| {
135 log::debug!("Unexpected PSK error: {e:?}");
136 LibraryError::custom("Unexpected PSK error")
137 })?;
138
139 let mut key_schedule =
140 KeySchedule::init(ciphersuite, provider.crypto(), &joiner_secret, psk_secret)?;
141 key_schedule
142 .add_context(provider.crypto(), &serialized_group_context)
143 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?;
144
145 let EpochSecretsResult {
146 epoch_secrets,
147 #[cfg(feature = "extensions-draft-08")]
148 application_exporter,
149 } = key_schedule
150 .epoch_secrets(provider.crypto(), ciphersuite)
151 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?;
152
153 let (group_epoch_secrets, message_secrets) = epoch_secrets.split_secrets(
154 serialized_group_context,
155 TreeSize::new(1),
156 LeafNodeIndex::new(0u32),
157 );
158
159 let initial_confirmation_tag = message_secrets
160 .confirmation_key()
161 .tag(provider.crypto(), ciphersuite, &[])
162 .map_err(LibraryError::unexpected_crypto_error)?;
163
164 let message_secrets_store = MessageSecretsStore::new_with_secret(
165 mls_group_create_config
166 .join_config
167 .past_epoch_deletion_policy(),
168 message_secrets,
169 );
170
171 let public_group = public_group_builder
172 .with_confirmation_tag(initial_confirmation_tag)
173 .build(provider.crypto())?;
174
175 let resumption_psk = group_epoch_secrets.resumption_psk();
177 resumption_psk_store.add(public_group.group_context().epoch(), resumption_psk.clone());
178
179 #[cfg(feature = "extensions-draft-08")]
180 let application_export_tree = ApplicationExportTree::new(application_exporter);
181
182 let mls_group = MlsGroup {
183 mls_group_config: mls_group_create_config.join_config.clone(),
184 own_leaf_nodes: vec![],
185 aad: vec![],
186 group_state: MlsGroupState::Operational,
187 public_group,
188 group_epoch_secrets,
189 own_leaf_index: LeafNodeIndex::new(0),
190 message_secrets_store,
191 resumption_psk_store,
192 #[cfg(feature = "extensions-draft-08")]
193 application_export_tree: Some(application_export_tree),
194 };
195
196 mls_group
197 .store(provider.storage())
198 .map_err(NewGroupError::StorageError)?;
199 mls_group
200 .store_epoch_keypairs(provider.storage(), &[leaf_keypair])
201 .map_err(NewGroupError::StorageError)?;
202
203 Ok(mls_group)
204 }
205
206 pub fn with_wire_format_policy(mut self, wire_format_policy: WireFormatPolicy) -> Self {
210 self.mls_group_create_config_builder = self
211 .mls_group_create_config_builder
212 .wire_format_policy(wire_format_policy);
213 self
214 }
215
216 pub fn padding_size(mut self, padding_size: usize) -> Self {
218 self.mls_group_create_config_builder = self
219 .mls_group_create_config_builder
220 .padding_size(padding_size);
221 self
222 }
223
224 pub fn max_past_epochs(mut self, max_past_epochs: usize) -> Self {
242 self.mls_group_create_config_builder = self
243 .mls_group_create_config_builder
244 .max_past_epochs(max_past_epochs);
245 self
246 }
247
248 pub fn set_past_epoch_deletion_policy(mut self, policy: PastEpochDeletionPolicy) -> Self {
262 self.mls_group_create_config_builder = self
263 .mls_group_create_config_builder
264 .set_past_epoch_deletion_policy(policy);
265 self
266 }
267
268 pub fn number_of_resumption_psks(mut self, number_of_resumption_psks: usize) -> Self {
270 self.mls_group_create_config_builder = self
271 .mls_group_create_config_builder
272 .number_of_resumption_psks(number_of_resumption_psks);
273 self
274 }
275
276 pub fn use_ratchet_tree_extension(mut self, use_ratchet_tree_extension: bool) -> Self {
278 self.mls_group_create_config_builder = self
279 .mls_group_create_config_builder
280 .use_ratchet_tree_extension(use_ratchet_tree_extension);
281 self
282 }
283
284 pub fn sender_ratchet_configuration(
287 mut self,
288 sender_ratchet_configuration: SenderRatchetConfiguration,
289 ) -> Self {
290 self.mls_group_create_config_builder = self
291 .mls_group_create_config_builder
292 .sender_ratchet_configuration(sender_ratchet_configuration);
293 self
294 }
295
296 pub fn lifetime(mut self, lifetime: Lifetime) -> Self {
298 self.mls_group_create_config_builder =
299 self.mls_group_create_config_builder.lifetime(lifetime);
300 self
301 }
302
303 pub fn ciphersuite(mut self, ciphersuite: Ciphersuite) -> Self {
305 self.mls_group_create_config_builder = self
306 .mls_group_create_config_builder
307 .ciphersuite(ciphersuite);
308 self
309 }
310
311 pub fn with_group_context_extensions(mut self, extensions: Extensions<GroupContext>) -> Self {
313 self.mls_group_create_config_builder = self
314 .mls_group_create_config_builder
315 .with_group_context_extensions(extensions);
316 self
317 }
318
319 pub fn with_leaf_node_extensions(
321 mut self,
322 extensions: Extensions<LeafNode>,
323 ) -> Result<Self, LeafNodeValidationError> {
324 self.mls_group_create_config_builder = self
325 .mls_group_create_config_builder
326 .with_leaf_node_extensions(extensions)?;
327 Ok(self)
328 }
329
330 pub fn with_capabilities(mut self, capabilities: Capabilities) -> Self {
332 self.mls_group_create_config_builder = self
333 .mls_group_create_config_builder
334 .capabilities(capabilities);
335 self
336 }
337}