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