core_crypto/e2e_identity/
init_certificates.rs1use super::{Error, Result};
2use crate::{MlsError, RecursiveError, context::CentralContext, e2e_identity::CrlRegistration, prelude::MlsCentral};
3use core_crypto_keystore::{
4 connection::FetchFromDatabase,
5 entities::{E2eiAcmeCA, E2eiCrl, E2eiIntermediateCert},
6};
7use openmls_traits::OpenMlsCryptoProvider;
8use std::collections::HashSet;
9use wire_e2e_identity::prelude::x509::{
10 extract_crl_uris, extract_expiration_from_crl,
11 revocation::{PkiEnvironment, PkiEnvironmentParams},
12};
13use x509_cert::der::{Decode, EncodePem, pem::LineEnding};
14
15#[derive(Debug, Clone, derive_more::From, derive_more::Into, derive_more::Deref, derive_more::DerefMut)]
16pub struct NewCrlDistributionPoint(Option<HashSet<String>>);
17
18impl From<NewCrlDistributionPoint> for Option<Vec<String>> {
19 fn from(mut dp: NewCrlDistributionPoint) -> Self {
20 dp.take().map(|d| d.into_iter().collect())
21 }
22}
23
24#[derive(Debug, Clone)]
25pub struct E2eiDumpedPkiEnv {
27 pub root_ca: String,
29 pub intermediates: Vec<String>,
31 pub crls: Vec<String>,
33}
34
35impl CentralContext {
36 pub async fn e2ei_is_pki_env_setup(&self) -> Result<bool> {
39 Ok(self
40 .mls_provider()
41 .await
42 .map_err(RecursiveError::root("getting mls provider"))?
43 .authentication_service()
44 .is_env_setup()
45 .await)
46 }
47
48 pub async fn e2ei_dump_pki_env(&self) -> Result<Option<E2eiDumpedPkiEnv>> {
50 if !self.e2ei_is_pki_env_setup().await? {
51 return Ok(None);
52 }
53 let mls_provider = self
54 .mls_provider()
55 .await
56 .map_err(RecursiveError::root("getting mls provider"))?;
57 let Some(pki_env) = &*mls_provider.authentication_service().borrow().await else {
58 return Ok(None);
59 };
60 E2eiDumpedPkiEnv::from_pki_env(pki_env).await
61 }
62
63 pub async fn e2ei_register_acme_ca(&self, trust_anchor_pem: String) -> Result<()> {
71 {
72 if self
73 .mls_provider()
74 .await
75 .map_err(RecursiveError::root("getting mls provider"))?
76 .keystore()
77 .find_unique::<E2eiAcmeCA>()
78 .await
79 .is_ok()
80 {
81 return Err(Error::TrustAnchorAlreadyRegistered);
82 }
83 }
84
85 let pki_env = PkiEnvironment::init(PkiEnvironmentParams {
86 intermediates: Default::default(),
87 trust_roots: Default::default(),
88 crls: Default::default(),
89 time_of_interest: Default::default(),
90 })?;
91
92 let root_cert = PkiEnvironment::decode_pem_cert(trust_anchor_pem)?;
94
95 pki_env.validate_trust_anchor_cert(&root_cert)?;
97
98 let cert_der = PkiEnvironment::encode_cert_to_der(&root_cert)?;
100 let acme_ca = E2eiAcmeCA { content: cert_der };
101 self.mls_provider()
102 .await
103 .map_err(RecursiveError::root("getting mls provider"))?
104 .keystore()
105 .save(acme_ca)
106 .await?;
107
108 self.init_pki_env().await?;
110
111 Ok(())
112 }
113
114 pub(crate) async fn init_pki_env(&self) -> Result<()> {
115 if let Some(pki_env) = restore_pki_env(
116 &self
117 .mls_provider()
118 .await
119 .map_err(RecursiveError::root("getting mls provider"))?
120 .keystore(),
121 )
122 .await?
123 {
124 let provider = self
125 .mls_provider()
126 .await
127 .map_err(RecursiveError::root("getting mls provider"))?;
128 provider
129 .authentication_service()
130 .update_env(pki_env)
131 .await
132 .map_err(MlsError::wrap("updating authentication service env"))?;
133 }
134
135 Ok(())
136 }
137
138 pub async fn e2ei_register_intermediate_ca_pem(&self, cert_pem: String) -> Result<NewCrlDistributionPoint> {
146 let inter_ca = PkiEnvironment::decode_pem_cert(cert_pem)?;
148 self.e2ei_register_intermediate_ca(inter_ca).await
149 }
150
151 pub(crate) async fn e2ei_register_intermediate_ca_der(&self, cert_der: &[u8]) -> Result<NewCrlDistributionPoint> {
152 let inter_ca = x509_cert::Certificate::from_der(cert_der)?;
153 self.e2ei_register_intermediate_ca(inter_ca).await
154 }
155
156 async fn e2ei_register_intermediate_ca(&self, inter_ca: x509_cert::Certificate) -> Result<NewCrlDistributionPoint> {
157 let keystore = self
159 .keystore()
160 .await
161 .map_err(RecursiveError::root("getting keystore"))?;
162 let trust_anchor = keystore.find_unique::<E2eiAcmeCA>().await?;
163 let trust_anchor = x509_cert::Certificate::from_der(&trust_anchor.content)?;
164
165 if inter_ca == trust_anchor {
168 return Ok(None.into());
169 }
170
171 let intermediate_crl = extract_crl_uris(&inter_ca)?.map(|s| s.into_iter().collect());
172
173 let (ski, aki) = PkiEnvironment::extract_ski_aki_from_cert(&inter_ca)?;
174
175 let ski_aki_pair = format!("{ski}:{}", aki.unwrap_or_default());
176
177 {
179 let provider = self
180 .mls_provider()
181 .await
182 .map_err(RecursiveError::root("getting mls provider"))?;
183 let auth_service_arc = provider.authentication_service().borrow().await;
184 let Some(pki_env) = auth_service_arc.as_ref() else {
185 return Err(Error::PkiEnvironmentUnset);
186 };
187 pki_env.validate_cert_and_revocation(&inter_ca)?;
188 }
189
190 let cert_der = PkiEnvironment::encode_cert_to_der(&inter_ca)?;
192 let intermediate_ca = E2eiIntermediateCert {
193 content: cert_der,
194 ski_aki_pair,
195 };
196 keystore.save(intermediate_ca).await?;
197
198 self.init_pki_env().await?;
199
200 Ok(intermediate_crl.into())
201 }
202
203 pub async fn e2ei_register_crl(&self, crl_dp: String, crl_der: Vec<u8>) -> Result<CrlRegistration> {
215 let crl = {
217 let provider = self
218 .mls_provider()
219 .await
220 .map_err(RecursiveError::root("getting mls provider"))?;
221 let auth_service_arc = provider.authentication_service().borrow().await;
222 let Some(pki_env) = auth_service_arc.as_ref() else {
223 return Err(Error::PkiEnvironmentUnset);
224 };
225 pki_env.validate_crl_with_raw(&crl_der)?
226 };
227
228 let expiration = extract_expiration_from_crl(&crl);
229
230 let ks = self
231 .keystore()
232 .await
233 .map_err(RecursiveError::root("getting keystore"))?;
234
235 let dirty = ks
236 .find::<E2eiCrl>(crl_dp.as_bytes())
237 .await
238 .ok()
239 .flatten()
240 .map(|existing_crl| {
241 PkiEnvironment::decode_der_crl(existing_crl.content.clone())
242 .map(|old_crl| old_crl.tbs_cert_list.revoked_certificates != crl.tbs_cert_list.revoked_certificates)
243 })
244 .transpose()?
245 .unwrap_or_default();
246
247 let crl_data = E2eiCrl {
249 content: PkiEnvironment::encode_crl_to_der(&crl)?,
250 distribution_point: crl_dp,
251 };
252 ks.save(crl_data).await?;
253
254 self.init_pki_env().await?;
255
256 Ok(CrlRegistration { expiration, dirty })
257 }
258}
259
260impl MlsCentral {
261 pub async fn e2ei_is_pki_env_setup(&self) -> bool {
263 self.mls_backend.is_pki_env_setup().await
264 }
265
266 pub async fn e2ei_dump_pki_env(&self) -> Result<Option<E2eiDumpedPkiEnv>> {
268 if !self.e2ei_is_pki_env_setup().await {
269 return Ok(None);
270 }
271 let pki_env_lock = self.mls_backend.authentication_service().borrow().await;
272 let Some(pki_env) = &*pki_env_lock else {
273 return Ok(None);
274 };
275 E2eiDumpedPkiEnv::from_pki_env(pki_env).await
276 }
277}
278
279impl E2eiDumpedPkiEnv {
280 async fn from_pki_env(pki_env: &PkiEnvironment) -> Result<Option<E2eiDumpedPkiEnv>> {
281 let Some(root) = pki_env
282 .get_trust_anchors()
283 .map_err(Error::certificate_validation("getting pki trust anchors"))?
284 .pop()
285 else {
286 return Ok(None);
287 };
288
289 let x509_cert::anchor::TrustAnchorChoice::Certificate(root) = &root.decoded_ta else {
290 return Ok(None);
291 };
292
293 let root_ca = root.to_pem(LineEnding::LF)?;
294
295 let inner_intermediates = pki_env
296 .get_intermediates()
297 .map_err(Error::certificate_validation("getting pki intermediates"))?;
298
299 let mut intermediates = Vec::with_capacity(inner_intermediates.len());
300
301 for inter in inner_intermediates {
302 let pem_inter = inter.decoded_cert.to_pem(LineEnding::LF)?;
303 intermediates.push(pem_inter);
304 }
305
306 let inner_crls: Vec<Vec<u8>> = pki_env
307 .get_all_crls()
308 .map_err(Error::certificate_validation("getting all crls"))?;
309
310 let mut crls = Vec::with_capacity(inner_crls.len());
311 for crl in inner_crls.into_iter() {
312 let crl_pem = x509_cert::der::pem::encode_string("X509 CRL", LineEnding::LF, &crl)
313 .map_err(Error::certificate_validation("encoding crl title to pem"))?;
314 crls.push(crl_pem);
315 }
316
317 Ok(Some(E2eiDumpedPkiEnv {
318 root_ca,
319 intermediates,
320 crls,
321 }))
322 }
323}
324
325pub(crate) async fn restore_pki_env(data_provider: &impl FetchFromDatabase) -> Result<Option<PkiEnvironment>> {
326 let mut trust_roots = vec![];
327 let Ok(ta_raw) = data_provider.find_unique::<E2eiAcmeCA>().await else {
328 return Ok(None);
329 };
330
331 trust_roots.push(
332 x509_cert::Certificate::from_der(&ta_raw.content).map(x509_cert::anchor::TrustAnchorChoice::Certificate)?,
333 );
334
335 let intermediates = data_provider
336 .find_all::<E2eiIntermediateCert>(Default::default())
337 .await?
338 .into_iter()
339 .map(|inter| x509_cert::Certificate::from_der(&inter.content))
340 .collect::<Result<Vec<_>, _>>()?;
341
342 let crls = data_provider
343 .find_all::<E2eiCrl>(Default::default())
344 .await?
345 .into_iter()
346 .map(|crl| x509_cert::crl::CertificateList::from_der(&crl.content))
347 .collect::<Result<Vec<_>, _>>()?;
348
349 let params = PkiEnvironmentParams {
350 trust_roots: &trust_roots,
351 intermediates: &intermediates,
352 crls: &crls,
353 time_of_interest: None,
354 };
355
356 Ok(Some(PkiEnvironment::init(params)?))
357}
358
359#[cfg(test)]
360mod tests {
361 use wasm_bindgen_test::*;
362 use x509_cert::der::EncodePem;
363
364 use crate::test_utils::*;
365
366 use super::super::Error;
367
368 wasm_bindgen_test_configure!(run_in_browser);
369
370 #[apply(all_cred_cipher)]
371 #[wasm_bindgen_test]
372 async fn register_acme_ca_should_fail_when_already_set(case: TestCase) {
373 use x509_cert::der::pem::LineEnding;
374
375 if !case.is_x509() {
376 return;
377 }
378 run_test_with_client_ids(case.clone(), ["alice"], move |[alice_central]| {
379 Box::pin(async move {
380 let alice_test_chain = alice_central.x509_test_chain.as_ref().as_ref().unwrap();
381 let alice_ta = alice_test_chain
382 .trust_anchor
383 .certificate
384 .to_pem(LineEnding::CRLF)
385 .unwrap();
386
387 assert!(matches!(
388 alice_central.context.e2ei_register_acme_ca(alice_ta).await.unwrap_err(),
389 Error::TrustAnchorAlreadyRegistered
390 ));
391 })
392 })
393 .await;
394 }
395
396 #[apply(all_cred_cipher)]
397 #[wasm_bindgen_test]
398 async fn x509_restore_should_not_happen_if_basic(case: TestCase) {
399 if case.is_x509() {
400 return;
401 }
402 run_test_with_client_ids(case.clone(), ["alice"], move |[alice_ctx]| {
403 Box::pin(async move {
404 let ClientContext {
405 context,
406 x509_test_chain,
407 ..
408 } = alice_ctx;
409
410 assert!(x509_test_chain.is_none());
411 assert!(!context.e2ei_is_pki_env_setup().await.unwrap());
412
413 assert!(x509_test_chain.is_none());
416 })
418 })
419 .await;
420 }
421}