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