1use create_commit::CreateCommitParams;
7use past_secrets::MessageSecretsStore;
8use proposal_store::ProposalQueue;
9use serde::{Deserialize, Serialize};
10use staged_commit::{MemberStagedCommitState, StagedCommitState};
11use tls_codec::Serialize as _;
12
13#[cfg(test)]
14use crate::treesync::node::leaf_node::TreePosition;
15
16use super::{
17 diff::compute_path::PathComputationResult,
18 proposal_store::{ProposalStore, QueuedProposal},
19};
20use crate::{
21 binary_tree::array_representation::LeafNodeIndex,
22 ciphersuite::{hash_ref::ProposalRef, signable::Signable},
23 credentials::Credential,
24 error::LibraryError,
25 framing::{mls_auth_content::AuthenticatedContent, *},
26 group::{
27 CreateCommitError, CreateGroupContextExtProposalError, Extension, ExtensionType,
28 Extensions, ExternalPubExtension, GroupContext, GroupEpoch, GroupId, MlsGroupJoinConfig,
29 MlsGroupStateError, OutgoingWireFormatPolicy, ProposalQueueError, PublicGroup,
30 RatchetTreeExtension, RequiredCapabilitiesExtension, StagedCommit,
31 },
32 key_packages::KeyPackageBundle,
33 messages::{
34 group_info::{GroupInfo, GroupInfoTBS, VerifiableGroupInfo},
35 proposals::*,
36 Commit, ConfirmationTag, GroupSecrets, Welcome,
37 },
38 schedule::{
39 message_secrets::MessageSecrets,
40 psk::{load_psks, store::ResumptionPskStore, PskSecret},
41 GroupEpochSecrets, JoinerSecret, KeySchedule,
42 },
43 storage::{OpenMlsProvider, StorageProvider},
44 treesync::{
45 node::{encryption_keys::EncryptionKeyPair, leaf_node::LeafNode},
46 RatchetTree,
47 },
48 versions::ProtocolVersion,
49};
50use openmls_traits::{signatures::Signer, storage::StorageProvider as _, types::Ciphersuite};
51
52mod application;
54mod creation;
55mod exporting;
56mod updates;
57
58use config::*;
59
60pub(crate) mod builder;
62pub(crate) mod commit_builder;
63pub(crate) mod config;
64pub(crate) mod create_commit;
65pub(crate) mod errors;
66pub(crate) mod membership;
67pub(crate) mod past_secrets;
68pub(crate) mod processing;
69pub(crate) mod proposal;
70pub(crate) mod proposal_store;
71pub(crate) mod staged_commit;
72
73#[cfg(test)]
75pub(crate) mod tests_and_kats;
76
77#[derive(Debug)]
78pub(crate) struct CreateCommitResult {
79 pub(crate) commit: AuthenticatedContent,
80 pub(crate) welcome_option: Option<Welcome>,
81 pub(crate) staged_commit: StagedCommit,
82 pub(crate) group_info: Option<GroupInfo>,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct Member {
88 pub index: LeafNodeIndex,
90 pub credential: Credential,
92 pub encryption_key: Vec<u8>,
94 pub signature_key: Vec<u8>,
96}
97
98impl Member {
99 pub fn new(
101 index: LeafNodeIndex,
102 encryption_key: Vec<u8>,
103 signature_key: Vec<u8>,
104 credential: Credential,
105 ) -> Self {
106 Self {
107 index,
108 encryption_key,
109 signature_key,
110 credential,
111 }
112 }
113}
114
115#[derive(Debug, Serialize, Deserialize)]
118#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
119pub enum PendingCommitState {
120 Member(StagedCommit),
122 External(StagedCommit),
124}
125
126impl PendingCommitState {
127 pub(crate) fn staged_commit(&self) -> &StagedCommit {
130 match self {
131 PendingCommitState::Member(pc) => pc,
132 PendingCommitState::External(pc) => pc,
133 }
134 }
135}
136
137impl From<PendingCommitState> for StagedCommit {
138 fn from(pcs: PendingCommitState) -> Self {
139 match pcs {
140 PendingCommitState::Member(pc) => pc,
141 PendingCommitState::External(pc) => pc,
142 }
143 }
144}
145
146#[derive(Debug, Serialize, Deserialize)]
191#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
192pub enum MlsGroupState {
193 PendingCommit(Box<PendingCommitState>),
195 Operational,
197 Inactive,
199}
200
201#[derive(Debug)]
226#[cfg_attr(feature = "test-utils", derive(Clone, PartialEq))]
227pub struct MlsGroup {
228 mls_group_config: MlsGroupJoinConfig,
230 public_group: PublicGroup,
232 group_epoch_secrets: GroupEpochSecrets,
234 own_leaf_index: LeafNodeIndex,
236 message_secrets_store: MessageSecretsStore,
243 resumption_psk_store: ResumptionPskStore,
245 own_leaf_nodes: Vec<LeafNode>,
249 aad: Vec<u8>,
253 group_state: MlsGroupState,
256}
257
258impl MlsGroup {
259 pub fn configuration(&self) -> &MlsGroupJoinConfig {
263 &self.mls_group_config
264 }
265
266 pub fn set_configuration<Storage: StorageProvider>(
268 &mut self,
269 storage: &Storage,
270 mls_group_config: &MlsGroupJoinConfig,
271 ) -> Result<(), Storage::Error> {
272 self.mls_group_config = mls_group_config.clone();
273 storage.write_mls_join_config(self.group_id(), mls_group_config)
274 }
275
276 pub fn set_aad(&mut self, aad: Vec<u8>) {
280 self.aad = aad;
281 }
282
283 pub fn aad(&self) -> &[u8] {
286 &self.aad
287 }
288
289 pub fn ciphersuite(&self) -> Ciphersuite {
293 self.public_group.ciphersuite()
294 }
295
296 pub fn confirmation_tag(&self) -> &ConfirmationTag {
298 self.public_group.confirmation_tag()
299 }
300
301 pub fn is_active(&self) -> bool {
304 !matches!(self.group_state, MlsGroupState::Inactive)
305 }
306
307 pub fn credential(&self) -> Result<&Credential, MlsGroupStateError> {
310 if !self.is_active() {
311 return Err(MlsGroupStateError::UseAfterEviction);
312 }
313 self.public_group
314 .leaf(self.own_leaf_index())
315 .map(|node| node.credential())
316 .ok_or_else(|| LibraryError::custom("Own leaf node missing").into())
317 }
318
319 pub fn own_leaf_index(&self) -> LeafNodeIndex {
321 self.own_leaf_index
322 }
323
324 pub fn own_leaf_node(&self) -> Option<&LeafNode> {
326 self.public_group().leaf(self.own_leaf_index())
327 }
328
329 pub fn group_id(&self) -> &GroupId {
331 self.public_group.group_id()
332 }
333
334 pub fn epoch(&self) -> GroupEpoch {
336 self.public_group.group_context().epoch()
337 }
338
339 pub fn pending_proposals(&self) -> impl Iterator<Item = &QueuedProposal> {
341 self.proposal_store().proposals()
342 }
343
344 pub fn pending_commit(&self) -> Option<&StagedCommit> {
348 match self.group_state {
349 MlsGroupState::PendingCommit(ref pending_commit_state) => {
350 Some(pending_commit_state.staged_commit())
351 }
352 MlsGroupState::Operational => None,
353 MlsGroupState::Inactive => None,
354 }
355 }
356
357 pub fn clear_pending_commit<Storage: StorageProvider>(
369 &mut self,
370 storage: &Storage,
371 ) -> Result<(), Storage::Error> {
372 match self.group_state {
373 MlsGroupState::PendingCommit(ref pending_commit_state) => {
374 if let PendingCommitState::Member(_) = **pending_commit_state {
375 self.group_state = MlsGroupState::Operational;
376 storage.write_group_state(self.group_id(), &self.group_state)
377 } else {
378 Ok(())
379 }
380 }
381 MlsGroupState::Operational | MlsGroupState::Inactive => Ok(()),
382 }
383 }
384
385 pub fn clear_pending_proposals<Storage: StorageProvider>(
392 &mut self,
393 storage: &Storage,
394 ) -> Result<(), Storage::Error> {
395 if !self.proposal_store().is_empty() {
397 self.proposal_store_mut().empty();
399
400 storage.clear_proposal_queue::<GroupId, ProposalRef>(self.group_id())?;
402 }
403
404 Ok(())
405 }
406
407 pub fn extensions(&self) -> &Extensions {
409 self.public_group().group_context().extensions()
410 }
411
412 pub fn ext_commit_sender_index(
414 &self,
415 commit: &StagedCommit,
416 ) -> Result<LeafNodeIndex, LibraryError> {
417 self.public_group().ext_commit_sender_index(commit)
418 }
419
420 pub fn load<Storage: crate::storage::StorageProvider>(
424 storage: &Storage,
425 group_id: &GroupId,
426 ) -> Result<Option<MlsGroup>, Storage::Error> {
427 let public_group = PublicGroup::load(storage, group_id)?;
428 let group_epoch_secrets = storage.group_epoch_secrets(group_id)?;
429 let own_leaf_index = storage.own_leaf_index(group_id)?;
430 let message_secrets_store = storage.message_secrets(group_id)?;
431 let resumption_psk_store = storage.resumption_psk_store(group_id)?;
432 let mls_group_config = storage.mls_group_join_config(group_id)?;
433 let own_leaf_nodes = storage.own_leaf_nodes(group_id)?;
434 let group_state = storage.group_state(group_id)?;
435
436 let build = || -> Option<Self> {
437 Some(Self {
438 public_group: public_group?,
439 group_epoch_secrets: group_epoch_secrets?,
440 own_leaf_index: own_leaf_index?,
441 message_secrets_store: message_secrets_store?,
442 resumption_psk_store: resumption_psk_store?,
443 mls_group_config: mls_group_config?,
444 own_leaf_nodes,
445 aad: vec![],
446 group_state: group_state?,
447 })
448 };
449
450 Ok(build())
451 }
452
453 pub fn delete<Storage: crate::storage::StorageProvider>(
457 &mut self,
458 storage: &Storage,
459 ) -> Result<(), Storage::Error> {
460 PublicGroup::delete(storage, self.group_id())?;
461 storage.delete_own_leaf_index(self.group_id())?;
462 storage.delete_group_epoch_secrets(self.group_id())?;
463 storage.delete_message_secrets(self.group_id())?;
464 storage.delete_all_resumption_psk_secrets(self.group_id())?;
465 storage.delete_group_config(self.group_id())?;
466 storage.delete_own_leaf_nodes(self.group_id())?;
467 storage.delete_group_state(self.group_id())?;
468 storage.clear_proposal_queue::<GroupId, ProposalRef>(self.group_id())?;
469
470 self.proposal_store_mut().empty();
471 storage.delete_encryption_epoch_key_pairs(
472 self.group_id(),
473 &self.epoch(),
474 self.own_leaf_index().u32(),
475 )?;
476
477 Ok(())
478 }
479
480 pub fn export_ratchet_tree(&self) -> RatchetTree {
484 self.public_group().export_ratchet_tree()
485 }
486}
487
488impl MlsGroup {
490 pub(crate) fn required_capabilities(&self) -> Option<&RequiredCapabilitiesExtension> {
492 self.public_group.required_capabilities()
493 }
494
495 pub(crate) fn group_epoch_secrets(&self) -> &GroupEpochSecrets {
497 &self.group_epoch_secrets
498 }
499
500 pub(crate) fn message_secrets(&self) -> &MessageSecrets {
502 self.message_secrets_store.message_secrets()
503 }
504
505 pub(crate) fn set_max_past_epochs(&mut self, max_past_epochs: usize) {
509 self.message_secrets_store.resize(max_past_epochs);
510 }
511
512 pub(crate) fn message_secrets_mut(
514 &mut self,
515 epoch: GroupEpoch,
516 ) -> Result<&mut MessageSecrets, SecretTreeError> {
517 if epoch < self.context().epoch() {
518 self.message_secrets_store
519 .secrets_for_epoch_mut(epoch)
520 .ok_or(SecretTreeError::TooDistantInThePast)
521 } else {
522 Ok(self.message_secrets_store.message_secrets_mut())
523 }
524 }
525
526 pub(crate) fn message_secrets_for_epoch(
528 &self,
529 epoch: GroupEpoch,
530 ) -> Result<&MessageSecrets, SecretTreeError> {
531 if epoch < self.context().epoch() {
532 self.message_secrets_store
533 .secrets_for_epoch(epoch)
534 .ok_or(SecretTreeError::TooDistantInThePast)
535 } else {
536 Ok(self.message_secrets_store.message_secrets())
537 }
538 }
539
540 pub(crate) fn message_secrets_and_leaves_mut(
546 &mut self,
547 epoch: GroupEpoch,
548 ) -> Result<(&mut MessageSecrets, &[Member]), SecretTreeError> {
549 if epoch < self.context().epoch() {
550 self.message_secrets_store
551 .secrets_and_leaves_for_epoch_mut(epoch)
552 .ok_or(SecretTreeError::TooDistantInThePast)
553 } else {
554 Ok((self.message_secrets_store.message_secrets_mut(), &[]))
557 }
558 }
559
560 pub(crate) fn create_group_context_ext_proposal<Provider: OpenMlsProvider>(
562 &self,
563 framing_parameters: FramingParameters,
564 extensions: Extensions,
565 signer: &impl Signer,
566 ) -> Result<AuthenticatedContent, CreateGroupContextExtProposalError<Provider::StorageError>>
567 {
568 let required_extension = extensions
570 .iter()
571 .find(|extension| extension.extension_type() == ExtensionType::RequiredCapabilities);
572 if let Some(required_extension) = required_extension {
573 let required_capabilities = required_extension.as_required_capabilities_extension()?;
574 self.own_leaf_node()
576 .ok_or_else(|| LibraryError::custom("Tree has no own leaf."))?
577 .capabilities()
578 .supports_required_capabilities(required_capabilities)?;
579
580 self.public_group()
583 .check_extension_support(required_capabilities.extension_types())?;
584 }
585 let proposal = GroupContextExtensionProposal::new(extensions);
586 let proposal = Proposal::GroupContextExtensions(proposal);
587 AuthenticatedContent::member_proposal(
588 framing_parameters,
589 self.own_leaf_index(),
590 proposal,
591 self.context(),
592 signer,
593 )
594 .map_err(|e| e.into())
595 }
596
597 pub(crate) fn encrypt<Provider: OpenMlsProvider>(
599 &mut self,
600 public_message: AuthenticatedContent,
601 provider: &Provider,
602 ) -> Result<PrivateMessage, MessageEncryptionError<Provider::StorageError>> {
603 let padding_size = self.configuration().padding_size();
604 let msg = PrivateMessage::try_from_authenticated_content(
605 provider.crypto(),
606 provider.rand(),
607 &public_message,
608 self.ciphersuite(),
609 self.message_secrets_store.message_secrets_mut(),
610 padding_size,
611 )?;
612
613 provider
614 .storage()
615 .write_message_secrets(self.group_id(), &self.message_secrets_store)
616 .map_err(MessageEncryptionError::StorageError)?;
617
618 Ok(msg)
619 }
620
621 pub(crate) fn framing_parameters(&self) -> FramingParameters {
623 FramingParameters::new(
624 &self.aad,
625 self.mls_group_config.wire_format_policy().outgoing(),
626 )
627 }
628
629 pub(crate) fn proposal_store(&self) -> &ProposalStore {
631 self.public_group.proposal_store()
632 }
633
634 pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore {
636 self.public_group.proposal_store_mut()
637 }
638
639 pub(crate) fn context(&self) -> &GroupContext {
641 self.public_group.group_context()
642 }
643
644 pub(crate) fn version(&self) -> ProtocolVersion {
646 self.public_group.version()
647 }
648
649 #[inline]
651 pub(crate) fn reset_aad(&mut self) {
652 self.aad.clear();
653 }
654
655 pub(crate) fn public_group(&self) -> &PublicGroup {
657 &self.public_group
658 }
659}
660
661impl MlsGroup {
663 pub(super) fn store_epoch_keypairs<Storage: StorageProvider>(
668 &self,
669 store: &Storage,
670 keypair_references: &[EncryptionKeyPair],
671 ) -> Result<(), Storage::Error> {
672 store.write_encryption_epoch_key_pairs(
673 self.group_id(),
674 &self.context().epoch(),
675 self.own_leaf_index().u32(),
676 keypair_references,
677 )
678 }
679
680 pub(super) fn read_epoch_keypairs<Storage: StorageProvider>(
685 &self,
686 store: &Storage,
687 ) -> Result<Vec<EncryptionKeyPair>, Storage::Error> {
688 store.encryption_epoch_key_pairs(
689 self.group_id(),
690 &self.context().epoch(),
691 self.own_leaf_index().u32(),
692 )
693 }
694
695 pub(super) fn delete_previous_epoch_keypairs<Storage: StorageProvider>(
700 &self,
701 store: &Storage,
702 ) -> Result<(), Storage::Error> {
703 store.delete_encryption_epoch_key_pairs(
704 self.group_id(),
705 &GroupEpoch::from(self.context().epoch().as_u64() - 1),
706 self.own_leaf_index().u32(),
707 )
708 }
709
710 pub(super) fn store<Storage: crate::storage::StorageProvider>(
713 &self,
714 storage: &Storage,
715 ) -> Result<(), Storage::Error> {
716 self.public_group.store(storage)?;
717 storage.write_group_epoch_secrets(self.group_id(), &self.group_epoch_secrets)?;
718 storage.write_own_leaf_index(self.group_id(), &self.own_leaf_index)?;
719 storage.write_message_secrets(self.group_id(), &self.message_secrets_store)?;
720 storage.write_resumption_psk_store(self.group_id(), &self.resumption_psk_store)?;
721 storage.write_mls_join_config(self.group_id(), &self.mls_group_config)?;
722 storage.write_group_state(self.group_id(), &self.group_state)?;
723
724 Ok(())
725 }
726
727 fn content_to_mls_message(
731 &mut self,
732 mls_auth_content: AuthenticatedContent,
733 provider: &impl OpenMlsProvider,
734 ) -> Result<MlsMessageOut, LibraryError> {
735 let msg = match self.configuration().wire_format_policy().outgoing() {
736 OutgoingWireFormatPolicy::AlwaysPlaintext => {
737 let mut plaintext: PublicMessage = mls_auth_content.into();
738 if plaintext.sender().is_member() {
740 plaintext.set_membership_tag(
741 provider.crypto(),
742 self.ciphersuite(),
743 self.message_secrets().membership_key(),
744 self.message_secrets().serialized_context(),
745 )?;
746 }
747 plaintext.into()
748 }
749 OutgoingWireFormatPolicy::AlwaysCiphertext => {
750 let ciphertext = self
751 .encrypt(mls_auth_content, provider)
752 .map_err(|_| LibraryError::custom("Malformed plaintext"))?;
754 MlsMessageOut::from_private_message(ciphertext, self.version())
755 }
756 };
757 Ok(msg)
758 }
759
760 fn is_operational(&self) -> Result<(), MlsGroupStateError> {
763 match self.group_state {
764 MlsGroupState::PendingCommit(_) => Err(MlsGroupStateError::PendingCommit),
765 MlsGroupState::Inactive => Err(MlsGroupStateError::UseAfterEviction),
766 MlsGroupState::Operational => Ok(()),
767 }
768 }
769}
770
771impl MlsGroup {
773 #[cfg(any(feature = "test-utils", test))]
774 pub fn export_group_context(&self) -> &GroupContext {
775 self.context()
776 }
777
778 #[cfg(any(feature = "test-utils", test))]
779 pub fn tree_hash(&self) -> &[u8] {
780 self.public_group().group_context().tree_hash()
781 }
782
783 #[cfg(any(feature = "test-utils", test))]
784 pub(crate) fn message_secrets_test_mut(&mut self) -> &mut MessageSecrets {
785 self.message_secrets_store.message_secrets_mut()
786 }
787
788 #[cfg(any(feature = "test-utils", test))]
789 pub fn print_ratchet_tree(&self, message: &str) {
790 println!("{}: {}", message, self.public_group().export_ratchet_tree());
791 }
792
793 #[cfg(any(feature = "test-utils", test))]
794 pub(crate) fn context_mut(&mut self) -> &mut GroupContext {
795 self.public_group.context_mut()
796 }
797
798 #[cfg(test)]
799 pub(crate) fn set_own_leaf_index(&mut self, own_leaf_index: LeafNodeIndex) {
800 self.own_leaf_index = own_leaf_index;
801 }
802
803 #[cfg(test)]
804 pub(crate) fn own_tree_position(&self) -> TreePosition {
805 TreePosition::new(self.group_id().clone(), self.own_leaf_index())
806 }
807
808 #[cfg(test)]
809 pub(crate) fn message_secrets_store(&self) -> &MessageSecretsStore {
810 &self.message_secrets_store
811 }
812
813 #[cfg(test)]
814 pub(crate) fn resumption_psk_store(&self) -> &ResumptionPskStore {
815 &self.resumption_psk_store
816 }
817
818 #[cfg(test)]
819 pub(crate) fn set_group_context(&mut self, group_context: GroupContext) {
820 self.public_group.set_group_context(group_context)
821 }
822}
823
824#[derive(Debug)]
827pub struct StagedWelcome {
828 mls_group_config: MlsGroupJoinConfig,
830 public_group: PublicGroup,
831 group_epoch_secrets: GroupEpochSecrets,
832 own_leaf_index: LeafNodeIndex,
833
834 message_secrets_store: MessageSecretsStore,
841
842 resumption_psk_store: ResumptionPskStore,
844
845 verifiable_group_info: VerifiableGroupInfo,
847
848 key_package_bundle: KeyPackageBundle,
850
851 path_keypairs: Option<Vec<EncryptionKeyPair>>,
853}
854
855pub struct ProcessedWelcome {
862 mls_group_config: MlsGroupJoinConfig,
864
865 ciphersuite: Ciphersuite,
868 group_secrets: GroupSecrets,
869 key_schedule: crate::schedule::KeySchedule,
870 verifiable_group_info: crate::messages::group_info::VerifiableGroupInfo,
871 resumption_psk_store: crate::schedule::psk::store::ResumptionPskStore,
872 key_package_bundle: KeyPackageBundle,
873}