From 77ca9512d7ec8abca0d68965e4e5c26635b2d48d Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Wed, 23 Oct 2024 17:07:47 +0200 Subject: [PATCH] style: run cargo fmt --- crypto-ffi/src/generic/context/mod.rs | 16 +- crypto-ffi/src/generic/context/proteus.rs | 2 +- crypto-ffi/src/wasm/context/mod.rs | 4 +- crypto-ffi/src/wasm/context/proteus.rs | 54 +- crypto-ffi/src/wasm/mod.rs | 2 +- crypto/src/context.rs | 12 +- crypto/src/e2e_identity/conversation_state.rs | 229 ++--- crypto/src/e2e_identity/enabled.rs | 7 +- crypto/src/e2e_identity/identity.rs | 31 +- crypto/src/e2e_identity/init_certificates.rs | 36 +- crypto/src/e2e_identity/mod.rs | 4 +- crypto/src/e2e_identity/rotate.rs | 101 +- crypto/src/e2e_identity/stash.rs | 8 +- crypto/src/lib.rs | 4 +- crypto/src/mls/buffer_external_commit.rs | 20 +- crypto/src/mls/client/identities.rs | 85 +- crypto/src/mls/client/key_package.rs | 16 +- crypto/src/mls/client/mod.rs | 19 +- .../src/mls/conversation/buffer_messages.rs | 37 +- crypto/src/mls/conversation/commit.rs | 834 +++++++--------- crypto/src/mls/conversation/commit_delay.rs | 36 +- crypto/src/mls/conversation/config.rs | 14 +- crypto/src/mls/conversation/db_count.rs | 2 +- crypto/src/mls/conversation/decrypt.rs | 905 ++++++++---------- crypto/src/mls/conversation/duplicate.rs | 397 ++++---- crypto/src/mls/conversation/encrypt.rs | 131 ++- crypto/src/mls/conversation/export.rs | 40 +- .../src/mls/conversation/external_sender.rs | 2 +- .../mls/conversation/leaf_node_validation.rs | 115 +-- crypto/src/mls/conversation/merge.rs | 75 +- crypto/src/mls/conversation/mod.rs | 146 ++- crypto/src/mls/conversation/orphan_welcome.rs | 74 +- crypto/src/mls/conversation/own_commit.rs | 123 ++- crypto/src/mls/conversation/proposal.rs | 285 +++--- crypto/src/mls/conversation/renew.rs | 153 +-- crypto/src/mls/conversation/welcome.rs | 138 ++- crypto/src/mls/conversation/wipe.rs | 2 +- crypto/src/mls/credential/crl.rs | 2 +- crypto/src/mls/credential/mod.rs | 33 +- crypto/src/mls/external_commit.rs | 800 +++++++--------- crypto/src/mls/external_proposal.rs | 105 +- crypto/src/mls/mod.rs | 55 +- crypto/src/mls/proposal.rs | 102 +- crypto/src/test_utils/central.rs | 95 +- crypto/src/test_utils/mod.rs | 30 +- crypto/src/test_utils/x509.rs | 4 +- interop/src/clients/corecrypto/native.rs | 1 - interop/src/main.rs | 4 +- keystore/src/connection/mod.rs | 48 +- keystore/src/entities/mls.rs | 25 +- keystore/src/entities/mod.rs | 2 +- .../entities/platform/generic/mls/e2ei_crl.rs | 5 +- .../generic/mls/e2ei_intermediate_cert.rs | 5 +- .../generic/mls/encryption_keypair.rs | 5 +- .../platform/generic/mls/enrollment.rs | 5 +- .../generic/mls/epoch_encryption_keypair.rs | 5 +- .../entities/platform/generic/mls/group.rs | 5 +- .../platform/generic/mls/hpke_private_key.rs | 5 +- .../platform/generic/mls/keypackage.rs | 5 +- .../platform/generic/mls/pending_group.rs | 5 +- .../platform/generic/mls/pending_message.rs | 19 +- .../platform/generic/mls/psk_bundle.rs | 5 +- .../platform/generic/mls/signature_keypair.rs | 5 +- .../platform/generic/proteus/identity.rs | 8 +- .../platform/generic/proteus/prekey.rs | 7 +- .../platform/generic/proteus/session.rs | 7 +- .../entities/platform/wasm/mls/credential.rs | 4 +- .../entities/platform/wasm/mls/enrollment.rs | 3 +- .../platform/wasm/mls/pending_message.rs | 4 +- .../platform/wasm/proteus/identity.rs | 4 +- keystore/src/transaction.rs | 4 +- keystore/tests/common.rs | 17 +- keystore/tests/proteus.rs | 3 +- keystore/tests/z_entities.rs | 2 +- mls-provider/src/lib.rs | 2 +- 75 files changed, 2378 insertions(+), 3226 deletions(-) diff --git a/crypto-ffi/src/generic/context/mod.rs b/crypto-ffi/src/generic/context/mod.rs index 9e7e732de..462a27074 100644 --- a/crypto-ffi/src/generic/context/mod.rs +++ b/crypto-ffi/src/generic/context/mod.rs @@ -1,5 +1,9 @@ -use std::{ops::Deref, sync::Arc}; -use std::sync::atomic::AtomicU32; +use super::{ + BufferedDecryptedMessage, Ciphersuite, Ciphersuites, ClientId, CommitBundle, ConversationConfiguration, + ConversationInitBundle, CoreCrypto, CoreCryptoError, CoreCryptoResult, CustomConfiguration, DecryptedMessage, + MemberAddedMessages, MlsCredentialType, ProposalBundle, WelcomeBundle, +}; +use core_crypto::context::CentralContext; use core_crypto::{ prelude::{ ClientIdentifier, ConversationId, KeyPackageIn, KeyPackageRef, MlsConversationConfiguration, @@ -7,13 +11,9 @@ use core_crypto::{ }, CryptoError, MlsError, }; +use std::sync::atomic::AtomicU32; +use std::{ops::Deref, sync::Arc}; use tls_codec::{Deserialize, Serialize}; -use core_crypto::context::CentralContext; -use super::{ - BufferedDecryptedMessage, Ciphersuite, Ciphersuites, ClientId, CommitBundle, ConversationConfiguration, - ConversationInitBundle, CoreCrypto, CoreCryptoError, CoreCryptoResult, CustomConfiguration, DecryptedMessage, - MemberAddedMessages, MlsCredentialType, ProposalBundle, WelcomeBundle, -}; pub mod e2ei; pub mod proteus; diff --git a/crypto-ffi/src/generic/context/proteus.rs b/crypto-ffi/src/generic/context/proteus.rs index a73815002..cd2c5ece5 100644 --- a/crypto-ffi/src/generic/context/proteus.rs +++ b/crypto-ffi/src/generic/context/proteus.rs @@ -1,7 +1,7 @@ use crate::context::CoreCryptoContext; use crate::generic::CoreCryptoResult; -use crate::{CoreCryptoError, ProteusAutoPrekeyBundle}; use crate::proteus_impl; +use crate::{CoreCryptoError, ProteusAutoPrekeyBundle}; #[uniffi::export] impl CoreCryptoContext { diff --git a/crypto-ffi/src/wasm/context/mod.rs b/crypto-ffi/src/wasm/context/mod.rs index 46f7fd4dd..996a2a439 100644 --- a/crypto-ffi/src/wasm/context/mod.rs +++ b/crypto-ffi/src/wasm/context/mod.rs @@ -4,6 +4,7 @@ use crate::{ CoreCryptoError, CoreCryptoResult, CredentialType, CustomConfiguration, DecryptedMessage, FfiClientId, MemberAddedMessages, ProposalBundle, WasmCryptoResult, WelcomeBundle, }; +use core_crypto::context::CentralContext; use core_crypto::prelude::{ CiphersuiteName, ClientId, ClientIdentifier, ConversationId, KeyPackageIn, KeyPackageRef, MlsConversationConfiguration, VerifiableGroupInfo, @@ -16,7 +17,6 @@ use tls_codec::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use wasm_bindgen_futures::future_to_promise; -use core_crypto::context::CentralContext; mod e2ei; mod proteus; @@ -25,7 +25,7 @@ mod proteus; #[derive(Clone)] pub struct CoreCryptoContext { pub(crate) inner: Arc, - pub(crate) proteus_last_error_code: Arc> + pub(crate) proteus_last_error_code: Arc>, } #[wasm_bindgen] diff --git a/crypto-ffi/src/wasm/context/proteus.rs b/crypto-ffi/src/wasm/context/proteus.rs index 17b62a527..afbb62fad 100644 --- a/crypto-ffi/src/wasm/context/proteus.rs +++ b/crypto-ffi/src/wasm/context/proteus.rs @@ -1,13 +1,13 @@ +use crate::proteus_impl; +use crate::wasm::context::CoreCryptoContext; +use crate::wasm::CoreCryptoError; +use crate::WasmError; +use crate::{ProteusAutoPrekeyBundle, WasmCryptoResult}; use futures_util::TryFutureExt; use js_sys::{Promise, Uint8Array}; -use wasm_bindgen::JsValue; use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; use wasm_bindgen_futures::future_to_promise; -use crate::wasm::context::CoreCryptoContext; -use crate::{ProteusAutoPrekeyBundle, WasmCryptoResult}; -use crate::WasmError; -use crate::proteus_impl; -use crate::wasm::CoreCryptoError; #[wasm_bindgen] impl CoreCryptoContext { @@ -25,10 +25,10 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(JsValue::UNDEFINED) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } - + /// Returns: [`WasmCryptoResult>`] /// /// See [core_crypto::context::CentralContext::proteus_session_from_message] @@ -43,14 +43,14 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(Uint8Array::from(payload.as_slice()).into()) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } - + /// Returns: [`WasmCryptoResult<()>`] - /// + /// /// /// **Note**: This isn't usually needed as persisting sessions happens automatically when decrypting/encrypting messages and initializing Sessions - /// + /// /// See [core_crypto::context::CentralContext::proteus_session_save] pub fn proteus_session_save(&self, session_id: String) -> Promise { let errcode_dest = self.proteus_last_error_code.clone(); @@ -63,10 +63,10 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(JsValue::UNDEFINED) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } - + /// Returns: [`WasmCryptoResult<()>`] /// /// See [core_crypto::context::CentralContext::proteus_session_delete] @@ -81,10 +81,10 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(JsValue::UNDEFINED) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } - + /// Returns: [`WasmCryptoResult`] /// /// See [core_crypto::context::CentralContext::proteus_session_exists] @@ -99,12 +99,12 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(JsValue::from_bool(exists)) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } /// Returns: [`WasmCryptoResult>`] - /// + /// /// See [core_crypto::context::CentralContext::proteus_decrypt] pub fn proteus_decrypt(&self, session_id: String, ciphertext: Box<[u8]>) -> Promise { let errcode_dest = self.proteus_last_error_code.clone(); @@ -166,7 +166,7 @@ impl CoreCryptoContext { pub fn proteus_new_prekey(&self, prekey_id: u16) -> Promise { let errcode_dest = self.proteus_last_error_code.clone(); let context = self.inner.clone(); - + future_to_promise( async move { proteus_impl! { errcode_dest => { @@ -174,12 +174,12 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(Uint8Array::from(prekey_raw.as_slice()).into()) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } /// Returns: [`WasmCryptoResult`] - /// + /// /// See [core_crypto::context::CentralContext::proteus_new_prekey_auto] pub fn proteus_new_prekey_auto(&self) -> Promise { let errcode_dest = self.proteus_last_error_code.clone(); @@ -191,12 +191,12 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(ProteusAutoPrekeyBundle { id, pkb }.into()) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } /// Returns [`WasmCryptoResult`] - /// + /// /// See [core_crypto::context::CentralContext::proteus_last_resort_prekey] pub fn proteus_last_resort_prekey(&self) -> Promise { let errcode_dest = self.proteus_last_error_code.clone(); @@ -283,12 +283,12 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(JsValue::UNDEFINED) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } /// Returns: [`WasmCryptoResult`] - /// + /// /// NOTE: This will clear the last error code. pub fn proteus_last_error_code(&self) -> Promise { let errcode = self.proteus_last_error_code.clone(); @@ -302,7 +302,7 @@ impl CoreCryptoContext { WasmCryptoResult::Ok(prev_value.into()) } or throw WasmCryptoResult<_> } } - .err_into(), + .err_into(), ) } -} \ No newline at end of file +} diff --git a/crypto-ffi/src/wasm/mod.rs b/crypto-ffi/src/wasm/mod.rs index 8b219d76c..5d3d86dd2 100644 --- a/crypto-ffi/src/wasm/mod.rs +++ b/crypto-ffi/src/wasm/mod.rs @@ -21,6 +21,7 @@ mod utils; use std::collections::HashMap; use std::ops::Deref; +use crate::proteus_impl; use core_crypto::prelude::*; use core_crypto::CryptoError; use futures_util::future::TryFutureExt; @@ -34,7 +35,6 @@ use utils::*; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; -use crate::proteus_impl; #[allow(dead_code)] pub(super) const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/crypto/src/context.rs b/crypto/src/context.rs index e3626e687..6d92ec650 100644 --- a/crypto/src/context.rs +++ b/crypto/src/context.rs @@ -1,13 +1,17 @@ //! This module contains the primitives to enable transactional support on a higher level within the //! [MlsCentral]. All mutating operations need to be done through a [CentralContext]. -use std::{ops::Deref, sync::Arc}; use async_lock::{Mutex, RwLock, RwLockReadGuardArc, RwLockWriteGuardArc}; use mls_crypto_provider::{CryptoKeystore, TransactionalCryptoProvider}; +use std::{ops::Deref, sync::Arc}; use crate::mls::MlsCentral; -use crate::{group_store::GroupStore, prelude::{Client, MlsConversation}, CoreCrypto, CoreCryptoCallbacks, CryptoError, CryptoResult}; use crate::proteus::ProteusCentral; +use crate::{ + group_store::GroupStore, + prelude::{Client, MlsConversation}, + CoreCrypto, CoreCryptoCallbacks, CryptoError, CryptoResult, +}; /// This struct provides transactional support for Core Crypto. /// @@ -57,7 +61,7 @@ impl CentralContext { callbacks, provider: mls_central.mls_backend.clone(), mls_groups, - proteus_central + proteus_central, } .into(), ), @@ -122,7 +126,7 @@ impl CentralContext { ContextState::Invalid => Err(CryptoError::InvalidContext), } } - + pub(crate) async fn proteus_central(&self) -> CryptoResult>>> { match self.state.read().await.deref() { ContextState::Valid { proteus_central, .. } => Ok(proteus_central.clone()), diff --git a/crypto/src/e2e_identity/conversation_state.rs b/crypto/src/e2e_identity/conversation_state.rs index a0902b592..212ec3bd5 100644 --- a/crypto/src/e2e_identity/conversation_state.rs +++ b/crypto/src/e2e_identity/conversation_state.rs @@ -248,58 +248,51 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn uniform_conversation_should_be_not_verified_when_basic(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - - // That way the conversation creator (Alice) will have the same credential type as Bob - let creator_ct = case.credential_type; - alice_central - .context - .new_conversation(&id, creator_ct, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - match case.credential_type { - MlsCredentialType::Basic => { - let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); - let bob_state = bob_central.context.e2ei_conversation_state(&id).await.unwrap(); - assert_eq!(alice_state, E2eiConversationState::NotEnabled); - assert_eq!(bob_state, E2eiConversationState::NotEnabled); - - let gi = alice_central.get_group_info(&id).await; - let state = alice_central - .context - .get_credential_in_use(gi, MlsCredentialType::X509) - .await - .unwrap(); - assert_eq!(state, E2eiConversationState::NotEnabled); - } - MlsCredentialType::X509 => { - let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); - let bob_state = bob_central.context.e2ei_conversation_state(&id).await.unwrap(); - assert_eq!(alice_state, E2eiConversationState::Verified); - assert_eq!(bob_state, E2eiConversationState::Verified); - - let gi = alice_central.get_group_info(&id).await; - let state = alice_central - .context - .get_credential_in_use(gi, MlsCredentialType::X509) - .await - .unwrap(); - assert_eq!(state, E2eiConversationState::Verified); - } + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + + // That way the conversation creator (Alice) will have the same credential type as Bob + let creator_ct = case.credential_type; + alice_central + .context + .new_conversation(&id, creator_ct, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + match case.credential_type { + MlsCredentialType::Basic => { + let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); + let bob_state = bob_central.context.e2ei_conversation_state(&id).await.unwrap(); + assert_eq!(alice_state, E2eiConversationState::NotEnabled); + assert_eq!(bob_state, E2eiConversationState::NotEnabled); + + let gi = alice_central.get_group_info(&id).await; + let state = alice_central + .context + .get_credential_in_use(gi, MlsCredentialType::X509) + .await + .unwrap(); + assert_eq!(state, E2eiConversationState::NotEnabled); } - }) - }, - ) + MlsCredentialType::X509 => { + let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); + let bob_state = bob_central.context.e2ei_conversation_state(&id).await.unwrap(); + assert_eq!(alice_state, E2eiConversationState::Verified); + assert_eq!(bob_state, E2eiConversationState::Verified); + + let gi = alice_central.get_group_info(&id).await; + let state = alice_central + .context + .get_credential_in_use(gi, MlsCredentialType::X509) + .await + .unwrap(); + assert_eq!(state, E2eiConversationState::Verified); + } + } + }) + }) .await } @@ -351,10 +344,7 @@ mod tests { .new_conversation(&id, creator_ct, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // since in that case both have a different credential type the conversation is always not verified let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); @@ -381,77 +371,70 @@ mod tests { if !case.is_x509() { return; } - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let expiration_time = core::time::Duration::from_secs(14); - let start = fluvio_wasm_timer::Instant::now(); - - let cert = CertificateBundle::new_with_default_values( - alice_central - .x509_test_chain - .as_ref() - .as_ref() - .expect("No x509 test chain") - .find_local_intermediate_ca(), - Some(expiration_time), - ); - let cb = Client::new_x509_credential_bundle(cert.clone()).unwrap(); - let commit = alice_central.context.e2ei_rotate(&id, Some(&cb)).await.unwrap().commit; - alice_central.context.commit_accepted(&id).await.unwrap(); - bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - - let mut alice_client_guard = alice_central.context.mls_client_mut().await.unwrap(); - let alice_client = alice_client_guard.as_mut().unwrap(); - let alice_provider = alice_central.context.mls_provider().await.unwrap(); - // Needed because 'e2ei_rotate' does not do it directly and it's required for 'get_group_info' - alice_client - .save_new_x509_credential_bundle(&alice_provider.keystore(), case.signature_scheme(), cert) - .await - .unwrap(); - - drop(alice_client_guard); - // Need to fetch it before it becomes invalid & expires - let gi = alice_central.get_group_info(&id).await; + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let elapsed = start.elapsed(); - // Give time to the certificate to expire - if expiration_time > elapsed { - async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1)).await; - } + let expiration_time = core::time::Duration::from_secs(14); + let start = fluvio_wasm_timer::Instant::now(); - let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); - let bob_state = bob_central.context.e2ei_conversation_state(&id).await.unwrap(); - assert_eq!(alice_state, E2eiConversationState::NotVerified); - assert_eq!(bob_state, E2eiConversationState::NotVerified); - - let state = alice_central - .context - .get_credential_in_use(gi, MlsCredentialType::X509) - .await - .unwrap(); - assert_eq!(state, E2eiConversationState::NotVerified); - }) - }, - ) + let cert = CertificateBundle::new_with_default_values( + alice_central + .x509_test_chain + .as_ref() + .as_ref() + .expect("No x509 test chain") + .find_local_intermediate_ca(), + Some(expiration_time), + ); + let cb = Client::new_x509_credential_bundle(cert.clone()).unwrap(); + let commit = alice_central.context.e2ei_rotate(&id, Some(&cb)).await.unwrap().commit; + alice_central.context.commit_accepted(&id).await.unwrap(); + bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + + let mut alice_client_guard = alice_central.context.mls_client_mut().await.unwrap(); + let alice_client = alice_client_guard.as_mut().unwrap(); + let alice_provider = alice_central.context.mls_provider().await.unwrap(); + // Needed because 'e2ei_rotate' does not do it directly and it's required for 'get_group_info' + alice_client + .save_new_x509_credential_bundle(&alice_provider.keystore(), case.signature_scheme(), cert) + .await + .unwrap(); + + drop(alice_client_guard); + // Need to fetch it before it becomes invalid & expires + let gi = alice_central.get_group_info(&id).await; + + let elapsed = start.elapsed(); + // Give time to the certificate to expire + if expiration_time > elapsed { + async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1)).await; + } + + let alice_state = alice_central.context.e2ei_conversation_state(&id).await.unwrap(); + let bob_state = bob_central.context.e2ei_conversation_state(&id).await.unwrap(); + assert_eq!(alice_state, E2eiConversationState::NotVerified); + assert_eq!(bob_state, E2eiConversationState::NotVerified); + + let state = alice_central + .context + .get_credential_in_use(gi, MlsCredentialType::X509) + .await + .unwrap(); + assert_eq!(state, E2eiConversationState::NotVerified); + }) + }) .await } diff --git a/crypto/src/e2e_identity/enabled.rs b/crypto/src/e2e_identity/enabled.rs index 447047aef..7782bb764 100644 --- a/crypto/src/e2e_identity/enabled.rs +++ b/crypto/src/e2e_identity/enabled.rs @@ -26,10 +26,13 @@ impl MlsCentral { impl Client { async fn e2ei_is_enabled(&self, signature_scheme: SignatureScheme) -> CryptoResult { - let maybe_x509 = self.find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::X509).await; + let maybe_x509 = self + .find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::X509) + .await; match maybe_x509 { None => { - self.find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::Basic).await + self.find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::Basic) + .await .ok_or(CryptoError::CredentialNotFound(MlsCredentialType::Basic))?; Ok(false) } diff --git a/crypto/src/e2e_identity/identity.rs b/crypto/src/e2e_identity/identity.rs index 97692fdb2..d9d856a43 100644 --- a/crypto/src/e2e_identity/identity.rs +++ b/crypto/src/e2e_identity/identity.rs @@ -5,8 +5,8 @@ use itertools::Itertools; use openmls_traits::OpenMlsCryptoProvider; use x509_cert::der::pem::LineEnding; -use crate::e2e_identity::id::WireQualifiedClientId; use crate::context::CentralContext; +use crate::e2e_identity::id::WireQualifiedClientId; use crate::mls::credential::ext::CredentialExt; use crate::prelude::MlsCredentialType; use crate::{ @@ -210,13 +210,13 @@ impl MlsConversation { mod tests { use wasm_bindgen_test::*; + use crate::context::CentralContext; use crate::prelude::{ClientId, ConversationId, MlsCredentialType}; use crate::{ prelude::{DeviceStatus, E2eiConversationState}, test_utils::*, CryptoError, }; - use crate::context::CentralContext; wasm_bindgen_test_configure!(run_in_browser); @@ -359,16 +359,7 @@ mod tests { .await .unwrap(); alice - .invite_all( - &case, - &id, - [ - &bob, - &rupert, - &dilbert, - &john, - ], - ) + .invite_all(&case, &id, [&bob, &rupert, &dilbert, &john]) .await .unwrap(); @@ -419,10 +410,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice - .invite_all(&case, &id, [&bob, &rupert]) - .await - .unwrap(); + alice.invite_all(&case, &id, [&bob, &rupert]).await.unwrap(); let (alice_id, bob_id, rupert_id) = ( alice.get_client_id().await, @@ -639,11 +627,7 @@ mod tests { .await .unwrap(); alice_android_central - .invite_all( - &case, - &id, - [&alice_ios_central, &bob_android_central], - ) + .invite_all(&case, &id, [&alice_ios_central, &bob_android_central]) .await .unwrap(); @@ -754,7 +738,10 @@ mod tests { .len(); assert_eq!(nb_members, 6); - assert_eq!(alicem_android_central.get_user_id().await, alicem_ios_central.get_user_id().await); + assert_eq!( + alicem_android_central.get_user_id().await, + alicem_ios_central.get_user_id().await + ); // cross server communication bobt_android_central diff --git a/crypto/src/e2e_identity/init_certificates.rs b/crypto/src/e2e_identity/init_certificates.rs index d9c871b01..587031f5c 100644 --- a/crypto/src/e2e_identity/init_certificates.rs +++ b/crypto/src/e2e_identity/init_certificates.rs @@ -62,7 +62,14 @@ impl CentralContext { #[cfg_attr(not(test), tracing::instrument(err, skip_all))] pub async fn e2ei_register_acme_ca(&self, trust_anchor_pem: String) -> CryptoResult<()> { { - if self.mls_provider().await?.keystore().find_unique::().await.is_ok() { + if self + .mls_provider() + .await? + .keystore() + .find_unique::() + .await + .is_ok() + { return Err(CryptoError::E2eiError( super::E2eIdentityError::TrustAnchorAlreadyRegistered, )); @@ -387,23 +394,22 @@ mod tests { return; } run_test_with_client_ids(case.clone(), ["alice"], move |[alice_ctx]| { - Box::pin(async move { - let ClientContext { - context, - x509_test_chain, - .. - } = alice_ctx; + Box::pin(async move { + let ClientContext { + context, + x509_test_chain, + .. + } = alice_ctx; - assert!(x509_test_chain.is_none()); - assert!(!context.e2ei_is_pki_env_setup().await.unwrap()); + assert!(x509_test_chain.is_none()); + assert!(!context.e2ei_is_pki_env_setup().await.unwrap()); - // mls_central.restore_from_disk().await.unwrap(); + // mls_central.restore_from_disk().await.unwrap(); - assert!(x509_test_chain.is_none()); - // assert!(!mls_central.mls_backend.is_pki_env_setup().await); - }) + assert!(x509_test_chain.is_none()); + // assert!(!mls_central.mls_backend.is_pki_env_setup().await); }) - .await; - + }) + .await; } } diff --git a/crypto/src/e2e_identity/mod.rs b/crypto/src/e2e_identity/mod.rs index 5a47317db..48085178c 100644 --- a/crypto/src/e2e_identity/mod.rs +++ b/crypto/src/e2e_identity/mod.rs @@ -601,10 +601,10 @@ pub(crate) mod tests { use openmls_traits::OpenMlsCryptoProvider; use serde_json::json; use wasm_bindgen_test::*; - + + use crate::context::CentralContext; #[cfg(not(target_family = "wasm"))] use crate::e2e_identity::refresh_token::RefreshToken; - use crate::context::CentralContext; use crate::{ e2e_identity::{id::QualifiedE2eiClientId, tests::x509::X509TestChain}, prelude::{ diff --git a/crypto/src/e2e_identity/rotate.rs b/crypto/src/e2e_identity/rotate.rs index 21783e30c..cd3bd1ead 100644 --- a/crypto/src/e2e_identity/rotate.rs +++ b/crypto/src/e2e_identity/rotate.rs @@ -7,10 +7,10 @@ use core_crypto_keystore::connection::FetchFromDatabase; use core_crypto_keystore::{entities::MlsKeyPackage, CryptoKeystoreMls}; use mls_crypto_provider::TransactionalCryptoProvider; +use crate::context::CentralContext; use crate::e2e_identity::init_certificates::NewCrlDistributionPoint; #[cfg(not(target_family = "wasm"))] use crate::e2e_identity::refresh_token::RefreshToken; -use crate::context::CentralContext; use crate::{ mls::credential::{ext::CredentialExt, x509::CertificatePrivateKey, CredentialBundle}, prelude::{ @@ -40,7 +40,8 @@ impl CentralContext { let mls_provider = self.mls_provider().await?; // look for existing credential of type basic. If there isn't, then this method has been misused let cb = client - .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), MlsCredentialType::Basic).await + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), MlsCredentialType::Basic) + .await .ok_or(E2eIdentityError::MissingExistingClient(MlsCredentialType::Basic))?; let client_id = cb.credential().identity().into(); @@ -79,7 +80,8 @@ impl CentralContext { let mls_provider = self.mls_provider().await?; // look for existing credential of type x509. If there isn't, then this method has been misused let cb = client - .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), MlsCredentialType::X509).await + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), MlsCredentialType::X509) + .await .ok_or(E2eIdentityError::MissingExistingClient(MlsCredentialType::X509))?; let client_id = cb.credential().identity().into(); let sign_keypair = Some((&cb.signature_key).try_into()?); @@ -146,7 +148,11 @@ impl CentralContext { let mut client_guard = self.mls_client_mut().await?; let client = client_guard.as_mut().ok_or(CryptoError::MlsNotInitialized)?; let new_cb = client - .save_new_x509_credential_bundle(&self.mls_provider().await?.keystore(), cs.signature_algorithm(), cert_bundle) + .save_new_x509_credential_bundle( + &self.mls_provider().await?.keystore(), + cs.signature_algorithm(), + cert_bundle, + ) .await?; let commits = self.e2ei_update_all(client, &new_cb).await?; @@ -166,7 +172,11 @@ impl CentralContext { } #[cfg_attr(not(test), tracing::instrument(err, skip_all))] - async fn e2ei_update_all(&self, client: &mut Client, cb: &CredentialBundle) -> CryptoResult> { + async fn e2ei_update_all( + &self, + client: &mut Client, + cb: &CredentialBundle, + ) -> CryptoResult> { let all_conversations = self.get_all_conversations().await?; let mut commits = HashMap::with_capacity(all_conversations.len()); @@ -416,10 +426,7 @@ pub(crate) mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); ids.push(id) } @@ -654,24 +661,24 @@ pub(crate) mod tests { assert_eq!(old_cb, old_cb_found); let (cid, all_credentials, scs, old_nb_identities) = { let alice_client = alice_central.client().await; - let old_nb_identities = alice_client - .identities - .as_vec().await - .len(); + let old_nb_identities = alice_client.identities.as_vec().await.len(); // Let's simulate an app crash, client gets deleted and restored from keystore let cid = alice_client.id().clone(); let scs = HashSet::from([case.signature_scheme()]); let all_credentials = alice_central .context - .keystore().await.unwrap() + .keystore() + .await + .unwrap() .find_all::(EntityFindParams::default()) .await .unwrap() .into_iter() .map(|c| { let credential = - openmls::prelude::Credential::tls_deserialize(&mut c.credential.as_slice()).unwrap(); + openmls::prelude::Credential::tls_deserialize(&mut c.credential.as_slice()) + .unwrap(); (credential, c.created_at) }) .collect::>(); @@ -682,9 +689,7 @@ pub(crate) mod tests { backend.keystore().commit_transaction().await.unwrap(); backend.keystore().new_transaction().await.unwrap(); - let client = Client::load(backend, &cid, all_credentials, scs) - .await - .unwrap(); + let client = Client::load(backend, &cid, all_credentials, scs).await.unwrap(); let mut alice_client_guard = alice_central.context.mls_client_mut().await.unwrap(); *alice_client_guard = Some(client); drop(alice_client_guard); @@ -707,10 +712,7 @@ pub(crate) mod tests { format!("wireapp://%40{NEW_HANDLE}@world.com") ); - assert_eq!( - alice_client.identities.as_vec().await.len(), - old_nb_identities - ); + assert_eq!(alice_client.identities.as_vec().await.len(), old_nb_identities); }) }) .await @@ -736,10 +738,7 @@ pub(crate) mod tests { .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Alice's turn const ALICE_NEW_HANDLE: &str = "new_alice_wire"; const ALICE_NEW_DISPLAY_NAME: &str = "New Alice Smith"; @@ -749,13 +748,16 @@ pub(crate) mod tests { let E2eiInitWrapper { context: cc, case } = wrapper; let cs = case.ciphersuite(); match case.credential_type { - MlsCredentialType::Basic => cc.e2ei_new_activation_enrollment( - ALICE_NEW_DISPLAY_NAME.to_string(), - ALICE_NEW_HANDLE.to_string(), - Some(TEAM.to_string()), - E2EI_EXPIRY, - cs, - ).await, + MlsCredentialType::Basic => { + cc.e2ei_new_activation_enrollment( + ALICE_NEW_DISPLAY_NAME.to_string(), + ALICE_NEW_HANDLE.to_string(), + Some(TEAM.to_string()), + E2EI_EXPIRY, + cs, + ) + .await + } MlsCredentialType::X509 => { cc.e2ei_new_rotate_enrollment( Some(ALICE_NEW_DISPLAY_NAME.to_string()), @@ -763,7 +765,8 @@ pub(crate) mod tests { Some(TEAM.to_string()), E2EI_EXPIRY, cs, - ).await + ) + .await } } }) @@ -812,13 +815,16 @@ pub(crate) mod tests { let E2eiInitWrapper { context: cc, case } = wrapper; let cs = case.ciphersuite(); match case.credential_type { - MlsCredentialType::Basic => cc.e2ei_new_activation_enrollment( - BOB_NEW_DISPLAY_NAME.to_string(), - BOB_NEW_HANDLE.to_string(), - Some(TEAM.to_string()), - E2EI_EXPIRY, - cs, - ).await, + MlsCredentialType::Basic => { + cc.e2ei_new_activation_enrollment( + BOB_NEW_DISPLAY_NAME.to_string(), + BOB_NEW_HANDLE.to_string(), + Some(TEAM.to_string()), + E2EI_EXPIRY, + cs, + ) + .await + } MlsCredentialType::X509 => { cc.e2ei_new_rotate_enrollment( Some(BOB_NEW_DISPLAY_NAME.to_string()), @@ -826,7 +832,8 @@ pub(crate) mod tests { Some(TEAM.to_string()), E2EI_EXPIRY, cs, - ).await + ) + .await } } }) @@ -890,10 +897,7 @@ pub(crate) mod tests { .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let init_count = alice_central.context.count_entities().await; let x509_test_chain = alice_central.x509_test_chain.as_ref().as_ref().unwrap(); @@ -977,10 +981,7 @@ pub(crate) mod tests { .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let init_count = alice_central.context.count_entities().await; diff --git a/crypto/src/e2e_identity/stash.rs b/crypto/src/e2e_identity/stash.rs index a1638b13c..7147e8cab 100644 --- a/crypto/src/e2e_identity/stash.rs +++ b/crypto/src/e2e_identity/stash.rs @@ -65,7 +65,7 @@ impl CentralContext { #[cfg(test)] mod tests { - + use mls_crypto_provider::MlsCryptoProvider; use wasm_bindgen_test::*; @@ -132,10 +132,8 @@ mod tests { move |e, _cc| { Box::pin(async move { // this restore recreates a partial enrollment - let backend = MlsCryptoProvider::try_new_in_memory("new") - .await - .unwrap(); - backend.new_transaction().await.unwrap(); + let backend = MlsCryptoProvider::try_new_in_memory("new").await.unwrap(); + backend.new_transaction().await.unwrap(); let client_id = e.client_id.parse::().unwrap(); E2eiEnrollment::try_new( client_id.into(), diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index c79c0557b..0a7379d4b 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -23,10 +23,10 @@ #![cfg_attr(not(test), deny(missing_docs))] #![allow(clippy::single_component_path_imports)] -use std::sync::Arc; use async_lock::Mutex; #[cfg(test)] pub use core_crypto_attributes::{dispotent, durable, idempotent}; +use std::sync::Arc; pub use self::error::*; @@ -47,8 +47,8 @@ pub mod e2e_identity; /// Proteus Abstraction pub mod proteus; -mod group_store; pub mod context; +mod group_store; /// Common imports that should be useful for most uses of the crate pub mod prelude { diff --git a/crypto/src/mls/buffer_external_commit.rs b/crypto/src/mls/buffer_external_commit.rs index f3407e421..34ba1962a 100644 --- a/crypto/src/mls/buffer_external_commit.rs +++ b/crypto/src/mls/buffer_external_commit.rs @@ -154,20 +154,11 @@ mod tests { } } // because external commit got merged - assert!(bob_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &alice_central).await.is_ok()); // because Alice's commit got merged - assert!(bob_central - .try_talk_to(&id, &charlie_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &charlie_central).await.is_ok()); // because Debbie's external proposal got merged through the commit - assert!(bob_central - .try_talk_to(&id, &debbie_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &debbie_central).await.is_ok()); // After merging we should erase all those pending messages assert_eq!(bob_central.context.count_entities().await.pending_messages, 0); @@ -191,10 +182,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&mut bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&mut bob_central]).await.unwrap(); // Alice will never see this commit bob_central.context.update_keying_material(&id).await.unwrap(); diff --git a/crypto/src/mls/client/identities.rs b/crypto/src/mls/client/identities.rs index 05131f6ce..86a1ab94d 100644 --- a/crypto/src/mls/client/identities.rs +++ b/crypto/src/mls/client/identities.rs @@ -2,11 +2,11 @@ use crate::{ mls::credential::{typ::MlsCredentialType, CredentialBundle}, prelude::{Client, CryptoError, CryptoResult, MlsConversation}, }; +use async_lock::Mutex; use openmls::prelude::{Credential, SignaturePublicKey}; use openmls_traits::types::SignatureScheme; use std::collections::HashMap; use std::sync::Arc; -use async_lock::Mutex; /// In memory Map of a Client's identities: one per SignatureScheme. /// We need `indexmap::IndexSet` because each `CredentialBundle` has to be unique and insertion @@ -27,11 +27,14 @@ impl ClientIdentities { ) -> Option { let credential_bundles_by_signature_schemes = self.0.lock().await; let credential_bundles = credential_bundles_by_signature_schemes.get(&sc)?; - credential_bundles.iter().find(|c| { - let ct_match = ct == c.credential.credential_type().into(); - let pk_match = c.signature_key.public() == pk.as_slice(); - ct_match && pk_match - }).cloned() + credential_bundles + .iter() + .find(|c| { + let ct_match = ct == c.credential.credential_type().into(); + let pk_match = c.signature_key.public() == pk.as_slice(); + ct_match && pk_match + }) + .cloned() } pub(crate) async fn find_most_recent_credential_bundle( @@ -43,12 +46,17 @@ impl ClientIdentities { let credential_bundles = credential_bundles_by_signature_schemes.get(&sc)?; credential_bundles .iter() - .rfind(|c| ct == c.credential.credential_type().into()).cloned() + .rfind(|c| ct == c.credential.credential_type().into()) + .cloned() } /// Having `cb` requiring ownership kinda forces the caller to first persist it in the keystore and /// only then store it in this in-memory map - pub(crate) async fn push_credential_bundle(&mut self, sc: SignatureScheme, cb: CredentialBundle) -> CryptoResult<()> { + pub(crate) async fn push_credential_bundle( + &mut self, + sc: SignatureScheme, + cb: CredentialBundle, + ) -> CryptoResult<()> { // this would mean we have messed something up and that we do no init this CredentialBundle from a keypair just inserted in the keystore debug_assert_ne!(cb.created_at, 0); let mut credential_bundles_by_signature_schemes = self.0.lock().await; @@ -77,12 +85,10 @@ impl ClientIdentities { pub(crate) async fn as_vec(&self) -> Vec<(SignatureScheme, CredentialBundle)> { let credential_bundles_by_signature_schemes = self.0.lock().await; - + credential_bundles_by_signature_schemes .iter() - .flat_map(|(sig_scheme, bundles)| { - bundles.iter().cloned().map(move |bundle| (*sig_scheme, bundle)) - }) + .flat_map(|(sig_scheme, bundles)| bundles.iter().cloned().map(move |bundle| (*sig_scheme, bundle))) .collect() } } @@ -98,7 +104,8 @@ impl MlsConversation { Ok(client .identities - .find_credential_bundle_by_public_key(sc, ct, own_leaf.signature_key()).await) + .find_credential_bundle_by_public_key(sc, ct, own_leaf.signature_key()) + .await) } pub(crate) async fn find_most_recent_credential_bundle( @@ -140,22 +147,12 @@ mod tests { run_test_with_client_ids(case.clone(), ["alice"], move |[mut central]| { Box::pin(async move { let cert = central.get_intermediate_ca().cloned(); - let old = central - .new_credential_bundle( - &case, - cert.as_ref() - ) - .await; + let old = central.new_credential_bundle(&case, cert.as_ref()).await; // wait to make sure we're not in the same second async_std::task::sleep(core::time::Duration::from_secs(1)).await; - let new = central - .new_credential_bundle( - &case, - cert.as_ref() - ) - .await; + let new = central.new_credential_bundle(&case, cert.as_ref()).await; assert_ne!(old, new); let found = central @@ -179,12 +176,7 @@ mod tests { let mut to_search = None; for i in 0..N { let cert = central.get_intermediate_ca().cloned(); - let cb = central - .new_credential_bundle( - &case, - cert.as_ref(), - ) - .await; + let cb = central.new_credential_bundle(&case, cert.as_ref()).await; if i == r { to_search = Some(cb.clone()); } @@ -214,24 +206,11 @@ mod tests { run_test_with_client_ids(case.clone(), ["alice"], move |[mut central]| { Box::pin(async move { let client = central.client().await; - let prev_count = client - .identities - .as_vec() - .await - .len(); + let prev_count = client.identities.as_vec().await.len(); let cert = central.get_intermediate_ca().cloned(); // this calls 'push_credential_bundle' under the hood - central - .new_credential_bundle( - &case, - cert.as_ref(), - ) - .await; - let next_count = client - .identities - .as_vec() - .await - .len(); + central.new_credential_bundle(&case, cert.as_ref()).await; + let next_count = client.identities.as_vec().await.len(); assert_eq!(next_count, prev_count + 1); }) }) @@ -244,15 +223,13 @@ mod tests { run_test_with_client_ids(case.clone(), ["alice"], move |[mut central]| { Box::pin(async move { let cert = central.get_intermediate_ca().cloned(); - let cb = central - .new_credential_bundle( - &case, - cert.as_ref(), - ) - .await; + let cb = central.new_credential_bundle(&case, cert.as_ref()).await; let mut client = central.context.mls_client_mut().await.unwrap(); let client = client.as_mut().unwrap(); - let push = client.identities.push_credential_bundle(case.signature_scheme(), cb).await; + let push = client + .identities + .push_credential_bundle(case.signature_scheme(), cb) + .await; assert!(matches!( push.unwrap_err(), crate::CryptoError::CredentialBundleConflict diff --git a/crypto/src/mls/client/key_package.rs b/crypto/src/mls/client/key_package.rs index 7a62476b8..7ec8fbaca 100644 --- a/crypto/src/mls/client/key_package.rs +++ b/crypto/src/mls/client/key_package.rs @@ -26,13 +26,13 @@ use core_crypto_keystore::{ }; use mls_crypto_provider::{CryptoKeystore, TransactionalCryptoProvider}; +use crate::context::CentralContext; use crate::{ mls::credential::CredentialBundle, prelude::{ Client, CryptoError, CryptoResult, MlsCiphersuite, MlsConversationConfiguration, MlsCredentialType, MlsError, }, }; -use crate::context::CentralContext; /// Default number of KeyPackages a client generates the first time it's created #[cfg(not(test))] @@ -116,7 +116,8 @@ impl Client { let mut kps = if count > kpb_count { let to_generate = count - kpb_count; let cb = self - .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type).await + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type) + .await .ok_or(CryptoError::MlsNotInitialized)?; self.generate_new_keypackages(backend, ciphersuite, &cb, to_generate) .await? @@ -235,10 +236,7 @@ impl Client { let all_to_delete = kps.iter().all(|kpr| kp_to_delete.contains(&kpr.as_slice())); if all_to_delete { // then delete this Credential - backend - .keystore() - .cred_delete_by_credential(credential.clone()) - .await?; + backend.keystore().cred_delete_by_credential(credential.clone()).await?; let credential = Credential::tls_deserialize(&mut credential.as_slice()).map_err(MlsError::from)?; self.identities.remove(&credential).await?; } @@ -276,9 +274,11 @@ impl Client { for (kp, kp_ref) in &kp_to_delete { // TODO: maybe rewrite this to optimize it. But honestly it's called so rarely and on a so tiny amount of data. Tacking issue: WPB-9600 keystore.remove::(kp_ref.as_slice()).await?; - keystore.remove::(kp.hpke_init_key().as_slice()) + keystore + .remove::(kp.hpke_init_key().as_slice()) .await?; - keystore.remove::(kp.leaf_node().encryption_key().as_slice()) + keystore + .remove::(kp.leaf_node().encryption_key().as_slice()) .await?; } diff --git a/crypto/src/mls/client/mod.rs b/crypto/src/mls/client/mod.rs index 24d53a966..c30b931ab 100644 --- a/crypto/src/mls/client/mod.rs +++ b/crypto/src/mls/client/mod.rs @@ -424,11 +424,13 @@ impl Client { self.init_basic_credential_bundle_if_missing(backend, sc) .in_current_span() .await?; - self.find_most_recent_credential_bundle(sc, ct).await + self.find_most_recent_credential_bundle(sc, ct) + .await .ok_or(CryptoError::CredentialNotFound(ct)) } MlsCredentialType::X509 => self - .find_most_recent_credential_bundle(sc, ct).await + .find_most_recent_credential_bundle(sc, ct) + .await .ok_or(CryptoError::E2eiEnrollmentNotDone), } } @@ -439,7 +441,9 @@ impl Client { backend: &TransactionalCryptoProvider, sc: SignatureScheme, ) -> CryptoResult<()> { - let existing_cb = self.find_most_recent_credential_bundle(sc, MlsCredentialType::Basic).await; + let existing_cb = self + .find_most_recent_credential_bundle(sc, MlsCredentialType::Basic) + .await; if existing_cb.is_none() { debug!(id = %self.id(), "Initializing basic credential bundle"); let cb = Self::new_basic_credential_bundle(self.id(), sc, backend)?; @@ -509,12 +513,12 @@ impl Client { #[cfg(test)] mod tests { - use core_crypto_keystore::entities::{EntityFindParams, MlsSignatureKeyPair}; - use wasm_bindgen_test::*; - use core_crypto_keystore::connection::FetchFromDatabase; use crate::prelude::ClientId; use crate::test_utils::*; + use core_crypto_keystore::connection::FetchFromDatabase; + use core_crypto_keystore::entities::{EntityFindParams, MlsSignatureKeyPair}; use mls_crypto_provider::MlsCryptoProvider; + use wasm_bindgen_test::*; use super::Client; @@ -585,7 +589,8 @@ mod tests { // Make sure both client id and PK are intact assert_eq!(alice.id(), &client_id); let cb = alice - .find_most_recent_credential_bundle(case.signature_scheme(), case.credential_type).await + .find_most_recent_credential_bundle(case.signature_scheme(), case.credential_type) + .await .unwrap(); let client_id: ClientId = cb.credential().identity().into(); assert_eq!(&client_id, handles.first().unwrap()); diff --git a/crypto/src/mls/conversation/buffer_messages.rs b/crypto/src/mls/conversation/buffer_messages.rs index a73a0c81c..9601be638 100644 --- a/crypto/src/mls/conversation/buffer_messages.rs +++ b/crypto/src/mls/conversation/buffer_messages.rs @@ -3,6 +3,7 @@ //! //! Feel free to delete all of this when the issue is fixed on the DS side ! +use crate::context::CentralContext; use crate::{ group_store::GroupStoreValue, prelude::{ @@ -18,7 +19,6 @@ use mls_crypto_provider::TransactionalCryptoProvider; use openmls::prelude::{MlsMessageIn, MlsMessageInBody}; use tls_codec::Deserialize; use tracing::{error, span, trace, Instrument, Level}; -use crate::context::CentralContext; impl CentralContext { #[cfg_attr(not(test), tracing::instrument(err, skip(self, message), fields(id = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, id))))] @@ -163,10 +163,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Bob creates a commit but won't merge it immediately let unmerged_commit = bob_central.context.update_keying_material(&id).await.unwrap(); @@ -257,20 +254,11 @@ mod tests { } } // because external commit got merged - assert!(bob_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &alice_central).await.is_ok()); // because Alice's commit got merged - assert!(bob_central - .try_talk_to(&id, &charlie_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &charlie_central).await.is_ok()); // because Debbie's external proposal got merged through the commit - assert!(bob_central - .try_talk_to(&id, &debbie_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &debbie_central).await.is_ok()); // After merging we should erase all those pending messages assert_eq!(bob_central.context.count_entities().await.pending_messages, 0); @@ -401,20 +389,11 @@ mod tests { } } // because external commit got merged - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); // because Alice's commit got merged - assert!(alice_central - .try_talk_to(&id, &charlie_central) - .await - .is_ok()); + assert!(alice_central.try_talk_to(&id, &charlie_central).await.is_ok()); // because Debbie's external proposal got merged through the commit - assert!(alice_central - .try_talk_to(&id, &debbie_central) - .await - .is_ok()); + assert!(alice_central.try_talk_to(&id, &debbie_central).await.is_ok()); // After merging we should erase all those pending messages assert_eq!(alice_central.context.count_entities().await.pending_messages, 0); diff --git a/crypto/src/mls/conversation/commit.rs b/crypto/src/mls/conversation/commit.rs index 40dcaa322..3f34b2eae 100644 --- a/crypto/src/mls/conversation/commit.rs +++ b/crypto/src/mls/conversation/commit.rs @@ -9,6 +9,8 @@ use openmls::prelude::{KeyPackageIn, LeafNode, LeafNodeIndex, MlsMessageOut}; use mls_crypto_provider::TransactionalCryptoProvider; +use super::MlsConversation; +use crate::context::CentralContext; use crate::{ e2e_identity::init_certificates::NewCrlDistributionPoint, mls::credential::{ @@ -18,8 +20,6 @@ use crate::{ prelude::{Client, ClientId, ConversationId, CryptoError, CryptoResult, MlsError, MlsGroupInfoBundle}, }; use tracing::Instrument; -use crate::context::CentralContext; -use super::MlsConversation; impl CentralContext { /// Adds new members to the group/conversation @@ -158,7 +158,8 @@ impl MlsConversation { backend: &TransactionalCryptoProvider, ) -> CryptoResult { let signer = &self - .find_most_recent_credential_bundle(client).await? + .find_most_recent_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; @@ -221,7 +222,8 @@ impl MlsConversation { })?; let signer = &self - .find_most_recent_credential_bundle(client).await? + .find_most_recent_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; @@ -256,9 +258,13 @@ impl MlsConversation { leaf_node: Option, ) -> CryptoResult { let cb = match cb { - None => &self.find_most_recent_credential_bundle(client).await.ok().flatten() + None => &self + .find_most_recent_credential_bundle(client) + .await + .ok() + .flatten() .ok_or(CryptoError::IdentityInitializationError)?, - Some(cb) => cb + Some(cb) => cb, }; let (commit, welcome, group_info) = self .group @@ -290,7 +296,8 @@ impl MlsConversation { ) -> CryptoResult> { if self.group.pending_proposals().count() > 0 { let signer = &self - .find_most_recent_credential_bundle(client).await? + .find_most_recent_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; @@ -390,118 +397,83 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn can_add_members_to_conversation(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - let bob = bob_central.rand_key_package(&case).await; - let MlsConversationCreationMessage { welcome, .. } = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); - // before merging, commit is not applied - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); - alice_central.context.commit_accepted(&id).await.unwrap(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + let bob = bob_central.rand_key_package(&case).await; + let MlsConversationCreationMessage { welcome, .. } = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap(); - assert_eq!(alice_central.get_conversation_unchecked(&id).await.id, id); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .group - .group_id() - .as_slice(), - id - ); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + // before merging, commit is not applied + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); + alice_central.context.commit_accepted(&id).await.unwrap(); - bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await - .unwrap(); - assert_eq!( - alice_central.get_conversation_unchecked(&id).await.id(), - bob_central.get_conversation_unchecked(&id).await.id() - ); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - assert!(alice_central - .try_talk_to(&id, &bob_central) + assert_eq!(alice_central.get_conversation_unchecked(&id).await.id, id); + assert_eq!( + alice_central + .get_conversation_unchecked(&id) .await - .is_ok()); - }) - }, - ) + .group + .group_id() + .as_slice(), + id + ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + + bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await + .unwrap(); + assert_eq!( + alice_central.get_conversation_unchecked(&id).await.id(), + bob_central.get_conversation_unchecked(&id).await.id() + ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_return_valid_welcome(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); - let bob = bob_central.rand_key_package(&case).await; - let welcome = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap() - .welcome; - alice_central.context.commit_accepted(&id).await.unwrap(); + let bob = bob_central.rand_key_package(&case).await; + let welcome = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap() + .welcome; + alice_central.context.commit_accepted(&id).await.unwrap(); - bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await - .unwrap(); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); - }) - }, - ) + bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await + .unwrap(); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + }) + }) .await } @@ -546,67 +518,43 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn alice_can_remove_bob_from_conversation(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let MlsCommitBundle { commit, welcome, .. } = alice_central - .context - .remove_members_from_conversation(&id, &[bob_central.get_client_id().await]) - .await - .unwrap(); - assert!(welcome.is_none()); + let MlsCommitBundle { commit, welcome, .. } = alice_central + .context + .remove_members_from_conversation(&id, &[bob_central.get_client_id().await]) + .await + .unwrap(); + assert!(welcome.is_none()); - // before merging, commit is not applied - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + // before merging, commit is not applied + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + alice_central.context.commit_accepted(&id).await.unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); - bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); + bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); - // But has been removed from the conversation - assert!(matches!( - bob_central.context.get_conversation(&id).await.unwrap_err(), - CryptoError::ConversationNotFound(conv_id) if conv_id == id - )); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_err()); - }) - }, - ) + // But has been removed from the conversation + assert!(matches!( + bob_central.context.get_conversation(&id).await.unwrap_err(), + CryptoError::ConversationNotFound(conv_id) if conv_id == id + )); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_err()); + }) + }) .await; } @@ -625,10 +573,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let proposal = alice_central .context @@ -659,10 +604,7 @@ mod tests { .await .is_ok()); // because Bob has been removed from the group - assert!(guest_central - .try_talk_to(&id, &bob_central) - .await - .is_err()); + assert!(guest_central.try_talk_to(&id, &bob_central).await.is_err()); }) }, ) @@ -684,10 +626,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let commit_bundle = alice_central .context @@ -703,10 +642,7 @@ mod tests { .await .is_ok()); // because Bob has been removed from the group - assert!(guest_central - .try_talk_to(&id, &bob_central) - .await - .is_err()); + assert!(guest_central.try_talk_to(&id, &bob_central).await.is_err()); }) }, ) @@ -720,94 +656,84 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_succeed(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let init_count = alice_central.context.count_entities().await; + let init_count = alice_central.context.count_entities().await; - let bob_keys = bob_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .collect::>>(); - let alice_keys = alice_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .collect::>>(); - assert!(alice_keys.iter().all(|a_key| bob_keys.contains(a_key))); + let bob_keys = bob_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .collect::>>(); + let alice_keys = alice_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .collect::>>(); + assert!(alice_keys.iter().all(|a_key| bob_keys.contains(a_key))); - let alice_key = alice_central - .encryption_key_of(&id, alice_central.get_client_id().await) - .await; + let alice_key = alice_central + .encryption_key_of(&id, alice_central.get_client_id().await) + .await; - // proposing the key update for alice - let MlsCommitBundle { commit, welcome, .. } = - alice_central.context.update_keying_material(&id).await.unwrap(); - assert!(welcome.is_none()); + // proposing the key update for alice + let MlsCommitBundle { commit, welcome, .. } = + alice_central.context.update_keying_material(&id).await.unwrap(); + assert!(welcome.is_none()); - // before merging, commit is not applied - assert!(alice_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .contains(&alice_key)); + // before merging, commit is not applied + assert!(alice_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .contains(&alice_key)); - alice_central.context.commit_accepted(&id).await.unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); - assert!(!alice_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .contains(&alice_key)); + assert!(!alice_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .contains(&alice_key)); - let alice_new_keys = alice_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .collect::>(); - assert!(!alice_new_keys.contains(&alice_key)); + let alice_new_keys = alice_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .collect::>(); + assert!(!alice_new_keys.contains(&alice_key)); - // receiving the commit on bob's side (updating key from alice) - bob_central - .context - .decrypt_message(&id, &commit.to_bytes().unwrap()) - .await - .unwrap(); + // receiving the commit on bob's side (updating key from alice) + bob_central + .context + .decrypt_message(&id, &commit.to_bytes().unwrap()) + .await + .unwrap(); - let bob_new_keys = bob_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .collect::>(); - assert!(alice_new_keys.iter().all(|a_key| bob_new_keys.contains(a_key))); + let bob_new_keys = bob_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .collect::>(); + assert!(alice_new_keys.iter().all(|a_key| bob_new_keys.contains(a_key))); - // ensuring both can encrypt messages - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + // ensuring both can encrypt messages + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); - // make sure inline update commit + merge does not leak anything - // that's obvious since no new encryption keypair is created in this case - let final_count = alice_central.context.count_entities().await; - assert_eq!(init_count, final_count); - }) - }, - ) + // make sure inline update commit + merge does not leak anything + // that's obvious since no new encryption keypair is created in this case + let final_count = alice_central.context.count_entities().await; + assert_eq!(init_count, final_count); + }) + }) .await; } @@ -825,10 +751,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let bob_keys = bob_central .get_conversation_unchecked(&id) @@ -883,31 +806,10 @@ mod tests { .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); - assert_eq!( - charlie_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 3); + assert_eq!(charlie_central.get_conversation_unchecked(&id).await.members().len(), 3); // bob still didn't receive the message with the updated key and charlie's addition - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); let alice_new_keys = alice_central .get_conversation_unchecked(&id) @@ -922,14 +824,7 @@ mod tests { .decrypt_message(&id, &commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 3); let bob_new_keys = bob_central .get_conversation_unchecked(&id) @@ -939,18 +834,9 @@ mod tests { assert!(alice_new_keys.iter().all(|a_key| bob_new_keys.contains(a_key))); // ensure all parties can encrypt messages - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); - assert!(bob_central - .try_talk_to(&id, &charlie_central) - .await - .is_ok()); - assert!(charlie_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + assert!(bob_central.try_talk_to(&id, &charlie_central).await.is_ok()); + assert!(charlie_central.try_talk_to(&id, &alice_central).await.is_ok()); }) }, ) @@ -971,10 +857,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let proposal = alice_central .context @@ -1027,10 +910,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let commit_bundle = alice_central.context.update_keying_material(&id).await.unwrap(); let group_info = commit_bundle.group_info.get_group_info(); @@ -1070,14 +950,7 @@ mod tests { .await .unwrap(); assert!(!alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); let MlsCommitBundle { welcome, .. } = alice_central .context .commit_pending_proposals(&id) @@ -1085,24 +958,14 @@ mod tests { .unwrap() .unwrap(); alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); bob_central .context .process_welcome_message(welcome.unwrap().into(), case.custom_cfg()) .await .unwrap(); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); }) }, ) @@ -1146,24 +1009,14 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let proposal = bob_central .context .new_add_proposal(&id, charlie_central.get_one_key_package(&case).await) .await .unwrap(); assert!(!bob_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); alice_central .context .decrypt_message(&id, proposal.proposal.to_bytes().unwrap()) @@ -1177,32 +1030,15 @@ mod tests { .unwrap() .unwrap(); alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 3); bob_central .context .decrypt_message(&id, commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 3); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); }) }, ) @@ -1212,42 +1048,35 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_return_valid_welcome(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .context - .new_add_proposal(&id, bob_central.get_one_key_package(&case).await) - .await - .unwrap(); - let MlsCommitBundle { welcome, .. } = alice_central - .context - .commit_pending_proposals(&id) - .await - .unwrap() - .unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central + .context + .new_add_proposal(&id, bob_central.get_one_key_package(&case).await) + .await + .unwrap(); + let MlsCommitBundle { welcome, .. } = alice_central + .context + .commit_pending_proposals(&id) + .await + .unwrap() + .unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); - bob_central - .context - .process_welcome_message(welcome.unwrap().into(), case.custom_cfg()) - .await - .unwrap(); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); - }) - }, - ) + bob_central + .context + .process_welcome_message(welcome.unwrap().into(), case.custom_cfg()) + .await + .unwrap(); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + }) + }) .await; } @@ -1298,53 +1127,71 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_prevent_out_of_order_commits(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let commit1 = alice_central.context.update_keying_material(&id).await.unwrap().commit; - let commit1 = commit1.to_bytes().unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - let commit2 = alice_central.context.update_keying_material(&id).await.unwrap().commit; - let commit2 = commit2.to_bytes().unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - - // fails when a commit is skipped - let out_of_order = bob_central.context.decrypt_message(&id, &commit2).await; - assert!(matches!(out_of_order.unwrap_err(), CryptoError::BufferedFutureMessage)); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let commit1 = alice_central.context.update_keying_material(&id).await.unwrap().commit; + let commit1 = commit1.to_bytes().unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + let commit2 = alice_central.context.update_keying_material(&id).await.unwrap().commit; + let commit2 = commit2.to_bytes().unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + + // fails when a commit is skipped + let out_of_order = bob_central.context.decrypt_message(&id, &commit2).await; + assert!(matches!(out_of_order.unwrap_err(), CryptoError::BufferedFutureMessage)); + + // works in the right order though + // NB: here 'commit2' has been buffered so it is also applied when we decrypt commit1 + bob_central.context.decrypt_message(&id, &commit1).await.unwrap(); + + // and then fails again when trying to decrypt a commit with an epoch in the past + let past_commit = bob_central.context.decrypt_message(&id, &commit1).await; + assert!(matches!(past_commit.unwrap_err(), CryptoError::StaleCommit)); + }) + }) + .await; + } - // works in the right order though - // NB: here 'commit2' has been buffered so it is also applied when we decrypt commit1 - bob_central.context.decrypt_message(&id, &commit1).await.unwrap(); + #[apply(all_cred_cipher)] + #[wasm_bindgen_test] + async fn should_allow_dropped_commits(case: TestCase) { + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - // and then fails again when trying to decrypt a commit with an epoch in the past - let past_commit = bob_central.context.decrypt_message(&id, &commit1).await; - assert!(matches!(past_commit.unwrap_err(), CryptoError::StaleCommit)); - }) - }, - ) + let _alice_commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + let bob_commit = bob_central.context.update_keying_material(&id).await.unwrap().commit; + // Bob commit arrives first and has precedence hence Alice's commit is dropped + alice_central + .context + .decrypt_message(&id, bob_commit.to_bytes().unwrap()) + .await + .unwrap(); + bob_central.context.commit_accepted(&id).await.unwrap(); + }) + }) .await; } #[apply(all_cred_cipher)] #[wasm_bindgen_test] - async fn should_allow_dropped_commits(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { + async fn should_prevent_replayed_encrypted_handshake_messages(case: TestCase) { + if case.custom_cfg().wire_policy == MlsWirePolicy::Ciphertext { + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { Box::pin(async move { let id = conversation_id(); alice_central @@ -1352,94 +1199,55 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let proposal1 = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; + let proposal2 = proposal1.clone(); alice_central - .invite_all(&case, &id, [&bob_central]) + .get_conversation_unchecked(&id) .await - .unwrap(); + .group + .clear_pending_proposals(); - let _alice_commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - let bob_commit = bob_central.context.update_keying_material(&id).await.unwrap().commit; - // Bob commit arrives first and has precedence hence Alice's commit is dropped - alice_central + let commit1 = alice_central.context.update_keying_material(&id).await.unwrap().commit; + let commit2 = commit1.clone(); + + // replayed encrypted proposal should fail + bob_central .context - .decrypt_message(&id, bob_commit.to_bytes().unwrap()) + .decrypt_message(&id, proposal1.to_bytes().unwrap()) .await .unwrap(); - bob_central.context.commit_accepted(&id).await.unwrap(); - }) - }, - ) - .await; - } - - #[apply(all_cred_cipher)] - #[wasm_bindgen_test] - async fn should_prevent_replayed_encrypted_handshake_messages(case: TestCase) { - if case.custom_cfg().wire_policy == MlsWirePolicy::Ciphertext { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let proposal1 = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; - let proposal2 = proposal1.clone(); - alice_central - .get_conversation_unchecked(&id) - .await - .group - .clear_pending_proposals(); - - let commit1 = alice_central.context.update_keying_material(&id).await.unwrap().commit; - let commit2 = commit1.clone(); - - // replayed encrypted proposal should fail + assert!(matches!( bob_central .context - .decrypt_message(&id, proposal1.to_bytes().unwrap()) - .await - .unwrap(); - assert!(matches!( - bob_central - .context - .decrypt_message(&id, proposal2.to_bytes().unwrap()) - .await - .unwrap_err(), - CryptoError::DuplicateMessage - )); - bob_central - .get_conversation_unchecked(&id) + .decrypt_message(&id, proposal2.to_bytes().unwrap()) .await - .group - .clear_pending_proposals(); + .unwrap_err(), + CryptoError::DuplicateMessage + )); + bob_central + .get_conversation_unchecked(&id) + .await + .group + .clear_pending_proposals(); - // replayed encrypted commit should fail + // replayed encrypted commit should fail + bob_central + .context + .decrypt_message(&id, commit1.to_bytes().unwrap()) + .await + .unwrap(); + assert!(matches!( bob_central .context - .decrypt_message(&id, commit1.to_bytes().unwrap()) + .decrypt_message(&id, commit2.to_bytes().unwrap()) .await - .unwrap(); - assert!(matches!( - bob_central - .context - .decrypt_message(&id, commit2.to_bytes().unwrap()) - .await - .unwrap_err(), - CryptoError::StaleCommit - )); - }) - }, - ) + .unwrap_err(), + CryptoError::StaleCommit + )); + }) + }) .await; } } diff --git a/crypto/src/mls/conversation/commit_delay.rs b/crypto/src/mls/conversation/commit_delay.rs index b03dceabb..ab6c23772 100644 --- a/crypto/src/mls/conversation/commit_delay.rs +++ b/crypto/src/mls/conversation/commit_delay.rs @@ -189,23 +189,9 @@ mod tests { .add_members_to_conversation(&id, vec![bob]) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); bob_central .context @@ -223,23 +209,9 @@ mod tests { .add_members_to_conversation(&id, vec![charlie]) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 3); let _ = bob_central .context diff --git a/crypto/src/mls/conversation/config.rs b/crypto/src/mls/conversation/config.rs index 96d852336..278ae88f3 100644 --- a/crypto/src/mls/conversation/config.rs +++ b/crypto/src/mls/conversation/config.rs @@ -31,9 +31,9 @@ use openmls_traits::OpenMlsCryptoProvider; use serde::{Deserialize, Serialize}; use wire_e2e_identity::prelude::parse_json_jwk; -use crate::MlsError; -use crate::prelude::{CryptoResult, E2eIdentityError, MlsCiphersuite}; use crate::context::CentralContext; +use crate::prelude::{CryptoResult, E2eIdentityError, MlsCiphersuite}; +use crate::MlsError; /// Sets the config in OpenMls for the oldest possible epoch(past current) that a message can be decrypted pub(crate) const MAX_PAST_EPOCHS: usize = 3; @@ -317,14 +317,17 @@ mod tests { Box::pin(async move { let (_sk, pk) = cc .context - .mls_provider().await.unwrap() + .mls_provider() + .await + .unwrap() .crypto() .signature_key_gen(case.signature_scheme()) .unwrap(); assert!(cc .context - .set_raw_external_senders(&mut case.cfg.clone(), vec![pk]).await + .set_raw_external_senders(&mut case.cfg.clone(), vec![pk]) + .await .is_ok()); }) }) @@ -349,7 +352,8 @@ mod tests { let jwk = wire_e2e_identity::prelude::generate_jwk(alg); let _ = cc .context - .set_raw_external_senders(&mut case.cfg.clone(), vec![jwk]).await + .set_raw_external_senders(&mut case.cfg.clone(), vec![jwk]) + .await .unwrap(); }) }) diff --git a/crypto/src/mls/conversation/db_count.rs b/crypto/src/mls/conversation/db_count.rs index f8844b079..d5a60d886 100644 --- a/crypto/src/mls/conversation/db_count.rs +++ b/crypto/src/mls/conversation/db_count.rs @@ -1,3 +1,4 @@ +use crate::context::CentralContext; use crate::prelude::MlsCentral; use core_crypto_keystore::{ connection::FetchFromDatabase, @@ -7,7 +8,6 @@ use core_crypto_keystore::{ PersistedMlsPendingGroup, }, }; -use crate::context::CentralContext; #[derive(Debug, Clone, Eq, PartialEq)] pub struct EntitiesCount { diff --git a/crypto/src/mls/conversation/decrypt.rs b/crypto/src/mls/conversation/decrypt.rs index ec555715d..4ab107a50 100644 --- a/crypto/src/mls/conversation/decrypt.rs +++ b/crypto/src/mls/conversation/decrypt.rs @@ -23,6 +23,7 @@ use tls_codec::Deserialize; use core_crypto_keystore::entities::MlsPendingMessage; use mls_crypto_provider::TransactionalCryptoProvider; +use crate::context::CentralContext; use crate::{ e2e_identity::{conversation_state::compute_state, init_certificates::NewCrlDistributionPoint}, group_store::GroupStoreValue, @@ -40,7 +41,6 @@ use crate::{ prelude::{E2eiConversationState, MlsProposalBundle, WireIdentity}, CoreCryptoCallbacks, CryptoError, CryptoResult, MlsError, }; -use crate::context::CentralContext; /// Represents the potential items a consumer might require after passing us an encrypted message we /// have decrypted for him @@ -443,69 +443,54 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn decrypting_a_regular_commit_should_leave_conversation_active(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let MlsCommitBundle { commit, .. } = - bob_central.context.update_keying_material(&id).await.unwrap(); - let MlsConversationDecryptMessage { is_active, .. } = alice_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - assert!(is_active) - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let MlsCommitBundle { commit, .. } = bob_central.context.update_keying_material(&id).await.unwrap(); + let MlsConversationDecryptMessage { is_active, .. } = alice_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + assert!(is_active) + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn decrypting_a_commit_removing_self_should_set_conversation_inactive(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let MlsCommitBundle { commit, .. } = bob_central - .context - .remove_members_from_conversation(&id, &[alice_central.get_client_id().await]) - .await - .unwrap(); - let MlsConversationDecryptMessage { is_active, .. } = alice_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - assert!(!is_active) - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let MlsCommitBundle { commit, .. } = bob_central + .context + .remove_members_from_conversation(&id, &[alice_central.get_client_id().await]) + .await + .unwrap(); + let MlsConversationDecryptMessage { is_active, .. } = alice_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + assert!(!is_active) + }) + }) .await } } @@ -516,43 +501,36 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn decrypting_a_commit_should_succeed(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let epoch_before = alice_central.context.conversation_epoch(&id).await.unwrap(); - - let MlsCommitBundle { commit, .. } = - alice_central.context.update_keying_material(&id).await.unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - - let decrypted = bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - let epoch_after = bob_central.context.conversation_epoch(&id).await.unwrap(); - assert_eq!(epoch_after, epoch_before + 1); - assert!(decrypted.has_epoch_changed); - assert!(decrypted.delay.is_none()); - assert!(decrypted.app_msg.is_none()); - - alice_central.verify_sender_identity(&case, &decrypted).await; - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let epoch_before = alice_central.context.conversation_epoch(&id).await.unwrap(); + + let MlsCommitBundle { commit, .. } = + alice_central.context.update_keying_material(&id).await.unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + + let decrypted = bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + let epoch_after = bob_central.context.conversation_epoch(&id).await.unwrap(); + assert_eq!(epoch_after, epoch_before + 1); + assert!(decrypted.has_epoch_changed); + assert!(decrypted.delay.is_none()); + assert!(decrypted.app_msg.is_none()); + + alice_central.verify_sender_identity(&case, &decrypted).await; + }) + }) .await } @@ -570,10 +548,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Alice creates a commit which will be superseded by Bob's one let charlie = charlie_central.rand_key_package(&case).await; @@ -630,10 +605,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Alice will create a commit to add Charlie // Bob will create a commit which will be accepted first by DS so Alice will decrypt it @@ -722,10 +694,7 @@ mod tests { .await .unwrap() .id; - assert!(charlie_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); + assert!(charlie_central.try_talk_to(&id, &alice_central).await.is_ok()); }) }, ) @@ -746,10 +715,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Bob will create a proposal to add Charlie // Alice will decrypt this proposal @@ -801,10 +767,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Alice will create a proposal to add Charlie // Bob will create a commit which Alice will decrypt @@ -898,10 +861,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // DS will create an external proposal to add Charlie // But meanwhile Bob, before receiving the external proposal, @@ -911,11 +871,7 @@ mod tests { .context .new_external_add_proposal( id.clone(), - alice_central - .get_conversation_unchecked(&id) - .await - .group - .epoch(), + alice_central.get_conversation_unchecked(&id).await.group.epoch(), case.ciphersuite(), case.credential_type, ) @@ -948,41 +904,34 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_not_return_sender_client_id(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - - let sender_client_id = bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap() - .sender_client_id; - assert!(sender_client_id.is_none()); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + + let sender_client_id = bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap() + .sender_client_id; + assert!(sender_client_id.is_none()); + }) + }) .await } } mod external_proposal { - use std::sync::Arc; use super::*; + use std::sync::Arc; #[apply(all_cred_cipher)] #[wasm_bindgen_test] @@ -998,16 +947,9 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let epoch = alice_central - .get_conversation_unchecked(&id) - .await - .group - .epoch(); + let epoch = alice_central.get_conversation_unchecked(&id).await.group.epoch(); let ext_proposal = alice2_central .context .new_external_add_proposal(id.clone(), epoch, case.ciphersuite(), case.credential_type) @@ -1051,16 +993,9 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let epoch = alice_central - .get_conversation_unchecked(&id) - .await - .group - .epoch(); + let epoch = alice_central.get_conversation_unchecked(&id).await.group.epoch(); let message = alice2_central .context .new_external_add_proposal(id.clone(), epoch, case.ciphersuite(), case.credential_type) @@ -1104,23 +1039,18 @@ mod tests { .set_callbacks(Some(Arc::new(ValidationCallbacks { client_is_existing_group_user: false, ..Default::default() - }))).await.unwrap(); + }))) + .await + .unwrap(); alice_central .context .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let epoch = alice_central - .get_conversation_unchecked(&id) - .await - .group - .epoch(); + let epoch = alice_central.get_conversation_unchecked(&id).await.group.epoch(); let external_proposal = alice2_central .context .new_external_add_proposal(id.clone(), epoch, case.ciphersuite(), case.credential_type) @@ -1135,7 +1065,7 @@ mod tests { assert!(matches!(error, CryptoError::UnauthorizedExternalAddProposal)); - bob_central.context.set_callbacks(None).await.unwrap(); + bob_central.context.set_callbacks(None).await.unwrap(); let error = bob_central .context .decrypt_message(&id, &external_proposal.to_bytes().unwrap()) @@ -1168,10 +1098,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let charlie_kp = charlie_central.get_one_key_package(&case).await; let proposal = alice_central @@ -1187,14 +1114,7 @@ mod tests { .await .unwrap(); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); // if 'decrypt_message' is not durable the commit won't contain the add proposal bob_central .context @@ -1203,14 +1123,7 @@ mod tests { .unwrap() .unwrap(); bob_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 3); assert!(!decrypted.has_epoch_changed); alice_central.verify_sender_identity(&case, &decrypted).await; @@ -1223,34 +1136,27 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_not_return_sender_client_id(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; - - let sender_client_id = bob_central - .context - .decrypt_message(&id, proposal.to_bytes().unwrap()) - .await - .unwrap() - .sender_client_id; - assert!(sender_client_id.is_none()); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; + + let sender_client_id = bob_central + .context + .decrypt_message(&id, proposal.to_bytes().unwrap()) + .await + .unwrap() + .sender_client_id; + assert!(sender_client_id.is_none()); + }) + }) .await } } @@ -1261,133 +1167,112 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn can_decrypt_app_message(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - assert_ne!(&msg[..], &encrypted[..]); - let decrypted = bob_central.context.decrypt_message(&id, encrypted).await.unwrap(); - let dec_msg = decrypted.app_msg.as_ref().unwrap().as_slice(); - assert_eq!(dec_msg, &msg[..]); - assert!(!decrypted.has_epoch_changed); - alice_central.verify_sender_identity(&case, &decrypted).await; - - let msg = b"Hello alice"; - let encrypted = bob_central.context.encrypt_message(&id, msg).await.unwrap(); - assert_ne!(&msg[..], &encrypted[..]); - let decrypted = alice_central.context.decrypt_message(&id, encrypted).await.unwrap(); - let dec_msg = decrypted.app_msg.as_ref().unwrap().as_slice(); - assert_eq!(dec_msg, &msg[..]); - assert!(!decrypted.has_epoch_changed); - bob_central.verify_sender_identity(&case, &decrypted).await; - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + assert_ne!(&msg[..], &encrypted[..]); + let decrypted = bob_central.context.decrypt_message(&id, encrypted).await.unwrap(); + let dec_msg = decrypted.app_msg.as_ref().unwrap().as_slice(); + assert_eq!(dec_msg, &msg[..]); + assert!(!decrypted.has_epoch_changed); + alice_central.verify_sender_identity(&case, &decrypted).await; + + let msg = b"Hello alice"; + let encrypted = bob_central.context.encrypt_message(&id, msg).await.unwrap(); + assert_ne!(&msg[..], &encrypted[..]); + let decrypted = alice_central.context.decrypt_message(&id, encrypted).await.unwrap(); + let dec_msg = decrypted.app_msg.as_ref().unwrap().as_slice(); + assert_eq!(dec_msg, &msg[..]); + assert!(!decrypted.has_epoch_changed); + bob_central.verify_sender_identity(&case, &decrypted).await; + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn cannot_decrypt_app_message_after_rejoining(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - // encrypt a message in epoch 1 - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - - // Now Bob will rejoin the group and try to decrypt Alice's message - // in epoch 2 which should fail - let gi = alice_central.get_group_info(&id).await; - bob_central - .context - .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - bob_central - .context - .merge_pending_group_from_external_commit(&id) - .await - .unwrap(); - - // fails because of Forward Secrecy - let decrypt = bob_central.context.decrypt_message(&id, &encrypted).await; - assert!(matches!(decrypt.unwrap_err(), CryptoError::DecryptionError)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + // encrypt a message in epoch 1 + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + + // Now Bob will rejoin the group and try to decrypt Alice's message + // in epoch 2 which should fail + let gi = alice_central.get_group_info(&id).await; + bob_central + .context + .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + bob_central + .context + .merge_pending_group_from_external_commit(&id) + .await + .unwrap(); + + // fails because of Forward Secrecy + let decrypt = bob_central.context.decrypt_message(&id, &encrypted).await; + assert!(matches!(decrypt.unwrap_err(), CryptoError::DecryptionError)); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn cannot_decrypt_app_message_from_future_epoch(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - // only Alice will change epoch without notifying Bob - let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - alice_central.context.commit_accepted(&id).await.unwrap(); - - // Now in epoch 2 Alice will encrypt a message - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - - // which Bob cannot decrypt because of Post CompromiseSecurity - let decrypt = bob_central.context.decrypt_message(&id, &encrypted).await; - assert!(matches!(decrypt.unwrap_err(), CryptoError::BufferedFutureMessage)); - - let decrypted_commit = bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - let buffered_msg = decrypted_commit.buffered_messages.unwrap(); - let decrypted_msg = buffered_msg.first().unwrap().app_msg.clone().unwrap(); - assert_eq!(&decrypted_msg, msg); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + // only Alice will change epoch without notifying Bob + let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + alice_central.context.commit_accepted(&id).await.unwrap(); + + // Now in epoch 2 Alice will encrypt a message + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + + // which Bob cannot decrypt because of Post CompromiseSecurity + let decrypt = bob_central.context.decrypt_message(&id, &encrypted).await; + assert!(matches!(decrypt.unwrap_err(), CryptoError::BufferedFutureMessage)); + + let decrypted_commit = bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + let buffered_msg = decrypted_commit.buffered_messages.unwrap(); + let decrypted_msg = buffered_msg.first().unwrap().app_msg.clone().unwrap(); + assert_eq!(&decrypted_msg, msg); + }) + }) .await } @@ -1397,84 +1282,70 @@ mod tests { // otherwise the test would fail because we decrypt messages in reverse order which is // kinda dropping them case.cfg.custom.maximum_forward_distance = 0; - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let out_of_order_tolerance = case.custom_cfg().out_of_order_tolerance; - let nb_messages = out_of_order_tolerance * 2; - let mut messages = vec![]; - - // stack up encrypted messages.. - for i in 0..nb_messages { - let msg = format!("Hello {i}"); - let encrypted = alice_central.context.encrypt_message(&id, &msg).await.unwrap(); - messages.push((msg, encrypted)); - } + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let out_of_order_tolerance = case.custom_cfg().out_of_order_tolerance; + let nb_messages = out_of_order_tolerance * 2; + let mut messages = vec![]; + + // stack up encrypted messages.. + for i in 0..nb_messages { + let msg = format!("Hello {i}"); + let encrypted = alice_central.context.encrypt_message(&id, &msg).await.unwrap(); + messages.push((msg, encrypted)); + } - // ..then unstack them to see out_of_order_tolerance come into play - messages.reverse(); - for (i, (original, encrypted)) in messages.iter().enumerate() { - let decrypt = bob_central.context.decrypt_message(&id, encrypted).await; - if i > out_of_order_tolerance as usize { - let decrypted = decrypt.unwrap().app_msg.unwrap(); - assert_eq!(decrypted, original.as_bytes()); - } else { - assert!(matches!(decrypt.unwrap_err(), CryptoError::DuplicateMessage)) - } + // ..then unstack them to see out_of_order_tolerance come into play + messages.reverse(); + for (i, (original, encrypted)) in messages.iter().enumerate() { + let decrypt = bob_central.context.decrypt_message(&id, encrypted).await; + if i > out_of_order_tolerance as usize { + let decrypted = decrypt.unwrap().app_msg.unwrap(); + assert_eq!(decrypted, original.as_bytes()); + } else { + assert!(matches!(decrypt.unwrap_err(), CryptoError::DuplicateMessage)) } - }) - }, - ) + } + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn returns_sender_client_id(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - assert_ne!(&msg[..], &encrypted[..]); - - let sender_client_id = bob_central - .context - .decrypt_message(&id, encrypted) - .await - .unwrap() - .sender_client_id - .unwrap(); - assert_eq!(sender_client_id, alice_central.get_client_id().await); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + assert_ne!(&msg[..], &encrypted[..]); + + let sender_client_id = bob_central + .context + .decrypt_message(&id, encrypted) + .await + .unwrap() + .sender_client_id + .unwrap(); + assert_eq!(sender_client_id, alice_central.get_client_id().await); + }) + }) .await } } @@ -1486,45 +1357,26 @@ mod tests { #[wasm_bindgen_test] async fn should_throw_specialized_error_when_epoch_too_old(mut case: TestCase) { case.cfg.custom.out_of_order_tolerance = 0; - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - // Alice encrypts a message to Bob - let bob_message1 = alice_central.context.encrypt_message(&id, b"Hello Bob").await.unwrap(); - let bob_message2 = alice_central - .context - .encrypt_message(&id, b"Hello again Bob") - .await - .unwrap(); - - // Move group's epoch forward by self updating - for _ in 0..MAX_PAST_EPOCHS { - let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - alice_central.context.commit_accepted(&id).await.unwrap(); - bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - } - // Decrypt should work - let decrypt = bob_central.context.decrypt_message(&id, &bob_message1).await.unwrap(); - assert_eq!(decrypt.app_msg.unwrap(), b"Hello Bob"); - - // Moving the epochs once more should cause an error + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + // Alice encrypts a message to Bob + let bob_message1 = alice_central.context.encrypt_message(&id, b"Hello Bob").await.unwrap(); + let bob_message2 = alice_central + .context + .encrypt_message(&id, b"Hello again Bob") + .await + .unwrap(); + + // Move group's epoch forward by self updating + for _ in 0..MAX_PAST_EPOCHS { let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; alice_central.context.commit_accepted(&id).await.unwrap(); bob_central @@ -1532,12 +1384,24 @@ mod tests { .decrypt_message(&id, commit.to_bytes().unwrap()) .await .unwrap(); - - let decrypt = bob_central.context.decrypt_message(&id, &bob_message2).await; - assert!(matches!(decrypt.unwrap_err(), CryptoError::MessageEpochTooOld)); - }) - }, - ) + } + // Decrypt should work + let decrypt = bob_central.context.decrypt_message(&id, &bob_message1).await.unwrap(); + assert_eq!(decrypt.app_msg.unwrap(), b"Hello Bob"); + + // Moving the epochs once more should cause an error + let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + alice_central.context.commit_accepted(&id).await.unwrap(); + bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + + let decrypt = bob_central.context.decrypt_message(&id, &bob_message2).await; + assert!(matches!(decrypt.unwrap_err(), CryptoError::MessageEpochTooOld)); + }) + }) .await } @@ -1545,70 +1409,63 @@ mod tests { #[wasm_bindgen_test] async fn should_throw_specialized_error_when_epoch_desynchronized(mut case: TestCase) { case.cfg.custom.out_of_order_tolerance = 0; - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - // Alice generates a bunch of soon to be outdated messages - let old_proposal = alice_central - .context - .new_update_proposal(&id) - .await - .unwrap() - .proposal - .to_bytes() - .unwrap(); - alice_central - .get_conversation_unchecked(&id) - .await - .group - .clear_pending_proposals(); - let old_commit = alice_central - .context - .update_keying_material(&id) - .await - .unwrap() - .commit - .to_bytes() - .unwrap(); - alice_central.context.clear_pending_commit(&id).await.unwrap(); - - // Now let's jump to next epoch - let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - alice_central.context.commit_accepted(&id).await.unwrap(); - bob_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - - // trying to consume outdated messages should fail with a dedicated error - let decrypt_err = bob_central - .context - .decrypt_message(&id, &old_proposal) - .await - .unwrap_err(); - - assert!(matches!(decrypt_err, CryptoError::StaleProposal)); - - let decrypt_err = bob_central.context.decrypt_message(&id, &old_commit).await.unwrap_err(); - - assert!(matches!(decrypt_err, CryptoError::StaleCommit)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + // Alice generates a bunch of soon to be outdated messages + let old_proposal = alice_central + .context + .new_update_proposal(&id) + .await + .unwrap() + .proposal + .to_bytes() + .unwrap(); + alice_central + .get_conversation_unchecked(&id) + .await + .group + .clear_pending_proposals(); + let old_commit = alice_central + .context + .update_keying_material(&id) + .await + .unwrap() + .commit + .to_bytes() + .unwrap(); + alice_central.context.clear_pending_commit(&id).await.unwrap(); + + // Now let's jump to next epoch + let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + alice_central.context.commit_accepted(&id).await.unwrap(); + bob_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + + // trying to consume outdated messages should fail with a dedicated error + let decrypt_err = bob_central + .context + .decrypt_message(&id, &old_proposal) + .await + .unwrap_err(); + + assert!(matches!(decrypt_err, CryptoError::StaleProposal)); + + let decrypt_err = bob_central.context.decrypt_message(&id, &old_commit).await.unwrap_err(); + + assert!(matches!(decrypt_err, CryptoError::StaleCommit)); + }) + }) .await } } diff --git a/crypto/src/mls/conversation/duplicate.rs b/crypto/src/mls/conversation/duplicate.rs index 78be7dfeb..1ef0b8678 100644 --- a/crypto/src/mls/conversation/duplicate.rs +++ b/crypto/src/mls/conversation/duplicate.rs @@ -55,63 +55,7 @@ mod tests { async fn decrypting_duplicate_member_commit_should_fail(case: TestCase) { // cannot work in pure ciphertext since we'd have to decrypt the message first if !case.is_pure_ciphertext() { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - // an commit to verify that we can still detect wrong epoch correctly - let unknown_commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - alice_central.context.clear_pending_commit(&id).await.unwrap(); - - let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; - alice_central.context.commit_accepted(&id).await.unwrap(); - - // decrypt once ... ok - bob_central - .context - .decrypt_message(&id, &commit.to_bytes().unwrap()) - .await - .unwrap(); - // decrypt twice ... not ok - let decrypt_duplicate = bob_central - .context - .decrypt_message(&id, &commit.to_bytes().unwrap()) - .await; - assert!(matches!(decrypt_duplicate.unwrap_err(), CryptoError::DuplicateMessage)); - - // Decrypting unknown commit. - // It fails with this error since it's not the commit who has created this epoch - let decrypt_lost_commit = bob_central - .context - .decrypt_message(&id, &unknown_commit.to_bytes().unwrap()) - .await; - assert!(matches!(decrypt_lost_commit.unwrap_err(), CryptoError::StaleCommit)); - }) - }, - ) - .await - } - } - - #[apply(all_cred_cipher)] - #[wasm_bindgen_test] - async fn decrypting_duplicate_external_commit_should_fail(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { Box::pin(async move { let id = conversation_id(); alice_central @@ -119,161 +63,195 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let gi = alice_central.get_group_info(&id).await; + // an commit to verify that we can still detect wrong epoch correctly + let unknown_commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + alice_central.context.clear_pending_commit(&id).await.unwrap(); - // an external commit to verify that we can still detect wrong epoch correctly - let unknown_ext_commit = bob_central - .context - .join_by_external_commit(gi.clone(), case.custom_cfg(), case.credential_type) - .await - .unwrap() - .commit; - bob_central - .context - .clear_pending_group_from_external_commit(&id) - .await - .unwrap(); - - let ext_commit = bob_central - .context - .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) - .await - .unwrap() - .commit; - bob_central - .context - .merge_pending_group_from_external_commit(&id) - .await - .unwrap(); + let commit = alice_central.context.update_keying_material(&id).await.unwrap().commit; + alice_central.context.commit_accepted(&id).await.unwrap(); // decrypt once ... ok - alice_central + bob_central .context - .decrypt_message(&id, &ext_commit.to_bytes().unwrap()) + .decrypt_message(&id, &commit.to_bytes().unwrap()) .await .unwrap(); // decrypt twice ... not ok - let decryption = alice_central + let decrypt_duplicate = bob_central .context - .decrypt_message(&id, &ext_commit.to_bytes().unwrap()) + .decrypt_message(&id, &commit.to_bytes().unwrap()) .await; - assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); + assert!(matches!(decrypt_duplicate.unwrap_err(), CryptoError::DuplicateMessage)); - // Decrypting unknown external commit. - // It fails with this error since it's not the external commit who has created this epoch - let decryption = alice_central + // Decrypting unknown commit. + // It fails with this error since it's not the commit who has created this epoch + let decrypt_lost_commit = bob_central .context - .decrypt_message(&id, &unknown_ext_commit.to_bytes().unwrap()) + .decrypt_message(&id, &unknown_commit.to_bytes().unwrap()) .await; - assert!(matches!(decryption.unwrap_err(), CryptoError::StaleCommit)); + assert!(matches!(decrypt_lost_commit.unwrap_err(), CryptoError::StaleCommit)); }) - }, - ) + }) + .await + } + } + + #[apply(all_cred_cipher)] + #[wasm_bindgen_test] + async fn decrypting_duplicate_external_commit_should_fail(case: TestCase) { + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let gi = alice_central.get_group_info(&id).await; + + // an external commit to verify that we can still detect wrong epoch correctly + let unknown_ext_commit = bob_central + .context + .join_by_external_commit(gi.clone(), case.custom_cfg(), case.credential_type) + .await + .unwrap() + .commit; + bob_central + .context + .clear_pending_group_from_external_commit(&id) + .await + .unwrap(); + + let ext_commit = bob_central + .context + .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) + .await + .unwrap() + .commit; + bob_central + .context + .merge_pending_group_from_external_commit(&id) + .await + .unwrap(); + + // decrypt once ... ok + alice_central + .context + .decrypt_message(&id, &ext_commit.to_bytes().unwrap()) + .await + .unwrap(); + // decrypt twice ... not ok + let decryption = alice_central + .context + .decrypt_message(&id, &ext_commit.to_bytes().unwrap()) + .await; + assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); + + // Decrypting unknown external commit. + // It fails with this error since it's not the external commit who has created this epoch + let decryption = alice_central + .context + .decrypt_message(&id, &unknown_ext_commit.to_bytes().unwrap()) + .await; + assert!(matches!(decryption.unwrap_err(), CryptoError::StaleCommit)); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn decrypting_duplicate_proposal_should_fail(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; - - // decrypt once ... ok - bob_central - .context - .decrypt_message(&id, &proposal.to_bytes().unwrap()) - .await - .unwrap(); - - // decrypt twice ... not ok - let decryption = bob_central - .context - .decrypt_message(&id, &proposal.to_bytes().unwrap()) - .await; - assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); - - // advance Bob's epoch to trigger failure - bob_central.context.commit_pending_proposals(&id).await.unwrap(); - bob_central.context.commit_accepted(&id).await.unwrap(); - - // Epoch has advanced so we cannot detect duplicates anymore - let decryption = bob_central - .context - .decrypt_message(&id, &proposal.to_bytes().unwrap()) - .await; - assert!(matches!(decryption.unwrap_err(), CryptoError::StaleProposal)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; + + // decrypt once ... ok + bob_central + .context + .decrypt_message(&id, &proposal.to_bytes().unwrap()) + .await + .unwrap(); + + // decrypt twice ... not ok + let decryption = bob_central + .context + .decrypt_message(&id, &proposal.to_bytes().unwrap()) + .await; + assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); + + // advance Bob's epoch to trigger failure + bob_central.context.commit_pending_proposals(&id).await.unwrap(); + bob_central.context.commit_accepted(&id).await.unwrap(); + + // Epoch has advanced so we cannot detect duplicates anymore + let decryption = bob_central + .context + .decrypt_message(&id, &proposal.to_bytes().unwrap()) + .await; + assert!(matches!(decryption.unwrap_err(), CryptoError::StaleProposal)); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn decrypting_duplicate_external_proposal_should_fail(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let epoch = alice_central.context.conversation_epoch(&id).await.unwrap(); - - let ext_proposal = bob_central - .context - .new_external_add_proposal(id.clone(), epoch.into(), case.ciphersuite(), case.credential_type) - .await - .unwrap(); - - // decrypt once ... ok - alice_central - .context - .decrypt_message(&id, &ext_proposal.to_bytes().unwrap()) - .await - .unwrap(); - - // decrypt twice ... not ok - let decryption = alice_central - .context - .decrypt_message(&id, &ext_proposal.to_bytes().unwrap()) - .await; - assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); - - // advance alice's epoch - alice_central.context.commit_pending_proposals(&id).await.unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - - // Epoch has advanced so we cannot detect duplicates anymore - let decryption = alice_central - .context - .decrypt_message(&id, &ext_proposal.to_bytes().unwrap()) - .await; - assert!(matches!(decryption.unwrap_err(), CryptoError::StaleProposal)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let epoch = alice_central.context.conversation_epoch(&id).await.unwrap(); + + let ext_proposal = bob_central + .context + .new_external_add_proposal(id.clone(), epoch.into(), case.ciphersuite(), case.credential_type) + .await + .unwrap(); + + // decrypt once ... ok + alice_central + .context + .decrypt_message(&id, &ext_proposal.to_bytes().unwrap()) + .await + .unwrap(); + + // decrypt twice ... not ok + let decryption = alice_central + .context + .decrypt_message(&id, &ext_proposal.to_bytes().unwrap()) + .await; + assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); + + // advance alice's epoch + alice_central.context.commit_pending_proposals(&id).await.unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + + // Epoch has advanced so we cannot detect duplicates anymore + let decryption = alice_central + .context + .decrypt_message(&id, &ext_proposal.to_bytes().unwrap()) + .await; + assert!(matches!(decryption.unwrap_err(), CryptoError::StaleProposal)); + }) + }) .await } @@ -281,33 +259,26 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn decrypting_duplicate_application_message_should_fail(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - - // decrypt once .. ok - bob_central.context.decrypt_message(&id, &encrypted).await.unwrap(); - // decrypt twice .. not ok - let decryption = bob_central.context.decrypt_message(&id, &encrypted).await; - assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + + // decrypt once .. ok + bob_central.context.decrypt_message(&id, &encrypted).await.unwrap(); + // decrypt twice .. not ok + let decryption = bob_central.context.decrypt_message(&id, &encrypted).await; + assert!(matches!(decryption.unwrap_err(), CryptoError::DuplicateMessage)); + }) + }) .await } } diff --git a/crypto/src/mls/conversation/encrypt.rs b/crypto/src/mls/conversation/encrypt.rs index fb7a39c0e..89016f12e 100644 --- a/crypto/src/mls/conversation/encrypt.rs +++ b/crypto/src/mls/conversation/encrypt.rs @@ -10,10 +10,10 @@ use mls_crypto_provider::TransactionalCryptoProvider; use openmls::prelude::MlsMessageOutBody; +use super::MlsConversation; +use crate::context::CentralContext; use crate::prelude::Client; use crate::{mls::ConversationId, CryptoError, CryptoResult, MlsError}; -use crate::context::CentralContext; -use super::MlsConversation; /// Abstraction over a MLS group capable of encrypting a MLS message impl MlsConversation { @@ -28,7 +28,8 @@ impl MlsConversation { backend: &TransactionalCryptoProvider, ) -> CryptoResult> { let signer = &self - .find_current_credential_bundle(client).await? + .find_current_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; let encrypted = self @@ -88,36 +89,29 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn can_encrypt_app_message(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - assert_ne!(&msg[..], &encrypted[..]); - let decrypted = bob_central - .context - .decrypt_message(&id, encrypted) - .await - .unwrap() - .app_msg - .unwrap(); - assert_eq!(&decrypted[..], &msg[..]); - }) - }, - ) + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + assert_ne!(&msg[..], &encrypted[..]); + let decrypted = bob_central + .context + .decrypt_message(&id, encrypted) + .await + .unwrap() + .app_msg + .unwrap(); + assert_eq!(&decrypted[..], &msg[..]); + }) + }) .await } @@ -125,48 +119,41 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn can_encrypt_consecutive_messages(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); - let msg = b"Hello bob"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - assert_ne!(&msg[..], &encrypted[..]); - let decrypted = bob_central - .context - .decrypt_message(&id, encrypted) - .await - .unwrap() - .app_msg - .unwrap(); - assert_eq!(&decrypted[..], &msg[..]); + let msg = b"Hello bob"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + assert_ne!(&msg[..], &encrypted[..]); + let decrypted = bob_central + .context + .decrypt_message(&id, encrypted) + .await + .unwrap() + .app_msg + .unwrap(); + assert_eq!(&decrypted[..], &msg[..]); - let msg = b"Hello bob again"; - let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); - assert_ne!(&msg[..], &encrypted[..]); - let decrypted = bob_central - .context - .decrypt_message(&id, encrypted) - .await - .unwrap() - .app_msg - .unwrap(); - assert_eq!(&decrypted[..], &msg[..]); - }) - }, - ) + let msg = b"Hello bob again"; + let encrypted = alice_central.context.encrypt_message(&id, msg).await.unwrap(); + assert_ne!(&msg[..], &encrypted[..]); + let decrypted = bob_central + .context + .decrypt_message(&id, encrypted) + .await + .unwrap() + .app_msg + .unwrap(); + assert_eq!(&decrypted[..], &msg[..]); + }) + }) .await } } diff --git a/crypto/src/mls/conversation/export.rs b/crypto/src/mls/conversation/export.rs index b6817bfb9..003ec0802 100644 --- a/crypto/src/mls/conversation/export.rs +++ b/crypto/src/mls/conversation/export.rs @@ -18,8 +18,7 @@ use crate::context::CentralContext; use crate::mls::{ - client::id::ClientId, ConversationId, CryptoError, CryptoResult, MlsCentral, - MlsConversation, MlsError, + client::id::ClientId, ConversationId, CryptoError, CryptoResult, MlsCentral, MlsConversation, MlsError, }; impl MlsConversation { @@ -199,28 +198,21 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn can_get_client_ids(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - assert_eq!(alice_central.context.get_client_ids(&id).await.unwrap().len(), 1); - - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - assert_eq!(alice_central.context.get_client_ids(&id).await.unwrap().len(), 2); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + assert_eq!(alice_central.context.get_client_ids(&id).await.unwrap().len(), 1); + + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + assert_eq!(alice_central.context.get_client_ids(&id).await.unwrap().len(), 2); + }) + }) .await } diff --git a/crypto/src/mls/conversation/external_sender.rs b/crypto/src/mls/conversation/external_sender.rs index 1282f7b54..70ace5243 100644 --- a/crypto/src/mls/conversation/external_sender.rs +++ b/crypto/src/mls/conversation/external_sender.rs @@ -1,5 +1,5 @@ -use crate::prelude::{ConversationId, CryptoError, CryptoResult, MlsCentral, MlsConversation}; use crate::context::CentralContext; +use crate::prelude::{ConversationId, CryptoError, CryptoResult, MlsCentral, MlsConversation}; impl MlsCentral { /// Returns the raw public key of the single external sender present in this group. diff --git a/crypto/src/mls/conversation/leaf_node_validation.rs b/crypto/src/mls/conversation/leaf_node_validation.rs index fbbc39a4d..d4853ec3b 100644 --- a/crypto/src/mls/conversation/leaf_node_validation.rs +++ b/crypto/src/mls/conversation/leaf_node_validation.rs @@ -34,9 +34,7 @@ mod tests { .unwrap(); // should fail when creating Add proposal - let invalid_kp = bob_central - .new_keypackage(&case, Lifetime::new(expiration_time)) - .await; + let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await; // Give time to the KeyPackage to expire let expiration_time = core::time::Duration::from_secs(expiration_time); @@ -59,9 +57,7 @@ mod tests { let expiration_time = 14; let start = fluvio_wasm_timer::Instant::now(); - let invalid_kp = bob_central - .new_keypackage(&case, Lifetime::new(expiration_time)) - .await; + let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await; // Give time to the KeyPackage to expire let expiration_time = core::time::Duration::from_secs(expiration_time); @@ -108,10 +104,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let invalid_kp = charlie_central .new_keypackage(&case, Lifetime::new(expiration_time)) @@ -164,10 +157,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // should fail when receiving Add commit let invalid_kp = charlie_central @@ -210,58 +200,51 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_validate_leaf_node_when_receiving_welcome(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let expiration_time = 14; - let start = fluvio_wasm_timer::Instant::now(); - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let invalid_kp = bob_central - .new_keypackage(&case, Lifetime::new(expiration_time)) - .await; - let commit = alice_central - .context - .add_members_to_conversation(&id, vec![invalid_kp.into()]) - .await - .unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - - let elapsed = start.elapsed(); - // Give time to the certificate to expire - let expiration_time = core::time::Duration::from_secs(expiration_time); - if expiration_time > elapsed { - async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1)) - .await; - } - - let process_welcome = bob_central - .context - .process_welcome_message(commit.welcome.into(), case.custom_cfg()) - .await; - - // TODO: currently succeeds as we don't anymore validate KeyPackage lifetime upon reception: find another way to craft an invalid KeyPackage. Tracking issue number: WPB-9623 - process_welcome.unwrap(); - /*assert!(matches!( - process_welcome.unwrap_err(), - CryptoError::MlsError(MlsError::MlsWelcomeError(WelcomeError::PublicGroupError( - CreationFromExternalError::TreeSyncError( - TreeSyncFromNodesError::LeafNodeValidationError(LeafNodeValidationError::Lifetime( - _ - )) - ) - ))) - ));*/ - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let expiration_time = 14; + let start = fluvio_wasm_timer::Instant::now(); + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await; + let commit = alice_central + .context + .add_members_to_conversation(&id, vec![invalid_kp.into()]) + .await + .unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + + let elapsed = start.elapsed(); + // Give time to the certificate to expire + let expiration_time = core::time::Duration::from_secs(expiration_time); + if expiration_time > elapsed { + async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1)).await; + } + + let process_welcome = bob_central + .context + .process_welcome_message(commit.welcome.into(), case.custom_cfg()) + .await; + + // TODO: currently succeeds as we don't anymore validate KeyPackage lifetime upon reception: find another way to craft an invalid KeyPackage. Tracking issue number: WPB-9623 + process_welcome.unwrap(); + /*assert!(matches!( + process_welcome.unwrap_err(), + CryptoError::MlsError(MlsError::MlsWelcomeError(WelcomeError::PublicGroupError( + CreationFromExternalError::TreeSyncError( + TreeSyncFromNodesError::LeafNodeValidationError(LeafNodeValidationError::Lifetime( + _ + )) + ) + ))) + ));*/ + }) + }) .await } } diff --git a/crypto/src/mls/conversation/merge.rs b/crypto/src/mls/conversation/merge.rs index f7ecd645c..73fbcca0d 100644 --- a/crypto/src/mls/conversation/merge.rs +++ b/crypto/src/mls/conversation/merge.rs @@ -17,12 +17,12 @@ use openmls_traits::OpenMlsCryptoProvider; use mls_crypto_provider::TransactionalCryptoProvider; +use crate::context::CentralContext; use crate::{ mls::{ConversationId, MlsConversation}, prelude::{decrypt::MlsBufferedConversationDecryptMessage, MlsProposalRef}, CryptoError, CryptoResult, MlsError, }; -use crate::context::CentralContext; /// Abstraction over a MLS group capable of merging a commit impl MlsConversation { @@ -181,54 +181,26 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_apply_pending_commit(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - alice_central - .context - .remove_members_from_conversation(&id, &[bob_central.get_client_id().await]) - .await - .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + alice_central + .context + .remove_members_from_conversation(&id, &[bob_central.get_client_id().await]) + .await + .unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + alice_central.context.commit_accepted(&id).await.unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); + }) + }) .await } @@ -313,10 +285,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); assert!(alice_central.pending_proposals(&id).await.is_empty()); let charlie_kp = charlie_central.get_one_key_package(&case).await; diff --git a/crypto/src/mls/conversation/mod.rs b/crypto/src/mls/conversation/mod.rs index 1d23d7b4e..438984539 100644 --- a/crypto/src/mls/conversation/mod.rs +++ b/crypto/src/mls/conversation/mod.rs @@ -228,11 +228,7 @@ impl MlsConversation { /// Marks this conversation as child of another. /// Prequisite: Being a member of this group and for it to be stored in the keystore #[cfg_attr(not(test), tracing::instrument(err, skip(self, keystore), fields(parent_id = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, parent_id))))] - pub async fn mark_as_child_of( - &mut self, - parent_id: &ConversationId, - keystore: &Connection, - ) -> CryptoResult<()> { + pub async fn mark_as_child_of(&mut self, parent_id: &ConversationId, keystore: &Connection) -> CryptoResult<()> { if keystore.mls_group_exists(parent_id).await { self.parent_id = Some(parent_id.clone()); self.persist_group_when_changed(keystore, true).await?; @@ -322,12 +318,16 @@ impl CentralContext { #[cfg(test)] mod tests { use crate::e2e_identity::rotate::tests::all::failsafe_ctx; - + use wasm_bindgen_test::*; - use crate::{prelude::{ - ClientIdentifier, MlsCentralConfiguration, MlsConversationCreationMessage, INITIAL_KEYING_MATERIAL_COUNT, - }, test_utils::*, CoreCrypto}; + use crate::{ + prelude::{ + ClientIdentifier, MlsCentralConfiguration, MlsConversationCreationMessage, INITIAL_KEYING_MATERIAL_COUNT, + }, + test_utils::*, + CoreCrypto, + }; use super::*; @@ -354,14 +354,7 @@ mod tests { .as_slice(), id ); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); let alice_can_send_message = alice_central.context.encrypt_message(&id, b"me").await; assert!(alice_can_send_message.is_ok()); }) @@ -372,72 +365,51 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn create_1_1_conversation_should_succeed(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let bob = bob_central.rand_key_package(&case).await; + let MlsConversationCreationMessage { welcome, .. } = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap(); + // before merging, commit is not applied + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); + alice_central.context.commit_accepted(&id).await.unwrap(); + + assert_eq!(alice_central.get_conversation_unchecked(&id).await.id, id); + assert_eq!( alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) + .get_conversation_unchecked(&id) .await - .unwrap(); + .group + .group_id() + .as_slice(), + id + ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); - let bob = bob_central.rand_key_package(&case).await; - let MlsConversationCreationMessage { welcome, .. } = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap(); - // before merging, commit is not applied - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); - alice_central.context.commit_accepted(&id).await.unwrap(); - - assert_eq!(alice_central.get_conversation_unchecked(&id).await.id, id); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .group - .group_id() - .as_slice(), - id - ); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - - bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await - .unwrap(); + bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await + .unwrap(); - assert_eq!( - bob_central.get_conversation_unchecked(&id).await.id(), - alice_central.get_conversation_unchecked(&id).await.id() - ); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); - }) - }, - ) + assert_eq!( + bob_central.get_conversation_unchecked(&id).await.id(), + alice_central.get_conversation_unchecked(&id).await.id() + ); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + }) + }) .await; } @@ -523,14 +495,7 @@ mod tests { .await .unwrap(); // before merging, commit is not applied - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); alice_central.context.commit_accepted(&id).await.unwrap(); assert_eq!(alice_central.get_conversation_unchecked(&id).await.id, id); @@ -544,18 +509,15 @@ mod tests { id ); assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), + alice_central.get_conversation_unchecked(&id).await.members().len(), 1 + number_of_friends ); let mut bob_and_friends_groups = Vec::with_capacity(bob_and_friends.len()); // TODO: Do things in parallel, this is waaaaay too slow (takes around 5 minutes). Tracking issue: WPB-9624 for c in bob_and_friends { - c.context.process_welcome_message(welcome.clone().into(), case.custom_cfg()) + c.context + .process_welcome_message(welcome.clone().into(), case.custom_cfg()) .await .unwrap(); assert!(c.try_talk_to(&id, &alice_central).await.is_ok()); diff --git a/crypto/src/mls/conversation/orphan_welcome.rs b/crypto/src/mls/conversation/orphan_welcome.rs index 113120ac7..a0b9c40b4 100644 --- a/crypto/src/mls/conversation/orphan_welcome.rs +++ b/crypto/src/mls/conversation/orphan_welcome.rs @@ -16,45 +16,41 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn orphan_welcome_should_generate_external_commit(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let bob = bob_central.rand_key_package(&case).await; - let bob_kp_ref = KeyPackage::from(bob.clone()) - .hash_ref(bob_central.context.mls_provider().await.unwrap().crypto()) - .unwrap(); - - // Alice invites Bob with a KeyPackage... - let welcome = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap() - .welcome; - - // ...Bob deletes locally (with the associated private key) before processing the Welcome - bob_central.context.delete_keypackages(&[bob_kp_ref]).await.unwrap(); - - // in that case a dedicated error is thrown for clients to identify this case - // and rejoin with an external commit - let process_welcome = bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await; - assert!(matches!(process_welcome.unwrap_err(), CryptoError::OrphanWelcome)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let bob = bob_central.rand_key_package(&case).await; + let bob_kp_ref = KeyPackage::from(bob.clone()) + .hash_ref(bob_central.context.mls_provider().await.unwrap().crypto()) + .unwrap(); + + // Alice invites Bob with a KeyPackage... + let welcome = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap() + .welcome; + + // ...Bob deletes locally (with the associated private key) before processing the Welcome + bob_central.context.delete_keypackages(&[bob_kp_ref]).await.unwrap(); + + // in that case a dedicated error is thrown for clients to identify this case + // and rejoin with an external commit + let process_welcome = bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await; + assert!(matches!(process_welcome.unwrap_err(), CryptoError::OrphanWelcome)); + }) + }) .await; } } diff --git a/crypto/src/mls/conversation/own_commit.rs b/crypto/src/mls/conversation/own_commit.rs index cf409111d..593da1435 100644 --- a/crypto/src/mls/conversation/own_commit.rs +++ b/crypto/src/mls/conversation/own_commit.rs @@ -169,12 +169,7 @@ mod tests { .await; // create a commit. This will also store it in the store - let commit = alice_central - .context - .e2ei_rotate(&id, Some(&cb)) - .await - .unwrap() - .commit; + let commit = alice_central.context.e2ei_rotate(&id, Some(&cb)).await.unwrap().commit; assert!(alice_central.pending_commit(&id).await.is_some()); // since the pending commit is the same as the incoming one, it should succeed @@ -294,66 +289,62 @@ mod tests { if case.is_pure_ciphertext() { return; }; - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let conversation_id = conversation_id(); - alice_central - .context - .new_conversation(&conversation_id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - // No pending commit yet. - assert!(alice_central.pending_commit(&conversation_id).await.is_none()); - - let bob_key_package = bob_central.rand_key_package(&case).await; - - // Create the commit that we're going to tamper with. - let add_bob_message = alice_central - .context - .add_members_to_conversation(&conversation_id, vec![bob_key_package]) - .await - .unwrap(); - - // Now there is a pending commit. - assert!(alice_central.pending_commit(&conversation_id).await.is_some()); - - let commit_serialized = &mut add_bob_message.commit.to_bytes().unwrap(); - - // Tamper with the commit; this is the signature region, however, - // the membership tag covers the signature, so this will result in an - // invalid membership tag error emitted by openmls. - commit_serialized[355] = commit_serialized[355].wrapping_add(1); - - let decryption_result = alice_central - .context - .decrypt_message(&conversation_id, commit_serialized) - .await; - assert!(matches!( - decryption_result.unwrap_err(), - CryptoError::MlsError(MlsError::MlsMessageError(ProcessMessageError::ValidationError( - ValidationError::InvalidMembershipTag - ))) - )); - - // There is still a pending commit. - assert!(alice_central.pending_commit(&conversation_id).await.is_some()); - - // Positive case: Alice decrypts the commit... - assert!(alice_central - .context - .decrypt_message(&conversation_id, &add_bob_message.commit.to_bytes().unwrap()) - .await - .is_ok()); - - // ...and has cleared the pending commit. - assert!(alice_central.pending_commit(&conversation_id).await.is_none()); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let conversation_id = conversation_id(); + alice_central + .context + .new_conversation(&conversation_id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + // No pending commit yet. + assert!(alice_central.pending_commit(&conversation_id).await.is_none()); + + let bob_key_package = bob_central.rand_key_package(&case).await; + + // Create the commit that we're going to tamper with. + let add_bob_message = alice_central + .context + .add_members_to_conversation(&conversation_id, vec![bob_key_package]) + .await + .unwrap(); + + // Now there is a pending commit. + assert!(alice_central.pending_commit(&conversation_id).await.is_some()); + + let commit_serialized = &mut add_bob_message.commit.to_bytes().unwrap(); + + // Tamper with the commit; this is the signature region, however, + // the membership tag covers the signature, so this will result in an + // invalid membership tag error emitted by openmls. + commit_serialized[355] = commit_serialized[355].wrapping_add(1); + + let decryption_result = alice_central + .context + .decrypt_message(&conversation_id, commit_serialized) + .await; + assert!(matches!( + decryption_result.unwrap_err(), + CryptoError::MlsError(MlsError::MlsMessageError(ProcessMessageError::ValidationError( + ValidationError::InvalidMembershipTag + ))) + )); + + // There is still a pending commit. + assert!(alice_central.pending_commit(&conversation_id).await.is_some()); + + // Positive case: Alice decrypts the commit... + assert!(alice_central + .context + .decrypt_message(&conversation_id, &add_bob_message.commit.to_bytes().unwrap()) + .await + .is_ok()); + + // ...and has cleared the pending commit. + assert!(alice_central.pending_commit(&conversation_id).await.is_none()); + }) + }) .await } } diff --git a/crypto/src/mls/conversation/proposal.rs b/crypto/src/mls/conversation/proposal.rs index 0f6175df3..602e5b6ae 100644 --- a/crypto/src/mls/conversation/proposal.rs +++ b/crypto/src/mls/conversation/proposal.rs @@ -30,7 +30,8 @@ impl MlsConversation { key_package: KeyPackageIn, ) -> CryptoResult { let signer = &self - .find_current_credential_bundle(client).await? + .find_current_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; @@ -64,7 +65,8 @@ impl MlsConversation { member: LeafNodeIndex, ) -> CryptoResult { let signer = &self - .find_current_credential_bundle(client).await? + .find_current_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; let proposal = self @@ -98,13 +100,15 @@ impl MlsConversation { leaf_node: Option, ) -> CryptoResult { let msg_signer = &self - .find_current_credential_bundle(client).await? + .find_current_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; let proposal = if let Some(leaf_node) = leaf_node { let leaf_node_signer = &self - .find_most_recent_credential_bundle(client).await? + .find_most_recent_credential_bundle(client) + .await? .ok_or(CryptoError::IdentityInitializationError)? .signature_key; @@ -186,10 +190,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let charlie_kp = charlie_central.get_one_key_package(&case).await; assert!(alice_central.pending_proposals(&id).await.is_empty()); @@ -212,14 +213,7 @@ mod tests { .unwrap() .unwrap(); bob_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 3); // if 'new_proposal' wasn't durable this would fail because proposal would // not be referenced in commit @@ -228,14 +222,7 @@ mod tests { .decrypt_message(&id, commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 3); charlie_central .try_join_from_welcome( @@ -246,14 +233,7 @@ mod tests { ) .await .unwrap(); - assert_eq!( - charlie_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(charlie_central.get_conversation_unchecked(&id).await.members().len(), 3); }) }, ) @@ -304,14 +284,7 @@ mod tests { .unwrap() .commit; bob_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); // if 'new_proposal' wasn't durable this would fail because proposal would // not be referenced in commit @@ -320,14 +293,7 @@ mod tests { .decrypt_message(&id, commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); }) }, ) @@ -341,90 +307,80 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn can_propose_updating(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let bob_keys = bob_central - .get_conversation_unchecked(&id) - .await - .signature_keys() - .collect::>(); - let alice_keys = alice_central - .get_conversation_unchecked(&id) - .await - .signature_keys() - .collect::>(); - assert!(alice_keys.iter().all(|a_key| bob_keys.contains(a_key))); - let alice_key = alice_central - .encryption_key_of(&id, alice_central.get_client_id().await) - .await; - - let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; - bob_central - .context - .decrypt_message(&id, proposal.to_bytes().unwrap()) - .await - .unwrap(); - let commit = bob_central - .context - .commit_pending_proposals(&id) - .await - .unwrap() - .unwrap() - .commit; - - // before merging, commit is not applied - assert!(bob_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .contains(&alice_key)); - bob_central.context.commit_accepted(&id).await.unwrap(); - assert!(!bob_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .contains(&alice_key)); - - assert!(alice_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .contains(&alice_key)); - // if 'new_proposal' wasn't durable this would fail because proposal would - // not be referenced in commit - alice_central - .context - .decrypt_message(&id, commit.to_bytes().unwrap()) - .await - .unwrap(); - assert!(!alice_central - .get_conversation_unchecked(&id) - .await - .encryption_keys() - .contains(&alice_key)); - - // ensuring both can encrypt messages - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let bob_keys = bob_central + .get_conversation_unchecked(&id) + .await + .signature_keys() + .collect::>(); + let alice_keys = alice_central + .get_conversation_unchecked(&id) + .await + .signature_keys() + .collect::>(); + assert!(alice_keys.iter().all(|a_key| bob_keys.contains(a_key))); + let alice_key = alice_central + .encryption_key_of(&id, alice_central.get_client_id().await) + .await; + + let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; + bob_central + .context + .decrypt_message(&id, proposal.to_bytes().unwrap()) + .await + .unwrap(); + let commit = bob_central + .context + .commit_pending_proposals(&id) + .await + .unwrap() + .unwrap() + .commit; + + // before merging, commit is not applied + assert!(bob_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .contains(&alice_key)); + bob_central.context.commit_accepted(&id).await.unwrap(); + assert!(!bob_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .contains(&alice_key)); + + assert!(alice_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .contains(&alice_key)); + // if 'new_proposal' wasn't durable this would fail because proposal would + // not be referenced in commit + alice_central + .context + .decrypt_message(&id, commit.to_bytes().unwrap()) + .await + .unwrap(); + assert!(!alice_central + .get_conversation_unchecked(&id) + .await + .encryption_keys() + .contains(&alice_key)); + + // ensuring both can encrypt messages + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + }) + }) .await; } } @@ -435,42 +391,35 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_prevent_out_of_order_proposals(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - - let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; - - bob_central - .context - .decrypt_message(&id, &proposal.to_bytes().unwrap()) - .await - .unwrap(); - bob_central.context.commit_pending_proposals(&id).await.unwrap(); - // epoch++ - bob_central.context.commit_accepted(&id).await.unwrap(); - - // fails when we try to decrypt a proposal for past epoch - let past_proposal = bob_central - .context - .decrypt_message(&id, &proposal.to_bytes().unwrap()) - .await; - assert!(matches!(past_proposal.unwrap_err(), CryptoError::StaleProposal)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + + let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; + + bob_central + .context + .decrypt_message(&id, &proposal.to_bytes().unwrap()) + .await + .unwrap(); + bob_central.context.commit_pending_proposals(&id).await.unwrap(); + // epoch++ + bob_central.context.commit_accepted(&id).await.unwrap(); + + // fails when we try to decrypt a proposal for past epoch + let past_proposal = bob_central + .context + .decrypt_message(&id, &proposal.to_bytes().unwrap()) + .await; + assert!(matches!(past_proposal.unwrap_err(), CryptoError::StaleProposal)); + }) + }) .await; } } diff --git a/crypto/src/mls/conversation/renew.rs b/crypto/src/mls/conversation/renew.rs index 54cf49a43..511dfd103 100644 --- a/crypto/src/mls/conversation/renew.rs +++ b/crypto/src/mls/conversation/renew.rs @@ -150,7 +150,8 @@ impl MlsConversation { let sc = self.signature_scheme(); let ct = self.own_credential_type()?; let cb = client - .find_most_recent_credential_bundle(sc, ct).await + .find_most_recent_credential_bundle(sc, ct) + .await .ok_or(CryptoError::MlsNotInitialized)?; leaf_node.set_credential_with_key(cb.to_mls_credential_with_key()); @@ -189,10 +190,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); assert!(alice_central.pending_proposals(&id).await.is_empty()); alice_central.context.new_update_proposal(&id).await.unwrap(); @@ -210,10 +208,7 @@ mod tests { .proposals; // Alice should renew the proposal because its hers assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); // It should also renew the proposal when in pending_commit alice_central.context.commit_pending_proposals(&id).await.unwrap(); @@ -228,10 +223,7 @@ mod tests { // Alice should renew the proposal because its hers // It should also replace existing one assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -252,10 +244,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); alice_central.context.update_keying_material(&id).await.unwrap(); assert!(alice_central.pending_commit(&id).await.is_some()); @@ -271,10 +260,7 @@ mod tests { .proposals; // Alice should renew the proposal because its her's assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -295,10 +281,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); assert!(alice_central.pending_proposals(&id).await.is_empty()); let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; @@ -323,10 +306,7 @@ mod tests { .proposals; // Alice proposal should not be renew as it was in valid commit assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); // Same if proposal is also in pending commit let proposal = alice_central.context.new_update_proposal(&id).await.unwrap().proposal; @@ -347,10 +327,7 @@ mod tests { .proposals; // Alice should not be renew as it was in valid commit assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -400,10 +377,7 @@ mod tests { .proposals; // Alice should not renew Bob's update proposal assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -428,10 +402,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let charlie_kp = charlie_central.get_one_key_package(&case).await; assert!(alice_central.pending_proposals(&id).await.is_empty()); @@ -453,10 +424,7 @@ mod tests { .proposals; // Alice proposal is not renewed since she also wanted to add Charlie assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -477,10 +445,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); let charlie_kp = charlie_central.get_one_key_package(&case).await; assert!(alice_central.pending_proposals(&id).await.is_empty()); @@ -506,10 +471,7 @@ mod tests { .proposals; // Alice proposal is not renewed since she also wanted to add Charlie assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -565,10 +527,7 @@ mod tests { .proposals; // which Alice should not renew since it's not hers assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -589,10 +548,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Alice proposes adding Charlie let charlie_kp = charlie_central.get_one_key_package(&case).await; @@ -611,10 +567,7 @@ mod tests { .proposals; // So Alice proposal should be renewed assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); // And same should happen when proposal is in pending commit alice_central.context.commit_pending_proposals(&id).await.unwrap(); @@ -629,10 +582,7 @@ mod tests { // So Alice proposal should also be renewed // It should also replace existing one assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -653,10 +603,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); // Alice commits adding Charlie let charlie = charlie_central.rand_key_package(&case).await; @@ -677,10 +624,7 @@ mod tests { .proposals; // So Alice proposal should be renewed assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -732,10 +676,7 @@ mod tests { .proposals; // Remove proposal is not renewed since commit does same assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -789,10 +730,7 @@ mod tests { .proposals; // Remove proposal is not renewed since by ref assert!(alice_central.pending_proposals(&id).await.is_empty()); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -814,15 +752,7 @@ mod tests { .await .unwrap(); alice_central - .invite_all( - &case, - &id, - [ - &bob_central, - &charlie_central, - &debbie_central, - ], - ) + .invite_all(&case, &id, [&bob_central, &charlie_central, &debbie_central]) .await .unwrap(); @@ -850,10 +780,7 @@ mod tests { .proposals; // Remove is renewed since valid commit removes another assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -875,15 +802,7 @@ mod tests { .await .unwrap(); alice_central - .invite_all( - &case, - &id, - [ - &bob_central, - &charlie_central, - &debbie_central, - ], - ) + .invite_all(&case, &id, [&bob_central, &charlie_central, &debbie_central]) .await .unwrap(); @@ -910,10 +829,7 @@ mod tests { .proposals; // Remove is renewed since valid commit removes another assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) @@ -935,15 +851,7 @@ mod tests { .await .unwrap(); alice_central - .invite_all( - &case, - &id, - [ - &bob_central, - &charlie_central, - &debbie_central, - ], - ) + .invite_all(&case, &id, [&bob_central, &charlie_central, &debbie_central]) .await .unwrap(); @@ -972,10 +880,7 @@ mod tests { .proposals; // Remove is renewed since valid commit removes another assert_eq!(alice_central.pending_proposals(&id).await.len(), 1); - assert_eq!( - proposals.len(), - alice_central.pending_proposals(&id).await.len() - ); + assert_eq!(proposals.len(), alice_central.pending_proposals(&id).await.len()); }) }, ) diff --git a/crypto/src/mls/conversation/welcome.rs b/crypto/src/mls/conversation/welcome.rs index 1b5f71308..192ecc4c2 100644 --- a/crypto/src/mls/conversation/welcome.rs +++ b/crypto/src/mls/conversation/welcome.rs @@ -1,5 +1,6 @@ use std::borrow::BorrowMut; +use crate::context::CentralContext; use crate::{ e2e_identity::init_certificates::NewCrlDistributionPoint, group_store::GroupStore, @@ -14,7 +15,6 @@ use mls_crypto_provider::TransactionalCryptoProvider; use openmls::prelude::{MlsGroup, MlsMessageIn, MlsMessageInBody, Welcome}; use openmls_traits::OpenMlsCryptoProvider; use tls_codec::Deserialize; -use crate::context::CentralContext; /// Contains everything client needs to know after decrypting an (encrypted) Welcome message #[derive(Debug)] @@ -155,84 +155,76 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn joining_from_welcome_should_prune_local_key_material(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - // has to be before the original key_package count because it creates one - let bob = bob_central.rand_key_package(&case).await; - // Keep track of the whatever amount was initially generated - let prev_count = bob_central.context.count_entities().await; - - // Create a conversation from alice, where she invites bob - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let MlsConversationCreationMessage { welcome, .. } = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap(); - - // Bob accepts the welcome message, and as such, it should prune the used keypackage from the store - bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await - .unwrap(); - - // Ensure we're left with 1 less keypackage bundle in the store, because it was consumed with the OpenMLS Welcome message - let next_count = bob_central.context.count_entities().await; - assert_eq!(next_count.key_package, prev_count.key_package - 1); - assert_eq!(next_count.hpke_private_key, prev_count.hpke_private_key - 1); - assert_eq!(next_count.encryption_keypair, prev_count.encryption_keypair - 1); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + // has to be before the original key_package count because it creates one + let bob = bob_central.rand_key_package(&case).await; + // Keep track of the whatever amount was initially generated + let prev_count = bob_central.context.count_entities().await; + + // Create a conversation from alice, where she invites bob + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let MlsConversationCreationMessage { welcome, .. } = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap(); + + // Bob accepts the welcome message, and as such, it should prune the used keypackage from the store + bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await + .unwrap(); + + // Ensure we're left with 1 less keypackage bundle in the store, because it was consumed with the OpenMLS Welcome message + let next_count = bob_central.context.count_entities().await; + assert_eq!(next_count.key_package, prev_count.key_package - 1); + assert_eq!(next_count.hpke_private_key, prev_count.hpke_private_key - 1); + assert_eq!(next_count.encryption_keypair, prev_count.encryption_keypair - 1); + }) + }) .await; } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn process_welcome_should_fail_when_already_exists(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - let bob = bob_central.rand_key_package(&case).await; - let welcome = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap() - .welcome; - - // Meanwhile Bob creates a conversation with the exact same id as the one he's trying to join - bob_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - let join_welcome = bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await; - assert!(matches!(join_welcome.unwrap_err(), CryptoError::ConversationAlreadyExists(i) if i == id)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + let bob = bob_central.rand_key_package(&case).await; + let welcome = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap() + .welcome; + + // Meanwhile Bob creates a conversation with the exact same id as the one he's trying to join + bob_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + let join_welcome = bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await; + assert!(matches!(join_welcome.unwrap_err(), CryptoError::ConversationAlreadyExists(i) if i == id)); + }) + }) .await; } } diff --git a/crypto/src/mls/conversation/wipe.rs b/crypto/src/mls/conversation/wipe.rs index 44322d638..c157da1ed 100644 --- a/crypto/src/mls/conversation/wipe.rs +++ b/crypto/src/mls/conversation/wipe.rs @@ -1,8 +1,8 @@ +use crate::context::CentralContext; use crate::prelude::{ConversationId, CryptoResult, MlsConversation, MlsError}; use core_crypto_keystore::CryptoKeystoreMls; use mls_crypto_provider::TransactionalCryptoProvider; use openmls_traits::OpenMlsCryptoProvider; -use crate::context::CentralContext; impl CentralContext { /// Destroys a group locally diff --git a/crypto/src/mls/credential/crl.rs b/crypto/src/mls/credential/crl.rs index 457f51606..efd1fc6c9 100644 --- a/crypto/src/mls/credential/crl.rs +++ b/crypto/src/mls/credential/crl.rs @@ -1,3 +1,4 @@ +use crate::context::CentralContext; use crate::e2e_identity::init_certificates::NewCrlDistributionPoint; use crate::{CryptoError, CryptoResult}; use core_crypto_keystore::{connection::FetchFromDatabase, entities::E2eiCrl}; @@ -9,7 +10,6 @@ use openmls::{ use openmls_traits::OpenMlsCryptoProvider; use std::collections::HashSet; use wire_e2e_identity::prelude::x509::extract_crl_uris; -use crate::context::CentralContext; #[cfg_attr(not(test), tracing::instrument(err, skip_all))] pub(crate) fn extract_crl_uris_from_credentials<'a>( diff --git a/crypto/src/mls/credential/mod.rs b/crypto/src/mls/credential/mod.rs index 54e5f6ea4..454bfe386 100644 --- a/crypto/src/mls/credential/mod.rs +++ b/crypto/src/mls/credential/mod.rs @@ -147,14 +147,19 @@ mod tests { use std::sync::Arc; use wasm_bindgen_test::*; - use crate::{mls::credential::x509::CertificatePrivateKey, prelude::{ - ClientIdentifier, ConversationId, CryptoError, E2eiConversationState, MlsCentral, MlsCentralConfiguration, - MlsCredentialType, INITIAL_KEYING_MATERIAL_COUNT, - }, test_utils::{ - x509::{CertificateParams, X509TestChain}, - *, - }, CoreCrypto}; - + use crate::{ + mls::credential::x509::CertificatePrivateKey, + prelude::{ + ClientIdentifier, ConversationId, CryptoError, E2eiConversationState, MlsCentral, MlsCentralConfiguration, + MlsCredentialType, INITIAL_KEYING_MATERIAL_COUNT, + }, + test_utils::{ + x509::{CertificateParams, X509TestChain}, + *, + }, + CoreCrypto, + }; + use super::*; wasm_bindgen_test_configure!(run_in_browser); @@ -398,7 +403,7 @@ mod tests { ) .await .unwrap(); - + let charlie_context = ClientContext { context: charlie_transaction, central: charlie_central, @@ -512,16 +517,16 @@ mod tests { let cc = CoreCrypto::from(creator_central); let creator_transaction = cc.new_transaction().await?; let creator_central = cc.mls; - + if let Some(x509_test_chain) = &x509_test_chain { x509_test_chain.register_with_central(&creator_transaction).await; } - let creator_client_context = ClientContext{ + let creator_client_context = ClientContext { context: creator_transaction.clone(), central: creator_central, x509_test_chain: Arc::new(x509_test_chain.cloned()), }; - + creator_transaction .mls_init( creator_identifier, @@ -558,8 +563,8 @@ mod tests { creator_transaction .new_conversation(&id, creator_ct, case.cfg.clone()) .await?; - - let guest_client_context = ClientContext{ + + let guest_client_context = ClientContext { context: guest_transaction.clone(), central: guest_central, x509_test_chain: Arc::new(x509_test_chain.cloned()), diff --git a/crypto/src/mls/external_commit.rs b/crypto/src/mls/external_commit.rs index bc868be58..5b585d986 100644 --- a/crypto/src/mls/external_commit.rs +++ b/crypto/src/mls/external_commit.rs @@ -318,8 +318,8 @@ impl MlsConversation { #[cfg(test)] mod tests { - use std::sync::Arc; use openmls::prelude::*; + use std::sync::Arc; use wasm_bindgen_test::*; use core_crypto_keystore::{CryptoKeystoreError, CryptoKeystoreMls, MissingKeyErrorKind}; @@ -360,27 +360,13 @@ mod tests { assert_eq!(group_id.as_slice(), &id); // Alice acks the request and adds the new member - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); let decrypted = alice_central .context .decrypt_message(&id, &external_commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); // verify Bob's (sender) identity bob_central.verify_sender_identity(&case, &decrypted).await; @@ -394,23 +380,15 @@ mod tests { .await .unwrap(); assert!(bob_central.context.get_conversation(&id).await.is_ok()); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); // Pending group removed from keystore let error = alice_central .context - .keystore().await.unwrap() + .keystore() + .await + .unwrap() .mls_pending_groups_load(&id) .await; assert!(matches!( @@ -420,10 +398,7 @@ mod tests { // Ensure it's durable i.e. MLS group has been persisted bob_central.context.drop_and_restore(&group_id).await; - assert!(bob_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); + assert!(bob_central.try_talk_to(&id, &alice_central).await.is_ok()); }) }, ) @@ -433,166 +408,127 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn join_by_external_commit_should_be_retriable(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - // export Alice group info - let group_info = alice_central.get_group_info(&id).await; - - // Bob tries to join Alice's group - bob_central - .context - .join_by_external_commit(group_info.clone(), case.custom_cfg(), case.credential_type) - .await - .unwrap(); - // BUT for some reason the Delivery Service will reject this external commit - // e.g. another commit arrived meanwhile and the [GroupInfo] is no longer valid - - // Retrying - let MlsConversationInitBundle { - conversation_id, - commit: external_commit, - .. - } = bob_central - .context - .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - assert_eq!(conversation_id.as_slice(), &id); - - // Alice decrypts the external commit and adds Bob - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); - alice_central - .context - .decrypt_message(&id, &external_commit.to_bytes().unwrap()) - .await - .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - - // And Bob can merge its external commit - bob_central - .context - .merge_pending_group_from_external_commit(&id) - .await - .unwrap(); - assert!(bob_central.context.get_conversation(&id).await.is_ok()); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + // export Alice group info + let group_info = alice_central.get_group_info(&id).await; + + // Bob tries to join Alice's group + bob_central + .context + .join_by_external_commit(group_info.clone(), case.custom_cfg(), case.credential_type) + .await + .unwrap(); + // BUT for some reason the Delivery Service will reject this external commit + // e.g. another commit arrived meanwhile and the [GroupInfo] is no longer valid + + // Retrying + let MlsConversationInitBundle { + conversation_id, + commit: external_commit, + .. + } = bob_central + .context + .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + assert_eq!(conversation_id.as_slice(), &id); + + // Alice decrypts the external commit and adds Bob + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); + alice_central + .context + .decrypt_message(&id, &external_commit.to_bytes().unwrap()) + .await + .unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + + // And Bob can merge its external commit + bob_central + .context + .merge_pending_group_from_external_commit(&id) + .await + .unwrap(); + assert!(bob_central.context.get_conversation(&id).await.is_ok()); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_fail_when_bad_epoch(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let group_info = alice_central.get_group_info(&id).await; - // try to make an external join into Alice's group - let MlsConversationInitBundle { - commit: external_commit, - .. - } = bob_central - .context - .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - - // Alice creates a new commit before receiving the external join - alice_central.context.update_keying_material(&id).await.unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - - // receiving the external join with outdated epoch should fail because of - // the wrong epoch - let result = alice_central - .context - .decrypt_message(&id, &external_commit.to_bytes().unwrap()) - .await; - assert!(matches!(result.unwrap_err(), crate::CryptoError::StaleCommit)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let group_info = alice_central.get_group_info(&id).await; + // try to make an external join into Alice's group + let MlsConversationInitBundle { + commit: external_commit, + .. + } = bob_central + .context + .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + + // Alice creates a new commit before receiving the external join + alice_central.context.update_keying_material(&id).await.unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + + // receiving the external join with outdated epoch should fail because of + // the wrong epoch + let result = alice_central + .context + .decrypt_message(&id, &external_commit.to_bytes().unwrap()) + .await; + assert!(matches!(result.unwrap_err(), crate::CryptoError::StaleCommit)); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn existing_clients_can_join(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - let group_info = alice_central.get_group_info(&id).await; - // Alice can rejoin by external commit - alice_central - .context - .join_by_external_commit(group_info.clone(), case.custom_cfg(), case.credential_type) - .await - .unwrap(); - alice_central - .context - .merge_pending_group_from_external_commit(&id) - .await - .unwrap(); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + let group_info = alice_central.get_group_info(&id).await; + // Alice can rejoin by external commit + alice_central + .context + .join_by_external_commit(group_info.clone(), case.custom_cfg(), case.credential_type) + .await + .unwrap(); + alice_central + .context + .merge_pending_group_from_external_commit(&id) + .await + .unwrap(); + }) + }) .await } @@ -651,14 +587,7 @@ mod tests { .decrypt_message(&id, &bob_external_commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); // Bob merges the commit, he's also in ! bob_central @@ -667,18 +596,8 @@ mod tests { .await .unwrap(); assert!(bob_central.context.get_conversation(&id).await.is_ok()); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - assert!(alice_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); + assert!(alice_central.try_talk_to(&id, &bob_central).await.is_ok()); // Now charlie wants to join with the [GroupInfo] from Bob's external commit let bob_gi = group_info.get_group_info(); @@ -702,22 +621,8 @@ mod tests { .decrypt_message(&id, charlie_external_commit.to_bytes().unwrap()) .await .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 3); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 3); // Charlie merges the commit, he's also in ! charlie_central @@ -726,22 +631,9 @@ mod tests { .await .unwrap(); assert!(charlie_central.context.get_conversation(&id).await.is_ok()); - assert_eq!( - charlie_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 3 - ); - assert!(charlie_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); - assert!(charlie_central - .try_talk_to(&id, &bob_central) - .await - .is_ok()); + assert_eq!(charlie_central.get_conversation_unchecked(&id).await.members().len(), 3); + assert!(charlie_central.try_talk_to(&id, &alice_central).await.is_ok()); + assert!(charlie_central.try_talk_to(&id, &bob_central).await.is_ok()); }) }, ) @@ -751,225 +643,207 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_fail_when_sender_user_not_in_group(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - - alice_central - .context - .set_callbacks(Some(Arc::new(ValidationCallbacks { - client_is_existing_group_user: false, - ..Default::default() - }))).await.unwrap(); - - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - // export Alice group info - let group_info = alice_central.get_group_info(&id).await; + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); - // Bob tries to join Alice's group - let MlsConversationInitBundle { commit, .. } = bob_central - .context - .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - let alice_accepts_ext_commit = alice_central - .context - .decrypt_message(&id, &commit.to_bytes().unwrap()) - .await; - assert!(matches!( - alice_accepts_ext_commit.unwrap_err(), - CryptoError::UnauthorizedExternalCommit - )) - }) - }, - ) + alice_central + .context + .set_callbacks(Some(Arc::new(ValidationCallbacks { + client_is_existing_group_user: false, + ..Default::default() + }))) + .await + .unwrap(); + + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + // export Alice group info + let group_info = alice_central.get_group_info(&id).await; + + // Bob tries to join Alice's group + let MlsConversationInitBundle { commit, .. } = bob_central + .context + .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + let alice_accepts_ext_commit = alice_central + .context + .decrypt_message(&id, &commit.to_bytes().unwrap()) + .await; + assert!(matches!( + alice_accepts_ext_commit.unwrap_err(), + CryptoError::UnauthorizedExternalCommit + )) + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn should_fail_when_sender_lacks_role(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - - alice_central - .context - .set_callbacks(Some(Arc::new(ValidationCallbacks { - user_authorize: false, - ..Default::default() - }))).await.unwrap(); - - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - // export Alice group info - let group_info = alice_central.get_group_info(&id).await; + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); - // Bob tries to join Alice's group - let MlsConversationInitBundle { commit, .. } = bob_central - .context - .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - let alice_accepts_ext_commit = alice_central - .context - .decrypt_message(&id, &commit.to_bytes().unwrap()) - .await; - assert!(matches!( - alice_accepts_ext_commit.unwrap_err(), - CryptoError::UnauthorizedExternalCommit - )) - }) - }, - ) + alice_central + .context + .set_callbacks(Some(Arc::new(ValidationCallbacks { + user_authorize: false, + ..Default::default() + }))) + .await + .unwrap(); + + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + // export Alice group info + let group_info = alice_central.get_group_info(&id).await; + + // Bob tries to join Alice's group + let MlsConversationInitBundle { commit, .. } = bob_central + .context + .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + let alice_accepts_ext_commit = alice_central + .context + .decrypt_message(&id, &commit.to_bytes().unwrap()) + .await; + assert!(matches!( + alice_accepts_ext_commit.unwrap_err(), + CryptoError::UnauthorizedExternalCommit + )) + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn clear_pending_group_should_succeed(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let initial_count = alice_central.context.count_entities().await; - - // export Alice group info - let group_info = alice_central.get_group_info(&id).await; - - // Bob tries to join Alice's group - bob_central - .context - .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - - // But for some reason, Bob wants to abort joining the group - bob_central - .context - .clear_pending_group_from_external_commit(&id) - .await - .unwrap(); - - let final_count = alice_central.context.count_entities().await; - assert_eq!(initial_count, final_count); - - // Hence trying to merge the pending should fail - let result = bob_central.context.merge_pending_group_from_external_commit(&id).await; - assert!(matches!( - result.unwrap_err(), - CryptoError::KeyStoreError(CryptoKeystoreError::MissingKeyInStore( - MissingKeyErrorKind::MlsPendingGroup - )) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let initial_count = alice_central.context.count_entities().await; + + // export Alice group info + let group_info = alice_central.get_group_info(&id).await; + + // Bob tries to join Alice's group + bob_central + .context + .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + + // But for some reason, Bob wants to abort joining the group + bob_central + .context + .clear_pending_group_from_external_commit(&id) + .await + .unwrap(); + + let final_count = alice_central.context.count_entities().await; + assert_eq!(initial_count, final_count); + + // Hence trying to merge the pending should fail + let result = bob_central.context.merge_pending_group_from_external_commit(&id).await; + assert!(matches!( + result.unwrap_err(), + CryptoError::KeyStoreError(CryptoKeystoreError::MissingKeyInStore( + MissingKeyErrorKind::MlsPendingGroup )) - }) - }, - ) + )) + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn new_with_inflight_join_should_fail_when_already_exists(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - let gi = alice_central.get_group_info(&id).await; - - // Bob to join a conversation but while the server processes its request he - // creates a conversation with the id of the conversation he's trying to join - bob_central - .context - .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - // erroneous call - let conflict_join = bob_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await; - assert!(matches!(conflict_join.unwrap_err(), CryptoError::ConversationAlreadyExists(i) if i == id)); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + let gi = alice_central.get_group_info(&id).await; + + // Bob to join a conversation but while the server processes its request he + // creates a conversation with the id of the conversation he's trying to join + bob_central + .context + .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + // erroneous call + let conflict_join = bob_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await; + assert!(matches!(conflict_join.unwrap_err(), CryptoError::ConversationAlreadyExists(i) if i == id)); + }) + }) .await } #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn new_with_inflight_welcome_should_fail_when_already_exists(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - let gi = alice_central.get_group_info(&id).await; - - // While Bob tries to join a conversation via external commit he's also invited - // to a conversation with the same id through a Welcome message - bob_central - .context - .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - - let bob = bob_central.rand_key_package(&case).await; - let welcome = alice_central - .context - .add_members_to_conversation(&id, vec![bob]) - .await - .unwrap() - .welcome; - - // erroneous call - let conflict_welcome = bob_central - .context - .process_welcome_message(welcome.into(), case.custom_cfg()) - .await; - - assert!( - matches!(conflict_welcome.unwrap_err(), CryptoError::ConversationAlreadyExists(i) if i == id) - ); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + let gi = alice_central.get_group_info(&id).await; + + // While Bob tries to join a conversation via external commit he's also invited + // to a conversation with the same id through a Welcome message + bob_central + .context + .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + + let bob = bob_central.rand_key_package(&case).await; + let welcome = alice_central + .context + .add_members_to_conversation(&id, vec![bob]) + .await + .unwrap() + .welcome; + + // erroneous call + let conflict_welcome = bob_central + .context + .process_welcome_message(welcome.into(), case.custom_cfg()) + .await; + + assert!(matches!(conflict_welcome.unwrap_err(), CryptoError::ConversationAlreadyExists(i) if i == id)); + }) + }) .await } @@ -990,9 +864,7 @@ mod tests { .await .unwrap(); - let invalid_kp = bob_central - .new_keypackage(&case, Lifetime::new(expiration_time)) - .await; + let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await; alice_central .context .add_members_to_conversation(&id, vec![invalid_kp.into()]) @@ -1033,43 +905,39 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn group_should_have_right_config(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - - let gi = alice_central.get_group_info(&id).await; - bob_central - .context - .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) - .await - .unwrap(); - bob_central - .context - .merge_pending_group_from_external_commit(&id) - .await - .unwrap(); - let group = bob_central.get_conversation_unchecked(&id).await; - - let capabilities = group.group.group_context_extensions().required_capabilities().unwrap(); - - // see https://www.rfc-editor.org/rfc/rfc9420.html#section-11.1 - assert!(capabilities.extension_types().is_empty()); - assert!(capabilities.proposal_types().is_empty()); - assert_eq!( - capabilities.credential_types(), - MlsConversationConfiguration::DEFAULT_SUPPORTED_CREDENTIALS - ); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + + let gi = alice_central.get_group_info(&id).await; + bob_central + .context + .join_by_external_commit(gi, case.custom_cfg(), case.credential_type) + .await + .unwrap(); + bob_central + .context + .merge_pending_group_from_external_commit(&id) + .await + .unwrap(); + let group = bob_central.get_conversation_unchecked(&id).await; + + let capabilities = group.group.group_context_extensions().required_capabilities().unwrap(); + + // see https://www.rfc-editor.org/rfc/rfc9420.html#section-11.1 + assert!(capabilities.extension_types().is_empty()); + assert!(capabilities.proposal_types().is_empty()); + assert_eq!( + capabilities.credential_types(), + MlsConversationConfiguration::DEFAULT_SUPPORTED_CREDENTIALS + ); + }) + }) .await } } diff --git a/crypto/src/mls/external_proposal.rs b/crypto/src/mls/external_proposal.rs index 6b86bc082..278a9d874 100644 --- a/crypto/src/mls/external_proposal.rs +++ b/crypto/src/mls/external_proposal.rs @@ -123,7 +123,9 @@ impl CentralContext { let mut client_guard = self.mls_client_mut().await?; let client = client_guard.as_mut().ok_or(CryptoError::MlsNotInitialized)?; - let cb = client.find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type).await; + let cb = client + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type) + .await; let cb = match (cb, credential_type) { (Some(cb), _) => cb, (None, MlsCredentialType::Basic) => { @@ -133,7 +135,8 @@ impl CentralContext { .await?; client - .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type).await + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type) + .await .ok_or(CryptoError::CredentialNotFound(credential_type))? } (None, MlsCredentialType::X509) => return Err(CryptoError::E2eiEnrollmentNotDone), @@ -172,11 +175,7 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - let epoch = owner_central - .get_conversation_unchecked(&id) - .await - .group - .epoch(); + let epoch = owner_central.get_conversation_unchecked(&id).await.group.epoch(); // Craft an external proposal from guest let external_add = guest_central @@ -192,14 +191,7 @@ mod tests { .await .unwrap(); // just owner for now - assert_eq!( - owner_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(owner_central.get_conversation_unchecked(&id).await.members().len(), 1); // verify Guest's (sender) identity guest_central.verify_sender_identity(&case, &decrypted).await; @@ -213,33 +205,16 @@ mod tests { .unwrap(); owner_central.context.commit_accepted(&id).await.unwrap(); // guest joined the group - assert_eq!( - owner_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(owner_central.get_conversation_unchecked(&id).await.members().len(), 2); guest_central .context .process_welcome_message(welcome.unwrap().into(), case.custom_cfg()) .await .unwrap(); - assert_eq!( - guest_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + assert_eq!(guest_central.get_conversation_unchecked(&id).await.members().len(), 2); // guest can send messages in the group - assert!(guest_central - .try_talk_to(&id, &owner_central) - .await - .is_ok()); + assert!(guest_central.try_talk_to(&id, &owner_central).await.is_ok()); }) }, ) @@ -266,17 +241,15 @@ mod tests { let ds_signature_key = ds.client_signature_key(&case).await.as_slice().to_vec(); let mut cfg = case.cfg.clone(); owner_central - .set_raw_external_senders(&mut cfg, vec![ds_signature_key]).await + .set_raw_external_senders(&mut cfg, vec![ds_signature_key]) + .await .unwrap(); owner_central .new_conversation(&id, case.credential_type, cfg) .await .unwrap(); - owner - .invite_all(&case, &id, [&guest]) - .await - .unwrap(); + owner.invite_all(&case, &id, [&guest]).await.unwrap(); assert_eq!(owner.get_conversation_unchecked(&id).await.members().len(), 2); // now, as e.g. a Delivery Service, let's create an external remove proposal @@ -285,9 +258,7 @@ mod tests { let sender_index = SenderExtensionIndex::new(0); let (sc, ct) = (case.signature_scheme(), case.credential_type); - let cb = ds - .find_most_recent_credential_bundle(sc, ct).await - .unwrap(); + let cb = ds.find_most_recent_credential_bundle(sc, ct).await.unwrap(); let group_id = GroupId::from_slice(&id[..]); let epoch = owner.get_conversation_unchecked(&id).await.group.epoch(); @@ -335,17 +306,18 @@ mod tests { // Delivery service key is used in the group.. let ds_signature_key = ds.client_signature_key(&case).await.as_slice().to_vec(); let mut cfg = case.cfg.clone(); - owner.context - .set_raw_external_senders(&mut cfg, vec![ds_signature_key]).await + owner + .context + .set_raw_external_senders(&mut cfg, vec![ds_signature_key]) + .await .unwrap(); - owner.context + owner + .context .new_conversation(&id, case.credential_type, cfg) .await .unwrap(); - owner - .invite_all(&case, &id, [&guest]).await - .unwrap(); + owner.invite_all(&case, &id, [&guest]).await.unwrap(); assert_eq!(owner.get_conversation_unchecked(&id).await.members().len(), 2); // now, attacker will try to remove guest from the group, and should fail @@ -353,9 +325,7 @@ mod tests { let sender_index = SenderExtensionIndex::new(1); let (sc, ct) = (case.signature_scheme(), case.credential_type); - let cb = attacker - .find_most_recent_credential_bundle(sc, ct).await - .unwrap(); + let cb = attacker.find_most_recent_credential_bundle(sc, ct).await.unwrap(); let group_id = GroupId::from_slice(&id[..]); let epoch = owner.get_conversation_unchecked(&id).await.group.epoch(); let proposal = @@ -396,17 +366,18 @@ mod tests { // intentionally _not_ use that key when generating the remove proposal below. let key = ds.client_signature_key(&case).await.as_slice().to_vec(); let mut cfg = case.cfg.clone(); - owner.context - .set_raw_external_senders(&mut cfg, vec![key.as_slice().to_vec()]).await + owner + .context + .set_raw_external_senders(&mut cfg, vec![key.as_slice().to_vec()]) + .await .unwrap(); - owner.context + owner + .context .new_conversation(&id, case.credential_type, cfg) .await .unwrap(); - owner - .invite_all(&case, &id, [&guest]).await - .unwrap(); + owner.invite_all(&case, &id, [&guest]).await.unwrap(); assert_eq!(owner.get_conversation_unchecked(&id).await.members().len(), 2); let to_remove = owner.index_of(&id, guest.get_client_id().await).await; @@ -415,9 +386,7 @@ mod tests { let (sc, ct) = (case.signature_scheme(), case.credential_type); // Intentionally use the guest's credential, and therefore the guest's signature // key when generating the proposal so that the signature verification fails. - let cb = guest - .find_most_recent_credential_bundle(sc, ct).await - .unwrap(); + let cb = guest.find_most_recent_credential_bundle(sc, ct).await.unwrap(); let group_id = GroupId::from_slice(&id[..]); let epoch = owner.get_conversation_unchecked(&id).await.group.epoch(); let proposal = @@ -456,7 +425,8 @@ mod tests { let ds_signature_key = ds.client_signature_key(&case).await.as_slice().to_vec(); let mut cfg = case.cfg.clone(); alice_central - .set_raw_external_senders(&mut cfg, vec![ds_signature_key]).await + .set_raw_external_senders(&mut cfg, vec![ds_signature_key]) + .await .unwrap(); alice_central @@ -493,9 +463,7 @@ mod tests { let to_remove = alice.index_of(&id, bob.get_client_id().await).await; let sender_index = SenderExtensionIndex::new(0); let (sc, ct) = (case.signature_scheme(), case.credential_type); - let cb = ds - .find_most_recent_credential_bundle(sc, ct).await - .unwrap(); + let cb = ds.find_most_recent_credential_bundle(sc, ct).await.unwrap(); let group_id = GroupId::from_slice(&id[..]); let epoch = alice.get_conversation_unchecked(&id).await.group.epoch(); let proposal = @@ -559,7 +527,8 @@ mod tests { let ds_signature_key = ds.client_signature_key(&case).await.as_slice().to_vec(); let mut cfg = case.cfg.clone(); alice_central - .set_raw_external_senders(&mut cfg, vec![ds_signature_key]).await + .set_raw_external_senders(&mut cfg, vec![ds_signature_key]) + .await .unwrap(); alice_central @@ -601,9 +570,7 @@ mod tests { let to_remove = alice.index_of(&id, bob.get_client_id().await).await; let sender_index = SenderExtensionIndex::new(0); let (sc, ct) = (case.signature_scheme(), case.credential_type); - let cb = ds - .find_most_recent_credential_bundle(sc, ct).await - .unwrap(); + let cb = ds.find_most_recent_credential_bundle(sc, ct).await.unwrap(); let group_id = GroupId::from_slice(&id[..]); let epoch = alice.get_conversation_unchecked(&id).await.group.epoch(); let proposal = diff --git a/crypto/src/mls/mod.rs b/crypto/src/mls/mod.rs index 27b28b175..61b0b6814 100644 --- a/crypto/src/mls/mod.rs +++ b/crypto/src/mls/mod.rs @@ -3,14 +3,14 @@ use std::sync::Arc; use async_lock::{Mutex, RwLock}; use tracing::{trace, Instrument}; -use mls_crypto_provider::{EntropySeed, MlsCryptoProvider, MlsCryptoProviderConfiguration}; -use openmls_traits::OpenMlsCryptoProvider; -use crate::CoreCrypto; use crate::prelude::{ identifier::ClientIdentifier, key_package::INITIAL_KEYING_MATERIAL_COUNT, Client, ClientId, ConversationId, CoreCryptoCallbacks, CryptoError, CryptoResult, MlsCentralConfiguration, MlsCiphersuite, MlsConversation, MlsConversationConfiguration, MlsCredentialType, MlsError, }; +use crate::CoreCrypto; +use mls_crypto_provider::{EntropySeed, MlsCryptoProvider, MlsCryptoProviderConfiguration}; +use openmls_traits::OpenMlsCryptoProvider; use crate::context::CentralContext; @@ -178,7 +178,7 @@ impl MlsCentral { .in_current_span() .await?; mls_backend.new_transaction().await?; - let keystore = mls_backend.keystore(); + let keystore = mls_backend.keystore(); let mls_client = if let Some(id) = configuration.client_id { // Init client identity (load or create) Arc::new( @@ -256,7 +256,7 @@ impl MlsCentral { callbacks: Arc::new(None.into()), transaction_lock: Arc::new(Mutex::new(())), }; - + let cc = CoreCrypto::from(central); let context = cc.new_transaction().await?; let central = cc.mls; @@ -288,7 +288,8 @@ impl MlsCentral { let client_guard = self.mls_client.read().await; let client = client_guard.as_ref().ok_or(CryptoError::MlsNotInitialized)?; let cb = client - .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type).await + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type) + .await .ok_or(CryptoError::ClientSignatureNotFound)?; Ok(cb.signature_key.to_public_vec()) } @@ -446,7 +447,8 @@ impl CentralContext { let client_guard = self.mls_client().await?; let client = client_guard.as_ref().ok_or(CryptoError::MlsNotInitialized)?; let cb = client - .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type).await + .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type) + .await .ok_or(CryptoError::ClientSignatureNotFound)?; Ok(cb.signature_key.to_public_vec()) } @@ -543,7 +545,11 @@ mod tests { use wasm_bindgen_test::*; use crate::prelude::{CertificateBundle, ClientIdentifier, MlsCredentialType, INITIAL_KEYING_MATERIAL_COUNT}; - use crate::{mls::{CryptoError, MlsCentral, MlsCentralConfiguration}, test_utils::{x509::X509TestChain, *}, CoreCrypto}; + use crate::{ + mls::{CryptoError, MlsCentral, MlsCentralConfiguration}, + test_utils::{x509::X509TestChain, *}, + CoreCrypto, + }; wasm_bindgen_test_configure!(run_in_browser); @@ -571,26 +577,19 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] async fn can_get_conversation_epoch(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - let epoch = alice_central.context.conversation_epoch(&id).await.unwrap(); - assert_eq!(epoch, 1); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + let epoch = alice_central.context.conversation_epoch(&id).await.unwrap(); + assert_eq!(epoch, 1); + }) + }) .await; } diff --git a/crypto/src/mls/proposal.rs b/crypto/src/mls/proposal.rs index 8d10df872..20133d33f 100644 --- a/crypto/src/mls/proposal.rs +++ b/crypto/src/mls/proposal.rs @@ -161,48 +161,34 @@ mod tests { #[apply(all_cred_cipher)] #[wasm_bindgen_test] pub async fn should_add_member(case: TestCase) { - run_test_with_client_ids( - case.clone(), - ["alice", "bob"], - move |[alice_central, bob_central]| { - Box::pin(async move { - let id = conversation_id(); - alice_central - .context - .new_conversation(&id, case.credential_type, case.cfg.clone()) - .await - .unwrap(); - let bob_kp = bob_central.get_one_key_package(&case).await; - alice_central.context.new_add_proposal(&id, bob_kp).await.unwrap(); - let MlsCommitBundle { welcome, .. } = alice_central - .context - .commit_pending_proposals(&id) - .await - .unwrap() - .unwrap(); - alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - let new_id = bob_central - .context - .process_welcome_message(welcome.unwrap().into(), case.custom_cfg()) - .await - .unwrap() - .id; - assert_eq!(id, new_id); - assert!(bob_central - .try_talk_to(&id, &alice_central) - .await - .is_ok()); - }) - }, - ) + run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| { + Box::pin(async move { + let id = conversation_id(); + alice_central + .context + .new_conversation(&id, case.credential_type, case.cfg.clone()) + .await + .unwrap(); + let bob_kp = bob_central.get_one_key_package(&case).await; + alice_central.context.new_add_proposal(&id, bob_kp).await.unwrap(); + let MlsCommitBundle { welcome, .. } = alice_central + .context + .commit_pending_proposals(&id) + .await + .unwrap() + .unwrap(); + alice_central.context.commit_accepted(&id).await.unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + let new_id = bob_central + .context + .process_welcome_message(welcome.unwrap().into(), case.custom_cfg()) + .await + .unwrap() + .id; + assert_eq!(id, new_id); + assert!(bob_central.try_talk_to(&id, &alice_central).await.is_ok()); + }) + }) .await } } @@ -258,26 +244,9 @@ mod tests { .new_conversation(&id, case.credential_type, case.cfg.clone()) .await .unwrap(); - alice_central - .invite_all(&case, &id, [&bob_central]) - .await - .unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); - assert_eq!( - bob_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 2 - ); + alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap(); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2); + assert_eq!(bob_central.get_conversation_unchecked(&id).await.members().len(), 2); let remove_proposal = alice_central .context @@ -296,14 +265,7 @@ mod tests { .unwrap() .unwrap(); alice_central.context.commit_accepted(&id).await.unwrap(); - assert_eq!( - alice_central - .get_conversation_unchecked(&id) - .await - .members() - .len(), - 1 - ); + assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1); bob_central .context diff --git a/crypto/src/test_utils/central.rs b/crypto/src/test_utils/central.rs index 573626956..dd075b166 100644 --- a/crypto/src/test_utils/central.rs +++ b/crypto/src/test_utils/central.rs @@ -30,6 +30,7 @@ use wire_e2e_identity::prelude::WireIdentityReader; use x509_cert::der::Encode; use crate::group_store::GroupStore; +use crate::test_utils::ClientContext; use crate::{ e2e_identity::{ device_status::DeviceStatus, @@ -43,15 +44,15 @@ use crate::{ }, test_utils::{x509::X509Certificate, MessageExt, TestCase}, }; -use crate::test_utils::ClientContext; #[allow(clippy::redundant_static_lifetimes)] pub const TEAM: &'static str = "world"; impl ClientContext { pub async fn get_one_key_package(&self, case: &TestCase) -> KeyPackage { - let kps = self. - context.get_or_create_client_keypackages(case.ciphersuite(), case.credential_type, 1) + let kps = self + .context + .get_or_create_client_keypackages(case.ciphersuite(), case.credential_type, 1) .await .unwrap(); kps.first().unwrap().clone() @@ -82,7 +83,8 @@ impl ClientContext { } pub async fn count_key_package(&self, cs: MlsCiphersuite, ct: Option) -> usize { - self.context.mls_provider() + self.context + .mls_provider() .await .unwrap() .key_store() @@ -134,7 +136,8 @@ impl ClientContext { pub async fn try_talk_to(&self, id: &ConversationId, other: &Self) -> CryptoResult<()> { let msg = b"Hello other"; let encrypted = self.context.encrypt_message(id, msg).await?; - let decrypted = other.context + let decrypted = other + .context .decrypt_message(id, encrypted) .await? .app_msg @@ -143,7 +146,8 @@ impl ClientContext { // other --> self let msg = b"Hello self"; let encrypted = other.context.encrypt_message(id, msg).await?; - let decrypted = self.context + let decrypted = self + .context .decrypt_message(id, encrypted) .await? .app_msg @@ -181,7 +185,8 @@ impl ClientContext { for (other, ..) in &others { other - .context.process_welcome_message(welcome.clone().into(), case.custom_cfg()) + .context + .process_welcome_message(welcome.clone().into(), case.custom_cfg()) .await?; } @@ -216,9 +221,12 @@ impl ClientContext { commit, .. } = self - .context.join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .context + .join_by_external_commit(group_info, case.custom_cfg(), case.credential_type) + .await?; + self.context + .merge_pending_group_from_external_commit(&conversation_id) .await?; - self.context.merge_pending_group_from_external_commit(&conversation_id).await?; assert_eq!(conversation_id.as_slice(), id.as_slice()); for other in others { let commit = commit.tls_serialize_detached().map_err(MlsError::from)?; @@ -250,7 +258,8 @@ impl ClientContext { let cs = group.ciphersuite(); let client = self.client().await; let cb = client - .find_most_recent_credential_bundle(cs.into(), ct.into()).await + .find_most_recent_credential_bundle(cs.into(), ct.into()) + .await .unwrap(); let gi = group @@ -261,7 +270,8 @@ impl ClientContext { /// Finds the [SignaturePublicKey] of a [Client] within a [MlsGroup] pub async fn signature_key_of(&self, conv_id: &ConversationId, client_id: ClientId) -> SignaturePublicKey { - let sign_key = self.context + let sign_key = self + .context .mls_groups() .await .unwrap() @@ -282,7 +292,8 @@ impl ClientContext { /// Finds the HPKE Public key of a [Client] within a [MlsGroup] pub async fn encryption_key_of(&self, conv_id: &ConversationId, client_id: ClientId) -> Vec { - self.context.mls_groups() + self.context + .mls_groups() .await .unwrap() .get_fetch(conv_id, &self.context.keystore().await.unwrap(), None) @@ -300,7 +311,8 @@ impl ClientContext { /// Finds the [LeafNodeIndex] of a [Client] within a [MlsGroup] pub async fn index_of(&self, conv_id: &ConversationId, client_id: ClientId) -> LeafNodeIndex { - self.context.mls_groups() + self.context + .mls_groups() .await .unwrap() .get_fetch(conv_id, &self.context.keystore().await.unwrap(), None) @@ -319,9 +331,7 @@ impl ClientContext { pub async fn client_signature_key(&self, case: &TestCase) -> SignaturePublicKey { let (sc, ct) = (case.signature_scheme(), case.credential_type); let client = self.client().await; - let cb = client - .find_most_recent_credential_bundle(sc, ct).await - .unwrap(); + let cb = client.find_most_recent_credential_bundle(sc, ct).await.unwrap(); SignaturePublicKey::from(cb.signature_key.public()) } @@ -367,12 +377,14 @@ impl ClientContext { &mut self, id: &ConversationId, ) -> Option { - self.context.get_conversation(id) + self.context + .get_conversation(id) .await .unwrap() .read() .await - .find_most_recent_credential_bundle(&self.client().await).await + .find_most_recent_credential_bundle(&self.client().await) + .await .unwrap() .map(|cb| cb.clone()) } @@ -382,8 +394,10 @@ impl ClientContext { sc: SignatureScheme, ct: MlsCredentialType, ) -> Option { - self.client().await - .find_most_recent_credential_bundle(sc, ct).await + self.client() + .await + .find_most_recent_credential_bundle(sc, ct) + .await .map(|cb| cb.clone()) } @@ -393,14 +407,17 @@ impl ClientContext { ct: MlsCredentialType, pk: &SignaturePublicKey, ) -> Option { - self.client().await + self.client() + .await .identities - .find_credential_bundle_by_public_key(sc, ct, pk).await + .find_credential_bundle_by_public_key(sc, ct, pk) + .await .map(|cb| cb.clone()) } pub async fn find_signature_keypair_from_keystore(&self, id: &[u8]) -> Option { - self.context.keystore() + self.context + .keystore() .await .unwrap() .find::(id) @@ -409,7 +426,8 @@ impl ClientContext { } pub async fn find_hpke_private_key_from_keystore(&self, skp: &HpkePublicKey) -> Option { - self.context.keystore() + self.context + .keystore() .await .unwrap() .find::(&skp.tls_serialize_detached().unwrap()) @@ -419,7 +437,8 @@ impl ClientContext { pub async fn find_credential_from_keystore(&self, cb: &CredentialBundle) -> Option { let credential = cb.credential.tls_serialize_detached().unwrap(); - self.context.keystore() + self.context + .keystore() .await .unwrap() .find_all::(EntityFindParams::default()) @@ -430,7 +449,8 @@ impl ClientContext { } pub async fn count_hpke_private_key(&self) -> usize { - self.context.keystore() + self.context + .keystore() .await .unwrap() .count::() @@ -439,7 +459,8 @@ impl ClientContext { } pub async fn count_encryption_keypairs(&self) -> usize { - self.context.keystore() + self.context + .keystore() .await .unwrap() .count::() @@ -448,7 +469,8 @@ impl ClientContext { } pub async fn count_credentials_in_keystore(&self) -> usize { - self.context.keystore() + self.context + .keystore() .await .unwrap() .count::() @@ -474,7 +496,11 @@ impl ClientContext { ); let mut client = self.client().await; client - .save_new_x509_credential_bundle(&self.context.keystore().await.unwrap(), case.signature_scheme(), new_cert) + .save_new_x509_credential_bundle( + &self.context.keystore().await.unwrap(), + case.signature_scheme(), + new_cert, + ) .await .unwrap() } @@ -485,10 +511,9 @@ impl ClientContext { let cid: String = cid.parse::().unwrap().try_into().unwrap(); wire_e2e_identity::prelude::E2eiClientId::try_from_qualified(&cid).unwrap() } - + pub fn get_intermediate_ca(&self) -> Option<&X509Certificate> { - self - .x509_test_chain + self.x509_test_chain .as_ref() .as_ref() .map(|chain| chain.find_local_intermediate_ca()) @@ -659,7 +684,8 @@ impl Client { ) -> CryptoResult<()> { let existing_cb = self .identities - .find_most_recent_credential_bundle(sc, MlsCredentialType::X509).await + .find_most_recent_credential_bundle(sc, MlsCredentialType::X509) + .await .is_none(); if existing_cb { self.save_new_x509_credential_bundle(&backend.keystore(), sc, cb) @@ -676,7 +702,8 @@ impl Client { ct: MlsCredentialType, ) -> CryptoResult { let cb = self - .find_most_recent_credential_bundle(cs.signature_algorithm(), ct).await + .find_most_recent_credential_bundle(cs.signature_algorithm(), ct) + .await .ok_or(CryptoError::MlsNotInitialized)?; self.generate_one_keypackage_from_credential_bundle(backend, cs, &cb) .await diff --git a/crypto/src/test_utils/mod.rs b/crypto/src/test_utils/mod.rs index 78f3a3c1a..ca5e7ea99 100644 --- a/crypto/src/test_utils/mod.rs +++ b/crypto/src/test_utils/mod.rs @@ -14,12 +14,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use crate::{ + prelude::{ClientId, ConversationId, MlsCentral, MlsCentralConfiguration}, + test_utils::x509::{CertificateParams, X509TestChain, X509TestChainActorArg, X509TestChainArgs}, + CoreCrypto, CoreCryptoCallbacks, +}; pub use openmls_traits::types::SignatureScheme; pub use rstest::*; pub use rstest_reuse::{self, *}; use std::collections::HashMap; use std::sync::Arc; -use crate::{prelude::{ClientId, ConversationId, MlsCentral, MlsCentralConfiguration}, test_utils::x509::{CertificateParams, X509TestChain, X509TestChainActorArg, X509TestChainArgs}, CoreCrypto, CoreCryptoCallbacks}; pub mod central; pub mod fixtures; @@ -29,12 +33,12 @@ pub mod x509; #[cfg(feature = "proteus")] pub mod proteus_utils; +use crate::context::CentralContext; use crate::e2e_identity::id::{QualifiedE2eiClientId, WireQualifiedClientId}; +use crate::prelude::Client; pub use crate::prelude::{ClientIdentifier, MlsCredentialType, INITIAL_KEYING_MATERIAL_COUNT}; pub use fixtures::{TestCase, *}; pub use message::*; -use crate::context::CentralContext; -use crate::prelude::Client; pub const GROUP_SAMPLE_SIZE: usize = 9; @@ -56,12 +60,12 @@ impl ClientContext { pub fn replace_x509_chain(&mut self, new_chain: std::sync::Arc>) { self.x509_test_chain = new_chain; } - + pub async fn client(&self) -> Client { let client_guard = self.context.mls_client().await.unwrap(); client_guard.as_ref().unwrap().clone() } - + pub async fn get_client_id(&self) -> ClientId { self.client().await.id().clone() } @@ -215,7 +219,7 @@ pub async fn run_cross_signed_tests_with_client_ids( .await .unwrap(); context.finish().await.unwrap(); - central.callbacks(std::sync::Arc::::default()).await; + central + .callbacks(std::sync::Arc::::default()) + .await; central } }); @@ -364,7 +370,7 @@ pub async fn run_test_with_deterministic_client_ids_and_revocation::default()).await; + central + .callbacks(std::sync::Arc::::default()) + .await; let cc = CoreCrypto::from(central); let context = cc.new_transaction().await.unwrap(); test(ClientContext { diff --git a/crypto/src/test_utils/x509.rs b/crypto/src/test_utils/x509.rs index 5cf15cbf5..7796a56f9 100644 --- a/crypto/src/test_utils/x509.rs +++ b/crypto/src/test_utils/x509.rs @@ -1,7 +1,5 @@ use crate::{ - e2e_identity::id::QualifiedE2eiClientId, - mls::client::identifier::ClientIdentifier, - prelude::E2eIdentityError, + e2e_identity::id::QualifiedE2eiClientId, mls::client::identifier::ClientIdentifier, prelude::E2eIdentityError, CryptoError, }; use std::{fmt::Display, time::Duration}; diff --git a/interop/src/clients/corecrypto/native.rs b/interop/src/clients/corecrypto/native.rs index 63242add6..03461ba8e 100644 --- a/interop/src/clients/corecrypto/native.rs +++ b/interop/src/clients/corecrypto/native.rs @@ -205,7 +205,6 @@ impl crate::clients::EmulatedProteusClient for CoreCryptoNativeClient { let result = transaction.proteus_encrypt(session_id, plaintext).await?; transaction.finish().await?; Ok(result) - } async fn decrypt(&mut self, session_id: &str, ciphertext: &[u8]) -> Result> { diff --git a/interop/src/main.rs b/interop/src/main.rs index 166caaaae..3862bdb4c 100644 --- a/interop/src/main.rs +++ b/interop/src/main.rs @@ -357,9 +357,7 @@ async fn run_proteus_test(chrome_driver_addr: &std::net::SocketAddr) -> Result<( prng.fill_bytes(&mut message); - let mut messages_to_decrypt = transaction - .proteus_encrypt_batched(&master_sessions, &message) - .await?; + let mut messages_to_decrypt = transaction.proteus_encrypt_batched(&master_sessions, &message).await?; for c in clients.iter_mut() { let fingerprint = c.fingerprint().await?; diff --git a/keystore/src/connection/mod.rs b/keystore/src/connection/mod.rs index 1b1bd106c..d452365f1 100644 --- a/keystore/src/connection/mod.rs +++ b/keystore/src/connection/mod.rs @@ -29,17 +29,15 @@ pub mod platform { } } -use std::ops::DerefMut; pub use self::platform::*; -use crate::{ - entities::{Entity, EntityFindParams, StringEntityId}, -}; +use crate::entities::{Entity, EntityFindParams, StringEntityId}; +use std::ops::DerefMut; use crate::entities::{EntityTransactionExt, UniqueEntity}; +use crate::transaction::KeystoreTransaction; use crate::{CryptoKeystoreError, CryptoKeystoreResult}; use async_lock::{Mutex, MutexGuard}; use std::sync::Arc; -use crate::transaction::KeystoreTransaction; /// Limit on the length of a blob to be stored in the database. /// @@ -135,7 +133,10 @@ impl Connection { .into(); #[allow(clippy::arc_with_non_send_sync)] // see https://github.com/rustwasm/wasm-bindgen/pull/955 let conn = Arc::new(conn); - Ok(Self { conn, transaction: Default::default() }) + Ok(Self { + conn, + transaction: Default::default(), + }) } pub async fn open_in_memory_with_key(name: impl AsRef, key: impl AsRef) -> CryptoKeystoreResult { @@ -144,7 +145,10 @@ impl Connection { .into(); #[allow(clippy::arc_with_non_send_sync)] // see https://github.com/rustwasm/wasm-bindgen/pull/955 let conn = Arc::new(conn); - Ok(Self { conn, transaction: Default::default() }) + Ok(Self { + conn, + transaction: Default::default(), + }) } pub async fn borrow_conn(&self) -> CryptoKeystoreResult> { @@ -178,7 +182,7 @@ impl Connection { *transaction = Some(KeystoreTransaction::new(self.clone()).await?); Ok(()) } - + pub async fn commit_transaction(&self) -> CryptoKeystoreResult<()> { let mut transaction_guard = self.transaction.lock().await; let Some(transaction) = transaction_guard.as_ref() else { @@ -199,9 +203,7 @@ impl Connection { } pub async fn child_groups< - E: Entity - + crate::entities::PersistedMlsGroupExt - + Sync, + E: Entity + crate::entities::PersistedMlsGroupExt + Sync, >( &self, entity: E, @@ -215,7 +217,7 @@ impl Connection { }; transaction.child_groups(entity, persisted_records).await } - + pub async fn save + Sync + EntityTransactionExt>( &self, entity: E, @@ -226,8 +228,11 @@ impl Connection { }; transaction.save_mut(entity).await } - - pub async fn remove + EntityTransactionExt, S: AsRef<[u8]>>( + + pub async fn remove< + E: Entity + EntityTransactionExt, + S: AsRef<[u8]>, + >( &self, id: S, ) -> CryptoKeystoreResult<()> { @@ -245,7 +250,6 @@ impl Connection { }; transaction.cred_delete_by_credential(cred).await } - } #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] @@ -255,24 +259,24 @@ impl FetchFromDatabase for Connection { &self, id: &[u8], ) -> CryptoKeystoreResult> { - // If a transaction is in progress... + // If a transaction is in progress... if let Some(transaction) = self.transaction.lock().await.as_ref() { - //... and it has information about this entity, ... + //... and it has information about this entity, ... if let Some(result) = transaction.find::(id).await? { // ... return that result return Ok(result); } } - + // Otherwise get it from the database let mut conn = self.conn.lock().await; E::find_one(&mut conn, &id.into()).await } async fn find_unique(&self) -> CryptoKeystoreResult { - // If a transaction is in progress... + // If a transaction is in progress... if let Some(transaction) = self.transaction.lock().await.as_ref() { - //... and it has information about this entity, ... + //... and it has information about this entity, ... if let Some(result) = transaction.find_unique::().await? { // ... return that result return Ok(result); @@ -289,7 +293,7 @@ impl FetchFromDatabase for Connection { ) -> CryptoKeystoreResult> { let mut conn = self.conn.lock().await; let persisted_records = E::find_all(&mut conn, params.clone()).await?; - + let transaction_guard = self.transaction.lock().await; let Some(transaction) = transaction_guard.as_ref() else { return Ok(persisted_records); @@ -314,7 +318,7 @@ impl FetchFromDatabase for Connection { async fn count>(&self) -> CryptoKeystoreResult { if self.transaction.lock().await.is_some() { - // Unfortunately, we have to do this because of possible record id overlap + // Unfortunately, we have to do this because of possible record id overlap // between cache and db. return Ok(self.find_all::(Default::default()).await?.len()); }; diff --git a/keystore/src/entities/mls.rs b/keystore/src/entities/mls.rs index b5559ed5e..f6c4b2c8e 100644 --- a/keystore/src/entities/mls.rs +++ b/keystore/src/entities/mls.rs @@ -221,9 +221,7 @@ where async fn find_all(conn: &mut Self::ConnectionType, _params: EntityFindParams) -> CryptoKeystoreResult> { match Self::find_unique(conn).await { - Ok(record) => { - Ok(vec![record]) - } + Ok(record) => Ok(vec![record]), Err(CryptoKeystoreError::NotFound(_, _)) => Ok(vec![]), Err(err) => Err(err), } @@ -260,12 +258,10 @@ pub trait UniqueEntity: Entity CryptoKeystoreResult> { match Self::find_unique(conn).await { - Ok(record) => { - Ok(vec![record]) - } + Ok(record) => Ok(vec![record]), Err(CryptoKeystoreError::NotFound(_, _)) => Ok(vec![]), Err(err) => Err(err), } @@ -307,8 +303,7 @@ pub trait UniqueEntity: Entity EntityTransactionExt for T { - +impl EntityTransactionExt for T { #[cfg(not(target_family = "wasm"))] async fn save(&self, tx: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> { self.replace(tx).await @@ -320,12 +315,18 @@ impl EntityTransactionExt for T { } #[cfg(not(target_family = "wasm"))] - async fn delete_fail_on_missing_id(_: &TransactionWrapper<'_>, _id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + _: &TransactionWrapper<'_>, + _id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { Err(CryptoKeystoreError::NotImplemented) } - + #[cfg(target_family = "wasm")] - async fn delete_fail_on_missing_id<'a>(_: &TransactionWrapper<'a>, _id: StringEntityId<'a>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id<'a>( + _: &TransactionWrapper<'a>, + _id: StringEntityId<'a>, + ) -> CryptoKeystoreResult<()> { Err(CryptoKeystoreError::NotImplemented) } } diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index 802575e93..ee81ed9a0 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -204,7 +204,7 @@ cfg_if::cfg_if! { } fn id_raw(&self) -> &[u8]; - + /// The query results that are obtained during a transaction /// from the transaction cache and the database are merged by this key. fn merge_key(&self) -> Vec { diff --git a/keystore/src/entities/platform/generic/mls/e2ei_crl.rs b/keystore/src/entities/platform/generic/mls/e2ei_crl.rs index f273c7877..59c29c96a 100644 --- a/keystore/src/entities/platform/generic/mls/e2ei_crl.rs +++ b/keystore/src/entities/platform/generic/mls/e2ei_crl.rs @@ -140,7 +140,10 @@ impl EntityTransactionExt for E2eiCrl { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM e2ei_crls WHERE distribution_point = ?", [id.try_as_str()?])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs b/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs index 5fc4c65f8..380c5b935 100644 --- a/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs +++ b/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs @@ -156,7 +156,10 @@ impl EntityTransactionExt for E2eiIntermediateCert { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute( "DELETE FROM e2ei_intermediate_certs WHERE ski_aki_pair = ?", [id.try_as_str()?], diff --git a/keystore/src/entities/platform/generic/mls/encryption_keypair.rs b/keystore/src/entities/platform/generic/mls/encryption_keypair.rs index d357e69b5..af47a9b55 100644 --- a/keystore/src/entities/platform/generic/mls/encryption_keypair.rs +++ b/keystore/src/entities/platform/generic/mls/encryption_keypair.rs @@ -192,7 +192,10 @@ impl EntityTransactionExt for MlsEncryptionKeyPair { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_encryption_keypairs WHERE pk_sha256 = ?", [id.sha256()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/enrollment.rs b/keystore/src/entities/platform/generic/mls/enrollment.rs index 491b9a6b0..77218804c 100644 --- a/keystore/src/entities/platform/generic/mls/enrollment.rs +++ b/keystore/src/entities/platform/generic/mls/enrollment.rs @@ -144,7 +144,10 @@ impl EntityTransactionExt for E2eiEnrollment { } } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM e2ei_enrollment WHERE id = ?", [id.as_slice()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs b/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs index 2c5f3fcd4..594e9edbc 100644 --- a/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs +++ b/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs @@ -161,7 +161,10 @@ impl EntityTransactionExt for MlsEpochEncryptionKeyPair { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute( "DELETE FROM mls_epoch_encryption_keypairs WHERE id_hex = ?", [id.as_hex_string()], diff --git a/keystore/src/entities/platform/generic/mls/group.rs b/keystore/src/entities/platform/generic/mls/group.rs index 687dfea34..85b7b19fd 100644 --- a/keystore/src/entities/platform/generic/mls/group.rs +++ b/keystore/src/entities/platform/generic/mls/group.rs @@ -183,7 +183,10 @@ impl EntityTransactionExt for PersistedMlsGroup { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_groups WHERE id_hex = ?", [id.as_hex_string()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/hpke_private_key.rs b/keystore/src/entities/platform/generic/mls/hpke_private_key.rs index a92be914b..3ec2b4f53 100644 --- a/keystore/src/entities/platform/generic/mls/hpke_private_key.rs +++ b/keystore/src/entities/platform/generic/mls/hpke_private_key.rs @@ -171,7 +171,10 @@ impl EntityTransactionExt for MlsHpkePrivateKey { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_hpke_private_keys WHERE pk_sha256 = ?", [id.sha256()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/keypackage.rs b/keystore/src/entities/platform/generic/mls/keypackage.rs index 3e0e053fb..829d71c32 100644 --- a/keystore/src/entities/platform/generic/mls/keypackage.rs +++ b/keystore/src/entities/platform/generic/mls/keypackage.rs @@ -169,7 +169,10 @@ impl EntityTransactionExt for MlsKeyPackage { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute( "DELETE FROM mls_keypackages WHERE keypackage_ref_hex = ?", [id.as_hex_string()], diff --git a/keystore/src/entities/platform/generic/mls/pending_group.rs b/keystore/src/entities/platform/generic/mls/pending_group.rs index 55b6f6eec..480d6c153 100644 --- a/keystore/src/entities/platform/generic/mls/pending_group.rs +++ b/keystore/src/entities/platform/generic/mls/pending_group.rs @@ -320,7 +320,10 @@ impl EntityTransactionExt for PersistedMlsPendingGroup { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_pending_groups WHERE id = ?", [id.as_slice()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/pending_message.rs b/keystore/src/entities/platform/generic/mls/pending_message.rs index 674a4e26e..c48ba82a2 100644 --- a/keystore/src/entities/platform/generic/mls/pending_message.rs +++ b/keystore/src/entities/platform/generic/mls/pending_message.rs @@ -26,8 +26,8 @@ impl Entity for MlsPendingMessage { } fn merge_key(&self) -> Vec { - // Use this as a merge key because the `id` is not used as a primary key - // but as a foreign key: it's the ID of the PersistedMlsPendingGroup. + // Use this as a merge key because the `id` is not used as a primary key + // but as a foreign key: it's the ID of the PersistedMlsPendingGroup. self.message.clone() } } @@ -81,7 +81,10 @@ impl EntityBase for MlsPendingMessage { blob.read_to_end(&mut message)?; blob.close()?; - Ok(Some(Self { foreign_id: id, message })) + Ok(Some(Self { + foreign_id: id, + message, + })) } None => Ok(None), } @@ -117,7 +120,10 @@ impl EntityBase for MlsPendingMessage { blob.read_to_end(&mut message)?; blob.close()?; - acc.push(Self { foreign_id: id, message }); + acc.push(Self { + foreign_id: id, + message, + }); crate::CryptoKeystoreResult::Ok(acc) })?; @@ -174,7 +180,10 @@ impl EntityTransactionExt for MlsPendingMessage { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_pending_messages WHERE id = ?", [id.as_slice()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/psk_bundle.rs b/keystore/src/entities/platform/generic/mls/psk_bundle.rs index 5fcd15be9..855063438 100644 --- a/keystore/src/entities/platform/generic/mls/psk_bundle.rs +++ b/keystore/src/entities/platform/generic/mls/psk_bundle.rs @@ -153,7 +153,10 @@ impl EntityTransactionExt for MlsPskBundle { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_psk_bundles WHERE id_sha256 = ?", [id.sha256()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/mls/signature_keypair.rs b/keystore/src/entities/platform/generic/mls/signature_keypair.rs index d9f9d6e34..b74e34295 100644 --- a/keystore/src/entities/platform/generic/mls/signature_keypair.rs +++ b/keystore/src/entities/platform/generic/mls/signature_keypair.rs @@ -235,7 +235,10 @@ impl EntityTransactionExt for MlsSignatureKeyPair { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> CryptoKeystoreResult<()> { let updated = transaction.execute("DELETE FROM mls_signature_keypairs WHERE pk = ?", [id.as_slice()])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/proteus/identity.rs b/keystore/src/entities/platform/generic/proteus/identity.rs index 444e05619..8782031fd 100644 --- a/keystore/src/entities/platform/generic/proteus/identity.rs +++ b/keystore/src/entities/platform/generic/proteus/identity.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use crate::connection::TransactionWrapper; use crate::entities::{EntityFindParams, EntityTransactionExt, ProteusIdentity, StringEntityId}; use crate::CryptoKeystoreError; use crate::{ @@ -22,7 +23,6 @@ use crate::{ MissingKeyErrorKind, }; use rusqlite::OptionalExtension; -use crate::connection::TransactionWrapper; impl Entity for ProteusIdentity { fn id_raw(&self) -> &[u8] { @@ -142,7 +142,10 @@ impl EntityTransactionExt for ProteusIdentity { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, _id: StringEntityId<'_>) -> crate::CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + _id: StringEntityId<'_>, + ) -> crate::CryptoKeystoreResult<()> { let row_id = transaction.query_row( "SELECT rowid FROM proteus_identities ORDER BY rowid ASC LIMIT 1", [], @@ -153,5 +156,4 @@ impl EntityTransactionExt for ProteusIdentity { Ok(()) } - } diff --git a/keystore/src/entities/platform/generic/proteus/prekey.rs b/keystore/src/entities/platform/generic/proteus/prekey.rs index 107c31d16..b3cd79e7a 100644 --- a/keystore/src/entities/platform/generic/proteus/prekey.rs +++ b/keystore/src/entities/platform/generic/proteus/prekey.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use crate::connection::TransactionWrapper; use crate::entities::{EntityFindParams, EntityTransactionExt, ProteusPrekey, StringEntityId}; use crate::{ connection::KeystoreDatabaseConnection, entities::{Entity, EntityBase}, MissingKeyErrorKind, }; -use crate::connection::TransactionWrapper; impl Entity for ProteusPrekey { fn id_raw(&self) -> &[u8] { @@ -134,7 +134,10 @@ impl EntityTransactionExt for ProteusPrekey { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> crate::CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> crate::CryptoKeystoreResult<()> { let id = ProteusPrekey::id_from_slice(id.as_slice()); let updated = transaction.execute("DELETE FROM proteus_prekeys WHERE id = ?", [id])?; if updated > 0 { diff --git a/keystore/src/entities/platform/generic/proteus/session.rs b/keystore/src/entities/platform/generic/proteus/session.rs index de7448bb6..57e27f45d 100644 --- a/keystore/src/entities/platform/generic/proteus/session.rs +++ b/keystore/src/entities/platform/generic/proteus/session.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use crate::connection::TransactionWrapper; use crate::entities::{EntityFindParams, EntityTransactionExt, ProteusSession, StringEntityId}; use crate::CryptoKeystoreError; use crate::{ @@ -21,7 +22,6 @@ use crate::{ entities::{Entity, EntityBase}, MissingKeyErrorKind, }; -use crate::connection::TransactionWrapper; impl Entity for ProteusSession { fn id_raw(&self) -> &[u8] { @@ -177,7 +177,10 @@ impl EntityTransactionExt for ProteusSession { Ok(()) } - async fn delete_fail_on_missing_id(transaction: &TransactionWrapper<'_>, id: StringEntityId<'_>) -> crate::CryptoKeystoreResult<()> { + async fn delete_fail_on_missing_id( + transaction: &TransactionWrapper<'_>, + id: StringEntityId<'_>, + ) -> crate::CryptoKeystoreResult<()> { let id_string: String = (&id).try_into()?; let updated = transaction.execute("DELETE FROM proteus_sessions WHERE id = ?", [id_string])?; diff --git a/keystore/src/entities/platform/wasm/mls/credential.rs b/keystore/src/entities/platform/wasm/mls/credential.rs index 1e351e017..5197c1e0e 100644 --- a/keystore/src/entities/platform/wasm/mls/credential.rs +++ b/keystore/src/entities/platform/wasm/mls/credential.rs @@ -16,7 +16,9 @@ use crate::{ connection::{storage::WasmStorageTransaction, DatabaseConnection, KeystoreDatabaseConnection}, - entities::{Entity, EntityBase, EntityFindParams, EntityTransactionExt, MlsCredential, MlsCredentialExt, StringEntityId}, + entities::{ + Entity, EntityBase, EntityFindParams, EntityTransactionExt, MlsCredential, MlsCredentialExt, StringEntityId, + }, CryptoKeystoreError, CryptoKeystoreResult, MissingKeyErrorKind, }; use fluvio_wasm_timer::SystemTime; diff --git a/keystore/src/entities/platform/wasm/mls/enrollment.rs b/keystore/src/entities/platform/wasm/mls/enrollment.rs index ea6d06a7f..0240c9ef3 100644 --- a/keystore/src/entities/platform/wasm/mls/enrollment.rs +++ b/keystore/src/entities/platform/wasm/mls/enrollment.rs @@ -16,7 +16,8 @@ use crate::{ connection::{DatabaseConnection, KeystoreDatabaseConnection}, - entities::{E2eiEnrollment, Entity, EntityBase, EntityFindParams, EntityTransactionExt, StringEntityId}, CryptoKeystoreResult, MissingKeyErrorKind, + entities::{E2eiEnrollment, Entity, EntityBase, EntityFindParams, EntityTransactionExt, StringEntityId}, + CryptoKeystoreResult, MissingKeyErrorKind, }; #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] diff --git a/keystore/src/entities/platform/wasm/mls/pending_message.rs b/keystore/src/entities/platform/wasm/mls/pending_message.rs index 8ec032bd4..f4f47e53d 100644 --- a/keystore/src/entities/platform/wasm/mls/pending_message.rs +++ b/keystore/src/entities/platform/wasm/mls/pending_message.rs @@ -58,8 +58,8 @@ impl Entity for MlsPendingMessage { } fn merge_key(&self) -> Vec { - // Use this as a merge key because the `id` is not used as a primary key - // but as a foreign key: it's the ID of the PersistedMlsPendingGroup. + // Use this as a merge key because the `id` is not used as a primary key + // but as a foreign key: it's the ID of the PersistedMlsPendingGroup. self.message.clone() } diff --git a/keystore/src/entities/platform/wasm/proteus/identity.rs b/keystore/src/entities/platform/wasm/proteus/identity.rs index fda2fbfc2..efce1a4d4 100644 --- a/keystore/src/entities/platform/wasm/proteus/identity.rs +++ b/keystore/src/entities/platform/wasm/proteus/identity.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use crate::entities::EntityTransactionExt; use crate::{ connection::{DatabaseConnection, KeystoreDatabaseConnection}, entities::{Entity, EntityBase, EntityFindParams, ProteusIdentity, StringEntityId}, CryptoKeystoreResult, MissingKeyErrorKind, }; -use crate::entities::EntityTransactionExt; #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] @@ -60,7 +60,7 @@ impl EntityBase for ProteusIdentity { } #[async_trait::async_trait(?Send)] -impl EntityTransactionExt for ProteusIdentity { } +impl EntityTransactionExt for ProteusIdentity {} impl Entity for ProteusIdentity { fn id_raw(&self) -> &[u8] { diff --git a/keystore/src/transaction.rs b/keystore/src/transaction.rs index bdfbc8680..84444cfa8 100644 --- a/keystore/src/transaction.rs +++ b/keystore/src/transaction.rs @@ -106,7 +106,7 @@ impl EntityId { } #[cfg(target_family = "wasm")] - fn collection_name(&self) -> &'static str { + fn collection_name(&self) -> &'static str { match self { EntityId::SignatureKeyPair(_) => MlsSignatureKeyPair::COLLECTION_NAME, EntityId::KeyPackage(_) => MlsKeyPackage::COLLECTION_NAME, @@ -334,7 +334,7 @@ impl KeystoreTransaction { } else { merged.collect() }; - + if merged.is_empty() { return merged; } diff --git a/keystore/tests/common.rs b/keystore/tests/common.rs index 977574040..fd6e62764 100644 --- a/keystore/tests/common.rs +++ b/keystore/tests/common.rs @@ -16,12 +16,12 @@ #![allow(dead_code, unused_macros, unused_imports)] -use std::sync::Arc; pub(crate) use core_crypto_keystore::Connection as CryptoKeystore; +use std::sync::Arc; +use core_crypto_keystore::connection::{DatabaseConnection, KeystoreDatabaseConnection}; pub(crate) use rstest::*; pub(crate) use rstest_reuse::{self, *}; -use core_crypto_keystore::connection::{DatabaseConnection, KeystoreDatabaseConnection}; pub(crate) const TEST_ENCRYPTION_KEY: &str = "test1234"; @@ -50,9 +50,7 @@ pub async fn setup(name: impl AsRef, in_memory: bool) -> KeystoreTestContex } .expect("Could not open keystore"); store.new_transaction().await.expect("Could not create transaction"); - KeystoreTestContext { - store: Some(store), - } + KeystoreTestContext { store: Some(store) } } pub struct KeystoreTestContext { @@ -63,7 +61,7 @@ impl KeystoreTestContext { pub fn store(&self) -> &core_crypto_keystore::Connection { self.store.as_ref().expect("KeystoreTestFixture store is missing") } - + pub fn store_mut(&mut self) -> &mut core_crypto_keystore::Connection { self.store.as_mut().expect("KeystoreTestFixture store is missing") } @@ -80,13 +78,8 @@ impl Drop for KeystoreTestContext { } } - #[template] #[rstest] #[case::persistent(setup(store_name(), false).await)] #[case::in_memory(setup(store_name(), true).await)] -pub async fn all_storage_types( - #[case] - context: KeystoreTestContext, -) { -} +pub async fn all_storage_types(#[case] context: KeystoreTestContext) {} diff --git a/keystore/tests/proteus.rs b/keystore/tests/proteus.rs index 903e6f14b..aa92ca406 100644 --- a/keystore/tests/proteus.rs +++ b/keystore/tests/proteus.rs @@ -23,7 +23,8 @@ mod common; mod tests { use crate::common::*; use core_crypto_keystore::{ - entities::{EntityBase, ProteusPrekey}, MissingKeyErrorKind, + entities::{EntityBase, ProteusPrekey}, + MissingKeyErrorKind, }; use proteus_wasm::keys::{PreKey, PreKeyId}; use wasm_bindgen_test::*; diff --git a/keystore/tests/z_entities.rs b/keystore/tests/z_entities.rs index 875f1b0b3..546ff4739 100644 --- a/keystore/tests/z_entities.rs +++ b/keystore/tests/z_entities.rs @@ -141,11 +141,11 @@ macro_rules! test_migration_to_db_v1_for_entity { mod tests_impl { use super::common::*; use crate::{utils::EntityRandomUpdateExt, ENTITY_COUNT}; + use core_crypto_keystore::entities::EntityTransactionExt; use core_crypto_keystore::{ connection::{FetchFromDatabase, KeystoreDatabaseConnection}, entities::{Entity, EntityFindParams}, }; - use core_crypto_keystore::entities::EntityTransactionExt; pub(crate) async fn can_save_entity< R: EntityRandomUpdateExt + Entity + EntityTransactionExt + Sync, diff --git a/mls-provider/src/lib.rs b/mls-provider/src/lib.rs index 0a45387c8..6abeffc88 100644 --- a/mls-provider/src/lib.rs +++ b/mls-provider/src/lib.rs @@ -95,7 +95,7 @@ pub struct MlsCryptoProvider { key_store: CryptoKeystore, pki_env: PkiEnvironmentProvider, } -pub type TransactionalCryptoProvider = MlsCryptoProvider; +pub type TransactionalCryptoProvider = MlsCryptoProvider; impl MlsCryptoProvider { /// Initialize a CryptoProvider with a backend following the provided `config` (see: [MlsCryptoProviderConfiguration])