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

#[cfg(all(target_arch = "wasm32", not(feature = "js")))]
compile_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.");

// === Testing ===

/// Single place, re-exporting all structs and functions needed for integration tests
#[cfg(any(feature = "test-utils", test))]
pub mod prelude_test;

#[cfg(any(feature = "test-utils", test))]
pub use rstest_reuse;

#[cfg(any(feature = "test-utils", test))]
#[macro_use]
pub mod test_utils;

#[cfg(test)]
pub mod kat_vl;

// === Modules ===

#[macro_use]
mod utils;

pub mod error;

// Public
pub mod ciphersuite;
pub mod credentials;
pub mod extensions;
pub mod framing;
pub mod group;
pub mod key_packages;
pub mod messages;
pub mod schedule;
pub mod treesync;
pub mod versions;

// Private
mod binary_tree;
mod tree;

/// Single place, re-exporting the most used public functions.
pub mod prelude;

// this is a workaround, see https://github.com/la10736/rstest/issues/211#issuecomment-1701238125
#[cfg(any(test, feature = "test-utils"))]
pub mod wasm {
    pub use wasm_bindgen_test::wasm_bindgen_test as test;
}