wire_e2e_identity/acquisition/
mod.rs1use std::sync::Arc;
2
3use openmls_traits::types::{Ciphersuite, SignatureScheme};
4use rusty_jwt_tools::{
5 jsonwebtoken::jwk::Jwk,
6 prelude::{ClientId, JwsAlgorithm, Pem},
7};
8use url::Url;
9
10use crate::{
11 acme::AcmeJws,
12 pki_env::{
13 PkiEnvironment,
14 hooks::{HttpHeader, HttpMethod, HttpResponse},
15 },
16};
17
18mod checks;
19mod dpop_challenge;
20mod error;
21mod initial;
22mod oidc_challenge;
23mod serialization;
24
25pub mod identity;
26pub mod thumbprint;
27
28pub use error::{CertificateError, Error as AcquisitionError};
29pub use serialization::ClientIdDef;
30
31#[derive(Debug, serde::Serialize, serde::Deserialize)]
32pub struct X509CredentialConfiguration {
33 pub acme_directory_url: String,
34 pub cipher_suite: Ciphersuite,
36 pub display_name: String,
37 #[serde(with = "ClientIdDef")]
38 pub client_id: ClientId,
39 pub handle: String,
40 pub domain: String,
41 pub team: Option<String>,
42 pub validity_period: std::time::Duration,
43}
44
45pub trait CiphersuiteExt {
47 fn jws_algorithm(&self) -> Option<JwsAlgorithm>;
49}
50
51impl CiphersuiteExt for Ciphersuite {
52 fn jws_algorithm(&self) -> Option<JwsAlgorithm> {
53 self.signature_algorithm().jws_algorithm()
54 }
55}
56
57pub trait SignatureSchemeExt {
59 fn jws_algorithm(&self) -> Option<JwsAlgorithm>;
61}
62
63impl SignatureSchemeExt for SignatureScheme {
64 fn jws_algorithm(&self) -> Option<JwsAlgorithm> {
65 match self {
66 SignatureScheme::ED25519 => Some(JwsAlgorithm::EdDSA),
67 SignatureScheme::ECDSA_SECP256R1_SHA256 => Some(JwsAlgorithm::ES256),
68 SignatureScheme::ECDSA_SECP384R1_SHA384 => Some(JwsAlgorithm::ES384),
69 SignatureScheme::ECDSA_SECP521R1_SHA512 => Some(JwsAlgorithm::ES512),
70 SignatureScheme::ED448
72 | SignatureScheme::MLDSA44
74 | SignatureScheme::MLDSA65
75 | SignatureScheme::MLDSA87 => None,
76 }
77 }
78}
79
80pub mod states {
81 use crate::acme::{AcmeAccount, AcmeChallenge, AcmeOrder};
82
83 #[derive(Debug, serde::Serialize, serde::Deserialize)]
84 pub struct Initialized;
85
86 #[derive(Debug, serde::Serialize, serde::Deserialize)]
87 pub struct DpopChallengeCompleted {
88 pub nonce: String,
89 pub acme_account: AcmeAccount,
90 pub order: AcmeOrder,
91 pub oidc_challenge: AcmeChallenge,
92 }
93}
94
95#[derive(core_crypto_macros::Debug)]
96#[derive(serde::Serialize)]
132pub struct X509CredentialAcquisition<T: std::fmt::Debug = states::Initialized> {
133 #[serde(skip)]
135 pki_env: Arc<PkiEnvironment>,
136 config: X509CredentialConfiguration,
138 #[serde(skip)]
140 sign_alg: JwsAlgorithm,
141 #[sensitive]
145 sign_kp: Pem,
146 #[sensitive]
149 acme_kp: Pem,
150 acme_jwk: Jwk,
152 data: T,
154}
155
156pub type Result<T> = std::result::Result<T, error::Error>;
157
158fn get_header(resp: &HttpResponse, header: &'static str) -> Result<String> {
159 resp.first_header(header)
160 .ok_or_else(|| error::Error::MissingHeader(header))
161}
162
163impl<T: std::fmt::Debug> X509CredentialAcquisition<T> {
164 pub fn sign_alg(&self) -> JwsAlgorithm {
166 self.sign_alg
167 }
168
169 pub fn cipher_suite(&self) -> Ciphersuite {
171 self.config.cipher_suite
172 }
173
174 async fn acme_request(&self, url: &Url, body: &AcmeJws) -> Result<(String, serde_json::Value)> {
178 let headers = vec![HttpHeader {
179 name: "content-type".into(),
180 value: "application/jose+json".into(),
181 }];
182 let body = serde_json::to_string(&body)?.into();
183 let response = self
184 .pki_env
185 .hooks()
186 .http_request(HttpMethod::Post, url.to_string(), headers, body)
187 .await?;
188
189 let nonce = get_header(&response, "replay-nonce")?;
190 Ok((nonce, response.json()?))
191 }
192}