Coverage Report

Created: 2026-03-14 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/openssl-0.10.62/src/ocsp.rs
Line
Count
Source
1
use bitflags::bitflags;
2
use foreign_types::ForeignTypeRef;
3
use libc::{c_int, c_long, c_ulong};
4
use std::mem;
5
use std::ptr;
6
7
use crate::asn1::Asn1GeneralizedTimeRef;
8
use crate::error::ErrorStack;
9
use crate::hash::MessageDigest;
10
use crate::stack::StackRef;
11
use crate::util::ForeignTypeRefExt;
12
use crate::x509::store::X509StoreRef;
13
use crate::x509::{X509Ref, X509};
14
use crate::{cvt, cvt_p};
15
use openssl_macros::corresponds;
16
17
bitflags! {
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)]
36
pub struct OcspResponseStatus(c_int);
37
38
impl 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
0
    pub fn from_raw(raw: c_int) -> OcspResponseStatus {
53
0
        OcspResponseStatus(raw)
54
0
    }
55
56
    #[allow(clippy::trivially_copy_pass_by_ref)]
57
0
    pub fn as_raw(&self) -> c_int {
58
0
        self.0
59
0
    }
60
}
61
62
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
63
pub struct OcspCertStatus(c_int);
64
65
impl 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
0
    pub fn from_raw(raw: c_int) -> OcspCertStatus {
71
0
        OcspCertStatus(raw)
72
0
    }
73
74
    #[allow(clippy::trivially_copy_pass_by_ref)]
75
0
    pub fn as_raw(&self) -> c_int {
76
0
        self.0
77
0
    }
78
}
79
80
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
81
pub struct OcspRevokedStatus(c_int);
82
83
impl 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
0
    pub fn from_raw(raw: c_int) -> OcspRevokedStatus {
103
0
        OcspRevokedStatus(raw)
104
0
    }
105
106
    #[allow(clippy::trivially_copy_pass_by_ref)]
107
0
    pub fn as_raw(&self) -> c_int {
108
0
        self.0
109
0
    }
110
}
111
112
pub 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
125
impl<'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
0
    pub fn check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack> {
135
        unsafe {
136
0
            cvt(ffi::OCSP_check_validity(
137
0
                self.this_update.as_ptr(),
138
0
                self.next_update.as_ptr(),
139
0
                nsec as c_long,
140
0
                maxsec.map(|n| n as c_long).unwrap_or(-1),
141
            ))
142
0
            .map(|_| ())
143
        }
144
0
    }
145
}
146
147
foreign_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
155
impl 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
0
    pub fn verify(
162
0
        &self,
163
0
        certs: &StackRef<X509>,
164
0
        store: &X509StoreRef,
165
0
        flags: OcspFlag,
166
0
    ) -> Result<(), ErrorStack> {
167
        unsafe {
168
0
            cvt(ffi::OCSP_basic_verify(
169
0
                self.as_ptr(),
170
0
                certs.as_ptr(),
171
0
                store.as_ptr(),
172
0
                flags.bits(),
173
            ))
174
0
            .map(|_| ())
175
        }
176
0
    }
177
178
    /// Looks up the status for the specified certificate ID.
179
    #[corresponds(OCSP_resp_find_status)]
180
0
    pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<OcspStatus<'a>> {
181
        unsafe {
182
0
            let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN;
183
0
            let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS;
184
0
            let mut revocation_time = ptr::null_mut();
185
0
            let mut this_update = ptr::null_mut();
186
0
            let mut next_update = ptr::null_mut();
187
188
0
            let r = ffi::OCSP_resp_find_status(
189
0
                self.as_ptr(),
190
0
                id.as_ptr(),
191
0
                &mut status,
192
0
                &mut reason,
193
0
                &mut revocation_time,
194
0
                &mut this_update,
195
0
                &mut next_update,
196
            );
197
0
            if r == 1 {
198
0
                let revocation_time = Asn1GeneralizedTimeRef::from_const_ptr_opt(revocation_time);
199
200
0
                Some(OcspStatus {
201
0
                    status: OcspCertStatus(status),
202
0
                    reason: OcspRevokedStatus(status),
203
0
                    revocation_time,
204
0
                    this_update: Asn1GeneralizedTimeRef::from_ptr(this_update),
205
0
                    next_update: Asn1GeneralizedTimeRef::from_ptr(next_update),
206
0
                })
207
            } else {
208
0
                None
209
            }
210
        }
211
0
    }
212
}
213
214
foreign_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
222
impl OcspCertId {
223
    /// Constructs a certificate ID for certificate `subject`.
224
    #[corresponds(OCSP_cert_to_id)]
225
0
    pub fn from_cert(
226
0
        digest: MessageDigest,
227
0
        subject: &X509Ref,
228
0
        issuer: &X509Ref,
229
0
    ) -> Result<OcspCertId, ErrorStack> {
230
        unsafe {
231
0
            cvt_p(ffi::OCSP_cert_to_id(
232
0
                digest.as_ptr(),
233
0
                subject.as_ptr(),
234
0
                issuer.as_ptr(),
235
            ))
236
0
            .map(OcspCertId)
237
        }
238
0
    }
239
}
240
241
foreign_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
249
impl 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
0
    pub fn create(
255
0
        status: OcspResponseStatus,
256
0
        body: Option<&OcspBasicResponseRef>,
257
0
    ) -> Result<OcspResponse, ErrorStack> {
258
        unsafe {
259
0
            ffi::init();
260
261
0
            cvt_p(ffi::OCSP_response_create(
262
0
                status.as_raw(),
263
0
                body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut()),
264
            ))
265
0
            .map(OcspResponse)
266
        }
267
0
    }
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
278
impl 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
0
    pub fn status(&self) -> OcspResponseStatus {
289
0
        unsafe { OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr())) }
290
0
    }
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
0
    pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> {
297
0
        unsafe { cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(OcspBasicResponse) }
298
0
    }
299
}
300
301
foreign_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
309
impl OcspRequest {
310
    #[corresponds(OCSP_REQUEST_new)]
311
0
    pub fn new() -> Result<OcspRequest, ErrorStack> {
312
        unsafe {
313
0
            ffi::init();
314
315
0
            cvt_p(ffi::OCSP_REQUEST_new()).map(OcspRequest)
316
        }
317
0
    }
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
328
impl 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
0
    pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> {
338
        unsafe {
339
0
            let ptr = cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id.as_ptr()))?;
340
0
            mem::forget(id);
341
0
            Ok(OcspOneReqRef::from_ptr_mut(ptr))
342
        }
343
0
    }
344
}
345
346
foreign_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
}