Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge upstream (Aug 19th, 2024) #35

Merged
merged 56 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
1c9a654
wip
franziskuskiefer May 24, 2024
40e484c
example as bench
franziskuskiefer May 31, 2024
90854fe
performance numbers
franziskuskiefer May 31, 2024
7c4f258
update large group benchmarks
franziskuskiefer Jun 4, 2024
780e29f
better benchmarks
franziskuskiefer Jun 5, 2024
6ae56ff
don't copy so much
franziskuskiefer Jun 5, 2024
ae8d336
cleanup
franziskuskiefer Jun 5, 2024
285e6de
Merge branch 'main' into franziskus/large-group-benchmarks
franziskuskiefer Jun 10, 2024
0af69a5
progress bars and reuse groups
franziskuskiefer Jun 10, 2024
3cd31ca
Merge branch 'main' into franziskus/large-group-benchmarks
franziskuskiefer Jun 27, 2024
58df3c7
Make backtrace an optional feature (#1602)
keks Jul 3, 2024
f7e0dc8
Merge branch 'main' into franziskus/large-group-benchmarks
franziskuskiefer Jul 5, 2024
287ad2b
add usage infos and ci
franziskuskiefer Jul 5, 2024
233931b
Merge pull request #1600 from openmls/franziskus/large-group-benchmarks
franziskuskiefer Jul 8, 2024
6adcaf0
Move some external commit tests to MlsGroup (#1608)
raphaelrobert Jul 8, 2024
4d73c98
Assorted Documentation Fixes for v0.6 (#1603)
keks Jul 9, 2024
8d97a32
De-duplicate ProposalStore (#1601)
raphaelrobert Jul 9, 2024
c2f5b7b
Remove clippy warnings & change CI (#1611)
raphaelrobert Jul 10, 2024
9c45bdd
LeafNode parameters (#1606)
raphaelrobert Jul 10, 2024
b947071
more update proposal tests
franziskuskiefer Jul 12, 2024
5fb5e62
Merge pull request #1612 from openmls/franziskus/update-proposal-tests
franziskuskiefer Jul 15, 2024
c44386f
Test all Validation Checks for GroupContextExtensions (#1599)
keks Jul 16, 2024
07fc3b2
Add a KAT for testing storage stability (#1610)
keks Jul 16, 2024
a1bc0ca
Change how the AAD is set (#1615)
raphaelrobert Jul 17, 2024
d775391
Re-organize and re-name test and kat modules (#1620)
kkohbrok Jul 19, 2024
6aae27f
Migrate tests to `MlsGroup` and fix a small bug (#1619)
kkohbrok Jul 22, 2024
ea5ac13
Bump versions and remove git deps for release (#1625)
keks Jul 23, 2024
e5ad6ca
make all the versions pre-release
keks Jul 23, 2024
b21beea
missed one
keks Jul 23, 2024
000cffb
make openmls_test 0.1.0-pre.1
keks Jul 23, 2024
51ff1c6
Merge pull request #1626 from openmls/keks/prepare-v0.6
franziskuskiefer Jul 23, 2024
1f0b14a
Downgrade tls_codec to 0.4.1 and bump a few prerelease versions (#1627)
keks Jul 23, 2024
fb42f4b
Add metadata to openmls_test crate
franziskuskiefer Jul 24, 2024
5cf547f
Update openmls_test/Cargo.toml
franziskuskiefer Jul 24, 2024
28e12c6
Merge pull request #1628 from openmls/franziskus/openmls_test-crate-m…
franziskuskiefer Jul 24, 2024
d4e88e2
set versions for openmls_test
franziskuskiefer Jul 24, 2024
dc2de1f
Merge pull request #1630 from openmls/franziskus/fix-more-versions
franziskuskiefer Jul 24, 2024
15eb787
openmls v0.6.0-pre.2
franziskuskiefer Jul 25, 2024
2c22356
Fix minor problems, mostly in docs (#1634)
kkohbrok Jul 25, 2024
b66deda
Remove `aad` getter from storage provider trait (#1633)
kkohbrok Jul 25, 2024
c2d4563
Merge branch 'main' into franziskus/dont-package-testvectors
raphaelrobert Jul 25, 2024
8c928e7
Merge pull request #1632 from openmls/franziskus/dont-package-testvec…
franziskuskiefer Jul 26, 2024
ad07ef5
update traits.md in the book for v0.6
franziskuskiefer Jul 29, 2024
7d797e9
Merge pull request #1636 from openmls/franziskus/misc-book-fixes-v0_6
franziskuskiefer Jul 29, 2024
7dd4983
Add `MlsGroup` endpoint to create Add commit without path (#1629)
kkohbrok Jul 30, 2024
96c38a8
Migrate remaining `CoreGroup` tests to `MlsGroup` (#1631)
kkohbrok Jul 30, 2024
5e7ba80
Remove MlsSerializedMlsGroup (#1637)
raphaelrobert Aug 6, 2024
e543c63
Remove serde for PublicGroup and CoreGroup (#1638)
raphaelrobert Aug 7, 2024
c69910e
Introduce PublicStorageProvider (#1639)
raphaelrobert Aug 8, 2024
be9fb71
Align storage provider naming (#1640)
raphaelrobert Aug 8, 2024
5269b2c
Consistent handling of queued proposals. (#1641)
raphaelrobert Aug 8, 2024
7d3cb92
Remove OpenMlsProvider dependency of PublicGroup. (#1642)
raphaelrobert Aug 12, 2024
8c4671d
Remove `use_ratchet_tree_extension` storage (#1643)
kkohbrok Aug 13, 2024
ed075ea
Fix `MlsGroup::delete()` function to delete encryption keypairs (#1644)
kkohbrok Aug 14, 2024
2f0c9a9
Merge remote-tracking branch 'upstream/main' into nm/merge-latest-upd…
neekolas Aug 19, 2024
cf42738
Fix errors
neekolas Aug 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/benches.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Benchmarks

concurrency:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: true

on:
push:
Expand Down Expand Up @@ -32,3 +32,8 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Benchmarks
run: cargo bench -p openmls --verbose

- name: Large groups
run: |
cargo run -p openmls --example large-groups --release -- --write -g 2 3
cargo run -p openmls --example large-groups --release -- -g 3
5 changes: 4 additions & 1 deletion .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ jobs:
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy -p openmls --tests -- -D warnings
- run: |
sudo apt-get -y install protoc-gen-go # Needed to build the interop client
echo $(go env GOPATH)/bin >> $GITHUB_PATH
cargo clippy -p openmls --tests --benches --examples -p openmls_basic_credential -p cli -p interop_client -p mls-ds -p ds-lib -p openmls_libcrux_crypto -p openmls_memory_storage -p openmls_rust_crypto -p openmls_test -p openmls-wasm -p openmls_traits -- -D warnings
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,31 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## 0.6.0-pre.2 (2024-08-XX)

### Added

- [#1639](https://github.com/openmls/openmls/pull/1639): Introduce `PublicStorageProvider` trait to independently allow for the storage of `PublicGroup` instances.
- [#1641](https://github.com/openmls/openmls/pull/1641): Extend the `PublicGroup` API with `add_proposal()`, `remove_proposal()`, and `queued_proposals()`.

### Changed

- [#1637](https://github.com/openmls/openmls/pull/1637): Remove `serde` from `MlsGroup`.
- [#1638](https://github.com/openmls/openmls/pull/1638): Remove `serde` from `PublicGroup`. `PublicGroup::load()` becomes public to load a group from the storage provider.
- [#1642](https://github.com/openmls/openmls/pull/1642): `OpenMlsProvider` is no longer required for the `PublicGroup` API. The `PublicGroup` API now uses the `PublicStorageProvider` trait directly. `ProcessMessageError::InvalidSignature` was removed and replaced with `ValidationError::InvalidSignature`.

### Removed


### Fixed

- [#1641](https://github.com/openmls/openmls/pull/1641): Fixed missing storage of queued proposals & clearing of the queued proposals.

## 0.6.0-pre.1 (2024-07-22)

### Added

- [#1629](https://github.com/openmls/openmls/pull/1629): Add `add_members_without_update` function to `MlsGroup` to allow the creation of add-only commits
- [#1506](https://github.com/openmls/openmls/pull/1506): Add `StagedWelcome` and `StagedCoreWelcome` to make joining a group staged in order to inspect the `Welcome` message. This was followed up with PR [#1533](https://github.com/openmls/openmls/pull/1533) to adjust the API.
- [#1516](https://github.com/openmls/openmls/pull/1516): Add `MlsGroup::clear_pending_proposals` to the public API; this allows users to clear a group's internal `ProposalStore`
- [#1565](https://github.com/openmls/openmls/pull/1565): Add new `StorageProvider` trait to the `openmls_traits` crate.
Expand All @@ -29,6 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1542](https://github.com/openmls/openmls/pull/1542): Add support for custom proposals. ProposalType::Unknown is now called ProposalType::Other. Proposal::Unknown is now called Proposal::Other.
- [#1559](https://github.com/openmls/openmls/pull/1559): Remove the `PartialEq` type constraint on the error type of both the `OpenMlsRand` and `OpenMlsKeyStore` traits. Additionally, remove the `Clone` type constraint on the error type of the `OpenMlsRand` trait.
- [#1565](https://github.com/openmls/openmls/pull/1565): Removed `OpenMlsKeyStore` and replace it with a new `StorageProvider` trait in the `openmls_traits` crate.
- [#1606](https://github.com/openmls/openmls/pull/1606): Added additional `LeafNodeParameters` argument to `MlsGroup.self_update()` and `MlsGroup.propose_self_update()` to allow for updating the leaf node with custom parameters. `MlsGroup::join_by_external_commit()` now also takes optional parameters to set the capabilities and the extensions of the LeafNode.
- [#1615](https://github.com/openmls/openmls/pull/1615): Changes the AAD handling. The AAD is no longer persisted and needs to be set before every API call that generates an `MlsMessageOut`. The functions `ProccessedMessage` to accees the AAD has been renamed to `aad()`.

### Fixed

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ resolver = "2"

# Central dependency management for some crates
[workspace.dependencies]
tls_codec = { version = "0.4.2-pre.1", features = [
tls_codec = { version = "0.4.1", features = [
"derive",
"serde",
"mls",
], git = "https://github.com/rustcrypto/formats" }
]}
4 changes: 2 additions & 2 deletions basic_credential/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "openmls_basic_credential"
version = "0.2.0"
version = "0.3.0-pre.1"
authors = ["OpenMLS Authors"]
edition = "2021"
description = "A Basic Credential implementation for OpenMLS"
Expand All @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/basic_credential"
readme = "README.md"

[dependencies]
openmls_traits = { version = "0.2.0", path = "../traits" }
openmls_traits = { version = "0.3.0-pre.2", path = "../traits" }
tls_codec = { workspace = true }
serde = "1.0"

Expand Down
12 changes: 12 additions & 0 deletions basic_credential/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ impl SignatureKeyPair {
.flatten()
}

/// Delete a signature key pair from the key store.
pub fn delete<T: StorageProvider<CURRENT_VERSION>>(
store: &T,
public_key: &[u8],
signature_scheme: SignatureScheme,
) -> Result<(), T::Error> {
let id = StorageId {
value: id(public_key, signature_scheme),
};
store.delete_signature_key_pair(&id)
}

/// Get the public key as byte slice.
pub fn public(&self) -> &[u8] {
self.public.as_ref()
Expand Down
2 changes: 2 additions & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
- [Group configuration](user_manual/group_config.md)
- [Creating groups](user_manual/create_group.md)
- [Join a group from a Welcome message](user_manual/join_from_welcome.md)
- [Join a group from an External Commit message](user_manual/join_from_external_commit.md)
- [Adding members to a group](user_manual/add_members.md)
- [Removing members from a group](user_manual/remove_members.md)
- [Updating own key package](user_manual/updates.md)
- [Using Additional Authenticated Data (AAD)](user_manual/aad.md)
- [Leaving a group](user_manual/leaving.md)
- [Custom proposals](user_manual/custom_proposals.md)
- [Creating application messages](user_manual/application_messages.md)
Expand Down
62 changes: 62 additions & 0 deletions book/src/app_validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,64 @@
>
> **⚠️** This chapter is work in progress (see [#1504](https://github.com/openmls/openmls/issues/1504)).

## Credential Validation

### Acceptable Presented Identifiers

> The application using MLS is responsible for specifying which identifiers
> it finds acceptable for each member in a group. In other words, following
> the model that [[RFC6125]] describes for TLS, the application maintains a list
> of "reference identifiers" for the members of a group, and the credentials
> provide "presented identifiers". A member of a group is authenticated by first
> validating that the member's credential legitimately represents some presented
> identifiers, and then ensuring that the reference identifiers for the member
> are authenticated by those presented identifiers
>
> -- [RFC9420, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc9420.html#section-5.3.1-1)
>
### Validity of Updated Presented Identifiers

> In cases where a member's credential is being replaced, such as the Update and
> Commit cases above, the AS MUST also verify that the set of presented identifiers
> in the new credential is valid as a successor to the set of presented identifiers
> in the old credential, according to the application's policy.
>
> -- [RFC9420, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc9420.html#section-5.3.1-5)

### Application ID is Not Authenticed by AS

> However, applications MUST NOT rely on the data in an application_id extension
> as if it were authenticated by the Authentication Service, and SHOULD gracefully
> handle cases where the identifier presented is not unique.
>
> -- [RFC9420, Section 5.3.3](https://www.rfc-editor.org/rfc/rfc9420.html#section-5.3.3-6)

## LeafNode Validation

### Specifying the Maximum Total Acceptable Lifetime

> Applications MUST define a maximum total lifetime that is acceptable for a
> LeafNode, and reject any LeafNode where the total lifetime is longer than this
> duration. In order to avoid disagreements about whether a LeafNode has a valid
> lifetime, the clients in a group SHOULD maintain time synchronization (e.g.,
> using the Network Time Protocol [[RFC5905]]).
>
> -- [RFC9420, Section 7.2](https://www.rfc-editor.org/rfc/rfc9420.html#section-7.2-10)

## PrivateMessage Validation

### Structure of AAD is Application-Defined

> It is up to the application to decide what authenticated_data to provide and
> how much padding to add to a given message (if any). The overall size of the
> AAD and ciphertext MUST fit within the limits established for the group's AEAD
> algorithm in [[CFRG-AEAD-LIMITS]].
>
> -- [RFC9420, Section 6.3.1](https://www.rfc-editor.org/rfc/rfc9420.html#section-6.3.1-11)

Therefore, the application must also validate whether the AAD adheres to the
prescribed format.

## Proposal Validation

When processing a commit, the application has to ensure that the application
Expand All @@ -24,3 +82,7 @@ The RFC requires the following check

Since OpenMLS does not know the relevant policies, the application MUST ensure
that the credentials are checked according to the policy.

[RFC6125]: https://www.rfc-editor.org/rfc/rfc6125.html
[RFC5905]: https://www.rfc-editor.org/rfc/rfc5905.html
[CFRG-AEAD-LIMITS]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aead-limits-08
4 changes: 2 additions & 2 deletions book/src/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ It is the same scenario as the somewhat stable group but with a very small Y and

In addition to the three scenarios above extreme and corner cases are interesting.

### Every second leave is blank
### Every second leaf is blank

Only every second leave in the tree is non-blank.
Only every second leaf in the tree is non-blank.

## Use Case Scenarios

Expand Down
39 changes: 18 additions & 21 deletions book/src/traits/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ It simply needs to implement two functions to generate cryptographically secure
randomness and store it in an array or vector.

```rust,no_run,noplayground
{{#include ../../../traits/src/random.rs:8:16}}
{{#include ../../../traits/src/random.rs:openmls_rand}}
```

### OpenMlsCrypto
Expand All @@ -48,27 +48,29 @@ This trait defines all cryptographic functions required by OpenMLS. In particula
- Signatures
- HPKE

```rust,no_run,noplayground
{{#include ../../../traits/src/crypto.rs:10}}
```

### StorageProvider

This trait defines an API for a storage backend that is used for all OpenMLS
persistence.

The store provides functions to `store`, `read`, and `delete` values.
Note that it does not allow updating values.
Instead, entries must be deleted and newly stored.
The store provides functions for reading and updating stored values.
Each sort of value has separate methods for accessing or mutating the state.
In order to decouple the provider from the OpenMLS implementation, while still
having legible types at the provider, there are traits that mirror all the types
stored by OpenMLS. The provider methods use values constrained by these traits as
as arguments.

```rust,no_run,noplayground
{{#include ../../../traits/src/storage.rs:16:25}}
{{#include ../../../traits/src/storage.rs:traits}}
```

The trait is generic over a `VERSION`, which is used to ensure that the values
The traits are generic over a `VERSION`, which is used to ensure that the values
that are persisted can be upgraded when OpenMLS changes the stored structs.

Every function takes `Key` and `Value` arguments.
The traits used as arguments to the storage methods are constrained to implement
the `Key` or `Entity` traits as well, depending on whether they are only used for
addressing (in which case they are a `Key`) or whether they represent a stored
value (in which case they are an `Entity`).

```rust,no_run,noplayground
{{#include ../../../traits/src/storage.rs:key_trait}}
Expand All @@ -78,13 +80,6 @@ Every function takes `Key` and `Value` arguments.
{{#include ../../../traits/src/storage.rs:entity_trait}}
```

To ensure that each function takes the correct input, they use trait bounds.
These are the available traits.

```rust,no_run,noplayground
{{#include ../../../traits/src/storage.rs:traits}}
```

An implementation of the storage trait should ensure that it can address and
efficiently handle values.

Expand Down Expand Up @@ -127,20 +122,22 @@ fn write_key_package<
This allows the application to iterate over the hash references and delete outdated
key packages.

### OpenMlsCryptoProvider
### OpenMlsProvider

Additionally, there's a wrapper trait defined that is expected to be passed into
the public OpenMLS API.
Some OpenMLS APIs require only one of the sub-traits, though.

```rust,no_run,noplayground
{{#include ../../../traits/src/traits.rs:15:28}}
{{#include ../../../traits/src/traits.rs:openmls_provider}}
```

## Implementation Notes

It is not necessary to implement all sub-traits if one functionality is missing.
Suppose you want to use a persisting key store. In that case, it is sufficient to do a new implementation of the key store trait and combine it with one of the provided crypto and randomness trait implementations.
Suppose you want to use a persisting storage provider. In that case, it is
sufficient to do a new implementation of the `StorageProvider` trait and
combine it with one of the provided crypto and randomness trait implementations.

[rust crypto]: https://crates.io/crates/openmls_rust_crypto
[libcrux crypto]: https://crates.io/crates/openmls_libcrux_crypto
Expand Down
6 changes: 5 additions & 1 deletion book/src/user_manual/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ The user manual describes how to use the different parts of the OpenMLS API.
## Prerequisites

Most operations in OpenMLS require a `provider` object that provides all required cryptographic algorithms via the [`OpenMlsCryptoProvider`] trait.
Currently, there are two implementations available through the [openmls_rust_crypto] crate.
Currently, there are two implementations available:

- one through the [openmls_rust_crypto] crate.
- one through the [openmls_libcrux_crypto] crate.

Thus, you can create the `provider` object for the following examples using ...

Expand All @@ -15,3 +18,4 @@ Thus, you can create the `provider` object for the following examples using ...

[`openmlscryptoprovider`]: https://docs.rs/openmls/latest/openmls/prelude/trait.OpenMlsCryptoProvider.html
[openmls_rust_crypto]: https://crates.io/crates/openmls_rust_crypto
[openmls_libcrux_crypto]: https://crates.io/crates/openmls_libcrux_crypto
20 changes: 20 additions & 0 deletions book/src/user_manual/aad.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Using Additional Authenticated Data (AAD)

The Additional Authenticated Data (AAD) is a byte sequence that can be included in both private and public messages. By design, it is always authenticated (signed) but never encrypted. Its purpose is to contain data that can be inspected but not changed while a message is in transit.

## Setting the AAD

Members can set the AAD by calling the `.set_aad()` function. The AAD will remain set until the next API call that successfully generates an `MlsMessageOut`. Until then, the AAD can be inspected with the `.aad()` function.

```rust,no_run,noplayground
{{#include ../../../openmls/tests/book_code.rs:set_aad}}
```

## Inspecting the AAD

Members can inspect the AAD of an incoming message once the message has been processed. The AAD can be accessed with the `.aad()` function of a `ProcessedMessage`.

```rust,no_run,noplayground
{{#include ../../../openmls/tests/book_code.rs:inspect_aad}}
```

6 changes: 6 additions & 0 deletions book/src/user_manual/add_members.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Members can be added to the group atomically with the `.add_members()` function.

The function returns the tuple `(MlsMessageOut, Welcome)`. The `MlsMessageOut` contains a Commit message that needs to be fanned out to existing group members. The `Welcome` message must be sent to the newly added members.

### Adding members without update

The `.add_members_without_update()` function functions the same as the `.add_members()` function, except that it will only include an update to the sender's key material if the sender's proposal store includes a proposal that requires a path. For a list of proposals and an indication whether they require a `path` (i.e. a key material update) see [Section 17.4 of RFC 9420](https://www.rfc-editor.org/rfc/rfc9420.html#section-17.4).

Not sending an update means that the sender will not achieve post-compromise security with this particular commit. However, not sending an update saves on performance both in terms of computation and bandwidth. Using `.add_members_without_update()` can thus be a useful option if the ciphersuite of the group features large public keys and/or expensive encryption operations.

## Proposal

Members can also be added as a proposal (without the corresponding Commit message) by using the `.propose_add_member()` function:
Expand Down
14 changes: 6 additions & 8 deletions book/src/user_manual/persistence.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# Persistence of Group Data

The state of a given `MlsGroup` instance can be written or read at any time using the `.save()` or `.load()` functions respectively. The functions take as input a struct implementing either the `Write` (`.save()`) or `Read` (`.load()`) trait.

Since some group operations might or might not change the `MlsGroup` state depending on the context, the group maintains the `state_changed` flag, which is set to `true` whenever the state is changed by an `MlsGroup` function. The state of the flag can be queried using the `.state_changed()` function.

## Group Lockout Upon State Loss

MLS provides strong Post-Compromise Security properties, which means that key material is regularly refreshed and old key material becomes stale very quickly. Consequently, regularly persisting state is important, especially after the client has created a commit or issued an Update proposal, thus introducing new key material into the group. A loss of state in such a situation is only recoverable in specific cases where the commit was rejected by the Delivery Service or if the proposed Update was not committed. A re-join is required in most cases to continue participating in a group after a loss of group state. To avoid a loss of state and the associated re-join, persisting `MlsGroup` state after each state-changing group operation is mandatory.
The state of a given `MlsGroup` instance is continuously written to the configured
`StorageProvider`. Later, the `MlsGroup` can be loaded from the provider using
the `load` constructor, which can be called with the respective storage provider
as well as the `GroupId` of the group to be loaded. For this to work, the group
must have been written to the provider previously.

## Forward-Secrecy Considerations

The `MlsGroup` state that is persisted using the `.save()` function contains private key material. As a consequence, the application needs to delete old group states to achieve Forward-Secrecy w.r.t. that key material. Since, as detailed above, an old group state is stale immediately after most group operations, we recommend deleting old group states as soon as a new one has been written.
OpenMLS uses the `StorageProvider` to store sensitive key material. To achieve forward-secrecy (i.e. to prevent an adversary from decrypting messages sent in the past if a client is compromised), OpenMLS frequently deletes previously used key material through calls to the `StorageProvider`. `StorageProvider` implementations must thus take care to ensure that values deleted through any of the `delete_` functions of the trait are irrevocably deleted and that no copies are kept.
Loading
Loading