1use errors::NewGroupError;
2use openmls_traits::{signatures::Signer, storage::StorageProvider as StorageProviderTrait};
3
4use super::{builder::MlsGroupBuilder, *};
5use crate::{
6 credentials::CredentialWithKey,
7 group::errors::{ExternalCommitError, WelcomeError},
8 messages::{
9 group_info::{GroupInfo, VerifiableGroupInfo},
10 Welcome,
11 },
12 schedule::{
13 psk::{store::ResumptionPskStore, PreSharedKeyId},
14 EpochSecrets, InitSecret,
15 },
16 storage::OpenMlsProvider,
17 treesync::{
18 errors::{DerivePathError, PublicTreeError},
19 node::leaf_node::{Capabilities, LeafNodeParameters},
20 RatchetTreeIn,
21 },
22};
23
24#[cfg(doc)]
25use crate::key_packages::KeyPackage;
26
27impl MlsGroup {
28 pub fn builder() -> MlsGroupBuilder {
33 MlsGroupBuilder::new()
34 }
35
36 pub fn new<Provider: OpenMlsProvider>(
42 provider: &Provider,
43 signer: &impl Signer,
44 mls_group_create_config: &MlsGroupCreateConfig,
45 credential_with_key: CredentialWithKey,
46 ) -> Result<Self, NewGroupError<Provider::StorageError>> {
47 MlsGroupBuilder::new().build_internal(
48 provider,
49 signer,
50 credential_with_key,
51 Some(mls_group_create_config.clone()),
52 )
53 }
54
55 pub fn new_with_group_id<Provider: OpenMlsProvider>(
58 provider: &Provider,
59 signer: &impl Signer,
60 mls_group_create_config: &MlsGroupCreateConfig,
61 group_id: GroupId,
62 credential_with_key: CredentialWithKey,
63 ) -> Result<Self, NewGroupError<Provider::StorageError>> {
64 MlsGroupBuilder::new()
65 .with_group_id(group_id)
66 .build_internal(
67 provider,
68 signer,
69 credential_with_key,
70 Some(mls_group_create_config.clone()),
71 )
72 }
73
74 #[allow(clippy::too_many_arguments)]
89 pub fn join_by_external_commit<Provider: OpenMlsProvider>(
90 provider: &Provider,
91 signer: &impl Signer,
92 ratchet_tree: Option<RatchetTreeIn>,
93 verifiable_group_info: VerifiableGroupInfo,
94 mls_group_config: &MlsGroupJoinConfig,
95 capabilities: Option<Capabilities>,
96 extensions: Option<Extensions>,
97 aad: &[u8],
98 credential_with_key: CredentialWithKey,
99 ) -> Result<(Self, MlsMessageOut, Option<GroupInfo>), ExternalCommitError<Provider::StorageError>>
100 {
101 let framing_parameters = FramingParameters::new(aad, WireFormat::PublicMessage);
103
104 let leaf_node_parameters = LeafNodeParameters::builder()
105 .with_capabilities(capabilities.unwrap_or_default())
106 .with_extensions(extensions.unwrap_or_default())
107 .build();
108 let mut params = CreateCommitParams::builder()
109 .external_commit(credential_with_key, framing_parameters)
110 .leaf_node_parameters(leaf_node_parameters)
111 .build();
112
113 let ratchet_tree = match verifiable_group_info.extensions().ratchet_tree() {
120 Some(extension) => extension.ratchet_tree().clone(),
121 None => match ratchet_tree {
122 Some(ratchet_tree) => ratchet_tree,
123 None => return Err(ExternalCommitError::MissingRatchetTree),
124 },
125 };
126
127 let (public_group, group_info) = PublicGroup::from_external_internal(
128 provider.crypto(),
129 ratchet_tree,
130 verifiable_group_info,
131 ProposalStore::new(),
133 )?;
134 let group_context = public_group.group_context();
135
136 let external_pub = group_info
138 .extensions()
139 .external_pub()
140 .ok_or(ExternalCommitError::MissingExternalPub)?
141 .external_pub();
142
143 let (init_secret, kem_output) = InitSecret::from_group_context(
144 provider.crypto(),
145 group_context,
146 external_pub.as_slice(),
147 )
148 .map_err(|_| ExternalCommitError::UnsupportedCiphersuite)?;
149
150 let epoch_secrets = EpochSecrets::with_init_secret(
154 provider.crypto(),
155 group_info.group_context().ciphersuite(),
156 init_secret,
157 )
158 .map_err(LibraryError::unexpected_crypto_error)?;
159 let (group_epoch_secrets, message_secrets) = epoch_secrets.split_secrets(
160 group_context
161 .tls_serialize_detached()
162 .map_err(LibraryError::missing_bound_check)?,
163 public_group.tree_size(),
164 LeafNodeIndex::new(0u32),
168 );
169 let message_secrets_store = MessageSecretsStore::new_with_secret(0, message_secrets);
170
171 let external_init_proposal = Proposal::ExternalInit(ExternalInitProposal::from(kem_output));
172
173 let mut inline_proposals = vec![external_init_proposal];
174
175 let signature_key = params.credential_with_key().signature_key.as_slice();
178 if let Some(us) = public_group
179 .members()
180 .find(|member| member.signature_key == signature_key)
181 {
182 let remove_proposal = Proposal::Remove(RemoveProposal { removed: us.index });
183 inline_proposals.push(remove_proposal);
184 };
185
186 let own_leaf_index = public_group.leftmost_free_index(inline_proposals.iter().map(Some))?;
187 params.set_inline_proposals(inline_proposals);
188
189 let mut mls_group = MlsGroup {
190 mls_group_config: mls_group_config.clone(),
191 own_leaf_nodes: vec![],
192 aad: vec![],
193 group_state: MlsGroupState::Operational,
194 public_group,
195 group_epoch_secrets,
196 own_leaf_index,
197 message_secrets_store,
198 resumption_psk_store: ResumptionPskStore::new(32),
199 };
200
201 mls_group.set_max_past_epochs(mls_group_config.max_past_epochs);
202
203 let create_commit_result = mls_group
205 .create_external_commit(params, provider, signer)
206 .map_err(|_| ExternalCommitError::CommitError)?;
207
208 mls_group.group_state = MlsGroupState::PendingCommit(Box::new(
209 PendingCommitState::External(create_commit_result.staged_commit),
210 ));
211
212 mls_group
213 .store(provider.storage())
214 .map_err(ExternalCommitError::StorageError)?;
215
216 let public_message: PublicMessage = create_commit_result.commit.into();
217
218 Ok((
219 mls_group,
220 public_message.into(),
221 create_commit_result.group_info,
222 ))
223 }
224}
225
226impl ProcessedWelcome {
227 pub fn new_from_welcome<Provider: OpenMlsProvider>(
234 provider: &Provider,
235 mls_group_config: &MlsGroupJoinConfig,
236 welcome: Welcome,
237 ) -> Result<Self, WelcomeError<Provider::StorageError>> {
238 let (resumption_psk_store, key_package_bundle) =
239 keys_for_welcome(mls_group_config, &welcome, provider)?;
240
241 let ciphersuite = welcome.ciphersuite();
242 let Some(egs) = welcome.find_encrypted_group_secret(
243 key_package_bundle
244 .key_package()
245 .hash_ref(provider.crypto())?,
246 ) else {
247 return Err(WelcomeError::JoinerSecretNotFound);
248 };
249
250 if welcome.ciphersuite() != key_package_bundle.key_package().ciphersuite() {
253 let e = WelcomeError::CiphersuiteMismatch;
254 log::debug!("new_from_welcome {:?}", e);
255 return Err(e);
256 }
257
258 let group_secrets = GroupSecrets::try_from_ciphertext(
259 key_package_bundle.init_private_key(),
260 egs.encrypted_group_secrets(),
261 welcome.encrypted_group_info(),
262 ciphersuite,
263 provider.crypto(),
264 )?;
265 let psk_secret = {
266 let psks = load_psks(
267 provider.storage(),
268 &resumption_psk_store,
269 &group_secrets.psks,
270 )?;
271
272 PskSecret::new(provider.crypto(), ciphersuite, psks)?
273 };
274 let key_schedule = KeySchedule::init(
275 ciphersuite,
276 provider.crypto(),
277 &group_secrets.joiner_secret,
278 psk_secret,
279 )?;
280 let (welcome_key, welcome_nonce) = key_schedule
281 .welcome(provider.crypto(), ciphersuite)
282 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?
283 .derive_welcome_key_nonce(provider.crypto(), ciphersuite)
284 .map_err(LibraryError::unexpected_crypto_error)?;
285 let verifiable_group_info = VerifiableGroupInfo::try_from_ciphertext(
286 &welcome_key,
287 &welcome_nonce,
288 welcome.encrypted_group_info(),
289 &[],
290 provider.crypto(),
291 )?;
292 if let Some(required_capabilities) =
293 verifiable_group_info.extensions().required_capabilities()
294 {
295 key_package_bundle
298 .key_package()
299 .leaf_node()
300 .capabilities()
301 .supports_required_capabilities(required_capabilities)?;
302 }
303
304 if verifiable_group_info.ciphersuite() != key_package_bundle.key_package().ciphersuite() {
308 let e = WelcomeError::CiphersuiteMismatch;
309 log::debug!("new_from_welcome {:?}", e);
310 return Err(e);
311 }
312
313 Ok(Self {
314 mls_group_config: mls_group_config.clone(),
315 ciphersuite,
316 group_secrets,
317 key_schedule,
318 verifiable_group_info,
319 resumption_psk_store,
320 key_package_bundle,
321 })
322 }
323
324 pub fn unverified_group_info(&self) -> &VerifiableGroupInfo {
328 &self.verifiable_group_info
329 }
330
331 pub fn psks(&self) -> &[PreSharedKeyId] {
335 &self.group_secrets.psks
336 }
337
338 pub fn into_staged_welcome<Provider: OpenMlsProvider>(
341 mut self,
342 provider: &Provider,
343 ratchet_tree: Option<RatchetTreeIn>,
344 ) -> Result<StagedWelcome, WelcomeError<Provider::StorageError>> {
345 let ratchet_tree = match self.verifiable_group_info.extensions().ratchet_tree() {
352 Some(extension) => extension.ratchet_tree().clone(),
353 None => match ratchet_tree {
354 Some(ratchet_tree) => ratchet_tree,
355 None => return Err(WelcomeError::MissingRatchetTree),
356 },
357 };
358
359 let (public_group, _group_info_extensions) = PublicGroup::from_external_internal(
362 provider.crypto(),
363 ratchet_tree,
364 self.verifiable_group_info.clone(),
365 ProposalStore::new(),
366 )?;
367
368 let own_leaf_index = public_group
370 .members()
371 .find_map(|m| {
372 if m.signature_key
373 == self
374 .key_package_bundle
375 .key_package()
376 .leaf_node()
377 .signature_key()
378 .as_slice()
379 {
380 Some(m.index)
381 } else {
382 None
383 }
384 })
385 .ok_or(WelcomeError::PublicTreeError(
386 PublicTreeError::MalformedTree,
387 ))?;
388
389 let (group_epoch_secrets, message_secrets) = {
390 let serialized_group_context = public_group
391 .group_context()
392 .tls_serialize_detached()
393 .map_err(LibraryError::missing_bound_check)?;
394
395 self.key_schedule
397 .add_context(provider.crypto(), &serialized_group_context)
398 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?;
399
400 let epoch_secrets = self
401 .key_schedule
402 .epoch_secrets(provider.crypto(), self.ciphersuite)
403 .map_err(|_| LibraryError::custom("Using the key schedule in the wrong state"))?;
404
405 epoch_secrets.split_secrets(
406 serialized_group_context,
407 public_group.tree_size(),
408 own_leaf_index,
409 )
410 };
411
412 let confirmation_tag = message_secrets
413 .confirmation_key()
414 .tag(
415 provider.crypto(),
416 self.ciphersuite,
417 public_group.group_context().confirmed_transcript_hash(),
418 )
419 .map_err(LibraryError::unexpected_crypto_error)?;
420
421 if &confirmation_tag != public_group.confirmation_tag() {
424 log::error!("Confirmation tag mismatch");
425 log_crypto!(trace, " Got: {:x?}", confirmation_tag);
426 log_crypto!(trace, " Expected: {:x?}", public_group.confirmation_tag());
427 debug_assert!(false, "Confirmation tag mismatch");
428
429 if !crate::skip_validation::is_disabled::confirmation_tag() {
432 return Err(WelcomeError::ConfirmationTagMismatch);
433 }
434 }
435
436 let message_secrets_store = MessageSecretsStore::new_with_secret(0, message_secrets);
437
438 let resumption_psk = group_epoch_secrets.resumption_psk();
440 self.resumption_psk_store
441 .add(public_group.group_context().epoch(), resumption_psk.clone());
442
443 let welcome_sender_index = self.verifiable_group_info.signer();
444 let path_keypairs = if let Some(path_secret) = self.group_secrets.path_secret {
445 let (path_keypairs, _commit_secret) = public_group
446 .derive_path_secrets(
447 provider.crypto(),
448 self.ciphersuite,
449 path_secret,
450 welcome_sender_index,
451 own_leaf_index,
452 )
453 .map_err(|e| match e {
454 DerivePathError::LibraryError(e) => e.into(),
455 DerivePathError::PublicKeyMismatch => {
456 WelcomeError::PublicTreeError(PublicTreeError::PublicKeyMismatch)
457 }
458 })?;
459 Some(path_keypairs)
460 } else {
461 None
462 };
463
464 let staged_welcome = StagedWelcome {
465 mls_group_config: self.mls_group_config,
466 public_group,
467 group_epoch_secrets,
468 own_leaf_index,
469 message_secrets_store,
470 resumption_psk_store: self.resumption_psk_store,
471 verifiable_group_info: self.verifiable_group_info,
472 key_package_bundle: self.key_package_bundle,
473 path_keypairs,
474 };
475
476 Ok(staged_welcome)
477 }
478}
479
480impl StagedWelcome {
481 pub fn new_from_welcome<Provider: OpenMlsProvider>(
489 provider: &Provider,
490 mls_group_config: &MlsGroupJoinConfig,
491 welcome: Welcome,
492 ratchet_tree: Option<RatchetTreeIn>,
493 ) -> Result<Self, WelcomeError<Provider::StorageError>> {
494 let processed_welcome =
495 ProcessedWelcome::new_from_welcome(provider, mls_group_config, welcome)?;
496
497 processed_welcome.into_staged_welcome(provider, ratchet_tree)
498 }
499
500 pub fn welcome_sender_index(&self) -> LeafNodeIndex {
504 self.verifiable_group_info.signer()
505 }
506
507 pub fn welcome_sender(&self) -> Result<&LeafNode, LibraryError> {
511 let sender_index = self.welcome_sender_index();
512 self.public_group
513 .leaf(sender_index)
514 .ok_or(LibraryError::custom(
515 "no leaf with given welcome sender index exists",
516 ))
517 }
518
519 pub fn group_context(&self) -> &GroupContext {
521 self.public_group.group_context()
522 }
523
524 pub fn members(&self) -> impl Iterator<Item = Member> + '_ {
526 self.public_group.members()
527 }
528
529 pub fn into_group<Provider: OpenMlsProvider>(
531 self,
532 provider: &Provider,
533 ) -> Result<MlsGroup, WelcomeError<Provider::StorageError>> {
534 let group_keypairs = if let Some(path_keypairs) = self.path_keypairs {
537 let mut keypairs = vec![self.key_package_bundle.encryption_key_pair()];
538 keypairs.extend_from_slice(&path_keypairs);
539 keypairs
540 } else {
541 vec![self.key_package_bundle.encryption_key_pair()]
542 };
543
544 let mut mls_group = MlsGroup {
545 mls_group_config: self.mls_group_config,
546 own_leaf_nodes: vec![],
547 aad: vec![],
548 group_state: MlsGroupState::Operational,
549 public_group: self.public_group,
550 group_epoch_secrets: self.group_epoch_secrets,
551 own_leaf_index: self.own_leaf_index,
552 message_secrets_store: self.message_secrets_store,
553 resumption_psk_store: self.resumption_psk_store,
554 };
555
556 mls_group
557 .store_epoch_keypairs(provider.storage(), group_keypairs.as_slice())
558 .map_err(WelcomeError::StorageError)?;
559 mls_group.set_max_past_epochs(mls_group.mls_group_config.max_past_epochs);
560
561 mls_group
562 .store(provider.storage())
563 .map_err(WelcomeError::StorageError)?;
564
565 Ok(mls_group)
566 }
567}
568
569fn keys_for_welcome<Provider: OpenMlsProvider>(
570 mls_group_config: &MlsGroupJoinConfig,
571 welcome: &Welcome,
572 provider: &Provider,
573) -> Result<
574 (ResumptionPskStore, KeyPackageBundle),
575 WelcomeError<<Provider as OpenMlsProvider>::StorageError>,
576> {
577 let resumption_psk_store = ResumptionPskStore::new(mls_group_config.number_of_resumption_psks);
578 let key_package_bundle: KeyPackageBundle = welcome
579 .secrets()
580 .iter()
581 .find_map(|egs| {
582 let hash_ref = egs.new_member();
583
584 provider
585 .storage()
586 .key_package(&hash_ref)
587 .map_err(WelcomeError::StorageError)
588 .transpose()
589 })
590 .ok_or(WelcomeError::NoMatchingKeyPackage)??;
591 if !key_package_bundle.key_package().last_resort() {
592 provider
593 .storage()
594 .delete_key_package(&key_package_bundle.key_package.hash_ref(provider.crypto())?)
595 .map_err(WelcomeError::StorageError)?;
596 } else {
597 log::debug!("Key package has last resort extension, not deleting");
598 }
599 Ok((resumption_psk_store, key_package_bundle))
600}