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}