Skip to main content

GroupManager

Struct GroupManager 

Source
pub struct GroupManager { /* private fields */ }
Expand description

Manages MLS groups for encrypted messaging.

Implementations§

Source§

impl GroupManager

Source

pub fn new(storage: Arc<dyn MlsStorage>, provider: MlsProvider) -> Self

Creates a new group manager.

Source

pub fn set_enforce_admin_commits(&mut self, enforce: bool)

Enables or disables receive-side authorization of membership commits.

Off by default, and changing that is a group-partitioning decision, not a hardening toggle. Refusing a commit means not merging it: our epoch stays behind every member who did merge, which MLS cannot heal — the app has to re-invite us. Enforcement is therefore safe only if every member reaches the same verdict, and the admin overlay is replicated best-effort (role changes ride unreconciled mesh notifications; joiners get a point-in-time snapshot). Members whose role maps merely disagree will partition each other with no attacker involved.

authorize_membership_commit (private) fails open on absent knowledge to keep the common lag case harmless, but it cannot detect divergent knowledge. Enable this only for a closed deployment that controls role distribution.

Source

pub fn provider(&self) -> &MlsProvider

Returns a reference to the crypto provider.

Source

pub fn create_group( &self, group_id: &GroupId, credential_with_key: &CredentialWithKey, signer: &impl Signer, ) -> Result<MlsGroup>

Creates a new MLS group.

Source

pub fn load_group(&self, group_id: &GroupId) -> Result<Option<MlsGroup>>

Loads a group from storage.

Source

pub fn save_group(&self, group_id: &GroupId, group: &MlsGroup) -> Result<()>

Saves a group marker to storage.

OpenMLS persists group state through the provider during mutations. This marker is kept in our storage layer for listing/enumeration.

Source

pub fn delete_group(&self, group_id: &GroupId) -> Result<()>

Deletes a group from storage, including all OpenMLS provider state.

Source

pub fn list_groups(&self) -> Result<Vec<GroupId>>

Lists all groups.

Stored keys that fail group-id validation (possible only for state written before validation was enforced) are skipped with a warning rather than resurrected as live group ids.

Source

pub fn add_member( &self, group: &mut MlsGroup, key_package: KeyPackage, signer: &impl Signer, ) -> Result<(MlsMessageOut, MlsMessageOut)>

Adds a member to a group using their key package.

Source

pub fn remove_member( &self, group: &mut MlsGroup, member_indices: &[LeafNodeIndex], signer: &impl Signer, ) -> Result<MlsMessageOut>

Removes members from a group.

Takes every leaf to remove in one commit rather than one leaf per call, because removing “that member” has to mean all of them or the peer stays in the group holding live keys while every roster read shows them gone.

Two leaves for one identity cannot arrive through the wire gates — MLS requires unique signature keys and the binding ties credential to key — but they can exist in a tampered provider store, where a forged leaf claims a peer’s address while carrying the attacker’s key. See crate::MlsManager::remove_group_member for the full argument; do not restate it here, and do not reduce this to a first match.

Source

pub fn encrypt_message( &self, group: &mut MlsGroup, plaintext: &[u8], signer: &impl Signer, ) -> Result<MlsMessageOut>

Creates an encrypted application message.

Source

pub fn decrypt_message( &self, group: &mut MlsGroup, group_id: &GroupId, message: MlsMessageIn, claimed_sender: &str, ) -> Result<Option<Vec<u8>>>

Decrypts an incoming MLS message.

Security (SEC-M1): claimed_sender is the identity the caller will attribute this message to (the transport-level sender). It is cross-checked against the MLS-authenticated credential of the processed message before any plaintext is surfaced or any commit is merged, so a group member cannot impersonate another member by lying in the wire envelope.

Source

pub fn join_group( &self, welcome: Welcome, group_id: &GroupId, ) -> Result<MlsGroup>

Joins a group using a Welcome message.

Source

pub fn join_group_replacing( &self, welcome: Welcome, group_id: &GroupId, ) -> Result<MlsGroup>

Joins a group from a Welcome, replacing any existing group at the same group_id. Non-destructive on the common failure (a duplicate Welcome); best-effort — NOT atomic — on the rare storage failure.

Staging the incoming Welcome (StagedWelcome::new_from_welcome) is the step that consumes the one-time key package, so it is the natural failure point: if the key package is unavailable — e.g. a retransmitted Welcome we already adopted, or a first-contact key-package race — staging returns Err before we touch the existing group, which is therefore left completely intact. This makes both-create adoption idempotent: a duplicate Welcome is a safe no-op rather than a re-brick, the case this method exists to handle.

Caveat — once staging succeeds the swap is not atomic. We delete the prior group (to avoid orphaning its old-leaf epoch encryption keypairs, which are keyed by (group_id, epoch, leaf_index); the adopted group installs at a different leaf and so would not overwrite them) and then call into_group, which performs several sequential, non-transactional storage writes. If one of those fails (e.g. a Keychain/Keystore write error), the old group is already gone and the new one is absent — a lost session. The key package is spent by then, so the same Welcome cannot simply be retried; recovery is a fresh key-package exchange. The caller distinguishes this “lost group” outcome from a benign duplicate by observing has_session (false here, true for a duplicate) and surfaces it as a session failure.

Source

pub fn get_group_info(&self, group: &MlsGroup, group_id: &GroupId) -> GroupInfo

Gets information about a group.

Members whose leaf does not carry the address its own signature key derives to are skipped with a warning, the same disposition Self::list_groups gives a stored group id that fails validation.

Post-gates such a leaf cannot arrive over the wire, so one appearing here means local group state was written behind the SDK’s back. Keeping it out of the roster matters because the roster is not only what the app displays: it addresses the per-member fan-out, feeds the group’s rich-payload capability gate, and supplies the candidates for the address-ordered tiebreakers (leave election, admin auto-promotion, fork leader). A forged entry in it is not cosmetic.

Skipping rather than failing keeps a tampered store from taking the whole group down, and the message it would have carried is refused anyway — Self::verify_sender_leaf judges the same leaf at decrypt.

Skipping silently would be the mistake, though: this is the only seam at which a leaf already seated in local state surfaces at all, and a warn! reaches no app. Every other refusal in this family is reported. So the count rides out on GroupInfo::unproven_members, and the protocol layer turns a non-zero one into the same GroupLeafIdentityUnproven warning the wire gates emit. The count and not the identities, for the reason that field documents.

O(N) in the roster, against Self::verify_sender_leaf’s O(1): one SHA-256 over 32 bytes plus a bech32m encode per member, per call, and process_commit_core calls this twice per commit to derive a membership delta. Immaterial next to the MLS crypto on the same path — microseconds at a hundred members — but this is the expensive seam of the two, so do not reach for it in a loop.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Classify for T

Source§

type Classified = T

Source§

fn classify(self) -> T

Source§

impl<T> Declassify for T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more