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]), MessageDecryptionError> {
549 if epoch < self.context().epoch() {
550 self.message_secrets_store
551 .secrets_and_leaves_for_epoch_mut(epoch)
552 .ok_or({
553 MessageDecryptionError::SecretTreeError(SecretTreeError::TooDistantInThePast)
554 })
555 } else {
556 Ok((self.message_secrets_store.message_secrets_mut(), &[]))
559 }
560 }
561
562 pub(crate) fn create_group_context_ext_proposal<Provider: OpenMlsProvider>(
564 &self,
565 framing_parameters: FramingParameters,
566 extensions: Extensions,
567 signer: &impl Signer,
568 ) -> Result<AuthenticatedContent, CreateGroupContextExtProposalError<Provider::StorageError>>
569 {
570 let required_extension = extensions
572 .iter()
573 .find(|extension| extension.extension_type() == ExtensionType::RequiredCapabilities);
574 if let Some(required_extension) = required_extension {
575 let required_capabilities = required_extension.as_required_capabilities_extension()?;
576 self.own_leaf_node()
578 .ok_or_else(|| LibraryError::custom("Tree has no own leaf."))?
579 .capabilities()
580 .supports_required_capabilities(required_capabilities)?;
581
582 self.public_group()
585 .check_extension_support(required_capabilities.extension_types())?;
586 }
587 let proposal = GroupContextExtensionProposal::new(extensions);
588 let proposal = Proposal::GroupContextExtensions(proposal);
589 AuthenticatedContent::member_proposal(
590 framing_parameters,
591 self.own_leaf_index(),
592 proposal,
593 self.context(),
594 signer,
595 )
596 .map_err(|e| e.into())
597 }
598
599 pub(crate) fn encrypt<Provider: OpenMlsProvider>(
601 &mut self,
602 public_message: AuthenticatedContent,
603 provider: &Provider,
604 ) -> Result<PrivateMessage, MessageEncryptionError<Provider::StorageError>> {
605 let padding_size = self.configuration().padding_size();
606 let msg = PrivateMessage::try_from_authenticated_content(
607 provider.crypto(),
608 provider.rand(),
609 &public_message,
610 self.ciphersuite(),
611 self.message_secrets_store.message_secrets_mut(),
612 padding_size,
613 )?;
614
615 provider
616 .storage()
617 .write_message_secrets(self.group_id(), &self.message_secrets_store)
618 .map_err(MessageEncryptionError::StorageError)?;
619
620 Ok(msg)
621 }
622
623 pub(crate) fn framing_parameters(&self) -> FramingParameters {
625 FramingParameters::new(
626 &self.aad,
627 self.mls_group_config.wire_format_policy().outgoing(),
628 )
629 }
630
631 pub(crate) fn proposal_store(&self) -> &ProposalStore {
633 self.public_group.proposal_store()
634 }
635
636 pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore {
638 self.public_group.proposal_store_mut()
639 }
640
641 pub(crate) fn context(&self) -> &GroupContext {
643 self.public_group.group_context()
644 }
645
646 pub(crate) fn version(&self) -> ProtocolVersion {
648 self.public_group.version()
649 }
650
651 #[inline]
653 pub(crate) fn reset_aad(&mut self) {
654 self.aad.clear();
655 }
656
657 pub(crate) fn public_group(&self) -> &PublicGroup {
659 &self.public_group
660 }
661}
662
663impl MlsGroup {
665 pub(super) fn store_epoch_keypairs<Storage: StorageProvider>(
670 &self,
671 store: &Storage,
672 keypair_references: &[EncryptionKeyPair],
673 ) -> Result<(), Storage::Error> {
674 store.write_encryption_epoch_key_pairs(
675 self.group_id(),
676 &self.context().epoch(),
677 self.own_leaf_index().u32(),
678 keypair_references,
679 )
680 }
681
682 pub(super) fn read_epoch_keypairs<Storage: StorageProvider>(
687 &self,
688 store: &Storage,
689 ) -> Result<Vec<EncryptionKeyPair>, Storage::Error> {
690 store.encryption_epoch_key_pairs(
691 self.group_id(),
692 &self.context().epoch(),
693 self.own_leaf_index().u32(),
694 )
695 }
696
697 pub(super) fn delete_previous_epoch_keypairs<Storage: StorageProvider>(
702 &self,
703 store: &Storage,
704 ) -> Result<(), Storage::Error> {
705 store.delete_encryption_epoch_key_pairs(
706 self.group_id(),
707 &GroupEpoch::from(self.context().epoch().as_u64() - 1),
708 self.own_leaf_index().u32(),
709 )
710 }
711
712 pub(super) fn store<Storage: crate::storage::StorageProvider>(
715 &self,
716 storage: &Storage,
717 ) -> Result<(), Storage::Error> {
718 self.public_group.store(storage)?;
719 storage.write_group_epoch_secrets(self.group_id(), &self.group_epoch_secrets)?;
720 storage.write_own_leaf_index(self.group_id(), &self.own_leaf_index)?;
721 storage.write_message_secrets(self.group_id(), &self.message_secrets_store)?;
722 storage.write_resumption_psk_store(self.group_id(), &self.resumption_psk_store)?;
723 storage.write_mls_join_config(self.group_id(), &self.mls_group_config)?;
724 storage.write_group_state(self.group_id(), &self.group_state)?;
725
726 Ok(())
727 }
728
729 fn content_to_mls_message(
733 &mut self,
734 mls_auth_content: AuthenticatedContent,
735 provider: &impl OpenMlsProvider,
736 ) -> Result<MlsMessageOut, LibraryError> {
737 let msg = match self.configuration().wire_format_policy().outgoing() {
738 OutgoingWireFormatPolicy::AlwaysPlaintext => {
739 let mut plaintext: PublicMessage = mls_auth_content.into();
740 if plaintext.sender().is_member() {
742 plaintext.set_membership_tag(
743 provider.crypto(),
744 self.ciphersuite(),
745 self.message_secrets().membership_key(),
746 self.message_secrets().serialized_context(),
747 )?;
748 }
749 plaintext.into()
750 }
751 OutgoingWireFormatPolicy::AlwaysCiphertext => {
752 let ciphertext = self
753 .encrypt(mls_auth_content, provider)
754 .map_err(|_| LibraryError::custom("Malformed plaintext"))?;
756 MlsMessageOut::from_private_message(ciphertext, self.version())
757 }
758 };
759 Ok(msg)
760 }
761
762 fn is_operational(&self) -> Result<(), MlsGroupStateError> {
765 match self.group_state {
766 MlsGroupState::PendingCommit(_) => Err(MlsGroupStateError::PendingCommit),
767 MlsGroupState::Inactive => Err(MlsGroupStateError::UseAfterEviction),
768 MlsGroupState::Operational => Ok(()),
769 }
770 }
771}
772
773impl MlsGroup {
775 #[cfg(any(feature = "test-utils", test))]
776 pub fn export_group_context(&self) -> &GroupContext {
777 self.context()
778 }
779
780 #[cfg(any(feature = "test-utils", test))]
781 pub fn tree_hash(&self) -> &[u8] {
782 self.public_group().group_context().tree_hash()
783 }
784
785 #[cfg(any(feature = "test-utils", test))]
786 pub(crate) fn message_secrets_test_mut(&mut self) -> &mut MessageSecrets {
787 self.message_secrets_store.message_secrets_mut()
788 }
789
790 #[cfg(any(feature = "test-utils", test))]
791 pub fn print_ratchet_tree(&self, message: &str) {
792 println!("{}: {}", message, self.public_group().export_ratchet_tree());
793 }
794
795 #[cfg(any(feature = "test-utils", test))]
796 pub(crate) fn context_mut(&mut self) -> &mut GroupContext {
797 self.public_group.context_mut()
798 }
799
800 #[cfg(test)]
801 pub(crate) fn set_own_leaf_index(&mut self, own_leaf_index: LeafNodeIndex) {
802 self.own_leaf_index = own_leaf_index;
803 }
804
805 #[cfg(test)]
806 pub(crate) fn own_tree_position(&self) -> TreePosition {
807 TreePosition::new(self.group_id().clone(), self.own_leaf_index())
808 }
809
810 #[cfg(test)]
811 pub(crate) fn message_secrets_store(&self) -> &MessageSecretsStore {
812 &self.message_secrets_store
813 }
814
815 #[cfg(test)]
816 pub(crate) fn resumption_psk_store(&self) -> &ResumptionPskStore {
817 &self.resumption_psk_store
818 }
819
820 #[cfg(test)]
821 pub(crate) fn set_group_context(&mut self, group_context: GroupContext) {
822 self.public_group.set_group_context(group_context)
823 }
824}
825
826#[derive(Debug)]
829pub struct StagedWelcome {
830 mls_group_config: MlsGroupJoinConfig,
832 public_group: PublicGroup,
833 group_epoch_secrets: GroupEpochSecrets,
834 own_leaf_index: LeafNodeIndex,
835
836 message_secrets_store: MessageSecretsStore,
843
844 resumption_psk_store: ResumptionPskStore,
846
847 verifiable_group_info: VerifiableGroupInfo,
849
850 key_package_bundle: KeyPackageBundle,
852
853 path_keypairs: Option<Vec<EncryptionKeyPair>>,
855}
856
857pub struct ProcessedWelcome {
864 mls_group_config: MlsGroupJoinConfig,
866
867 ciphersuite: Ciphersuite,
870 group_secrets: GroupSecrets,
871 key_schedule: crate::schedule::KeySchedule,
872 verifiable_group_info: crate::messages::group_info::VerifiableGroupInfo,
873 resumption_psk_store: crate::schedule::psk::store::ResumptionPskStore,
874 key_package_bundle: KeyPackageBundle,
875}