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}