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