1use past_secrets::MessageSecretsStore;
7use proposal_store::ProposalQueue;
8use serde::{Deserialize, Serialize};
9use tls_codec::Serialize as _;
10
11#[cfg(test)]
12use crate::treesync::node::leaf_node::TreePosition;
13
14use super::proposal_store::{ProposalStore, QueuedProposal};
15use crate::{
16 binary_tree::array_representation::LeafNodeIndex,
17 ciphersuite::{hash_ref::ProposalRef, signable::Signable},
18 credentials::Credential,
19 error::LibraryError,
20 framing::{mls_auth_content::AuthenticatedContent, *},
21 group::{
22 CreateGroupContextExtProposalError, Extension, ExtensionType, Extensions,
23 ExternalPubExtension, GroupContext, GroupEpoch, GroupId, MlsGroupJoinConfig,
24 MlsGroupStateError, OutgoingWireFormatPolicy, PublicGroup, RatchetTreeExtension,
25 RequiredCapabilitiesExtension, StagedCommit,
26 },
27 key_packages::KeyPackageBundle,
28 messages::{
29 group_info::{GroupInfo, GroupInfoTBS, VerifiableGroupInfo},
30 proposals::*,
31 ConfirmationTag, GroupSecrets, Welcome,
32 },
33 schedule::{
34 message_secrets::MessageSecrets,
35 psk::{load_psks, store::ResumptionPskStore, PskSecret},
36 GroupEpochSecrets, JoinerSecret, KeySchedule,
37 },
38 storage::{OpenMlsProvider, StorageProvider},
39 treesync::{
40 node::{encryption_keys::EncryptionKeyPair, leaf_node::LeafNode},
41 RatchetTree,
42 },
43 versions::ProtocolVersion,
44};
45use openmls_traits::{signatures::Signer, storage::StorageProvider as _, types::Ciphersuite};
46
47mod application;
49mod exporting;
50mod updates;
51
52use config::*;
53
54pub(crate) mod builder;
56pub(crate) mod commit_builder;
57pub(crate) mod config;
58pub(crate) mod creation;
59pub(crate) mod errors;
60pub(crate) mod membership;
61pub(crate) mod past_secrets;
62pub(crate) mod processing;
63pub(crate) mod proposal;
64pub(crate) mod proposal_store;
65pub(crate) mod staged_commit;
66
67#[cfg(test)]
69pub(crate) mod tests_and_kats;
70
71#[derive(Debug)]
72pub(crate) struct CreateCommitResult {
73 pub(crate) commit: AuthenticatedContent,
74 pub(crate) welcome_option: Option<Welcome>,
75 pub(crate) staged_commit: StagedCommit,
76 pub(crate) group_info: Option<GroupInfo>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
81pub struct Member {
82 pub index: LeafNodeIndex,
84 pub credential: Credential,
86 pub encryption_key: Vec<u8>,
88 pub signature_key: Vec<u8>,
90}
91
92impl Member {
93 pub fn new(
95 index: LeafNodeIndex,
96 encryption_key: Vec<u8>,
97 signature_key: Vec<u8>,
98 credential: Credential,
99 ) -> Self {
100 Self {
101 index,
102 encryption_key,
103 signature_key,
104 credential,
105 }
106 }
107}
108
109#[derive(Debug, Serialize, Deserialize)]
112#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
113pub enum PendingCommitState {
114 Member(StagedCommit),
116 External(StagedCommit),
118}
119
120impl PendingCommitState {
121 pub(crate) fn staged_commit(&self) -> &StagedCommit {
124 match self {
125 PendingCommitState::Member(pc) => pc,
126 PendingCommitState::External(pc) => pc,
127 }
128 }
129}
130
131impl From<PendingCommitState> for StagedCommit {
132 fn from(pcs: PendingCommitState) -> Self {
133 match pcs {
134 PendingCommitState::Member(pc) => pc,
135 PendingCommitState::External(pc) => pc,
136 }
137 }
138}
139
140#[derive(Debug, Serialize, Deserialize)]
185#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
186pub enum MlsGroupState {
187 PendingCommit(Box<PendingCommitState>),
189 Operational,
191 Inactive,
193}
194
195#[derive(Debug)]
220#[cfg_attr(feature = "test-utils", derive(Clone, PartialEq))]
221pub struct MlsGroup {
222 mls_group_config: MlsGroupJoinConfig,
224 public_group: PublicGroup,
226 group_epoch_secrets: GroupEpochSecrets,
228 own_leaf_index: LeafNodeIndex,
230 message_secrets_store: MessageSecretsStore,
237 resumption_psk_store: ResumptionPskStore,
239 own_leaf_nodes: Vec<LeafNode>,
243 aad: Vec<u8>,
247 group_state: MlsGroupState,
250}
251
252impl MlsGroup {
253 pub fn configuration(&self) -> &MlsGroupJoinConfig {
257 &self.mls_group_config
258 }
259
260 pub fn set_configuration<Storage: StorageProvider>(
262 &mut self,
263 storage: &Storage,
264 mls_group_config: &MlsGroupJoinConfig,
265 ) -> Result<(), Storage::Error> {
266 self.mls_group_config = mls_group_config.clone();
267 storage.write_mls_join_config(self.group_id(), mls_group_config)
268 }
269
270 pub fn set_aad(&mut self, aad: Vec<u8>) {
274 self.aad = aad;
275 }
276
277 pub fn aad(&self) -> &[u8] {
280 &self.aad
281 }
282
283 pub fn ciphersuite(&self) -> Ciphersuite {
287 self.public_group.ciphersuite()
288 }
289
290 pub fn confirmation_tag(&self) -> &ConfirmationTag {
292 self.public_group.confirmation_tag()
293 }
294
295 pub fn is_active(&self) -> bool {
298 !matches!(self.group_state, MlsGroupState::Inactive)
299 }
300
301 pub fn credential(&self) -> Result<&Credential, MlsGroupStateError> {
304 if !self.is_active() {
305 return Err(MlsGroupStateError::UseAfterEviction);
306 }
307 self.public_group
308 .leaf(self.own_leaf_index())
309 .map(|node| node.credential())
310 .ok_or_else(|| LibraryError::custom("Own leaf node missing").into())
311 }
312
313 pub fn own_leaf_index(&self) -> LeafNodeIndex {
315 self.own_leaf_index
316 }
317
318 pub fn own_leaf_node(&self) -> Option<&LeafNode> {
320 self.public_group().leaf(self.own_leaf_index())
321 }
322
323 pub fn group_id(&self) -> &GroupId {
325 self.public_group.group_id()
326 }
327
328 pub fn epoch(&self) -> GroupEpoch {
330 self.public_group.group_context().epoch()
331 }
332
333 pub fn pending_proposals(&self) -> impl Iterator<Item = &QueuedProposal> {
335 self.proposal_store().proposals()
336 }
337
338 pub fn pending_commit(&self) -> Option<&StagedCommit> {
342 match self.group_state {
343 MlsGroupState::PendingCommit(ref pending_commit_state) => {
344 Some(pending_commit_state.staged_commit())
345 }
346 MlsGroupState::Operational => None,
347 MlsGroupState::Inactive => None,
348 }
349 }
350
351 pub fn clear_pending_commit<Storage: StorageProvider>(
363 &mut self,
364 storage: &Storage,
365 ) -> Result<(), Storage::Error> {
366 match self.group_state {
367 MlsGroupState::PendingCommit(ref pending_commit_state) => {
368 if let PendingCommitState::Member(_) = **pending_commit_state {
369 self.group_state = MlsGroupState::Operational;
370 storage.write_group_state(self.group_id(), &self.group_state)
371 } else {
372 Ok(())
373 }
374 }
375 MlsGroupState::Operational | MlsGroupState::Inactive => Ok(()),
376 }
377 }
378
379 pub fn clear_pending_proposals<Storage: StorageProvider>(
386 &mut self,
387 storage: &Storage,
388 ) -> Result<(), Storage::Error> {
389 if !self.proposal_store().is_empty() {
391 self.proposal_store_mut().empty();
393
394 storage.clear_proposal_queue::<GroupId, ProposalRef>(self.group_id())?;
396 }
397
398 Ok(())
399 }
400
401 pub fn extensions(&self) -> &Extensions {
403 self.public_group().group_context().extensions()
404 }
405
406 pub fn ext_commit_sender_index(
408 &self,
409 commit: &StagedCommit,
410 ) -> Result<LeafNodeIndex, LibraryError> {
411 self.public_group().ext_commit_sender_index(commit)
412 }
413
414 pub fn load<Storage: crate::storage::StorageProvider>(
418 storage: &Storage,
419 group_id: &GroupId,
420 ) -> Result<Option<MlsGroup>, Storage::Error> {
421 let public_group = PublicGroup::load(storage, group_id)?;
422 let group_epoch_secrets = storage.group_epoch_secrets(group_id)?;
423 let own_leaf_index = storage.own_leaf_index(group_id)?;
424 let message_secrets_store = storage.message_secrets(group_id)?;
425 let resumption_psk_store = storage.resumption_psk_store(group_id)?;
426 let mls_group_config = storage.mls_group_join_config(group_id)?;
427 let own_leaf_nodes = storage.own_leaf_nodes(group_id)?;
428 let group_state = storage.group_state(group_id)?;
429
430 let build = || -> Option<Self> {
431 Some(Self {
432 public_group: public_group?,
433 group_epoch_secrets: group_epoch_secrets?,
434 own_leaf_index: own_leaf_index?,
435 message_secrets_store: message_secrets_store?,
436 resumption_psk_store: resumption_psk_store?,
437 mls_group_config: mls_group_config?,
438 own_leaf_nodes,
439 aad: vec![],
440 group_state: group_state?,
441 })
442 };
443
444 Ok(build())
445 }
446
447 pub fn delete<Storage: crate::storage::StorageProvider>(
451 &mut self,
452 storage: &Storage,
453 ) -> Result<(), Storage::Error> {
454 PublicGroup::delete(storage, self.group_id())?;
455 storage.delete_own_leaf_index(self.group_id())?;
456 storage.delete_group_epoch_secrets(self.group_id())?;
457 storage.delete_message_secrets(self.group_id())?;
458 storage.delete_all_resumption_psk_secrets(self.group_id())?;
459 storage.delete_group_config(self.group_id())?;
460 storage.delete_own_leaf_nodes(self.group_id())?;
461 storage.delete_group_state(self.group_id())?;
462 storage.clear_proposal_queue::<GroupId, ProposalRef>(self.group_id())?;
463
464 self.proposal_store_mut().empty();
465 storage.delete_encryption_epoch_key_pairs(
466 self.group_id(),
467 &self.epoch(),
468 self.own_leaf_index().u32(),
469 )?;
470
471 Ok(())
472 }
473
474 pub fn export_ratchet_tree(&self) -> RatchetTree {
478 self.public_group().export_ratchet_tree()
479 }
480}
481
482impl MlsGroup {
484 pub(crate) fn required_capabilities(&self) -> Option<&RequiredCapabilitiesExtension> {
486 self.public_group.required_capabilities()
487 }
488
489 pub(crate) fn group_epoch_secrets(&self) -> &GroupEpochSecrets {
491 &self.group_epoch_secrets
492 }
493
494 pub(crate) fn message_secrets(&self) -> &MessageSecrets {
496 self.message_secrets_store.message_secrets()
497 }
498
499 pub(crate) fn set_max_past_epochs(&mut self, max_past_epochs: usize) {
503 self.message_secrets_store.resize(max_past_epochs);
504 }
505
506 pub(crate) fn message_secrets_mut(
508 &mut self,
509 epoch: GroupEpoch,
510 ) -> Result<&mut MessageSecrets, SecretTreeError> {
511 if epoch < self.context().epoch() {
512 self.message_secrets_store
513 .secrets_for_epoch_mut(epoch)
514 .ok_or(SecretTreeError::TooDistantInThePast)
515 } else {
516 Ok(self.message_secrets_store.message_secrets_mut())
517 }
518 }
519
520 pub(crate) fn message_secrets_for_epoch(
522 &self,
523 epoch: GroupEpoch,
524 ) -> Result<&MessageSecrets, SecretTreeError> {
525 if epoch < self.context().epoch() {
526 self.message_secrets_store
527 .secrets_for_epoch(epoch)
528 .ok_or(SecretTreeError::TooDistantInThePast)
529 } else {
530 Ok(self.message_secrets_store.message_secrets())
531 }
532 }
533
534 pub(crate) fn message_secrets_and_leaves_mut(
540 &mut self,
541 epoch: GroupEpoch,
542 ) -> Result<(&mut MessageSecrets, &[Member]), SecretTreeError> {
543 if epoch < self.context().epoch() {
544 self.message_secrets_store
545 .secrets_and_leaves_for_epoch_mut(epoch)
546 .ok_or(SecretTreeError::TooDistantInThePast)
547 } else {
548 Ok((self.message_secrets_store.message_secrets_mut(), &[]))
551 }
552 }
553
554 pub(crate) fn create_group_context_ext_proposal<Provider: OpenMlsProvider>(
556 &self,
557 framing_parameters: FramingParameters,
558 extensions: Extensions,
559 signer: &impl Signer,
560 ) -> Result<AuthenticatedContent, CreateGroupContextExtProposalError<Provider::StorageError>>
561 {
562 let required_extension = extensions
564 .iter()
565 .find(|extension| extension.extension_type() == ExtensionType::RequiredCapabilities);
566 if let Some(required_extension) = required_extension {
567 let required_capabilities = required_extension.as_required_capabilities_extension()?;
568 self.own_leaf_node()
570 .ok_or_else(|| LibraryError::custom("Tree has no own leaf."))?
571 .capabilities()
572 .supports_required_capabilities(required_capabilities)?;
573
574 self.public_group()
577 .check_extension_support(required_capabilities.extension_types())?;
578 }
579 let proposal = GroupContextExtensionProposal::new(extensions);
580 let proposal = Proposal::GroupContextExtensions(proposal);
581 AuthenticatedContent::member_proposal(
582 framing_parameters,
583 self.own_leaf_index(),
584 proposal,
585 self.context(),
586 signer,
587 )
588 .map_err(|e| e.into())
589 }
590
591 pub(crate) fn encrypt<Provider: OpenMlsProvider>(
593 &mut self,
594 public_message: AuthenticatedContent,
595 provider: &Provider,
596 ) -> Result<PrivateMessage, MessageEncryptionError<Provider::StorageError>> {
597 let padding_size = self.configuration().padding_size();
598 let msg = PrivateMessage::try_from_authenticated_content(
599 provider.crypto(),
600 provider.rand(),
601 &public_message,
602 self.ciphersuite(),
603 self.message_secrets_store.message_secrets_mut(),
604 padding_size,
605 )?;
606
607 provider
608 .storage()
609 .write_message_secrets(self.group_id(), &self.message_secrets_store)
610 .map_err(MessageEncryptionError::StorageError)?;
611
612 Ok(msg)
613 }
614
615 pub(crate) fn framing_parameters(&self) -> FramingParameters<'_> {
617 FramingParameters::new(
618 &self.aad,
619 self.mls_group_config.wire_format_policy().outgoing(),
620 )
621 }
622
623 pub(crate) fn proposal_store(&self) -> &ProposalStore {
625 self.public_group.proposal_store()
626 }
627
628 pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore {
630 self.public_group.proposal_store_mut()
631 }
632
633 pub(crate) fn context(&self) -> &GroupContext {
635 self.public_group.group_context()
636 }
637
638 pub(crate) fn version(&self) -> ProtocolVersion {
640 self.public_group.version()
641 }
642
643 #[inline]
645 pub(crate) fn reset_aad(&mut self) {
646 self.aad.clear();
647 }
648
649 pub(crate) fn public_group(&self) -> &PublicGroup {
651 &self.public_group
652 }
653}
654
655impl MlsGroup {
657 pub(super) fn store_epoch_keypairs<Storage: StorageProvider>(
662 &self,
663 store: &Storage,
664 keypair_references: &[EncryptionKeyPair],
665 ) -> Result<(), Storage::Error> {
666 store.write_encryption_epoch_key_pairs(
667 self.group_id(),
668 &self.context().epoch(),
669 self.own_leaf_index().u32(),
670 keypair_references,
671 )
672 }
673
674 pub(super) fn read_epoch_keypairs<Storage: StorageProvider>(
679 &self,
680 store: &Storage,
681 ) -> Result<Vec<EncryptionKeyPair>, Storage::Error> {
682 store.encryption_epoch_key_pairs(
683 self.group_id(),
684 &self.context().epoch(),
685 self.own_leaf_index().u32(),
686 )
687 }
688
689 pub(super) fn delete_previous_epoch_keypairs<Storage: StorageProvider>(
694 &self,
695 store: &Storage,
696 ) -> Result<(), Storage::Error> {
697 store.delete_encryption_epoch_key_pairs(
698 self.group_id(),
699 &GroupEpoch::from(self.context().epoch().as_u64() - 1),
700 self.own_leaf_index().u32(),
701 )
702 }
703
704 pub(super) fn store<Storage: crate::storage::StorageProvider>(
707 &self,
708 storage: &Storage,
709 ) -> Result<(), Storage::Error> {
710 self.public_group.store(storage)?;
711 storage.write_group_epoch_secrets(self.group_id(), &self.group_epoch_secrets)?;
712 storage.write_own_leaf_index(self.group_id(), &self.own_leaf_index)?;
713 storage.write_message_secrets(self.group_id(), &self.message_secrets_store)?;
714 storage.write_resumption_psk_store(self.group_id(), &self.resumption_psk_store)?;
715 storage.write_mls_join_config(self.group_id(), &self.mls_group_config)?;
716 storage.write_group_state(self.group_id(), &self.group_state)?;
717
718 Ok(())
719 }
720
721 fn content_to_mls_message(
725 &mut self,
726 mls_auth_content: AuthenticatedContent,
727 provider: &impl OpenMlsProvider,
728 ) -> Result<MlsMessageOut, LibraryError> {
729 let msg = match self.configuration().wire_format_policy().outgoing() {
730 OutgoingWireFormatPolicy::AlwaysPlaintext => {
731 let mut plaintext: PublicMessage = mls_auth_content.into();
732 if plaintext.sender().is_member() {
734 plaintext.set_membership_tag(
735 provider.crypto(),
736 self.ciphersuite(),
737 self.message_secrets().membership_key(),
738 self.message_secrets().serialized_context(),
739 )?;
740 }
741 plaintext.into()
742 }
743 OutgoingWireFormatPolicy::AlwaysCiphertext => {
744 let ciphertext = self
745 .encrypt(mls_auth_content, provider)
746 .map_err(|_| LibraryError::custom("Malformed plaintext"))?;
748 MlsMessageOut::from_private_message(ciphertext, self.version())
749 }
750 };
751 Ok(msg)
752 }
753
754 fn is_operational(&self) -> Result<(), MlsGroupStateError> {
757 match self.group_state {
758 MlsGroupState::PendingCommit(_) => Err(MlsGroupStateError::PendingCommit),
759 MlsGroupState::Inactive => Err(MlsGroupStateError::UseAfterEviction),
760 MlsGroupState::Operational => Ok(()),
761 }
762 }
763}
764
765impl MlsGroup {
767 #[cfg(any(feature = "test-utils", test))]
768 pub fn export_group_context(&self) -> &GroupContext {
769 self.context()
770 }
771
772 #[cfg(any(feature = "test-utils", test))]
773 pub fn tree_hash(&self) -> &[u8] {
774 self.public_group().group_context().tree_hash()
775 }
776
777 #[cfg(any(feature = "test-utils", test))]
778 pub(crate) fn message_secrets_test_mut(&mut self) -> &mut MessageSecrets {
779 self.message_secrets_store.message_secrets_mut()
780 }
781
782 #[cfg(any(feature = "test-utils", test))]
783 pub fn print_ratchet_tree(&self, message: &str) {
784 println!("{}: {}", message, self.public_group().export_ratchet_tree());
785 }
786
787 #[cfg(any(feature = "test-utils", test))]
788 pub(crate) fn context_mut(&mut self) -> &mut GroupContext {
789 self.public_group.context_mut()
790 }
791
792 #[cfg(test)]
793 pub(crate) fn set_own_leaf_index(&mut self, own_leaf_index: LeafNodeIndex) {
794 self.own_leaf_index = own_leaf_index;
795 }
796
797 #[cfg(test)]
798 pub(crate) fn own_tree_position(&self) -> TreePosition {
799 TreePosition::new(self.group_id().clone(), self.own_leaf_index())
800 }
801
802 #[cfg(test)]
803 pub(crate) fn message_secrets_store(&self) -> &MessageSecretsStore {
804 &self.message_secrets_store
805 }
806
807 #[cfg(test)]
808 pub(crate) fn resumption_psk_store(&self) -> &ResumptionPskStore {
809 &self.resumption_psk_store
810 }
811
812 #[cfg(test)]
813 pub(crate) fn set_group_context(&mut self, group_context: GroupContext) {
814 self.public_group.set_group_context(group_context)
815 }
816}
817
818#[derive(Debug)]
821pub struct StagedWelcome {
822 mls_group_config: MlsGroupJoinConfig,
824 public_group: PublicGroup,
825 group_epoch_secrets: GroupEpochSecrets,
826 own_leaf_index: LeafNodeIndex,
827
828 message_secrets_store: MessageSecretsStore,
835
836 resumption_psk_store: ResumptionPskStore,
838
839 verifiable_group_info: VerifiableGroupInfo,
841
842 key_package_bundle: KeyPackageBundle,
844
845 path_keypairs: Option<Vec<EncryptionKeyPair>>,
847}
848
849pub struct ProcessedWelcome {
856 mls_group_config: MlsGroupJoinConfig,
858
859 ciphersuite: Ciphersuite,
862 group_secrets: GroupSecrets,
863 key_schedule: crate::schedule::KeySchedule,
864 verifiable_group_info: crate::messages::group_info::VerifiableGroupInfo,
865 resumption_psk_store: crate::schedule::psk::store::ResumptionPskStore,
866 key_package_bundle: KeyPackageBundle,
867}