1use bitflags::bitflags;
2use foreign_types::ForeignTypeRef;
3use libc::{c_int, c_long, c_ulong};
4use std::mem;
5use std::ptr;
6
7use crate::asn1::Asn1GeneralizedTimeRef;
8use crate::error::ErrorStack;
9use crate::hash::MessageDigest;
10use crate::stack::StackRef;
11use crate::util::ForeignTypeRefExt;
12use crate::x509::store::X509StoreRef;
13use crate::x509::{X509Ref, X509};
14use crate::{cvt, cvt_p};
15use openssl_macros::corresponds;
16
17bitflags! {
18 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19 #[repr(transparent)]
20 pub struct OcspFlag: c_ulong {
21 const NO_CERTS = ffi::OCSP_NOCERTS;
22 const NO_INTERN = ffi::OCSP_NOINTERN;
23 const NO_CHAIN = ffi::OCSP_NOCHAIN;
24 const NO_VERIFY = ffi::OCSP_NOVERIFY;
25 const NO_EXPLICIT = ffi::OCSP_NOEXPLICIT;
26 const NO_CA_SIGN = ffi::OCSP_NOCASIGN;
27 const NO_DELEGATED = ffi::OCSP_NODELEGATED;
28 const NO_CHECKS = ffi::OCSP_NOCHECKS;
29 const TRUST_OTHER = ffi::OCSP_TRUSTOTHER;
30 const RESPID_KEY = ffi::OCSP_RESPID_KEY;
31 const NO_TIME = ffi::OCSP_NOTIME;
32 }
33}
34
35#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36pub struct OcspResponseStatus(c_int);
37
38impl OcspResponseStatus {
39 pub const SUCCESSFUL: OcspResponseStatus =
40 OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SUCCESSFUL);
41 pub const MALFORMED_REQUEST: OcspResponseStatus =
42 OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST);
43 pub const INTERNAL_ERROR: OcspResponseStatus =
44 OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_INTERNALERROR);
45 pub const TRY_LATER: OcspResponseStatus =
46 OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_TRYLATER);
47 pub const SIG_REQUIRED: OcspResponseStatus =
48 OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SIGREQUIRED);
49 pub const UNAUTHORIZED: OcspResponseStatus =
50 OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_UNAUTHORIZED);
51
52 pub fn from_raw(raw: c_int) -> OcspResponseStatus {
53 OcspResponseStatus(raw)
54 }
55
56 #[allow(clippy::trivially_copy_pass_by_ref)]
57 pub fn as_raw(&self) -> c_int {
58 self.0
59 }
60}
61
62#[derive(Copy, Clone, Debug, PartialEq, Eq)]
63pub struct OcspCertStatus(c_int);
64
65impl OcspCertStatus {
66 pub const GOOD: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_GOOD);
67 pub const REVOKED: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_REVOKED);
68 pub const UNKNOWN: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_UNKNOWN);
69
70 pub fn from_raw(raw: c_int) -> OcspCertStatus {
71 OcspCertStatus(raw)
72 }
73
74 #[allow(clippy::trivially_copy_pass_by_ref)]
75 pub fn as_raw(&self) -> c_int {
76 self.0
77 }
78}
79
80#[derive(Copy, Clone, Debug, PartialEq, Eq)]
81pub struct OcspRevokedStatus(c_int);
82
83impl OcspRevokedStatus {
84 pub const NO_STATUS: OcspRevokedStatus = OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_NOSTATUS);
85 pub const UNSPECIFIED: OcspRevokedStatus =
86 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_UNSPECIFIED);
87 pub const KEY_COMPROMISE: OcspRevokedStatus =
88 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_KEYCOMPROMISE);
89 pub const CA_COMPROMISE: OcspRevokedStatus =
90 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CACOMPROMISE);
91 pub const AFFILIATION_CHANGED: OcspRevokedStatus =
92 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_AFFILIATIONCHANGED);
93 pub const STATUS_SUPERSEDED: OcspRevokedStatus =
94 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_SUPERSEDED);
95 pub const STATUS_CESSATION_OF_OPERATION: OcspRevokedStatus =
96 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CESSATIONOFOPERATION);
97 pub const STATUS_CERTIFICATE_HOLD: OcspRevokedStatus =
98 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CERTIFICATEHOLD);
99 pub const REMOVE_FROM_CRL: OcspRevokedStatus =
100 OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_REMOVEFROMCRL);
101
102 pub fn from_raw(raw: c_int) -> OcspRevokedStatus {
103 OcspRevokedStatus(raw)
104 }
105
106 #[allow(clippy::trivially_copy_pass_by_ref)]
107 pub fn as_raw(&self) -> c_int {
108 self.0
109 }
110}
111
112pub struct OcspStatus<'a> {
113 /// The overall status of the response.
114 pub status: OcspCertStatus,
115 /// If `status` is `CERT_STATUS_REVOKED`, the reason for the revocation.
116 pub reason: OcspRevokedStatus,
117 /// If `status` is `CERT_STATUS_REVOKED`, the time at which the certificate was revoked.
118 pub revocation_time: Option<&'a Asn1GeneralizedTimeRef>,
119 /// The time that this revocation check was performed.
120 pub this_update: &'a Asn1GeneralizedTimeRef,
121 /// The time at which this revocation check expires.
122 pub next_update: &'a Asn1GeneralizedTimeRef,
123}
124
125impl<'a> OcspStatus<'a> {
126 /// Checks validity of the `this_update` and `next_update` fields.
127 ///
128 /// The `nsec` parameter specifies an amount of slack time that will be used when comparing
129 /// those times with the current time to account for delays and clock skew.
130 ///
131 /// The `maxsec` parameter limits the maximum age of the `this_update` parameter to prohibit
132 /// very old responses.
133 #[corresponds(OCSP_check_validity)]
134 pub fn check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack> {
135 unsafe {
136 cvt(ffi::OCSP_check_validity(
137 self.this_update.as_ptr(),
138 self.next_update.as_ptr(),
139 nsec as c_long,
140 maxsec.map(|n| n as c_long).unwrap_or(-1),
141 ))
142 .map(|_| ())
143 }
144 }
145}
146
147foreign_type_and_impl_send_sync! {
148 type CType = ffi::OCSP_BASICRESP;
149 fn drop = ffi::OCSP_BASICRESP_free;
150
151 pub struct OcspBasicResponse;
152 pub struct OcspBasicResponseRef;
153}
154
155impl OcspBasicResponseRef {
156 /// Verifies the validity of the response.
157 ///
158 /// The `certs` parameter contains a set of certificates that will be searched when locating the
159 /// OCSP response signing certificate. Some responders do not include this in the response.
160 #[corresponds(OCSP_basic_verify)]
161 pub fn verify(
162 &self,
163 certs: &StackRef<X509>,
164 store: &X509StoreRef,
165 flags: OcspFlag,
166 ) -> Result<(), ErrorStack> {
167 unsafe {
168 cvt(ffi::OCSP_basic_verify(
169 self.as_ptr(),
170 certs.as_ptr(),
171 store.as_ptr(),
172 flags.bits(),
173 ))
174 .map(|_| ())
175 }
176 }
177
178 /// Looks up the status for the specified certificate ID.
179 #[corresponds(OCSP_resp_find_status)]
180 pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<OcspStatus<'a>> {
181 unsafe {
182 let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN;
183 let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS;
184 let mut revocation_time = ptr::null_mut();
185 let mut this_update = ptr::null_mut();
186 let mut next_update = ptr::null_mut();
187
188 let r = ffi::OCSP_resp_find_status(
189 self.as_ptr(),
190 id.as_ptr(),
191 &mut status,
192 &mut reason,
193 &mut revocation_time,
194 &mut this_update,
195 &mut next_update,
196 );
197 if r == 1 {
198 let revocation_time = Asn1GeneralizedTimeRef::from_const_ptr_opt(revocation_time);
199
200 Some(OcspStatus {
201 status: OcspCertStatus(status),
202 reason: OcspRevokedStatus(status),
203 revocation_time,
204 this_update: Asn1GeneralizedTimeRef::from_ptr(this_update),
205 next_update: Asn1GeneralizedTimeRef::from_ptr(next_update),
206 })
207 } else {
208 None
209 }
210 }
211 }
212}
213
214foreign_type_and_impl_send_sync! {
215 type CType = ffi::OCSP_CERTID;
216 fn drop = ffi::OCSP_CERTID_free;
217
218 pub struct OcspCertId;
219 pub struct OcspCertIdRef;
220}
221
222impl OcspCertId {
223 /// Constructs a certificate ID for certificate `subject`.
224 #[corresponds(OCSP_cert_to_id)]
225 pub fn from_cert(
226 digest: MessageDigest,
227 subject: &X509Ref,
228 issuer: &X509Ref,
229 ) -> Result<OcspCertId, ErrorStack> {
230 unsafe {
231 cvt_p(ffi::OCSP_cert_to_id(
232 digest.as_ptr(),
233 subject.as_ptr(),
234 issuer.as_ptr(),
235 ))
236 .map(op:OcspCertId)
237 }
238 }
239}
240
241foreign_type_and_impl_send_sync! {
242 type CType = ffi::OCSP_RESPONSE;
243 fn drop = ffi::OCSP_RESPONSE_free;
244
245 pub struct OcspResponse;
246 pub struct OcspResponseRef;
247}
248
249impl OcspResponse {
250 /// Creates an OCSP response from the status and optional body.
251 ///
252 /// A body should only be provided if `status` is `RESPONSE_STATUS_SUCCESSFUL`.
253 #[corresponds(OCSP_response_create)]
254 pub fn create(
255 status: OcspResponseStatus,
256 body: Option<&OcspBasicResponseRef>,
257 ) -> Result<OcspResponse, ErrorStack> {
258 unsafe {
259 ffi::init();
260
261 cvt_p(ffi::OCSP_response_create(
262 status.as_raw(),
263 body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut()),
264 ))
265 .map(OcspResponse)
266 }
267 }
268
269 from_der! {
270 /// Deserializes a DER-encoded OCSP response.
271 #[corresponds(d2i_OCSP_RESPONSE)]
272 from_der,
273 OcspResponse,
274 ffi::d2i_OCSP_RESPONSE
275 }
276}
277
278impl OcspResponseRef {
279 to_der! {
280 /// Serializes the response to its standard DER encoding.
281 #[corresponds(i2d_OCSP_RESPONSE)]
282 to_der,
283 ffi::i2d_OCSP_RESPONSE
284 }
285
286 /// Returns the status of the response.
287 #[corresponds(OCSP_response_status)]
288 pub fn status(&self) -> OcspResponseStatus {
289 unsafe { OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr())) }
290 }
291
292 /// Returns the basic response.
293 ///
294 /// This will only succeed if `status()` returns `RESPONSE_STATUS_SUCCESSFUL`.
295 #[corresponds(OCSP_response_get1_basic)]
296 pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> {
297 unsafe { cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(op:OcspBasicResponse) }
298 }
299}
300
301foreign_type_and_impl_send_sync! {
302 type CType = ffi::OCSP_REQUEST;
303 fn drop = ffi::OCSP_REQUEST_free;
304
305 pub struct OcspRequest;
306 pub struct OcspRequestRef;
307}
308
309impl OcspRequest {
310 #[corresponds(OCSP_REQUEST_new)]
311 pub fn new() -> Result<OcspRequest, ErrorStack> {
312 unsafe {
313 ffi::init();
314
315 cvt_p(ffi::OCSP_REQUEST_new()).map(op:OcspRequest)
316 }
317 }
318
319 from_der! {
320 /// Deserializes a DER-encoded OCSP request.
321 #[corresponds(d2i_OCSP_REQUEST)]
322 from_der,
323 OcspRequest,
324 ffi::d2i_OCSP_REQUEST
325 }
326}
327
328impl OcspRequestRef {
329 to_der! {
330 /// Serializes the request to its standard DER encoding.
331 #[corresponds(i2d_OCSP_REQUEST)]
332 to_der,
333 ffi::i2d_OCSP_REQUEST
334 }
335
336 #[corresponds(OCSP_request_add0_id)]
337 pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> {
338 unsafe {
339 let ptr: *mut OCSP_ONEREQ = cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id:id.as_ptr()))?;
340 mem::forget(id);
341 Ok(OcspOneReqRef::from_ptr_mut(ptr))
342 }
343 }
344}
345
346foreign_type_and_impl_send_sync! {
347 type CType = ffi::OCSP_ONEREQ;
348 fn drop = ffi::OCSP_ONEREQ_free;
349
350 pub struct OcspOneReq;
351 pub struct OcspOneReqRef;
352}
353