core_crypto/e2e_identity/
init_certificates.rs1use crate::context::CentralContext;
2use crate::{e2e_identity::CrlRegistration, prelude::MlsCentral, CryptoError, CryptoResult};
3use core_crypto_keystore::connection::FetchFromDatabase;
4use core_crypto_keystore::entities::{E2eiAcmeCA, E2eiCrl, E2eiIntermediateCert};
5use openmls_traits::OpenMlsCryptoProvider;
6use std::collections::HashSet;
7use wire_e2e_identity::prelude::x509::{
8 extract_crl_uris, extract_expiration_from_crl,
9 revocation::{PkiEnvironment, PkiEnvironmentParams},
10 RustyX509CheckError,
11};
12use x509_cert::der::pem::LineEnding;
13use x509_cert::der::{Decode, EncodePem};
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) -> CryptoResult<bool> {
39 Ok(self.mls_provider().await?.authentication_service().is_env_setup().await)
40 }
41
42 pub async fn e2ei_dump_pki_env(&self) -> CryptoResult<Option<E2eiDumpedPkiEnv>> {
44 if !self.e2ei_is_pki_env_setup().await? {
45 return Ok(None);
46 }
47 let mls_provider = self.mls_provider().await?;
48 let Some(pki_env) = &*mls_provider.authentication_service().borrow().await else {
49 return Ok(None);
50 };
51 E2eiDumpedPkiEnv::from_pki_env(pki_env).await
52 }
53
54 pub async fn e2ei_register_acme_ca(&self, trust_anchor_pem: String) -> CryptoResult<()> {
62 {
63 if self
64 .mls_provider()
65 .await?
66 .keystore()
67 .find_unique::<E2eiAcmeCA>()
68 .await
69 .is_ok()
70 {
71 return Err(CryptoError::E2eiError(
72 super::E2eIdentityError::TrustAnchorAlreadyRegistered,
73 ));
74 }
75 }
76
77 let pki_env = PkiEnvironment::init(PkiEnvironmentParams {
78 intermediates: Default::default(),
79 trust_roots: Default::default(),
80 crls: Default::default(),
81 time_of_interest: Default::default(),
82 })
83 .map_err(|e| CryptoError::E2eiError(e.into()))?;
84
85 let root_cert =
87 PkiEnvironment::decode_pem_cert(trust_anchor_pem).map_err(|e| CryptoError::E2eiError(e.into()))?;
88
89 pki_env
91 .validate_trust_anchor_cert(&root_cert)
92 .map_err(|e| CryptoError::E2eiError(e.into()))?;
93
94 let cert_der = PkiEnvironment::encode_cert_to_der(&root_cert).map_err(|e| CryptoError::E2eiError(e.into()))?;
96 let acme_ca = E2eiAcmeCA { content: cert_der };
97 self.mls_provider().await?.keystore().save(acme_ca).await?;
98
99 self.init_pki_env().await?;
101
102 Ok(())
103 }
104
105 pub(crate) async fn init_pki_env(&self) -> CryptoResult<()> {
106 if let Some(pki_env) = restore_pki_env(&self.mls_provider().await?.keystore()).await? {
107 let provider = self.mls_provider().await?;
108 provider.authentication_service().update_env(pki_env).await?;
109 }
110
111 Ok(())
112 }
113
114 pub async fn e2ei_register_intermediate_ca_pem(&self, cert_pem: String) -> CryptoResult<NewCrlDistributionPoint> {
122 let inter_ca = PkiEnvironment::decode_pem_cert(cert_pem).map_err(|e| CryptoError::E2eiError(e.into()))?;
124 self.e2ei_register_intermediate_ca(inter_ca).await
125 }
126
127 pub(crate) async fn e2ei_register_intermediate_ca_der(
128 &self,
129 cert_der: &[u8],
130 ) -> CryptoResult<NewCrlDistributionPoint> {
131 let inter_ca = x509_cert::Certificate::from_der(cert_der)?;
132 self.e2ei_register_intermediate_ca(inter_ca).await
133 }
134
135 async fn e2ei_register_intermediate_ca(
136 &self,
137 inter_ca: x509_cert::Certificate,
138 ) -> CryptoResult<NewCrlDistributionPoint> {
139 let keystore = self.keystore().await?;
141 let trust_anchor = keystore.find_unique::<E2eiAcmeCA>().await?;
142 let trust_anchor = x509_cert::Certificate::from_der(&trust_anchor.content)?;
143
144 if inter_ca == trust_anchor {
147 return Ok(None.into());
148 }
149
150 let intermediate_crl = extract_crl_uris(&inter_ca)
151 .map_err(|e| CryptoError::E2eiError(e.into()))?
152 .map(|s| s.into_iter().collect());
153
154 let (ski, aki) =
155 PkiEnvironment::extract_ski_aki_from_cert(&inter_ca).map_err(|e| CryptoError::E2eiError(e.into()))?;
156
157 let ski_aki_pair = format!("{ski}:{}", aki.unwrap_or_default());
158
159 {
161 let provider = self.mls_provider().await?;
162 let auth_service_arc = provider.authentication_service().borrow().await;
163 let Some(pki_env) = auth_service_arc.as_ref() else {
164 return Err(CryptoError::ConsumerError);
165 };
166 pki_env
167 .validate_cert_and_revocation(&inter_ca)
168 .map_err(|e| CryptoError::E2eiError(e.into()))?;
169 }
170
171 let cert_der = PkiEnvironment::encode_cert_to_der(&inter_ca).map_err(|e| CryptoError::E2eiError(e.into()))?;
173 let intermediate_ca = E2eiIntermediateCert {
174 content: cert_der,
175 ski_aki_pair,
176 };
177 keystore.save(intermediate_ca).await?;
178
179 self.init_pki_env().await?;
180
181 Ok(intermediate_crl.into())
182 }
183
184 pub async fn e2ei_register_crl(&self, crl_dp: String, crl_der: Vec<u8>) -> CryptoResult<CrlRegistration> {
196 let crl = {
198 let provider = self.mls_provider().await?;
199 let auth_service_arc = provider.authentication_service().borrow().await;
200 let Some(pki_env) = auth_service_arc.as_ref() else {
201 return Err(CryptoError::ConsumerError);
202 };
203 pki_env
204 .validate_crl_with_raw(&crl_der)
205 .map_err(|e| CryptoError::E2eiError(e.into()))?
206 };
207
208 let expiration = extract_expiration_from_crl(&crl);
209
210 let ks = self.keystore().await?;
211
212 let dirty = if let Some(existing_crl) = ks.find::<E2eiCrl>(crl_dp.as_bytes()).await.ok().flatten() {
213 let old_crl = PkiEnvironment::decode_der_crl(existing_crl.content.clone())
214 .map_err(|e| CryptoError::E2eiError(e.into()))?;
215
216 old_crl.tbs_cert_list.revoked_certificates != crl.tbs_cert_list.revoked_certificates
217 } else {
218 false
219 };
220
221 let crl_data = E2eiCrl {
223 content: PkiEnvironment::encode_crl_to_der(&crl).map_err(|e| CryptoError::E2eiError(e.into()))?,
224 distribution_point: crl_dp,
225 };
226 ks.save(crl_data).await?;
227
228 self.init_pki_env().await?;
229
230 Ok(CrlRegistration { expiration, dirty })
231 }
232}
233
234impl MlsCentral {
235 pub async fn e2ei_is_pki_env_setup(&self) -> bool {
237 self.mls_backend.is_pki_env_setup().await
238 }
239
240 pub async fn e2ei_dump_pki_env(&self) -> CryptoResult<Option<E2eiDumpedPkiEnv>> {
242 if !self.e2ei_is_pki_env_setup().await {
243 return Ok(None);
244 }
245 let pki_env_lock = self.mls_backend.authentication_service().borrow().await;
246 let Some(pki_env) = &*pki_env_lock else {
247 return Ok(None);
248 };
249 E2eiDumpedPkiEnv::from_pki_env(pki_env).await
250 }
251}
252
253impl E2eiDumpedPkiEnv {
254 async fn from_pki_env(pki_env: &PkiEnvironment) -> CryptoResult<Option<E2eiDumpedPkiEnv>> {
255 let Some(root) = pki_env
256 .get_trust_anchors()
257 .map_err(|e| CryptoError::E2eiError(RustyX509CheckError::from(e).into()))?
258 .pop()
259 else {
260 return Ok(None);
261 };
262
263 let x509_cert::anchor::TrustAnchorChoice::Certificate(root) = &root.decoded_ta else {
264 return Ok(None);
265 };
266
267 let root_ca = root
268 .to_pem(LineEnding::LF)
269 .map_err(|e| CryptoError::E2eiError(RustyX509CheckError::from(e).into()))?;
270
271 let inner_intermediates = pki_env
272 .get_intermediates()
273 .map_err(|e| CryptoError::E2eiError(RustyX509CheckError::from(e).into()))?;
274
275 let mut intermediates = Vec::with_capacity(inner_intermediates.len());
276
277 for inter in inner_intermediates {
278 let pem_inter = inter
279 .decoded_cert
280 .to_pem(LineEnding::LF)
281 .map_err(|e| CryptoError::E2eiError(RustyX509CheckError::from(e).into()))?;
282 intermediates.push(pem_inter);
283 }
284
285 let inner_crls: Vec<Vec<u8>> = pki_env
286 .get_all_crls()
287 .map_err(|e| CryptoError::E2eiError(RustyX509CheckError::from(e).into()))?;
288
289 let mut crls = Vec::with_capacity(inner_crls.len());
290 for crl in inner_crls.into_iter() {
291 let crl_pem = x509_cert::der::pem::encode_string("X509 CRL", LineEnding::LF, &crl)
292 .map_err(|e| CryptoError::E2eiError(RustyX509CheckError::from(e).into()))?;
293 crls.push(crl_pem);
294 }
295
296 Ok(Some(E2eiDumpedPkiEnv {
297 root_ca,
298 intermediates,
299 crls,
300 }))
301 }
302}
303
304pub(crate) async fn restore_pki_env(data_provider: &impl FetchFromDatabase) -> CryptoResult<Option<PkiEnvironment>> {
305 let mut trust_roots = vec![];
306 let Ok(ta_raw) = data_provider.find_unique::<E2eiAcmeCA>().await else {
307 return Ok(None);
308 };
309
310 trust_roots.push(
311 x509_cert::Certificate::from_der(&ta_raw.content).map(x509_cert::anchor::TrustAnchorChoice::Certificate)?,
312 );
313
314 let intermediates = data_provider
315 .find_all::<E2eiIntermediateCert>(Default::default())
316 .await?
317 .into_iter()
318 .try_fold(vec![], |mut acc, inter| {
319 acc.push(x509_cert::Certificate::from_der(&inter.content)?);
320 CryptoResult::Ok(acc)
321 })?;
322
323 let crls = data_provider
324 .find_all::<E2eiCrl>(Default::default())
325 .await?
326 .into_iter()
327 .try_fold(vec![], |mut acc, crl| {
328 acc.push(x509_cert::crl::CertificateList::from_der(&crl.content)?);
329 CryptoResult::Ok(acc)
330 })?;
331
332 let params = PkiEnvironmentParams {
333 trust_roots: &trust_roots,
334 intermediates: &intermediates,
335 crls: &crls,
336 time_of_interest: None,
337 };
338
339 Ok(Some(
340 PkiEnvironment::init(params).map_err(|e| CryptoError::E2eiError(e.into()))?,
341 ))
342}
343
344#[cfg(test)]
345mod tests {
346 use crate::prelude::E2eIdentityError;
347 use wasm_bindgen_test::*;
348 use x509_cert::der::pem::LineEnding;
349 use x509_cert::der::EncodePem;
350
351 use crate::test_utils::*;
352
353 use super::*;
354
355 wasm_bindgen_test_configure!(run_in_browser);
356
357 #[apply(all_cred_cipher)]
358 #[wasm_bindgen_test]
359 async fn register_acme_ca_should_fail_when_already_set(case: TestCase) {
360 if !case.is_x509() {
361 return;
362 }
363 run_test_with_client_ids(case.clone(), ["alice"], move |[alice_central]| {
364 Box::pin(async move {
365 let alice_test_chain = alice_central.x509_test_chain.as_ref().as_ref().unwrap();
366 let alice_ta = alice_test_chain
367 .trust_anchor
368 .certificate
369 .to_pem(LineEnding::CRLF)
370 .unwrap();
371
372 assert!(matches!(
373 alice_central.context.e2ei_register_acme_ca(alice_ta).await.unwrap_err(),
374 CryptoError::E2eiError(E2eIdentityError::TrustAnchorAlreadyRegistered)
375 ));
376 })
377 })
378 .await;
379 }
380
381 #[apply(all_cred_cipher)]
382 #[wasm_bindgen_test]
383 async fn x509_restore_should_not_happen_if_basic(case: TestCase) {
384 if case.is_x509() {
385 return;
386 }
387 run_test_with_client_ids(case.clone(), ["alice"], move |[alice_ctx]| {
388 Box::pin(async move {
389 let ClientContext {
390 context,
391 x509_test_chain,
392 ..
393 } = alice_ctx;
394
395 assert!(x509_test_chain.is_none());
396 assert!(!context.e2ei_is_pki_env_setup().await.unwrap());
397
398 assert!(x509_test_chain.is_none());
401 })
403 })
404 .await;
405 }
406}