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::PkiEnvironment,
10 pki_env_hooks::{HttpHeader, HttpMethod, HttpResponse},
11};
12
13mod dpop_challenge;
14mod error;
15mod initial;
16mod oidc_challenge;
17
18#[derive(Debug)]
19pub struct X509CredentialConfiguration {
20 pub acme_url: String,
21 pub idp_url: String,
22 pub sign_alg: JwsAlgorithm,
23 pub hash_alg: HashAlgorithm,
24 pub display_name: String,
25 pub client_id: ClientId,
26 pub handle: String,
27 pub team: Option<String>,
28 pub validity_period: std::time::Duration,
29}
30
31pub mod states {
32 use crate::acme::{AcmeAccount, AcmeChallenge, AcmeOrder};
33
34 #[derive(Debug)]
35 pub struct Initialized;
36
37 #[derive(Debug)]
38 pub struct DpopChallengeCompleted {
39 pub nonce: String,
40 pub acme_account: AcmeAccount,
41 pub order: AcmeOrder,
42 pub oidc_challenge: AcmeChallenge,
43 }
44}
45
46#[derive(core_crypto_macros::Debug)]
47pub struct X509CredentialAcquisition<T: std::fmt::Debug = states::Initialized> {
83 pki_env: Arc<PkiEnvironment>,
85 config: X509CredentialConfiguration,
87 #[sensitive]
91 sign_kp: Pem,
92 #[sensitive]
95 acme_kp: Pem,
96 acme_jwk: Jwk,
98 data: T,
100}
101
102pub type Result<T> = std::result::Result<T, error::Error>;
103
104fn get_header(resp: &HttpResponse, header: &'static str) -> Result<String> {
105 resp.first_header(header)
106 .ok_or_else(|| error::Error::MissingHeader(header))
107}
108
109impl<T: std::fmt::Debug> X509CredentialAcquisition<T> {
110 async fn acme_request(&self, url: &Url, body: &AcmeJws) -> Result<(String, serde_json::Value)> {
114 let headers = vec![HttpHeader {
115 name: "content-type".into(),
116 value: "application/jose+json".into(),
117 }];
118 let body = serde_json::to_string(&body)?.into();
119 let response = self
120 .pki_env
121 .hooks()
122 .http_request(HttpMethod::Post, url.to_string(), headers, body)
123 .await?;
124
125 let nonce = get_header(&response, "replay-nonce")?;
126 Ok((nonce, response.json()?))
127 }
128
129 fn acme_url(&self, path: &str) -> Url {
130 format!("https://{}/acme/wire/{path}", self.config.acme_url)
131 .parse()
132 .expect("valid URL")
133 }
134}