Skip to main content

wire_e2e_identity/acquisition/
oidc_challenge.rs

1use obfuscate::Obfuscated;
2use rusty_jwt_tools::{
3    jwk_thumbprint::JwkThumbprint,
4    prelude::{HashAlgorithm, Pem},
5};
6use x509_cert::Certificate;
7
8use super::{Result, X509CredentialAcquisition, states};
9use crate::{
10    acme,
11    pki_env::hooks::{HttpHeader, HttpMethod},
12};
13
14impl X509CredentialAcquisition<states::DpopChallengeCompleted> {
15    /// Complete the OIDC challenge and get the certificate chain.
16    ///
17    /// Returns (signing keypair in PEM format, certificate chain).
18    /// The first certificate in the chain is the end-entity certificate,
19    /// i.e. the one certifying the public portion of the signing keypair.
20    pub async fn complete_oidc_challenge(self) -> Result<(Pem, Vec<Certificate>)> {
21        let hooks = self.pki_env.hooks();
22
23        // Complete the OIDC challenge.
24        let oidc_challenge_token = &self.data.oidc_challenge.token;
25        // RFC 8555 section 8.1 mandates SHA-256 for the key authorization thumbprint.
26        let thumbprint = JwkThumbprint::generate(&self.acme_jwk, HashAlgorithm::SHA256)?.kid;
27        let key_auth = format!("{oidc_challenge_token}.{thumbprint}");
28        let snapshot = serde_json::to_vec(&self)?;
29
30        let url = &self.data.oidc_challenge.url;
31        let target = self.data.oidc_challenge.target.to_string();
32        let id_token = hooks.authenticate(target, key_auth, url.to_string(), snapshot).await?;
33        log::debug!(
34            "acquisition({:?}): got the ID token from the OIDC server",
35            Obfuscated::from(&self.sign_kp),
36        );
37
38        let oidc_challenge_request = acme::oidc_chall_request(
39            id_token,
40            &self.data.oidc_challenge,
41            &self.data.acme_account,
42            self.sign_alg,
43            &self.acme_kp,
44            self.data.nonce.clone(),
45        )?;
46        let (nonce, response) = self.acme_request(url, &oidc_challenge_request).await?;
47        let _ = acme::new_chall_response(response)?;
48        log::info!(
49            "acquisition({:?}): OIDC challenge completed",
50            Obfuscated::from(&self.sign_kp),
51        );
52
53        // Finalize the order. This generates a CSR (Certificate Signing Request) and
54        // sends it to the ACME server.
55        let finalize_request = acme::finalize_req(
56            &self.data.order,
57            &self.data.acme_account,
58            self.sign_alg,
59            &self.acme_kp,
60            &self.sign_kp,
61            nonce,
62        )?;
63        let (nonce, response) = self.acme_request(&self.data.order.finalize, &finalize_request).await?;
64        let finalize = acme::finalize_response(response)?;
65        log::debug!(
66            "acquisition({:?}): ACME order finalized",
67            Obfuscated::from(&self.sign_kp),
68        );
69
70        // Get the certificate chain.
71        //
72        // See [RFC 8555 Section 7.4.2](https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2).
73        let certificate_request =
74            acme::certificate_req(&finalize, &self.data.acme_account, self.sign_alg, &self.acme_kp, nonce)?;
75        let headers = vec![HttpHeader {
76            name: "content-type".into(),
77            value: "application/jose+json".into(),
78        }];
79        let body = serde_json::to_string(&certificate_request)?.into();
80        let response = hooks
81            .http_request(HttpMethod::Post, finalize.certificate.to_string(), headers, body)
82            .await?;
83        let response = String::from_utf8(response.body).map_err(|e| acme::Error::from(e.utf8_error()))?;
84        let certificates = acme::certificate_response(response, &self.data.order)?;
85        log::debug!(
86            "acquisition({:?}): got the certificate",
87            Obfuscated::from(&self.sign_kp),
88        );
89
90        self.verify_cert_chain(&certificates).await?;
91        log::info!(
92            "acquisition({:?}): certificate verified successfully",
93            Obfuscated::from(&self.sign_kp),
94        );
95
96        Ok((self.sign_kp, certificates))
97    }
98}