wire_e2e_identity/
pki_env.rs1use std::{collections::HashSet, sync::Arc};
4
5use async_lock::{RwLock, RwLockReadGuard};
6use core_crypto_keystore::{
7 connection::Database,
8 entities::{E2eiAcmeCA, E2eiCrl, E2eiIntermediateCert},
9 traits::FetchFromDatabase,
10};
11use openmls_traits::authentication_service::{CredentialAuthenticationStatus, CredentialRef};
12use x509_cert::der::Decode as _;
13
14use crate::{
15 error::E2eIdentityError,
16 pki_env_hooks::PkiEnvironmentHooks,
17 x509_check::{
18 RustyX509CheckError,
19 revocation::{PkiEnvironment as RjtPkiEnvironment, PkiEnvironmentParams},
20 },
21};
22
23pub type Result<T> = core::result::Result<T, Error>;
24
25#[derive(Debug, thiserror::Error)]
26pub enum Error {
27 #[error(transparent)]
28 IdentityError(#[from] E2eIdentityError),
29 #[error(transparent)]
30 X509Error(#[from] RustyX509CheckError),
31 #[error(transparent)]
32 UrlError(#[from] url::ParseError),
33 #[error(transparent)]
34 JsonError(#[from] serde_json::Error),
35 #[error(transparent)]
36 X509CertDerError(#[from] x509_cert::der::Error),
37 #[error(transparent)]
38 KeystoreError(#[from] core_crypto_keystore::CryptoKeystoreError),
39}
40
41#[derive(Debug, Clone, derive_more::From, derive_more::Into, derive_more::Deref, derive_more::DerefMut)]
43pub struct NewCrlDistributionPoints(Option<HashSet<String>>);
44
45impl From<NewCrlDistributionPoints> for Option<Vec<String>> {
46 fn from(mut dp: NewCrlDistributionPoints) -> Self {
47 dp.take().map(|d| d.into_iter().collect())
48 }
49}
50
51impl IntoIterator for NewCrlDistributionPoints {
52 type Item = String;
53
54 type IntoIter = std::collections::hash_set::IntoIter<String>;
55
56 fn into_iter(self) -> Self::IntoIter {
57 let items = self.0.unwrap_or_default();
58 items.into_iter()
59 }
60}
61
62async fn restore_pki_env(data_provider: &impl FetchFromDatabase) -> Result<Option<RjtPkiEnvironment>> {
63 let mut trust_roots = vec![];
64 let Ok(Some(ta_raw)) = data_provider.get_unique::<E2eiAcmeCA>().await else {
65 return Ok(None);
66 };
67
68 trust_roots.push(
69 x509_cert::Certificate::from_der(&ta_raw.content).map(x509_cert::anchor::TrustAnchorChoice::Certificate)?,
70 );
71
72 let intermediates = data_provider
73 .load_all::<E2eiIntermediateCert>()
74 .await?
75 .into_iter()
76 .map(|inter| x509_cert::Certificate::from_der(&inter.content))
77 .collect::<core::result::Result<Vec<_>, _>>()?;
78
79 let crls = data_provider
80 .load_all::<E2eiCrl>()
81 .await?
82 .into_iter()
83 .map(|crl| x509_cert::crl::CertificateList::from_der(&crl.content))
84 .collect::<core::result::Result<Vec<_>, _>>()?;
85
86 let params = PkiEnvironmentParams {
87 trust_roots: &trust_roots,
88 intermediates: &intermediates,
89 crls: &crls,
90 time_of_interest: None,
91 };
92
93 Ok(Some(RjtPkiEnvironment::init(params)?))
94}
95
96#[derive(Debug, Clone)]
98pub struct PkiEnvironment {
99 #[expect(dead_code)]
102 hooks: Arc<dyn PkiEnvironmentHooks>,
103 database: Database,
105 mls_pki_env_provider: PkiEnvironmentProvider,
109}
110
111impl PkiEnvironment {
112 pub async fn new(hooks: Arc<dyn PkiEnvironmentHooks>, database: Database) -> Result<PkiEnvironment> {
114 let mls_pki_env_provider = restore_pki_env(&database)
115 .await?
116 .map(PkiEnvironmentProvider::from)
117 .unwrap_or_default();
118 Ok(Self {
119 hooks,
120 database,
121 mls_pki_env_provider,
122 })
123 }
124
125 pub async fn provider_is_setup(&self) -> bool {
127 self.mls_pki_env_provider.is_env_setup().await
128 }
129
130 pub fn mls_pki_env_provider(&self) -> PkiEnvironmentProvider {
131 self.mls_pki_env_provider.clone()
132 }
133
134 pub async fn update_pki_environment_provider(&self) -> Result<()> {
135 if let Some(rjt_pki_environment) = restore_pki_env(&self.database).await? {
136 self.mls_pki_env_provider.update_env(Some(rjt_pki_environment)).await;
137 }
138 Ok(())
139 }
140
141 pub fn database(&self) -> &Database {
142 &self.database
143 }
144}
145
146#[derive(Debug, Clone, Default)]
147pub struct PkiEnvironmentProvider(Arc<RwLock<Option<RjtPkiEnvironment>>>);
148
149impl From<RjtPkiEnvironment> for PkiEnvironmentProvider {
150 fn from(value: RjtPkiEnvironment) -> Self {
151 Self(Arc::new(Some(value).into()))
152 }
153}
154
155impl PkiEnvironmentProvider {
156 pub async fn refresh_time_of_interest(&self) {
157 if let Some(pki) = self.0.write().await.as_mut() {
158 let _ = pki.refresh_time_of_interest();
159 }
160 }
161
162 pub async fn borrow(&self) -> RwLockReadGuard<'_, Option<RjtPkiEnvironment>> {
163 self.0.read().await
164 }
165
166 pub async fn is_env_setup(&self) -> bool {
167 self.0.read().await.is_some()
168 }
169
170 pub async fn update_env(&self, env: Option<RjtPkiEnvironment>) {
171 let mut guard = self.0.write().await;
172 *guard = env;
173 }
174}
175
176#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
177#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
178impl openmls_traits::authentication_service::AuthenticationServiceDelegate for PkiEnvironmentProvider {
179 async fn validate_credential<'a>(&'a self, credential: CredentialRef<'a>) -> CredentialAuthenticationStatus {
180 match credential {
181 CredentialRef::Basic { identity: _ } => CredentialAuthenticationStatus::Valid,
183
184 CredentialRef::X509 { certificates } => {
185 self.refresh_time_of_interest().await;
186
187 let binding = self.0.read().await;
188 let Some(pki_env) = binding.as_ref() else {
189 return CredentialAuthenticationStatus::Valid;
193 };
194
195 use x509_cert::der::Decode as _;
196 let Some(cert) = certificates
197 .first()
198 .and_then(|cert_raw| x509_cert::Certificate::from_der(cert_raw).ok())
199 else {
200 return CredentialAuthenticationStatus::Invalid;
201 };
202
203 if let Err(validation_error) = pki_env.validate_cert_and_revocation(&cert) {
204 use crate::x509_check::{
205 RustyX509CheckError,
206 reexports::certval::{Error as CertvalError, PathValidationStatus},
207 };
208
209 if let RustyX509CheckError::CertValError(CertvalError::PathValidation(
210 certificate_validation_error,
211 )) = validation_error
212 {
213 match certificate_validation_error {
214 PathValidationStatus::Valid
215 | PathValidationStatus::RevocationStatusNotAvailable
216 | PathValidationStatus::RevocationStatusNotDetermined => {}
217 PathValidationStatus::CertificateRevoked
218 | PathValidationStatus::CertificateRevokedEndEntity
219 | PathValidationStatus::CertificateRevokedIntermediateCa => {
220 }
223 PathValidationStatus::InvalidNotAfterDate => {
224 }
227 _ => return CredentialAuthenticationStatus::Invalid,
228 }
229 } else {
230 return CredentialAuthenticationStatus::Unknown;
231 }
232 }
233
234 CredentialAuthenticationStatus::Valid
235 }
236 }
237 }
238}