Skip to main content

openmls/
lib.rs

1#![doc = include_str!("../README.md")]
2//! ## Quick Start
3//! For a quick start to learn how OpenMLS works here's the basic code to set
4//! up to parties and have them create a group.
5//!
6//! ```
7//! use openmls::{prelude::{*,  tls_codec::*}};
8//! use openmls_rust_crypto::{OpenMlsRustCrypto};
9//! use openmls_basic_credential::SignatureKeyPair;
10//!
11//! // Define ciphersuite ...
12//! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519;
13//! // ... and providers for both parties to use.
14//! let sasha_provider = &OpenMlsRustCrypto::default();
15//! let maxim_provider = &OpenMlsRustCrypto::default();
16//!
17//! // Now let's create two participants.
18//!
19//! // A helper to create and store credentials.
20//! fn generate_credential_with_key(
21//!     identity: Vec<u8>,
22//!     credential_type: CredentialType,
23//!     signature_algorithm: SignatureScheme,
24//!     provider: &impl OpenMlsProvider,
25//! ) -> (CredentialWithKey, SignatureKeyPair) {
26//!     let credential = BasicCredential::new(identity);
27//!     let signature_keys =
28//!         SignatureKeyPair::new(signature_algorithm)
29//!             .expect("Error generating a signature key pair.");
30//!
31//!     // Store the signature key into the key store so OpenMLS has access
32//!     // to it.
33//!     signature_keys
34//!         .store(provider.storage())
35//!         .expect("Error storing signature keys in key store.");
36//!
37//!     (
38//!         CredentialWithKey {
39//!             credential: credential.into(),
40//!             signature_key: signature_keys.public().into(),
41//!         },
42//!         signature_keys,
43//!     )
44//! }
45//!
46//! // A helper to create key package bundles.
47//! fn generate_key_package(
48//!     ciphersuite: Ciphersuite,
49//!     provider: &impl OpenMlsProvider,
50//!     signer: &SignatureKeyPair,
51//!     credential_with_key: CredentialWithKey,
52//! ) -> KeyPackageBundle {
53//!     // Create the key package
54//!     KeyPackage::builder()
55//!         .build(
56//!             ciphersuite,
57//!             provider,
58//!             signer,
59//!             credential_with_key,
60//!         )
61//!         .unwrap()
62//! }
63//!
64//! // First they need credentials to identify them
65//! let (sasha_credential_with_key, sasha_signer) = generate_credential_with_key(
66//!     "Sasha".into(),
67//!     CredentialType::Basic,
68//!     ciphersuite.signature_algorithm(),
69//!     sasha_provider,
70//! );
71//!
72//! let (maxim_credential_with_key, maxim_signer) = generate_credential_with_key(
73//!     "Maxim".into(),
74//!     CredentialType::Basic,
75//!     ciphersuite.signature_algorithm(),
76//!     maxim_provider,
77//! );
78//!
79//! // Then they generate key packages to facilitate the asynchronous handshakes
80//! // in MLS
81//!
82//! // Generate KeyPackages
83//! let maxim_key_package = generate_key_package(ciphersuite, maxim_provider, &maxim_signer, maxim_credential_with_key);
84//!
85//! // Now Sasha starts a new group ...
86//! let mut sasha_group = MlsGroup::new(
87//!     sasha_provider,
88//!     &sasha_signer,
89//!     &MlsGroupCreateConfig::default(),
90//!     sasha_credential_with_key,
91//! )
92//! .expect("An unexpected error occurred.");
93//!
94//! // ... and invites Maxim.
95//! // The key package has to be retrieved from Maxim in some way. Most likely
96//! // via a server storing key packages for users.
97//! let (mls_message_out, welcome_out, group_info) = sasha_group
98//!     .add_members(sasha_provider, &sasha_signer, core::slice::from_ref(maxim_key_package.key_package()))
99//!     .expect("Could not add members.");
100//!
101//! // Sasha merges the pending commit that adds Maxim.
102//! sasha_group
103//!    .merge_pending_commit(sasha_provider)
104//!    .expect("error merging pending commit");
105//!
106//! // Sasha serializes the [`MlsMessageOut`] containing the [`Welcome`].
107//! let serialized_welcome = welcome_out
108//!    .tls_serialize_detached()
109//!    .expect("Error serializing welcome");
110//!
111//! // Maxim can now de-serialize the message as an [`MlsMessageIn`] ...
112//! let mls_message_in = MlsMessageIn::tls_deserialize(&mut serialized_welcome.as_slice())
113//!    .expect("An unexpected error occurred.");
114//!
115//! // ... and inspect the message.
116//! let welcome = match mls_message_in.extract() {
117//!    MlsMessageBodyIn::Welcome(welcome) => welcome,
118//!    // We know it's a welcome message, so we ignore all other cases.
119//!    _ => unreachable!("Unexpected message type."),
120//! };
121//!
122//! // Now Maxim can build a staged join for the group in order to inspect the welcome
123//! let maxim_staged_join = StagedWelcome::new_from_welcome(
124//!     maxim_provider,
125//!     &MlsGroupJoinConfig::default(),
126//!     welcome,
127//!     // The public tree is needed and transferred out of band.
128//!     // It is also possible to use the [`RatchetTreeExtension`]
129//!     Some(sasha_group.export_ratchet_tree().into()),
130//! )
131//! .expect("Error creating a staged join from Welcome");
132//!
133//! // Finally, Maxim can create the group
134//! let mut maxim_group = maxim_staged_join
135//!     .into_group(maxim_provider)
136//!     .expect("Error creating the group from the staged join");
137//! ```
138//!
139//! [//]: # "links and badges"
140//! [user Manual]: https://book.openmls.tech
141#![cfg_attr(docsrs, feature(doc_cfg))]
142#![cfg_attr(not(test), forbid(unsafe_code))]
143#![cfg_attr(not(feature = "test-utils"), deny(missing_docs))]
144#![deny(rustdoc::broken_intra_doc_links)]
145#![deny(rustdoc::private_intra_doc_links)]
146#![cfg(any(target_pointer_width = "32", target_pointer_width = "64",))]
147
148#[cfg(all(target_arch = "wasm32", not(feature = "js")))]
149compile_error!("In order for OpenMLS to build for WebAssembly, JavaScript APIs must be available (for access to secure randomness and the current time). This can be signalled by setting the `js` feature on OpenMLS.");
150
151// === Testing ===
152
153/// Single place, re-exporting all structs and functions needed for integration tests
154#[cfg(any(feature = "test-utils", test))]
155pub mod prelude_test;
156
157#[cfg(any(feature = "test-utils", test))]
158#[macro_use]
159pub mod test_utils;
160
161#[cfg(test)]
162pub mod kat_vl;
163
164// === Modules ===
165
166#[macro_use]
167mod utils;
168
169pub mod error;
170
171// Public
172pub mod ciphersuite;
173#[cfg(feature = "extensions-draft-08")]
174pub mod component;
175pub mod credentials;
176pub mod extensions;
177pub mod framing;
178pub mod grease;
179pub mod group;
180pub mod key_packages;
181pub mod messages;
182pub mod schedule;
183pub mod treesync;
184pub mod versions;
185
186// implement storage traits
187// public
188pub mod storage;
189
190// Private
191mod binary_tree;
192mod skip_validation;
193mod tree;
194
195/// Single place, re-exporting the most used public functions.
196pub mod prelude;
197
198// this is a workaround, see https://github.com/la10736/rstest/issues/211#issuecomment-1701238125
199#[cfg(any(test, feature = "test-utils"))]
200pub mod wasm {
201    pub use wasm_bindgen_test::wasm_bindgen_test as test;
202}