wire_e2e_identity/acme/x509_check/
mod.rs

1use certval::PathValidationStatus;
2
3pub mod reexports {
4    pub use certval;
5}
6
7use revocation::PkiEnvironment;
8
9pub mod revocation;
10
11#[derive(Debug, thiserror::Error)]
12pub enum RustyX509CheckError {
13    /// Failed mapping a DER certificate
14    #[error(transparent)]
15    DerError(#[from] x509_cert::der::Error),
16    /// PEM de/serialization error
17    #[error("PEM en/decoding error: {0}")]
18    PemError(x509_cert::der::pem::Error),
19    /// Poisoned lock error
20    #[error("A lock has been poisoned and cannot be recovered from.")]
21    LockPoisonError,
22    /// Error for when the current UNIX epoch time cannot be determined.
23    #[error("Cannot determine current UNIX epoch")]
24    CannotDetermineCurrentTime,
25    /// Certificate / revocation validation error
26    #[error("Certificate validation error: {0}")]
27    CertValError(certval::Error),
28    /// Error when we have no idea what the cert status is
29    #[error("Something went wrong, we cannot determine if this certificate is OK. You might want to ignore this")]
30    CannotDetermineVerificationStatus,
31    /// Required 'Subject Key Identifier' extension is missing
32    #[error("Required 'Subject Key Identifier' extension is missing")]
33    MissingSki,
34    /// Implementation error
35    #[error("Implementation error")]
36    ImplementationError,
37}
38
39impl From<x509_cert::der::pem::Error> for RustyX509CheckError {
40    fn from(value: x509_cert::der::pem::Error) -> Self {
41        RustyX509CheckError::PemError(value)
42    }
43}
44
45impl From<certval::Error> for RustyX509CheckError {
46    fn from(value: certval::Error) -> Self {
47        RustyX509CheckError::CertValError(value)
48    }
49}
50
51pub type RustyX509CheckResult<T> = Result<T, RustyX509CheckError>;
52
53#[derive(Debug, Clone, Eq, PartialEq)]
54pub enum IdentityStatus {
55    /// All is fine
56    Valid,
57    /// The Certificate is expired
58    Expired,
59    /// The Certificate is revoked
60    Revoked,
61}
62
63impl IdentityStatus {
64    pub fn from_cert(cert: &x509_cert::Certificate, env: Option<&PkiEnvironment>) -> Self {
65        match env.map(|e| e.validate_cert_and_revocation(cert)) {
66            Some(Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(e)))) => match e {
67                PathValidationStatus::InvalidNotAfterDate => IdentityStatus::Expired,
68                PathValidationStatus::CertificateRevoked
69                | PathValidationStatus::CertificateRevokedEndEntity
70                | PathValidationStatus::NoPathsFound
71                | PathValidationStatus::CertificateRevokedIntermediateCa => IdentityStatus::Revoked,
72                _ => IdentityStatus::Valid,
73            },
74            _ => IdentityStatus::Valid,
75        }
76    }
77}
78
79/// Extracts the CRL Distribution points that are FullName URIs from the Certificate
80pub fn extract_crl_uris(
81    cert: &x509_cert::Certificate,
82) -> RustyX509CheckResult<Option<std::collections::HashSet<String>>> {
83    use certval::validator::{PDVCertificate, PDVExtension};
84    use x509_cert::ext::pkix::name::{DistributionPointName, GeneralName};
85
86    Ok(PDVCertificate::try_from(cert.clone())?
87        .parsed_extensions
88        .get(&const_oid::db::rfc5280::ID_CE_CRL_DISTRIBUTION_POINTS)
89        .and_then(|ext| {
90            let PDVExtension::CrlDistributionPoints(crl_distribution_points) = ext else {
91                return None;
92            };
93
94            Some(crl_distribution_points.0.iter().fold(
95                Default::default(),
96                |mut set: std::collections::HashSet<String>, dp| {
97                    if let Some(DistributionPointName::FullName(dp_full_names)) = dp.distribution_point.as_ref() {
98                        for gn in dp_full_names.iter() {
99                            if let GeneralName::UniformResourceIdentifier(uri) = gn {
100                                set.insert(uri.to_string());
101                            }
102                        }
103                    }
104
105                    set
106                },
107            ))
108        }))
109}
110
111/// Extracts the expiration date from a parsed CRL
112pub fn extract_expiration_from_crl(crl: &x509_cert::crl::CertificateList) -> Option<u64> {
113    crl.tbs_cert_list.next_update.map(|t| t.to_unix_duration().as_secs())
114}