wire_e2e_identity/acme/
certificate.rs1use rusty_jwt_tools::prelude::*;
2use x509_cert::{Certificate, anchor::TrustAnchorChoice};
3
4use crate::acme::{
5 error::CertificateError,
6 identifier::CanonicalIdentifier,
7 prelude::*,
8 x509_check::revocation::{PkiEnvironment, PkiEnvironmentParams},
9};
10
11impl RustyAcme {
12 pub fn certificate_req(
15 finalize: AcmeFinalize,
16 account: AcmeAccount,
17 alg: JwsAlgorithm,
18 kp: &Pem,
19 previous_nonce: String,
20 ) -> RustyAcmeResult<AcmeJws> {
21 let acct_url = account.acct_url()?;
23
24 let payload = None::<serde_json::Value>;
26 let req = AcmeJws::new(alg, previous_nonce, &finalize.certificate, Some(&acct_url), payload, kp)?;
27 Ok(req)
28 }
29
30 pub fn certificate_response(
32 response: String,
33 order: AcmeOrder,
34 hash_alg: HashAlgorithm,
35 env: Option<&PkiEnvironment>,
36 ) -> RustyAcmeResult<Vec<Vec<u8>>> {
37 order.verify()?;
38 let pems: Vec<pem::Pem> = pem::parse_many(response)?;
39 let intermediates = Self::extract_intermediates(&pems)?;
40 let env = env.and_then(|env| {
41 let trust_anchors = env.get_trust_anchors().unwrap_or_default();
42 let trust_roots: Vec<TrustAnchorChoice> = trust_anchors.iter().map(|f| f.decoded_ta.clone()).collect();
43 PkiEnvironment::init(PkiEnvironmentParams {
44 trust_roots: trust_roots.as_slice(),
45 intermediates: intermediates.as_slice(),
46 crls: &[],
47 time_of_interest: None,
48 })
49 .ok()
50 });
51
52 pems.into_iter()
53 .enumerate()
54 .try_fold(vec![], move |mut acc, (i, cert_pem)| -> RustyAcmeResult<Vec<Vec<u8>>> {
55 if cert_pem.tag() != "CERTIFICATE" {
57 return Err(RustyAcmeError::SmallstepImplementationError(
58 "Something other than x509 certificates was returned by the ACME server",
59 ));
60 }
61 use x509_cert::der::Decode as _;
62 let cert = x509_cert::Certificate::from_der(cert_pem.contents())?;
63
64 PkiEnvironment::extract_ski_aki_from_cert(&cert)?;
65
66 if i == 0 {
68 Self::verify_leaf_certificate(cert, &order.try_get_coalesce_identifier()?, hash_alg, env.as_ref())?;
69 }
70 acc.push(cert_pem.contents().to_vec());
71 Ok(acc)
72 })
73 }
74
75 fn extract_intermediates(pems: &[pem::Pem]) -> RustyAcmeResult<Vec<Certificate>> {
76 use x509_cert::der::Decode as _;
77 pems.iter()
78 .skip(1)
79 .try_fold(vec![], |mut acc, pem| -> RustyAcmeResult<Vec<Certificate>> {
80 let cert = x509_cert::Certificate::from_der(pem.contents())?;
81 acc.push(cert);
82 Ok(acc)
83 })
84 }
85
86 fn verify_leaf_certificate(
89 cert: Certificate,
90 identifier: &CanonicalIdentifier,
91 hash_alg: HashAlgorithm,
92 env: Option<&PkiEnvironment>,
93 ) -> RustyAcmeResult<()> {
94 if let Some(env) = env {
95 env.validate_cert(&cert)?;
96 }
97
98 let cert_identity = cert.extract_identity(env, hash_alg)?;
100
101 let invalid_client_id =
102 ClientId::try_from_qualified(&cert_identity.client_id)? != ClientId::try_from_uri(&identifier.client_id)?;
103 if invalid_client_id {
104 return Err(CertificateError::ClientIdMismatch)?;
105 }
106
107 let invalid_display_name = cert_identity.display_name != identifier.display_name;
108 if invalid_display_name {
109 return Err(CertificateError::DisplayNameMismatch)?;
110 }
111
112 let invalid_handle = cert_identity.handle != identifier.handle;
113 if invalid_handle {
114 return Err(CertificateError::HandleMismatch)?;
115 }
116
117 let invalid_domain = cert_identity.domain != identifier.domain;
118 if invalid_domain {
119 return Err(CertificateError::DomainMismatch)?;
120 }
121 Ok(())
122 }
123}