openssl/
ocsp.rs

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 as c_ulong;
22        const NO_INTERN = ffi::OCSP_NOINTERN as c_ulong;
23        const NO_CHAIN = ffi::OCSP_NOCHAIN as c_ulong;
24        const NO_VERIFY = ffi::OCSP_NOVERIFY as c_ulong;
25        const NO_EXPLICIT = ffi::OCSP_NOEXPLICIT as c_ulong;
26        const NO_CA_SIGN = ffi::OCSP_NOCASIGN as c_ulong;
27        const NO_DELEGATED = ffi::OCSP_NODELEGATED as c_ulong;
28        const NO_CHECKS = ffi::OCSP_NOCHECKS as c_ulong;
29        const TRUST_OTHER = ffi::OCSP_TRUSTOTHER as c_ulong;
30        const RESPID_KEY = ffi::OCSP_RESPID_KEY as c_ulong;
31        const NO_TIME = ffi::OCSP_NOTIME as c_ulong;
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 OcspStatus<'_> {
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(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(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(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 = cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), 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}