wire_e2e_identity/pki_env/
mod.rs1mod crl;
4pub mod hooks;
5
6use std::{collections::HashSet, sync::Arc};
7
8use async_lock::{RwLock, RwLockReadGuard};
9use certval::{CertVector as _, TaSource};
10use core_crypto_keystore::{
11 connection::Database,
12 entities::{E2eiAcmeCA, E2eiCrl, E2eiIntermediateCert},
13 traits::FetchFromDatabase,
14};
15use openmls_traits::authentication_service::{CredentialAuthenticationStatus, CredentialRef};
16use x509_cert::{
17 Certificate,
18 der::{Decode as _, Encode as _},
19};
20
21use crate::{
22 pki_env::hooks::PkiEnvironmentHooks,
23 x509_check::{
24 RustyX509CheckError,
25 revocation::{PkiEnvironment as RjtPkiEnvironment, PkiEnvironmentParams},
26 },
27};
28
29pub type Result<T> = core::result::Result<T, Error>;
30
31#[derive(Debug, thiserror::Error)]
32pub enum Error {
33 #[error("The trust anchor certificate couldn't be loaded from the database.")]
34 NoTrustAnchor,
35 #[error("Failed to fetch CRL from '{uri}': HTTP {status}")]
36 CrlFetchUnsuccessful { uri: String, status: u16 },
37 #[error(transparent)]
38 HooksError(#[from] hooks::PkiEnvironmentHooksError),
39 #[error(transparent)]
40 X509Error(#[from] RustyX509CheckError),
41 #[error(transparent)]
42 UrlError(#[from] url::ParseError),
43 #[error(transparent)]
44 JsonError(#[from] serde_json::Error),
45 #[error(transparent)]
46 X509CertDerError(#[from] x509_cert::der::Error),
47 #[error(transparent)]
48 KeystoreError(#[from] core_crypto_keystore::CryptoKeystoreError),
49 #[error("certval error: {0}")]
50 Certval(certval::Error),
51}
52
53#[derive(Debug, Clone, derive_more::From, derive_more::Into, derive_more::Deref, derive_more::DerefMut)]
55pub struct NewCrlDistributionPoints(Option<HashSet<String>>);
56
57impl From<NewCrlDistributionPoints> for Option<Vec<String>> {
58 fn from(mut dp: NewCrlDistributionPoints) -> Self {
59 dp.take().map(|d| d.into_iter().collect())
60 }
61}
62
63impl IntoIterator for NewCrlDistributionPoints {
64 type Item = String;
65
66 type IntoIter = std::collections::hash_set::IntoIter<String>;
67
68 fn into_iter(self) -> Self::IntoIter {
69 let items = self.0.unwrap_or_default();
70 items.into_iter()
71 }
72}
73
74async fn restore_pki_env(data_provider: &impl FetchFromDatabase) -> Result<RjtPkiEnvironment> {
75 let mut trust_roots = vec![];
76 if let Ok(Some(ta_raw)) = data_provider.get_unique::<E2eiAcmeCA>().await {
77 trust_roots.push(
78 x509_cert::Certificate::from_der(&ta_raw.content).map(x509_cert::anchor::TrustAnchorChoice::Certificate)?,
79 );
80 }
81
82 let intermediates = data_provider
83 .load_all::<E2eiIntermediateCert>()
84 .await?
85 .into_iter()
86 .map(|inter| x509_cert::Certificate::from_der(&inter.content))
87 .collect::<core::result::Result<Vec<_>, _>>()?;
88
89 let crls = data_provider
90 .load_all::<E2eiCrl>()
91 .await?
92 .into_iter()
93 .map(|crl| x509_cert::crl::CertificateList::from_der(&crl.content))
94 .collect::<core::result::Result<Vec<_>, _>>()?;
95
96 let params = PkiEnvironmentParams {
97 trust_roots: &trust_roots,
98 intermediates: &intermediates,
99 crls: &crls,
100 time_of_interest: None,
101 };
102
103 Ok(RjtPkiEnvironment::init(params)?)
104}
105
106#[derive(Debug, Clone)]
108pub struct PkiEnvironment {
109 hooks: Arc<dyn PkiEnvironmentHooks>,
111 database: Database,
113 mls_pki_env_provider: PkiEnvironmentProvider,
117}
118
119impl PkiEnvironment {
120 pub async fn new(hooks: Arc<dyn PkiEnvironmentHooks>, database: Database) -> Result<PkiEnvironment> {
122 let mls_pki_env_provider = PkiEnvironmentProvider::from(restore_pki_env(&database).await?);
123 Ok(Self {
124 hooks,
125 database,
126 mls_pki_env_provider,
127 })
128 }
129
130 pub async fn provider_is_setup(&self) -> bool {
132 self.mls_pki_env_provider.is_env_setup().await
133 }
134
135 pub fn mls_pki_env_provider(&self) -> PkiEnvironmentProvider {
136 self.mls_pki_env_provider.clone()
137 }
138
139 pub async fn update_pki_environment_provider(&self) -> Result<()> {
140 let rjt_pki_environment = restore_pki_env(&self.database).await?;
141 self.mls_pki_env_provider.update_env(Some(rjt_pki_environment)).await;
142 Ok(())
143 }
144
145 pub fn hooks(&self) -> Arc<dyn PkiEnvironmentHooks> {
146 self.hooks.clone()
147 }
148
149 pub fn database(&self) -> &Database {
150 &self.database
151 }
152
153 pub async fn trust_anchor(&self) -> Result<Certificate> {
154 let trust_anchor = self
155 .database
156 .get_unique::<E2eiAcmeCA>()
157 .await?
158 .ok_or(Error::NoTrustAnchor)?;
159
160 let trust_anchor = x509_cert::Certificate::from_der(&trust_anchor.content)?;
161 Ok(trust_anchor)
162 }
163
164 pub async fn add_trust_anchor(&mut self, name: &str, cert: Certificate) -> Result<()> {
165 let mut guard = self.mls_pki_env_provider.0.write().await;
166 let pki_env = guard.as_mut().expect("inner PKI environment must be set");
167
168 let mut trust_anchors = TaSource::new();
169 trust_anchors.push(certval::CertFile {
170 filename: name.to_owned(),
171 bytes: cert.to_der()?,
172 });
173 trust_anchors.initialize().map_err(Error::Certval)?;
174 pki_env.add_trust_anchor_source(Box::new(trust_anchors));
175 Ok(())
176 }
177}
178
179#[derive(Debug, Clone, Default)]
180pub struct PkiEnvironmentProvider(Arc<RwLock<Option<RjtPkiEnvironment>>>);
181
182impl From<RjtPkiEnvironment> for PkiEnvironmentProvider {
183 fn from(value: RjtPkiEnvironment) -> Self {
184 Self(Arc::new(Some(value).into()))
185 }
186}
187
188impl PkiEnvironmentProvider {
189 pub async fn refresh_time_of_interest(&self) {
190 if let Some(pki) = self.0.write().await.as_mut() {
191 let _ = pki.refresh_time_of_interest();
192 }
193 }
194
195 pub async fn borrow(&self) -> RwLockReadGuard<'_, Option<RjtPkiEnvironment>> {
196 self.0.read().await
197 }
198
199 pub async fn is_env_setup(&self) -> bool {
200 self.0.read().await.is_some()
201 }
202
203 pub async fn update_env(&self, env: Option<RjtPkiEnvironment>) {
204 let mut guard = self.0.write().await;
205 *guard = env;
206 }
207}
208
209#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
210#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
211impl openmls_traits::authentication_service::AuthenticationServiceDelegate for PkiEnvironmentProvider {
212 async fn validate_credential<'a>(&'a self, credential: CredentialRef<'a>) -> CredentialAuthenticationStatus {
213 match credential {
214 CredentialRef::Basic { identity: _ } => CredentialAuthenticationStatus::Valid,
216
217 CredentialRef::X509 { certificates } => {
218 self.refresh_time_of_interest().await;
219
220 let binding = self.0.read().await;
221 let Some(pki_env) = binding.as_ref() else {
222 return CredentialAuthenticationStatus::Valid;
226 };
227
228 use x509_cert::der::Decode as _;
229 let Some(cert) = certificates
230 .first()
231 .and_then(|cert_raw| x509_cert::Certificate::from_der(cert_raw).ok())
232 else {
233 return CredentialAuthenticationStatus::Invalid;
234 };
235
236 if let Err(validation_error) = pki_env.validate_cert_and_revocation(&cert) {
237 use crate::x509_check::{
238 RustyX509CheckError,
239 reexports::certval::{Error as CertvalError, PathValidationStatus},
240 };
241
242 if let RustyX509CheckError::CertValError(CertvalError::PathValidation(
243 certificate_validation_error,
244 )) = validation_error
245 {
246 match certificate_validation_error {
247 PathValidationStatus::Valid
248 | PathValidationStatus::RevocationStatusNotAvailable
249 | PathValidationStatus::RevocationStatusNotDetermined => {}
250 PathValidationStatus::CertificateRevoked
251 | PathValidationStatus::CertificateRevokedEndEntity
252 | PathValidationStatus::CertificateRevokedIntermediateCa => {
253 }
256 PathValidationStatus::InvalidNotAfterDate => {
257 }
260 _ => return CredentialAuthenticationStatus::Invalid,
261 }
262 } else {
263 return CredentialAuthenticationStatus::Unknown;
264 }
265 }
266
267 CredentialAuthenticationStatus::Valid
268 }
269 }
270 }
271}