wire_e2e_identity/acquisition/
mod.rs1use std::sync::Arc;
2
3use jwt_simple::prelude::Jwk;
4use openmls_traits::types::{Ciphersuite, SignatureScheme};
5use rusty_jwt_tools::prelude::{ClientId, JwsAlgorithm, Pem};
6use url::Url;
7
8use crate::{
9 acme::AcmeJws,
10 pki_env::{
11 PkiEnvironment,
12 hooks::{HttpHeader, HttpMethod, HttpResponse},
13 },
14};
15
16mod checks;
17mod dpop_challenge;
18mod error;
19mod initial;
20mod oidc_challenge;
21mod serialization;
22
23pub mod identity;
24pub mod thumbprint;
25
26pub use error::Error as AcquisitionError;
27pub use serialization::ClientIdDef;
28
29#[derive(Debug, serde::Serialize, serde::Deserialize)]
30pub struct X509CredentialConfiguration {
31 pub acme_directory_url: String,
32 pub cipher_suite: Ciphersuite,
34 pub display_name: String,
35 #[serde(with = "ClientIdDef")]
36 pub client_id: ClientId,
37 pub handle: String,
38 pub domain: String,
39 pub team: Option<String>,
40 pub validity_period: std::time::Duration,
41}
42
43pub trait CiphersuiteExt {
45 fn jws_algorithm(&self) -> Option<JwsAlgorithm>;
47}
48
49impl CiphersuiteExt for Ciphersuite {
50 fn jws_algorithm(&self) -> Option<JwsAlgorithm> {
51 match self.signature_algorithm() {
52 SignatureScheme::ED25519 => Some(JwsAlgorithm::Ed25519),
53 SignatureScheme::ECDSA_SECP256R1_SHA256 => Some(JwsAlgorithm::P256),
54 SignatureScheme::ECDSA_SECP384R1_SHA384 => Some(JwsAlgorithm::P384),
55 SignatureScheme::ECDSA_SECP521R1_SHA512 => Some(JwsAlgorithm::P521),
56 SignatureScheme::ED448
58 | SignatureScheme::MLDSA44
60 | SignatureScheme::MLDSA65
61 | SignatureScheme::MLDSA87 => None,
62 }
63 }
64}
65
66pub mod states {
67 use crate::acme::{AcmeAccount, AcmeChallenge, AcmeOrder};
68
69 #[derive(Debug, serde::Serialize, serde::Deserialize)]
70 pub struct Initialized;
71
72 #[derive(Debug, serde::Serialize, serde::Deserialize)]
73 pub struct DpopChallengeCompleted {
74 pub nonce: String,
75 pub acme_account: AcmeAccount,
76 pub order: AcmeOrder,
77 pub oidc_challenge: AcmeChallenge,
78 }
79}
80
81#[derive(core_crypto_macros::Debug)]
82#[derive(serde::Serialize)]
118pub struct X509CredentialAcquisition<T: std::fmt::Debug = states::Initialized> {
119 #[serde(skip)]
121 pki_env: Arc<PkiEnvironment>,
122 config: X509CredentialConfiguration,
124 #[serde(skip)]
126 sign_alg: JwsAlgorithm,
127 #[sensitive]
131 sign_kp: Pem,
132 #[sensitive]
135 acme_kp: Pem,
136 acme_jwk: Jwk,
138 data: T,
140}
141
142pub type Result<T> = std::result::Result<T, error::Error>;
143
144fn get_header(resp: &HttpResponse, header: &'static str) -> Result<String> {
145 resp.first_header(header)
146 .ok_or_else(|| error::Error::MissingHeader(header))
147}
148
149impl<T: std::fmt::Debug> X509CredentialAcquisition<T> {
150 pub fn sign_alg(&self) -> JwsAlgorithm {
152 self.sign_alg
153 }
154
155 pub fn cipher_suite(&self) -> Ciphersuite {
157 self.config.cipher_suite
158 }
159
160 async fn acme_request(&self, url: &Url, body: &AcmeJws) -> Result<(String, serde_json::Value)> {
164 let headers = vec![HttpHeader {
165 name: "content-type".into(),
166 value: "application/jose+json".into(),
167 }];
168 let body = serde_json::to_string(&body)?.into();
169 let response = self
170 .pki_env
171 .hooks()
172 .http_request(HttpMethod::Post, url.to_string(), headers, body)
173 .await?;
174
175 let nonce = get_header(&response, "replay-nonce")?;
176 Ok((nonce, response.json()?))
177 }
178}