wire_e2e_identity/legacy/
mod.rs1use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite};
2use zeroize::Zeroize as _;
3
4use crate::{E2eiAcmeAuthorization, RustyE2eIdentity};
5
6pub mod crypto;
7pub mod device_status;
8pub mod id;
9pub mod types;
10
11use crypto::{E2eiSignatureKeypair, ciphersuite_to_jws_algo};
12use id::{ClientId, QualifiedE2eiClientId};
13
14use super::error::{E2eIdentityError as Error, E2eIdentityResult as Result};
15
16type Json = Vec<u8>;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct CrlRegistration {
21 pub dirty: bool,
23 pub expiration: Option<u64>,
25}
26
27#[derive(Debug, serde::Serialize, serde::Deserialize)]
29pub struct E2eiEnrollment {
30 delegate: RustyE2eIdentity,
31 pub sign_sk: E2eiSignatureKeypair,
32 pub(super) client_id: String,
33 pub(super) display_name: String,
34 pub(super) handle: String,
35 pub(super) team: Option<String>,
36 expiry: core::time::Duration,
37 directory: Option<types::E2eiAcmeDirectory>,
38 account: Option<crate::E2eiAcmeAccount>,
39 user_authz: Option<E2eiAcmeAuthorization>,
40 device_authz: Option<E2eiAcmeAuthorization>,
41 valid_order: Option<crate::E2eiAcmeOrder>,
42 finalize: Option<crate::E2eiAcmeFinalize>,
43 pub(super) ciphersuite: Ciphersuite,
44 has_called_new_oidc_challenge_request: bool,
45}
46
47impl std::ops::Deref for E2eiEnrollment {
48 type Target = RustyE2eIdentity;
49
50 fn deref(&self) -> &Self::Target {
51 &self.delegate
52 }
53}
54
55impl E2eiEnrollment {
56 #[allow(clippy::too_many_arguments)]
65 pub fn try_new<T>(
66 client_id: ClientId,
67 display_name: String,
68 handle: String,
69 team: Option<String>,
70 expiry_sec: u32,
71 ciphersuite: Ciphersuite,
72 has_called_new_oidc_challenge_request: bool,
73 crypto: &impl OpenMlsCrypto,
74 ) -> Result<Self> {
75 let alg = ciphersuite_to_jws_algo(ciphersuite)?;
76 let sign_sk = Self::new_sign_key(crypto, ciphersuite)?;
77
78 let client_id = QualifiedE2eiClientId::try_from(client_id.as_slice())?;
79 let client_id = String::try_from(client_id)?;
80 let expiry = core::time::Duration::from_secs(u64::from(expiry_sec));
81 let delegate = RustyE2eIdentity::try_new(alg, sign_sk.clone())?;
82 Ok(Self {
83 delegate,
84 sign_sk,
85 client_id,
86 display_name,
87 handle,
88 team,
89 expiry,
90 directory: None,
91 account: None,
92 user_authz: None,
93 device_authz: None,
94 valid_order: None,
95 finalize: None,
96 ciphersuite,
97 has_called_new_oidc_challenge_request,
98 })
99 }
100
101 pub(crate) fn new_sign_key(crypto: &impl OpenMlsCrypto, ciphersuite: Ciphersuite) -> Result<E2eiSignatureKeypair> {
102 let (sk, _) = crypto.signature_key_gen(ciphersuite.signature_algorithm())?;
103 E2eiSignatureKeypair::try_new(ciphersuite.signature_algorithm(), sk)
104 }
105
106 pub fn ciphersuite(&self) -> &Ciphersuite {
107 &self.ciphersuite
108 }
109
110 pub fn directory_response(&mut self, directory: Json) -> Result<types::E2eiAcmeDirectory> {
119 let directory = serde_json::from_slice(&directory[..])?;
120 let directory: types::E2eiAcmeDirectory = self.acme_directory_response(directory)?.into();
121 self.directory = Some(directory.clone());
122 Ok(directory)
123 }
124
125 pub fn new_account_request(&self, previous_nonce: String) -> Result<Json> {
134 let directory = self
135 .directory
136 .as_ref()
137 .ok_or(Error::OutOfOrderEnrollment("You must first call 'directoryResponse()'"))?;
138 let account = self.acme_new_account_request(&directory.try_into()?, previous_nonce)?;
139 let account = serde_json::to_vec(&account)?;
140 Ok(account)
141 }
142
143 pub fn new_account_response(&mut self, account: Json) -> Result<()> {
150 let account = serde_json::from_slice(&account[..])?;
151 let account = self.acme_new_account_response(account)?;
152 self.account = Some(account);
153 Ok(())
154 }
155
156 pub fn new_order_request(&self, previous_nonce: String) -> Result<Json> {
163 let directory = self
164 .directory
165 .as_ref()
166 .ok_or(Error::OutOfOrderEnrollment("You must first call 'directoryResponse()'"))?;
167 let account = self.account.as_ref().ok_or(Error::OutOfOrderEnrollment(
168 "You must first call 'newAccountResponse()'",
169 ))?;
170 let order = self.acme_new_order_request(
171 &self.display_name,
172 &self.client_id,
173 &self.handle,
174 self.expiry,
175 &directory.try_into()?,
176 account,
177 previous_nonce,
178 )?;
179 let order = serde_json::to_vec(&order)?;
180 Ok(order)
181 }
182
183 pub fn new_order_response(&self, order: Json) -> Result<types::E2eiNewAcmeOrder> {
190 let order = serde_json::from_slice(&order[..])?;
191 self.acme_new_order_response(order)?.try_into()
192 }
193
194 pub fn new_authz_request(&self, url: String, previous_nonce: String) -> Result<Json> {
204 let account = self.account.as_ref().ok_or(Error::OutOfOrderEnrollment(
205 "You must first call 'newAccountResponse()'",
206 ))?;
207 let authz = self.acme_new_authz_request(&url.parse()?, account, previous_nonce)?;
208 let authz = serde_json::to_vec(&authz)?;
209 Ok(authz)
210 }
211
212 pub fn new_authz_response(&mut self, authz: Json) -> Result<types::E2eiNewAcmeAuthz> {
219 let authz = serde_json::from_slice(&authz[..])?;
220 let authz = self.acme_new_authz_response(authz)?;
221 match &authz {
222 E2eiAcmeAuthorization::User { .. } => self.user_authz = Some(authz.clone()),
223 E2eiAcmeAuthorization::Device { .. } => self.device_authz = Some(authz.clone()),
224 };
225 authz.try_into()
226 }
227
228 #[allow(clippy::too_many_arguments)]
241 pub fn create_dpop_token(&self, expiry_secs: u32, backend_nonce: String) -> Result<String> {
242 let expiry = core::time::Duration::from_secs(expiry_secs as u64);
243 let authz = self
244 .device_authz
245 .as_ref()
246 .ok_or(Error::OutOfOrderEnrollment("You must first call 'newAuthzResponse()'"))?;
247 let challenge = match authz {
248 E2eiAcmeAuthorization::Device { challenge, .. } => challenge,
249 E2eiAcmeAuthorization::User { .. } => return Err(Error::ImplementationError),
250 };
251 self.new_dpop_token(
252 &self.client_id,
253 self.display_name.as_str(),
254 challenge,
255 backend_nonce,
256 self.handle.as_str(),
257 self.team.clone(),
258 expiry,
259 )
260 }
261
262 pub fn new_dpop_challenge_request(&self, access_token: String, previous_nonce: String) -> Result<Json> {
272 let authz = self
273 .device_authz
274 .as_ref()
275 .ok_or(Error::OutOfOrderEnrollment("You must first call 'newAuthzResponse()'"))?;
276 let challenge = match authz {
277 E2eiAcmeAuthorization::Device { challenge, .. } => challenge,
278 E2eiAcmeAuthorization::User { .. } => return Err(Error::ImplementationError),
279 };
280 let account = self.account.as_ref().ok_or(Error::OutOfOrderEnrollment(
281 "You must first call 'newAccountResponse()'",
282 ))?;
283 let challenge = self.acme_dpop_challenge_request(access_token, challenge, account, previous_nonce)?;
284 let challenge = serde_json::to_vec(&challenge)?;
285 Ok(challenge)
286 }
287
288 pub fn new_dpop_challenge_response(&self, challenge: Json) -> Result<()> {
295 let challenge = serde_json::from_slice(&challenge[..])?;
296 self.acme_new_challenge_response(challenge)
297 }
298
299 pub fn new_oidc_challenge_request(&mut self, id_token: String, previous_nonce: String) -> Result<Json> {
309 let authz = self
310 .user_authz
311 .as_ref()
312 .ok_or(Error::OutOfOrderEnrollment("You must first call 'newAuthzResponse()'"))?;
313 let challenge = match authz {
314 E2eiAcmeAuthorization::User { challenge, .. } => challenge,
315 E2eiAcmeAuthorization::Device { .. } => return Err(Error::ImplementationError),
316 };
317 let account = self.account.as_ref().ok_or(Error::OutOfOrderEnrollment(
318 "You must first call 'newAccountResponse()'",
319 ))?;
320 let challenge = self.acme_oidc_challenge_request(id_token, challenge, account, previous_nonce)?;
321 let challenge = serde_json::to_vec(&challenge)?;
322
323 self.has_called_new_oidc_challenge_request = true;
324
325 Ok(challenge)
326 }
327
328 pub fn new_oidc_challenge_response(&mut self, challenge: Json) -> Result<()> {
335 let challenge = serde_json::from_slice(&challenge[..])?;
336 self.acme_new_challenge_response(challenge)?;
337
338 if !self.has_called_new_oidc_challenge_request {
339 return Err(Error::OutOfOrderEnrollment(
340 "You must first call 'new_oidc_challenge_request()'",
341 ));
342 }
343
344 Ok(())
345 }
346
347 pub fn check_order_request(&self, order_url: String, previous_nonce: String) -> Result<Json> {
357 let account = self.account.as_ref().ok_or(Error::OutOfOrderEnrollment(
358 "You must first call 'newAccountResponse()'",
359 ))?;
360 let order = self.acme_check_order_request(order_url.parse()?, account, previous_nonce)?;
361 let order = serde_json::to_vec(&order)?;
362 Ok(order)
363 }
364
365 pub fn check_order_response(&mut self, order: Json) -> Result<String> {
375 let order = serde_json::from_slice(&order[..])?;
376 let valid_order = self.acme_check_order_response(order)?;
377 let finalize_url = valid_order.finalize_url.to_string();
378 self.valid_order = Some(valid_order);
379 Ok(finalize_url)
380 }
381
382 pub fn finalize_request(&mut self, previous_nonce: String) -> Result<Json> {
392 let account = self.account.as_ref().ok_or(Error::OutOfOrderEnrollment(
393 "You must first call 'newAccountResponse()'",
394 ))?;
395 let order = self.valid_order.as_ref().ok_or(Error::OutOfOrderEnrollment(
396 "You must first call 'checkOrderResponse()'",
397 ))?;
398 let finalize = self.acme_finalize_request(order, account, previous_nonce)?;
399 let finalize = serde_json::to_vec(&finalize)?;
400 Ok(finalize)
401 }
402
403 pub fn finalize_response(&mut self, finalize: Json) -> Result<String> {
413 let finalize = serde_json::from_slice(&finalize[..])?;
414 let finalize = self.acme_finalize_response(finalize)?;
415 let certificate_url = finalize.certificate_url.to_string();
416 self.finalize = Some(finalize);
417 Ok(certificate_url)
418 }
419
420 pub fn certificate_request(&mut self, previous_nonce: String) -> Result<Json> {
430 let account = self.account.take().ok_or(Error::OutOfOrderEnrollment(
431 "You must first call 'newAccountResponse()'",
432 ))?;
433 let finalize = self
434 .finalize
435 .take()
436 .ok_or(Error::OutOfOrderEnrollment("You must first call 'finalizeResponse()'"))?;
437 let certificate = self.acme_x509_certificate_request(finalize, account, previous_nonce)?;
438 let certificate = serde_json::to_vec(&certificate)?;
439 Ok(certificate)
440 }
441
442 pub async fn certificate_response(
443 &mut self,
444 certificate_chain: String,
445 env: &crate::x509_check::revocation::PkiEnvironment,
446 ) -> Result<Vec<Vec<u8>>> {
447 let order = self.valid_order.take().ok_or(Error::OutOfOrderEnrollment(
448 "You must first call 'checkOrderResponse()'",
449 ))?;
450 let certificates = self.acme_x509_certificate_response(certificate_chain, order, Some(env))?;
451
452 self.sign_sk.zeroize();
454 self.delegate.sign_kp.zeroize();
455 self.delegate.acme_kp.zeroize();
456
457 Ok(certificates)
458 }
459}