From e16624fc084ae131744dafca8e0fde24223708e3 Mon Sep 17 00:00:00 2001 From: beltram Date: Wed, 29 Nov 2023 10:56:57 +0100 Subject: [PATCH] feat: better errors: 'ImplementationError' was way too often used as a fallback when the developer was too lazy to create a new error. This tries to cure that, especially with e2ei errors. It also tries to distinguish client errors from internal errors --- crypto-ffi/src/generic.rs | 63 ++++++------- crypto-ffi/src/wasm.rs | 6 +- crypto/src/e2e_identity/crypto.rs | 14 +-- crypto/src/e2e_identity/enabled.rs | 4 +- crypto/src/e2e_identity/error.rs | 17 +++- crypto/src/e2e_identity/identity.rs | 8 +- crypto/src/e2e_identity/mod.rs | 88 +++++++++++++------ crypto/src/e2e_identity/rotate.rs | 20 ++--- crypto/src/e2e_identity/stash.rs | 11 +-- crypto/src/error.rs | 17 +++- crypto/src/mls/client/identities.rs | 7 +- crypto/src/mls/client/key_package.rs | 4 +- crypto/src/mls/client/mod.rs | 11 ++- .../src/mls/conversation/buffer_messages.rs | 2 +- crypto/src/mls/conversation/config.rs | 2 +- crypto/src/mls/conversation/merge.rs | 4 +- crypto/src/mls/conversation/mod.rs | 2 +- crypto/src/mls/conversation/welcome.rs | 2 +- crypto/src/mls/credential/x509.rs | 4 +- crypto/src/mls/external_proposal.rs | 2 +- crypto/src/mls/mod.rs | 6 +- crypto/src/test_utils/mod.rs | 2 +- crypto/src/test_utils/x509.rs | 2 +- 23 files changed, 177 insertions(+), 121 deletions(-) diff --git a/crypto-ffi/src/generic.rs b/crypto-ffi/src/generic.rs index af96a70a1d..bb8db77735 100644 --- a/crypto-ffi/src/generic.rs +++ b/crypto-ffi/src/generic.rs @@ -132,7 +132,7 @@ impl UniffiCustomTypeConverter for Ciphersuite { fn into_custom(val: Self::Builtin) -> uniffi::Result { core_crypto::prelude::CiphersuiteName::try_from(val) .map(Into::into) - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError.into()) + .map_err(|_| CryptoError::ImplementationError.into()) } fn from_custom(obj: Self) -> Self::Builtin { @@ -173,7 +173,7 @@ impl UniffiCustomTypeConverter for Ciphersuites { val.iter().try_fold(Self(vec![]), |mut acc, c| -> uniffi::Result { let cs = core_crypto::prelude::CiphersuiteName::try_from(*c) .map(Into::into) - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError)?; + .map_err(|_| CryptoError::ImplementationError)?; acc.0.push(cs); Ok(acc) }) @@ -814,7 +814,7 @@ impl CoreCrypto { central.take().close().await?; Ok(()) } else { - Err(core_crypto::prelude::CryptoError::LockPoisonError.into()) + Err(CryptoError::LockPoisonError.into()) } } @@ -825,7 +825,7 @@ impl CoreCrypto { central.take().wipe().await?; Ok(()) } else { - Err(core_crypto::prelude::CryptoError::LockPoisonError.into()) + Err(CryptoError::LockPoisonError.into()) } } @@ -862,7 +862,7 @@ impl CoreCrypto { Ok(kp .tls_serialize_detached() .map_err(MlsError::from) - .map_err(core_crypto::prelude::CryptoError::from)?) + .map_err(CryptoError::from)?) }) .collect::>>>() } @@ -1281,7 +1281,8 @@ impl CoreCrypto { expiry_days: u32, ciphersuite: Ciphersuite, ) -> CoreCryptoResult> { - self.central + Ok(self + .central .lock() .await .e2ei_new_enrollment( @@ -1295,8 +1296,7 @@ impl CoreCrypto { .map(async_lock::Mutex::new) .map(std::sync::Arc::new) .map(E2eiEnrollment) - .map(std::sync::Arc::new) - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError.into()) + .map(std::sync::Arc::new)?) } /// See [core_crypto::mls::MlsCentral::e2ei_new_activation_enrollment] @@ -1309,7 +1309,8 @@ impl CoreCrypto { expiry_days: u32, ciphersuite: Ciphersuite, ) -> CoreCryptoResult> { - self.central + Ok(self + .central .lock() .await .e2ei_new_activation_enrollment( @@ -1323,8 +1324,7 @@ impl CoreCrypto { .map(async_lock::Mutex::new) .map(std::sync::Arc::new) .map(E2eiEnrollment) - .map(std::sync::Arc::new) - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError.into()) + .map(std::sync::Arc::new)?) } /// See [core_crypto::mls::MlsCentral::e2ei_new_rotate_enrollment] @@ -1337,7 +1337,8 @@ impl CoreCrypto { expiry_days: u32, ciphersuite: Ciphersuite, ) -> CoreCryptoResult> { - self.central + Ok(self + .central .lock() .await .e2ei_new_rotate_enrollment( @@ -1351,8 +1352,7 @@ impl CoreCrypto { .map(async_lock::Mutex::new) .map(std::sync::Arc::new) .map(E2eiEnrollment) - .map(std::sync::Arc::new) - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError.into()) + .map(std::sync::Arc::new)?) } /// See [core_crypto::mls::MlsCentral::e2ei_mls_init_only] @@ -1370,10 +1370,9 @@ impl CoreCrypto { std::sync::Arc::decrement_strong_count(std::sync::Arc::as_ptr(&enrollment)); } } - let e2ei = - std::sync::Arc::into_inner(enrollment).ok_or_else(|| core_crypto::prelude::CryptoError::LockPoisonError)?; + let e2ei = std::sync::Arc::into_inner(enrollment).ok_or_else(|| CryptoError::LockPoisonError)?; let e2ei = std::sync::Arc::into_inner(e2ei.0) - .ok_or_else(|| core_crypto::prelude::CryptoError::LockPoisonError)? + .ok_or_else(|| CryptoError::LockPoisonError)? .into_inner(); let nb_key_package = nb_key_package @@ -1381,12 +1380,12 @@ impl CoreCrypto { .transpose() .map_err(CryptoError::from)?; - self.central + Ok(self + .central .lock() .await .e2ei_mls_init_only(e2ei, certificate_chain, nb_key_package) - .await - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError.into()) + .await?) } /// See [core_crypto::mls::MlsCentral::e2ei_rotate_all] @@ -1404,36 +1403,27 @@ impl CoreCrypto { std::sync::Arc::decrement_strong_count(std::sync::Arc::as_ptr(&enrollment)); } } - let e2ei = - std::sync::Arc::into_inner(enrollment).ok_or_else(|| core_crypto::prelude::CryptoError::LockPoisonError)?; + let e2ei = std::sync::Arc::into_inner(enrollment).ok_or_else(|| CryptoError::LockPoisonError)?; let e2ei = std::sync::Arc::into_inner(e2ei.0) - .ok_or_else(|| core_crypto::prelude::CryptoError::LockPoisonError)? + .ok_or_else(|| CryptoError::LockPoisonError)? .into_inner(); self.central .lock() .await .e2ei_rotate_all(e2ei, certificate_chain, new_key_packages_count as usize) - .await - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError)? + .await? .try_into() } /// See [core_crypto::mls::MlsCentral::e2ei_enrollment_stash] pub async fn e2ei_enrollment_stash(&self, enrollment: std::sync::Arc) -> CoreCryptoResult> { - let enrollment = - std::sync::Arc::into_inner(enrollment).ok_or_else(|| core_crypto::prelude::CryptoError::LockPoisonError)?; + let enrollment = std::sync::Arc::into_inner(enrollment).ok_or_else(|| CryptoError::LockPoisonError)?; let enrollment = std::sync::Arc::into_inner(enrollment.0) - .ok_or_else(|| core_crypto::prelude::CryptoError::LockPoisonError)? + .ok_or_else(|| CryptoError::LockPoisonError)? .into_inner(); - Ok(self - .central - .lock() - .await - .e2ei_enrollment_stash(enrollment) - .await - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError)?) + Ok(self.central.lock().await.e2ei_enrollment_stash(enrollment).await?) } /// See [core_crypto::mls::MlsCentral::e2ei_enrollment_stash_pop] @@ -1447,8 +1437,7 @@ impl CoreCrypto { .map(async_lock::Mutex::new) .map(std::sync::Arc::new) .map(E2eiEnrollment) - .map(std::sync::Arc::new) - .map_err(|_| core_crypto::prelude::CryptoError::ImplementationError)?) + .map(std::sync::Arc::new)?) } /// See [core_crypto::mls::MlsCentral::e2ei_conversation_state] diff --git a/crypto-ffi/src/wasm.rs b/crypto-ffi/src/wasm.rs index 7742757e55..2aef1d9cad 100644 --- a/crypto-ffi/src/wasm.rs +++ b/crypto-ffi/src/wasm.rs @@ -2406,7 +2406,6 @@ impl CoreCrypto { ciphersuite.into(), ) .map(E2eiEnrollment) - .map_err(|_| CryptoError::ImplementationError) .map_err(CoreCryptoError::from)?; WasmCryptoResult::Ok(enrollment.into()) @@ -2442,7 +2441,6 @@ impl CoreCrypto { ciphersuite.into(), ) .map(E2eiEnrollment) - .map_err(|_| CryptoError::ImplementationError) .map_err(CoreCryptoError::from)?; WasmCryptoResult::Ok(enrollment.into()) @@ -2478,7 +2476,6 @@ impl CoreCrypto { ciphersuite.into(), ) .map(E2eiEnrollment) - .map_err(|_| CryptoError::ImplementationError) .map_err(CoreCryptoError::from)?; WasmCryptoResult::Ok(enrollment.into()) @@ -2554,7 +2551,6 @@ impl CoreCrypto { .e2ei_enrollment_stash_pop(handle.to_vec()) .await .map(E2eiEnrollment) - .map_err(|_| CryptoError::ImplementationError) .map_err(CoreCryptoError::from)?; WasmCryptoResult::Ok(enrollment.into()) @@ -2633,7 +2629,7 @@ impl CoreCrypto { let this = self.inner.clone(); future_to_promise( async move { - let identities: HashMap> = this + let identities = this .write() .await .get_user_identities(&conversation_id, user_ids.deref()) diff --git a/crypto/src/e2e_identity/crypto.rs b/crypto/src/e2e_identity/crypto.rs index f2b137c3a4..25e18b4244 100644 --- a/crypto/src/e2e_identity/crypto.rs +++ b/crypto/src/e2e_identity/crypto.rs @@ -1,6 +1,6 @@ use super::error::*; use crate::prelude::MlsCiphersuite; -use crate::CryptoError; +use crate::{CryptoError, CryptoResult, MlsError}; use mls_crypto_provider::MlsCryptoProvider; use openmls_basic_credential::SignatureKeyPair; use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite, OpenMlsCryptoProvider}; @@ -14,18 +14,20 @@ impl super::E2eiEnrollment { pub(super) fn new_sign_key( ciphersuite: MlsCiphersuite, backend: &MlsCryptoProvider, - ) -> E2eIdentityResult { + ) -> CryptoResult { let crypto = backend.crypto(); let cs = openmls_traits::types::Ciphersuite::from(ciphersuite); - let (sk, pk) = crypto.signature_key_gen(cs.signature_algorithm())?; + let (sk, pk) = crypto + .signature_key_gen(cs.signature_algorithm()) + .map_err(MlsError::from)?; Ok((sk, pk).into()) } - pub(super) fn get_sign_key_for_mls(&self) -> E2eIdentityResult> { + pub(super) fn get_sign_key_for_mls(&self) -> CryptoResult> { let sk = match self.sign_sk.len() { SIGN_KEYPAIR_LENGTH => &self.sign_sk[..SIGN_KEY_LENGTH], SIGN_KEY_LENGTH => &self.sign_sk, - _ => return Err(E2eIdentityError::ImplementationError), + _ => return Err(E2eIdentityError::InvalidSignatureKey.into()), }; Ok(sk.to_vec()) } @@ -67,7 +69,7 @@ impl TryFrom for E2eiSignatureKeypair { let sk = match sk.len() { SIGN_KEY_LENGTH => sk, SIGN_KEYPAIR_LENGTH => &sk[..SIGN_KEY_LENGTH], - _ => return Err(CryptoError::ImplementationError), + _ => return Err(E2eIdentityError::InvalidSignatureKey.into()), }; Ok((sk.to_vec(), kp.to_public_vec()).into()) } diff --git a/crypto/src/e2e_identity/enabled.rs b/crypto/src/e2e_identity/enabled.rs index 55c57fed2d..0171af2829 100644 --- a/crypto/src/e2e_identity/enabled.rs +++ b/crypto/src/e2e_identity/enabled.rs @@ -12,7 +12,7 @@ impl MlsCentral { None => { client .find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::Basic) - .ok_or(CryptoError::CredentialNotFound)?; + .ok_or(CryptoError::CredentialNotFound(MlsCredentialType::Basic))?; Ok(false) } Some(_) => Ok(true), @@ -69,7 +69,7 @@ pub mod tests { }; assert!(matches!( cc.e2ei_is_enabled(other_sc).unwrap_err(), - CryptoError::CredentialNotFound + CryptoError::CredentialNotFound(_) )); }) }) diff --git a/crypto/src/e2e_identity/error.rs b/crypto/src/e2e_identity/error.rs index 95a32339c2..fdbf6aa874 100644 --- a/crypto/src/e2e_identity/error.rs +++ b/crypto/src/e2e_identity/error.rs @@ -1,6 +1,6 @@ //! End to end identity errors -use crate::CryptoError; +use crate::prelude::MlsCredentialType; /// Wrapper over a [Result] of an end to end identity error pub type E2eIdentityResult = Result; @@ -16,6 +16,18 @@ pub enum E2eIdentityError { /// Incoming support #[error("Not yet supported")] NotYetSupported, + /// The required local MLS client was not initialized. It's likely a consumer error + #[error("Expected a MLS client with credential type {0:?} but none found")] + MissingExistingClient(MlsCredentialType), + /// Cannot read the identity in the EE certificate + #[error("Could not the identity information in the Credential's certificate")] + InvalidIdentity, + /// Failed converting the MLS signature key for the e2ei enrollment + #[error("Failed converting the MLS signature key for the e2ei enrollment")] + InvalidSignatureKey, + /// Enrollment methods are called out of order + #[error("Enrollment methods are called out of order: {0}")] + OutOfOrderEnrollment(&'static str), /// Error when an end-to-end-identity domain is not well-formed utf-16, which means it's out of spec #[error("The E2EI provided domain is invalid utf-16")] E2eiInvalidDomain, @@ -34,9 +46,6 @@ pub enum E2eIdentityError { /// Utf8 error #[error(transparent)] Utf8Error(#[from] ::core::str::Utf8Error), - /// MLS error - #[error(transparent)] - MlsError(#[from] CryptoError), /// !!!! Something went very wrong and one of our locks has been poisoned by an in-thread panic !!!! #[error("One of the locks has been poisoned")] LockPoisonError, diff --git a/crypto/src/e2e_identity/identity.rs b/crypto/src/e2e_identity/identity.rs index 79b7a0c02a..dfbb6e6ffd 100644 --- a/crypto/src/e2e_identity/identity.rs +++ b/crypto/src/e2e_identity/identity.rs @@ -86,7 +86,7 @@ impl MlsCentral { impl MlsConversation { fn get_device_identities(&self, device_ids: &[ClientId]) -> CryptoResult> { if device_ids.is_empty() { - return Err(CryptoError::ImplementationError); + return Err(CryptoError::ConsumerError); } self.members() .into_iter() @@ -97,7 +97,7 @@ impl MlsConversation { fn get_user_identities(&self, user_ids: &[String]) -> CryptoResult>> { if user_ids.is_empty() { - return Err(CryptoError::ImplementationError); + return Err(CryptoError::ConsumerError); } let user_ids = user_ids.iter().map(|uid| uid.as_bytes()).collect::>(); self.members() @@ -191,7 +191,7 @@ pub mod tests { ); let invalid = alice_android_central.get_device_identities(&id, &[]).await; - assert!(matches!(invalid.unwrap_err(), CryptoError::ImplementationError)); + assert!(matches!(invalid.unwrap_err(), CryptoError::ConsumerError)); }) }, ) @@ -311,7 +311,7 @@ pub mod tests { // Invalid usage let invalid = alice_android_central.get_user_identities(&id, &[]).await; - assert!(matches!(invalid.unwrap_err(), CryptoError::ImplementationError)); + assert!(matches!(invalid.unwrap_err(), CryptoError::ConsumerError)); }) }, ) diff --git a/crypto/src/e2e_identity/mod.rs b/crypto/src/e2e_identity/mod.rs index 71f0d60b1d..20768fd584 100644 --- a/crypto/src/e2e_identity/mod.rs +++ b/crypto/src/e2e_identity/mod.rs @@ -8,6 +8,7 @@ use crate::e2e_identity::crypto::E2eiSignatureKeypair; use crate::{ mls::credential::x509::CertificatePrivateKey, prelude::{id::ClientId, identifier::ClientIdentifier, CertificateBundle, MlsCentral, MlsCiphersuite}, + CryptoResult, }; pub(crate) mod conversation_state; @@ -39,7 +40,7 @@ impl MlsCentral { team: Option, expiry_days: u32, ciphersuite: MlsCiphersuite, - ) -> E2eIdentityResult { + ) -> CryptoResult { E2eiEnrollment::try_new( client_id, display_name, @@ -59,7 +60,7 @@ impl MlsCentral { enrollment: E2eiEnrollment, certificate_chain: String, nb_init_key_packages: Option, - ) -> E2eIdentityResult<()> { + ) -> CryptoResult<()> { let sk = enrollment.get_sign_key_for_mls()?; let cs = enrollment.ciphersuite; let certificate_chain = enrollment.certificate_response(certificate_chain).await?; @@ -123,7 +124,7 @@ impl E2eiEnrollment { backend: &MlsCryptoProvider, ciphersuite: MlsCiphersuite, sign_keypair: Option, - ) -> E2eIdentityResult { + ) -> CryptoResult { let alg = ciphersuite.try_into()?; let sign_sk = if let Some(kp) = sign_keypair { kp.0 @@ -134,7 +135,7 @@ impl E2eiEnrollment { let client_id = std::str::from_utf8(&client_id[..])?.to_string(); let expiry = core::time::Duration::from_secs(u64::from(expiry_days) * 24 * 3600); Ok(Self { - delegate: RustyE2eIdentity::try_new(alg, sign_sk.clone())?, + delegate: RustyE2eIdentity::try_new(alg, sign_sk.clone()).map_err(E2eIdentityError::from)?, sign_sk, client_id, display_name, @@ -174,7 +175,9 @@ impl E2eiEnrollment { /// * `directory` - you got from [Self::directory_response] /// * `previous_nonce` - you got from calling `HEAD {directory.new_nonce}` pub fn new_account_request(&self, previous_nonce: String) -> E2eIdentityResult { - let directory = self.directory.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let directory = self.directory.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'directoryResponse()'", + ))?; let account = self.acme_new_account_request(&directory.try_into()?, previous_nonce)?; let account = serde_json::to_vec(&account)?; Ok(account) @@ -200,8 +203,12 @@ impl E2eiEnrollment { /// # Parameters /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/new-account` pub fn new_order_request(&self, previous_nonce: String) -> E2eIdentityResult { - let directory = self.directory.as_ref().ok_or(E2eIdentityError::ImplementationError)?; - let account = self.account.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let directory = self.directory.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'directoryResponse()'", + ))?; + let account = self.account.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; let order = self.acme_new_order_request( &self.display_name, &self.client_id, @@ -236,7 +243,9 @@ impl E2eiEnrollment { /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/new-order` /// (or from the previous to this method if you are creating the second authorization) pub fn new_authz_request(&self, url: String, previous_nonce: String) -> E2eIdentityResult { - let account = self.account.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let account = self.account.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; let authz = self.acme_new_authz_request(&url.parse()?, account, previous_nonce)?; let authz = serde_json::to_vec(&authz)?; Ok(authz) @@ -271,11 +280,15 @@ impl E2eiEnrollment { #[allow(clippy::too_many_arguments)] pub fn create_dpop_token(&self, expiry_secs: u32, backend_nonce: String) -> E2eIdentityResult { let expiry = core::time::Duration::from_secs(expiry_secs as u64); - let authz = self.authz.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let authz = self.authz.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAuthzResponse()'", + ))?; let dpop_challenge = authz .wire_dpop_challenge .as_ref() - .ok_or(E2eIdentityError::ImplementationError)?; + .ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAuthzResponse()'", + ))?; Ok(self.new_dpop_token( &self.client_id, dpop_challenge, @@ -296,12 +309,18 @@ impl E2eiEnrollment { /// * `account` - you got from [Self::new_account_response] /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/authz/{authz-id}` pub fn new_dpop_challenge_request(&self, access_token: String, previous_nonce: String) -> E2eIdentityResult { - let authz = self.authz.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let authz = self.authz.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAuthzResponse()'", + ))?; let dpop_challenge = authz .wire_dpop_challenge .as_ref() - .ok_or(E2eIdentityError::ImplementationError)?; - let account = self.account.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + .ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'createDpopToken()'", + ))?; + let account = self.account.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; let challenge = self.acme_dpop_challenge_request(access_token, dpop_challenge, account, previous_nonce)?; let challenge = serde_json::to_vec(&challenge)?; Ok(challenge) @@ -317,12 +336,18 @@ impl E2eiEnrollment { /// * `account` - you got from [Self::new_account_response] /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/authz/{authz-id}` pub fn new_oidc_challenge_request(&self, id_token: String, previous_nonce: String) -> E2eIdentityResult { - let authz = self.authz.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let authz = self.authz.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAuthzResponse()'", + ))?; let oidc_challenge = authz .wire_oidc_challenge .as_ref() - .ok_or(E2eIdentityError::ImplementationError)?; - let account = self.account.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + .ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAuthzResponse()'", + ))?; + let account = self.account.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; let challenge = self.acme_oidc_challenge_request(id_token, oidc_challenge, account, previous_nonce)?; let challenge = serde_json::to_vec(&challenge)?; Ok(challenge) @@ -348,7 +373,9 @@ impl E2eiEnrollment { /// * `account` - you got from [Self::new_account_response] /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/challenge/{challenge-id}` pub fn check_order_request(&self, order_url: String, previous_nonce: String) -> E2eIdentityResult { - let account = self.account.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let account = self.account.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; let order = self.acme_check_order_request(order_url.parse()?, account, previous_nonce)?; let order = serde_json::to_vec(&order)?; Ok(order) @@ -381,8 +408,12 @@ impl E2eiEnrollment { /// * `account` - you got from [Self::new_account_response] /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/order/{order-id}` pub fn finalize_request(&mut self, previous_nonce: String) -> E2eIdentityResult { - let account = self.account.as_ref().ok_or(E2eIdentityError::ImplementationError)?; - let order = self.valid_order.as_ref().ok_or(E2eIdentityError::ImplementationError)?; + let account = self.account.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; + let order = self.valid_order.as_ref().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'checkOrderResponse()'", + ))?; let finalize = self.acme_finalize_request(order, account, previous_nonce)?; let finalize = serde_json::to_vec(&finalize)?; Ok(finalize) @@ -414,15 +445,21 @@ impl E2eiEnrollment { /// * `account` - you got from [Self::new_account_response] /// * `previous_nonce` - `replay-nonce` response header from `POST /acme/{provisioner-name}/order/{order-id}/finalize` pub fn certificate_request(&mut self, previous_nonce: String) -> E2eIdentityResult { - let account = self.account.take().ok_or(E2eIdentityError::ImplementationError)?; - let finalize = self.finalize.take().ok_or(E2eIdentityError::ImplementationError)?; + let account = self.account.take().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'newAccountResponse()'", + ))?; + let finalize = self.finalize.take().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'finalizeResponse()'", + ))?; let certificate = self.acme_x509_certificate_request(finalize, account, previous_nonce)?; let certificate = serde_json::to_vec(&certificate)?; Ok(certificate) } async fn certificate_response(mut self, certificate_chain: String) -> E2eIdentityResult>> { - let order = self.valid_order.take().ok_or(E2eIdentityError::ImplementationError)?; + let order = self.valid_order.take().ok_or(E2eIdentityError::OutOfOrderEnrollment( + "You must first call 'checkOrderResponse()'", + ))?; Ok(self.acme_x509_certificate_response(certificate_chain, order)?) } } @@ -435,6 +472,7 @@ pub mod tests { INITIAL_KEYING_MATERIAL_COUNT, }, test_utils::{central::TEAM, *}, + CryptoResult, }; use itertools::Itertools; use serde_json::json; @@ -490,11 +528,11 @@ pub mod tests { pub async fn e2ei_enrollment<'a>( cc: MlsCentral, client_id: Option<&str>, - init: impl Fn(&MlsCentral) -> E2eIdentityResult, + init: impl Fn(&MlsCentral) -> CryptoResult, // used to verify persisting the instance actually does restore it entirely restore: impl Fn(E2eiEnrollment, MlsCentral) -> RestoreFnResult<'a> + 'a, ) -> E2eIdentityResult<(MlsCentral, E2eiEnrollment, String)> { - let mut enrollment = init(&cc)?; + let mut enrollment = init(&cc).map_err(|_| E2eIdentityError::ImplementationError)?; let (display_name, handle) = (enrollment.display_name.clone(), &enrollment.handle.clone()); @@ -552,7 +590,7 @@ pub mod tests { let authz_url = new_order .authorizations - .get(0) + .first() .ok_or(E2eIdentityError::ImplementationError)?; let _authz_req = enrollment.new_authz_request(authz_url.to_string(), previous_nonce.to_string())?; diff --git a/crypto/src/e2e_identity/rotate.rs b/crypto/src/e2e_identity/rotate.rs index a52da817b7..61e380c125 100644 --- a/crypto/src/e2e_identity/rotate.rs +++ b/crypto/src/e2e_identity/rotate.rs @@ -2,8 +2,8 @@ use crate::prelude::ConversationId; use crate::{ mls::credential::{ext::CredentialExt, x509::CertificatePrivateKey, CredentialBundle}, prelude::{ - CertificateBundle, Client, ClientId, CryptoError, CryptoResult, E2eIdentityError, E2eIdentityResult, - E2eiEnrollment, MlsCentral, MlsCiphersuite, MlsCommitBundle, MlsConversation, MlsCredentialType, + CertificateBundle, Client, ClientId, CryptoError, CryptoResult, E2eIdentityError, E2eiEnrollment, MlsCentral, + MlsCiphersuite, MlsCommitBundle, MlsConversation, MlsCredentialType, }, MlsError, }; @@ -73,13 +73,13 @@ impl MlsCentral { team: Option, expiry_days: u32, ciphersuite: MlsCiphersuite, - ) -> E2eIdentityResult { + ) -> CryptoResult { let client = self.mls_client()?; // 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) - .ok_or(E2eIdentityError::ImplementationError)?; + .ok_or(E2eIdentityError::MissingExistingClient(MlsCredentialType::Basic))?; let sign_keypair = Some(cb.signature_key.clone().try_into()?); @@ -108,18 +108,18 @@ impl MlsCentral { team: Option, expiry_days: u32, ciphersuite: MlsCiphersuite, - ) -> E2eIdentityResult { + ) -> CryptoResult { let client = self.mls_client()?; // 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) - .ok_or(E2eIdentityError::ImplementationError)?; + .ok_or(E2eIdentityError::MissingExistingClient(MlsCredentialType::X509))?; let sign_keypair = Some(cb.signature_key.clone().try_into()?); let existing_identity = cb .credential() .extract_identity()? - .ok_or(E2eIdentityError::ImplementationError)?; + .ok_or(E2eIdentityError::InvalidIdentity)?; let display_name = display_name.unwrap_or(existing_identity.display_name); let handle = handle.unwrap_or(existing_identity.handle); @@ -144,7 +144,7 @@ impl MlsCentral { enrollment: E2eiEnrollment, certificate_chain: String, new_key_packages_count: usize, - ) -> E2eIdentityResult { + ) -> CryptoResult { let sk = enrollment.get_sign_key_for_mls()?; let cs = enrollment.ciphersuite; let certificate_chain = enrollment.certificate_response(certificate_chain).await?; @@ -161,7 +161,7 @@ impl MlsCentral { let new_cb = self .mls_client .as_mut() - .ok_or(CryptoError::ImplementationError)? + .ok_or(CryptoError::MlsNotInitialized)? .save_new_x509_credential_bundle(&self.mls_backend, cs.signature_algorithm(), cert_bundle) .await?; @@ -794,7 +794,7 @@ pub mod tests { // Alice's previous rotate commit should have been renewed so that she can re-commit it assert_eq!(decrypted.proposals.len(), 1); - let renewed_proposal = decrypted.proposals.get(0).unwrap(); + let renewed_proposal = decrypted.proposals.first().unwrap(); bob_central .decrypt_message(&id, renewed_proposal.proposal.to_bytes().unwrap()) .await diff --git a/crypto/src/e2e_identity/stash.rs b/crypto/src/e2e_identity/stash.rs index 681ff20a6f..cf8295311f 100644 --- a/crypto/src/e2e_identity/stash.rs +++ b/crypto/src/e2e_identity/stash.rs @@ -1,4 +1,5 @@ -use crate::prelude::{CryptoError, E2eIdentityResult, E2eiEnrollment, MlsCentral}; +use crate::prelude::{CryptoError, E2eiEnrollment, MlsCentral}; +use crate::CryptoResult; use core_crypto_keystore::CryptoKeystoreMls; use mls_crypto_provider::MlsCryptoProvider; use openmls_traits::{random::OpenMlsRand, OpenMlsCryptoProvider}; @@ -8,7 +9,7 @@ use openmls_traits::{random::OpenMlsRand, OpenMlsCryptoProvider}; pub(crate) type EnrollmentHandle = Vec; impl E2eiEnrollment { - pub(crate) async fn stash(self, backend: &MlsCryptoProvider) -> E2eIdentityResult { + pub(crate) async fn stash(self, backend: &MlsCryptoProvider) -> CryptoResult { // should be enough to prevent collisions const HANDLE_SIZE: usize = 32; @@ -22,7 +23,7 @@ impl E2eiEnrollment { Ok(handle) } - pub(crate) async fn stash_pop(backend: &MlsCryptoProvider, handle: EnrollmentHandle) -> E2eIdentityResult { + pub(crate) async fn stash_pop(backend: &MlsCryptoProvider, handle: EnrollmentHandle) -> CryptoResult { let content = backend .key_store() .pop_e2ei_enrollment(&handle) @@ -41,7 +42,7 @@ impl MlsCentral { /// /// # Returns /// A handle for retrieving the enrollment later on - pub async fn e2ei_enrollment_stash(&self, enrollment: E2eiEnrollment) -> E2eIdentityResult { + pub async fn e2ei_enrollment_stash(&self, enrollment: E2eiEnrollment) -> CryptoResult { enrollment.stash(&self.mls_backend).await } @@ -49,7 +50,7 @@ impl MlsCentral { /// /// # Arguments /// * `handle` - returned by [MlsCentral::e2ei_enrollment_stash] - pub async fn e2ei_enrollment_stash_pop(&self, handle: EnrollmentHandle) -> E2eIdentityResult { + pub async fn e2ei_enrollment_stash_pop(&self, handle: EnrollmentHandle) -> CryptoResult { E2eiEnrollment::stash_pop(&self.mls_backend, handle).await } } diff --git a/crypto/src/error.rs b/crypto/src/error.rs index 19e4c30550..5d7d67dcd4 100644 --- a/crypto/src/error.rs +++ b/crypto/src/error.rs @@ -15,12 +15,16 @@ // along with this program. If not, see http://www.gnu.org/licenses/. use crate::mls::conversation::config::MAX_PAST_EPOCHS; +use crate::prelude::{E2eIdentityError, MlsCredentialType}; /// CoreCrypto errors #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] #[cfg_attr(feature = "uniffi", derive(uniffi::Error))] #[cfg_attr(feature = "uniffi", uniffi(flat_error))] pub enum CryptoError { + /// End to end identity error + #[error("End to end identity error")] + E2eiError(#[from] E2eIdentityError), /// This error is emitted when the requested conversation couldn't be found in our store #[error("Couldn't find conversation")] ConversationNotFound(crate::prelude::ConversationId), @@ -66,6 +70,12 @@ pub enum CryptoError { /// We have done something terribly wrong #[error("We have done something terribly wrong and it needs to be fixed")] ImplementationError, + /// Tried to insert an already existing CredentialBundle + #[error("Tried to insert an already existing CredentialBundle")] + CredentialBundleConflict, + /// The consumer of this library has misused it + #[error("The consumer of this library has misused it")] + ConsumerError, /// Errors that are sent by our MLS Provider #[error(transparent)] MlsProviderError(#[from] mls_crypto_provider::MlsProviderError), @@ -158,8 +168,8 @@ pub enum CryptoError { #[error("End-to-end identity enrollment has not been done")] E2eiEnrollmentNotDone, /// A Credential was not found locally which is very likely an implementation error - #[error("A Credential was not found locally which is very likely an implementation error")] - CredentialNotFound, + #[error("A Credential of type {0:?} was not found locally which is very likely an implementation error")] + CredentialNotFound(MlsCredentialType), /// The MLS group is in an invalid state for an unknown reason #[error("The MLS group is in an invalid state for an unknown reason")] InternalMlsError, @@ -224,6 +234,9 @@ pub enum CryptoError { /// The encountered ClientId does not match Wire's definition #[error("The encountered ClientId does not match Wire's definition")] InvalidClientId, + /// Json error + #[error(transparent)] + JsonError(#[from] serde_json::Error), } /// A simpler definition for Result types that the Error is a [CryptoError] diff --git a/crypto/src/mls/client/identities.rs b/crypto/src/mls/client/identities.rs index 4c174a864a..c01eb8da45 100644 --- a/crypto/src/mls/client/identities.rs +++ b/crypto/src/mls/client/identities.rs @@ -52,7 +52,7 @@ impl ClientIdentities { Some(cbs) => { let already_exists = !cbs.insert(cb); if already_exists { - return Err(CryptoError::ImplementationError); + return Err(CryptoError::CredentialBundleConflict); } } None => { @@ -204,7 +204,10 @@ pub mod tests { let cb = central.new_credential_bundle(&case).await; let client = central.mls_client.as_mut().unwrap(); let push = client.identities.push_credential_bundle(case.signature_scheme(), cb); - assert!(push.is_err()); + assert!(matches!( + push.unwrap_err(), + crate::CryptoError::CredentialBundleConflict + )); }) }) .await diff --git a/crypto/src/mls/client/key_package.rs b/crypto/src/mls/client/key_package.rs index 53931abb0e..0c352bb49c 100644 --- a/crypto/src/mls/client/key_package.rs +++ b/crypto/src/mls/client/key_package.rs @@ -348,7 +348,7 @@ impl MlsCentral { #[cfg_attr(test, crate::dispotent)] pub async fn delete_keypackages(&mut self, refs: &[KeyPackageRef]) -> CryptoResult<()> { if refs.is_empty() { - return Err(CryptoError::ImplementationError); + return Err(CryptoError::ConsumerError); } let client = self.mls_client.as_mut().ok_or(CryptoError::MlsNotInitialized)?; client.prune_keypackages_and_credential(&self.mls_backend, refs).await @@ -557,7 +557,7 @@ pub mod tests { kp.leaf_node().capabilities().ciphersuites().to_vec(), MlsConversationConfiguration::DEFAULT_SUPPORTED_CIPHERSUITES .iter() - .map(|c| VerifiableCiphersuite::try_from(*c).unwrap()) + .map(|c| VerifiableCiphersuite::from(*c)) .collect::>() ); assert!(kp.leaf_node().capabilities().proposals().is_empty()); diff --git a/crypto/src/mls/client/mod.rs b/crypto/src/mls/client/mod.rs index dcc1081959..46f219d26f 100644 --- a/crypto/src/mls/client/mod.rs +++ b/crypto/src/mls/client/mod.rs @@ -27,6 +27,7 @@ use crate::{ CryptoError, CryptoResult, MlsCentral, MlsCiphersuite, MlsCredentialType, MlsError, }, }; +use core_crypto_keystore::CryptoKeystoreError; use openmls::prelude::{Credential, CredentialType}; use openmls_basic_credential::SignatureKeyPair; use openmls_traits::{crypto::OpenMlsCrypto, types::SignatureScheme, OpenMlsCryptoProvider}; @@ -360,9 +361,10 @@ impl Client { let id = id.unwrap_or_else(|| self.id()); + let credential = cb.credential.tls_serialize_detached().map_err(MlsError::from)?; let credential = MlsCredential { id: id.clone().into(), - credential: cb.credential.tls_serialize_detached().map_err(MlsError::from)?, + credential, created_at: 0, }; let created_at = credential.insert(&mut conn).await?; @@ -373,7 +375,10 @@ impl Client { cb.signature_key.tls_serialize_detached().map_err(MlsError::from)?, id.clone().into(), ); - sign_kp.save(&mut conn).await?; + sign_kp.save(&mut conn).await.map_err(|e| match e { + CryptoKeystoreError::AlreadyExists => CryptoError::CredentialBundleConflict, + _ => e.into(), + })?; // set the creation date of the signature keypair which is the same for the CredentialBundle cb.created_at = created_at; @@ -398,7 +403,7 @@ impl Client { MlsCredentialType::Basic => { self.init_basic_credential_bundle_if_missing(backend, sc).await?; self.find_most_recent_credential_bundle(sc, ct) - .ok_or(CryptoError::ImplementationError) + .ok_or(CryptoError::CredentialNotFound(ct)) } MlsCredentialType::X509 => self .find_most_recent_credential_bundle(sc, ct) diff --git a/crypto/src/mls/conversation/buffer_messages.rs b/crypto/src/mls/conversation/buffer_messages.rs index 4d10ffef34..ad92bae162 100644 --- a/crypto/src/mls/conversation/buffer_messages.rs +++ b/crypto/src/mls/conversation/buffer_messages.rs @@ -88,7 +88,7 @@ impl MlsConversation { let ct = match msg.body_as_ref() { MlsMessageInBody::PublicMessage(m) => Ok(m.content_type()), MlsMessageInBody::PrivateMessage(m) => Ok(m.content_type()), - _ => Err(CryptoError::ImplementationError), + _ => Err(CryptoError::ConsumerError), }?; acc.push((ct as u8, msg)); CryptoResult::Ok(acc) diff --git a/crypto/src/mls/conversation/config.rs b/crypto/src/mls/conversation/config.rs index ad554658df..7961539ba0 100644 --- a/crypto/src/mls/conversation/config.rs +++ b/crypto/src/mls/conversation/config.rs @@ -258,7 +258,7 @@ pub mod tests { creator_capabilities.ciphersuites().to_vec(), MlsConversationConfiguration::DEFAULT_SUPPORTED_CIPHERSUITES .iter() - .map(|c| VerifiableCiphersuite::try_from(*c).unwrap()) + .map(|c| VerifiableCiphersuite::from(*c)) .collect::>() ); diff --git a/crypto/src/mls/conversation/merge.rs b/crypto/src/mls/conversation/merge.rs index 75fa56f526..ca01fa4f0e 100644 --- a/crypto/src/mls/conversation/merge.rs +++ b/crypto/src/mls/conversation/merge.rs @@ -327,7 +327,7 @@ pub mod tests { run_test_with_client_ids(case.clone(), ["alice"], move |[mut alice_central]| { Box::pin(async move { let id = conversation_id(); - let simple_ref = MlsProposalRef::try_from(vec![0; case.ciphersuite().hash_length()]).unwrap(); + let simple_ref = MlsProposalRef::from(vec![0; case.ciphersuite().hash_length()]); let clear = alice_central.clear_pending_proposal(&id, simple_ref).await; assert!(matches!(clear.unwrap_err(), CryptoError::ConversationNotFound(conv_id) if conv_id == id)) }) @@ -346,7 +346,7 @@ pub mod tests { .await .unwrap(); assert!(alice_central.pending_proposals(&id).await.is_empty()); - let any_ref = MlsProposalRef::try_from(vec![0; case.ciphersuite().hash_length()]).unwrap(); + let any_ref = MlsProposalRef::from(vec![0; case.ciphersuite().hash_length()]); let clear = alice_central.clear_pending_proposal(&id, any_ref.clone()).await; assert!(matches!(clear.unwrap_err(), CryptoError::PendingProposalNotFound(prop_ref) if prop_ref == any_ref)) }) diff --git a/crypto/src/mls/conversation/mod.rs b/crypto/src/mls/conversation/mod.rs index 88d9963c39..f96cca026c 100644 --- a/crypto/src/mls/conversation/mod.rs +++ b/crypto/src/mls/conversation/mod.rs @@ -218,7 +218,7 @@ impl MlsConversation { Ok(self .group .own_leaf_node() - .ok_or(CryptoError::ImplementationError)? + .ok_or(CryptoError::InternalMlsError)? .credential() .credential_type() .into()) diff --git a/crypto/src/mls/conversation/welcome.rs b/crypto/src/mls/conversation/welcome.rs index 8786ae99a3..270f9bef74 100644 --- a/crypto/src/mls/conversation/welcome.rs +++ b/crypto/src/mls/conversation/welcome.rs @@ -55,7 +55,7 @@ impl MlsCentral { ) -> CryptoResult { let welcome = match welcome.extract() { MlsMessageInBody::Welcome(welcome) => welcome, - _ => return Err(CryptoError::ImplementationError), + _ => return Err(CryptoError::ConsumerError), }; let cs = welcome.ciphersuite().into(); let configuration = MlsConversationConfiguration { diff --git a/crypto/src/mls/credential/x509.rs b/crypto/src/mls/credential/x509.rs index b2c07392b1..1b1603fb72 100644 --- a/crypto/src/mls/credential/x509.rs +++ b/crypto/src/mls/credential/x509.rs @@ -34,14 +34,14 @@ pub struct CertificateBundle { impl CertificateBundle { /// Reads the client_id from the leaf certificate pub fn get_client_id(&self) -> CryptoResult { - let leaf = self.certificate_chain.get(0).ok_or(CryptoError::InvalidIdentity)?; + let leaf = self.certificate_chain.first().ok_or(CryptoError::InvalidIdentity)?; let identity = leaf.extract_identity().map_err(|_| CryptoError::InvalidIdentity)?; Ok(identity.client_id.as_bytes().into()) } /// Reads the 'Not Before' claim from the leaf certificate pub fn get_created_at(&self) -> CryptoResult { - let leaf = self.certificate_chain.get(0).ok_or(CryptoError::InvalidIdentity)?; + let leaf = self.certificate_chain.first().ok_or(CryptoError::InvalidIdentity)?; leaf.extract_created_at().map_err(|_| CryptoError::InvalidIdentity) } } diff --git a/crypto/src/mls/external_proposal.rs b/crypto/src/mls/external_proposal.rs index 7598cf003b..cbfacc8977 100644 --- a/crypto/src/mls/external_proposal.rs +++ b/crypto/src/mls/external_proposal.rs @@ -127,7 +127,7 @@ impl MlsCentral { self.mls_client()? .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type) - .ok_or(CryptoError::ImplementationError)? + .ok_or(CryptoError::CredentialNotFound(credential_type))? } (None, MlsCredentialType::X509) => return Err(CryptoError::E2eiEnrollmentNotDone), }; diff --git a/crypto/src/mls/mod.rs b/crypto/src/mls/mod.rs index 47ce06f37d..8414616f8d 100644 --- a/crypto/src/mls/mod.rs +++ b/crypto/src/mls/mod.rs @@ -243,7 +243,7 @@ impl MlsCentral { ) -> CryptoResult<()> { if self.mls_client.is_some() { // prevents wrong usage of the method instead of silently hiding the mistake - return Err(CryptoError::ImplementationError); + return Err(CryptoError::ConsumerError); } let nb_key_package = nb_init_key_packages.unwrap_or(INITIAL_KEYING_MATERIAL_COUNT); let mls_client = Client::init(identifier, &ciphersuites, &self.mls_backend, nb_key_package).await?; @@ -259,7 +259,7 @@ impl MlsCentral { pub async fn mls_generate_keypairs(&self, ciphersuites: Vec) -> CryptoResult> { if self.mls_client.is_some() { // prevents wrong usage of the method instead of silently hiding the mistake - return Err(CryptoError::ImplementationError); + return Err(CryptoError::ConsumerError); } Client::generate_raw_keypairs(&ciphersuites, &self.mls_backend).await @@ -277,7 +277,7 @@ impl MlsCentral { ) -> CryptoResult<()> { if self.mls_client.is_some() { // prevents wrong usage of the method instead of silently hiding the mistake - return Err(CryptoError::ImplementationError); + return Err(CryptoError::ConsumerError); } let mls_client = diff --git a/crypto/src/test_utils/mod.rs b/crypto/src/test_utils/mod.rs index cd8ed7e167..5e75b280c1 100644 --- a/crypto/src/test_utils/mod.rs +++ b/crypto/src/test_utils/mod.rs @@ -123,7 +123,7 @@ pub async fn run_test_wo_clients( ) { run_tests(move |paths: [String; 1]| { Box::pin(async move { - let p = paths.get(0).unwrap(); + let p = paths.first().unwrap(); let ciphersuites = vec![case.cfg.ciphersuite]; let configuration = MlsCentralConfiguration::try_new( diff --git a/crypto/src/test_utils/x509.rs b/crypto/src/test_utils/x509.rs index 232912fdbf..5907a91def 100644 --- a/crypto/src/test_utils/x509.rs +++ b/crypto/src/test_utils/x509.rs @@ -42,7 +42,7 @@ impl From for PerDomainTrustAnchor { .map(|c| pem::Pem::new("CERTIFICATE", c.to_der().unwrap())) .collect::>(); Self { - domain_name: domains.get(0).cloned().unwrap_or_default(), + domain_name: domains.first().cloned().unwrap_or_default(), intermediate_certificate_chain: pem::encode_many(&pems), } }