diff --git a/Cargo.toml b/Cargo.toml index 233786089f..09685d8235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ resolver = "2" async-trait = "0.1" [workspace.dependencies.tls_codec] -version = "0.4.0" +version = "0.4" features = ["derive", "serde", "mls"] [workspace.dependencies.tls_codec_derive] -version = "0.4.0" +version = "0.4" features = ["derive", "serde", "mls"] diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 6e0d6a0c7b..aec1c59f1c 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -24,7 +24,7 @@ x509-cert = "0.2" subtle = "2.5" fluvio-wasm-timer = "0.2" indexmap = "2.0" -itertools = "0.11" +itertools = "0.12" # Only required for tests. rand = { version = "0.8", optional = true, features = ["getrandom"] } diff --git a/openmls/src/credentials/errors.rs b/openmls/src/credentials/errors.rs index f0788eb260..0a7c1bc43a 100644 --- a/openmls/src/credentials/errors.rs +++ b/openmls/src/credentials/errors.rs @@ -3,10 +3,11 @@ //! This module exposes [`CredentialError`]. use crate::error::LibraryError; +use openmls_traits::authentication_service::CredentialAuthenticationStatus; use thiserror::Error; /// An error that occurs in methods of a [`super::Credential`]. -#[derive(Error, Debug, PartialEq, Clone)] +#[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum CredentialError { /// A library error occurred. #[error(transparent)] @@ -26,4 +27,6 @@ pub enum CredentialError { /// x509 certificate chain is either unordered or a child is missigned by its issuer #[error("Invalid x509 certificate chain.")] InvalidCertificateChain, + #[error("The Authentication Service callback rejected this credential for the following reason: {0}")] + AuthenticationServiceValidationFailure(CredentialAuthenticationStatus), } diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index 1f6d091ba7..e5abf5f441 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -24,6 +24,10 @@ use std::io::{Read, Write}; +use openmls_traits::{ + authentication_service::{AuthenticationServiceDelegate, CredentialAuthenticationStatus}, + OpenMlsCryptoProvider, +}; use serde::{Deserialize, Serialize}; use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize, VLBytes}; @@ -271,6 +275,56 @@ impl Credential { MlsCredentialType::X509(cert) => cert.identity.as_slice(), } } + + pub async fn validate( + &self, + backend: &impl OpenMlsCryptoProvider, + ) -> Result<(), CredentialError> { + let tmp_certs = if let MlsCredentialType::X509(x509_certs) = &self.credential { + Some( + x509_certs + .certificates + .iter() + .map(|bytes| bytes.as_slice()) + .collect::>(), + ) + } else { + None + }; + + let credential_ref = match &self.credential { + MlsCredentialType::Basic(basic_cred) => { + openmls_traits::authentication_service::CredentialRef::Basic { + identity: basic_cred.identity.as_slice(), + } + } + + MlsCredentialType::X509(_) => { + let credential_ref = openmls_traits::authentication_service::CredentialRef::X509 { + certificates: tmp_certs.as_ref().unwrap().as_slice(), + }; + credential_ref + } + }; + + let credential_authentication = backend + .authentication_service() + .validate_credential(credential_ref) + .await; + + if !matches!( + credential_authentication, + CredentialAuthenticationStatus::Valid | CredentialAuthenticationStatus::Expired + ) { + return Err(CredentialError::AuthenticationServiceValidationFailure( + credential_authentication, + )); + } + + drop(tmp_certs); + + Ok(()) + } } impl From for Credential { diff --git a/openmls/src/framing/mls_auth_content_in.rs b/openmls/src/framing/mls_auth_content_in.rs index 214a618352..01d7e3908a 100644 --- a/openmls/src/framing/mls_auth_content_in.rs +++ b/openmls/src/framing/mls_auth_content_in.rs @@ -9,7 +9,7 @@ use std::io::Read; -use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite}; +use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; use tls_codec::Serialize as TlsSerializeTrait; use super::{mls_auth_content::*, mls_content_in::*, *}; @@ -47,23 +47,26 @@ pub struct AuthenticatedContentIn { impl AuthenticatedContentIn { /// Returns a [`AuthenticatedContent`] after successful validation. - pub fn validate( + pub async fn validate( self, ciphersuite: Ciphersuite, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, sender_context: Option, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result { Ok(AuthenticatedContent { wire_format: self.wire_format, - content: self.content.validate( - ciphersuite, - crypto, - sender_context, - protocol_version, - group, - )?, + content: self + .content + .validate( + ciphersuite, + backend, + sender_context, + protocol_version, + group, + ) + .await?, auth: self.auth, }) } diff --git a/openmls/src/framing/mls_content_in.rs b/openmls/src/framing/mls_content_in.rs index dce5797e9b..a300a986ea 100644 --- a/openmls/src/framing/mls_content_in.rs +++ b/openmls/src/framing/mls_content_in.rs @@ -19,7 +19,7 @@ use super::{ }; use crate::prelude::PublicGroup; -use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite}; +use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; use serde::{Deserialize, Serialize}; use tls_codec::{ Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTrait, Size, TlsDeserialize, @@ -50,10 +50,10 @@ pub struct FramedContentIn { impl FramedContentIn { /// Returns a [`FramedContent`] after successful validation. - pub fn validate( + pub async fn validate( self, ciphersuite: Ciphersuite, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, sender_context: Option, protocol_version: ProtocolVersion, group: &PublicGroup, @@ -63,13 +63,16 @@ impl FramedContentIn { epoch: self.epoch, sender: self.sender, authenticated_data: self.authenticated_data, - body: self.body.validate( - ciphersuite, - crypto, - sender_context, - protocol_version, - group, - )?, + body: self + .body + .validate( + ciphersuite, + backend, + sender_context, + protocol_version, + group, + ) + .await?, }) } } @@ -135,35 +138,41 @@ impl FramedContentBodyIn { } /// Returns a [`FramedContentBody`] after successful validation. - pub fn validate( + pub async fn validate( self, ciphersuite: Ciphersuite, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, sender_context: Option, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result { Ok(match self { FramedContentBodyIn::Application(bytes) => FramedContentBody::Application(bytes), - FramedContentBodyIn::Proposal(proposal_in) => { - FramedContentBody::Proposal(proposal_in.validate( - crypto, - ciphersuite, - sender_context, - protocol_version, - group, - )?) - } + FramedContentBodyIn::Proposal(proposal_in) => FramedContentBody::Proposal( + proposal_in + .validate( + backend, + ciphersuite, + sender_context, + protocol_version, + group, + ) + .await?, + ), FramedContentBodyIn::Commit(commit_in) => { let sender_context = sender_context .ok_or(LibraryError::custom("Forgot the commit sender context"))?; - FramedContentBody::Commit(commit_in.validate( - ciphersuite, - crypto, - sender_context, - protocol_version, - group, - )?) + FramedContentBody::Commit( + commit_in + .validate( + ciphersuite, + backend, + sender_context, + protocol_version, + group, + ) + .await?, + ) } }) } diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index 92098e304d..85ddd07deb 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -24,7 +24,6 @@ // TODO #106/#151: Update the above diagram use openmls_traits::{ - crypto::OpenMlsCrypto, types::{Ciphersuite, CryptoError}, OpenMlsCryptoProvider, }; @@ -273,17 +272,17 @@ impl UnverifiedMessage { /// Verify the [`UnverifiedMessage`]. Returns the [`AuthenticatedContent`] /// and the internal [`Credential`]. - pub(crate) fn verify( + pub(crate) async fn verify( self, ciphersuite: Ciphersuite, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result<(AuthenticatedContent, Credential), ProcessMessageError> { let content: AuthenticatedContentIn = match self.credential.mls_credential() { MlsCredentialType::Basic(_) => self .verifiable_content - .verify(crypto, &self.sender_pk) + .verify(backend.crypto(), &self.sender_pk) .map_err(|_| ProcessMessageError::InvalidSignature)?, MlsCredentialType::X509(certificate_chain) => { certificate_chain @@ -300,7 +299,7 @@ impl UnverifiedMessage { child_cert.is_valid()?; // verify that child is signed by parent - child_cert.is_signed_by(crypto, parent_cert)?; + child_cert.is_signed_by(backend.crypto(), parent_cert)?; Ok((parent_idx, parent_cert)) }, @@ -312,17 +311,19 @@ impl UnverifiedMessage { })??; self.verifiable_content // sender pk should be the leaf certificate - .verify(crypto, &self.sender_pk) + .verify(backend.crypto(), &self.sender_pk) .map_err(|_| ProcessMessageError::InvalidSignature)? } }; - let content = content.validate( - ciphersuite, - crypto, - self.sender_context, - protocol_version, - group, - )?; + let content = content + .validate( + ciphersuite, + backend, + self.sender_context, + protocol_version, + group, + ) + .await?; Ok((content, self.credential)) } diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 9c8ff5ef06..8837eda493 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -243,7 +243,7 @@ impl CoreGroupBuilder { /// [`OpenMlsCryptoProvider`]. pub(crate) async fn build( self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ) -> Result> { let (public_group_builder, commit_secret, leaf_keypair) = @@ -270,7 +270,7 @@ impl CoreGroupBuilder { .map_err(LibraryError::unexpected_crypto_error)?, &serialized_group_context, ) - .map_err(LibraryError::unexpected_crypto_error)?; + .map_err(LibraryError::unexpected_crypto_error)?; // TODO(#1357) let resumption_psk_store = ResumptionPskStore::new(32); @@ -407,7 +407,7 @@ impl CoreGroup { self.context(), signer, ) - .map_err(ValidationError::LibraryError) + .map_err(ValidationError::LibraryError) } // 11.1.4. PreSharedKey @@ -437,7 +437,7 @@ impl CoreGroup { pub(crate) fn members_support_extensions<'a>( &self, extensions: &Extensions, - pending_proposals: impl Iterator, + pending_proposals: impl Iterator, ) -> Result<(), MemberExtensionValidationError> { let required_extension = extensions .iter() @@ -470,7 +470,7 @@ impl CoreGroup { &self, framing_parameters: FramingParameters, extensions: Extensions, - pending_proposals: impl Iterator, + pending_proposals: impl Iterator, signer: &impl Signer, ) -> Result { // Ensure that the group supports all the extensions that are wanted. @@ -485,7 +485,7 @@ impl CoreGroup { self.context(), signer, ) - .map_err(|e| e.into()) + .map_err(|e| e.into()) } /// Create a `ReInit` proposal pub(crate) fn create_reinit_proposal( @@ -512,7 +512,7 @@ impl CoreGroup { self.context(), signer, ) - .map_err(|e| e.into()) + .map_err(|e| e.into()) } // Create application message pub(crate) fn create_application_message( @@ -815,7 +815,7 @@ impl CoreGroup { /// Returns an error if access to the key store fails. pub(super) async fn store_epoch_keypairs( &self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, epoch_encryption_keypair: EpochEncryptionKeyPair, ) -> Result<(), KeyStore::Error> { let k = EpochKeypairId::new( @@ -835,7 +835,7 @@ impl CoreGroup { /// Returns `None` if access to the key store fails. pub(super) async fn read_epoch_keypairs( &self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, ) -> EpochEncryptionKeyPair { let k = EpochKeypairId::new( self.group_id(), @@ -855,7 +855,7 @@ impl CoreGroup { /// Returns an error if access to the key store fails. pub(super) async fn delete_previous_epoch_keypairs( &self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { let k = EpochKeypairId::new( self.group_id(), @@ -871,7 +871,7 @@ impl CoreGroup { pub(crate) async fn create_commit( &self, mut params: CreateCommitParams<'_>, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, ) -> Result> { let ciphersuite = self.ciphersuite(); @@ -890,15 +890,15 @@ impl CoreGroup { params.inline_proposals(), self.own_leaf_index(), ) - .map_err(|e| match e { - crate::group::errors::ProposalQueueError::LibraryError(e) => e.into(), - crate::group::errors::ProposalQueueError::ProposalNotFound => { - CreateCommitError::MissingProposal - } - crate::group::errors::ProposalQueueError::SenderError(_) => { - CreateCommitError::WrongProposalSenderType - } - })?; + .map_err(|e| match e { + crate::group::errors::ProposalQueueError::LibraryError(e) => e.into(), + crate::group::errors::ProposalQueueError::ProposalNotFound => { + CreateCommitError::MissingProposal + } + crate::group::errors::ProposalQueueError::SenderError(_) => { + CreateCommitError::WrongProposalSenderType + } + })?; // TODO: #581 Filter proposals by support // 11.2: @@ -1028,7 +1028,7 @@ impl CoreGroup { self.group_epoch_secrets().init_secret(), &serialized_provisional_group_context, ) - .map_err(LibraryError::unexpected_crypto_error)?; + .map_err(LibraryError::unexpected_crypto_error)?; // Prepare the PskSecret let psk_secret = { @@ -1037,7 +1037,7 @@ impl CoreGroup { &self.resumption_psk_store, &apply_proposals_values.presharedkeys, ) - .await?; + .await?; PskSecret::new(backend, ciphersuite, psks).await? }; @@ -1191,7 +1191,7 @@ impl MlsGroup { /// re-export pub async fn delete_previous_epoch_keypairs( &self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, ) -> Result<(), KeyStore::Error> { self.group.delete_previous_epoch_keypairs(backend).await } diff --git a/openmls/src/group/core_group/new_from_welcome.rs b/openmls/src/group/core_group/new_from_welcome.rs index e4cf491058..7ce8cbc0be 100644 --- a/openmls/src/group/core_group/new_from_welcome.rs +++ b/openmls/src/group/core_group/new_from_welcome.rs @@ -153,11 +153,9 @@ impl CoreGroup { ProposalStore::new(), )?; - KeyPackageIn::from(key_package.clone()).validate( - backend.crypto(), - ProtocolVersion::Mls10, - &public_group, - )?; + KeyPackageIn::from(key_package.clone()) + .validate(backend, ProtocolVersion::Mls10, &public_group) + .await?; // Find our own leaf in the tree. let own_leaf_index = public_group diff --git a/openmls/src/group/core_group/process.rs b/openmls/src/group/core_group/process.rs index fd27c82340..3b3be81c48 100644 --- a/openmls/src/group/core_group/process.rs +++ b/openmls/src/group/core_group/process.rs @@ -49,12 +49,14 @@ impl CoreGroup { // Checks the following semantic validation: // - ValSem010 // - ValSem246 (as part of ValSem010) - let (content, credential) = unverified_message.verify( - self.ciphersuite(), - backend.crypto(), - self.version(), - self.public_group(), - )?; + let (content, credential) = unverified_message + .verify( + self.ciphersuite(), + backend, + self.version(), + self.public_group(), + ) + .await?; match content.sender() { Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => { diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 8576d84d4c..2a4b9aa445 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -43,7 +43,7 @@ impl CoreGroup { &init_secret, serialized_provisional_group_context, ) - .map_err(LibraryError::unexpected_crypto_error)? + .map_err(LibraryError::unexpected_crypto_error)? } else { JoinerSecret::new( backend, @@ -51,7 +51,7 @@ impl CoreGroup { epoch_secrets.init_secret(), serialized_provisional_group_context, ) - .map_err(LibraryError::unexpected_crypto_error)? + .map_err(LibraryError::unexpected_crypto_error)? }; // Prepare the PskSecret @@ -61,7 +61,7 @@ impl CoreGroup { &self.resumption_psk_store, &apply_proposals_values.presharedkeys, ) - .await?; + .await?; PskSecret::new(backend, self.ciphersuite(), psks).await? }; @@ -201,7 +201,12 @@ impl CoreGroup { let update_path_leaf_node = Some(path.leaf_node().clone()); debug_assert_eq!(diff.leaf(sender_index), path.leaf_node().into()); - (commit_secret, new_keypairs, new_leaf_keypair_option, update_path_leaf_node) + ( + commit_secret, + new_keypairs, + new_leaf_keypair_option, + update_path_leaf_node, + ) } else { if apply_proposals_values.path_required { // ValSem201 @@ -285,7 +290,7 @@ impl CoreGroup { /// might throw a `LibraryError`. pub(crate) async fn merge_commit( &mut self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, staged_commit: StagedCommit, ) -> Result, MergeCommitError> { // Get all keypairs from the old epoch, so we can later store the ones @@ -309,7 +314,7 @@ impl CoreGroup { async fn merge_member_commit( &mut self, - backend: &impl OpenMlsCryptoProvider, + backend: &impl OpenMlsCryptoProvider, mut old_epoch_keypairs: EpochEncryptionKeyPair, mut state: Box, is_member: bool, @@ -352,7 +357,7 @@ impl CoreGroup { return Err(LibraryError::custom( "We should have all the private key material we need.", ) - .into()); + .into()); } // Store the relevant keys under the new epoch @@ -399,7 +404,7 @@ impl CoreGroup { leaf_node_keypairs, backend, ) - .await + .await } } @@ -428,27 +433,27 @@ impl StagedCommit { } /// Returns the Add proposals that are covered by the Commit message as in iterator over [QueuedAddProposal]. - pub fn add_proposals(&self) -> impl Iterator { + pub fn add_proposals(&self) -> impl Iterator { self.staged_proposal_queue.add_proposals() } /// Returns the Remove proposals that are covered by the Commit message as in iterator over [QueuedRemoveProposal]. - pub fn remove_proposals(&self) -> impl Iterator { + pub fn remove_proposals(&self) -> impl Iterator { self.staged_proposal_queue.remove_proposals() } /// Returns the Update proposals that are covered by the Commit message as in iterator over [QueuedUpdateProposal]. - pub fn update_proposals(&self) -> impl Iterator { + pub fn update_proposals(&self) -> impl Iterator { self.staged_proposal_queue.update_proposals() } /// Returns the PresharedKey proposals that are covered by the Commit message as in iterator over [QueuedPskProposal]. - pub fn psk_proposals(&self) -> impl Iterator { + pub fn psk_proposals(&self) -> impl Iterator { self.staged_proposal_queue.psk_proposals() } /// Returns an interator over all [`QueuedProposal`]s - pub fn queued_proposals(&self) -> impl Iterator { + pub fn queued_proposals(&self) -> impl Iterator { self.staged_proposal_queue.queued_proposals() } @@ -479,12 +484,12 @@ impl StagedCommit { } } - /// Returns the leaf node of the (optional) update path. pub fn get_update_path_leaf_node(&self) -> Option<&LeafNode> { match self.state { StagedCommitState::PublicState(_) => None, - StagedCommitState::GroupMember(ref member) | StagedCommitState::ExternalMember(ref member) => { + StagedCommitState::GroupMember(ref member) + | StagedCommitState::ExternalMember(ref member) => { member.update_path_leaf_node.as_ref() } } diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index de3515b50e..7a6f97407e 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -50,7 +50,8 @@ async fn proposal_queue_functions(ciphersuite: Ciphersuite, backend: &impl OpenM let kpi = KeyPackageIn::from(alice_update_key_package.clone()); assert!(kpi - .standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + .standalone_validate(backend, ProtocolVersion::Mls10) + .await .is_ok()); let group_context = GroupContext::new( @@ -196,7 +197,8 @@ async fn proposal_queue_order(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let kpi = KeyPackageIn::from(alice_update_key_package.clone()); assert!(kpi - .standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + .standalone_validate(backend, ProtocolVersion::Mls10) + .await .is_ok()); let group_context = GroupContext::new( diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 832c5b478a..990a4f5c3a 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -45,17 +45,13 @@ impl MlsGroup { self.is_operational()?; // Create inline add proposals from key packages - let inline_proposals = key_packages - .into_iter() - .map(|key_package| { - let key_package = key_package.validate( - backend.crypto(), - ProtocolVersion::Mls10, - self.group().public_group(), - )?; - Ok(Proposal::Add(AddProposal { key_package })) - }) - .collect::, AddMembersError>>()?; + let mut inline_proposals = Vec::with_capacity(key_packages.len()); + for key_package in key_packages.into_iter() { + let key_package = key_package + .validate(backend, ProtocolVersion::Mls10, self.group().public_group()) + .await?; + inline_proposals.push(Proposal::Add(AddProposal { key_package })); + } // Create Commit over all proposals // TODO #751 diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 1f66126eaa..da9a4a88bf 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -97,7 +97,7 @@ impl MlsGroup { /// Creates proposals to add an external PSK to the key schedule. /// /// Returns an error if there is a pending commit. - pub fn propose_add_member_by_value( + pub async fn propose_add_member_by_value( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -105,11 +105,9 @@ impl MlsGroup { ) -> Result<(MlsMessageOut, ProposalRef), ProposalError> { self.is_operational()?; - let key_package = joiner_key_package.validate( - backend.crypto(), - ProtocolVersion::Mls10, - self.group().public_group(), - )?; + let key_package = joiner_key_package + .validate(backend, ProtocolVersion::Mls10, self.group().public_group()) + .await?; let proposal = self.group .create_add_proposal(self.framing_parameters(), key_package, signer)?; @@ -161,12 +159,12 @@ impl MlsGroup { ) -> Result<(MlsMessageOut, ProposalRef), ProposalError> { match propose { Propose::Add(key_package) => match ref_or_value { - ProposalOrRefType::Proposal => { - self.propose_add_member_by_value(backend, signer, key_package.into()) - } - ProposalOrRefType::Reference => self + ProposalOrRefType::Proposal => Ok(self + .propose_add_member_by_value(backend, signer, key_package.into()) + .await?), + ProposalOrRefType::Reference => Ok(self .propose_add_member(backend, signer, key_package.into()) - .map_err(|e| e.into()), + .await?), }, Propose::Update(leaf_node) => match ref_or_value { @@ -240,7 +238,7 @@ impl MlsGroup { /// Creates proposals to add members to the group. /// /// Returns an error if there is a pending commit. - pub fn propose_add_member( + pub async fn propose_add_member( &mut self, backend: &impl OpenMlsCryptoProvider, signer: &impl Signer, @@ -248,11 +246,9 @@ impl MlsGroup { ) -> Result<(MlsMessageOut, ProposalRef), ProposeAddMemberError> { self.is_operational()?; - let key_package = joiner_key_package.validate( - backend.crypto(), - ProtocolVersion::Mls10, - self.group().public_group(), - )?; + let key_package = joiner_key_package + .validate(backend, ProtocolVersion::Mls10, self.group().public_group()) + .await?; let add_proposal = self.group .create_add_proposal(self.framing_parameters(), key_package, signer)?; diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 4d343f5de2..b531b50a3e 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -404,6 +404,7 @@ async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl Open // Let's add bob let (proposal, _) = alice_group .propose_add_member(backend, &alice_signer, bob_key_package.clone().into()) + .await .expect("error creating self-update proposal"); let alice_processed_message = alice_group @@ -445,6 +446,7 @@ async fn test_pending_commit_logic(ciphersuite: Ciphersuite, backend: &impl Open )); let error = alice_group .propose_add_member(backend, &alice_signer, bob_key_package.clone().into()) + .await .expect_err("no error creating a proposal while a commit is pending"); assert!(matches!( error, @@ -676,6 +678,7 @@ async fn remove_prosposal_by_ref(ciphersuite: Ciphersuite, backend: &impl OpenMl // alice proposes to add charlie let (_, reference) = alice_group .propose_add_member(backend, &alice_signer, charlie_key_package.clone().into()) + .await .unwrap(); assert_eq!(alice_group.proposal_store.proposals().count(), 1); diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index ff55f1b639..2f08159426 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -188,7 +188,9 @@ impl MlsGroup { ) .into()); }; - let own_leaf = own_leaf.validate(self.group().public_group(), backend.crypto())?; + let own_leaf = own_leaf + .validate(self.group().public_group(), backend) + .await?; let update_proposal = self.group.create_update_proposal( self.framing_parameters(), @@ -269,7 +271,9 @@ impl MlsGroup { ) .into()); }; - let own_leaf = own_leaf.validate(self.group().public_group(), backend.crypto())?; + let own_leaf = own_leaf + .validate(self.group().public_group(), backend) + .await?; let update_proposal = self.group.create_update_proposal( self.framing_parameters(), diff --git a/openmls/src/group/public_group/process.rs b/openmls/src/group/public_group/process.rs index 518f28820b..70be92f0e1 100644 --- a/openmls/src/group/public_group/process.rs +++ b/openmls/src/group/public_group/process.rs @@ -131,7 +131,7 @@ impl PublicGroup { /// - ValSem244 /// - ValSem245 /// - ValSem246 (as part of ValSem010) - pub fn process_message( + pub async fn process_message( &self, backend: &impl OpenMlsCryptoProvider, message: impl Into, @@ -162,6 +162,7 @@ impl PublicGroup { .parse_message(decrypted_message, None) .map_err(ProcessMessageError::from)?; self.process_unverified_message(backend, unverified_message, &self.proposal_store, self) + .await } } @@ -193,7 +194,7 @@ impl PublicGroup { /// - ValSem243 /// - ValSem244 /// - ValSem246 (as part of ValSem010) - pub(crate) fn process_unverified_message( + pub(crate) async fn process_unverified_message( &self, backend: &impl OpenMlsCryptoProvider, unverified_message: UnverifiedMessage, @@ -203,12 +204,9 @@ impl PublicGroup { // Checks the following semantic validation: // - ValSem010 // - ValSem246 (as part of ValSem010) - let (content, credential) = unverified_message.verify( - self.ciphersuite(), - backend.crypto(), - self.version(), - group, - )?; + let (content, credential) = unverified_message + .verify(self.ciphersuite(), backend, self.version(), group) + .await?; match content.sender() { Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => { diff --git a/openmls/src/group/public_group/tests.rs b/openmls/src/group/public_group/tests.rs index f12d479fe1..cc3e451bb7 100644 --- a/openmls/src/group/public_group/tests.rs +++ b/openmls/src/group/public_group/tests.rs @@ -84,6 +84,7 @@ async fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv }; let processed_message = public_group .process_message(backend, public_message) + .await .unwrap(); // Further inspection of the message can take place here ... @@ -145,6 +146,7 @@ async fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv // The public group processes let ppm = public_group .process_message(backend, into_public_message(queued_messages)) + .await .unwrap(); public_group.merge_commit(extract_staged_commit(ppm)); @@ -183,6 +185,7 @@ async fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv // The public group processes let ppm = public_group .process_message(backend, into_public_message(queued_messages)) + .await .unwrap(); // We have to add the proposal to the public group's proposal store. match ppm.into_content() { @@ -229,6 +232,7 @@ async fn public_group(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoProv // The public group processes let ppm = public_group .process_message(backend, into_public_message(queued_messages.clone())) + .await .unwrap(); public_group.merge_commit(extract_staged_commit(ppm)); diff --git a/openmls/src/group/tests/test_gce_proposals.rs b/openmls/src/group/tests/test_gce_proposals.rs index c738086f4b..60685767b1 100644 --- a/openmls/src/group/tests/test_gce_proposals.rs +++ b/openmls/src/group/tests/test_gce_proposals.rs @@ -370,6 +370,7 @@ async fn gce_proposal_must_be_applied_first_then_used_to_validate_other_add_prop &alice_signer, charlie_key_package_bundle.key_package().clone().into(), ) + .await .unwrap(); let processed_message = bob_group diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index b2cff815d4..5fbaa3fa3a 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -1084,7 +1084,8 @@ async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr .await; let kpi: KeyPackageIn = charlie_key_package.clone().into(); - kpi.standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + kpi.standalone_validate(backend, ProtocolVersion::Mls10) + .await .unwrap(); // Let's just pick a ciphersuite that's not the one we're testing right now. @@ -1165,11 +1166,13 @@ async fn test_valsem105(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr for proposal_inclusion in [ProposalInclusion::ByReference, ProposalInclusion::ByValue] { match proposal_inclusion { ProposalInclusion::ByReference => { - let proposal_result = alice_group.propose_add_member( - backend, - &alice_credential_with_key_and_signer.signer, - test_kp.clone().into(), - ); + let proposal_result = alice_group + .propose_add_member( + backend, + &alice_credential_with_key_and_signer.signer, + test_kp.clone().into(), + ) + .await; match key_package_version { KeyPackageTestVersion::WrongCiphersuite => { diff --git a/openmls/src/key_packages/key_package_in.rs b/openmls/src/key_packages/key_package_in.rs index 2bc20f3526..afb92282e3 100644 --- a/openmls/src/key_packages/key_package_in.rs +++ b/openmls/src/key_packages/key_package_in.rs @@ -12,7 +12,7 @@ use crate::{ }, versions::ProtocolVersion, }; -use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite}; +use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; use serde::{Deserialize, Serialize}; use tls_codec::{Serialize as TlsSerializeTrait, TlsDeserialize, TlsSerialize, TlsSize}; @@ -74,7 +74,7 @@ mod private_mod { /// } KeyPackageTBS; /// ``` #[derive( -Debug, Clone, PartialEq, TlsSize, TlsSerialize, TlsDeserialize, Serialize, Deserialize, + Debug, Clone, PartialEq, TlsSize, TlsSerialize, TlsDeserialize, Serialize, Deserialize, )] struct KeyPackageTbsIn { protocol_version: ProtocolVersion, @@ -86,7 +86,7 @@ struct KeyPackageTbsIn { /// The key package struct. #[derive( -Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, + Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, )] pub struct KeyPackageIn { payload: KeyPackageTbsIn, @@ -114,27 +114,27 @@ impl KeyPackageIn { /// /// Returns a [`KeyPackage`] after having verified the signature or a /// [`KeyPackageVerifyError`] otherwise. - pub fn validate( + pub async fn validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result { - self._validate(crypto, protocol_version, Some(group)) + self._validate(backend, protocol_version, Some(group)).await } /// Verify that this key package is valid disregarding the group it is supposed to be used with. - pub fn standalone_validate( + pub async fn standalone_validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, protocol_version: ProtocolVersion, ) -> Result { - self._validate(crypto, protocol_version, None) + self._validate(backend, protocol_version, None).await } - fn _validate( + async fn _validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, protocol_version: ProtocolVersion, group: Option<&PublicGroup>, ) -> Result { @@ -154,9 +154,11 @@ impl KeyPackageIn { let leaf_node = match verifiable_leaf_node { VerifiableLeafNode::KeyPackage(leaf_node) => { if let Some(group) = group { - leaf_node.validate(group, crypto)? + leaf_node.validate(group, backend).await? } else { - leaf_node.standalone_validate(crypto, signature_scheme)? + leaf_node + .standalone_validate(backend, signature_scheme) + .await? } } _ => return Err(KeyPackageVerifyError::InvalidLeafNodeSourceType), @@ -174,7 +176,7 @@ impl KeyPackageIn { // Verify the KeyPackage signature let key_package = VerifiableKeyPackage::new(self.payload.into(), self.signature) - .verify::(crypto, signature_key) + .verify::(backend.crypto(), signature_key) .map_err(|_| KeyPackageVerifyError::InvalidSignature)?; // Extension included in the extensions or leaf_node.extensions fields diff --git a/openmls/src/key_packages/test_key_packages.rs b/openmls/src/key_packages/test_key_packages.rs index 4e733fc6d2..ec0b68d712 100644 --- a/openmls/src/key_packages/test_key_packages.rs +++ b/openmls/src/key_packages/test_key_packages.rs @@ -47,7 +47,8 @@ async fn generate_key_package(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr let kpi = KeyPackageIn::from(key_package); assert!(kpi - .standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + .standalone_validate(backend, ProtocolVersion::Mls10) + .await .is_ok()); } @@ -100,7 +101,8 @@ async fn application_id_extension(ciphersuite: Ciphersuite, backend: &impl OpenM let kpi = KeyPackageIn::from(key_package.clone()); assert!(kpi - .standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + .standalone_validate(backend, ProtocolVersion::Mls10) + .await .is_ok()); // Check ID @@ -136,7 +138,8 @@ async fn key_package_validation(ciphersuite: Ciphersuite, backend: &impl OpenMls let kpi = KeyPackageIn::tls_deserialize(&mut encoded.as_slice()).unwrap(); let err = kpi - .standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + .standalone_validate(backend, ProtocolVersion::Mls10) + .await .unwrap_err(); // Expect an invalid protocol version error assert_eq!(err, KeyPackageVerifyError::InvalidProtocolVersion); @@ -155,7 +158,8 @@ async fn key_package_validation(ciphersuite: Ciphersuite, backend: &impl OpenMls let kpi = KeyPackageIn::tls_deserialize(&mut encoded.as_slice()).unwrap(); let err = kpi - .standalone_validate(backend.crypto(), ProtocolVersion::Mls10) + .standalone_validate(backend, ProtocolVersion::Mls10) + .await .unwrap_err(); // Expect an invalid init/encryption key error assert_eq!(err, KeyPackageVerifyError::InitKeyEqualsEncryptionKey); diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index 82cf125dbf..81190cc8e2 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -189,19 +189,22 @@ impl CommitIn { } /// Returns a [`Commit`] after successful validation. - pub fn validate( + pub async fn validate( self, ciphersuite: Ciphersuite, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, sender_context: SenderContext, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result { - let proposals = self - .proposals - .into_iter() - .map(|p| p.validate(crypto, ciphersuite, protocol_version, group)) - .collect::, _>>()?; + let mut proposals = Vec::with_capacity(self.proposals.len()); + for proposal in self.proposals.into_iter() { + proposals.push( + proposal + .validate(backend, ciphersuite, protocol_version, group) + .await?, + ); + } let path = if let Some(path) = self.path { let tree_position = match sender_context { @@ -234,7 +237,7 @@ impl CommitIn { TreePosition::new(group_id, new_leaf_index) } }; - Some(path.into_verified(crypto, tree_position, group)?) + Some(path.into_verified(backend, tree_position, group).await?) } else { None }; diff --git a/openmls/src/messages/proposals_in.rs b/openmls/src/messages/proposals_in.rs index 4f1d897379..821703f763 100644 --- a/openmls/src/messages/proposals_in.rs +++ b/openmls/src/messages/proposals_in.rs @@ -17,7 +17,7 @@ use crate::{ use crate::prelude::PublicGroup; use crate::treesync::node::validate::ValidatableLeafNode; -use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite}; +use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider}; use serde::{Deserialize, Serialize}; use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize}; @@ -95,22 +95,23 @@ impl ProposalIn { } /// Returns a [`Proposal`] after successful validation. - pub(crate) fn validate( + pub(crate) async fn validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, sender_context: Option, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result { Ok(match self { - ProposalIn::Add(add) => { - Proposal::Add(add.validate(crypto, protocol_version, ciphersuite, group)?) - } + ProposalIn::Add(add) => Proposal::Add( + add.validate(backend, protocol_version, ciphersuite, group) + .await?, + ), ProposalIn::Update(update) => { let sender_context = sender_context.ok_or(ValidationError::CommitterIncludedOwnUpdate)?; - Proposal::Update(update.validate(crypto, sender_context, group)?) + Proposal::Update(update.validate(backend, sender_context, group).await?) } ProposalIn::Remove(remove) => Proposal::Remove(remove), ProposalIn::PreSharedKey(psk) => Proposal::PreSharedKey(psk), @@ -147,14 +148,17 @@ impl AddProposalIn { } /// Returns a [`AddProposal`] after successful validation. - pub(crate) fn validate( + pub(crate) async fn validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, protocol_version: ProtocolVersion, ciphersuite: Ciphersuite, group: &PublicGroup, ) -> Result { - let key_package = self.key_package.validate(crypto, protocol_version, group)?; + let key_package = self + .key_package + .validate(backend, protocol_version, group) + .await?; // Verify that the ciphersuite is valid if key_package.ciphersuite() != ciphersuite { return Err(ValidationError::InvalidAddProposalCiphersuite); @@ -183,9 +187,9 @@ pub struct UpdateProposalIn { impl UpdateProposalIn { /// Returns a [`UpdateProposal`] after successful validation. - pub(crate) fn validate( + pub(crate) async fn validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, sender_context: SenderContext, group: &PublicGroup, ) -> Result { @@ -199,7 +203,7 @@ impl UpdateProposalIn { .leaf_node .try_into_verifiable_leaf_node(Some(tree_position))?; let leaf_node = match verifiable_leaf_node { - VerifiableLeafNode::Update(leaf_node) => leaf_node.validate(group, crypto)?, + VerifiableLeafNode::Update(leaf_node) => leaf_node.validate(group, backend).await?, _ => return Err(ValidationError::InvalidLeafNodeSourceType), }; @@ -224,16 +228,18 @@ pub(crate) enum ProposalOrRefIn { impl ProposalOrRefIn { /// Returns a [`ProposalOrRef`] after successful validation. - pub(crate) fn validate( + pub(crate) async fn validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, protocol_version: ProtocolVersion, group: &PublicGroup, ) -> Result { Ok(match self { ProposalOrRefIn::Proposal(proposal_in) => ProposalOrRef::Proposal( - proposal_in.validate(crypto, ciphersuite, None, protocol_version, group)?, + proposal_in + .validate(backend, ciphersuite, None, protocol_version, group) + .await?, ), ProposalOrRefIn::Reference(reference) => ProposalOrRef::Reference(reference), }) diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index a956bdda2d..18082cc46f 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -281,6 +281,7 @@ impl Client { for key_package in key_packages { let message = group .propose_add_member(&self.crypto, &signer, key_package.clone()) + .await .map(|(out, _)| out)?; messages.push(message); } diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index b5980f5c6b..0fb65fe623 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -419,10 +419,11 @@ pub async fn run_test_vector( let processed_message: AuthenticatedContent = processed_unverified_message .verify( ciphersuite, - backend.crypto(), + backend, ProtocolVersion::Mls10, group.public_group(), ) + .await .unwrap() .0; match processed_message.content().to_owned() { @@ -538,7 +539,7 @@ pub async fn run_test_vector( let commit_priv = MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.commit_priv)).unwrap(); - fn test_commit_pub( + async fn test_commit_pub( mut group: CoreGroup, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -564,10 +565,11 @@ pub async fn run_test_vector( let processed_message: AuthenticatedContent = processed_unverified_message .verify( ciphersuite, - backend.crypto(), + backend, ProtocolVersion::Mls10, group.public_group(), ) + .await .unwrap() .0; match processed_message.content().to_owned() { @@ -584,9 +586,10 @@ pub async fn run_test_vector( ciphersuite, commit.clone(), commit_pub, - ); + ) + .await; - fn test_commit_priv( + async fn test_commit_priv( mut group: CoreGroup, backend: &impl OpenMlsCryptoProvider, ciphersuite: Ciphersuite, @@ -612,10 +615,11 @@ pub async fn run_test_vector( let processed_message: AuthenticatedContent = processed_unverified_message .verify( ciphersuite, - backend.crypto(), + backend, ProtocolVersion::Mls10, group.public_group(), ) + .await .unwrap() .0; match processed_message.content().to_owned() { @@ -632,7 +636,8 @@ pub async fn run_test_vector( ciphersuite, commit.clone(), commit_priv, - ); + ) + .await; // Wrap `commit` into a `PrivateMessage`. let group = setup_group(backend, ciphersuite, &test, false).await; @@ -660,7 +665,8 @@ pub async fn run_test_vector( ciphersuite, commit.clone(), my_commit_priv_out.into(), - ); + ) + .await; // Wrap `commit` into a `PublicMessage`. let group = setup_group(backend, ciphersuite, &test, false).await; @@ -692,7 +698,8 @@ pub async fn run_test_vector( ciphersuite, commit, my_commit_pub_out.into(), - ); + ) + .await; } // Application diff --git a/openmls/src/treesync/errors.rs b/openmls/src/treesync/errors.rs index d225230bad..8884849726 100644 --- a/openmls/src/treesync/errors.rs +++ b/openmls/src/treesync/errors.rs @@ -7,7 +7,7 @@ use thiserror::Error; use super::*; use crate::{ binary_tree::MlsBinaryTreeDiffError, ciphersuite::signable::SignatureError, - error::LibraryError, extensions::errors::ExtensionError, + credentials::errors::CredentialError, error::LibraryError, extensions::errors::ExtensionError, }; // === Public errors === @@ -262,6 +262,8 @@ pub enum LeafNodeValidationError { /// The credential used by a member is not supported by this leaf node. #[error("The credential used by a member is not supported by this leaf node.")] MemberCredentialNotSupportedByLeafNode, + #[error(transparent)] + InvalidCredential(#[from] CredentialError), /// A library error occurred. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/treesync/node/validate.rs b/openmls/src/treesync/node/validate.rs index d0781c2766..ac8bcef1cd 100644 --- a/openmls/src/treesync/node/validate.rs +++ b/openmls/src/treesync/node/validate.rs @@ -1,4 +1,5 @@ use crate::{ + credentials::Credential, prelude::{ Capabilities, CredentialType, ExtensionType, Extensions, SignaturePublicKey, Verifiable, }, @@ -13,7 +14,7 @@ use crate::{ }, }; use itertools::Itertools; -use openmls_traits::{crypto::OpenMlsCrypto, types::SignatureScheme}; +use openmls_traits::{crypto::OpenMlsCrypto, types::SignatureScheme, OpenMlsCryptoProvider}; impl ValidatableLeafNode for VerifiableCommitLeafNode { fn signature_key(&self) -> &SignaturePublicKey { @@ -28,19 +29,25 @@ impl ValidatableLeafNode for VerifiableCommitLeafNode { &self.payload.credential.credential_type } + fn credential(&self) -> &Credential { + &self.payload.credential + } + fn extensions(&self) -> &Extensions { &self.payload.extensions } } +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl ValidatableLeafNode for VerifiableUpdateLeafNode { - fn validate( + async fn validate( self, group: &PublicGroup, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, ) -> Result { self.validate_replaced_encryption_key(group)?; - self.generic_validate(crypto, group) + self.generic_validate(backend, group).await } fn signature_key(&self) -> &SignaturePublicKey { @@ -55,6 +62,10 @@ impl ValidatableLeafNode for VerifiableUpdateLeafNode { &self.payload.credential.credential_type } + fn credential(&self) -> &Credential { + &self.payload.credential + } + fn extensions(&self) -> &Extensions { &self.payload.extensions } @@ -77,14 +88,16 @@ impl VerifiableUpdateLeafNode { } } +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl ValidatableLeafNode for VerifiableKeyPackageLeafNode { - fn validate( + async fn validate( self, group: &PublicGroup, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, ) -> Result { self.validate_lifetime()?; - self.generic_validate(crypto, group) + self.generic_validate(backend, group).await } fn signature_key(&self) -> &SignaturePublicKey { @@ -99,6 +112,10 @@ impl ValidatableLeafNode for VerifiableKeyPackageLeafNode { &self.payload.credential.credential_type } + fn credential(&self) -> &Credential { + &self.payload.credential + } + fn extensions(&self) -> &Extensions { &self.payload.extensions } @@ -116,34 +133,38 @@ impl VerifiableKeyPackageLeafNode { } } -pub(crate) trait ValidatableLeafNode: Verifiable + Sized +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] +pub(crate) trait ValidatableLeafNode: Verifiable + Send + Sync + Sized where LeafNode: VerifiedStruct, { - fn standalone_validate( + async fn standalone_validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, signature_scheme: SignatureScheme, ) -> Result { let extension_types = self.extension_types(); - let leaf_node = self.verify_signature(crypto, signature_scheme)?; + self.validate_credential(backend).await?; + let leaf_node = self.verify_signature(backend.crypto(), signature_scheme)?; Self::validate_extension_support(&leaf_node, &extension_types[..])?; + Ok(leaf_node) } /// Validate a LeafNode as per https://www.rfc-editor.org/rfc/rfc9420.html#name-leaf-node-validation - fn validate( + async fn validate( self, group: &PublicGroup, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, ) -> Result { - self.generic_validate(crypto, group) + self.generic_validate(backend, group).await } /// Validation regardless of [LeafNode]'s source - fn generic_validate( + async fn generic_validate( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, group: &PublicGroup, ) -> Result { self.validate_capabilities(group)?; @@ -151,17 +172,20 @@ where let tree = group.treesync(); self.validate_signature_key_unique(tree)?; self.validate_encryption_key_unique(tree)?; + self.validate_credential(backend).await?; let extension_types = self.extension_types(); let signature_scheme = group.ciphersuite().signature_algorithm(); - let leaf_node = self.verify_signature(crypto, signature_scheme)?; + let leaf_node = self.verify_signature(backend.crypto(), signature_scheme)?; Self::validate_extension_support(&leaf_node, &extension_types[..])?; + Ok(leaf_node) } fn signature_key(&self) -> &SignaturePublicKey; fn capabilities(&self) -> &Capabilities; fn credential_type(&self) -> &CredentialType; + fn credential(&self) -> &Credential; fn extensions(&self) -> &Extensions; fn extension_types(&self) -> Vec { @@ -201,6 +225,13 @@ where Ok(()) } + async fn validate_credential( + &self, + backend: &impl OpenMlsCryptoProvider, + ) -> Result<(), LeafNodeValidationError> { + Ok(self.credential().validate(backend).await?) + } + /// Verify that the following fields are unique among the members of the group: signature_key fn validate_signature_key_unique( &self, diff --git a/openmls/src/treesync/treekem.rs b/openmls/src/treesync/treekem.rs index 0904ae253a..9a82289aaf 100644 --- a/openmls/src/treesync/treekem.rs +++ b/openmls/src/treesync/treekem.rs @@ -7,7 +7,6 @@ use std::collections::HashSet; use openmls_traits::{ - crypto::OpenMlsCrypto, types::{Ciphersuite, HpkeCiphertext}, OpenMlsCryptoProvider, }; @@ -379,9 +378,9 @@ impl UpdatePathIn { } /// Return a verified [`UpdatePath`]. - pub(crate) fn into_verified( + pub(crate) async fn into_verified( self, - crypto: &impl OpenMlsCrypto, + backend: &impl OpenMlsCryptoProvider, tree_position: TreePosition, group: &PublicGroup, ) -> Result { @@ -390,7 +389,7 @@ impl UpdatePathIn { leaf_node_in.try_into_verifiable_leaf_node(Some(tree_position))?; match verifiable_leaf_node { VerifiableLeafNode::Commit(commit_leaf_node) => { - let leaf_node = commit_leaf_node.validate(group, crypto)?; + let leaf_node = commit_leaf_node.validate(group, backend).await?; Ok(UpdatePath { leaf_node, nodes: self.nodes, diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index a0cb7d05be..14bc101595 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -846,6 +846,7 @@ async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP &alice_signature_keys, bob_key_package.clone().into(), ) + .await .expect("Could not create proposal to add Bob"); alice_group .remove_pending_proposal(backend.key_store(), &proposal_ref) @@ -861,6 +862,7 @@ async fn book_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoP &alice_signature_keys, bob_key_package.clone().into(), ) + .await .expect("Could not create proposal to add Bob"); // ANCHOR_END: propose_add diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index 06768f0aee..c8f49cf89b 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -648,6 +648,7 @@ async fn mls_group_operations(ciphersuite: Ciphersuite, backend: &impl OpenMlsCr // Create AddProposal and process it let (queued_message, _) = alice_group .propose_add_member(backend, &alice_signer, bob_key_package.into()) + .await .expect("Could not create proposal to add Bob"); let charlie_processed_message = charlie_group diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index cb0d269e8c..943d14b4cf 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/openmls_rust_crypto" readme = "README.md" [dependencies] +async-trait = { workspace = true } openmls_traits = { version = "0.2.0", path = "../traits" } openmls_memory_keystore = { version = "0.2.0", path = "../memory_keystore" } # Rust Crypto dependencies diff --git a/openmls_rust_crypto/src/lib.rs b/openmls_rust_crypto/src/lib.rs index 85b57b7e59..c3cc7fac80 100644 --- a/openmls_rust_crypto/src/lib.rs +++ b/openmls_rust_crypto/src/lib.rs @@ -9,6 +9,22 @@ use openmls_traits::OpenMlsCryptoProvider; mod provider; pub use provider::*; +#[derive(Debug)] +pub struct DummyAuthenticationService; + +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] +impl openmls_traits::authentication_service::AuthenticationServiceDelegate + for DummyAuthenticationService +{ + async fn validate_credential<'a>( + &'a self, + _credential: openmls_traits::authentication_service::CredentialRef<'a>, + ) -> openmls_traits::authentication_service::CredentialAuthenticationStatus { + openmls_traits::authentication_service::CredentialAuthenticationStatus::Valid + } +} + #[derive(Default, Debug)] pub struct OpenMlsRustCrypto { crypto: RustCrypto, @@ -19,6 +35,7 @@ impl OpenMlsCryptoProvider for OpenMlsRustCrypto { type CryptoProvider = RustCrypto; type RandProvider = RustCrypto; type KeyStoreProvider = MemoryKeyStore; + type AuthenticationServiceProvider = DummyAuthenticationService; fn crypto(&self) -> &Self::CryptoProvider { &self.crypto @@ -31,4 +48,8 @@ impl OpenMlsCryptoProvider for OpenMlsRustCrypto { fn key_store(&self) -> &Self::KeyStoreProvider { &self.key_store } + + fn authentication_service(&self) -> &Self::AuthenticationServiceProvider { + &DummyAuthenticationService + } } diff --git a/rustfmt.toml b/rustfmt.toml index bfe1bd58d6..3a26366d4d 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1 @@ -wrap_comments = true -edition = "2018" -format_code_in_doc_comments = true +edition = "2021" diff --git a/traits/src/authentication_service.rs b/traits/src/authentication_service.rs new file mode 100644 index 0000000000..a4773998d4 --- /dev/null +++ b/traits/src/authentication_service.rs @@ -0,0 +1,40 @@ +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum CredentialRef<'a> { + Basic { identity: &'a [u8] }, + X509 { certificates: &'a [&'a [u8]] }, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] +pub enum CredentialAuthenticationStatus { + #[default] + Unknown, + Valid, + Invalid, + Expired, + Revoked, +} + +impl std::fmt::Display for CredentialAuthenticationStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Valid => "valid", + Self::Invalid => "invalid", + Self::Expired => "expired", + Self::Revoked => "revoked", + Self::Unknown => "unknown", + } + ) + } +} + +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] +pub trait AuthenticationServiceDelegate: std::fmt::Debug + Send + Sync { + async fn validate_credential<'a>( + &'a self, + credential: CredentialRef<'a>, + ) -> CredentialAuthenticationStatus; +} diff --git a/traits/src/traits.rs b/traits/src/traits.rs index 021770d073..a5cb6d512a 100644 --- a/traits/src/traits.rs +++ b/traits/src/traits.rs @@ -3,6 +3,7 @@ //! This module defines a number of traits that are used by the public //! API of OpenMLS. +pub mod authentication_service; pub mod crypto; pub mod key_store; pub mod random; @@ -17,6 +18,7 @@ pub trait OpenMlsCryptoProvider: Send + Sync { type CryptoProvider: crypto::OpenMlsCrypto; type RandProvider: random::OpenMlsRand; type KeyStoreProvider: key_store::OpenMlsKeyStore; + type AuthenticationServiceProvider: authentication_service::AuthenticationServiceDelegate; /// Get the crypto provider. fn crypto(&self) -> &Self::CryptoProvider; @@ -26,4 +28,7 @@ pub trait OpenMlsCryptoProvider: Send + Sync { /// Get the key store provider. fn key_store(&self) -> &Self::KeyStoreProvider; + + /// Get the authentication service + fn authentication_service(&self) -> &Self::AuthenticationServiceProvider; }