1use openmls_traits::{signatures::Signer, types::Ciphersuite};
2use tls_codec::Serialize;
3
4use crate::{
5 binary_tree::{array_representation::TreeSize, LeafNodeIndex},
6 credentials::CredentialWithKey,
7 error::LibraryError,
8 extensions::{errors::InvalidExtensionError, Extensions},
9 group::{
10 past_secrets::MessageSecretsStore, public_group::errors::PublicGroupBuildError, GroupId,
11 MlsGroup, MlsGroupCreateConfig, MlsGroupCreateConfigBuilder, MlsGroupState, NewGroupError,
12 PublicGroup, WireFormatPolicy,
13 },
14 key_packages::Lifetime,
15 schedule::{
16 psk::{load_psks, store::ResumptionPskStore, PskSecret},
17 InitSecret, JoinerSecret, KeySchedule, PreSharedKeyId,
18 },
19 storage::OpenMlsProvider,
20 tree::sender_ratchet::SenderRatchetConfiguration,
21 treesync::{errors::LeafNodeValidationError, node::leaf_node::Capabilities},
22};
23
24#[derive(Default, Debug)]
26pub struct MlsGroupBuilder {
27 group_id: Option<GroupId>,
28 mls_group_create_config_builder: MlsGroupCreateConfigBuilder,
29 psk_ids: Vec<PreSharedKeyId>,
30}
31
32impl MlsGroupBuilder {
33 pub(super) fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn with_group_id(mut self, group_id: GroupId) -> Self {
39 self.group_id = Some(group_id);
40 self
41 }
42
43 pub fn build<Provider: OpenMlsProvider>(
45 self,
46 provider: &Provider,
47 signer: &impl Signer,
48 credential_with_key: CredentialWithKey,
49 ) -> Result<MlsGroup, NewGroupError<Provider::StorageError>> {
50 self.build_internal(provider, signer, credential_with_key, None)
51 }
52
53 pub(super) fn build_internal<Provider: OpenMlsProvider>(
59 self,
60 provider: &Provider,
61 signer: &impl Signer,
62 credential_with_key: CredentialWithKey,
63 mls_group_create_config_option: Option<MlsGroupCreateConfig>,
64 ) -> Result<MlsGroup, NewGroupError<Provider::StorageError>> {
65 let mls_group_create_config = mls_group_create_config_option
66 .unwrap_or_else(|| self.mls_group_create_config_builder.build());
67 let group_id = self
68 .group_id
69 .unwrap_or_else(|| GroupId::random(provider.rand()));
70 let ciphersuite = mls_group_create_config.ciphersuite;
71
72 let (public_group_builder, commit_secret, leaf_keypair) =
73 PublicGroup::builder(group_id, ciphersuite, credential_with_key)
74 .with_group_context_extensions(
75 mls_group_create_config.group_context_extensions.clone(),
76 )?
77 .with_leaf_node_extensions(mls_group_create_config.leaf_node_extensions.clone())?
78 .with_lifetime(*mls_group_create_config.lifetime())
79 .with_capabilities(mls_group_create_config.capabilities.clone())
80 .get_secrets(provider, signer)
81 .map_err(|e| match e {
82 PublicGroupBuildError::LibraryError(e) => NewGroupError::LibraryError(e),
83 PublicGroupBuildError::InvalidExtensions(e) => e.into(),
84 })?;
85
86 let serialized_group_context = public_group_builder
87 .group_context()
88 .tls_serialize_detached()
89 .map_err(LibraryError::missing_bound_check)?;
90
91 let joiner_secret = JoinerSecret::new(
95 provider.crypto(),
96 ciphersuite,
97 commit_secret,
98 &InitSecret::random(ciphersuite, provider.rand())
99 .map_err(LibraryError::unexpected_crypto_error)?,
100 &serialized_group_context,
101 )
102 .map_err(LibraryError::unexpected_crypto_error)?;
103
104 let mut resumption_psk_store = ResumptionPskStore::new(32);
106
107 let psk_secret = load_psks(provider.storage(), &resumption_psk_store, &self.psk_ids)
109 .and_then(|psks| PskSecret::new(provider.crypto(), ciphersuite, psks))
110 .map_err(|e| {
111 log::debug!("Unexpected PSK error: {e:?}");
112 LibraryError::custom("Unexpected PSK error")
113 })?;
114
115 let mut key_schedule =
116 KeySchedule::init(ciphersuite, provider.crypto(), &joiner_secret, psk_secret)?;
117 key_schedule
118 .add_context(provider.crypto(), &serialized_group_context)
119 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?;
120
121 let epoch_secrets = key_schedule
122 .epoch_secrets(provider.crypto(), ciphersuite)
123 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?;
124
125 let (group_epoch_secrets, message_secrets) = epoch_secrets.split_secrets(
126 serialized_group_context,
127 TreeSize::new(1),
128 LeafNodeIndex::new(0u32),
129 );
130
131 let initial_confirmation_tag = message_secrets
132 .confirmation_key()
133 .tag(provider.crypto(), ciphersuite, &[])
134 .map_err(LibraryError::unexpected_crypto_error)?;
135
136 let message_secrets_store = MessageSecretsStore::new_with_secret(
137 mls_group_create_config.max_past_epochs(),
138 message_secrets,
139 );
140
141 let public_group = public_group_builder
142 .with_confirmation_tag(initial_confirmation_tag)
143 .build(provider.crypto())?;
144
145 let resumption_psk = group_epoch_secrets.resumption_psk();
147 resumption_psk_store.add(public_group.group_context().epoch(), resumption_psk.clone());
148
149 let mls_group = MlsGroup {
150 mls_group_config: mls_group_create_config.join_config.clone(),
151 own_leaf_nodes: vec![],
152 aad: vec![],
153 group_state: MlsGroupState::Operational,
154 public_group,
155 group_epoch_secrets,
156 own_leaf_index: LeafNodeIndex::new(0),
157 message_secrets_store,
158 resumption_psk_store,
159 };
160
161 mls_group
162 .store(provider.storage())
163 .map_err(NewGroupError::StorageError)?;
164 mls_group
165 .store_epoch_keypairs(provider.storage(), &[leaf_keypair])
166 .map_err(NewGroupError::StorageError)?;
167
168 Ok(mls_group)
169 }
170
171 pub fn with_wire_format_policy(mut self, wire_format_policy: WireFormatPolicy) -> Self {
175 self.mls_group_create_config_builder = self
176 .mls_group_create_config_builder
177 .wire_format_policy(wire_format_policy);
178 self
179 }
180
181 pub fn padding_size(mut self, padding_size: usize) -> Self {
183 self.mls_group_create_config_builder = self
184 .mls_group_create_config_builder
185 .padding_size(padding_size);
186 self
187 }
188
189 pub fn max_past_epochs(mut self, max_past_epochs: usize) -> Self {
200 self.mls_group_create_config_builder = self
201 .mls_group_create_config_builder
202 .max_past_epochs(max_past_epochs);
203 self
204 }
205
206 pub fn number_of_resumption_psks(mut self, number_of_resumption_psks: usize) -> Self {
208 self.mls_group_create_config_builder = self
209 .mls_group_create_config_builder
210 .number_of_resumption_psks(number_of_resumption_psks);
211 self
212 }
213
214 pub fn use_ratchet_tree_extension(mut self, use_ratchet_tree_extension: bool) -> Self {
216 self.mls_group_create_config_builder = self
217 .mls_group_create_config_builder
218 .use_ratchet_tree_extension(use_ratchet_tree_extension);
219 self
220 }
221
222 pub fn sender_ratchet_configuration(
225 mut self,
226 sender_ratchet_configuration: SenderRatchetConfiguration,
227 ) -> Self {
228 self.mls_group_create_config_builder = self
229 .mls_group_create_config_builder
230 .sender_ratchet_configuration(sender_ratchet_configuration);
231 self
232 }
233
234 pub fn lifetime(mut self, lifetime: Lifetime) -> Self {
236 self.mls_group_create_config_builder =
237 self.mls_group_create_config_builder.lifetime(lifetime);
238 self
239 }
240
241 pub fn ciphersuite(mut self, ciphersuite: Ciphersuite) -> Self {
243 self.mls_group_create_config_builder = self
244 .mls_group_create_config_builder
245 .ciphersuite(ciphersuite);
246 self
247 }
248
249 pub fn with_group_context_extensions(
251 mut self,
252 extensions: Extensions,
253 ) -> Result<Self, InvalidExtensionError> {
254 self.mls_group_create_config_builder = self
255 .mls_group_create_config_builder
256 .with_group_context_extensions(extensions)?;
257 Ok(self)
258 }
259
260 pub fn with_leaf_node_extensions(
262 mut self,
263 extensions: Extensions,
264 ) -> Result<Self, LeafNodeValidationError> {
265 self.mls_group_create_config_builder = self
266 .mls_group_create_config_builder
267 .with_leaf_node_extensions(extensions)?;
268 Ok(self)
269 }
270
271 pub fn with_capabilities(mut self, capabilities: Capabilities) -> Self {
273 self.mls_group_create_config_builder = self
274 .mls_group_create_config_builder
275 .capabilities(capabilities);
276 self
277 }
278}