Skip to content

Commit 73733dc

Browse files
feat: add TransactionContext.check_credentials()
1 parent 4c47ea4 commit 73733dc

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use core_crypto_keystore::{entities::E2eiCrl, traits::FetchFromDatabase};
2+
use wire_e2e_identity::x509_check::extract_crl_uris;
3+
use x509_cert::Certificate;
4+
5+
use super::{Error, Result};
6+
use 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+
15+
impl TransactionContext {
16+
/// This function must be called at least once every 24 hours. It is recommended to do this during an idle period,
17+
/// because in case x509 credentials are used, HTTP requests are done to fetch new certificate revocation lists.
18+
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.into())
44+
.await
45+
.map_err(RecursiveError::e2e_identity("fetching crls"))?;
46+
47+
// store fresh CRLs
48+
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+
// check our own credentials for expiration or revocation
55+
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+
/// To get CRL URLs, we want to consider all sources of relevant certificates:
69+
/// - the stored credentials
70+
/// - the trust anchor
71+
/// - mls groups
72+
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 uri 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+
}

crypto/src/transaction_context/credential/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod check;
2+
13
use std::sync::Arc;
24

35
use super::{Error, Result};

0 commit comments

Comments
 (0)