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;
20mod serialization;
21
22pub mod identity;
23pub mod thumbprint;
24
25pub use error::Error as AcquisitionError;
26pub use serialization::ClientIdDef;
27
28#[derive(Debug, serde::Serialize, serde::Deserialize)]
29pub struct X509CredentialConfiguration {
30 pub acme_directory_url: String,
31 pub sign_alg: JwsAlgorithm,
32 pub hash_alg: HashAlgorithm,
33 pub display_name: String,
34 #[serde(with = "ClientIdDef")]
35 pub client_id: ClientId,
36 pub handle: String,
37 pub domain: String,
38 pub team: Option<String>,
39 pub validity_period: std::time::Duration,
40}
41
42pub mod states {
43 use crate::acme::{AcmeAccount, AcmeChallenge, AcmeOrder};
44
45 #[derive(Debug, serde::Serialize, serde::Deserialize)]
46 pub struct Initialized;
47
48 #[derive(Debug, serde::Serialize, serde::Deserialize)]
49 pub struct DpopChallengeCompleted {
50 pub nonce: String,
51 pub acme_account: AcmeAccount,
52 pub order: AcmeOrder,
53 pub oidc_challenge: AcmeChallenge,
54 }
55}
56
57#[derive(core_crypto_macros::Debug)]
58#[derive(serde::Serialize)]
94pub struct X509CredentialAcquisition<T: std::fmt::Debug = states::Initialized> {
95 #[serde(skip)]
97 pki_env: Arc<PkiEnvironment>,
98 config: X509CredentialConfiguration,
100 #[sensitive]
104 sign_kp: Pem,
105 #[sensitive]
108 acme_kp: Pem,
109 acme_jwk: Jwk,
111 data: T,
113}
114
115pub type Result<T> = std::result::Result<T, error::Error>;
116
117fn get_header(resp: &HttpResponse, header: &'static str) -> Result<String> {
118 resp.first_header(header)
119 .ok_or_else(|| error::Error::MissingHeader(header))
120}
121
122impl<T: std::fmt::Debug> X509CredentialAcquisition<T> {
123 pub fn sign_alg(&self) -> JwsAlgorithm {
125 self.config.sign_alg
126 }
127
128 async fn acme_request(&self, url: &Url, body: &AcmeJws) -> Result<(String, serde_json::Value)> {
132 let headers = vec![HttpHeader {
133 name: "content-type".into(),
134 value: "application/jose+json".into(),
135 }];
136 let body = serde_json::to_string(&body)?.into();
137 let response = self
138 .pki_env
139 .hooks()
140 .http_request(HttpMethod::Post, url.to_string(), headers, body)
141 .await?;
142
143 let nonce = get_header(&response, "replay-nonce")?;
144 Ok((nonce, response.json()?))
145 }
146}