core_crypto/transaction_context/credential/
check.rs1use core_crypto_keystore::{entities::E2eiCrl, traits::FetchFromDatabase};
2use wire_e2e_identity::x509_check::extract_crl_uris;
3use x509_cert::Certificate;
4
5use super::{Error, Result};
6use crate::{
7 Credential, CredentialRef, CredentialType, KeystoreError, MlsConversation, RecursiveError,
8 mls::credential::{
9 crl::{CrlUris, extract_crl_uris_from_credentials, extract_crl_uris_from_group},
10 ext::CredentialExt as _,
11 },
12 transaction_context::TransactionContext,
13};
14
15impl TransactionContext {
16 pub async fn check_credentials(&self) -> Result<()> {
19 let database = self.database().await?;
20 let pki_env = self.pki_environment().await?;
21
22 let credentials = Credential::get_all(&database)
23 .await
24 .map_err(RecursiveError::mls_credential("getting all credentials"))?;
25 let trust_anchor = pki_env
26 .trust_anchor()
27 .await
28 .map_err(RecursiveError::e2e_identity("reading trust anchor cert"))?;
29 let conversations_with_id =
30 MlsConversation::load_all(&database)
31 .await
32 .map_err(RecursiveError::mls_conversation(
33 "loading all conversations to check if the credential to be removed is present",
34 ))?;
35 let conversations = conversations_with_id
36 .iter()
37 .map(|conversation_with_id| conversation_with_id.1);
38 let relevant_crl_uris = Self::get_crl_uris(trust_anchor, credentials.iter(), conversations).await?;
39
40 self.clean_up_irrelevant_crls(&relevant_crl_uris).await?;
41
42 let crls = pki_env
43 .fetch_crls(relevant_crl_uris.iter().map(AsRef::as_ref))
44 .await
45 .map_err(RecursiveError::e2e_identity("fetching crls"))?;
46
47 for (crl_uri, crl) in crls {
49 self.e2ei_register_crl(crl_uri, crl).await?;
50 }
51
52 let mut invalid_credential_refs = Vec::new();
53
54 for credential in credentials {
56 if self.check_credential(&credential).await.is_err() {
57 invalid_credential_refs.push(CredentialRef::from_credential(&credential));
58 }
59 }
60
61 if !invalid_credential_refs.is_empty() {
62 return Err(Error::InvalidCredentials(invalid_credential_refs));
63 }
64
65 Ok(())
66 }
67
68 async fn get_crl_uris(
73 trust_anchor: Certificate,
74 credentials: impl Iterator<Item = &Credential>,
75 conversations: impl Iterator<Item = &MlsConversation>,
76 ) -> Result<CrlUris> {
77 let mls_credentials = credentials
78 .filter(|credential| credential.credential_type == CredentialType::X509)
79 .map(|credential| credential.mls_credential().mls_credential());
80
81 let mut crl_uris = extract_crl_uris_from_credentials(mls_credentials).map_err(
82 RecursiveError::mls_credential("extracting CRL URLs from stored credentials"),
83 )?;
84
85 crl_uris.extend(
86 extract_crl_uris(&trust_anchor)
87 .map_err(RecursiveError::e2e_identity("extracting CRL URL from trust anchor"))?
88 .unwrap_or_default(),
89 );
90
91 for conversation in conversations {
92 let uris_from_group = extract_crl_uris_from_group(conversation.group())
93 .map_err(RecursiveError::mls_credential("extracting CRL URLs from MLS groups"))?;
94 crl_uris.extend(uris_from_group);
95 }
96
97 Ok(crl_uris)
98 }
99
100 async fn check_credential(&self, credential: &Credential) -> Result<()> {
101 let pki_env = self.pki_environment().await?;
102 let provider = pki_env.mls_pki_env_provider();
103 let auth_service_arc = provider.borrow().await;
104 let Some(pki_env) = auth_service_arc.as_ref() else {
105 return Err(crate::transaction_context::e2e_identity::Error::PkiEnvironmentUnset.into());
106 };
107 let Some(cert) = credential
108 .mls_credential()
109 .parse_leaf_cert()
110 .map_err(RecursiveError::mls_credential("parsing leaf certificate"))?
111 else {
112 return Err(Error::InvalidCredential);
113 };
114 pki_env
115 .validate_cert_and_revocation(&cert)
116 .map_err(RecursiveError::e2e_identity("validating credential certificate"))?;
117 Ok(())
118 }
119
120 async fn clean_up_irrelevant_crls(&self, relevant_crl_uris: &CrlUris) -> Result<()> {
121 let database = self.database().await?;
122 for db_crl in database
123 .load_all::<E2eiCrl>()
124 .await
125 .map_err(KeystoreError::wrap("getting all database CRLs"))?
126 {
127 if !relevant_crl_uris.contains(&db_crl.distribution_point) {
128 database
129 .remove::<E2eiCrl>(&db_crl.distribution_point)
130 .await
131 .map_err(KeystoreError::wrap("removing irrelevant CRL"))?;
132 }
133 }
134 Ok(())
135 }
136}