x509_ocsp/
basic.rs

1//! Basic OCSP Response
2
3use crate::{
4    ext::Nonce, AsResponseBytes, CertId, CertStatus, OcspGeneralizedTime, ResponderId, Version,
5};
6use alloc::vec::Vec;
7use const_oid::{
8    db::rfc6960::{ID_PKIX_OCSP_BASIC, ID_PKIX_OCSP_NONCE},
9    AssociatedOid,
10};
11use core::{default::Default, option::Option};
12use der::{
13    asn1::{BitString, ObjectIdentifier},
14    Decode, Sequence,
15};
16use spki::AlgorithmIdentifierOwned;
17use x509_cert::{certificate::Certificate, ext::Extensions};
18
19/// BasicOcspResponse structure as defined in [RFC 6960 Section 4.2.1].
20///
21/// ```text
22/// BasicOCSPResponse ::= SEQUENCE {
23///   tbsResponseData          ResponseData,
24///   signatureAlgorithm       AlgorithmIdentifier,
25///   signature                BIT STRING,
26///   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
27/// ```
28///
29/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
30#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
31#[allow(missing_docs)]
32pub struct BasicOcspResponse {
33    pub tbs_response_data: ResponseData,
34    pub signature_algorithm: AlgorithmIdentifierOwned,
35    pub signature: BitString,
36
37    #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
38    pub certs: Option<Vec<Certificate>>,
39}
40
41impl BasicOcspResponse {
42    /// Returns the response's nonce value, if any. This method will return `None` if the response
43    /// has no `Nonce` extension or decoding of the `Nonce` extension fails.
44    pub fn nonce(&self) -> Option<Nonce> {
45        self.tbs_response_data.nonce()
46    }
47}
48
49impl AssociatedOid for BasicOcspResponse {
50    const OID: ObjectIdentifier = ID_PKIX_OCSP_BASIC;
51}
52
53impl AsResponseBytes for BasicOcspResponse {}
54
55/// ResponseData structure as defined in [RFC 6960 Section 4.2.1].
56///
57/// ```text
58/// ResponseData ::= SEQUENCE {
59///    version              [0] EXPLICIT Version DEFAULT v1,
60///    responderID             ResponderID,
61///    producedAt              GeneralizedTime,
62///    responses               SEQUENCE OF SingleResponse,
63///    responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
64/// ```
65///
66/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
67#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
68#[allow(missing_docs)]
69pub struct ResponseData {
70    #[asn1(
71        context_specific = "0",
72        default = "Default::default",
73        tag_mode = "EXPLICIT"
74    )]
75    pub version: Version,
76    pub responder_id: ResponderId,
77    pub produced_at: OcspGeneralizedTime,
78    pub responses: Vec<SingleResponse>,
79
80    #[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")]
81    pub response_extensions: Option<Extensions>,
82}
83
84impl ResponseData {
85    /// Returns the response's nonce value, if any. This method will return `None` if the response
86    /// has no `Nonce` extension or decoding of the `Nonce` extension fails.
87    pub fn nonce(&self) -> Option<Nonce> {
88        match &self.response_extensions {
89            Some(extns) => {
90                let mut filter = extns.iter().filter(|e| e.extn_id == ID_PKIX_OCSP_NONCE);
91                match filter.next() {
92                    Some(extn) => Nonce::from_der(extn.extn_value.as_bytes()).ok(),
93                    None => None,
94                }
95            }
96            None => None,
97        }
98    }
99}
100
101/// SingleResponse structure as defined in [RFC 6960 Section 4.2.1].
102///
103/// ```text
104/// SingleResponse ::= SEQUENCE {
105///    certID                  CertID,
106///    certStatus              CertStatus,
107///    thisUpdate              GeneralizedTime,
108///    nextUpdate              [0] EXPLICIT GeneralizedTime OPTIONAL,
109///    singleExtensions        [1] EXPLICIT Extensions OPTIONAL }
110/// ```
111///
112/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
113#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
114#[allow(missing_docs)]
115pub struct SingleResponse {
116    pub cert_id: CertId,
117    pub cert_status: CertStatus,
118    pub this_update: OcspGeneralizedTime,
119
120    #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
121    pub next_update: Option<OcspGeneralizedTime>,
122
123    #[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")]
124    pub single_extensions: Option<Extensions>,
125}
126
127#[cfg(feature = "builder")]
128mod builder {
129    use crate::{builder::Error, CertId, CertStatus, OcspGeneralizedTime, SingleResponse};
130    use const_oid::AssociatedOid;
131    use digest::Digest;
132    use x509_cert::{
133        crl::CertificateList, ext::AsExtension, name::Name, serial_number::SerialNumber,
134        Certificate,
135    };
136
137    impl SingleResponse {
138        /// Returns a `SingleResponse` given the `CertID`, `CertStatus`, and `This Update`. `Next
139        /// Update` is set to `None`.
140        pub fn new(
141            cert_id: CertId,
142            cert_status: CertStatus,
143            this_update: OcspGeneralizedTime,
144        ) -> Self {
145            Self {
146                cert_id,
147                cert_status,
148                this_update,
149                next_update: None,
150                single_extensions: None,
151            }
152        }
153
154        /// Sets `thisUpdate` in the `singleResponse` as defined in [RFC 6960 Section 4.2.1].
155        ///
156        /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
157        pub fn with_this_update(mut self, this_update: OcspGeneralizedTime) -> Self {
158            self.this_update = this_update;
159            self
160        }
161
162        /// Sets `nextUpdate` in the `singleResponse` as defined in [RFC 6960 Section 4.2.1].
163        ///
164        /// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
165        pub fn with_next_update(mut self, next_update: OcspGeneralizedTime) -> Self {
166            self.next_update = Some(next_update);
167            self
168        }
169
170        /// Adds a single response extension as specified in [RFC 6960 Section 4.4]. Errors when the
171        /// extension encoding fails.
172        ///
173        /// [RFC 6960 Section 4.4]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.4
174        pub fn with_extension(mut self, ext: impl AsExtension) -> Result<Self, Error> {
175            let ext = ext.to_extension(&Name::default(), &[])?;
176            match self.single_extensions {
177                Some(ref mut exts) => exts.push(ext),
178                None => self.single_extensions = Some(alloc::vec![ext]),
179            }
180            Ok(self)
181        }
182
183        /// Returns a `SingleResponse` by searching through the CRL to see if `serial` is revoked. If
184        /// not, the `CertStatus` is set to good. The `CertID` is built from the issuer and serial
185        /// number. This method does not ensure the CRL is issued by the issuer and only asserts the
186        /// serial is not revoked in the provided CRL.
187        ///
188        /// `thisUpdate` and `nextUpdate` will be pulled from the CRL.
189        ///
190        /// NOTE: this method complies with [RFC 2560 Section 2.2] and not [RFC 6960 Section 2.2].
191        /// [RFC 6960] limits the `good` status to only issued certificates. [RFC 2560] only asserts
192        /// the serial was not revoked and makes no assertion the serial was ever issued.
193        ///
194        /// [RFC 2560]: https://datatracker.ietf.org/doc/html/rfc2560
195        /// [RFC 2560 Section 2.2]: https://datatracker.ietf.org/doc/html/rfc2560#section-2.2
196        /// [RFC 6960]: https://datatracker.ietf.org/doc/html/rfc6960
197        /// [RFC 6960 Section 2.2]: https://datatracker.ietf.org/doc/html/rfc6960#section-2.2
198        pub fn from_crl<D>(
199            issuer: &Certificate,
200            crl: &CertificateList,
201            serial_number: SerialNumber,
202        ) -> Result<Self, Error>
203        where
204            D: Digest + AssociatedOid,
205        {
206            let cert_status = match &crl.tbs_cert_list.revoked_certificates {
207                Some(revoked_certs) => {
208                    let mut filter = revoked_certs
209                        .iter()
210                        .filter(|rc| rc.serial_number == serial_number);
211                    match filter.next() {
212                        None => CertStatus::good(),
213                        Some(rc) => CertStatus::revoked(rc),
214                    }
215                }
216                None => CertStatus::good(),
217            };
218            let cert_id = CertId::from_issuer::<D>(issuer, serial_number)?;
219            let this_update = crl.tbs_cert_list.this_update.into();
220            let next_update = crl.tbs_cert_list.next_update.map(|t| t.into());
221            Ok(Self {
222                cert_id,
223                cert_status,
224                this_update,
225                next_update,
226                single_extensions: None,
227            })
228        }
229    }
230}