wire_e2e_identity/acquisition/
mod.rs1use std::sync::Arc;
2
3use jwt_simple::prelude::Jwk;
4use rusty_jwt_tools::prelude::{ClientId, HashAlgorithm, JwsAlgorithm, Pem};
5use url::Url;
6
7use crate::{
8 acme::AcmeJws,
9 pki_env::{
10 PkiEnvironment,
11 hooks::{HttpHeader, HttpMethod, HttpResponse},
12 },
13};
14
15mod checks;
16mod dpop_challenge;
17mod error;
18mod initial;
19mod oidc_challenge;
20
21pub mod identity;
22pub mod thumbprint;
23
24#[derive(Debug)]
25pub struct X509CredentialConfiguration {
26 pub acme_url: String,
27 pub idp_url: String,
28 pub sign_alg: JwsAlgorithm,
29 pub hash_alg: HashAlgorithm,
30 pub display_name: String,
31 pub client_id: ClientId,
32 pub handle: String,
33 pub domain: String,
34 pub team: Option<String>,
35 pub validity_period: std::time::Duration,
36}
37
38pub mod states {
39 use crate::acme::{AcmeAccount, AcmeChallenge, AcmeOrder};
40
41 #[derive(Debug)]
42 pub struct Initialized;
43
44 #[derive(Debug)]
45 pub struct DpopChallengeCompleted {
46 pub nonce: String,
47 pub acme_account: AcmeAccount,
48 pub order: AcmeOrder,
49 pub oidc_challenge: AcmeChallenge,
50 }
51}
52
53#[derive(core_crypto_macros::Debug)]
54pub struct X509CredentialAcquisition<T: std::fmt::Debug = states::Initialized> {
90 pki_env: Arc<PkiEnvironment>,
92 config: X509CredentialConfiguration,
94 #[sensitive]
98 sign_kp: Pem,
99 #[sensitive]
102 acme_kp: Pem,
103 acme_jwk: Jwk,
105 data: T,
107}
108
109pub type Result<T> = std::result::Result<T, error::Error>;
110
111fn get_header(resp: &HttpResponse, header: &'static str) -> Result<String> {
112 resp.first_header(header)
113 .ok_or_else(|| error::Error::MissingHeader(header))
114}
115
116impl<T: std::fmt::Debug> X509CredentialAcquisition<T> {
117 async fn acme_request(&self, url: &Url, body: &AcmeJws) -> Result<(String, serde_json::Value)> {
121 let headers = vec![HttpHeader {
122 name: "content-type".into(),
123 value: "application/jose+json".into(),
124 }];
125 let body = serde_json::to_string(&body)?.into();
126 let response = self
127 .pki_env
128 .hooks()
129 .http_request(HttpMethod::Post, url.to_string(), headers, body)
130 .await?;
131
132 let nonce = get_header(&response, "replay-nonce")?;
133 Ok((nonce, response.json()?))
134 }
135
136 fn acme_url(&self, path: &str) -> Url {
137 format!("https://{}/acme/wire/{path}", self.config.acme_url)
138 .parse()
139 .expect("valid URL")
140 }
141}