diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index 764d0c8ae2..d73a60ec45 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -280,7 +280,7 @@ impl MlsClient for MlsClientImpl { let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); signature_keys.store(backend.key_store()).await.unwrap(); @@ -353,7 +353,7 @@ impl MlsClient for MlsClientImpl { let credential = Credential::new(identity, CredentialType::Basic).unwrap(); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *crypto_provider.rand().borrow_rand().unwrap(), + &mut *crypto_provider.rand().borrow_rand().await, ) .unwrap(); diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index d2daaeb3e2..9ff3c75193 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -36,6 +36,7 @@ async-lock = { version = "3.3", optional = true } rstest = { version = "0.18.2", optional = true } rstest_reuse = { version = "0.6.0", optional = true } tokio = { version = "1.24", optional = true, features = ["macros", "rt", "rt-multi-thread"] } +futures = "0.3.30" [features] default = [] diff --git a/openmls/benches/benchmark.rs b/openmls/benches/benchmark.rs index db2c452b7b..4fd6f7dada 100644 --- a/openmls/benches/benchmark.rs +++ b/openmls/benches/benchmark.rs @@ -1,6 +1,7 @@ use criterion::{ async_executor::FuturesExecutor, criterion_group, criterion_main, BenchmarkId, Criterion, }; +use futures::executor; use openmls::prelude::{config::CryptoConfig, *}; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; @@ -9,12 +10,9 @@ use openmls_traits::{crypto::OpenMlsCrypto, OpenMlsCryptoProvider}; fn criterion_benchmark(c: &mut Criterion) { let backend = OpenMlsRustCrypto::default(); for &ciphersuite in backend.crypto().supported_ciphersuites().iter() { + let mut rng = executor::block_on(backend.rand().borrow_rand()); let credential = Credential::new_basic(vec![1, 2, 3]); - let signer = SignatureKeyPair::new( - ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), - ) - .unwrap(); + let signer = SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut *rng).unwrap(); let credential_with_key = CredentialWithKey { credential, signature_key: signer.to_public_vec().into(), diff --git a/openmls/src/ciphersuite/aead.rs b/openmls/src/ciphersuite/aead.rs index 084138608d..221e4cc270 100644 --- a/openmls/src/ciphersuite/aead.rs +++ b/openmls/src/ciphersuite/aead.rs @@ -49,10 +49,10 @@ impl AeadKey { #[cfg(test)] /// Generate a random AEAD Key - pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self { + pub(crate) async fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self { AeadKey { aead_mode: ciphersuite.aead_algorithm(), - value: aead_key_gen(ciphersuite.aead_algorithm(), rng), + value: aead_key_gen(ciphersuite.aead_algorithm(), rng).await, } } @@ -111,8 +111,13 @@ impl AeadNonce { /// **NOTE: This has to wait until it can acquire the lock to get randomness!** /// TODO: This panics if another thread holding the rng panics. #[cfg(test)] - pub(crate) fn random(rng: &impl OpenMlsCryptoProvider) -> Self { - Self(rng.rand().random_array().expect("Not enough entropy.")) + pub(crate) async fn random(rng: &impl OpenMlsCryptoProvider) -> Self { + Self( + rng.rand() + .random_array() + .await + .expect("Not enough entropy."), + ) } /// Get a slice to the nonce value. @@ -138,18 +143,20 @@ impl AeadNonce { } #[cfg(test)] -pub(crate) fn aead_key_gen( +pub(crate) async fn aead_key_gen( alg: openmls_traits::types::AeadType, rng: &impl OpenMlsRand, ) -> SecretVLBytes { match alg { openmls_traits::types::AeadType::Aes128Gcm => rng .random_vec(16) + .await .expect("An unexpected error occurred.") .into(), openmls_traits::types::AeadType::Aes256Gcm | openmls_traits::types::AeadType::ChaCha20Poly1305 => rng .random_vec(32) + .await .expect("An unexpected error occurred.") .into(), } @@ -166,9 +173,10 @@ mod unit_tests { /// state. #[apply(backends)] async fn test_xor(backend: &impl OpenMlsCryptoProvider) { - let reuse_guard: ReuseGuard = - ReuseGuard::try_from_random(backend).expect("An unexpected error occurred."); - let original_nonce = AeadNonce::random(backend); + let reuse_guard: ReuseGuard = ReuseGuard::try_from_random(backend) + .await + .expect("An unexpected error occurred."); + let original_nonce = AeadNonce::random(backend).await; let xored_once = original_nonce.clone().xor_with_reuse_guard(&reuse_guard); assert_ne!( original_nonce, xored_once, diff --git a/openmls/src/ciphersuite/hpke.rs b/openmls/src/ciphersuite/hpke.rs index df6418324c..1783e37b7a 100644 --- a/openmls/src/ciphersuite/hpke.rs +++ b/openmls/src/ciphersuite/hpke.rs @@ -90,7 +90,7 @@ impl From<(&str, &[u8])> for EncryptContext { } /// Encrypt to an HPKE key with a label. -pub(crate) fn encrypt_with_label( +pub(crate) async fn encrypt_with_label( public_key: &[u8], label: &str, context: &[u8], @@ -109,13 +109,15 @@ pub(crate) fn encrypt_with_label( log_crypto!(debug, "* public key: {public_key:x?}"); log_crypto!(debug, "* plaintext: {plaintext:x?}"); - let cipher = crypto.hpke_seal( - ciphersuite.hpke_config(), - public_key, - &context, - &[], - plaintext, - )?; + let cipher = crypto + .hpke_seal( + ciphersuite.hpke_config(), + public_key, + &context, + &[], + plaintext, + ) + .await?; log_crypto!(debug, "* ciphertext: {:x?}", cipher); diff --git a/openmls/src/ciphersuite/reuse_guard.rs b/openmls/src/ciphersuite/reuse_guard.rs index d73d3e7dea..914f385f46 100644 --- a/openmls/src/ciphersuite/reuse_guard.rs +++ b/openmls/src/ciphersuite/reuse_guard.rs @@ -11,13 +11,14 @@ pub struct ReuseGuard { impl ReuseGuard { /// Samples a fresh reuse guard uniformly at random. - pub(crate) fn try_from_random( + pub(crate) async fn try_from_random( crypto: &impl OpenMlsCryptoProvider, ) -> Result { Ok(Self { value: crypto .rand() .random_array() + .await .map_err(|_| CryptoError::InsufficientRandomness)?, }) } diff --git a/openmls/src/ciphersuite/secret.rs b/openmls/src/ciphersuite/secret.rs index ce6a910166..9ded76ee7b 100644 --- a/openmls/src/ciphersuite/secret.rs +++ b/openmls/src/ciphersuite/secret.rs @@ -73,7 +73,7 @@ impl Secret { /// Randomly sample a fresh `Secret`. /// This default random initialiser uses the default Secret length of `hash_length`. /// The function can return a [`CryptoError`] if there is insufficient randomness. - pub(crate) fn random( + pub(crate) async fn random( ciphersuite: Ciphersuite, crypto: &impl OpenMlsCryptoProvider, version: impl Into>, @@ -88,6 +88,7 @@ impl Secret { value: crypto .rand() .random_vec(ciphersuite.hash_length()) + .await .map_err(|_| CryptoError::InsufficientRandomness)? .into(), mls_version, diff --git a/openmls/src/ciphersuite/tests/kat_crypto_basics.rs b/openmls/src/ciphersuite/tests/kat_crypto_basics.rs index fffb649528..768321964c 100644 --- a/openmls/src/ciphersuite/tests/kat_crypto_basics.rs +++ b/openmls/src/ciphersuite/tests/kat_crypto_basics.rs @@ -341,14 +341,17 @@ pub fn run_test_vector( assert_eq!(plaintext, decrypted_plaintext); // Check that encryption works. - let my_ciphertext = hpke::encrypt_with_label( - &public, - &label, - &context, - &plaintext, - ciphersuite, - backend.crypto(), - ) + let my_ciphertext = async_std::task::block_on(async { + hpke::encrypt_with_label( + &public, + &label, + &context, + &plaintext, + ciphersuite, + backend.crypto(), + ) + .await + }) .unwrap(); let decrypted_plaintext = hpke::decrypt_with_label( &private, diff --git a/openmls/src/ciphersuite/tests/test_ciphersuite.rs b/openmls/src/ciphersuite/tests/test_ciphersuite.rs index b8c8f1ec77..09bc206a41 100644 --- a/openmls/src/ciphersuite/tests/test_ciphersuite.rs +++ b/openmls/src/ciphersuite/tests/test_ciphersuite.rs @@ -16,6 +16,7 @@ async fn test_hpke_seal_open(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry .derive_hpke_keypair( ciphersuite.hpke_config(), Secret::random(ciphersuite, backend, None) + .await .expect("Not enough randomness.") .as_slice(), ) @@ -28,6 +29,7 @@ async fn test_hpke_seal_open(ciphersuite: Ciphersuite, backend: &impl OpenMlsCry ciphersuite, backend.crypto(), ) + .await .unwrap(); let decrypted_payload = hpke::decrypt_with_label( &kp.private, diff --git a/openmls/src/ciphersuite/tests/test_secrets.rs b/openmls/src/ciphersuite/tests/test_secrets.rs index 8c21f2b692..2413a33f4c 100644 --- a/openmls/src/ciphersuite/tests/test_secrets.rs +++ b/openmls/src/ciphersuite/tests/test_secrets.rs @@ -12,9 +12,11 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen_test::wasm_bindgen_test] async fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible - let default_secret = - Secret::random(ciphersuite, backend, None).expect("Not enough randomness."); + let default_secret = Secret::random(ciphersuite, backend, None) + .await + .expect("Not enough randomness."); let draft_secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10Draft11) + .await .expect("Not enough randomness."); let derived_default_secret = default_secret.derive_secret(backend, "my_test_label"); @@ -26,9 +28,11 @@ async fn secret_init(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi #[wasm_bindgen_test::wasm_bindgen_test] pub async fn secret_incompatible(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // These two secrets must be incompatible - let default_secret = - Secret::random(ciphersuite, backend, None).expect("Not enough randomness."); + let default_secret = Secret::random(ciphersuite, backend, None) + .await + .expect("Not enough randomness."); let draft_secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10Draft11) + .await .expect("Not enough randomness."); // This must panic because the two secrets have incompatible MLS versions. diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index fa8c7be392..c19cba24d8 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -436,11 +436,9 @@ pub mod test_utils { CredentialType::X509 => Credential::new_x509(cert_data.unwrap()).unwrap(), CredentialType::Unknown(_) => unimplemented!(), }; - let signature_keys = SignatureKeyPair::new( - signature_scheme, - &mut *backend.rand().borrow_rand().unwrap(), - ) - .unwrap(); + let signature_keys = + SignatureKeyPair::new(signature_scheme, &mut *backend.rand().borrow_rand().await) + .unwrap(); signature_keys.store(backend.key_store()).await.unwrap(); ( diff --git a/openmls/src/extensions/external_pub_extension.rs b/openmls/src/extensions/external_pub_extension.rs index 0478198eed..61ead74f30 100644 --- a/openmls/src/extensions/external_pub_extension.rs +++ b/openmls/src/extensions/external_pub_extension.rs @@ -30,6 +30,7 @@ impl ExternalPubExtension { #[cfg(test)] mod test { + use async_std::task::block_on; use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite, OpenMlsCryptoProvider}; use tls_codec::{Deserialize, Serialize}; @@ -47,11 +48,14 @@ mod test { for _ in 0..8 { let hpke_public_key = { - let ikm = Secret::random( - Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, - &backend, - ProtocolVersion::default(), - ) + let ikm = block_on(async { + Secret::random( + Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, + &backend, + ProtocolVersion::default(), + ) + .await + }) .unwrap(); let init_key = backend.crypto().derive_hpke_keypair( Ciphersuite::hpke_config( diff --git a/openmls/src/extensions/test_extensions.rs b/openmls/src/extensions/test_extensions.rs index c90f3f70de..4b53b07c83 100644 --- a/openmls/src/extensions/test_extensions.rs +++ b/openmls/src/extensions/test_extensions.rs @@ -65,7 +65,7 @@ async fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMls // === Alice creates a group with the ratchet tree extension === let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key.clone(), ) @@ -146,7 +146,7 @@ async fn ratchet_tree_extension(ciphersuite: Ciphersuite, backend: &impl OpenMls }; let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key, ) diff --git a/openmls/src/framing/private_message.rs b/openmls/src/framing/private_message.rs index a5010cc8a6..e95d3fd235 100644 --- a/openmls/src/framing/private_message.rs +++ b/openmls/src/framing/private_message.rs @@ -69,7 +69,7 @@ impl PrivateMessage { /// /// TODO #1148: Refactor theses constructors to avoid test code in main and /// to avoid validation using a special feature flag. - pub(crate) fn try_from_authenticated_content( + pub(crate) async fn try_from_authenticated_content( public_message: &AuthenticatedContent, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -90,10 +90,11 @@ impl PrivateMessage { message_secrets, padding_size, ) + .await } #[cfg(any(feature = "test-utils", test))] - pub(crate) fn encrypt_without_check( + pub(crate) async fn encrypt_without_check( public_message: &AuthenticatedContent, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -108,10 +109,11 @@ impl PrivateMessage { message_secrets, padding_size, ) + .await } #[cfg(test)] - pub(crate) fn encrypt_with_different_header( + pub(crate) async fn encrypt_with_different_header( public_message: &AuthenticatedContent, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -127,11 +129,12 @@ impl PrivateMessage { message_secrets, padding_size, ) + .await } /// Internal function to encrypt content. The extra message header is only used /// for tests. Otherwise, the data from the given `AuthenticatedContent` is used. - fn encrypt_content( + async fn encrypt_content( test_header: Option, public_message: &AuthenticatedContent, ciphersuite: Ciphersuite, @@ -170,8 +173,9 @@ impl PrivateMessage { // Even in tests we want to use the real sender index, so we have a key to encrypt. .secret_for_encryption(ciphersuite, backend, sender_index, secret_type)?; // Sample reuse guard uniformly at random. - let reuse_guard: ReuseGuard = - ReuseGuard::try_from_random(backend).map_err(LibraryError::unexpected_crypto_error)?; + let reuse_guard: ReuseGuard = ReuseGuard::try_from_random(backend) + .await + .map_err(LibraryError::unexpected_crypto_error)?; // Prepare the nonce by xoring with the reuse guard. let prepared_nonce = ratchet_nonce.xor_with_reuse_guard(&reuse_guard); // Encrypt the payload @@ -320,6 +324,7 @@ impl PrivateMessage { /// opaque padding[length_of_padding]; /// } PrivateMessageContent; /// ``` +#[allow(dead_code)] #[derive(Debug, Clone)] pub(crate) struct PrivateMessageContent { // The `content` field is serialized and deserialized manually without the diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/test_framing.rs index 4bad37dda9..3ea8b51519 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/test_framing.rs @@ -37,7 +37,7 @@ async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP let sender = Sender::build_member(LeafNodeIndex::new(987543210)); let group_context = GroupContext::new( ciphersuite, - GroupId::random(backend), + GroupId::random(backend).await, 1, vec![], vec![], @@ -49,7 +49,7 @@ async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP .expect("An unexpected error occurred."); let signature_input = FramedContentTbs::new( WireFormat::PublicMessage, - GroupId::random(backend), + GroupId::random(backend).await, 1, sender, vec![1, 2, 3].into(), @@ -63,6 +63,7 @@ async fn codec_plaintext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP let membership_key = MembershipKey::from_secret( Secret::random(ciphersuite, backend, None /* MLS version */) + .await .expect("Not enough randomness."), ); orig.set_membership_tag(backend, &membership_key, &serialized_context) @@ -98,7 +99,7 @@ async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto .expect("An unexpected error occurred."); let signature_input = FramedContentTbs::new( WireFormat::PrivateMessage, - GroupId::random(backend), + GroupId::random(backend).await, 1, sender, vec![1, 2, 3].into(), @@ -112,7 +113,7 @@ async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto let mut key_schedule = KeySchedule::init( ciphersuite, backend, - &JoinerSecret::random(ciphersuite, backend, ProtocolVersion::default()), + &JoinerSecret::random(ciphersuite, backend, ProtocolVersion::default()).await, PskSecret::from(Secret::zero(ciphersuite, ProtocolVersion::Mls10)), ) .expect("Could not create KeySchedule."); @@ -125,7 +126,8 @@ async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto .add_context(backend, &serialized_group_context) .expect("Could not add context to key schedule"); - let mut message_secrets = MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)); + let mut message_secrets = + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await; let orig = PrivateMessage::encrypt_with_different_header( &plaintext, @@ -139,6 +141,7 @@ async fn codec_ciphertext(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto &mut message_secrets, 0, ) + .await .expect("Could not encrypt PublicMessage."); let enc = orig @@ -159,10 +162,12 @@ async fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp let (plaintext, _credential, _keys) = create_content(ciphersuite, WireFormat::PrivateMessage, backend).await; - let mut message_secrets = MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)); + let mut message_secrets = + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await; let encryption_secret_bytes = backend .rand() .random_vec(ciphersuite.hash_length()) + .await .expect("An unexpected error occurred."); let sender_encryption_secret = EncryptionSecret::from_slice( &encryption_secret_bytes[..], @@ -200,6 +205,7 @@ async fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp &mut message_secrets, 0, ) + .await .expect("Could not encrypt PublicMessage.") .into(); @@ -244,6 +250,7 @@ async fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp &mut message_secrets, 0, ) + .await .expect("Could not encrypt PublicMessage.") .into(); @@ -284,6 +291,7 @@ async fn wire_format_checks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryp &mut message_secrets, 0, ) + .await .expect_err("Could encrypt despite wrong wire format."), MessageEncryptionError::WrongWireFormat ); @@ -310,7 +318,7 @@ async fn create_content( .expect("An unexpected error occurred."); let signature_input = FramedContentTbs::new( wire_format, - GroupId::random(backend), + GroupId::random(backend).await, 1, sender, vec![1, 2, 3].into(), @@ -331,7 +339,7 @@ async fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr test_utils::new_credential(backend, b"Creator", ciphersuite.signature_algorithm()).await; let group_context = GroupContext::new( ciphersuite, - GroupId::random(backend), + GroupId::random(backend).await, 1, vec![], vec![], @@ -339,6 +347,7 @@ async fn membership_tag(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr ); let membership_key = MembershipKey::from_secret( Secret::random(ciphersuite, backend, None /* MLS version */) + .await .expect("Not enough randomness."), ); let public_message: PublicMessage = AuthenticatedContent::new_application( @@ -408,7 +417,7 @@ async fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // Alice creates a group let mut group_alice = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential, ) @@ -575,6 +584,7 @@ async fn unknown_sender(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr group_alice.message_secrets_test_mut(), 0, ) + .await .expect("Encryption error"); let received_message = group_charlie.decrypt(&enc_message.into(), backend, configuration); @@ -645,7 +655,7 @@ pub(crate) async fn setup_alice_bob_group( // Alice creates a group let mut group_alice = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential, ) diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 8837eda493..7ce294b53e 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -246,8 +246,10 @@ impl CoreGroupBuilder { backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ) -> Result> { - let (public_group_builder, commit_secret, leaf_keypair) = - self.public_group_builder.get_secrets(backend, signer)?; + let (public_group_builder, commit_secret, leaf_keypair) = self + .public_group_builder + .get_secrets(backend, signer) + .await?; let ciphersuite = public_group_builder.crypto_config().ciphersuite; let config = self.config.unwrap_or_default(); @@ -267,6 +269,7 @@ impl CoreGroupBuilder { backend, commit_secret, &InitSecret::random(ciphersuite, backend, version) + .await .map_err(LibraryError::unexpected_crypto_error)?, &serialized_group_context, ) @@ -515,7 +518,7 @@ impl CoreGroup { .map_err(|e| e.into()) } // Create application message - pub(crate) fn create_application_message( + pub(crate) async fn create_application_message( &mut self, aad: &[u8], msg: &[u8], @@ -530,11 +533,11 @@ impl CoreGroup { self.context(), signer, )?; - self.encrypt(public_message, padding_size, backend) + self.encrypt(public_message, padding_size, backend).await } // Encrypt an PublicMessage into an PrivateMessage - pub(crate) fn encrypt( + pub(crate) async fn encrypt( &mut self, public_message: AuthenticatedContent, padding_size: usize, @@ -547,6 +550,7 @@ impl CoreGroup { self.message_secrets_store.message_secrets_mut(), padding_size, ) + .await } /// Decrypt an PrivateMessage into an PublicMessage @@ -986,7 +990,7 @@ impl CoreGroup { signer, params.take_credential_with_key(), apply_proposals_values.extensions, - )? + ).await? } else { // If path is not needed, update the group context and return // empty path processing results @@ -1127,15 +1131,17 @@ impl CoreGroup { // Create group secrets for later use, so we can afterwards consume the // `joiner_secret`. - let encrypted_secrets = diff.encrypt_group_secrets( - &joiner_secret, - apply_proposals_values.invitation_list, - path_computation_result.plain_path.as_deref(), - &apply_proposals_values.presharedkeys, - &encrypted_group_info, - backend, - self.own_leaf_index(), - )?; + let encrypted_secrets = diff + .encrypt_group_secrets( + &joiner_secret, + apply_proposals_values.invitation_list, + path_computation_result.plain_path.as_deref(), + &apply_proposals_values.presharedkeys, + &encrypted_group_info, + backend, + self.own_leaf_index(), + ) + .await?; // Create welcome message let welcome = Welcome::new(self.ciphersuite(), encrypted_secrets, encrypted_group_info); diff --git a/openmls/src/group/core_group/new_from_external_init.rs b/openmls/src/group/core_group/new_from_external_init.rs index b996f7cdc4..45007aabac 100644 --- a/openmls/src/group/core_group/new_from_external_init.rs +++ b/openmls/src/group/core_group/new_from_external_init.rs @@ -66,6 +66,7 @@ impl CoreGroup { let (init_secret, kem_output) = InitSecret::from_group_context(backend, group_context, external_pub.as_slice()) + .await .map_err(|_| ExternalCommitError::UnsupportedCiphersuite)?; // The `EpochSecrets` we create here are essentially zero, with the diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index e1e5eede56..f360d78f29 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -41,7 +41,7 @@ pub(crate) async fn setup_alice_group( // Alice creates a group let group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key.clone(), ) @@ -90,7 +90,7 @@ async fn test_failed_groupinfo_decryption( backend: &impl OpenMlsCryptoProvider, ) { let epoch = 123; - let group_id = GroupId::random(backend); + let group_id = GroupId::random(backend).await; let tree_hash = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; let confirmed_transcript_hash = vec![1, 1, 1]; let extensions = Extensions::empty(); @@ -129,8 +129,8 @@ async fn test_failed_groupinfo_decryption( }; // Generate key and nonce for the symmetric cipher. - let welcome_key = AeadKey::random(ciphersuite, backend.rand()); - let welcome_nonce = AeadNonce::random(backend); + let welcome_key = AeadKey::random(ciphersuite, backend.rand()).await; + let welcome_nonce = AeadNonce::random(backend).await; // Generate receiver key pair. let receiver_key_pair = backend @@ -138,6 +138,7 @@ async fn test_failed_groupinfo_decryption( .derive_hpke_keypair( ciphersuite.hpke_config(), Secret::random(ciphersuite, backend, None) + .await .expect("Not enough randomness.") .as_slice(), ) @@ -152,6 +153,7 @@ async fn test_failed_groupinfo_decryption( ciphersuite, backend.crypto(), ) + .await .unwrap(); let group_info = group_info_tbs @@ -347,17 +349,19 @@ async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide let psk_id = vec![1u8, 2, 3]; let secret = Secret::random(ciphersuite, backend, None /* MLS version */) + .await .expect("Not enough randomness."); let external_psk = ExternalPsk::new(psk_id); let preshared_key_id = PreSharedKeyId::new(ciphersuite, backend.rand(), Psk::External(external_psk)) + .await .expect("An unexpected error occured."); preshared_key_id .write_to_key_store(backend, ciphersuite, secret.as_slice()) .await .unwrap(); let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key, ) @@ -475,7 +479,7 @@ async fn test_staged_commit_creation( // === Alice creates a group === let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key, ) @@ -553,7 +557,7 @@ async fn test_own_commit_processing( // === Alice creates a group === let alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key, ) @@ -656,7 +660,7 @@ async fn test_proposal_application_after_self_was_removed( let (_, charlie_kpb, _, _) = setup_client("Charlie", ciphersuite, backend).await; let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, config::CryptoConfig::with_default_version(ciphersuite), alice_credential_with_key, ) diff --git a/openmls/src/group/core_group/test_past_secrets.rs b/openmls/src/group/core_group/test_past_secrets.rs index 1b4e38df8c..537775bf90 100644 --- a/openmls/src/group/core_group/test_past_secrets.rs +++ b/openmls/src/group/core_group/test_past_secrets.rs @@ -13,13 +13,13 @@ async fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMls // Create a store that keeps up to 3 epochs let mut message_secrets_store = MessageSecretsStore::new_with_secret( 3, - MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)), + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await, ); // Add message secrets to the store message_secrets_store.add( 0, - MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)), + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await, Vec::new(), ); @@ -30,7 +30,7 @@ async fn test_secret_tree_store(ciphersuite: Ciphersuite, backend: &impl OpenMls for i in 1..6u64 { message_secrets_store.add( i, - MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)), + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await, Vec::new(), ); } @@ -56,13 +56,13 @@ async fn test_empty_secret_tree_store( // Create a store that keeps no epochs let mut message_secrets_store = MessageSecretsStore::new_with_secret( 0, - MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)), + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await, ); // Add message secrets to the store message_secrets_store.add( 0, - MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)), + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await, Vec::new(), ); diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index 803397f797..7639862a89 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -56,7 +56,7 @@ async fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenM let group_context = GroupContext::new( ciphersuite, - GroupId::random(backend), + GroupId::random(backend).await, 0, vec![], vec![], @@ -203,7 +203,7 @@ async fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let group_context = GroupContext::new( ciphersuite, - GroupId::random(backend), + GroupId::random(backend).await, 0, vec![], vec![], @@ -308,7 +308,7 @@ async fn test_required_unsupported_proposals( // This must fail because we don't actually support AppAck proposals let e = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential, ) @@ -357,7 +357,7 @@ async fn test_group_context_extensions( let bob_key_package = bob_kpb.key_package(); let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential, ) @@ -431,7 +431,7 @@ async fn test_group_context_extension_proposal_fails( let required_capabilities = RequiredCapabilitiesExtension::new(&[], proposals, credentials); let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential, ) @@ -539,7 +539,7 @@ async fn test_group_context_extension_proposal( let bob_key_package = bob_kpb.key_package(); let mut alice_group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential, ) diff --git a/openmls/src/group/mls_group/application.rs b/openmls/src/group/mls_group/application.rs index 5e0e5b16f1..09cf428958 100644 --- a/openmls/src/group/mls_group/application.rs +++ b/openmls/src/group/mls_group/application.rs @@ -11,7 +11,7 @@ impl MlsGroup { /// Returns `CreateMessageError::MlsGroupStateError::PendingProposal` if pending proposals /// exist. In that case `.process_pending_proposals()` must be called first /// and incoming messages from the DS must be processed afterwards. - pub fn create_message( + pub async fn create_message( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -37,6 +37,7 @@ impl MlsGroup { backend, signer, ) + .await // We know the application message is wellformed and we have the key material of the current epoch .map_err(|_| LibraryError::custom("Malformed plaintext"))?; diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index 534e983984..bc82efdc09 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -31,7 +31,7 @@ impl MlsGroup { backend, signer, mls_group_config, - GroupId::random(backend), + GroupId::random(backend).await, credential_with_key, ) .await diff --git a/openmls/src/group/mls_group/extension.rs b/openmls/src/group/mls_group/extension.rs index 541bb170b4..8b55930b64 100644 --- a/openmls/src/group/mls_group/extension.rs +++ b/openmls/src/group/mls_group/extension.rs @@ -19,7 +19,7 @@ impl MlsGroup { /// of the group but does not merge them yet. /// /// Returns an error if there is a pending commit. - pub fn propose_extensions( + pub async fn propose_extensions( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -43,7 +43,7 @@ impl MlsGroup { self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(gce_proposal, backend)?; + let mls_message = self.content_to_mls_message(gce_proposal, backend).await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -84,7 +84,9 @@ impl MlsGroup { // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration - let mls_messages = self.content_to_mls_message(create_commit_result.commit, backend)?; + let mls_messages = self + .content_to_mls_message(create_commit_result.commit, backend) + .await?; // Set the current group state to [`MlsGroupState::PendingCommit`], // storing the current [`StagedCommit`] from the commit results diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 7f7bcd4ad8..ec7e632a2c 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -76,7 +76,9 @@ impl MlsGroup { // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration - let mls_messages = self.content_to_mls_message(create_commit_result.commit, backend)?; + let mls_messages = self + .content_to_mls_message(create_commit_result.commit, backend) + .await?; // Set the current group state to [`MlsGroupState::PendingCommit`], // storing the current [`StagedCommit`] from the commit results @@ -149,7 +151,9 @@ impl MlsGroup { // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration - let mls_message = self.content_to_mls_message(create_commit_result.commit, backend)?; + let mls_message = self + .content_to_mls_message(create_commit_result.commit, backend) + .await?; // Set the current group state to [`MlsGroupState::PendingCommit`], // storing the current [`StagedCommit`] from the commit results @@ -175,7 +179,7 @@ impl MlsGroup { /// The Remove Proposal is returned as a [`MlsMessageOut`]. /// /// Returns an error if there is a pending commit. - pub fn leave_group( + pub async fn leave_group( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -195,7 +199,9 @@ impl MlsGroup { remove_proposal.clone(), )?); - Ok(self.content_to_mls_message(remove_proposal, backend)?) + Ok(self + .content_to_mls_message(remove_proposal, backend) + .await?) } /// Returns a list of [`Member`]s in the group. diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 2dea8a478f..5001f183ed 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -357,7 +357,7 @@ impl MlsGroup { /// Converts PublicMessage to MlsMessage. Depending on whether handshake /// message should be encrypted, PublicMessage messages are encrypted to /// PrivateMessage first. - fn content_to_mls_message( + async fn content_to_mls_message( &mut self, mls_auth_content: AuthenticatedContent, backend: &impl OpenMlsCryptoProvider, @@ -383,6 +383,7 @@ impl MlsGroup { self.configuration().padding_size(), backend, ) + .await // We can be sure the encryption will work because the plaintext was created by us .map_err(|_| LibraryError::custom("Malformed plaintext"))?; MlsMessageOut::from_private_message(ciphertext, self.group.version()) diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index 82d4b8b900..79371e47c3 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -105,7 +105,9 @@ impl MlsGroup { // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration - let mls_message = self.content_to_mls_message(create_commit_result.commit, backend)?; + let mls_message = self + .content_to_mls_message(create_commit_result.commit, backend) + .await?; // Set the current group state to [`MlsGroupState::PendingCommit`], // storing the current [`StagedCommit`] from the commit results diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 2d931ee0aa..90ba211c82 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -61,7 +61,7 @@ macro_rules! impl_propose_fun { /// Creates proposals to add an external PSK to the key schedule. /// /// Returns an error if there is a pending commit. - pub fn $name( + pub async fn $name( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -83,7 +83,7 @@ macro_rules! impl_propose_fun { log::trace!("Storing proposal in queue {:?}", queued_proposal); self.proposal_store.add(queued_proposal); - let mls_message = self.content_to_mls_message(proposal, backend)?; + let mls_message = self.content_to_mls_message(proposal, backend).await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -126,7 +126,7 @@ impl MlsGroup { let proposal_ref = queued_proposal.proposal_reference().clone(); self.proposal_store.add(queued_proposal); - let mls_message = self.content_to_mls_message(proposal, backend)?; + let mls_message = self.content_to_mls_message(proposal, backend).await?; self.flag_state_change(); @@ -192,31 +192,38 @@ impl MlsGroup { }, Propose::Remove(leaf_index) => match ref_or_value { - ProposalOrRefType::Proposal => self.propose_remove_member_by_value( - backend, - signer, - LeafNodeIndex::new(leaf_index), - ), + ProposalOrRefType::Proposal => { + self.propose_remove_member_by_value( + backend, + signer, + LeafNodeIndex::new(leaf_index), + ) + .await + } ProposalOrRefType::Reference => self .propose_remove_member(backend, signer, LeafNodeIndex::new(leaf_index)) + .await .map_err(|e| e.into()), }, Propose::RemoveCredential(credential) => match ref_or_value { ProposalOrRefType::Proposal => { self.propose_remove_member_by_credential_by_value(backend, signer, &credential) + .await } ProposalOrRefType::Reference => self .propose_remove_member_by_credential(backend, signer, &credential) + .await .map_err(|e| e.into()), }, Propose::PreSharedKey(psk_id) => match psk_id.psk() { crate::schedule::Psk::External(_) => match ref_or_value { ProposalOrRefType::Proposal => { self.propose_external_psk_by_value(backend, signer, psk_id) + .await } ProposalOrRefType::Reference => { - self.propose_external_psk(backend, signer, psk_id) + self.propose_external_psk(backend, signer, psk_id).await } }, crate::schedule::Psk::Resumption(_) => Err(ProposalError::LibraryError( @@ -271,7 +278,7 @@ impl MlsGroup { let proposal_ref = proposal.proposal_reference().clone(); self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(add_proposal, backend)?; + let mls_message = self.content_to_mls_message(add_proposal, backend).await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -283,7 +290,7 @@ impl MlsGroup { /// The `member` has to be the member's leaf index. /// /// Returns an error if there is a pending commit. - pub fn propose_remove_member( + pub async fn propose_remove_member( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -304,7 +311,9 @@ impl MlsGroup { let proposal_ref = proposal.proposal_reference().clone(); self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(remove_proposal, backend)?; + let mls_message = self + .content_to_mls_message(remove_proposal, backend) + .await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -316,7 +325,7 @@ impl MlsGroup { /// The `member` has to be the member's credential. /// /// Returns an error if there is a pending commit. - pub fn propose_remove_member_by_credential( + pub async fn propose_remove_member_by_credential( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -332,6 +341,7 @@ impl MlsGroup { if let Some(member_index) = member_index { self.propose_remove_member(backend, signer, member_index) + .await } else { Err(ProposeRemoveMemberError::UnknownMember) } @@ -341,7 +351,7 @@ impl MlsGroup { /// The `member` has to be the member's credential. /// /// Returns an error if there is a pending commit. - pub fn propose_remove_member_by_credential_by_value( + pub async fn propose_remove_member_by_credential_by_value( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -357,6 +367,7 @@ impl MlsGroup { if let Some(member_index) = member_index { self.propose_remove_member_by_value(backend, signer, member_index) + .await } else { Err(ProposalError::ProposeRemoveMemberError( ProposeRemoveMemberError::UnknownMember, diff --git a/openmls/src/group/mls_group/reinit.rs b/openmls/src/group/mls_group/reinit.rs index 86a8996008..2f0456368f 100644 --- a/openmls/src/group/mls_group/reinit.rs +++ b/openmls/src/group/mls_group/reinit.rs @@ -12,7 +12,7 @@ impl MlsGroup { /// /// Returns an error if there is a pending commit, if the new proposed version is older than /// the current or if any member doesn't support the proposed extensions and/or ciphersuite. - pub fn propose_reinit( + pub async fn propose_reinit( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -40,7 +40,9 @@ impl MlsGroup { self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(reinit_proposal, backend)?; + let mls_message = self + .content_to_mls_message(reinit_proposal, backend) + .await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -85,7 +87,9 @@ impl MlsGroup { // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration - let mls_messages = self.content_to_mls_message(create_commit_result.commit, backend)?; + let mls_messages = self + .content_to_mls_message(create_commit_result.commit, backend) + .await?; // Set the current group state to [`MlsGroupState::PendingCommit`], // storing the current [`StagedCommit`] from the commit results diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index b531b50a3e..7d99f2a502 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -167,6 +167,7 @@ async fn remover(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) let (queued_messages, _) = alice_group .propose_remove_member(backend, &alice_signer, LeafNodeIndex::new(1)) + .await .expect("Could not propose removal"); let charlie_processed_message = charlie_group @@ -462,6 +463,7 @@ async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl Open )); let error = alice_group .propose_remove_member(backend, &alice_signer, LeafNodeIndex::new(1)) + .await .expect_err("no error creating a proposal while a commit is pending"); assert!(matches!( error, diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index db7df5e118..e15766503b 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -82,7 +82,9 @@ impl MlsGroup { // Convert PublicMessage messages to MLSMessage and encrypt them if required by // the configuration - let mls_message = self.content_to_mls_message(create_commit_result.commit, backend)?; + let mls_message = self + .content_to_mls_message(create_commit_result.commit, backend) + .await?; // Set the current group state to [`MlsGroupState::PendingCommit`], // storing the current [`StagedCommit`] from the commit results @@ -117,7 +119,9 @@ impl MlsGroup { let proposal_ref = proposal.proposal_reference().clone(); self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(update_proposal, backend)?; + let mls_message = self + .content_to_mls_message(update_proposal, backend) + .await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -140,7 +144,9 @@ impl MlsGroup { let proposal_ref = proposal.proposal_reference().clone(); self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(update_proposal, backend)?; + let mls_message = self + .content_to_mls_message(update_proposal, backend) + .await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -164,15 +170,17 @@ impl MlsGroup { .own_leaf() .ok_or_else(|| LibraryError::custom("The tree is broken. Couldn't find own leaf."))? .clone(); - let keypair = own_leaf.rekey( - self.group_id(), - self.own_leaf_index(), - None, - self.ciphersuite(), - ProtocolVersion::default(), // XXX: openmls/openmls#1065 - backend, - signer, - )?; + let keypair = own_leaf + .rekey( + self.group_id(), + self.own_leaf_index(), + None, + self.ciphersuite(), + ProtocolVersion::default(), // XXX: openmls/openmls#1065 + backend, + signer, + ) + .await?; keypair .write_to_key_store(backend) @@ -222,7 +230,9 @@ impl MlsGroup { let proposal_ref = proposal.proposal_reference().clone(); self.proposal_store.add(proposal); - let mls_message = self.content_to_mls_message(update_proposal, backend)?; + let mls_message = self + .content_to_mls_message(update_proposal, backend) + .await?; // Since the state of the group might be changed, arm the state flag self.flag_state_change(); @@ -247,15 +257,17 @@ impl MlsGroup { .ok_or_else(|| LibraryError::custom("The tree is broken. Couldn't find own leaf."))? .clone(); - let keypair = own_leaf.rekey( - self.group_id(), - self.own_leaf_index(), - Some(leaf_node), - self.ciphersuite(), - ProtocolVersion::Mls10, - backend, - leaf_node_signer, - )?; + let keypair = own_leaf + .rekey( + self.group_id(), + self.own_leaf_index(), + Some(leaf_node), + self.ciphersuite(), + ProtocolVersion::Mls10, + backend, + leaf_node_signer, + ) + .await?; keypair .write_to_key_store(backend) diff --git a/openmls/src/group/mod.rs b/openmls/src/group/mod.rs index b23cb31e40..752957f50f 100644 --- a/openmls/src/group/mod.rs +++ b/openmls/src/group/mod.rs @@ -61,11 +61,12 @@ impl GroupId { /// Create a new (random) group ID. /// /// Group IDs should be random and not be misused as, e.g., a group name. - pub fn random(backend: &impl OpenMlsCryptoProvider) -> Self { + pub async fn random(backend: &impl OpenMlsCryptoProvider) -> Self { Self { value: backend .rand() .random_vec(16) + .await .expect("Not enough randomness.") .into(), } diff --git a/openmls/src/group/public_group/builder.rs b/openmls/src/group/public_group/builder.rs index c7deec3a67..6b4177de3e 100644 --- a/openmls/src/group/public_group/builder.rs +++ b/openmls/src/group/public_group/builder.rs @@ -76,7 +76,7 @@ impl TempBuilderPG1 { self } - pub(crate) fn get_secrets( + pub(crate) async fn get_secrets( self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -89,7 +89,8 @@ impl TempBuilderPG1 { self.lifetime.unwrap_or_default(), self.leaf_capabilities.unwrap_or_default(), self.leaf_extensions.unwrap_or_default(), - )?; + ) + .await?; let required_capabilities = self.required_capabilities.unwrap_or_default(); required_capabilities.check_support().map_err(|e| match e { ExtensionError::UnsupportedProposalType => { diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index 20f7f24e9c..99df1a6b23 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -77,7 +77,7 @@ impl<'a> PublicGroupDiff<'a> { /// - the leaf index of a new member is identical to the own leaf index /// - the plain path does not contain the correct secrets #[allow(clippy::too_many_arguments)] - pub(crate) fn encrypt_group_secrets( + pub(crate) async fn encrypt_group_secrets( &self, joiner_secret: &JoinerSecret, invited_members: Vec<(LeafNodeIndex, AddProposal)>, @@ -87,15 +87,17 @@ impl<'a> PublicGroupDiff<'a> { backend: &impl OpenMlsCryptoProvider, leaf_index: LeafNodeIndex, ) -> Result, LibraryError> { - self.diff.encrypt_group_secrets( - joiner_secret, - invited_members, - plain_path_option, - presharedkeys, - encrypted_group_info, - backend, - leaf_index, - ) + self.diff + .encrypt_group_secrets( + joiner_secret, + invited_members, + plain_path_option, + presharedkeys, + encrypted_group_info, + backend, + leaf_index, + ) + .await } /// Returns the tree size diff --git a/openmls/src/group/public_group/diff/compute_path.rs b/openmls/src/group/public_group/diff/compute_path.rs index a86ec16a93..0bba4fdce1 100644 --- a/openmls/src/group/public_group/diff/compute_path.rs +++ b/openmls/src/group/public_group/diff/compute_path.rs @@ -39,7 +39,7 @@ pub(crate) struct PathComputationResult { impl<'a> PublicGroupDiff<'a> { #[allow(clippy::too_many_arguments)] - pub(crate) fn compute_path( + pub(crate) async fn compute_path( &mut self, backend: &impl OpenMlsCryptoProvider, leaf_index: LeafNodeIndex, @@ -89,7 +89,8 @@ impl<'a> PublicGroupDiff<'a> { backend, signer, credential_with_key.ok_or(CreateCommitError::MissingCredential)?, - )?; + ) + .await?; let leaf_node: LeafNode = key_package.into(); self.diff @@ -108,15 +109,17 @@ impl<'a> PublicGroupDiff<'a> { .leaf_mut(leaf_index) .ok_or_else(|| LibraryError::custom("Unable to get own leaf from diff"))?; - let encryption_keypair = own_diff_leaf.rekey( - &group_id, - leaf_index, - None, - ciphersuite, - version, - backend, - signer, - )?; + let encryption_keypair = own_diff_leaf + .rekey( + &group_id, + leaf_index, + None, + ciphersuite, + version, + backend, + signer, + ) + .await?; vec![encryption_keypair] }; @@ -124,7 +127,8 @@ impl<'a> PublicGroupDiff<'a> { // generated new leaf. let (plain_path, mut new_parent_keypairs, commit_secret) = self .diff - .apply_own_update_path(backend, signer, ciphersuite, group_id, leaf_index)?; + .apply_own_update_path(backend, signer, ciphersuite, group_id, leaf_index) + .await?; new_keypairs.append(&mut new_parent_keypairs); @@ -139,14 +143,17 @@ impl<'a> PublicGroupDiff<'a> { .map_err(LibraryError::missing_bound_check)?; // Encrypt the path to the correct recipient nodes. - let encrypted_path = self.diff.encrypt_path( - backend, - ciphersuite, - &plain_path, - &serialized_group_context, - &exclusion_list, - leaf_index, - )?; + let encrypted_path = self + .diff + .encrypt_path( + backend, + ciphersuite, + &plain_path, + &serialized_group_context, + &exclusion_list, + leaf_index, + ) + .await?; let leaf_node = self .diff .leaf(leaf_index) diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index 02160922dd..48dffac4c3 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -344,7 +344,7 @@ impl PublicGroup { } #[cfg(test)] - pub(crate) fn encrypt_path( + pub(crate) async fn encrypt_path( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -353,13 +353,16 @@ impl PublicGroup { exclusion_list: &std::collections::HashSet<&LeafNodeIndex>, own_leaf_index: LeafNodeIndex, ) -> Result, LibraryError> { - self.treesync().empty_diff().encrypt_path( - backend, - ciphersuite, - path, - group_context, - exclusion_list, - own_leaf_index, - ) + self.treesync() + .empty_diff() + .encrypt_path( + backend, + ciphersuite, + path, + group_context, + exclusion_list, + own_leaf_index, + ) + .await } } diff --git a/openmls/src/group/public_group/tests.rs b/openmls/src/group/public_group/tests.rs index 1e74309bda..cbff923bfe 100644 --- a/openmls/src/group/public_group/tests.rs +++ b/openmls/src/group/public_group/tests.rs @@ -21,280 +21,284 @@ use crate::test_utils::*; #[apply(ciphersuites_and_backends)] async fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - let group_id = GroupId::from_slice(b"Test Group"); - - let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, backend).await; - let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = - setup_client("Bob", ciphersuite, backend).await; - let (_charlie_credential, charlie_kpb, charlie_signer, _charlie_pk) = - setup_client("Charly", ciphersuite, backend).await; - - // Define the MlsGroup configuration - // Set plaintext wire format policy s.t. the public group can track changes. - let mls_group_config = MlsGroupConfigBuilder::new() - .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) - .crypto_config(CryptoConfig::with_default_version(ciphersuite)) - .build(); - - // === Alice creates a group === - let mut alice_group = MlsGroup::new_with_group_id( - backend, - &alice_signer, - &mls_group_config, - group_id, - alice_credential_with_key, - ) - .await - .expect("An unexpected error occurred."); - - // === Create a public group that tracks the changes throughout this test === - let verifiable_group_info = alice_group - .export_group_info(backend, &alice_signer, false) - .unwrap() - .into_verifiable_group_info() - .unwrap(); - let ratchet_tree = alice_group.export_ratchet_tree(); - let (mut public_group, _extensions) = PublicGroup::from_external( - backend, - ratchet_tree.into(), - verifiable_group_info, - ProposalStore::new(), - true, - ) - .await - .unwrap(); - - // === Alice adds Bob === - let (message, welcome, _group_info) = alice_group - .add_members( + Box::pin(async { + let group_id = GroupId::from_slice(b"Test Group"); + + let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = + setup_client("Alice", ciphersuite, backend).await; + let (_bob_credential, bob_kpb, bob_signer, _bob_pk) = + setup_client("Bob", ciphersuite, backend).await; + let (_charlie_credential, charlie_kpb, charlie_signer, _charlie_pk) = + setup_client("Charly", ciphersuite, backend).await; + + // Define the MlsGroup configuration + // Set plaintext wire format policy s.t. the public group can track changes. + let mls_group_config = MlsGroupConfigBuilder::new() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .crypto_config(CryptoConfig::with_default_version(ciphersuite)) + .build(); + + // === Alice creates a group === + let mut alice_group = MlsGroup::new_with_group_id( backend, &alice_signer, - vec![bob_kpb.key_package().clone().into()], + &mls_group_config, + group_id, + alice_credential_with_key, ) .await - .expect("Could not add member to group."); - - alice_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); - - let public_message = match message.into_protocol_message().unwrap() { - ProtocolMessage::PrivateMessage(_) => panic!("Unexpected message type."), - ProtocolMessage::PublicMessage(public_message) => public_message, - }; - let processed_message = public_group - .process_message(backend, public_message) - .await - .unwrap(); - - // Further inspection of the message can take place here ... - match processed_message.into_content() { - ProcessedMessageContent::ApplicationMessage(_) - | ProcessedMessageContent::ProposalMessage(_) - | ProcessedMessageContent::ExternalJoinProposalMessage(_) => { - panic!("Unexpected message type.") - } - ProcessedMessageContent::StagedCommitMessage(staged_commit) => { - // Merge the diff - public_group.merge_commit(*staged_commit) - } - }; - - // In the future, we'll use helper functions to skip the extraction steps above. - - let mut bob_group = MlsGroup::new_from_welcome( - backend, - &mls_group_config, - welcome.into_welcome().expect("Unexpected message type."), - Some(alice_group.export_ratchet_tree().into()), - ) - .await - .expect("Error creating group from Welcome"); - - // === Bob adds Charlie === - let (queued_messages, welcome, _group_info) = bob_group - .add_members( + .expect("An unexpected error occurred."); + + // === Create a public group that tracks the changes throughout this test === + let verifiable_group_info = alice_group + .export_group_info(backend, &alice_signer, false) + .unwrap() + .into_verifiable_group_info() + .unwrap(); + let ratchet_tree = alice_group.export_ratchet_tree(); + let (mut public_group, _extensions) = PublicGroup::from_external( backend, - &bob_signer, - vec![charlie_kpb.key_package().clone().into()], + ratchet_tree.into(), + verifiable_group_info, + ProposalStore::new(), + true, ) .await .unwrap(); - // Alice processes - let alice_processed_message = alice_group - .process_message( - backend, - queued_messages - .clone() - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process messages."); - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - alice_processed_message.into_content() - { + // === Alice adds Bob === + let (message, welcome, _group_info) = alice_group + .add_members( + backend, + &alice_signer, + vec![bob_kpb.key_package().clone().into()], + ) + .await + .expect("Could not add member to group."); + alice_group - .merge_staged_commit(backend, *staged_commit) + .merge_pending_commit(backend) .await - .expect("Error merging commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("error merging pending commit"); + + let public_message = match message.into_protocol_message().unwrap() { + ProtocolMessage::PrivateMessage(_) => panic!("Unexpected message type."), + ProtocolMessage::PublicMessage(public_message) => public_message, + }; + let processed_message = public_group + .process_message(backend, public_message) + .await + .unwrap(); + + // Further inspection of the message can take place here ... + match processed_message.into_content() { + ProcessedMessageContent::ApplicationMessage(_) + | ProcessedMessageContent::ProposalMessage(_) + | ProcessedMessageContent::ExternalJoinProposalMessage(_) => { + panic!("Unexpected message type.") + } + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + // Merge the diff + public_group.merge_commit(*staged_commit) + } + }; - // The public group processes - let ppm = public_group - .process_message(backend, into_public_message(queued_messages)) - .await - .unwrap(); - public_group.merge_commit(extract_staged_commit(ppm)); + // In the future, we'll use helper functions to skip the extraction steps above. - // Bob merges - bob_group - .merge_pending_commit(backend) + let mut bob_group = MlsGroup::new_from_welcome( + backend, + &mls_group_config, + welcome.into_welcome().expect("Unexpected message type."), + Some(alice_group.export_ratchet_tree().into()), + ) .await - .expect("error merging pending commit"); - - let mut charlie_group = MlsGroup::new_from_welcome( - backend, - &mls_group_config, - welcome.into_welcome().expect("Unexpected message type."), - Some(bob_group.export_ratchet_tree().into()), - ) - .await - .expect("Error creating group from Welcome"); + .expect("Error creating group from Welcome"); + + // === Bob adds Charlie === + let (queued_messages, welcome, _group_info) = bob_group + .add_members( + backend, + &bob_signer, + vec![charlie_kpb.key_package().clone().into()], + ) + .await + .unwrap(); + + // Alice processes + let alice_processed_message = alice_group + .process_message( + backend, + queued_messages + .clone() + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process messages."); + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + alice_processed_message.into_content() + { + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - // === Alice removes Bob & Charlie commits === + // The public group processes + let ppm = public_group + .process_message(backend, into_public_message(queued_messages)) + .await + .unwrap(); + public_group.merge_commit(extract_staged_commit(ppm)); - let (queued_messages, _) = alice_group - .propose_remove_member(backend, &alice_signer, LeafNodeIndex::new(1)) - .expect("Could not propose removal"); + // Bob merges + bob_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); - let charlie_processed_message = charlie_group - .process_message( + let mut charlie_group = MlsGroup::new_from_welcome( backend, - queued_messages - .clone() - .into_protocol_message() - .expect("Unexpected message type"), + &mls_group_config, + welcome.into_welcome().expect("Unexpected message type."), + Some(bob_group.export_ratchet_tree().into()), ) .await - .expect("Could not process messages."); + .expect("Error creating group from Welcome"); - // The public group processes - let ppm = public_group - .process_message(backend, into_public_message(queued_messages)) - .await - .unwrap(); - // We have to add the proposal to the public group's proposal store. - match ppm.into_content() { - ProcessedMessageContent::ApplicationMessage(_) - | ProcessedMessageContent::ExternalJoinProposalMessage(_) - | ProcessedMessageContent::StagedCommitMessage(_) => panic!("Unexpected message type."), - ProcessedMessageContent::ProposalMessage(p) => { - match p.proposal() { - Proposal::Remove(r) => assert_eq!(r.removed(), LeafNodeIndex::new(1)), - _ => panic!("Unexpected proposal type"), + // === Alice removes Bob & Charlie commits === + + let (queued_messages, _) = alice_group + .propose_remove_member(backend, &alice_signer, LeafNodeIndex::new(1)) + .await + .expect("Could not propose removal"); + + let charlie_processed_message = charlie_group + .process_message( + backend, + queued_messages + .clone() + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process messages."); + + // The public group processes + let ppm = public_group + .process_message(backend, into_public_message(queued_messages)) + .await + .unwrap(); + // We have to add the proposal to the public group's proposal store. + match ppm.into_content() { + ProcessedMessageContent::ApplicationMessage(_) + | ProcessedMessageContent::ExternalJoinProposalMessage(_) + | ProcessedMessageContent::StagedCommitMessage(_) => panic!("Unexpected message type."), + ProcessedMessageContent::ProposalMessage(p) => { + match p.proposal() { + Proposal::Remove(r) => assert_eq!(r.removed(), LeafNodeIndex::new(1)), + _ => panic!("Unexpected proposal type"), + } + public_group.add_proposal(*p); } - public_group.add_proposal(*p); } - } - // Check that we received the correct proposals - if let ProcessedMessageContent::ProposalMessage(staged_proposal) = - charlie_processed_message.into_content() - { - if let Proposal::Remove(ref remove_proposal) = staged_proposal.proposal() { - // Check that Bob was removed - assert_eq!(remove_proposal.removed(), LeafNodeIndex::new(1)); - // Store proposal - charlie_group.store_pending_proposal(*staged_proposal.clone()); + // Check that we received the correct proposals + if let ProcessedMessageContent::ProposalMessage(staged_proposal) = + charlie_processed_message.into_content() + { + if let Proposal::Remove(ref remove_proposal) = staged_proposal.proposal() { + // Check that Bob was removed + assert_eq!(remove_proposal.removed(), LeafNodeIndex::new(1)); + // Store proposal + charlie_group.store_pending_proposal(*staged_proposal.clone()); + } else { + unreachable!("Expected a Proposal."); + } + + // Check that Alice removed Bob + assert!(matches!( + staged_proposal.sender(), + Sender::Member(member) if member.u32() == 0 + )); } else { - unreachable!("Expected a Proposal."); + unreachable!("Expected a QueuedProposal."); } - // Check that Alice removed Bob - assert!(matches!( - staged_proposal.sender(), - Sender::Member(member) if member.u32() == 0 - )); - } else { - unreachable!("Expected a QueuedProposal."); - } - - // Charlie commits - let (queued_messages, _welcome, _group_info) = charlie_group - .commit_to_pending_proposals(backend, &charlie_signer) - .await - .expect("Could not commit proposal"); + // Charlie commits + let (queued_messages, _welcome, _group_info) = charlie_group + .commit_to_pending_proposals(backend, &charlie_signer) + .await + .expect("Could not commit proposal"); - // The public group processes - let ppm = public_group - .process_message(backend, into_public_message(queued_messages.clone())) - .await - .unwrap(); - public_group.merge_commit(extract_staged_commit(ppm)); - - // Check that we receive the correct proposal - if let Some(staged_commit) = charlie_group.pending_commit() { - let remove = staged_commit - .remove_proposals() - .next() - .expect("Expected a proposal."); - // Check that Bob was removed - assert_eq!(remove.remove_proposal().removed().u32(), 1); - // Check that Alice removed Bob - assert!(matches!(remove.sender(), Sender::Member(member) if member.u32() == 0)); - } else { - unreachable!("Expected a StagedCommit."); - }; - - charlie_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); + // The public group processes + let ppm = public_group + .process_message(backend, into_public_message(queued_messages.clone())) + .await + .unwrap(); + public_group.merge_commit(extract_staged_commit(ppm)); + + // Check that we receive the correct proposal + if let Some(staged_commit) = charlie_group.pending_commit() { + let remove = staged_commit + .remove_proposals() + .next() + .expect("Expected a proposal."); + // Check that Bob was removed + assert_eq!(remove.remove_proposal().removed().u32(), 1); + // Check that Alice removed Bob + assert!(matches!(remove.sender(), Sender::Member(member) if member.u32() == 0)); + } else { + unreachable!("Expected a StagedCommit."); + }; - // Alice processes - let alice_processed_message = alice_group - .process_message( - backend, - queued_messages - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process messages."); - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - alice_processed_message.into_content() - { - alice_group - .merge_staged_commit(backend, *staged_commit) + charlie_group + .merge_pending_commit(backend) .await - .expect("Error merging commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("error merging pending commit"); + + // Alice processes + let alice_processed_message = alice_group + .process_message( + backend, + queued_messages + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process messages."); + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + alice_processed_message.into_content() + { + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - // Check that the public group state matches that of all other participants - assert_eq!( - alice_group.export_group_context(), - public_group.group_context() - ); - assert_eq!( - charlie_group.export_group_context(), - public_group.group_context() - ); - assert_eq!( - alice_group.export_ratchet_tree(), - public_group.export_ratchet_tree() - ); - assert_eq!( - charlie_group.export_ratchet_tree(), - public_group.export_ratchet_tree() - ); + // Check that the public group state matches that of all other participants + assert_eq!( + alice_group.export_group_context(), + public_group.group_context() + ); + assert_eq!( + charlie_group.export_group_context(), + public_group.group_context() + ); + assert_eq!( + alice_group.export_ratchet_tree(), + public_group.export_ratchet_tree() + ); + assert_eq!( + charlie_group.export_ratchet_tree(), + public_group.export_ratchet_tree() + ); + }) + .await; } // A helper function diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests/external_add_proposal.rs index e81835f3d5..4e362edc8c 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests/external_add_proposal.rs @@ -341,6 +341,7 @@ async fn new_member_proposal_sender_should_be_reserved_for_join_proposals( // Remove proposal cannot have a 'new_member_proposal' sender let remove_proposal = alice_group .propose_remove_member(backend, &alice_signer, LeafNodeIndex::new(1)) + .await .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); if let MlsMessageInBody::PublicMessage(mut plaintext) = remove_proposal.body { diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests/test_commit_validation.rs index 5aa4d0e78a..3d0accf6d3 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests/test_commit_validation.rs @@ -1,6 +1,7 @@ //! This module tests the validation of commits as defined in //! https://openmls.tech/book/message_validation.html#commit-message-validation +use futures::{stream, StreamExt}; use openmls_rust_crypto::OpenMlsRustCrypto; use openmls_traits::{signatures::Signer, types::Ciphersuite}; use rstest::*; @@ -147,6 +148,7 @@ async fn test_valsem200(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr &alice_credential.signer, alice_group.own_leaf_index(), ) + .await .expect("error creating commit") .tls_serialize_detached() .expect("serialization error"); @@ -285,16 +287,18 @@ async fn test_valsem201(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr }; let psk_proposal = || async { - let secret = Secret::random(ciphersuite, backend, None).unwrap(); + let secret = Secret::random(ciphersuite, backend, None).await.unwrap(); let rand = backend .rand() .random_vec(ciphersuite.hash_length()) + .await .unwrap(); let psk_id = PreSharedKeyId::new( ciphersuite, backend.rand(), Psk::External(ExternalPsk::new(rand)), ) + .await .unwrap(); psk_id .write_to_key_store(backend, ciphersuite, secret.as_slice()) @@ -649,18 +653,18 @@ async fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // public keys derived from those secrets will then differ from the public // keys in the update path, thus causing the error. if let Some(ref mut path) = commit_content.path { - let new_plain_path: Vec = path - .nodes() - .iter() - .map(|upn| { + let new_plain_path = stream::iter(path.nodes()) + .then(|upn| async move { PlainUpdatePathNode::new( upn.encryption_key().clone(), Secret::random(ciphersuite, backend, ProtocolVersion::default()) + .await .unwrap() .into(), ) }) - .collect(); + .collect::>() + .await; let new_nodes = alice_group .group() .public_group() @@ -672,6 +676,7 @@ async fn test_valsem204(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr &[].into(), LeafNodeIndex::new(0), ) + .await .unwrap(); let new_path = UpdatePath::new(path.leaf_node().clone(), new_nodes); commit_content.path = Some(new_path); @@ -806,6 +811,7 @@ async fn test_partial_proposal_commit( // Create first proposal in Alice's group let proposal_1 = alice_group .propose_remove_member(backend, &alice_credential.signer, charlie_index) + .await .map(|(out, _)| MlsMessageIn::from(out)) .unwrap(); let proposal_1 = bob_group diff --git a/openmls/src/group/tests/test_encoding.rs b/openmls/src/group/tests/test_encoding.rs index b09455aaab..a68a9f0502 100644 --- a/openmls/src/group/tests/test_encoding.rs +++ b/openmls/src/group/tests/test_encoding.rs @@ -82,6 +82,7 @@ async fn test_application_message_encoding(backend: &impl OpenMlsCryptoProvider) backend, &credential_with_key_and_signer.signer, ) + .await .expect("An unexpected error occurred."); let encrypted_message_bytes = encrypted_message .tls_serialize_detached() diff --git a/openmls/src/group/tests/test_framing.rs b/openmls/src/group/tests/test_framing.rs index aaa42ad9d0..9369d62b69 100644 --- a/openmls/src/group/tests/test_framing.rs +++ b/openmls/src/group/tests/test_framing.rs @@ -80,6 +80,7 @@ async fn padding(backend: &impl OpenMlsCryptoProvider) { backend, &credential.signer, ) + .await .expect("An unexpected error occurred."); let ciphertext = private_message.ciphertext(); let length = ciphertext.len(); @@ -130,7 +131,7 @@ async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi let group_context = group_context::GroupContext::new( ciphersuite, - GroupId::random(backend), + GroupId::random(backend).await, 1, vec![], vec![], @@ -140,7 +141,7 @@ async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi let plaintext = { let plaintext_tbs = FramedContentTbs::new( WireFormat::PrivateMessage, - GroupId::random(backend), + GroupId::random(backend).await, 1, sender, vec![1, 2, 3].into(), @@ -154,11 +155,12 @@ async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi }; let mut message_secrets = - MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)); + MessageSecrets::random(ciphersuite, backend, LeafNodeIndex::new(0)).await; let encryption_secret_bytes = backend .rand() .random_vec(ciphersuite.hash_length()) + .await .unwrap(); let sender_secret_tree = { @@ -222,7 +224,7 @@ async fn bad_padding(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi .unwrap(); // Sample reuse guard uniformly at random. - let reuse_guard: ReuseGuard = ReuseGuard::try_from_random(backend).unwrap(); + let reuse_guard: ReuseGuard = ReuseGuard::try_from_random(backend).await.unwrap(); // Prepare the nonce by xoring with the reuse guard. let prepared_nonce = ratchet_nonce.xor_with_reuse_guard(&reuse_guard); diff --git a/openmls/src/group/tests/test_framing_validation.rs b/openmls/src/group/tests/test_framing_validation.rs index 556c5c5f22..dbf9a17aa7 100644 --- a/openmls/src/group/tests/test_framing_validation.rs +++ b/openmls/src/group/tests/test_framing_validation.rs @@ -405,6 +405,7 @@ async fn test_valsem006(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr let message = alice_group .create_message(backend, &_alice_credential.signer, &[1, 2, 3]) + .await .expect("An unexpected error occurred."); let serialized_message = message diff --git a/openmls/src/group/tests/test_gce_proposals.rs b/openmls/src/group/tests/test_gce_proposals.rs index 60685767b1..b76f503487 100644 --- a/openmls/src/group/tests/test_gce_proposals.rs +++ b/openmls/src/group/tests/test_gce_proposals.rs @@ -97,6 +97,7 @@ async fn gce_fails_when_it_contains_unsupported_extensions( &[], )); let e = alice_group.propose_extensions(backend, &alice_signer, Extensions::single(required_key_id.clone())) + .await .expect_err("Alice was able to create a gce proposal with a required extensions she doesn't support."); assert_eq!( e, @@ -108,6 +109,7 @@ async fn gce_fails_when_it_contains_unsupported_extensions( // This should fail because Alice doesn't support it. let e = bob_group .propose_extensions(backend, &bob_signer, Extensions::single(required_key_id)) + .await .expect_err("Bob was able to create a gce proposal for an extension not supported by all other parties."); assert_eq!( e, @@ -153,6 +155,7 @@ async fn gce_proposal_should_overwrite_previous( .unwrap(); alice_group .propose_extensions(backend, &alice_signer, new_extensions.clone()) + .await .unwrap(); alice_group .commit_to_pending_proposals(backend, &alice_signer) @@ -186,6 +189,7 @@ async fn gce_proposal_can_roundtrip( ))); let (gce_proposal, _) = alice_group .propose_extensions(backend, &alice_signer, new_extensions.clone()) + .await .unwrap(); let processed_message = bob_group .process_message(backend, MlsMessageIn::from(gce_proposal)) @@ -238,10 +242,12 @@ async fn creating_commit_with_more_than_one_gce_proposal_should_fail( let application_id = Extension::ApplicationId(ApplicationIdExtension::new(b"mls_test")); alice_group .propose_extensions(backend, &alice_signer, Extensions::single(application_id)) + .await .unwrap(); let external_senders = Extension::ExternalSenders(ExternalSendersExtension::default()); alice_group .propose_extensions(backend, &alice_signer, Extensions::single(external_senders)) + .await .unwrap(); assert_eq!(alice_group.pending_proposals().count(), 2); let commit = alice_group @@ -279,6 +285,7 @@ async fn validating_commit_with_more_than_one_gce_proposal_should_fail( let application_id = Extension::ApplicationId(ApplicationIdExtension::new(b"test_mls")); let (first_gce_proposal, _) = alice_group .propose_extensions(backend, &alice_signer, Extensions::single(application_id)) + .await .unwrap(); let processed_message = bob_group .process_message(backend, MlsMessageIn::from(first_gce_proposal)) @@ -360,6 +367,7 @@ async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_add_prop &alice_signer, Extensions::single(new_required_capabilities), ) + .await .unwrap(); // Charlie does not have ExternalSenders in its extensions, hence it should fail to be added to the group @@ -449,6 +457,7 @@ async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_external &alice_signer, Extensions::single(new_required_capabilities), ) + .await .unwrap(); // Charlie does not have ExternalSenders in its extensions, hence it should fail to be added to the group @@ -544,11 +553,13 @@ async fn gce_proposal_must_be_applied_first_but_ignored_for_remove_proposals( RequiredCapabilitiesExtension::new(&[], &[ProposalType::AppAck], &DEFAULT_CREDENTIAL_TYPES), ); - let extension_proposal = alice_group.propose_extensions( - backend, - &alice_signer, - Extensions::single(new_required_capabilities.clone()), - ); + let extension_proposal = alice_group + .propose_extensions( + backend, + &alice_signer, + Extensions::single(new_required_capabilities.clone()), + ) + .await; // because group contains Charlie which is incompatible with new extensions assert!(extension_proposal.is_err()); alice_group.clear_pending_proposals(); @@ -560,6 +571,7 @@ async fn gce_proposal_must_be_applied_first_but_ignored_for_remove_proposals( .unwrap(); let (charlie_remove_proposal, _) = alice_group .propose_remove_member(backend, &alice_signer, charlie_index) + .await .unwrap(); // Bob is able to process remove proposal @@ -574,6 +586,7 @@ async fn gce_proposal_must_be_applied_first_but_ignored_for_remove_proposals( &alice_signer, Extensions::single(new_required_capabilities), ) + .await .unwrap(); assert_eq!(alice_group.pending_proposals().count(), 2); @@ -673,6 +686,7 @@ async fn gce_proposal_must_be_applied_first_but_ignored_for_external_remove_prop &alice_signer, Extensions::single(new_required_capabilities), ) + .await .unwrap(); // Once validating proposals, it should not fail as even though Charlie does not support the new // required extensions, he is going to be removed from the group diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests/test_group.rs index 43515b228f..a013944621 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests/test_group.rs @@ -49,7 +49,7 @@ async fn create_commit_optional_path( // Alice creates a group let mut group_alice = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential_with_keys.credential_with_key, ) @@ -253,7 +253,7 @@ async fn basic_group_setup(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypt // Alice creates a group let group_alice = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential_with_keys.credential_with_key, ) @@ -335,7 +335,7 @@ async fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto // === Alice creates a group === let mut group_alice = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), alice_credential_with_keys.credential_with_key.clone(), ) @@ -417,6 +417,7 @@ async fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto backend, &alice_credential_with_keys.signer, ) + .await .expect("An unexpected error occurred.") .into(); @@ -775,6 +776,7 @@ async fn group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCrypto backend, &charlie_credential_with_keys.signer, ) + .await .expect("An unexpected error occurred.") .into(); diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests/test_past_secrets.rs index 7d28cb37b9..698ab7b45c 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests/test_past_secrets.rs @@ -96,6 +96,7 @@ async fn test_past_secrets_in_group( for _ in 0..max_epochs { let application_message = alice_group .create_message(backend, &alice_credential_with_keys.signer, &[1, 2, 3]) + .await .expect("An unexpected error occurred."); application_messages.push(application_message.into_protocol_message().unwrap()); diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index 1802ad8ca4..b996230b81 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -596,28 +596,28 @@ async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP KeyUniqueness::PositiveSameKeyWithRemove, ] { // 0. Initialize Alice and Bob - let new_kp = || { + let new_kp = || async { openmls_basic_credential::SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap() }; - let shared_signature_keypair = new_kp(); + let shared_signature_keypair = new_kp().await; let [alice_credential_with_key, bob_credential_with_key, target_credential_with_key] = match alice_and_bob_share_keys { KeyUniqueness::NegativeSameKey => [ ("Alice", shared_signature_keypair.clone()), - ("Bob", new_kp()), + ("Bob", new_kp().await), ("Charlie", shared_signature_keypair.clone()), ], KeyUniqueness::PositiveDifferentKey => [ - ("Alice", new_kp()), - ("Bob", new_kp()), - ("Charlie", new_kp()), + ("Alice", new_kp().await), + ("Bob", new_kp().await), + ("Charlie", new_kp().await), ], KeyUniqueness::PositiveSameKeyWithRemove => [ - ("Alice", new_kp()), + ("Alice", new_kp().await), ("Bob", shared_signature_keypair.clone()), ("Charlie", shared_signature_keypair.clone()), ], @@ -707,6 +707,7 @@ async fn test_valsem101b(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP .unwrap(); alice_group .propose_remove_member(backend, &alice_credential_with_key.signer, bob_index) + .await .unwrap(); alice_group .add_members(backend, &alice_credential_with_key.signer, vec![target_key_package.into()]) @@ -1461,6 +1462,7 @@ async fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr &alice_credential_with_key_and_signer.signer, bob_leaf_index, ) + .await .unwrap(); // While this shouldn't fail, it should produce a valid commit, i.e. one @@ -1575,6 +1577,7 @@ async fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr &alice_credential_with_key_and_signer.signer, fake_leaf_index, ) + .await .expect_err("Successfully created remove proposal for leaf not in the tree"); let _ = alice_group .commit_to_pending_proposals(backend, &alice_credential_with_key_and_signer.signer) @@ -1590,6 +1593,7 @@ async fn test_valsem108(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr &alice_credential_with_key_and_signer.signer, fake_leaf_index, ) + .await .expect_err("Successfully created remove proposal for unknown member"); assert_eq!(err, ProposeRemoveMemberError::UnknownMember); @@ -2172,6 +2176,7 @@ async fn test_valsem401_valsem402(ciphersuite: Ciphersuite, backend: &impl OpenM &alice_credential_with_key_and_signer.signer, psk_id, ) + .await .unwrap(); proposals.push(psk_proposal); diff --git a/openmls/src/group/tests/test_remove_operation.rs b/openmls/src/group/tests/test_remove_operation.rs index 12e86c9f54..500c45ccb6 100644 --- a/openmls/src/group/tests/test_remove_operation.rs +++ b/openmls/src/group/tests/test_remove_operation.rs @@ -142,6 +142,7 @@ async fn test_remove_operation_variants( // Bob leaves the group let message = bob_group .leave_group(&bob_backend, &bob_credential_with_key_and_signer.signer) + .await .expect("Could not leave group."); // Alice & Charlie store the pending proposal diff --git a/openmls/src/group/tests/utils.rs b/openmls/src/group/tests/utils.rs index 1eed19a6a7..7981a6403f 100644 --- a/openmls/src/group/tests/utils.rs +++ b/openmls/src/group/tests/utils.rs @@ -348,11 +348,9 @@ pub(crate) async fn generate_credential_with_key( ) -> CredentialWithKeyAndSigner { let (credential, signer) = { let credential = Credential::new_basic(identity); - let signature_keys = SignatureKeyPair::new( - signature_scheme, - &mut *backend.rand().borrow_rand().unwrap(), - ) - .unwrap(); + let signature_keys = + SignatureKeyPair::new(signature_scheme, &mut *backend.rand().borrow_rand().await) + .unwrap(); signature_keys.store(backend.key_store()).await.unwrap(); (credential, signature_keys) diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 87c4fe0716..86bd4e4f42 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -39,7 +39,7 @@ //! //! let credential = Credential::new_basic("identity".into()); //! let signer = -//! SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap()) +//! SignatureKeyPair::new(ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().await) //! .expect("Error generating a signature key pair."); //! let credential_with_key = CredentialWithKey { //! credential, @@ -198,7 +198,7 @@ impl KeyPackage { #[allow(clippy::too_many_arguments)] /// Create a new key package for the given `ciphersuite` and `identity`. - pub(crate) fn create( + pub(crate) async fn create( config: CryptoConfig, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -214,6 +214,7 @@ impl KeyPackage { // Create a new HPKE key pair let ikm = Secret::random(config.ciphersuite, backend, config.version) + .await .map_err(LibraryError::unexpected_crypto_error)?; let init_key = backend .crypto() @@ -229,7 +230,8 @@ impl KeyPackage { leaf_node_capabilities, leaf_node_extensions, init_key.public, - )?; + ) + .await?; Ok(KeyPackageCreationResult { key_package, @@ -248,7 +250,7 @@ impl KeyPackage { /// /// The caller is responsible for storing the new values. #[allow(clippy::too_many_arguments)] - fn new_from_keys( + async fn new_from_keys( config: CryptoConfig, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -272,7 +274,7 @@ impl KeyPackage { }; let (leaf_node, encryption_key_pair) = - LeafNode::new(backend, signer, new_leaf_node_params)?; + LeafNode::new(backend, signer, new_leaf_node_params).await?; let key_package_tbs = KeyPackageTbs { protocol_version: config.version, @@ -389,7 +391,8 @@ impl KeyPackage { leaf_node_capabilities, leaf_node_extensions, init_key, - )?; + ) + .await?; // Store the key package in the key store with the hash reference as id // for retrieval when parsing welcome messages. @@ -426,7 +429,9 @@ impl KeyPackage { encryption_key: EncryptionKey, ) -> Result> { // Create a new HPKE init key pair - let ikm = Secret::random(config.ciphersuite, backend, config.version).unwrap(); + let ikm = Secret::random(config.ciphersuite, backend, config.version) + .await + .unwrap(); let init_key = backend .crypto() .derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice()) @@ -575,7 +580,7 @@ impl KeyPackageBuilder { self } - pub(crate) fn build_without_key_storage( + pub(crate) async fn build_without_key_storage( self, config: CryptoConfig, backend: &impl OpenMlsCryptoProvider, @@ -592,6 +597,7 @@ impl KeyPackageBuilder { self.leaf_node_capabilities.unwrap_or_default(), self.leaf_node_extensions.unwrap_or_default(), ) + .await } /// Finalize and build the key package. @@ -615,7 +621,8 @@ impl KeyPackageBuilder { self.key_package_extensions.unwrap_or_default(), self.leaf_node_capabilities.unwrap_or_default(), self.leaf_node_extensions.unwrap_or_default(), - )?; + ) + .await?; // Store the key package in the key store with the hash reference as id // for retrieval when parsing welcome messages. diff --git a/openmls/src/key_packages/test_key_packages.rs b/openmls/src/key_packages/test_key_packages.rs index db0b0fe078..e4d2b3d63a 100644 --- a/openmls/src/key_packages/test_key_packages.rs +++ b/openmls/src/key_packages/test_key_packages.rs @@ -16,7 +16,7 @@ pub(crate) async fn key_package( let credential = Credential::new_basic(b"Sasha".to_vec()); let signer = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); @@ -74,7 +74,7 @@ async fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenM let credential = Credential::new_basic(b"Sasha".to_vec()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); diff --git a/openmls/src/lib.rs b/openmls/src/lib.rs index 562ba5c5e6..345fccb46d 100644 --- a/openmls/src/lib.rs +++ b/openmls/src/lib.rs @@ -24,7 +24,7 @@ //! ) -> (CredentialWithKey, SignatureKeyPair) { //! let credential = Credential::new_basic(identity); //! let signature_keys = -//! SignatureKeyPair::new(signature_algorithm, &mut *backend.rand().borrow_rand().unwrap()) +//! SignatureKeyPair::new(signature_algorithm, &mut *backend.rand().borrow_rand().await) //! .expect("Error generating a signature key pair."); //! //! // Store the signature key into the key store so OpenMLS has access diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index a75c012fbe..5f9a921f25 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -331,19 +331,21 @@ impl PathSecret { /// Encrypt the path secret under the given `HpkePublicKey` using the given /// `group_context`. - pub fn encrypt( + pub async fn encrypt( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, public_key: &EncryptionKey, group_context: &[u8], ) -> Result { - public_key.encrypt( - backend, - ciphersuite, - group_context, - self.path_secret.as_slice(), - ) + public_key + .encrypt( + backend, + ciphersuite, + group_context, + self.path_secret.as_slice(), + ) + .await } /// Consume the `PathSecret`, returning the internal `Secret` value. @@ -469,7 +471,7 @@ impl GroupSecrets { #[cfg(test)] impl GroupSecrets { #[allow(dead_code)] - pub fn random_encoded( + pub async fn random_encoded( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, version: ProtocolVersion, @@ -483,16 +485,19 @@ impl GroupSecrets { backend .rand() .random_vec(ciphersuite.hash_length()) + .await .expect("Not enough randomness."), )), ) + .await .expect("An unexpected error occurred."); let psks = vec![psk_id]; GroupSecrets::new_encoded( - &JoinerSecret::random(ciphersuite, backend, version), + &JoinerSecret::random(ciphersuite, backend, version).await, Some(&PathSecret { path_secret: Secret::random(ciphersuite, backend, version) + .await .expect("Not enough randomness."), }), &psks, diff --git a/openmls/src/messages/tests/test_codec.rs b/openmls/src/messages/tests/test_codec.rs index a8c4f1f238..ed5077394a 100644 --- a/openmls/src/messages/tests/test_codec.rs +++ b/openmls/src/messages/tests/test_codec.rs @@ -32,7 +32,7 @@ async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider let psk = PreSharedKeyId { psk: Psk::Resumption(ResumptionPsk::new( ResumptionPskUsage::Application, - GroupId::random(backend), + GroupId::random(backend).await, 1234.into(), )), psk_nonce: vec![1, 2, 3].into(), @@ -49,7 +49,7 @@ async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider let psk = PreSharedKeyId { psk: Psk::Resumption(ResumptionPsk::new( ResumptionPskUsage::Reinit, - GroupId::random(backend), + GroupId::random(backend).await, 1234.into(), )), psk_nonce: vec![1, 2, 3].into(), @@ -66,7 +66,7 @@ async fn test_pre_shared_key_proposal_codec(backend: &impl OpenMlsCryptoProvider let psk = PreSharedKeyId { psk: Psk::Resumption(ResumptionPsk::new( ResumptionPskUsage::Branch, - GroupId::random(backend), + GroupId::random(backend).await, 1234.into(), )), psk_nonce: vec![1, 2, 3].into(), @@ -88,7 +88,7 @@ async fn test_reinit_proposal_codec( backend: &impl OpenMlsCryptoProvider, ) { let orig = ReInitProposal { - group_id: GroupId::random(backend), + group_id: GroupId::random(backend).await, version: ProtocolVersion::default(), ciphersuite, extensions: Extensions::empty(), diff --git a/openmls/src/messages/tests/test_welcome.rs b/openmls/src/messages/tests/test_welcome.rs index f8ded54ae2..d3df3d2930 100644 --- a/openmls/src/messages/tests/test_welcome.rs +++ b/openmls/src/messages/tests/test_welcome.rs @@ -54,7 +54,7 @@ async fn test_welcome_context_mismatch( _ => Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, }; - let group_id = GroupId::random(backend); + let group_id = GroupId::random(backend).await; let mls_group_config = MlsGroupConfigBuilder::new() .crypto_config(CryptoConfig::with_default_version(ciphersuite)) .build(); @@ -215,15 +215,15 @@ async fn test_welcome_context_mismatch( #[apply(ciphersuites_and_backends)] #[wasm_bindgen_test::wasm_bindgen_test] async fn test_welcome_msg(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - test_welcome_message(ciphersuite, backend); + test_welcome_message(ciphersuite, backend).await; } -fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { +async fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { // We use this dummy group info in all test cases. let group_info_tbs = { let group_context = GroupContext::new( ciphersuite, - GroupId::random(backend), + GroupId::random(backend).await, 123, vec![1, 2, 3, 4, 5, 6, 7, 8, 9], vec![1, 1, 1], @@ -243,7 +243,7 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // We need a signer let signer = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); @@ -252,8 +252,8 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .expect("Error signing GroupInfo"); // Generate key and nonce for the symmetric cipher. - let welcome_key = AeadKey::random(ciphersuite, backend.rand()); - let welcome_nonce = AeadNonce::random(backend); + let welcome_key = AeadKey::random(ciphersuite, backend.rand()).await; + let welcome_nonce = AeadNonce::random(backend).await; // Generate receiver key pair. let receiver_key_pair = backend @@ -261,6 +261,7 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .derive_hpke_keypair( ciphersuite.hpke_config(), Secret::random(ciphersuite, backend, None) + .await .expect("Not enough randomness.") .as_slice(), ) @@ -278,6 +279,7 @@ fn test_welcome_message(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr ciphersuite, backend.crypto(), ) + .await .unwrap(), }]; diff --git a/openmls/src/schedule/kat_key_schedule.rs b/openmls/src/schedule/kat_key_schedule.rs index 2498000fa8..f4bae07c52 100644 --- a/openmls/src/schedule/kat_key_schedule.rs +++ b/openmls/src/schedule/kat_key_schedule.rs @@ -60,7 +60,7 @@ pub struct KeyScheduleTestVector { // Ignore clippy warning since this just used for testing #[allow(clippy::type_complexity)] -fn generate( +async fn generate( ciphersuite: Ciphersuite, init_secret: &InitSecret, group_id: &[u8], @@ -80,18 +80,24 @@ fn generate( let tree_hash = crypto .rand() .random_vec(ciphersuite.hash_length()) + .await .expect("An unexpected error occurred."); - let commit_secret = CommitSecret::random(ciphersuite, &crypto); + let commit_secret = CommitSecret::random(ciphersuite, &crypto).await; let confirmed_transcript_hash = crypto .rand() .random_vec(ciphersuite.hash_length()) + .await .expect("An unexpected error occurred."); // PSK secret can sometimes be the all zero vector - let a: [u8; 1] = crypto.rand().random_array().unwrap(); + let a: [u8; 1] = crypto.rand().random_array().await.unwrap(); let psk_secret = if a[0] > 127 { - PskSecret::from(Secret::random(ciphersuite, &crypto, ProtocolVersion::Mls10).unwrap()) + PskSecret::from( + Secret::random(ciphersuite, &crypto, ProtocolVersion::Mls10) + .await + .unwrap(), + ) } else { PskSecret::from(Secret::zero(ciphersuite, ProtocolVersion::Mls10)) }; @@ -150,7 +156,7 @@ fn generate( } #[cfg(any(feature = "test-utils", test))] -pub fn generate_test_vector( +pub async fn generate_test_vector( n_epochs: u64, ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, @@ -159,11 +165,13 @@ pub fn generate_test_vector( // Set up setting. let mut init_secret = InitSecret::random(ciphersuite, backend, ProtocolVersion::default()) + .await .expect("Not enough randomness."); let initial_init_secret = init_secret.clone(); let group_id = backend .rand() .random_vec(16) + .await .expect("An unexpected error occurred."); let mut epochs = Vec::new(); @@ -181,7 +189,7 @@ pub fn generate_test_vector( tree_hash, group_context, external_key_pair, - ) = generate(ciphersuite, &init_secret, &group_id, epoch); + ) = generate(ciphersuite, &init_secret, &group_id, epoch).await; // exporter let exporter_label = "exporter label"; diff --git a/openmls/src/schedule/message_secrets.rs b/openmls/src/schedule/message_secrets.rs index 9f19197739..6f6f175733 100644 --- a/openmls/src/schedule/message_secrets.rs +++ b/openmls/src/schedule/message_secrets.rs @@ -91,7 +91,7 @@ impl MessageSecrets { } #[cfg(test)] - pub(crate) fn random( + pub(crate) async fn random( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, own_index: LeafNodeIndex, @@ -99,15 +99,16 @@ impl MessageSecrets { use openmls_traits::random::OpenMlsRand; Self { - sender_data_secret: SenderDataSecret::random(ciphersuite, backend), - membership_key: MembershipKey::random(ciphersuite, backend), - confirmation_key: ConfirmationKey::random(ciphersuite, backend), + sender_data_secret: SenderDataSecret::random(ciphersuite, backend).await, + membership_key: MembershipKey::random(ciphersuite, backend).await, + confirmation_key: ConfirmationKey::random(ciphersuite, backend).await, serialized_context: backend .rand() .random_vec(10) + .await .expect("Not enough randomness."), secret_tree: SecretTree::new( - EncryptionSecret::random(ciphersuite, backend), + EncryptionSecret::random(ciphersuite, backend).await, TreeSize::new(10), own_index, ), diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index d1a0026434..8fca7b620d 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -236,9 +236,10 @@ impl CommitSecret { } #[cfg(any(feature = "test-utils", test))] - pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { + pub(crate) async fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { Self { secret: Secret::random(ciphersuite, rng, None /* MLS version */) + .await .expect("Not enough randomness."), } } @@ -284,31 +285,34 @@ impl InitSecret { } /// Sample a fresh, random `InitSecret` for the creation of a new group. - pub(crate) fn random( + pub(crate) async fn random( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, version: ProtocolVersion, ) -> Result { Ok(InitSecret { - secret: Secret::random(ciphersuite, backend, version)?, + secret: Secret::random(ciphersuite, backend, version).await?, }) } /// Create an `InitSecret` and the corresponding `kem_output` from a group info. - pub(crate) fn from_group_context( + pub(crate) async fn from_group_context( backend: &impl OpenMlsCryptoProvider, group_context: &GroupContext, external_pub: &[u8], ) -> Result<(Self, Vec), KeyScheduleError> { let ciphersuite = group_context.ciphersuite(); let version = group_context.protocol_version(); - let (kem_output, raw_init_secret) = backend.crypto().hpke_setup_sender_and_export( - ciphersuite.hpke_config(), - external_pub, - &[], - hpke_info_from_version(version).as_bytes(), - ciphersuite.hash_length(), - )?; + let (kem_output, raw_init_secret) = backend + .crypto() + .hpke_setup_sender_and_export( + ciphersuite.hpke_config(), + external_pub, + &[], + hpke_info_from_version(version).as_bytes(), + ciphersuite.hash_length(), + ) + .await?; Ok(( InitSecret { secret: Secret::from_slice(&raw_init_secret, version, ciphersuite), @@ -395,13 +399,15 @@ impl JoinerSecret { } #[cfg(test)] - pub(crate) fn random( + pub(crate) async fn random( ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider, version: ProtocolVersion, ) -> Self { Self { - secret: Secret::random(ciphersuite, backend, version).expect("Not enough randomness."), + secret: Secret::random(ciphersuite, backend, version) + .await + .expect("Not enough randomness."), } } } @@ -674,9 +680,10 @@ impl EncryptionSecret { /// Create a random `EncryptionSecret`. For testing purposes only. #[cfg(test)] - pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { + pub(crate) async fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { EncryptionSecret { secret: Secret::random(ciphersuite, rng, None /* MLS version */) + .await .expect("Not enough randomness."), } } @@ -831,9 +838,10 @@ impl ConfirmationKey { #[cfg(any(feature = "test-utils", test))] impl ConfirmationKey { - pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { + pub(crate) async fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { Self { secret: Secret::random(ciphersuite, rng, None /* MLS version */) + .await .expect("Not enough randomness."), } } @@ -895,9 +903,10 @@ impl MembershipKey { } #[cfg(any(feature = "test-utils", test))] - pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { + pub(crate) async fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { Self { secret: Secret::random(ciphersuite, rng, None /* MLS version */) + .await .expect("Not enough randomness."), } } @@ -974,9 +983,10 @@ impl SenderDataSecret { } #[cfg(any(feature = "test-utils", test))] - pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { + pub(crate) async fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsCryptoProvider) -> Self { Self { secret: Secret::random(ciphersuite, rng, None /* MLS version */) + .await .expect("Not enough randomness."), } } diff --git a/openmls/src/schedule/psk.rs b/openmls/src/schedule/psk.rs index 2c266b9675..1870c1f137 100644 --- a/openmls/src/schedule/psk.rs +++ b/openmls/src/schedule/psk.rs @@ -235,13 +235,14 @@ pub struct PreSharedKeyId { impl PreSharedKeyId { /// Construct a `PreSharedKeyID` with a random nonce. - pub fn new( + pub async fn new( ciphersuite: Ciphersuite, rand: &impl OpenMlsRand, psk: Psk, ) -> Result { let psk_nonce = rand .random_vec(ciphersuite.hash_length()) + .await .map_err(|_| CryptoError::InsufficientRandomness)? .into(); diff --git a/openmls/src/schedule/unit_tests.rs b/openmls/src/schedule/unit_tests.rs index 6fd2dbb38a..fdd671fcc5 100644 --- a/openmls/src/schedule/unit_tests.rs +++ b/openmls/src/schedule/unit_tests.rs @@ -10,6 +10,7 @@ use crate::{ test_utils::*, versions::ProtocolVersion, }; +use futures::{stream, StreamExt}; wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); @@ -19,28 +20,37 @@ async fn test_psks(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvide // Create a new PSK secret from multiple PSKs. let prng = backend.rand(); - let psk_ids = (0..33) - .map(|_| { - let id = prng.random_vec(12).expect("An unexpected error occurred."); + let psk_ids = stream::iter(0..33) + .then(|_| async { + let id = prng + .random_vec(12) + .await + .expect("An unexpected error occurred."); PreSharedKeyId::new( ciphersuite, backend.rand(), Psk::External(ExternalPsk::new(id)), ) + .await .expect("An unexpected error occurred.") }) - .collect::>(); - - for (secret, psk_id) in (0..33) - .map(|_| { + .collect::>() + .await; + let secrets_with_psk_ids = stream::iter(0..33) + .then(|_| async { Secret::from_slice( - &prng.random_vec(55).expect("An unexpected error occurred."), + &prng + .random_vec(55) + .await + .expect("An unexpected error occurred."), ProtocolVersion::Mls10, ciphersuite, ) }) - .zip(psk_ids.clone()) - { + .zip(stream::iter(psk_ids.clone())) + .collect::>() + .await; + for (secret, psk_id) in secrets_with_psk_ids.iter() { psk_id .write_to_key_store(backend, ciphersuite, secret.as_slice()) .await diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index b8d15fe03e..8afb5d9b86 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -98,7 +98,7 @@ pub(crate) async fn generate_group_candidate( let signature_keypair = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); @@ -167,6 +167,7 @@ pub(crate) async fn generate_group_candidate( &credential_with_key_and_signer.signer, credential_with_key_and_signer.credential_with_key.clone(), ) + .await .unwrap(); let init_keypair = HpkeKeyPair { diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index 18082cc46f..86e03125c9 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -328,6 +328,7 @@ impl Client { for target in targets { let message = group .propose_remove_member(&self.crypto, &signer, *target) + .await .map(|(out, _)| out)?; messages.push(message); } diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index fafe0bc304..406ddbbe60 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -151,7 +151,7 @@ impl MlsGroupTestSetup { let credential = Credential::new_basic(identity.clone()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *crypto.rand().borrow_rand().unwrap(), + &mut *crypto.rand().borrow_rand().await, ) .unwrap(); signature_keys.store(crypto.key_store()).await.unwrap(); @@ -381,6 +381,7 @@ impl MlsGroupTestSetup { .unwrap(); let message = group_state .create_message(&m.crypto, &signer, "Hello World!".as_bytes()) + .await .expect("Error composing message while checking group states."); messages.push((m_id.to_vec(), message)); } diff --git a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs index 20bfbafe80..c1f85f6c47 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs @@ -149,7 +149,7 @@ async fn generate_credential( let credential = Credential::new_basic(identity); let signature_keys = SignatureKeyPair::new( signature_algorithm, - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); signature_keys.store(backend.key_store()).await.unwrap(); @@ -174,7 +174,7 @@ async fn group( generate_credential("Kreator".into(), ciphersuite.signature_algorithm(), backend).await; let group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), credential_with_key.clone(), ) @@ -214,7 +214,7 @@ async fn receiver_group( // XXX: we could be more creative in generating these messages. #[cfg(any(feature = "test-utils", test))] -fn build_handshake_messages( +async fn build_handshake_messages( sender_index: LeafNodeIndex, group: &mut CoreGroup, signer: &impl Signer, @@ -229,6 +229,7 @@ fn build_handshake_messages( let framing_parameters = FramingParameters::new(&[1, 2, 3, 4], WireFormat::PrivateMessage); let membership_key = MembershipKey::from_secret( Secret::random(group.ciphersuite(), backend, None /* MLS version */) + .await .expect("Not enough randomness."), ); let content = AuthenticatedContentIn::from( @@ -259,6 +260,7 @@ fn build_handshake_messages( group.message_secrets_test_mut(), 0, ) + .await .expect("Could not create PrivateMessage"); ( plaintext @@ -271,7 +273,7 @@ fn build_handshake_messages( } #[cfg(any(feature = "test-utils", test))] -fn build_application_messages( +async fn build_application_messages( sender_index: LeafNodeIndex, group: &mut CoreGroup, signer: &impl Signer, @@ -285,6 +287,7 @@ fn build_application_messages( group.context_mut().set_epoch(epoch.into()); let membership_key = MembershipKey::from_secret( Secret::random(group.ciphersuite(), backend, None /* MLS version */) + .await .expect("Not enough randomness."), ); let content = AuthenticatedContent::new_application( @@ -309,7 +312,9 @@ fn build_application_messages( backend, group.message_secrets_test_mut(), 0, - ) { + ) + .await + { Ok(c) => c, Err(e) => panic!("Could not create PrivateMessage {e}"), }; @@ -336,14 +341,16 @@ pub async fn generate_test_vector( let encryption_secret_bytes = crypto .rand() .random_vec(ciphersuite.hash_length()) + .await .expect("An unexpected error occurred."); - let sender_data_secret = SenderDataSecret::random(ciphersuite, &crypto); + let sender_data_secret = SenderDataSecret::random(ciphersuite, &crypto).await; let sender_data_secret_bytes = sender_data_secret.as_slice(); // Create sender_data_key/secret let ciphertext = crypto .rand() .random_vec(77) + .await .expect("An unexpected error occurred."); let sender_data_key = sender_data_secret .derive_aead_key(&crypto, &ciphertext) @@ -404,7 +411,7 @@ pub async fn generate_test_vector( let application_key_string = bytes_to_hex(application_secret_key.as_slice()); let application_nonce_string = bytes_to_hex(application_secret_nonce.as_slice()); let (application_plaintext, application_ciphertext) = - build_application_messages(sender_leaf, &mut group, &signer, &crypto); + build_application_messages(sender_leaf, &mut group, &signer, &crypto).await; println!("Sender Group: {group:?}"); application.push(RatchetStep { key: application_key_string, @@ -428,7 +435,7 @@ pub async fn generate_test_vector( let handshake_nonce_string = bytes_to_hex(handshake_secret_nonce.as_slice()); let (handshake_plaintext, handshake_ciphertext) = - build_handshake_messages(sender_leaf, &mut group, &signer, &crypto); + build_handshake_messages(sender_leaf, &mut group, &signer, &crypto).await; handshake.push(RatchetStep { key: handshake_key_string, @@ -626,8 +633,8 @@ pub async fn run_test_vector( // above ratcheted the tree forward. let mut message_secrets = MessageSecrets::new( sender_data_secret.clone(), - MembershipKey::random(ciphersuite, backend), // we don't care about this value - ConfirmationKey::random(ciphersuite, backend), // we don't care about this value + MembershipKey::random(ciphersuite, backend).await, // we don't care about this value + ConfirmationKey::random(ciphersuite, backend).await, // we don't care about this value group.context().tls_serialize_detached().unwrap(), fresh_secret_tree.clone(), ); diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index 9c1b0a02d1..b292d59683 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -130,7 +130,7 @@ async fn generate_credential( let credential = Credential::new_basic(identity); let signature_keys = SignatureKeyPair::new( signature_algorithm, - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); signature_keys.store(backend.key_store()).await.unwrap(); @@ -155,7 +155,7 @@ async fn group( generate_credential("Kreator".into(), ciphersuite.signature_algorithm(), backend).await; let group = CoreGroup::builder( - GroupId::random(backend), + GroupId::random(backend).await, CryptoConfig::with_default_version(ciphersuite), credential_with_key.clone(), ) @@ -236,7 +236,7 @@ pub async fn run_test_vector( let signature_private_key = test.signature_priv.clone(); let random_own_signature_key = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); let random_own_signature_key = random_own_signature_key.public(); @@ -267,7 +267,7 @@ pub async fn run_test_vector( let random_own_signature_key = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); let random_own_signature_key = random_own_signature_key.public(); @@ -292,7 +292,7 @@ pub async fn run_test_vector( let credential = Credential::new_basic("Fake user".into()); let signature_keys = SignatureKeyPair::new( ciphersuite.signature_algorithm(), - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); let bob_key_package_bundle = KeyPackageBundle::new( @@ -478,6 +478,7 @@ pub async fn run_test_vector( .unwrap(); let my_proposal_priv = sender_group .encrypt(proposal_authenticated_content, 0, backend) + .await .unwrap(); let my_proposal_priv_out = MlsMessageOut::from_private_message(my_proposal_priv, group.version()); @@ -643,6 +644,7 @@ pub async fn run_test_vector( })); let my_commit_pub = sender_group .encrypt(commit_authenticated_content, 0, backend) + .await .unwrap(); let my_commit_priv_out = MlsMessageOut::from_private_message(my_commit_pub, group.version()); @@ -736,6 +738,7 @@ pub async fn run_test_vector( let mut sender_group = setup_group(backend, ciphersuite, &test, true).await; let private_message = sender_group .create_application_message(&[], application, 0, backend, &signer) + .await .unwrap(); let my_application_priv_out = MlsMessageOut::from_private_message(private_message, sender_group.version()); @@ -756,13 +759,16 @@ pub async fn run_test_vector( #[apply(backends)] async fn read_test_vectors_mp(backend: &impl OpenMlsCryptoProvider) { - let _ = pretty_env_logger::try_init(); - log::info!("Reading test vectors ..."); + Box::pin(async { + let _ = pretty_env_logger::try_init(); + log::info!("Reading test vectors ..."); - let tests: Vec = read("test_vectors/message-protection.json"); + let tests: Vec = read("test_vectors/message-protection.json"); - for test_vector in tests.into_iter() { - run_test_vector(test_vector, backend).await.unwrap(); - } - log::info!("Finished test vector verification"); + for test_vector in tests.into_iter() { + run_test_vector(test_vector, backend).await.unwrap(); + } + log::info!("Finished test vector verification"); + }) + .await; } diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs index 13d16b9a9c..56d7e897b4 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_secret_tree.rs @@ -17,7 +17,7 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen_test::wasm_bindgen_test] async fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); - let encryption_secret = EncryptionSecret::random(ciphersuite, backend); + let encryption_secret = EncryptionSecret::random(ciphersuite, backend).await; let mut secret_tree = SecretTree::new( encryption_secret, TreeSize::from_leaf_count(3u32), @@ -108,7 +108,7 @@ async fn test_boundaries(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP ), Err(SecretTreeError::IndexOutOfBounds) ); - let encryption_secret = EncryptionSecret::random(ciphersuite, backend); + let encryption_secret = EncryptionSecret::random(ciphersuite, backend).await; let mut largetree = SecretTree::new( encryption_secret, TreeSize::from_leaf_count(100_000u32), @@ -166,7 +166,7 @@ async fn increment_generation(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr const MAX_GENERATIONS: usize = 10; let mut unique_values: HashMap, bool> = HashMap::new(); - let encryption_secret = EncryptionSecret::random(ciphersuite, backend); + let encryption_secret = EncryptionSecret::random(ciphersuite, backend).await; let mut secret_tree = SecretTree::new( encryption_secret, TreeSize::from_leaf_count(SIZE as u32), @@ -239,6 +239,7 @@ async fn secret_tree(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvi &backend .rand() .random_vec(ciphersuite.hash_length()) + .await .expect("An unexpected error occurred.")[..], ProtocolVersion::default(), ciphersuite, diff --git a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs index ec8cc5ebf0..b87af8c6ea 100644 --- a/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs +++ b/openmls/src/tree/tests_and_kats/unit_tests/test_sender_ratchet.rs @@ -1,3 +1,4 @@ +use async_std::task::block_on; use openmls_rust_crypto::OpenMlsRustCrypto; use crate::{ @@ -13,6 +14,7 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); async fn test_max_forward_distance(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) + .await .expect("Not enough randomness."); let mut ratchet1 = DecryptionRatchet::new(secret.clone()); let mut ratchet2 = DecryptionRatchet::new(secret); @@ -55,6 +57,7 @@ async fn test_out_of_order_generations( ) { let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) + .await .expect("Not enough randomness."); let mut ratchet1 = DecryptionRatchet::new(secret); @@ -96,6 +99,7 @@ async fn test_forward_secrecy(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // any keys. Thus, we can only test FS on Decryption Ratchets. let configuration = &SenderRatchetConfiguration::default(); let secret = Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) + .await .expect("Not enough randomness."); let mut ratchet = DecryptionRatchet::new(secret); @@ -141,8 +145,9 @@ async fn test_forward_secrecy(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr fn sender_ratchet_generation_overflow() { let backend = OpenMlsRustCrypto::default(); let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; - let secret = Secret::random(ciphersuite, &backend, ProtocolVersion::Mls10) - .expect("Not enough randomness."); + let secret = + block_on(async { Secret::random(ciphersuite, &backend, ProtocolVersion::Mls10).await }) + .expect("Not enough randomness."); let mut ratchet = RatchetSecret::initial_ratchet_secret(secret); ratchet.set_generation(u32::MAX - 1); let _ = ratchet diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index 013f04236b..3316bf1315 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -263,7 +263,7 @@ impl<'a> TreeSyncDiff<'a> { /// Derive a new direct path for the leaf with the given index. /// /// Returns an error if the leaf is not in the tree - fn derive_path( + async fn derive_path( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -271,6 +271,7 @@ impl<'a> TreeSyncDiff<'a> { ) -> Result { let path_secret = PathSecret::from( Secret::random(ciphersuite, backend, None) + .await .map_err(LibraryError::unexpected_crypto_error)?, ); @@ -287,7 +288,7 @@ impl<'a> TreeSyncDiff<'a> { /// derivation, as well as the newly derived [`EncryptionKeyPair`]s. /// /// Returns an error if the target leaf is not in the tree. - pub(crate) fn apply_own_update_path( + pub(crate) async fn apply_own_update_path( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -301,7 +302,7 @@ impl<'a> TreeSyncDiff<'a> { ); let (path, update_path_nodes, keypairs, commit_secret) = - self.derive_path(backend, ciphersuite, leaf_index)?; + self.derive_path(backend, ciphersuite, leaf_index).await?; let parent_hash = self.process_update_path(backend, ciphersuite, leaf_index, path)?; diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 18a983c5b3..11f78c881c 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -378,7 +378,7 @@ impl TreeSync { /// /// Returns the resulting [`TreeSync`] instance, as well as the /// corresponding [`CommitSecret`]. - pub(crate) fn new( + pub(crate) async fn new( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, config: CryptoConfig, @@ -396,10 +396,12 @@ impl TreeSync { extensions, tree_info_tbs: TreeInfoTbs::KeyPackage, }; - let (leaf, encryption_key_pair) = LeafNode::new(backend, signer, new_leaf_node_params)?; + let (leaf, encryption_key_pair) = + LeafNode::new(backend, signer, new_leaf_node_params).await?; let node = Node::LeafNode(leaf); let path_secret: PathSecret = Secret::random(config.ciphersuite, backend, None) + .await .map_err(LibraryError::unexpected_crypto_error)? .into(); let commit_secret: CommitSecret = path_secret @@ -802,7 +804,7 @@ mod test { ciphersuite, backend.crypto(), test, - &GroupId::random(backend), + &GroupId::random(backend).await, ) .is_ok(); assert_eq!(got, expected); diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index b9febdd3a9..4b134aa9b1 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -38,7 +38,7 @@ impl EncryptionKey { } /// Encrypt to this HPKE public key. - pub(crate) fn encrypt( + pub(crate) async fn encrypt( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -53,6 +53,7 @@ impl EncryptionKey { ciphersuite, backend.crypto(), ) + .await .map_err(|_| LibraryError::custom("Encryption failed. A serialization issue really")) } } @@ -187,11 +188,12 @@ impl EncryptionKeyPair { &self.private_key } - pub(crate) fn random( + pub(crate) async fn random( backend: &impl OpenMlsCryptoProvider, config: CryptoConfig, ) -> Result { let ikm = Secret::random(config.ciphersuite, backend, config.version) + .await .map_err(LibraryError::unexpected_crypto_error)?; let kp: Self = backend .crypto() diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index ebcf1f31fc..e93d4cf218 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -91,7 +91,7 @@ impl LeafNode { /// This function generates a fresh HPKE key pair for the leaf node and /// returns the HPKE key pair along with the new leaf node. /// The caller is responsible for storing the private key. - pub(crate) fn new( + pub(crate) async fn new( backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, new_leaf_node_params: NewLeafNodeParams, @@ -106,7 +106,7 @@ impl LeafNode { } = new_leaf_node_params; // Create a new encryption key pair. - let encryption_key_pair = EncryptionKeyPair::random(backend, config)?; + let encryption_key_pair = EncryptionKeyPair::random(backend, config).await?; let leaf_node = Self::new_with_key( encryption_key_pair.public_key().clone(), @@ -232,7 +232,8 @@ impl LeafNode { tree_info_tbs, }; - let (leaf_node, encryption_key_pair) = Self::new(backend, signer, new_leaf_node_params)?; + let (leaf_node, encryption_key_pair) = + Self::new(backend, signer, new_leaf_node_params).await?; // Store the encryption key pair in the key store. encryption_key_pair @@ -291,7 +292,7 @@ impl LeafNode { /// /// This signs the new leaf node as well. #[allow(clippy::too_many_arguments)] - pub fn rekey( + pub async fn rekey( &mut self, group_id: &GroupId, leaf_index: LeafNodeIndex, @@ -326,7 +327,8 @@ impl LeafNode { ciphersuite, version: protocol_version, }, - )?; + ) + .await?; if let Some(mut leaf_node) = leaf_node { leaf_node.payload.encryption_key = keypair.public_key().clone(); diff --git a/openmls/src/treesync/node/parent_node.rs b/openmls/src/treesync/node/parent_node.rs index 0f74af8ed2..acc5fb9894 100644 --- a/openmls/src/treesync/node/parent_node.rs +++ b/openmls/src/treesync/node/parent_node.rs @@ -1,6 +1,7 @@ //! This module contains the [`ParentNode`] struct, its implementation, as well //! as the [`PlainUpdatePathNode`], a helper struct for the creation of //! [`UpdatePathNode`] instances. +use futures::{stream, StreamExt, TryStreamExt}; use openmls_traits::{ types::{Ciphersuite, HpkeCiphertext}, OpenMlsCryptoProvider, @@ -52,24 +53,25 @@ pub(crate) struct PlainUpdatePathNode { impl PlainUpdatePathNode { /// Encrypt this node and return the resulting [`UpdatePathNode`]. - pub(in crate::treesync) fn encrypt( + pub(in crate::treesync) async fn encrypt( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, public_keys: &[EncryptionKey], group_context: &[u8], ) -> Result { - public_keys - .iter() - .map(|pk| { + let encrypted_path_secrets = stream::iter(public_keys) + .then(|pk| async { self.path_secret .encrypt(backend, ciphersuite, pk, group_context) + .await }) - .collect::, LibraryError>>() - .map(|encrypted_path_secrets| UpdatePathNode { - public_key: self.public_key.clone(), - encrypted_path_secrets, - }) + .try_collect::>() + .await?; + Ok(UpdatePathNode { + public_key: self.public_key.clone(), + encrypted_path_secrets, + }) } /// Return a reference to the `path_secret` of this node. diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs b/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs index 704a3ac54d..f2d51fa063 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_tree_operations.rs @@ -57,7 +57,7 @@ async fn run_test_vector( ) -> Result<(), String> { let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; - let group_id = GroupId::random(backend); + let group_id = GroupId::random(backend).await; let nodes = Vec::>::tls_deserialize_exact(test.tree_before).unwrap(); @@ -79,7 +79,9 @@ async fn run_test_vector( let initial_confirmation_tag = ConfirmationTag( Mac::new( backend, - &Secret::random(ciphersuite, backend, ProtocolVersion::Mls10).unwrap(), + &Secret::random(ciphersuite, backend, ProtocolVersion::Mls10) + .await + .unwrap(), &[], ) .unwrap(), diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs index 0698045622..023eb1732b 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs @@ -259,6 +259,7 @@ pub async fn run_test_vector(test: TreeKemTest, backend: &impl OpenMlsCryptoProv group_context.group_id().clone(), LeafNodeIndex::new(path_test.sender), ) + .await .unwrap(); // TODO(#1279): Update GroupContext. @@ -274,6 +275,7 @@ pub async fn run_test_vector(test: TreeKemTest, backend: &impl OpenMlsCryptoProv &HashSet::new(), LeafNodeIndex::new(path_test.sender), ) + .await .unwrap(); ( diff --git a/openmls/src/treesync/tests_and_kats/tests.rs b/openmls/src/treesync/tests_and_kats/tests.rs index 3af61bfb1e..75262328e0 100644 --- a/openmls/src/treesync/tests_and_kats/tests.rs +++ b/openmls/src/treesync/tests_and_kats/tests.rs @@ -174,5 +174,6 @@ async fn that_commit_secret_is_derived_from_end_of_update_path_not_root( &charlie.credential_with_key_and_signer.signer, b"Hello, World!".as_slice(), ) + .await .unwrap(); } diff --git a/openmls/src/treesync/tests_and_kats/tests/test_diff.rs b/openmls/src/treesync/tests_and_kats/tests/test_diff.rs index 1481fb0f12..94e841ac07 100644 --- a/openmls/src/treesync/tests_and_kats/tests/test_diff.rs +++ b/openmls/src/treesync/tests_and_kats/tests/test_diff.rs @@ -37,7 +37,7 @@ async fn test_free_leaf_computation( ]); // Get the encryption key pair from the leaf. - let group_id = GroupId::random(backend); + let group_id = GroupId::random(backend).await; let tree = TreeSync::from_ratchet_tree(backend, ciphersuite, ratchet_tree, &group_id, true, true) .await diff --git a/openmls/src/treesync/treekem.rs b/openmls/src/treesync/treekem.rs index 81af6432b2..5182f45325 100644 --- a/openmls/src/treesync/treekem.rs +++ b/openmls/src/treesync/treekem.rs @@ -4,6 +4,8 @@ //! //! This module contains structs and functions to encrypt and decrypt path //! updates for a [`TreeSyncDiff`] instance. +use futures::StreamExt; +use futures::{stream, TryStreamExt}; use std::collections::HashSet; use openmls_traits::{ @@ -43,7 +45,7 @@ impl<'a> TreeSyncDiff<'a> { /// included in the resulting [`UpdatePath`]. /// /// Returns the encrypted path (i.e. an [`UpdatePath`] instance). - pub(crate) fn encrypt_path( + pub(crate) async fn encrypt_path( &self, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -71,10 +73,14 @@ impl<'a> TreeSyncDiff<'a> { debug_assert_eq!(copath_resolutions.len(), path.len()); // Encrypt the secrets - path.iter() - .zip(copath_resolutions.iter()) - .map(|(node, resolution)| node.encrypt(backend, ciphersuite, resolution, group_context)) - .collect::, LibraryError>>() + stream::iter(path) + .zip(stream::iter(copath_resolutions)) + .then(|(node, resolution)| async move { + node.encrypt(backend, ciphersuite, resolution.as_slice(), group_context) + .await + }) + .try_collect::>() + .await } /// Decrypt an [`UpdatePath`] originating from the given @@ -165,7 +171,7 @@ impl<'a> TreeSyncDiff<'a> { /// - the leaf index of a new member is identical to the own leaf index /// - the plain path does not contain the correct secrets #[allow(clippy::too_many_arguments)] - pub(crate) fn encrypt_group_secrets( + pub(crate) async fn encrypt_group_secrets( &self, joiner_secret: &JoinerSecret, invited_members: Vec<(LeafNodeIndex, AddProposal)>, @@ -209,6 +215,7 @@ impl<'a> TreeSyncDiff<'a> { key_package.ciphersuite(), backend.crypto(), ) + .await .map_err(|_| { LibraryError::custom( "Error while encrypting group secrets. \ diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 346ddfb7c4..41ffa6dd9c 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -29,7 +29,7 @@ async fn generate_credential( // ANCHOR: create_credential_keys let signature_keys = SignatureKeyPair::new( signature_algorithm, - &mut *backend.rand().borrow_rand().unwrap(), + &mut *backend.rand().borrow_rand().await, ) .unwrap(); signature_keys.store(backend.key_store()).await.unwrap(); @@ -82,1242 +82,1253 @@ async fn generate_key_package( /// - Test saving the group state #[apply(ciphersuites_and_backends)] async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { - // Generate credentials with keys - let (alice_credential, alice_signature_keys) = - generate_credential("Alice".into(), ciphersuite.signature_algorithm(), backend).await; - - let (bob_credential, bob_signature_keys) = - generate_credential("Bob".into(), ciphersuite.signature_algorithm(), backend).await; + Box::pin(async { + // Generate credentials with keys + let (alice_credential, alice_signature_keys) = + generate_credential("Alice".into(), ciphersuite.signature_algorithm(), backend).await; - let (charlie_credential, charlie_signature_keys) = - generate_credential("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; - - let (dave_credential, dave_signature_keys) = - generate_credential("Dave".into(), ciphersuite.signature_algorithm(), backend).await; - - // Generate KeyPackages - let bob_key_package = generate_key_package( - ciphersuite, - bob_credential.clone(), - Extensions::default(), - backend, - &bob_signature_keys, - ) - .await; + let (bob_credential, bob_signature_keys) = + generate_credential("Bob".into(), ciphersuite.signature_algorithm(), backend).await; - // Define the MlsGroup configuration - // delivery service credentials - let (ds_credential_with_key, ds_signature_keys) = generate_credential( - "delivery-service".into(), - ciphersuite.signature_algorithm(), - backend, - ) - .await; + let (charlie_credential, charlie_signature_keys) = + generate_credential("Charlie".into(), ciphersuite.signature_algorithm(), backend).await; - // ANCHOR: mls_group_config_example - let mls_group_config = MlsGroupConfig::builder() - .padding_size(100) - .sender_ratchet_configuration(SenderRatchetConfiguration::new( - 10, // out_of_order_tolerance - 2000, // maximum_forward_distance - )) - .external_senders(vec![ExternalSender::new( - ds_credential_with_key.signature_key, - ds_credential_with_key.credential, - )]) - .crypto_config(CryptoConfig::with_default_version(ciphersuite)) - .use_ratchet_tree_extension(true) - .build(); - // ANCHOR_END: mls_group_config_example - - // ANCHOR: alice_create_group - let mut alice_group = MlsGroup::new( - backend, - &alice_signature_keys, - &mls_group_config, - alice_credential.clone(), - ) - .await - .expect("An unexpected error occurred."); - // ANCHOR_END: alice_create_group + let (dave_credential, dave_signature_keys) = + generate_credential("Dave".into(), ciphersuite.signature_algorithm(), backend).await; - { - // ANCHOR: alice_create_group_with_group_id - // Some specific group ID generated by someone else. - let group_id = GroupId::from_slice(b"123e4567e89b"); + // Generate KeyPackages + let bob_key_package = generate_key_package( + ciphersuite, + bob_credential.clone(), + Extensions::default(), + backend, + &bob_signature_keys, + ) + .await; - let mut alice_group = MlsGroup::new_with_group_id( + // Define the MlsGroup configuration + // delivery service credentials + let (ds_credential_with_key, ds_signature_keys) = generate_credential( + "delivery-service".into(), + ciphersuite.signature_algorithm(), + backend, + ) + .await; + + // ANCHOR: mls_group_config_example + let mls_group_config = MlsGroupConfig::builder() + .padding_size(100) + .sender_ratchet_configuration(SenderRatchetConfiguration::new( + 10, // out_of_order_tolerance + 2000, // maximum_forward_distance + )) + .external_senders(vec![ExternalSender::new( + ds_credential_with_key.signature_key, + ds_credential_with_key.credential, + )]) + .crypto_config(CryptoConfig::with_default_version(ciphersuite)) + .use_ratchet_tree_extension(true) + .build(); + // ANCHOR_END: mls_group_config_example + + // ANCHOR: alice_create_group + let mut alice_group = MlsGroup::new( backend, &alice_signature_keys, &mls_group_config, - group_id, alice_credential.clone(), ) .await .expect("An unexpected error occurred."); - // ANCHOR_END: alice_create_group_with_group_id + // ANCHOR_END: alice_create_group - // Silence "unused variable" and "does not need to be mutable" warnings. - let _ignore_mut_warning = &mut alice_group; - } + { + // ANCHOR: alice_create_group_with_group_id + // Some specific group ID generated by someone else. + let group_id = GroupId::from_slice(b"123e4567e89b"); - let group_id = alice_group.group_id().clone(); + let mut alice_group = MlsGroup::new_with_group_id( + backend, + &alice_signature_keys, + &mls_group_config, + group_id, + alice_credential.clone(), + ) + .await + .expect("An unexpected error occurred."); + // ANCHOR_END: alice_create_group_with_group_id - // === Alice adds Bob === - // ANCHOR: alice_adds_bob - let (mls_message_out, welcome, group_info) = alice_group - .add_members(backend, &alice_signature_keys, vec![bob_key_package.into()]) - .await - .expect("Could not add members."); - // ANCHOR_END: alice_adds_bob - - // Suppress warning - let _mls_message_out = mls_message_out; - let _group_info = group_info; - - // Check that we received the correct proposals - if let Some(staged_commit) = alice_group.pending_commit() { - let add = staged_commit - .add_proposals() - .next() - .expect("Expected a proposal."); - // Check that Bob was added - assert_eq!( - add.add_proposal().key_package().leaf_node().credential(), - &bob_credential.credential - ); - // Check that Alice added Bob - assert!(matches!( - add.sender(), - Sender::Member(member) if *member == alice_group.own_leaf_index() - )); - } else { - unreachable!("Expected a StagedCommit."); - } + // Silence "unused variable" and "does not need to be mutable" warnings. + let _ignore_mut_warning = &mut alice_group; + } - alice_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); + let group_id = alice_group.group_id().clone(); - // Check that the group now has two members - assert_eq!(alice_group.members().count(), 2); + // === Alice adds Bob === + // ANCHOR: alice_adds_bob + let (mls_message_out, welcome, group_info) = alice_group + .add_members(backend, &alice_signature_keys, vec![bob_key_package.into()]) + .await + .expect("Could not add members."); + // ANCHOR_END: alice_adds_bob + + // Suppress warning + let _mls_message_out = mls_message_out; + let _group_info = group_info; + + // Check that we received the correct proposals + if let Some(staged_commit) = alice_group.pending_commit() { + let add = staged_commit + .add_proposals() + .next() + .expect("Expected a proposal."); + // Check that Bob was added + assert_eq!( + add.add_proposal().key_package().leaf_node().credential(), + &bob_credential.credential + ); + // Check that Alice added Bob + assert!(matches!( + add.sender(), + Sender::Member(member) if *member == alice_group.own_leaf_index() + )); + } else { + unreachable!("Expected a StagedCommit."); + } - // Check that Alice & Bob are the members of the group - let members = alice_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Bob"); + alice_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); - // ANCHOR: bob_joins_with_welcome - let mut bob_group = MlsGroup::new_from_welcome( - backend, - &mls_group_config, - welcome.into_welcome().expect("Unexpected message type."), - None, // We use the ratchet tree extension, so we don't provide a ratchet tree here - ) - .await - .expect("Error joining group from Welcome"); - // ANCHOR_END: bob_joins_with_welcome - - // ANCHOR: alice_exports_group_info - let verifiable_group_info = alice_group - .export_group_info(backend, &alice_signature_keys, true) - .expect("Cannot export group info") - .into_verifiable_group_info() - .expect("Could not get group info"); - // ANCHOR_END: alice_exports_group_info - - // ANCHOR: charlie_joins_external_commit - let (mut dave_group, _out, _group_info) = MlsGroup::join_by_external_commit( - backend, - &dave_signature_keys, - None, - verifiable_group_info, - &mls_group_config, - &[], - dave_credential, - ) - .await - .expect("Error joining from external commit"); - dave_group - .merge_pending_commit(backend) - .await - .expect("Cannot merge commit"); - // ANCHOR_END: charlie_joins_external_commit - - // Make sure that both groups have the same members - assert!(alice_group.members().eq(bob_group.members())); - - // Make sure that both groups have the same epoch authenticator - assert_eq!( - alice_group.epoch_authenticator().as_slice(), - bob_group.epoch_authenticator().as_slice() - ); - - // === Alice sends a message to Bob === - // ANCHOR: create_application_message - let message_alice = b"Hi, I'm Alice!"; - let mls_message_out = alice_group - .create_message(backend, &alice_signature_keys, message_alice) - .expect("Error creating application message."); - // ANCHOR_END: create_application_message - - // Message serialization - - let bytes = mls_message_out - .to_bytes() - .expect("Could not serialize message."); - - // ANCHOR: mls_message_in_from_bytes - let mls_message = - MlsMessageIn::tls_deserialize_exact(bytes).expect("Could not deserialize message."); - // ANCHOR_END: mls_message_in_from_bytes - - // ANCHOR: process_message - let protocol_message: ProtocolMessage = mls_message.into(); - let processed_message = bob_group - .process_message(backend, protocol_message) - .await - .expect("Could not process message."); - // ANCHOR_END: process_message - - // Check that we received the correct message - // ANCHOR: inspect_application_message - if let ProcessedMessageContent::ApplicationMessage(application_message) = - processed_message.into_content() - { - // Check the message - assert_eq!(application_message.into_bytes(), b"Hi, I'm Alice!"); - } - // ANCHOR_END: inspect_application_message - else { - unreachable!("Expected an ApplicationMessage."); - } - - // === Bob updates and commits === - // ANCHOR: self_update - let (mls_message_out, welcome_option, _group_info) = bob_group - .self_update(backend, &bob_signature_keys) - .await - .expect("Could not update own key package."); - // ANCHOR_END: self_update + // Check that the group now has two members + assert_eq!(alice_group.members().count(), 2); - let alice_processed_message = alice_group - .process_message( + // Check that Alice & Bob are the members of the group + let members = alice_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Bob"); + + // ANCHOR: bob_joins_with_welcome + let mut bob_group = MlsGroup::new_from_welcome( backend, - mls_message_out - .into_protocol_message() - .expect("Unexpected message type"), + &mls_group_config, + welcome.into_welcome().expect("Unexpected message type."), + None, // We use the ratchet tree extension, so we don't provide a ratchet tree here ) .await - .expect("Could not process message."); + .expect("Error joining group from Welcome"); + // ANCHOR_END: bob_joins_with_welcome + + // ANCHOR: alice_exports_group_info + let verifiable_group_info = alice_group + .export_group_info(backend, &alice_signature_keys, true) + .expect("Cannot export group info") + .into_verifiable_group_info() + .expect("Could not get group info"); + // ANCHOR_END: alice_exports_group_info + + // ANCHOR: charlie_joins_external_commit + let (mut dave_group, _out, _group_info) = MlsGroup::join_by_external_commit( + backend, + &dave_signature_keys, + None, + verifiable_group_info, + &mls_group_config, + &[], + dave_credential, + ) + .await + .expect("Error joining from external commit"); + dave_group + .merge_pending_commit(backend) + .await + .expect("Cannot merge commit"); + // ANCHOR_END: charlie_joins_external_commit - // Check that we received the correct message - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - alice_processed_message.into_content() - { - // Merge staged Commit - alice_group - .merge_staged_commit(backend, *staged_commit) + // Make sure that both groups have the same members + assert!(alice_group.members().eq(bob_group.members())); + + // Make sure that both groups have the same epoch authenticator + assert_eq!( + alice_group.epoch_authenticator().as_slice(), + bob_group.epoch_authenticator().as_slice() + ); + + // === Alice sends a message to Bob === + // ANCHOR: create_application_message + let message_alice = b"Hi, I'm Alice!"; + let mls_message_out = alice_group + .create_message(backend, &alice_signature_keys, message_alice) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("Error creating application message."); + // ANCHOR_END: create_application_message - bob_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); - - // Check we didn't receive a Welcome message - assert!(welcome_option.is_none()); - - // Check that both groups have the same state - assert_eq!( - alice_group.export_secret(backend, "", &[], 32), - bob_group.export_secret(backend, "", &[], 32) - ); - - // Make sure that both groups have the same public tree - assert_eq!( - alice_group.export_ratchet_tree(), - bob_group.export_ratchet_tree() - ); - - // === Alice updates and commits === - // ANCHOR: propose_self_update - let (mls_message_out, _proposal_ref) = alice_group - .propose_self_update(backend, &alice_signature_keys) - .await - .expect("Could not create update proposal."); - // ANCHOR_END: propose_self_update + // Message serialization - let bob_processed_message = bob_group - .process_message( - backend, - mls_message_out - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - - // Check that we received the correct proposals - if let ProcessedMessageContent::ProposalMessage(staged_proposal) = - bob_processed_message.into_content() - { - if let Proposal::Update(ref update_proposal) = staged_proposal.proposal() { - // Check that Alice updated - assert_eq!( - update_proposal.leaf_node().credential(), - &alice_credential.credential - ); - // Store proposal - alice_group.store_pending_proposal(*staged_proposal.clone()); - } else { - unreachable!("Expected a Proposal."); + let bytes = mls_message_out + .to_bytes() + .expect("Could not serialize message."); + + // ANCHOR: mls_message_in_from_bytes + let mls_message = + MlsMessageIn::tls_deserialize_exact(bytes).expect("Could not deserialize message."); + // ANCHOR_END: mls_message_in_from_bytes + + // ANCHOR: process_message + let protocol_message: ProtocolMessage = mls_message.into(); + let processed_message = bob_group + .process_message(backend, protocol_message) + .await + .expect("Could not process message."); + // ANCHOR_END: process_message + + // Check that we received the correct message + // ANCHOR: inspect_application_message + if let ProcessedMessageContent::ApplicationMessage(application_message) = + processed_message.into_content() + { + // Check the message + assert_eq!(application_message.into_bytes(), b"Hi, I'm Alice!"); + } + // ANCHOR_END: inspect_application_message + else { + unreachable!("Expected an ApplicationMessage."); } - // Check that Alice sent the proposal - assert!(matches!( - staged_proposal.sender(), - Sender::Member(member) if *member == alice_group.own_leaf_index() - )); - bob_group.store_pending_proposal(*staged_proposal); - } else { - unreachable!("Expected a QueuedProposal."); - } - - // ANCHOR: commit_to_proposals - let (mls_message_out, welcome_option, _group_info) = alice_group - .commit_to_pending_proposals(backend, &alice_signature_keys) - .await - .expect("Could not commit to pending proposals."); - // ANCHOR_END: commit_to_proposals + // === Bob updates and commits === + // ANCHOR: self_update + let (mls_message_out, welcome_option, _group_info) = bob_group + .self_update(backend, &bob_signature_keys) + .await + .expect("Could not update own key package."); + // ANCHOR_END: self_update - // Suppress warning - let _welcome_option = welcome_option; + let alice_processed_message = alice_group + .process_message( + backend, + mls_message_out + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); - let bob_processed_message = bob_group - .process_message( - backend, - mls_message_out - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); + // Check that we received the correct message + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + alice_processed_message.into_content() + { + // Merge staged Commit + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - // Check that we received the correct message - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - bob_processed_message.into_content() - { bob_group - .merge_staged_commit(backend, *staged_commit) + .merge_pending_commit(backend) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("error merging pending commit"); - alice_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); - - // Check that both groups have the same state - assert_eq!( - alice_group.export_secret(backend, "", &[], 32), - bob_group.export_secret(backend, "", &[], 32) - ); - - // Make sure that both groups have the same public tree - assert_eq!( - alice_group.export_ratchet_tree(), - bob_group.export_ratchet_tree() - ); - - // === Bob adds Charlie === - let charlie_key_package = generate_key_package( - ciphersuite, - charlie_credential.clone(), - Extensions::default(), - backend, - &charlie_signature_keys, - ) - .await; + // Check we didn't receive a Welcome message + assert!(welcome_option.is_none()); - let (queued_message, welcome, _group_info) = bob_group - .add_members( - backend, - &bob_signature_keys, - vec![charlie_key_package.into()], - ) - .await - .unwrap(); + // Check that both groups have the same state + assert_eq!( + alice_group.export_secret(backend, "", &[], 32), + bob_group.export_secret(backend, "", &[], 32) + ); - let alice_processed_message = alice_group - .process_message( - backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - bob_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); + // Make sure that both groups have the same public tree + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() + ); - // Merge Commit - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - alice_processed_message.into_content() - { - alice_group - .merge_staged_commit(backend, *staged_commit) + // === Alice updates and commits === + // ANCHOR: propose_self_update + let (mls_message_out, _proposal_ref) = alice_group + .propose_self_update(backend, &alice_signature_keys) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("Could not create update proposal."); + // ANCHOR_END: propose_self_update - let mut charlie_group = MlsGroup::new_from_welcome( - backend, - &mls_group_config, - welcome.into_welcome().expect("Unexpected message type."), - Some(bob_group.export_ratchet_tree().into()), - ) - .await - .expect("Error creating group from Welcome"); - - // Make sure that all groups have the same public tree - assert_eq!( - alice_group.export_ratchet_tree(), - bob_group.export_ratchet_tree(), - ); - assert_eq!( - alice_group.export_ratchet_tree(), - charlie_group.export_ratchet_tree() - ); - - // Check that Alice, Bob & Charlie are the members of the group - let members = alice_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Bob"); - assert_eq!(members[2].credential.identity(), b"Charlie"); - assert_eq!(members.len(), 3); - - // === Charlie sends a message to the group === - let message_charlie = b"Hi, I'm Charlie!"; - let queued_message = charlie_group - .create_message(backend, &charlie_signature_keys, message_charlie) - .expect("Error creating application message"); - - let _alice_processed_message = alice_group - .process_message( - backend, - queued_message - .clone() - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - let _bob_processed_message = bob_group - .process_message( - backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); + let bob_processed_message = bob_group + .process_message( + backend, + mls_message_out + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + + // Check that we received the correct proposals + if let ProcessedMessageContent::ProposalMessage(staged_proposal) = + bob_processed_message.into_content() + { + if let Proposal::Update(ref update_proposal) = staged_proposal.proposal() { + // Check that Alice updated + assert_eq!( + update_proposal.leaf_node().credential(), + &alice_credential.credential + ); + // Store proposal + alice_group.store_pending_proposal(*staged_proposal.clone()); + } else { + unreachable!("Expected a Proposal."); + } - // === Charlie updates and commits === - let (queued_message, welcome_option, _group_info) = charlie_group - .self_update(backend, &charlie_signature_keys) - .await - .unwrap(); + // Check that Alice sent the proposal + assert!(matches!( + staged_proposal.sender(), + Sender::Member(member) if *member == alice_group.own_leaf_index() + )); + bob_group.store_pending_proposal(*staged_proposal); + } else { + unreachable!("Expected a QueuedProposal."); + } - let alice_processed_message = alice_group - .process_message( - backend, - queued_message - .clone() - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - let bob_processed_message = bob_group - .process_message( - backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - charlie_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); + // ANCHOR: commit_to_proposals + let (mls_message_out, welcome_option, _group_info) = alice_group + .commit_to_pending_proposals(backend, &alice_signature_keys) + .await + .expect("Could not commit to pending proposals."); + // ANCHOR_END: commit_to_proposals - // Merge Commit - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - alice_processed_message.into_content() - { - alice_group - .merge_staged_commit(backend, *staged_commit) + // Suppress warning + let _welcome_option = welcome_option; + + let bob_processed_message = bob_group + .process_message( + backend, + mls_message_out + .into_protocol_message() + .expect("Unexpected message type"), + ) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } - - // Merge Commit - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - bob_processed_message.into_content() - { - bob_group - .merge_staged_commit(backend, *staged_commit) + .expect("Could not process message."); + + // Check that we received the correct message + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + bob_processed_message.into_content() + { + bob_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } else { + unreachable!("Expected a StagedCommit."); + } + + alice_group + .merge_pending_commit(backend) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } - - // Check we didn't receive a Welcome message - assert!(welcome_option.is_none()); - - // Check that all groups have the same state - assert_eq!( - alice_group.export_secret(backend, "", &[], 32), - bob_group.export_secret(backend, "", &[], 32) - ); - assert_eq!( - alice_group.export_secret(backend, "", &[], 32), - charlie_group.export_secret(backend, "", &[], 32) - ); - - // Make sure that all groups have the same public tree - assert_eq!( - alice_group.export_ratchet_tree(), - bob_group.export_ratchet_tree(), - ); - assert_eq!( - alice_group.export_ratchet_tree(), - charlie_group.export_ratchet_tree() - ); - - // ANCHOR: retrieve_members - let charlie_members = charlie_group.members().collect::>(); - // ANCHOR_END: retrieve_members - - let bob_member = charlie_members - .iter() - .find( - |Member { - index: _, - credential, - .. - }| credential.identity() == b"Bob", - ) - .expect("Couldn't find Bob in the list of group members."); + .expect("error merging pending commit"); - // Make sure that this is Bob's actual KP reference. - assert_eq!( - bob_member.credential.identity(), - bob_group - .own_identity() - .expect("An unexpected error occurred.") - ); - - // === Charlie removes Bob === - // ANCHOR: charlie_removes_bob - let (mls_message_out, welcome_option, _group_info) = charlie_group - .remove_members(backend, &charlie_signature_keys, &[bob_member.index]) - .await - .expect("Could not remove Bob from group."); - // ANCHOR_END: charlie_removes_bob + // Check that both groups have the same state + assert_eq!( + alice_group.export_secret(backend, "", &[], 32), + bob_group.export_secret(backend, "", &[], 32) + ); - // Check that Bob's group is still active - assert!(bob_group.is_active()); + // Make sure that both groups have the same public tree + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() + ); - let alice_processed_message = alice_group - .process_message( + // === Bob adds Charlie === + let charlie_key_package = generate_key_package( + ciphersuite, + charlie_credential.clone(), + Extensions::default(), backend, - mls_message_out - .clone() - .into_protocol_message() - .expect("Unexpected message type"), + &charlie_signature_keys, ) - .await - .expect("Could not process message."); + .await; - // Check that alice can use the member list to check if the message is - // actually from Charlie. - let mut alice_members = alice_group.members(); - let sender_leaf_index = match alice_processed_message.sender() { - Sender::Member(index) => index, - _ => panic!("Sender should have been a member"), - }; - let sender_credential = alice_processed_message.credential(); + let (queued_message, welcome, _group_info) = bob_group + .add_members( + backend, + &bob_signature_keys, + vec![charlie_key_package.into()], + ) + .await + .unwrap(); - assert!(alice_members.any(|Member { index, .. }| index == *sender_leaf_index)); - drop(alice_members); + let alice_processed_message = alice_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + bob_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); - assert_eq!(sender_credential, &charlie_credential); + // Merge Commit + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + alice_processed_message.into_content() + { + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - let bob_processed_message = bob_group - .process_message( + let mut charlie_group = MlsGroup::new_from_welcome( backend, - mls_message_out - .into_protocol_message() - .expect("Unexpected message type"), + &mls_group_config, + welcome.into_welcome().expect("Unexpected message type."), + Some(bob_group.export_ratchet_tree().into()), ) .await - .expect("Could not process message."); - let charlies_leaf_index = charlie_group.own_leaf_index(); - charlie_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); - - // Check that we receive the correct proposal for Alice - // ANCHOR: inspect_staged_commit - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - alice_processed_message.into_content() - { - // We expect a remove proposal - let remove = staged_commit - .remove_proposals() - .next() - .expect("Expected a proposal."); - // Check that Bob was removed + .expect("Error creating group from Welcome"); + + // Make sure that all groups have the same public tree assert_eq!( - remove.remove_proposal().removed(), - bob_group.own_leaf_index() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree(), ); - // Check that Charlie removed Bob - assert!(matches!( - remove.sender(), - Sender::Member(member) if *member == charlies_leaf_index - )); - // Merge staged commit - alice_group - .merge_staged_commit(backend, *staged_commit) + assert_eq!( + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() + ); + + // Check that Alice, Bob & Charlie are the members of the group + let members = alice_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Bob"); + assert_eq!(members[2].credential.identity(), b"Charlie"); + assert_eq!(members.len(), 3); + + // === Charlie sends a message to the group === + let message_charlie = b"Hi, I'm Charlie!"; + let queued_message = charlie_group + .create_message(backend, &charlie_signature_keys, message_charlie) .await - .expect("Error merging staged commit."); - } - // ANCHOR_END: inspect_staged_commit - else { - unreachable!("Expected a StagedCommit."); - } - - // Check that we receive the correct proposal for Bob - // ANCHOR: remove_operation - // ANCHOR: getting_removed - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - bob_processed_message.into_content() - { - let remove_proposal = staged_commit - .remove_proposals() - .next() - .expect("An unexpected error occurred."); + .expect("Error creating application message"); - // We construct a RemoveOperation enum to help us interpret the remove operation - let remove_operation = RemoveOperation::new(remove_proposal, &bob_group) - .expect("An unexpected Error occurred."); + let _alice_processed_message = alice_group + .process_message( + backend, + queued_message + .clone() + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + let _bob_processed_message = bob_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); - match remove_operation { - RemoveOperation::WeLeft => unreachable!(), - // We expect this variant, since Bob was removed by Charlie - RemoveOperation::WeWereRemovedBy(member) => { - assert!(matches!(member, Sender::Member(member) if member == charlies_leaf_index)); - } - RemoveOperation::TheyLeft(_) => unreachable!(), - RemoveOperation::TheyWereRemovedBy(_) => unreachable!(), - RemoveOperation::WeRemovedThem(_) => unreachable!(), - } + // === Charlie updates and commits === + let (queued_message, welcome_option, _group_info) = charlie_group + .self_update(backend, &charlie_signature_keys) + .await + .unwrap(); - // Merge staged Commit - bob_group - .merge_staged_commit(backend, *staged_commit) + let alice_processed_message = alice_group + .process_message( + backend, + queued_message + .clone() + .into_protocol_message() + .expect("Unexpected message type"), + ) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } - // ANCHOR_END: remove_operation - - // Check we didn't receive a Welcome message - assert!(welcome_option.is_none()); - - // Check that Bob's group is no longer active - assert!(!bob_group.is_active()); - let members = bob_group.members().collect::>(); - assert_eq!(members.len(), 2); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Charlie"); - // ANCHOR_END: getting_removed - - // Make sure that all groups have the same public tree - assert_eq!( - alice_group.export_ratchet_tree(), - charlie_group.export_ratchet_tree() - ); - - // Make sure the group only contains two members - assert_eq!(alice_group.members().count(), 2); - - // Check that Alice & Charlie are the members of the group - let members = alice_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Charlie"); - - // Check that Bob can no longer send messages - assert!(bob_group - .create_message(backend, &bob_signature_keys, b"Should not go through") - .is_err()); - - // === Alice removes Charlie and re-adds Bob === - - // Create a new KeyPackageBundle for Bob - let bob_key_package = generate_key_package( - ciphersuite, - bob_credential.clone(), - Extensions::default(), - backend, - &bob_signature_keys, - ) - .await; + .expect("Could not process message."); + let bob_processed_message = bob_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + charlie_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); - // Create RemoveProposal and process it - // ANCHOR: propose_remove - let (mls_message_out, _proposal_ref) = alice_group - .propose_remove_member( - backend, - &alice_signature_keys, - charlie_group.own_leaf_index(), - ) - .expect("Could not create proposal to remove Charlie."); - // ANCHOR_END: propose_remove + // Merge Commit + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + alice_processed_message.into_content() + { + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - let charlie_processed_message = charlie_group - .process_message( - backend, - mls_message_out - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - - // Check that we received the correct proposals - if let ProcessedMessageContent::ProposalMessage(staged_proposal) = - charlie_processed_message.into_content() - { - if let Proposal::Remove(ref remove_proposal) = staged_proposal.proposal() { - // Check that Charlie was removed - assert_eq!(remove_proposal.removed(), charlie_group.own_leaf_index()); - // Store proposal - charlie_group.store_pending_proposal(*staged_proposal.clone()); + // Merge Commit + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + bob_processed_message.into_content() + { + bob_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); } else { - unreachable!("Expected a Proposal."); + unreachable!("Expected a StagedCommit."); } - // Check that Alice removed Charlie - assert!(matches!( - staged_proposal.sender(), - Sender::Member(member) if *member == alice_group.own_leaf_index() - )); - } else { - unreachable!("Expected a QueuedProposal."); - } - - // Create AddProposal and remove it - // ANCHOR: rollback_proposal_by_ref - let (_mls_message_out, proposal_ref) = alice_group - .propose_add_member( - backend, - &alice_signature_keys, - bob_key_package.clone().into(), - ) - .await - .expect("Could not create proposal to add Bob"); - alice_group - .remove_pending_proposal(backend.key_store(), &proposal_ref) - .await - .expect("The proposal was not found"); - // ANCHOR_END: rollback_proposal_by_ref + // Check we didn't receive a Welcome message + assert!(welcome_option.is_none()); - // Create AddProposal and process it - // ANCHOR: propose_add - let (mls_message_out, _proposal_ref) = alice_group - .propose_add_member( - backend, - &alice_signature_keys, - bob_key_package.clone().into(), - ) - .await - .expect("Could not create proposal to add Bob"); - // ANCHOR_END: propose_add + // Check that all groups have the same state + assert_eq!( + alice_group.export_secret(backend, "", &[], 32), + bob_group.export_secret(backend, "", &[], 32) + ); + assert_eq!( + alice_group.export_secret(backend, "", &[], 32), + charlie_group.export_secret(backend, "", &[], 32) + ); - let charlie_processed_message = charlie_group - .process_message( - backend, - mls_message_out - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - - // Check that we received the correct proposals - // ANCHOR: inspect_add_proposal - if let ProcessedMessageContent::ProposalMessage(staged_proposal) = - charlie_processed_message.into_content() - { - // In the case we received an Add Proposal - if let Proposal::Add(add_proposal) = staged_proposal.proposal() { - // Check that Bob was added + // Make sure that all groups have the same public tree + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree(), + ); + assert_eq!( + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() + ); + + // ANCHOR: retrieve_members + let charlie_members = charlie_group.members().collect::>(); + // ANCHOR_END: retrieve_members + + let bob_member = charlie_members + .iter() + .find( + |Member { + index: _, + credential, + .. + }| credential.identity() == b"Bob", + ) + .expect("Couldn't find Bob in the list of group members."); + + // Make sure that this is Bob's actual KP reference. + assert_eq!( + bob_member.credential.identity(), + bob_group + .own_identity() + .expect("An unexpected error occurred.") + ); + + // === Charlie removes Bob === + // ANCHOR: charlie_removes_bob + let (mls_message_out, welcome_option, _group_info) = charlie_group + .remove_members(backend, &charlie_signature_keys, &[bob_member.index]) + .await + .expect("Could not remove Bob from group."); + // ANCHOR_END: charlie_removes_bob + + // Check that Bob's group is still active + assert!(bob_group.is_active()); + + let alice_processed_message = alice_group + .process_message( + backend, + mls_message_out + .clone() + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + + // Check that alice can use the member list to check if the message is + // actually from Charlie. + let mut alice_members = alice_group.members(); + let sender_leaf_index = match alice_processed_message.sender() { + Sender::Member(index) => index, + _ => panic!("Sender should have been a member"), + }; + let sender_credential = alice_processed_message.credential(); + + assert!(alice_members.any(|Member { index, .. }| index == *sender_leaf_index)); + drop(alice_members); + + assert_eq!(sender_credential, &charlie_credential); + + let bob_processed_message = bob_group + .process_message( + backend, + mls_message_out + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + let charlies_leaf_index = charlie_group.own_leaf_index(); + charlie_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); + + // Check that we receive the correct proposal for Alice + // ANCHOR: inspect_staged_commit + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + alice_processed_message.into_content() + { + // We expect a remove proposal + let remove = staged_commit + .remove_proposals() + .next() + .expect("Expected a proposal."); + // Check that Bob was removed assert_eq!( - add_proposal.key_package().leaf_node().credential(), - &bob_credential.credential + remove.remove_proposal().removed(), + bob_group.own_leaf_index() ); + // Check that Charlie removed Bob + assert!(matches!( + remove.sender(), + Sender::Member(member) if *member == charlies_leaf_index + )); + // Merge staged commit + alice_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } + // ANCHOR_END: inspect_staged_commit + else { + unreachable!("Expected a StagedCommit."); + } + + // Check that we receive the correct proposal for Bob + // ANCHOR: remove_operation + // ANCHOR: getting_removed + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + bob_processed_message.into_content() + { + let remove_proposal = staged_commit + .remove_proposals() + .next() + .expect("An unexpected error occurred."); + + // We construct a RemoveOperation enum to help us interpret the remove operation + let remove_operation = RemoveOperation::new(remove_proposal, &bob_group) + .expect("An unexpected Error occurred."); + + match remove_operation { + RemoveOperation::WeLeft => unreachable!(), + // We expect this variant, since Bob was removed by Charlie + RemoveOperation::WeWereRemovedBy(member) => { + assert!( + matches!(member, Sender::Member(member) if member == charlies_leaf_index) + ); + } + RemoveOperation::TheyLeft(_) => unreachable!(), + RemoveOperation::TheyWereRemovedBy(_) => unreachable!(), + RemoveOperation::WeRemovedThem(_) => unreachable!(), + } + + // Merge staged Commit + bob_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); } else { - panic!("Expected an AddProposal."); + unreachable!("Expected a StagedCommit."); } + // ANCHOR_END: remove_operation - // Check that Alice added Bob - assert!(matches!( - staged_proposal.sender(), - Sender::Member(member) if *member == alice_group.own_leaf_index() - )); - // Store proposal - charlie_group.store_pending_proposal(*staged_proposal); - } - // ANCHOR_END: inspect_add_proposal - else { - unreachable!("Expected a QueuedProposal."); - } - - // Commit to the proposals and process it - let (queued_message, welcome_option, _group_info) = alice_group - .commit_to_pending_proposals(backend, &alice_signature_keys) - .await - .expect("Could not flush proposals"); + // Check we didn't receive a Welcome message + assert!(welcome_option.is_none()); + + // Check that Bob's group is no longer active + assert!(!bob_group.is_active()); + let members = bob_group.members().collect::>(); + assert_eq!(members.len(), 2); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Charlie"); + // ANCHOR_END: getting_removed + + // Make sure that all groups have the same public tree + assert_eq!( + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() + ); + + // Make sure the group only contains two members + assert_eq!(alice_group.members().count(), 2); + + // Check that Alice & Charlie are the members of the group + let members = alice_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Charlie"); + + // Check that Bob can no longer send messages + assert!(bob_group + .create_message(backend, &bob_signature_keys, b"Should not go through") + .await + .is_err()); + + // === Alice removes Charlie and re-adds Bob === - let charlie_processed_message = charlie_group - .process_message( + // Create a new KeyPackageBundle for Bob + let bob_key_package = generate_key_package( + ciphersuite, + bob_credential.clone(), + Extensions::default(), backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), + &bob_signature_keys, ) - .await - .expect("Could not process message."); + .await; - // Merge Commit - alice_group - .merge_pending_commit(backend) - .await - .expect("error merging pending commit"); + // Create RemoveProposal and process it + // ANCHOR: propose_remove + let (mls_message_out, _proposal_ref) = alice_group + .propose_remove_member( + backend, + &alice_signature_keys, + charlie_group.own_leaf_index(), + ) + .await + .expect("Could not create proposal to remove Charlie."); + // ANCHOR_END: propose_remove - // Merge Commit - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - charlie_processed_message.into_content() - { - charlie_group - .merge_staged_commit(backend, *staged_commit) + let charlie_processed_message = charlie_group + .process_message( + backend, + mls_message_out + .into_protocol_message() + .expect("Unexpected message type"), + ) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("Could not process message."); + + // Check that we received the correct proposals + if let ProcessedMessageContent::ProposalMessage(staged_proposal) = + charlie_processed_message.into_content() + { + if let Proposal::Remove(ref remove_proposal) = staged_proposal.proposal() { + // Check that Charlie was removed + assert_eq!(remove_proposal.removed(), charlie_group.own_leaf_index()); + // Store proposal + charlie_group.store_pending_proposal(*staged_proposal.clone()); + } else { + unreachable!("Expected a Proposal."); + } + + // Check that Alice removed Charlie + assert!(matches!( + staged_proposal.sender(), + Sender::Member(member) if *member == alice_group.own_leaf_index() + )); + } else { + unreachable!("Expected a QueuedProposal."); + } - // Make sure the group contains two members - assert_eq!(alice_group.members().count(), 2); + // Create AddProposal and remove it + // ANCHOR: rollback_proposal_by_ref + let (_mls_message_out, proposal_ref) = alice_group + .propose_add_member( + backend, + &alice_signature_keys, + bob_key_package.clone().into(), + ) + .await + .expect("Could not create proposal to add Bob"); + alice_group + .remove_pending_proposal(backend.key_store(), &proposal_ref) + .await + .expect("The proposal was not found"); + // ANCHOR_END: rollback_proposal_by_ref - // Check that Alice & Bob are the members of the group - let members = alice_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Bob"); + // Create AddProposal and process it + // ANCHOR: propose_add + let (mls_message_out, _proposal_ref) = alice_group + .propose_add_member( + backend, + &alice_signature_keys, + bob_key_package.clone().into(), + ) + .await + .expect("Could not create proposal to add Bob"); + // ANCHOR_END: propose_add - // Bob creates a new group - let mut bob_group = MlsGroup::new_from_welcome( - backend, - &mls_group_config, - welcome_option - .expect("Welcome was not returned") - .into_welcome() - .expect("Unexpected message type."), - Some(alice_group.export_ratchet_tree().into()), - ) - .await - .expect("Error creating group from Welcome"); + let charlie_processed_message = charlie_group + .process_message( + backend, + mls_message_out + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + + // Check that we received the correct proposals + // ANCHOR: inspect_add_proposal + if let ProcessedMessageContent::ProposalMessage(staged_proposal) = + charlie_processed_message.into_content() + { + // In the case we received an Add Proposal + if let Proposal::Add(add_proposal) = staged_proposal.proposal() { + // Check that Bob was added + assert_eq!( + add_proposal.key_package().leaf_node().credential(), + &bob_credential.credential + ); + } else { + panic!("Expected an AddProposal."); + } + + // Check that Alice added Bob + assert!(matches!( + staged_proposal.sender(), + Sender::Member(member) if *member == alice_group.own_leaf_index() + )); + // Store proposal + charlie_group.store_pending_proposal(*staged_proposal); + } + // ANCHOR_END: inspect_add_proposal + else { + unreachable!("Expected a QueuedProposal."); + } - // Make sure the group contains two members - assert_eq!(alice_group.members().count(), 2); + // Commit to the proposals and process it + let (queued_message, welcome_option, _group_info) = alice_group + .commit_to_pending_proposals(backend, &alice_signature_keys) + .await + .expect("Could not flush proposals"); - // Check that Alice & Bob are the members of the group - let members = alice_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Bob"); + let charlie_processed_message = charlie_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); - // Make sure the group contains two members - assert_eq!(bob_group.members().count(), 2); + // Merge Commit + alice_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); + + // Merge Commit + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + charlie_processed_message.into_content() + { + charlie_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - // Check that Alice & Bob are the members of the group - let members = bob_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); - assert_eq!(members[1].credential.identity(), b"Bob"); + // Make sure the group contains two members + assert_eq!(alice_group.members().count(), 2); - // === Alice sends a message to the group === - let message_alice = b"Hi, I'm Alice!"; - let queued_message = alice_group - .create_message(backend, &alice_signature_keys, message_alice) - .expect("Error creating application message"); + // Check that Alice & Bob are the members of the group + let members = alice_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Bob"); - let bob_processed_message = bob_group - .process_message( + // Bob creates a new group + let mut bob_group = MlsGroup::new_from_welcome( backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), + &mls_group_config, + welcome_option + .expect("Welcome was not returned") + .into_welcome() + .expect("Unexpected message type."), + Some(alice_group.export_ratchet_tree().into()), ) .await - .expect("Could not process message."); + .expect("Error creating group from Welcome"); - // Get sender information - // As provided by the processed message - let sender_cred_from_msg = bob_processed_message.credential().clone(); + // Make sure the group contains two members + assert_eq!(alice_group.members().count(), 2); - // As provided by looking up the sender manually via the `member()` function - // ANCHOR: member_lookup - let sender_cred_from_group = - if let Sender::Member(sender_index) = bob_processed_message.sender() { - bob_group - .member(*sender_index) - .expect("Could not find sender in group.") - .clone() - } else { - unreachable!("Expected sender type to be `Member`.") - }; - // ANCHOR_END: member_lookup - - // Check that we received the correct message - if let ProcessedMessageContent::ApplicationMessage(application_message) = - bob_processed_message.into_content() - { - // Check the message - assert_eq!(application_message.into_bytes(), message_alice); - // Check that Alice sent the message - assert_eq!(sender_cred_from_msg.credential, sender_cred_from_group); - assert_eq!( - &sender_cred_from_msg.credential, - alice_group.credential().expect("Expected a credential.") - ); - } else { - unreachable!("Expected an ApplicationMessage."); - } + // Check that Alice & Bob are the members of the group + let members = alice_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Bob"); - // === Bob leaves the group === + // Make sure the group contains two members + assert_eq!(bob_group.members().count(), 2); - // ANCHOR: leaving - let queued_message = bob_group - .leave_group(backend, &bob_signature_keys) - .expect("Could not leave group"); - // ANCHOR_END: leaving + // Check that Alice & Bob are the members of the group + let members = bob_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); + assert_eq!(members[1].credential.identity(), b"Bob"); - let alice_processed_message = alice_group - .process_message( - backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); + // === Alice sends a message to the group === + let message_alice = b"Hi, I'm Alice!"; + let queued_message = alice_group + .create_message(backend, &alice_signature_keys, message_alice) + .await + .expect("Error creating application message"); - // Store proposal - if let ProcessedMessageContent::ProposalMessage(staged_proposal) = - alice_processed_message.into_content() - { - // Store proposal - alice_group.store_pending_proposal(*staged_proposal); - } else { - unreachable!("Expected a QueuedProposal."); - } + let bob_processed_message = bob_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + + // Get sender information + // As provided by the processed message + let sender_cred_from_msg = bob_processed_message.credential().clone(); + + // As provided by looking up the sender manually via the `member()` function + // ANCHOR: member_lookup + let sender_cred_from_group = + if let Sender::Member(sender_index) = bob_processed_message.sender() { + bob_group + .member(*sender_index) + .expect("Could not find sender in group.") + .clone() + } else { + unreachable!("Expected sender type to be `Member`.") + }; + // ANCHOR_END: member_lookup + + // Check that we received the correct message + if let ProcessedMessageContent::ApplicationMessage(application_message) = + bob_processed_message.into_content() + { + // Check the message + assert_eq!(application_message.into_bytes(), message_alice); + // Check that Alice sent the message + assert_eq!(sender_cred_from_msg.credential, sender_cred_from_group); + assert_eq!( + &sender_cred_from_msg.credential, + alice_group.credential().expect("Expected a credential.") + ); + } else { + unreachable!("Expected an ApplicationMessage."); + } - // Should fail because you cannot remove yourself from a group - assert!(matches!( - bob_group - .commit_to_pending_proposals(backend, &bob_signature_keys) - .await, - Err(CommitToPendingProposalsError::CreateCommitError( - CreateCommitError::CannotRemoveSelf - )) - )); + // === Bob leaves the group === - let (queued_message, _welcome_option, _group_info) = alice_group - .commit_to_pending_proposals(backend, &alice_signature_keys) - .await - .expect("Could not commit to proposals."); - - // Check that Bob's group is still active - assert!(bob_group.is_active()); - - // Check that we received the correct proposals - if let Some(staged_commit) = alice_group.pending_commit() { - let remove = staged_commit - .remove_proposals() - .next() - .expect("Expected a proposal."); - // Check that Bob was removed - assert_eq!( - remove.remove_proposal().removed(), - bob_group.own_leaf_index() - ); - // Check that Bob removed himself - assert!(matches!( - remove.sender(), - Sender::Member(member) if *member == bob_group.own_leaf_index() - )); - // Merge staged Commit - } else { - unreachable!("Expected a StagedCommit."); - } + // ANCHOR: leaving + let queued_message = bob_group + .leave_group(backend, &bob_signature_keys) + .await + .expect("Could not leave group"); + // ANCHOR_END: leaving - alice_group - .merge_pending_commit(backend) - .await - .expect("Could not merge Commit."); + let alice_processed_message = alice_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); - let bob_processed_message = bob_group - .process_message( - backend, - queued_message - .into_protocol_message() - .expect("Unexpected message type"), - ) - .await - .expect("Could not process message."); - - // Check that we received the correct proposals - if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = - bob_processed_message.into_content() - { - let remove = staged_commit - .remove_proposals() - .next() - .expect("Expected a proposal."); - // Check that Bob was removed - assert_eq!( - remove.remove_proposal().removed(), - bob_group.own_leaf_index() - ); - // Check that Bob removed himself + // Store proposal + if let ProcessedMessageContent::ProposalMessage(staged_proposal) = + alice_processed_message.into_content() + { + // Store proposal + alice_group.store_pending_proposal(*staged_proposal); + } else { + unreachable!("Expected a QueuedProposal."); + } + + // Should fail because you cannot remove yourself from a group assert!(matches!( - remove.sender(), - Sender::Member(member) if *member == bob_group.own_leaf_index() + bob_group + .commit_to_pending_proposals(backend, &bob_signature_keys) + .await, + Err(CommitToPendingProposalsError::CreateCommitError( + CreateCommitError::CannotRemoveSelf + )) )); - assert!(staged_commit.self_removed()); - // Merge staged Commit - bob_group - .merge_staged_commit(backend, *staged_commit) + + let (queued_message, _welcome_option, _group_info) = alice_group + .commit_to_pending_proposals(backend, &alice_signature_keys) .await - .expect("Error merging staged commit."); - } else { - unreachable!("Expected a StagedCommit."); - } + .expect("Could not commit to proposals."); + + // Check that Bob's group is still active + assert!(bob_group.is_active()); + + // Check that we received the correct proposals + if let Some(staged_commit) = alice_group.pending_commit() { + let remove = staged_commit + .remove_proposals() + .next() + .expect("Expected a proposal."); + // Check that Bob was removed + assert_eq!( + remove.remove_proposal().removed(), + bob_group.own_leaf_index() + ); + // Check that Bob removed himself + assert!(matches!( + remove.sender(), + Sender::Member(member) if *member == bob_group.own_leaf_index() + )); + // Merge staged Commit + } else { + unreachable!("Expected a StagedCommit."); + } - // Check that Bob's group is no longer active - assert!(!bob_group.is_active()); + alice_group + .merge_pending_commit(backend) + .await + .expect("Could not merge Commit."); - // Make sure the group contains one member - assert_eq!(alice_group.members().count(), 1); + let bob_processed_message = bob_group + .process_message( + backend, + queued_message + .into_protocol_message() + .expect("Unexpected message type"), + ) + .await + .expect("Could not process message."); + + // Check that we received the correct proposals + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + bob_processed_message.into_content() + { + let remove = staged_commit + .remove_proposals() + .next() + .expect("Expected a proposal."); + // Check that Bob was removed + assert_eq!( + remove.remove_proposal().removed(), + bob_group.own_leaf_index() + ); + // Check that Bob removed himself + assert!(matches!( + remove.sender(), + Sender::Member(member) if *member == bob_group.own_leaf_index() + )); + assert!(staged_commit.self_removed()); + // Merge staged Commit + bob_group + .merge_staged_commit(backend, *staged_commit) + .await + .expect("Error merging staged commit."); + } else { + unreachable!("Expected a StagedCommit."); + } - // Check that Alice is the only member of the group - let members = alice_group.members().collect::>(); - assert_eq!(members[0].credential.identity(), b"Alice"); + // Check that Bob's group is no longer active + assert!(!bob_group.is_active()); - // === Re-Add Bob with external Add proposal === + // Make sure the group contains one member + assert_eq!(alice_group.members().count(), 1); - // Create a new KeyPackageBundle for Bob - let bob_key_package = generate_key_package( - ciphersuite, - bob_credential.clone(), - Extensions::default(), - backend, - &bob_signature_keys, - ) - .await; + // Check that Alice is the only member of the group + let members = alice_group.members().collect::>(); + assert_eq!(members[0].credential.identity(), b"Alice"); - // ANCHOR: external_join_proposal - let proposal = JoinProposal::new( - bob_key_package, - alice_group.group_id().clone(), - alice_group.epoch(), - &bob_signature_keys, - ) - .expect("Could not create external Add proposal"); - // ANCHOR_END: external_join_proposal + // === Re-Add Bob with external Add proposal === - // ANCHOR: decrypt_external_join_proposal - let alice_processed_message = alice_group - .process_message( + // Create a new KeyPackageBundle for Bob + let bob_key_package = generate_key_package( + ciphersuite, + bob_credential.clone(), + Extensions::default(), backend, - proposal - .into_protocol_message() - .expect("Unexpected message type."), + &bob_signature_keys, ) - .await - .expect("Could not process message."); - match alice_processed_message.into_content() { - ProcessedMessageContent::ExternalJoinProposalMessage(proposal) => { - alice_group.store_pending_proposal(*proposal); - let (_commit, welcome, _group_info) = alice_group - .commit_to_pending_proposals(backend, &alice_signature_keys) - .await - .expect("Could not commit"); - assert_eq!(alice_group.members().count(), 1); - alice_group - .merge_pending_commit(backend) - .await - .expect("Could not merge commit"); - assert_eq!(alice_group.members().count(), 2); + .await; + + // ANCHOR: external_join_proposal + let proposal = JoinProposal::new( + bob_key_package, + alice_group.group_id().clone(), + alice_group.epoch(), + &bob_signature_keys, + ) + .expect("Could not create external Add proposal"); + // ANCHOR_END: external_join_proposal - let bob_group = MlsGroup::new_from_welcome( + // ANCHOR: decrypt_external_join_proposal + let alice_processed_message = alice_group + .process_message( backend, - &mls_group_config, - welcome - .unwrap() - .into_welcome() + proposal + .into_protocol_message() .expect("Unexpected message type."), - None, ) .await - .expect("Bob could not join the group"); - assert_eq!(bob_group.members().count(), 2); + .expect("Could not process message."); + match alice_processed_message.into_content() { + ProcessedMessageContent::ExternalJoinProposalMessage(proposal) => { + alice_group.store_pending_proposal(*proposal); + let (_commit, welcome, _group_info) = alice_group + .commit_to_pending_proposals(backend, &alice_signature_keys) + .await + .expect("Could not commit"); + assert_eq!(alice_group.members().count(), 1); + alice_group + .merge_pending_commit(backend) + .await + .expect("Could not merge commit"); + assert_eq!(alice_group.members().count(), 2); + + let bob_group = MlsGroup::new_from_welcome( + backend, + &mls_group_config, + welcome + .unwrap() + .into_welcome() + .expect("Unexpected message type."), + None, + ) + .await + .expect("Bob could not join the group"); + assert_eq!(bob_group.members().count(), 2); + } + _ => unreachable!(), } - _ => unreachable!(), - } - // ANCHOR_END: decrypt_external_join_proposal - - // get Bob's index - let bob_index = alice_group - .members() - .find_map(|member| { - if member.credential.identity() == b"Bob" { - Some(member.index) - } else { - None + // ANCHOR_END: decrypt_external_join_proposal + + // get Bob's index + let bob_index = alice_group + .members() + .find_map(|member| { + if member.credential.identity() == b"Bob" { + Some(member.index) + } else { + None + } + }) + .unwrap(); + + // ANCHOR: external_remove_proposal + let proposal = ExternalProposal::new_remove( + bob_index, + alice_group.group_id().clone(), + alice_group.epoch(), + &ds_signature_keys, + SenderExtensionIndex::new(0), + ) + .expect("Could not create external Remove proposal"); + // ANCHOR_END: external_remove_proposal + + // ANCHOR: decrypt_external_join_proposal + let alice_processed_message = alice_group + .process_message( + backend, + proposal + .into_protocol_message() + .expect("Unexpected message type."), + ) + .await + .expect("Could not process message."); + match alice_processed_message.into_content() { + ProcessedMessageContent::ProposalMessage(proposal) => { + alice_group.store_pending_proposal(*proposal); + assert_eq!(alice_group.members().count(), 2); + alice_group + .commit_to_pending_proposals(backend, &alice_signature_keys) + .await + .expect("Could not commit"); + alice_group + .merge_pending_commit(backend) + .await + .expect("Could not merge commit"); + assert_eq!(alice_group.members().count(), 1); } - }) - .unwrap(); - - // ANCHOR: external_remove_proposal - let proposal = ExternalProposal::new_remove( - bob_index, - alice_group.group_id().clone(), - alice_group.epoch(), - &ds_signature_keys, - SenderExtensionIndex::new(0), - ) - .expect("Could not create external Remove proposal"); - // ANCHOR_END: external_remove_proposal + _ => unreachable!(), + } + // ANCHOR_END: decrypt_external_join_proposal + + // === Save the group state === - // ANCHOR: decrypt_external_join_proposal - let alice_processed_message = alice_group - .process_message( + // Create a new KeyPackageBundle for Bob + let bob_key_package = generate_key_package( + ciphersuite, + bob_credential, + Extensions::default(), backend, - proposal - .into_protocol_message() - .expect("Unexpected message type."), + &bob_signature_keys, ) - .await - .expect("Could not process message."); - match alice_processed_message.into_content() { - ProcessedMessageContent::ProposalMessage(proposal) => { - alice_group.store_pending_proposal(*proposal); - assert_eq!(alice_group.members().count(), 2); - alice_group - .commit_to_pending_proposals(backend, &alice_signature_keys) - .await - .expect("Could not commit"); - alice_group - .merge_pending_commit(backend) - .await - .expect("Could not merge commit"); - assert_eq!(alice_group.members().count(), 1); - } - _ => unreachable!(), - } - // ANCHOR_END: decrypt_external_join_proposal - - // === Save the group state === + .await; - // Create a new KeyPackageBundle for Bob - let bob_key_package = generate_key_package( - ciphersuite, - bob_credential, - Extensions::default(), - backend, - &bob_signature_keys, - ) - .await; + // Add Bob to the group + let (_queued_message, welcome, _group_info) = alice_group + .add_members(backend, &alice_signature_keys, vec![bob_key_package.into()]) + .await + .expect("Could not add Bob"); - // Add Bob to the group - let (_queued_message, welcome, _group_info) = alice_group - .add_members(backend, &alice_signature_keys, vec![bob_key_package.into()]) - .await - .expect("Could not add Bob"); + // Merge Commit + alice_group + .merge_pending_commit(backend) + .await + .expect("error merging pending commit"); - // Merge Commit - alice_group - .merge_pending_commit(backend) + let mut bob_group = MlsGroup::new_from_welcome( + backend, + &mls_group_config, + welcome.into_welcome().expect("Unexpected message type."), + Some(alice_group.export_ratchet_tree().into()), + ) .await - .expect("error merging pending commit"); + .expect("Could not create group from Welcome"); - let mut bob_group = MlsGroup::new_from_welcome( - backend, - &mls_group_config, - welcome.into_welcome().expect("Unexpected message type."), - Some(alice_group.export_ratchet_tree().into()), - ) - .await - .expect("Could not create group from Welcome"); - - assert_eq!( - alice_group.export_secret(backend, "before load", &[], 32), - bob_group.export_secret(backend, "before load", &[], 32) - ); + assert_eq!( + alice_group.export_secret(backend, "before load", &[], 32), + bob_group.export_secret(backend, "before load", &[], 32) + ); - // Check that the state flag gets reset when saving - assert_eq!(bob_group.state_changed(), InnerState::Changed); - //save(&mut bob_group); + // Check that the state flag gets reset when saving + assert_eq!(bob_group.state_changed(), InnerState::Changed); + //save(&mut bob_group); - bob_group - .save(backend) - .await - .expect("Could not write group state to file"); + bob_group + .save(backend) + .await + .expect("Could not write group state to file"); - // Check that the state flag gets reset when saving - assert_eq!(bob_group.state_changed(), InnerState::Persisted); + // Check that the state flag gets reset when saving + assert_eq!(bob_group.state_changed(), InnerState::Persisted); - let bob_group = MlsGroup::load(&group_id, backend) - .await - .expect("Could not load group from file"); + let bob_group = MlsGroup::load(&group_id, backend) + .await + .expect("Could not load group from file"); - // Make sure the state is still the same - assert_eq!( - alice_group.export_secret(backend, "after load", &[], 32), - bob_group.export_secret(backend, "after load", &[], 32) - ); + // Make sure the state is still the same + assert_eq!( + alice_group.export_secret(backend, "after load", &[], 32), + bob_group.export_secret(backend, "after load", &[], 32) + ); + }) + .await; } #[apply(ciphersuites_and_backends)] diff --git a/openmls/tests/key_store.rs b/openmls/tests/key_store.rs index 3bb333e5f3..cf64399df1 100644 --- a/openmls/tests/key_store.rs +++ b/openmls/tests/key_store.rs @@ -7,11 +7,9 @@ async fn test_store_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMls // ANCHOR: key_store_store // First we generate a credential and key package for our user. let credential = Credential::new_basic(b"User ID".to_vec()); - let signature_keys = SignatureKeyPair::new( - ciphersuite.into(), - &mut *backend.rand().borrow_rand().unwrap(), - ) - .unwrap(); + let signature_keys = + SignatureKeyPair::new(ciphersuite.into(), &mut *backend.rand().borrow_rand().await) + .unwrap(); let key_package = KeyPackage::builder() .build( diff --git a/openmls/tests/test_external_commit.rs b/openmls/tests/test_external_commit.rs index c390b14c8a..4da572011f 100644 --- a/openmls/tests/test_external_commit.rs +++ b/openmls/tests/test_external_commit.rs @@ -176,6 +176,7 @@ async fn test_group_info(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP // bob sends a message to alice let message: MlsMessageIn = bob_group .create_message(backend, &bob_signature_keys, b"Hello Alice") + .await .unwrap() .into(); diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index bc12914ac0..b27dffde37 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -43,6 +43,7 @@ async fn generate_key_package( /// - Test saving the group state #[apply(ciphersuites_and_backends)] async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProvider) { + Box::pin(async { for wire_format_policy in WIRE_FORMAT_POLICIES.iter() { let group_id = GroupId::from_slice(b"Test Group"); @@ -147,6 +148,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let message_alice = b"Hi, I'm Alice!"; let queued_message = alice_group .create_message(backend, &alice_signer, message_alice) + .await .expect("Error creating application message"); let processed_message = bob_group @@ -386,6 +388,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let message_charlie = b"Hi, I'm Charlie!"; let queued_message = charlie_group .create_message(backend, &charlie_signer, message_charlie) + .await .expect("Error creating application message"); let _alice_processed_message = alice_group @@ -593,6 +596,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // Check that Bob can no longer send messages assert!(bob_group .create_message(backend, &bob_signer, b"Should not go through") + .await .is_err()); // === Alice removes Charlie and re-adds Bob === @@ -610,6 +614,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // Create RemoveProposal and process it let (queued_message, _) = alice_group .propose_remove_member(backend, &alice_signer, charlie_group.own_leaf_index()) + .await .expect("Could not create proposal to remove Charlie"); let charlie_processed_message = charlie_group @@ -763,6 +768,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let message_alice = b"Hi, I'm Alice!"; let queued_message = alice_group .create_message(backend, &alice_signer, message_alice) + .await .expect("Error creating application message"); let bob_processed_message = bob_group @@ -796,6 +802,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let queued_message = bob_group .leave_group(backend, &bob_signer) + .await .expect("Could not leave group"); let alice_processed_message = alice_group @@ -972,6 +979,8 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr bob_group.export_secret(backend, "after load", &[], 32) ); } +}) +.await; } #[apply(ciphersuites_and_backends)] diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index 13c5aced93..a94424660a 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -31,3 +31,4 @@ signature = "2.1" thiserror = "1.0" generic-array = "0.14" hpke = { version = "0.12", features = ["x25519", "p256", "p384", "p521"] } +async-lock = "2.8.0" diff --git a/openmls_rust_crypto/src/provider.rs b/openmls_rust_crypto/src/provider.rs index 7992a4ecef..5aaf0bf08c 100644 --- a/openmls_rust_crypto/src/provider.rs +++ b/openmls_rust_crypto/src/provider.rs @@ -1,5 +1,5 @@ +use async_lock::RwLock; use rand_core::{RngCore, SeedableRng}; -use std::sync::RwLock; use aes_gcm::{ aead::{Aead, Payload}, @@ -89,6 +89,8 @@ fn normalize_p521_secret_key(sk: &[u8]) -> zeroize::Zeroizing<[u8; 66]> { sk_buf } +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl OpenMlsCrypto for RustCrypto { fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> { match ciphersuite { @@ -248,14 +250,11 @@ impl OpenMlsCrypto for RustCrypto { } } - fn signature_key_gen( + async fn signature_key_gen( &self, alg: openmls_traits::types::SignatureScheme, ) -> Result<(Vec, Vec), openmls_traits::types::CryptoError> { - let mut rng = self - .rng - .write() - .map_err(|_| CryptoError::InsufficientRandomness)?; + let mut rng = self.rng.write().await; match alg { SignatureScheme::ECDSA_SECP256R1_SHA256 => { @@ -277,10 +276,7 @@ impl OpenMlsCrypto for RustCrypto { Ok((sk.to_bytes().to_vec(), pk)) } SignatureScheme::ED25519 => { - let mut rng = self - .rng - .write() - .map_err(|_| CryptoError::InsufficientRandomness)?; + let mut rng = self.rng.write().await; let k = ed25519_dalek::SigningKey::generate(&mut *rng); let pk = k.verifying_key(); Ok((k.to_bytes().into(), pk.to_bytes().into())) @@ -422,7 +418,7 @@ impl OpenMlsCrypto for RustCrypto { } } - fn hpke_seal( + async fn hpke_seal( &self, config: HpkeConfig, pk_r: &[u8], @@ -430,10 +426,7 @@ impl OpenMlsCrypto for RustCrypto { aad: &[u8], ptxt: &[u8], ) -> Result { - let mut rng = self - .rng - .write() - .map_err(|_| CryptoError::InsufficientRandomness)?; + let mut rng = self.rng.write().await; match config { HpkeConfig( @@ -575,7 +568,7 @@ impl OpenMlsCrypto for RustCrypto { Ok(plaintext) } - fn hpke_setup_sender_and_export( + async fn hpke_setup_sender_and_export( &self, config: HpkeConfig, pk_r: &[u8], @@ -583,10 +576,7 @@ impl OpenMlsCrypto for RustCrypto { exporter_context: &[u8], exporter_length: usize, ) -> Result<(Vec, ExporterSecret), CryptoError> { - let mut rng = self - .rng - .write() - .map_err(|_| CryptoError::InsufficientRandomness)?; + let mut rng = self.rng.write().await; let (kem_output, export) = match config { HpkeConfig( @@ -859,26 +849,28 @@ mod hpke_core { } } +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl OpenMlsRand for RustCrypto { type Error = RandError; type RandImpl = rand_chacha::ChaCha20Rng; - type BorrowTarget<'a> = std::sync::RwLockWriteGuard<'a, Self::RandImpl>; + type BorrowTarget<'a> = async_lock::RwLockWriteGuard<'a, Self::RandImpl>; - fn borrow_rand(&self) -> Result, Self::Error> { - self.rng.write().map_err(|_| Self::Error::LockPoisoned) + async fn borrow_rand(&self) -> Self::BorrowTarget<'_> { + self.rng.write().await } - fn random_array(&self) -> Result<[u8; N], Self::Error> { - let mut rng = self.borrow_rand()?; + async fn random_array(&self) -> Result<[u8; N], Self::Error> { + let mut rng = self.borrow_rand().await; let mut out = [0u8; N]; rng.try_fill_bytes(&mut out) .map_err(|_| Self::Error::NotEnoughRandomness)?; Ok(out) } - fn random_vec(&self, len: usize) -> Result, Self::Error> { - let mut rng = self.borrow_rand()?; + async fn random_vec(&self, len: usize) -> Result, Self::Error> { + let mut rng = self.borrow_rand().await; let mut out = vec![0u8; len]; rng.try_fill_bytes(&mut out) .map_err(|_| Self::Error::NotEnoughRandomness)?; diff --git a/traits/src/crypto.rs b/traits/src/crypto.rs index 20c69e6f34..68386da3cc 100644 --- a/traits/src/crypto.rs +++ b/traits/src/crypto.rs @@ -9,6 +9,8 @@ use crate::types::{ HpkeKeyPair, KemOutput, SignatureScheme, }; +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] pub trait OpenMlsCrypto { /// Check whether the [`Ciphersuite`] is supported by the backend or not. /// @@ -75,7 +77,10 @@ pub trait OpenMlsCrypto { /// /// Returns an error if the [`SignatureScheme`] is not supported or the key /// generation fails. - fn signature_key_gen(&self, alg: SignatureScheme) -> Result<(Vec, Vec), CryptoError>; + async fn signature_key_gen( + &self, + alg: SignatureScheme, + ) -> Result<(Vec, Vec), CryptoError>; /// Gives the length of a signature public key, in bytes fn signature_public_key_len(&self, alg: SignatureScheme) -> usize; @@ -109,7 +114,7 @@ pub trait OpenMlsCrypto { // === HPKE === // /// HPKE single-shot encryption of `ptxt` to `pk_r`, using `info` and `aad`. - fn hpke_seal( + async fn hpke_seal( &self, config: HpkeConfig, pk_r: &[u8], @@ -132,7 +137,7 @@ pub trait OpenMlsCrypto { /// HPKE single-shot setup of a sender and immediate export a secret. /// /// The encapsulated secret is returned together with the exported secret. - fn hpke_setup_sender_and_export( + async fn hpke_setup_sender_and_export( &self, config: HpkeConfig, pk_r: &[u8], diff --git a/traits/src/random.rs b/traits/src/random.rs index 5b56c69544..9e0b1e029a 100644 --- a/traits/src/random.rs +++ b/traits/src/random.rs @@ -3,6 +3,8 @@ //! The [`OpenMlsRand`] trait defines the functionality required by OpenMLS to //! source randomness. +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] pub trait OpenMlsRand { type Error: std::error::Error + std::fmt::Debug; type RandImpl: rand_core::CryptoRngCore; @@ -10,11 +12,11 @@ pub trait OpenMlsRand { where Self: 'a; - fn borrow_rand(&self) -> Result, Self::Error>; + async fn borrow_rand(&self) -> Self::BorrowTarget<'_>; /// Fill an array with random bytes. - fn random_array(&self) -> Result<[u8; N], Self::Error>; + async fn random_array(&self) -> Result<[u8; N], Self::Error>; /// Fill a vector of length `len` with bytes. - fn random_vec(&self, len: usize) -> Result, Self::Error>; + async fn random_vec(&self, len: usize) -> Result, Self::Error>; }