core_crypto/transaction_context/e2e_identity/
mod.rs1pub(crate) mod conversation_state;
4pub mod enabled;
5mod error;
6mod init_certificates;
7mod rotate;
8mod stash;
9
10use std::collections::{HashMap, HashSet};
11
12use crate::{
13 RecursiveError,
14 mls::credential::{crl::get_new_crl_distribution_points, x509::CertificatePrivateKey},
15 prelude::{CertificateBundle, ClientId, ClientIdentifier, E2eiEnrollment, MlsCiphersuite},
16};
17use openmls_traits::OpenMlsCryptoProvider as _;
18use wire_e2e_identity::prelude::x509::extract_crl_uris;
19
20use super::TransactionContext;
21pub use crate::e2e_identity::E2eiDumpedPkiEnv;
22use crate::e2e_identity::NewCrlDistributionPoints;
23pub use error::{Error, Result};
24
25impl TransactionContext {
26 pub async fn e2ei_new_enrollment(
35 &self,
36 client_id: ClientId,
37 display_name: String,
38 handle: String,
39 team: Option<String>,
40 expiry_sec: u32,
41 ciphersuite: MlsCiphersuite,
42 ) -> Result<E2eiEnrollment> {
43 let signature_keypair = None; E2eiEnrollment::try_new(
45 client_id,
46 display_name,
47 handle,
48 team,
49 expiry_sec,
50 &self
51 .mls_provider()
52 .await
53 .map_err(RecursiveError::transaction("getting mls provider"))?,
54 ciphersuite,
55 signature_keypair,
56 #[cfg(not(target_family = "wasm"))]
57 None, )
59 .map_err(RecursiveError::e2e_identity("creating new enrollment"))
60 .map_err(Into::into)
61 }
62
63 pub async fn e2ei_mls_init_only(
66 &self,
67 enrollment: &mut E2eiEnrollment,
68 certificate_chain: String,
69 nb_init_key_packages: Option<usize>,
70 ) -> Result<NewCrlDistributionPoints> {
71 let sk = enrollment
72 .get_sign_key_for_mls()
73 .map_err(RecursiveError::e2e_identity("creating new enrollment"))?;
74 let cs = *enrollment.ciphersuite();
75 let certificate_chain = enrollment
76 .certificate_response(
77 certificate_chain,
78 self.mls_provider()
79 .await
80 .map_err(RecursiveError::transaction("getting mls provider"))?
81 .authentication_service()
82 .borrow()
83 .await
84 .as_ref()
85 .ok_or(Error::PkiEnvironmentUnset)?,
86 )
87 .await
88 .map_err(RecursiveError::e2e_identity("getting certificate response"))?;
89
90 let crl_new_distribution_points = self.extract_dp_on_init(&certificate_chain[..]).await?;
91
92 let private_key = CertificatePrivateKey {
93 value: sk,
94 signature_scheme: cs.signature_algorithm(),
95 };
96
97 let cert_bundle = CertificateBundle {
98 certificate_chain,
99 private_key,
100 };
101 let identifier = ClientIdentifier::X509(HashMap::from([(cs.signature_algorithm(), cert_bundle)]));
102 self.mls_init(identifier, vec![cs], nb_init_key_packages)
103 .await
104 .map_err(RecursiveError::transaction("initializing mls"))?;
105 Ok(crl_new_distribution_points)
106 }
107
108 async fn extract_dp_on_init(&self, certificate_chain: &[Vec<u8>]) -> Result<NewCrlDistributionPoints> {
111 use x509_cert::der::Decode as _;
112
113 let size = certificate_chain.len();
115 let mut crl_new_distribution_points = HashSet::new();
116 if size > 1 {
117 for int in certificate_chain.iter().skip(1).rev() {
118 let mut crl_dp = self
119 .e2ei_register_intermediate_ca_der(int)
120 .await
121 .map_err(RecursiveError::transaction("registering intermediate ca der"))?;
122 if let Some(crl_dp) = crl_dp.take() {
123 crl_new_distribution_points.extend(crl_dp);
124 }
125 }
126 }
127
128 let ee = certificate_chain.first().ok_or(Error::InvalidCertificateChain)?;
129 let ee = x509_cert::Certificate::from_der(ee)
130 .map_err(crate::mls::credential::Error::DecodeX509)
131 .map_err(RecursiveError::mls_credential("decoding x509 credential"))?;
132 let mut ee_crl_dp = extract_crl_uris(&ee).map_err(RecursiveError::e2e_identity("extracting crl urls"))?;
133 if let Some(crl_dp) = ee_crl_dp.take() {
134 crl_new_distribution_points.extend(crl_dp);
135 }
136
137 get_new_crl_distribution_points(
138 &self
139 .mls_provider()
140 .await
141 .map_err(RecursiveError::transaction("getting mls provider"))?,
142 crl_new_distribution_points,
143 )
144 .await
145 .map_err(RecursiveError::mls_credential("getting new crl distribution points"))
146 .map_err(Into::into)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use crate::e2e_identity::enrollment::test_utils as e2ei_utils;
153 use crate::mls::conversation::Conversation as _;
154 use crate::test_utils::x509::X509TestChain;
155 use crate::{prelude::*, test_utils::*};
156 use wasm_bindgen_test::*;
157
158 wasm_bindgen_test_configure!(run_in_browser);
159
160 #[apply(all_cred_cipher)]
161 #[wasm_bindgen_test]
162 async fn e2e_identity_should_work(case: TestContext) {
163 use e2ei_utils::E2EI_CLIENT_ID_URI;
164
165 run_test_wo_clients(case.clone(), move |mut cc| {
166 Box::pin(async move {
167 let x509_test_chain = X509TestChain::init_empty(case.signature_scheme());
168
169 let is_renewal = false;
170
171 let (mut enrollment, cert) = e2ei_utils::e2ei_enrollment(
172 &mut cc,
173 &case,
174 &x509_test_chain,
175 Some(E2EI_CLIENT_ID_URI),
176 is_renewal,
177 e2ei_utils::init_enrollment,
178 e2ei_utils::noop_restore,
179 )
180 .await
181 .unwrap();
182
183 cc.transaction
184 .e2ei_mls_init_only(&mut enrollment, cert, Some(INITIAL_KEYING_MATERIAL_COUNT))
185 .await
186 .unwrap();
187
188 let id = conversation_id();
190 cc.transaction
191 .new_conversation(&id, MlsCredentialType::X509, case.cfg.clone())
192 .await
193 .unwrap();
194 cc.transaction
195 .conversation(&id)
196 .await
197 .unwrap()
198 .encrypt_message("Hello e2e identity !")
199 .await
200 .unwrap();
201 assert_eq!(
202 cc.transaction
203 .conversation(&id)
204 .await
205 .unwrap()
206 .e2ei_conversation_state()
207 .await
208 .unwrap(),
209 E2eiConversationState::Verified
210 );
211 assert!(cc.transaction.e2ei_is_enabled(case.signature_scheme()).await.unwrap());
212 })
213 })
214 .await
215 }
216}