x509_ocsp/
cert_status.rs

1//! X.509 OCSP CertStatus
2
3use crate::OcspGeneralizedTime;
4use const_oid::AssociatedOid;
5use core::option::Option;
6use der::{asn1::Null, Choice, Decode, Sequence};
7use x509_cert::{crl::RevokedCert, ext::pkix::CrlReason};
8
9/// CertStatus structure as defined in [RFC 6960 Section 4.2.1].
10///
11/// ```text
12/// CertStatus ::= CHOICE {
13///    good                [0] IMPLICIT NULL,
14///    revoked             [1] IMPLICIT RevokedInfo,
15///    unknown             [2] IMPLICIT UnknownInfo }
16/// ```
17///
18/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
19#[derive(Copy, Clone, Debug, Eq, PartialEq, Choice)]
20#[allow(missing_docs)]
21pub enum CertStatus {
22    #[asn1(context_specific = "0", tag_mode = "IMPLICIT")]
23    Good(Null),
24
25    #[asn1(context_specific = "1", tag_mode = "IMPLICIT", constructed = "true")]
26    Revoked(RevokedInfo),
27
28    #[asn1(context_specific = "2", tag_mode = "IMPLICIT")]
29    Unknown(UnknownInfo),
30}
31
32impl CertStatus {
33    /// Returns `CertStatus` set to `good` as defined in [RFC 6960 Section 4.2.1]
34    ///
35    /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
36    pub fn good() -> Self {
37        Self::Good(Null)
38    }
39
40    /// Returns `CertStatus` set to `revoked` as defined in [RFC 6960 Section 4.2.1]
41    ///
42    /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
43    pub fn revoked(info: impl Into<RevokedInfo>) -> Self {
44        Self::Revoked(info.into())
45    }
46
47    /// Returns `CertStatus` set to `unknown` as defined in [RFC 6960 Section 4.2.1]
48    ///
49    /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
50    pub fn unknown() -> Self {
51        Self::Unknown(Null)
52    }
53}
54
55/// RevokedInfo structure as defined in [RFC 6960 Section 4.2.1].
56///
57/// ```text
58/// RevokedInfo ::= SEQUENCE {
59///    revocationTime          GeneralizedTime,
60///    revocationReason        [0] EXPLICIT CRLReason OPTIONAL }
61/// ```
62///
63/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
64#[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence)]
65#[allow(missing_docs)]
66pub struct RevokedInfo {
67    pub revocation_time: OcspGeneralizedTime,
68
69    #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
70    pub revocation_reason: Option<CrlReason>,
71}
72
73/// RevokedInfo structure as defined in [RFC 6960 Section 4.2.1].
74///
75/// ```text
76/// UnknownInfo ::= NULL
77/// ```
78///
79/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
80pub type UnknownInfo = Null;
81
82impl From<&RevokedInfo> for RevokedInfo {
83    fn from(info: &RevokedInfo) -> Self {
84        *info
85    }
86}
87
88impl From<&RevokedCert> for RevokedInfo {
89    /// Converts [`RevokedCert`] to [`RevokedInfo`].
90    ///
91    /// Attempts to extract the [`CrlReason`]. If it fails, the `CrlReason` is set to `None`.
92    fn from(rc: &RevokedCert) -> Self {
93        Self {
94            revocation_time: rc.revocation_date.into(),
95            revocation_reason: match &rc.crl_entry_extensions {
96                Some(extns) => {
97                    let mut filter = extns.iter().filter(|extn| extn.extn_id == CrlReason::OID);
98                    match filter.next() {
99                        Some(extn) => CrlReason::from_der(extn.extn_value.as_bytes()).ok(),
100                        None => None,
101                    }
102                }
103                None => None,
104            },
105        }
106    }
107}
108
109impl From<RevokedCert> for RevokedInfo {
110    /// Converts [`RevokedCert`] to [`RevokedInfo`].
111    ///
112    /// Attempts to extract the [`CrlReason`]. If it fails, the `CrlReason` is set to `None`.
113    fn from(rc: RevokedCert) -> Self {
114        Self::from(&rc)
115    }
116}