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 hooks: Arc<dyn PkiEnvironmentHooks>,
101 database: Database,
103 mls_pki_env_provider: PkiEnvironmentProvider,
107}
108
109impl PkiEnvironment {
110 pub async fn new(hooks: Arc<dyn PkiEnvironmentHooks>, database: Database) -> Result<PkiEnvironment> {
112 let mls_pki_env_provider = restore_pki_env(&database)
113 .await?
114 .map(PkiEnvironmentProvider::from)
115 .unwrap_or_default();
116 Ok(Self {
117 hooks,
118 database,
119 mls_pki_env_provider,
120 })
121 }
122
123 pub async fn provider_is_setup(&self) -> bool {
125 self.mls_pki_env_provider.is_env_setup().await
126 }
127
128 pub fn mls_pki_env_provider(&self) -> PkiEnvironmentProvider {
129 self.mls_pki_env_provider.clone()
130 }
131
132 pub async fn update_pki_environment_provider(&self) -> Result<()> {
133 if let Some(rjt_pki_environment) = restore_pki_env(&self.database).await? {
134 self.mls_pki_env_provider.update_env(Some(rjt_pki_environment)).await;
135 }
136 Ok(())
137 }
138
139 pub fn hooks(&self) -> Arc<dyn PkiEnvironmentHooks> {
140 self.hooks.clone()
141 }
142
143 pub fn database(&self) -> &Database {
144 &self.database
145 }
146}
147
148#[derive(Debug, Clone, Default)]
149pub struct PkiEnvironmentProvider(Arc<RwLock<Option<RjtPkiEnvironment>>>);
150
151impl From<RjtPkiEnvironment> for PkiEnvironmentProvider {
152 fn from(value: RjtPkiEnvironment) -> Self {
153 Self(Arc::new(Some(value).into()))
154 }
155}
156
157impl PkiEnvironmentProvider {
158 pub async fn refresh_time_of_interest(&self) {
159 if let Some(pki) = self.0.write().await.as_mut() {
160 let _ = pki.refresh_time_of_interest();
161 }
162 }
163
164 pub async fn borrow(&self) -> RwLockReadGuard<'_, Option<RjtPkiEnvironment>> {
165 self.0.read().await
166 }
167
168 pub async fn is_env_setup(&self) -> bool {
169 self.0.read().await.is_some()
170 }
171
172 pub async fn update_env(&self, env: Option<RjtPkiEnvironment>) {
173 let mut guard = self.0.write().await;
174 *guard = env;
175 }
176}
177
178#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
179#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
180impl openmls_traits::authentication_service::AuthenticationServiceDelegate for PkiEnvironmentProvider {
181 async fn validate_credential<'a>(&'a self, credential: CredentialRef<'a>) -> CredentialAuthenticationStatus {
182 match credential {
183 CredentialRef::Basic { identity: _ } => CredentialAuthenticationStatus::Valid,
185
186 CredentialRef::X509 { certificates } => {
187 self.refresh_time_of_interest().await;
188
189 let binding = self.0.read().await;
190 let Some(pki_env) = binding.as_ref() else {
191 return CredentialAuthenticationStatus::Valid;
195 };
196
197 use x509_cert::der::Decode as _;
198 let Some(cert) = certificates
199 .first()
200 .and_then(|cert_raw| x509_cert::Certificate::from_der(cert_raw).ok())
201 else {
202 return CredentialAuthenticationStatus::Invalid;
203 };
204
205 if let Err(validation_error) = pki_env.validate_cert_and_revocation(&cert) {
206 use crate::x509_check::{
207 RustyX509CheckError,
208 reexports::certval::{Error as CertvalError, PathValidationStatus},
209 };
210
211 if let RustyX509CheckError::CertValError(CertvalError::PathValidation(
212 certificate_validation_error,
213 )) = validation_error
214 {
215 match certificate_validation_error {
216 PathValidationStatus::Valid
217 | PathValidationStatus::RevocationStatusNotAvailable
218 | PathValidationStatus::RevocationStatusNotDetermined => {}
219 PathValidationStatus::CertificateRevoked
220 | PathValidationStatus::CertificateRevokedEndEntity
221 | PathValidationStatus::CertificateRevokedIntermediateCa => {
222 }
225 PathValidationStatus::InvalidNotAfterDate => {
226 }
229 _ => return CredentialAuthenticationStatus::Invalid,
230 }
231 } else {
232 return CredentialAuthenticationStatus::Unknown;
233 }
234 }
235
236 CredentialAuthenticationStatus::Valid
237 }
238 }
239 }
240}