Skip to main content

openssl/x509/
mod.rs

1//! The standard defining the format of public key certificates.
2//!
3//! An `X509` certificate binds an identity to a public key, and is either
4//! signed by a certificate authority (CA) or self-signed. An entity that gets
5//! a hold of a certificate can both verify your identity (via a CA) and encrypt
6//! data with the included public key. `X509` certificates are used in many
7//! Internet protocols, including SSL/TLS, which is the basis for HTTPS,
8//! the secure protocol for browsing the web.
9
10use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
11use libc::{c_int, c_long, c_uint, c_void};
12use std::cmp::{self, Ordering};
13use std::convert::{TryFrom, TryInto};
14use std::error::Error;
15use std::ffi::{CStr, CString};
16use std::fmt;
17use std::marker::PhantomData;
18use std::mem;
19use std::net::IpAddr;
20use std::path::Path;
21use std::ptr;
22use std::str;
23
24use crate::asn1::{
25    Asn1BitStringRef, Asn1Enumerated, Asn1Integer, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
26    Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
27};
28use crate::bio::MemBioSlice;
29use crate::conf::ConfRef;
30use crate::error::ErrorStack;
31use crate::ex_data::Index;
32use crate::hash::{DigestBytes, MessageDigest};
33use crate::nid::Nid;
34use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
35use crate::ssl::SslRef;
36use crate::stack::{Stack, StackRef, Stackable};
37use crate::string::OpensslString;
38use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
39use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
40use openssl_macros::corresponds;
41
42pub use crate::x509::extension::CrlNumber;
43
44pub mod verify;
45
46pub mod extension;
47pub mod store;
48
49#[cfg(test)]
50mod tests;
51
52/// A type of X509 extension.
53///
54/// # Safety
55/// The value of NID and Output must match those in OpenSSL so that
56/// `Output::from_ptr_opt(*_get_ext_d2i(*, NID, ...))` is valid.
57pub unsafe trait ExtensionType {
58    const NID: Nid;
59    type Output: ForeignType;
60}
61
62foreign_type_and_impl_send_sync! {
63    type CType = ffi::X509_STORE_CTX;
64    fn drop = ffi::X509_STORE_CTX_free;
65
66    /// An `X509` certificate store context.
67    pub struct X509StoreContext;
68
69    /// A reference to an [`X509StoreContext`].
70    pub struct X509StoreContextRef;
71}
72
73impl X509StoreContext {
74    /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a
75    /// context.
76    #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
77    pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
78        unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
79    }
80
81    /// Creates a new `X509StoreContext` instance.
82    #[corresponds(X509_STORE_CTX_new)]
83    pub fn new() -> Result<X509StoreContext, ErrorStack> {
84        unsafe {
85            ffi::init();
86            cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
87        }
88    }
89}
90
91impl X509StoreContextRef {
92    /// Returns application data pertaining to an `X509` store context.
93    #[corresponds(X509_STORE_CTX_get_ex_data)]
94    pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
95        unsafe {
96            let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
97            if data.is_null() {
98                None
99            } else {
100                Some(&*(data as *const T))
101            }
102        }
103    }
104
105    /// Returns the error code of the context.
106    #[corresponds(X509_STORE_CTX_get_error)]
107    pub fn error(&self) -> X509VerifyResult {
108        unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
109    }
110
111    /// Initializes this context with the given certificate, certificates chain and certificate
112    /// store. After initializing the context, the `with_context` closure is called with the prepared
113    /// context. As long as the closure is running, the context stays initialized and can be used
114    /// to e.g. verify a certificate. The context will be cleaned up, after the closure finished.
115    ///
116    /// * `trust` - The certificate store with the trusted certificates.
117    /// * `cert` - The certificate that should be verified.
118    /// * `cert_chain` - The certificates chain.
119    /// * `with_context` - The closure that is called with the initialized context.
120    ///
121    /// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to
122    /// [`X509_STORE_CTX_cleanup`] after calling `with_context`.
123    ///
124    /// [`X509_STORE_CTX_init`]:  https://docs.openssl.org/master/man3/X509_STORE_CTX_init/
125    /// [`X509_STORE_CTX_cleanup`]:  https://docs.openssl.org/master/man3/X509_STORE_CTX_cleanup/
126    pub fn init<F, T>(
127        &mut self,
128        trust: &store::X509StoreRef,
129        cert: &X509Ref,
130        cert_chain: &StackRef<X509>,
131        with_context: F,
132    ) -> Result<T, ErrorStack>
133    where
134        F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
135    {
136        struct Cleanup<'a>(&'a mut X509StoreContextRef);
137
138        impl Drop for Cleanup<'_> {
139            fn drop(&mut self) {
140                unsafe {
141                    ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
142                }
143            }
144        }
145
146        unsafe {
147            cvt(ffi::X509_STORE_CTX_init(
148                self.as_ptr(),
149                trust.as_ptr(),
150                cert.as_ptr(),
151                cert_chain.as_ptr(),
152            ))?;
153
154            let cleanup = Cleanup(self);
155            with_context(cleanup.0)
156        }
157    }
158
159    /// Verifies the stored certificate.
160    ///
161    /// Returns `true` if verification succeeds. The `error` method will return the specific
162    /// validation error if the certificate was not valid.
163    ///
164    /// This will only work inside of a call to `init`.
165    #[corresponds(X509_verify_cert)]
166    pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
167        unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
168    }
169
170    /// Set the error code of the context.
171    #[corresponds(X509_STORE_CTX_set_error)]
172    pub fn set_error(&mut self, result: X509VerifyResult) {
173        unsafe {
174            ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
175        }
176    }
177
178    /// Returns a reference to the certificate which caused the error or None if
179    /// no certificate is relevant to the error.
180    #[corresponds(X509_STORE_CTX_get_current_cert)]
181    pub fn current_cert(&self) -> Option<&X509Ref> {
182        unsafe {
183            let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
184            X509Ref::from_const_ptr_opt(ptr)
185        }
186    }
187
188    /// Returns a non-negative integer representing the depth in the certificate
189    /// chain where the error occurred. If it is zero it occurred in the end
190    /// entity certificate, one if it is the certificate which signed the end
191    /// entity certificate and so on.
192    #[corresponds(X509_STORE_CTX_get_error_depth)]
193    pub fn error_depth(&self) -> u32 {
194        unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
195    }
196
197    /// Returns a reference to a complete valid `X509` certificate chain.
198    #[corresponds(X509_STORE_CTX_get0_chain)]
199    pub fn chain(&self) -> Option<&StackRef<X509>> {
200        unsafe {
201            let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
202
203            if chain.is_null() {
204                None
205            } else {
206                Some(StackRef::from_ptr(chain))
207            }
208        }
209    }
210}
211
212/// A builder used to construct an `X509`.
213pub struct X509Builder(X509);
214
215impl X509Builder {
216    /// Creates a new builder.
217    #[corresponds(X509_new)]
218    pub fn new() -> Result<X509Builder, ErrorStack> {
219        unsafe {
220            ffi::init();
221            cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
222        }
223    }
224
225    /// Sets the notAfter constraint on the certificate.
226    #[corresponds(X509_set1_notAfter)]
227    pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
228        unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
229    }
230
231    /// Sets the notBefore constraint on the certificate.
232    #[corresponds(X509_set1_notBefore)]
233    pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
234        unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
235    }
236
237    /// Sets the version of the certificate.
238    ///
239    /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of
240    /// the X.509 standard should pass `2` to this method.
241    #[corresponds(X509_set_version)]
242    #[allow(clippy::useless_conversion)]
243    pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
244        unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
245    }
246
247    /// Sets the serial number of the certificate.
248    #[corresponds(X509_set_serialNumber)]
249    pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
250        unsafe {
251            cvt(ffi::X509_set_serialNumber(
252                self.0.as_ptr(),
253                serial_number.as_ptr(),
254            ))
255            .map(|_| ())
256        }
257    }
258
259    /// Sets the issuer name of the certificate.
260    #[corresponds(X509_set_issuer_name)]
261    pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
262        unsafe {
263            cvt(ffi::X509_set_issuer_name(
264                self.0.as_ptr(),
265                issuer_name.as_ptr(),
266            ))
267            .map(|_| ())
268        }
269    }
270
271    /// Sets the subject name of the certificate.
272    ///
273    /// When building certificates, the `C`, `ST`, and `O` options are common when using the openssl command line tools.
274    /// The `CN` field is used for the common name, such as a DNS name.
275    ///
276    /// ```
277    /// use openssl::x509::{X509, X509NameBuilder};
278    ///
279    /// let mut x509_name = openssl::x509::X509NameBuilder::new().unwrap();
280    /// x509_name.append_entry_by_text("C", "US").unwrap();
281    /// x509_name.append_entry_by_text("ST", "CA").unwrap();
282    /// x509_name.append_entry_by_text("O", "Some organization").unwrap();
283    /// x509_name.append_entry_by_text("CN", "www.example.com").unwrap();
284    /// let x509_name = x509_name.build();
285    ///
286    /// let mut x509 = openssl::x509::X509::builder().unwrap();
287    /// x509.set_subject_name(&x509_name).unwrap();
288    /// ```
289    #[corresponds(X509_set_subject_name)]
290    pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
291        unsafe {
292            cvt(ffi::X509_set_subject_name(
293                self.0.as_ptr(),
294                subject_name.as_ptr(),
295            ))
296            .map(|_| ())
297        }
298    }
299
300    /// Sets the public key associated with the certificate.
301    #[corresponds(X509_set_pubkey)]
302    pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
303    where
304        T: HasPublic,
305    {
306        unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
307    }
308
309    /// Returns a context object which is needed to create certain X509 extension values.
310    ///
311    /// Set `issuer` to `None` if the certificate will be self-signed.
312    #[corresponds(X509V3_set_ctx)]
313    pub fn x509v3_context<'a>(
314        &'a self,
315        issuer: Option<&'a X509Ref>,
316        conf: Option<&'a ConfRef>,
317    ) -> X509v3Context<'a> {
318        unsafe {
319            let mut ctx = mem::zeroed();
320
321            let issuer = match issuer {
322                Some(issuer) => issuer.as_ptr(),
323                None => self.0.as_ptr(),
324            };
325            let subject = self.0.as_ptr();
326            ffi::X509V3_set_ctx(
327                &mut ctx,
328                issuer,
329                subject,
330                ptr::null_mut(),
331                ptr::null_mut(),
332                0,
333            );
334
335            // nodb case taken care of since we zeroed ctx above
336            if let Some(conf) = conf {
337                ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
338            }
339
340            X509v3Context(ctx, PhantomData)
341        }
342    }
343
344    /// Adds an X509 extension value to the certificate.
345    ///
346    /// This works just as `append_extension` except it takes ownership of the `X509Extension`.
347    pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
348        self.append_extension2(&extension)
349    }
350
351    /// Adds an X509 extension value to the certificate.
352    #[corresponds(X509_add_ext)]
353    pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
354        unsafe {
355            cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
356            Ok(())
357        }
358    }
359
360    /// Signs the certificate with a private key.
361    #[corresponds(X509_sign)]
362    pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
363    where
364        T: HasPrivate,
365    {
366        unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
367    }
368
369    /// Consumes the builder, returning the certificate.
370    pub fn build(self) -> X509 {
371        self.0
372    }
373}
374
375foreign_type_and_impl_send_sync! {
376    type CType = ffi::X509;
377    fn drop = ffi::X509_free;
378
379    /// An `X509` public key certificate.
380    pub struct X509;
381    /// Reference to `X509`.
382    pub struct X509Ref;
383}
384
385impl X509Ref {
386    /// Returns this certificate's subject name.
387    #[corresponds(X509_get_subject_name)]
388    pub fn subject_name(&self) -> &X509NameRef {
389        unsafe {
390            let name = ffi::X509_get_subject_name(self.as_ptr());
391            X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
392        }
393    }
394
395    /// Returns the hash of the certificates subject
396    #[corresponds(X509_subject_name_hash)]
397    pub fn subject_name_hash(&self) -> u32 {
398        #[allow(clippy::unnecessary_cast)]
399        unsafe {
400            ffi::X509_subject_name_hash(self.as_ptr()) as u32
401        }
402    }
403
404    /// Returns this certificate's issuer name.
405    #[corresponds(X509_get_issuer_name)]
406    pub fn issuer_name(&self) -> &X509NameRef {
407        unsafe {
408            let name = ffi::X509_get_issuer_name(self.as_ptr());
409            X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
410        }
411    }
412
413    /// Returns the hash of the certificates issuer
414    #[corresponds(X509_issuer_name_hash)]
415    pub fn issuer_name_hash(&self) -> u32 {
416        #[allow(clippy::unnecessary_cast)]
417        unsafe {
418            ffi::X509_issuer_name_hash(self.as_ptr()) as u32
419        }
420    }
421
422    /// Returns this certificate's subject alternative name entries, if they exist.
423    #[corresponds(X509_get_ext_d2i)]
424    pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
425        unsafe {
426            let stack = ffi::X509_get_ext_d2i(
427                self.as_ptr(),
428                ffi::NID_subject_alt_name,
429                ptr::null_mut(),
430                ptr::null_mut(),
431            );
432            Stack::from_ptr_opt(stack as *mut _)
433        }
434    }
435
436    /// Returns this certificate's CRL distribution points, if they exist.
437    #[corresponds(X509_get_ext_d2i)]
438    pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
439        unsafe {
440            let stack = ffi::X509_get_ext_d2i(
441                self.as_ptr(),
442                ffi::NID_crl_distribution_points,
443                ptr::null_mut(),
444                ptr::null_mut(),
445            );
446            Stack::from_ptr_opt(stack as *mut _)
447        }
448    }
449
450    /// Returns this certificate's issuer alternative name entries, if they exist.
451    #[corresponds(X509_get_ext_d2i)]
452    pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
453        unsafe {
454            let stack = ffi::X509_get_ext_d2i(
455                self.as_ptr(),
456                ffi::NID_issuer_alt_name,
457                ptr::null_mut(),
458                ptr::null_mut(),
459            );
460            Stack::from_ptr_opt(stack as *mut _)
461        }
462    }
463
464    /// Returns this certificate's [`authority information access`] entries, if they exist.
465    ///
466    /// [`authority information access`]: https://tools.ietf.org/html/rfc5280#section-4.2.2.1
467    #[corresponds(X509_get_ext_d2i)]
468    pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
469        unsafe {
470            let stack = ffi::X509_get_ext_d2i(
471                self.as_ptr(),
472                ffi::NID_info_access,
473                ptr::null_mut(),
474                ptr::null_mut(),
475            );
476            Stack::from_ptr_opt(stack as *mut _)
477        }
478    }
479
480    /// Retrieves the path length extension from a certificate, if it exists.
481    #[corresponds(X509_get_pathlen)]
482    #[cfg(any(ossl110, boringssl, awslc))]
483    pub fn pathlen(&self) -> Option<u32> {
484        let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
485        u32::try_from(v).ok()
486    }
487
488    /// Returns this certificate's subject key id, if it exists.
489    #[corresponds(X509_get0_subject_key_id)]
490    #[cfg(any(ossl110, boringssl, awslc))]
491    pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
492        unsafe {
493            let data = ffi::X509_get0_subject_key_id(self.as_ptr());
494            Asn1OctetStringRef::from_const_ptr_opt(data)
495        }
496    }
497
498    /// Returns this certificate's authority key id, if it exists.
499    #[corresponds(X509_get0_authority_key_id)]
500    #[cfg(any(ossl110, boringssl, awslc))]
501    pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
502        unsafe {
503            let data = ffi::X509_get0_authority_key_id(self.as_ptr());
504            Asn1OctetStringRef::from_const_ptr_opt(data)
505        }
506    }
507
508    /// Returns this certificate's authority issuer name entries, if they exist.
509    #[corresponds(X509_get0_authority_issuer)]
510    #[cfg(ossl111d)]
511    pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
512        unsafe {
513            let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
514            StackRef::from_const_ptr_opt(stack)
515        }
516    }
517
518    /// Returns this certificate's authority serial number, if it exists.
519    #[corresponds(X509_get0_authority_serial)]
520    #[cfg(ossl111d)]
521    pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
522        unsafe {
523            let r = ffi::X509_get0_authority_serial(self.as_ptr());
524            Asn1IntegerRef::from_const_ptr_opt(r)
525        }
526    }
527
528    #[corresponds(X509_get_pubkey)]
529    pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
530        unsafe {
531            let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
532            Ok(PKey::from_ptr(pkey))
533        }
534    }
535
536    /// Returns a digest of the DER representation of the certificate.
537    #[corresponds(X509_digest)]
538    pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
539        unsafe {
540            let mut digest = DigestBytes {
541                buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
542                len: ffi::EVP_MAX_MD_SIZE as usize,
543            };
544            let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
545            cvt(ffi::X509_digest(
546                self.as_ptr(),
547                hash_type.as_ptr(),
548                digest.buf.as_mut_ptr() as *mut _,
549                &mut len,
550            ))?;
551            digest.len = len as usize;
552
553            Ok(digest)
554        }
555    }
556
557    #[deprecated(since = "0.10.9", note = "renamed to digest")]
558    pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
559        self.digest(hash_type).map(|b| b.to_vec())
560    }
561
562    /// Returns the certificate's Not After validity period.
563    #[corresponds(X509_getm_notAfter)]
564    pub fn not_after(&self) -> &Asn1TimeRef {
565        unsafe {
566            let date = X509_getm_notAfter(self.as_ptr());
567            Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
568        }
569    }
570
571    /// Returns the certificate's Not Before validity period.
572    #[corresponds(X509_getm_notBefore)]
573    pub fn not_before(&self) -> &Asn1TimeRef {
574        unsafe {
575            let date = X509_getm_notBefore(self.as_ptr());
576            Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
577        }
578    }
579
580    /// Returns the certificate's signature
581    #[corresponds(X509_get0_signature)]
582    pub fn signature(&self) -> &Asn1BitStringRef {
583        unsafe {
584            let mut signature = ptr::null();
585            X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
586            Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
587        }
588    }
589
590    /// Returns the certificate's signature algorithm.
591    #[corresponds(X509_get0_signature)]
592    pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
593        unsafe {
594            let mut algor = ptr::null();
595            X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
596            X509AlgorithmRef::from_const_ptr_opt(algor)
597                .expect("signature algorithm must not be null")
598        }
599    }
600
601    /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information
602    /// Access field.
603    ///
604    /// Returns an error if any URL contains bytes that are not valid UTF-8, since OpenSSL
605    /// does not enforce that the underlying `IA5String` is ASCII.
606    #[corresponds(X509_get1_ocsp)]
607    pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
608        unsafe {
609            let stack: Stack<OpensslString> =
610                cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))?;
611            for entry in &stack {
612                let bytes = CStr::from_ptr(entry.as_ptr()).to_bytes();
613                if str::from_utf8(bytes).is_err() {
614                    return Err(ErrorStack::internal_error(
615                        "OCSP responder URL contained invalid UTF-8",
616                    ));
617                }
618            }
619            Ok(stack)
620        }
621    }
622
623    /// Checks that this certificate issued `subject`.
624    #[corresponds(X509_check_issued)]
625    pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
626        unsafe {
627            let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
628            X509VerifyResult::from_raw(r)
629        }
630    }
631
632    /// Returns certificate version. If this certificate has no explicit version set, it defaults to
633    /// version 1.
634    ///
635    /// Note that `0` return value stands for version 1, `1` for version 2 and so on.
636    #[corresponds(X509_get_version)]
637    #[cfg(ossl110)]
638    #[allow(clippy::unnecessary_cast)]
639    pub fn version(&self) -> i32 {
640        unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
641    }
642
643    /// Check if the certificate is signed using the given public key.
644    ///
645    /// Only the signature is checked: no other checks (such as certificate chain validity)
646    /// are performed.
647    ///
648    /// Returns `true` if verification succeeds.
649    #[corresponds(X509_verify)]
650    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
651    where
652        T: HasPublic,
653    {
654        unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
655    }
656
657    /// Returns this certificate's serial number.
658    #[corresponds(X509_get_serialNumber)]
659    pub fn serial_number(&self) -> &Asn1IntegerRef {
660        unsafe {
661            let r = ffi::X509_get_serialNumber(self.as_ptr());
662            Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
663        }
664    }
665
666    /// Returns this certificate's "alias". This field is populated by
667    /// OpenSSL in some situations -- specifically OpenSSL will store a
668    /// PKCS#12 `friendlyName` in this field. This is not a part of the X.509
669    /// certificate itself, OpenSSL merely attaches it to this structure in
670    /// memory.
671    #[corresponds(X509_alias_get0)]
672    pub fn alias(&self) -> Option<&[u8]> {
673        unsafe {
674            let mut len = 0;
675            let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
676            if ptr.is_null() {
677                None
678            } else {
679                Some(util::from_raw_parts(ptr, len as usize))
680            }
681        }
682    }
683
684    to_pem! {
685        /// Serializes the certificate into a PEM-encoded X509 structure.
686        ///
687        /// The output will have a header of `-----BEGIN CERTIFICATE-----`.
688        #[corresponds(PEM_write_bio_X509)]
689        to_pem,
690        ffi::PEM_write_bio_X509
691    }
692
693    to_der! {
694        /// Serializes the certificate into a DER-encoded X509 structure.
695        #[corresponds(i2d_X509)]
696        to_der,
697        ffi::i2d_X509
698    }
699
700    to_pem! {
701        /// Converts the certificate to human readable text.
702        #[corresponds(X509_print)]
703        to_text,
704        ffi::X509_print
705    }
706}
707
708impl ToOwned for X509Ref {
709    type Owned = X509;
710
711    fn to_owned(&self) -> X509 {
712        unsafe {
713            X509_up_ref(self.as_ptr());
714            X509::from_ptr(self.as_ptr())
715        }
716    }
717}
718
719impl Ord for X509Ref {
720    fn cmp(&self, other: &Self) -> cmp::Ordering {
721        // X509_cmp returns a number <0 for less than, 0 for equal and >0 for greater than.
722        // It can't fail if both pointers are valid, which we know is true.
723        let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
724        cmp.cmp(&0)
725    }
726}
727
728impl PartialOrd for X509Ref {
729    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
730        Some(self.cmp(other))
731    }
732}
733
734impl PartialOrd<X509> for X509Ref {
735    fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
736        <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
737    }
738}
739
740impl PartialEq for X509Ref {
741    fn eq(&self, other: &Self) -> bool {
742        self.cmp(other) == cmp::Ordering::Equal
743    }
744}
745
746impl PartialEq<X509> for X509Ref {
747    fn eq(&self, other: &X509) -> bool {
748        <X509Ref as PartialEq<X509Ref>>::eq(self, other)
749    }
750}
751
752impl Eq for X509Ref {}
753
754impl X509 {
755    /// Returns a new builder.
756    pub fn builder() -> Result<X509Builder, ErrorStack> {
757        X509Builder::new()
758    }
759
760    from_pem! {
761        /// Deserializes a PEM-encoded X509 structure.
762        ///
763        /// The input should have a header of `-----BEGIN CERTIFICATE-----`.
764        #[corresponds(PEM_read_bio_X509)]
765        from_pem,
766        X509,
767        ffi::PEM_read_bio_X509
768    }
769
770    from_der! {
771        /// Deserializes a DER-encoded X509 structure.
772        #[corresponds(d2i_X509)]
773        from_der,
774        X509,
775        ffi::d2i_X509
776    }
777
778    /// Deserializes a list of PEM-formatted certificates.
779    #[corresponds(PEM_read_bio_X509)]
780    pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
781        unsafe {
782            ffi::init();
783            let bio = MemBioSlice::new(pem)?;
784
785            let mut certs = vec![];
786            loop {
787                let r =
788                    ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
789                if r.is_null() {
790                    let e = ErrorStack::get();
791
792                    if let Some(err) = e.errors().last() {
793                        if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
794                            && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
795                        {
796                            break;
797                        }
798                    }
799
800                    return Err(e);
801                } else {
802                    certs.push(X509(r));
803                }
804            }
805
806            Ok(certs)
807        }
808    }
809}
810
811impl Clone for X509 {
812    fn clone(&self) -> X509 {
813        X509Ref::to_owned(self)
814    }
815}
816
817impl fmt::Debug for X509 {
818    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
819        let serial = match &self.serial_number().to_bn() {
820            Ok(bn) => match bn.to_hex_str() {
821                Ok(hex) => hex.to_string(),
822                Err(_) => "".to_string(),
823            },
824            Err(_) => "".to_string(),
825        };
826        let mut debug_struct = formatter.debug_struct("X509");
827        debug_struct.field("serial_number", &serial);
828        debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
829        debug_struct.field("issuer", &self.issuer_name());
830        debug_struct.field("subject", &self.subject_name());
831        if let Some(subject_alt_names) = &self.subject_alt_names() {
832            debug_struct.field("subject_alt_names", subject_alt_names);
833        }
834        debug_struct.field("not_before", &self.not_before());
835        debug_struct.field("not_after", &self.not_after());
836
837        if let Ok(public_key) = &self.public_key() {
838            debug_struct.field("public_key", public_key);
839        };
840        // TODO: Print extensions once they are supported on the X509 struct.
841
842        debug_struct.finish()
843    }
844}
845
846impl AsRef<X509Ref> for X509Ref {
847    fn as_ref(&self) -> &X509Ref {
848        self
849    }
850}
851
852impl Stackable for X509 {
853    type StackType = ffi::stack_st_X509;
854}
855
856impl Ord for X509 {
857    fn cmp(&self, other: &Self) -> cmp::Ordering {
858        X509Ref::cmp(self, other)
859    }
860}
861
862impl PartialOrd for X509 {
863    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
864        Some(self.cmp(other))
865    }
866}
867
868impl PartialOrd<X509Ref> for X509 {
869    fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
870        X509Ref::partial_cmp(self, other)
871    }
872}
873
874impl PartialEq for X509 {
875    fn eq(&self, other: &Self) -> bool {
876        X509Ref::eq(self, other)
877    }
878}
879
880impl PartialEq<X509Ref> for X509 {
881    fn eq(&self, other: &X509Ref) -> bool {
882        X509Ref::eq(self, other)
883    }
884}
885
886impl Eq for X509 {}
887
888/// A context object required to construct certain `X509` extension values.
889pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
890
891impl X509v3Context<'_> {
892    pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
893        &self.0 as *const _ as *mut _
894    }
895}
896
897foreign_type_and_impl_send_sync! {
898    type CType = ffi::X509_EXTENSION;
899    fn drop = ffi::X509_EXTENSION_free;
900
901    /// Permit additional fields to be added to an `X509` v3 certificate.
902    pub struct X509Extension;
903    /// Reference to `X509Extension`.
904    pub struct X509ExtensionRef;
905}
906
907impl Stackable for X509Extension {
908    type StackType = ffi::stack_st_X509_EXTENSION;
909}
910
911impl X509Extension {
912    /// Constructs an X509 extension value. See `man x509v3_config` for information on supported
913    /// names and their value formats.
914    ///
915    /// Some extension types, such as `subjectAlternativeName`, require an `X509v3Context` to be
916    /// provided.
917    ///
918    /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL
919    /// mini-language that can read arbitrary files.
920    ///
921    /// See the extension module for builder types which will construct certain common extensions.
922    ///
923    /// This function is deprecated, `X509Extension::new_from_der` or the
924    /// types in `x509::extension` should be used in its place.
925    #[deprecated(
926        note = "Use x509::extension types or new_from_der instead",
927        since = "0.10.51"
928    )]
929    pub fn new(
930        conf: Option<&ConfRef>,
931        context: Option<&X509v3Context<'_>>,
932        name: &str,
933        value: &str,
934    ) -> Result<X509Extension, ErrorStack> {
935        let name = CString::new(name).unwrap();
936        let value = CString::new(value).unwrap();
937        let mut ctx;
938        unsafe {
939            ffi::init();
940            let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
941            let context_ptr = match context {
942                Some(c) => c.as_ptr(),
943                None => {
944                    ctx = mem::zeroed();
945
946                    ffi::X509V3_set_ctx(
947                        &mut ctx,
948                        ptr::null_mut(),
949                        ptr::null_mut(),
950                        ptr::null_mut(),
951                        ptr::null_mut(),
952                        0,
953                    );
954                    &mut ctx
955                }
956            };
957            let name = name.as_ptr() as *mut _;
958            let value = value.as_ptr() as *mut _;
959
960            cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
961        }
962    }
963
964    /// Constructs an X509 extension value. See `man x509v3_config` for information on supported
965    /// extensions and their value formats.
966    ///
967    /// Some extension types, such as `nid::SUBJECT_ALTERNATIVE_NAME`, require an `X509v3Context` to
968    /// be provided.
969    ///
970    /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL
971    /// mini-language that can read arbitrary files.
972    ///
973    /// See the extension module for builder types which will construct certain common extensions.
974    ///
975    /// This function is deprecated, `X509Extension::new_from_der` or the
976    /// types in `x509::extension` should be used in its place.
977    #[deprecated(
978        note = "Use x509::extension types or new_from_der instead",
979        since = "0.10.51"
980    )]
981    pub fn new_nid(
982        conf: Option<&ConfRef>,
983        context: Option<&X509v3Context<'_>>,
984        name: Nid,
985        value: &str,
986    ) -> Result<X509Extension, ErrorStack> {
987        let value = CString::new(value).unwrap();
988        let mut ctx;
989        unsafe {
990            ffi::init();
991            let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
992            let context_ptr = match context {
993                Some(c) => c.as_ptr(),
994                None => {
995                    ctx = mem::zeroed();
996
997                    ffi::X509V3_set_ctx(
998                        &mut ctx,
999                        ptr::null_mut(),
1000                        ptr::null_mut(),
1001                        ptr::null_mut(),
1002                        ptr::null_mut(),
1003                        0,
1004                    );
1005                    &mut ctx
1006                }
1007            };
1008            let name = name.as_raw();
1009            let value = value.as_ptr() as *mut _;
1010
1011            cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1012        }
1013    }
1014
1015    /// Constructs a new X509 extension value from its OID, whether it's
1016    /// critical, and its DER contents.
1017    ///
1018    /// The extent structure of the DER value will vary based on the
1019    /// extension type, and can generally be found in the RFC defining the
1020    /// extension.
1021    ///
1022    /// For common extension types, there are Rust APIs provided in
1023    /// `openssl::x509::extensions` which are more ergonomic.
1024    pub fn new_from_der(
1025        oid: &Asn1ObjectRef,
1026        critical: bool,
1027        der_contents: &Asn1OctetStringRef,
1028    ) -> Result<X509Extension, ErrorStack> {
1029        unsafe {
1030            cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1031                ptr::null_mut(),
1032                oid.as_ptr(),
1033                critical as _,
1034                der_contents.as_ptr(),
1035            ))
1036            .map(X509Extension)
1037        }
1038    }
1039
1040    pub(crate) unsafe fn new_internal(
1041        nid: Nid,
1042        critical: bool,
1043        value: *mut c_void,
1044    ) -> Result<X509Extension, ErrorStack> {
1045        ffi::init();
1046        cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1047    }
1048
1049    /// Adds an alias for an extension
1050    ///
1051    /// # Safety
1052    ///
1053    /// This method modifies global state without locking and therefore is not thread safe
1054    #[cfg(not(libressl390))]
1055    #[corresponds(X509V3_EXT_add_alias)]
1056    #[deprecated(
1057        note = "Use x509::extension types or new_from_der and then this is not necessary",
1058        since = "0.10.51"
1059    )]
1060    pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1061        ffi::init();
1062        cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1063    }
1064}
1065
1066impl X509ExtensionRef {
1067    to_der! {
1068        /// Serializes the Extension to its standard DER encoding.
1069        #[corresponds(i2d_X509_EXTENSION)]
1070        to_der,
1071        ffi::i2d_X509_EXTENSION
1072    }
1073}
1074
1075/// A builder used to construct an `X509Name`.
1076pub struct X509NameBuilder(X509Name);
1077
1078impl X509NameBuilder {
1079    /// Creates a new builder.
1080    pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1081        unsafe {
1082            ffi::init();
1083            cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1084        }
1085    }
1086
1087    /// Add a name entry
1088    #[corresponds(X509_NAME_add_entry)]
1089    pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1090        unsafe {
1091            cvt(ffi::X509_NAME_add_entry(
1092                self.0.as_ptr(),
1093                ne.as_ptr(),
1094                -1,
1095                0,
1096            ))
1097            .map(|_| ())
1098        }
1099    }
1100
1101    /// Add a field entry by str.
1102    #[corresponds(X509_NAME_add_entry_by_txt)]
1103    pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1104        unsafe {
1105            let field = CString::new(field).unwrap();
1106            assert!(value.len() <= crate::SLenType::MAX as usize);
1107            cvt(ffi::X509_NAME_add_entry_by_txt(
1108                self.0.as_ptr(),
1109                field.as_ptr() as *mut _,
1110                ffi::MBSTRING_UTF8,
1111                value.as_ptr(),
1112                value.len() as crate::SLenType,
1113                -1,
1114                0,
1115            ))
1116            .map(|_| ())
1117        }
1118    }
1119
1120    /// Add a field entry by str with a specific type.
1121    #[corresponds(X509_NAME_add_entry_by_txt)]
1122    pub fn append_entry_by_text_with_type(
1123        &mut self,
1124        field: &str,
1125        value: &str,
1126        ty: Asn1Type,
1127    ) -> Result<(), ErrorStack> {
1128        unsafe {
1129            let field = CString::new(field).unwrap();
1130            assert!(value.len() <= crate::SLenType::MAX as usize);
1131            cvt(ffi::X509_NAME_add_entry_by_txt(
1132                self.0.as_ptr(),
1133                field.as_ptr() as *mut _,
1134                ty.as_raw(),
1135                value.as_ptr(),
1136                value.len() as crate::SLenType,
1137                -1,
1138                0,
1139            ))
1140            .map(|_| ())
1141        }
1142    }
1143
1144    /// Add a field entry by NID.
1145    #[corresponds(X509_NAME_add_entry_by_NID)]
1146    pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1147        unsafe {
1148            assert!(value.len() <= crate::SLenType::MAX as usize);
1149            cvt(ffi::X509_NAME_add_entry_by_NID(
1150                self.0.as_ptr(),
1151                field.as_raw(),
1152                ffi::MBSTRING_UTF8,
1153                value.as_ptr() as *mut _,
1154                value.len() as crate::SLenType,
1155                -1,
1156                0,
1157            ))
1158            .map(|_| ())
1159        }
1160    }
1161
1162    /// Add a field entry by NID with a specific type.
1163    #[corresponds(X509_NAME_add_entry_by_NID)]
1164    pub fn append_entry_by_nid_with_type(
1165        &mut self,
1166        field: Nid,
1167        value: &str,
1168        ty: Asn1Type,
1169    ) -> Result<(), ErrorStack> {
1170        unsafe {
1171            assert!(value.len() <= crate::SLenType::MAX as usize);
1172            cvt(ffi::X509_NAME_add_entry_by_NID(
1173                self.0.as_ptr(),
1174                field.as_raw(),
1175                ty.as_raw(),
1176                value.as_ptr() as *mut _,
1177                value.len() as crate::SLenType,
1178                -1,
1179                0,
1180            ))
1181            .map(|_| ())
1182        }
1183    }
1184
1185    /// Return an `X509Name`.
1186    pub fn build(self) -> X509Name {
1187        // Round-trip through bytes because OpenSSL is not const correct and
1188        // names in a "modified" state compute various things lazily. This can
1189        // lead to data-races because OpenSSL doesn't have locks or anything.
1190        X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1191    }
1192}
1193
1194foreign_type_and_impl_send_sync! {
1195    type CType = ffi::X509_NAME;
1196    fn drop = ffi::X509_NAME_free;
1197
1198    /// The names of an `X509` certificate.
1199    pub struct X509Name;
1200    /// Reference to `X509Name`.
1201    pub struct X509NameRef;
1202}
1203
1204impl X509Name {
1205    /// Returns a new builder.
1206    pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1207        X509NameBuilder::new()
1208    }
1209
1210    /// Loads subject names from a file containing PEM-formatted certificates.
1211    ///
1212    /// This is commonly used in conjunction with `SslContextBuilder::set_client_ca_list`.
1213    pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1214        let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1215        unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1216    }
1217
1218    from_der! {
1219        /// Deserializes a DER-encoded X509 name structure.
1220        ///
1221        /// This corresponds to [`d2i_X509_NAME`].
1222        ///
1223        /// [`d2i_X509_NAME`]: https://docs.openssl.org/master/man3/d2i_X509_NAME/
1224        from_der,
1225        X509Name,
1226        ffi::d2i_X509_NAME
1227    }
1228}
1229
1230impl Stackable for X509Name {
1231    type StackType = ffi::stack_st_X509_NAME;
1232}
1233
1234impl X509NameRef {
1235    /// Returns the name entries by the nid.
1236    pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1237        X509NameEntries {
1238            name: self,
1239            nid: Some(nid),
1240            loc: -1,
1241        }
1242    }
1243
1244    /// Returns an iterator over all `X509NameEntry` values
1245    pub fn entries(&self) -> X509NameEntries<'_> {
1246        X509NameEntries {
1247            name: self,
1248            nid: None,
1249            loc: -1,
1250        }
1251    }
1252
1253    /// Compare two names, like [`Ord`] but it may fail.
1254    ///
1255    /// With OpenSSL versions from 3.0.0 this may return an error if the underlying `X509_NAME_cmp`
1256    /// call fails.
1257    /// For OpenSSL versions before 3.0.0 it will never return an error, but due to a bug it may
1258    /// spuriously return `Ordering::Less` if the `X509_NAME_cmp` call fails.
1259    #[corresponds(X509_NAME_cmp)]
1260    pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1261        let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1262        if cfg!(ossl300) && cmp == -2 {
1263            return Err(ErrorStack::get());
1264        }
1265        Ok(cmp.cmp(&0))
1266    }
1267
1268    /// Copies the name to a new `X509Name`.
1269    #[corresponds(X509_NAME_dup)]
1270    pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1271        unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1272    }
1273
1274    to_der! {
1275        /// Serializes the certificate into a DER-encoded X509 name structure.
1276        ///
1277        /// This corresponds to [`i2d_X509_NAME`].
1278        ///
1279        /// [`i2d_X509_NAME`]: https://docs.openssl.org/master/man3/i2d_X509_NAME/
1280        to_der,
1281        ffi::i2d_X509_NAME
1282    }
1283}
1284
1285impl fmt::Debug for X509NameRef {
1286    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1287        formatter.debug_list().entries(self.entries()).finish()
1288    }
1289}
1290
1291/// A type to destructure and examine an `X509Name`.
1292pub struct X509NameEntries<'a> {
1293    name: &'a X509NameRef,
1294    nid: Option<Nid>,
1295    loc: c_int,
1296}
1297
1298impl<'a> Iterator for X509NameEntries<'a> {
1299    type Item = &'a X509NameEntryRef;
1300
1301    fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1302        unsafe {
1303            match self.nid {
1304                Some(nid) => {
1305                    // There is a `Nid` specified to search for
1306                    self.loc =
1307                        ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1308                    if self.loc == -1 {
1309                        return None;
1310                    }
1311                }
1312                None => {
1313                    // Iterate over all `Nid`s
1314                    self.loc += 1;
1315                    if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1316                        return None;
1317                    }
1318                }
1319            }
1320
1321            let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1322
1323            Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1324        }
1325    }
1326}
1327
1328foreign_type_and_impl_send_sync! {
1329    type CType = ffi::X509_NAME_ENTRY;
1330    fn drop = ffi::X509_NAME_ENTRY_free;
1331
1332    /// A name entry associated with a `X509Name`.
1333    pub struct X509NameEntry;
1334    /// Reference to `X509NameEntry`.
1335    pub struct X509NameEntryRef;
1336}
1337
1338impl X509NameEntryRef {
1339    /// Returns the field value of an `X509NameEntry`.
1340    #[corresponds(X509_NAME_ENTRY_get_data)]
1341    pub fn data(&self) -> &Asn1StringRef {
1342        unsafe {
1343            let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1344            Asn1StringRef::from_ptr(data as *mut _)
1345        }
1346    }
1347
1348    /// Returns the `Asn1Object` value of an `X509NameEntry`.
1349    /// This is useful for finding out about the actual `Nid` when iterating over all `X509NameEntries`.
1350    #[corresponds(X509_NAME_ENTRY_get_object)]
1351    pub fn object(&self) -> &Asn1ObjectRef {
1352        unsafe {
1353            let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1354            Asn1ObjectRef::from_ptr(object as *mut _)
1355        }
1356    }
1357}
1358
1359impl fmt::Debug for X509NameEntryRef {
1360    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1361        formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1362    }
1363}
1364
1365/// A builder used to construct an `X509Req`.
1366pub struct X509ReqBuilder(X509Req);
1367
1368impl X509ReqBuilder {
1369    /// Returns a builder for a certificate request.
1370    #[corresponds(X509_REQ_new)]
1371    pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1372        unsafe {
1373            ffi::init();
1374            cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1375        }
1376    }
1377
1378    /// Set the numerical value of the version field.
1379    #[corresponds(X509_REQ_set_version)]
1380    #[allow(clippy::useless_conversion)]
1381    pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1382        unsafe {
1383            cvt(ffi::X509_REQ_set_version(
1384                self.0.as_ptr(),
1385                version as c_long,
1386            ))
1387            .map(|_| ())
1388        }
1389    }
1390
1391    /// Set the issuer name.
1392    #[corresponds(X509_REQ_set_subject_name)]
1393    pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1394        unsafe {
1395            cvt(ffi::X509_REQ_set_subject_name(
1396                self.0.as_ptr(),
1397                subject_name.as_ptr(),
1398            ))
1399            .map(|_| ())
1400        }
1401    }
1402
1403    /// Set the public key.
1404    #[corresponds(X509_REQ_set_pubkey)]
1405    pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1406    where
1407        T: HasPublic,
1408    {
1409        unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1410    }
1411
1412    /// Return an `X509v3Context`. This context object can be used to construct
1413    /// certain `X509` extensions.
1414    pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1415        unsafe {
1416            let mut ctx = mem::zeroed();
1417
1418            ffi::X509V3_set_ctx(
1419                &mut ctx,
1420                ptr::null_mut(),
1421                ptr::null_mut(),
1422                self.0.as_ptr(),
1423                ptr::null_mut(),
1424                0,
1425            );
1426
1427            // nodb case taken care of since we zeroed ctx above
1428            if let Some(conf) = conf {
1429                ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1430            }
1431
1432            X509v3Context(ctx, PhantomData)
1433        }
1434    }
1435
1436    /// Permits any number of extension fields to be added to the certificate.
1437    pub fn add_extensions(
1438        &mut self,
1439        extensions: &StackRef<X509Extension>,
1440    ) -> Result<(), ErrorStack> {
1441        unsafe {
1442            cvt(ffi::X509_REQ_add_extensions(
1443                self.0.as_ptr(),
1444                extensions.as_ptr(),
1445            ))
1446            .map(|_| ())
1447        }
1448    }
1449
1450    /// Sign the request using a private key.
1451    #[corresponds(X509_REQ_sign)]
1452    pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1453    where
1454        T: HasPrivate,
1455    {
1456        unsafe {
1457            cvt(ffi::X509_REQ_sign(
1458                self.0.as_ptr(),
1459                key.as_ptr(),
1460                hash.as_ptr(),
1461            ))
1462            .map(|_| ())
1463        }
1464    }
1465
1466    /// Returns the `X509Req`.
1467    pub fn build(self) -> X509Req {
1468        self.0
1469    }
1470}
1471
1472foreign_type_and_impl_send_sync! {
1473    type CType = ffi::X509_REQ;
1474    fn drop = ffi::X509_REQ_free;
1475
1476    /// An `X509` certificate request.
1477    pub struct X509Req;
1478    /// Reference to `X509Req`.
1479    pub struct X509ReqRef;
1480}
1481
1482impl X509Req {
1483    /// A builder for `X509Req`.
1484    pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1485        X509ReqBuilder::new()
1486    }
1487
1488    from_pem! {
1489        /// Deserializes a PEM-encoded PKCS#10 certificate request structure.
1490        ///
1491        /// The input should have a header of `-----BEGIN CERTIFICATE REQUEST-----`.
1492        ///
1493        /// This corresponds to [`PEM_read_bio_X509_REQ`].
1494        ///
1495        /// [`PEM_read_bio_X509_REQ`]: https://docs.openssl.org/master/man3/PEM_read_bio_X509_REQ/
1496        from_pem,
1497        X509Req,
1498        ffi::PEM_read_bio_X509_REQ
1499    }
1500
1501    from_der! {
1502        /// Deserializes a DER-encoded PKCS#10 certificate request structure.
1503        ///
1504        /// This corresponds to [`d2i_X509_REQ`].
1505        ///
1506        /// [`d2i_X509_REQ`]: https://docs.openssl.org/master/man3/d2i_X509_REQ/
1507        from_der,
1508        X509Req,
1509        ffi::d2i_X509_REQ
1510    }
1511}
1512
1513impl X509ReqRef {
1514    to_pem! {
1515        /// Serializes the certificate request to a PEM-encoded PKCS#10 structure.
1516        ///
1517        /// The output will have a header of `-----BEGIN CERTIFICATE REQUEST-----`.
1518        ///
1519        /// This corresponds to [`PEM_write_bio_X509_REQ`].
1520        ///
1521        /// [`PEM_write_bio_X509_REQ`]: https://docs.openssl.org/master/man3/PEM_write_bio_X509_REQ/
1522        to_pem,
1523        ffi::PEM_write_bio_X509_REQ
1524    }
1525
1526    to_der! {
1527        /// Serializes the certificate request to a DER-encoded PKCS#10 structure.
1528        ///
1529        /// This corresponds to [`i2d_X509_REQ`].
1530        ///
1531        /// [`i2d_X509_REQ`]: https://docs.openssl.org/master/man3/i2d_X509_REQ/
1532        to_der,
1533        ffi::i2d_X509_REQ
1534    }
1535
1536    to_pem! {
1537        /// Converts the request to human readable text.
1538        #[corresponds(X509_Req_print)]
1539        to_text,
1540        ffi::X509_REQ_print
1541    }
1542
1543    /// Returns the numerical value of the version field of the certificate request.
1544    #[corresponds(X509_REQ_get_version)]
1545    #[allow(clippy::unnecessary_cast)]
1546    pub fn version(&self) -> i32 {
1547        unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1548    }
1549
1550    /// Returns the subject name of the certificate request.
1551    #[corresponds(X509_REQ_get_subject_name)]
1552    pub fn subject_name(&self) -> &X509NameRef {
1553        unsafe {
1554            let name = X509_REQ_get_subject_name(self.as_ptr());
1555            X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1556        }
1557    }
1558
1559    /// Returns the public key of the certificate request.
1560    #[corresponds(X509_REQ_get_pubkey)]
1561    pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1562        unsafe {
1563            let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1564            Ok(PKey::from_ptr(key))
1565        }
1566    }
1567
1568    /// Check if the certificate request is signed using the given public key.
1569    ///
1570    /// Returns `true` if verification succeeds.
1571    #[corresponds(X509_REQ_verify)]
1572    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1573    where
1574        T: HasPublic,
1575    {
1576        unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1577    }
1578
1579    /// Returns the extensions of the certificate request.
1580    #[corresponds(X509_REQ_get_extensions)]
1581    pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1582        unsafe {
1583            let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1584            Ok(Stack::from_ptr(extensions))
1585        }
1586    }
1587}
1588
1589/// The reason that a certificate was revoked.
1590#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1591pub struct CrlReason(c_int);
1592
1593#[allow(missing_docs)] // no need to document the constants
1594impl CrlReason {
1595    pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1596    pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1597    pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1598    pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1599    pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1600    pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1601    pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1602    pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1603    pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1604    pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1605
1606    /// Constructs an `CrlReason` from a raw OpenSSL value.
1607    pub const fn from_raw(value: c_int) -> Self {
1608        CrlReason(value)
1609    }
1610
1611    /// Returns the raw OpenSSL value represented by this type.
1612    pub const fn as_raw(&self) -> c_int {
1613        self.0
1614    }
1615}
1616
1617/// A builder used to construct an `X509Revoked`.
1618pub struct X509RevokedBuilder(X509Revoked);
1619
1620impl X509RevokedBuilder {
1621    /// Creates a new builder.
1622    #[corresponds(X509_REVOKED_new)]
1623    pub fn new() -> Result<Self, ErrorStack> {
1624        unsafe {
1625            ffi::init();
1626            cvt_p(ffi::X509_REVOKED_new()).map(|p| X509RevokedBuilder(X509Revoked(p)))
1627        }
1628    }
1629
1630    /// Set the revocation date of the `X509Revoked`.
1631    #[corresponds(X509_REVOKED_set_revocationDate)]
1632    pub fn set_revocation_date(&mut self, date: &Asn1TimeRef) -> Result<(), ErrorStack> {
1633        unsafe {
1634            cvt(ffi::X509_REVOKED_set_revocationDate(
1635                self.0.as_ptr(),
1636                date.as_ptr(),
1637            ))
1638            .map(|_| ())
1639        }
1640    }
1641
1642    /// Set the serial number of the `X509Revoked`.
1643    #[corresponds(X509_REVOKED_set_serialNumber)]
1644    pub fn set_serial_number(&mut self, serial: &Asn1IntegerRef) -> Result<(), ErrorStack> {
1645        unsafe {
1646            cvt(ffi::X509_REVOKED_set_serialNumber(
1647                self.0.as_ptr(),
1648                serial.as_ptr(),
1649            ))
1650            .map(|_| ())
1651        }
1652    }
1653
1654    /// Consumes the builder, returning the `X509Revoked`.
1655    pub fn build(self) -> X509Revoked {
1656        self.0
1657    }
1658}
1659
1660foreign_type_and_impl_send_sync! {
1661    type CType = ffi::X509_REVOKED;
1662    fn drop = ffi::X509_REVOKED_free;
1663
1664    /// An `X509` certificate revocation status.
1665    pub struct X509Revoked;
1666    /// Reference to `X509Revoked`.
1667    pub struct X509RevokedRef;
1668}
1669
1670impl Stackable for X509Revoked {
1671    type StackType = ffi::stack_st_X509_REVOKED;
1672}
1673
1674impl X509Revoked {
1675    from_der! {
1676        /// Deserializes a DER-encoded certificate revocation status
1677        #[corresponds(d2i_X509_REVOKED)]
1678        from_der,
1679        X509Revoked,
1680        ffi::d2i_X509_REVOKED
1681    }
1682}
1683
1684impl X509RevokedRef {
1685    to_der! {
1686        /// Serializes the certificate request to a DER-encoded certificate revocation status
1687        #[corresponds(d2i_X509_REVOKED)]
1688        to_der,
1689        ffi::i2d_X509_REVOKED
1690    }
1691
1692    /// Copies the entry to a new `X509Revoked`.
1693    #[corresponds(X509_REVOKED_dup)]
1694    pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1695        unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1696    }
1697
1698    /// Get the date that the certificate was revoked
1699    #[corresponds(X509_REVOKED_get0_revocationDate)]
1700    pub fn revocation_date(&self) -> &Asn1TimeRef {
1701        unsafe {
1702            let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1703            assert!(!r.is_null());
1704            Asn1TimeRef::from_ptr(r as *mut _)
1705        }
1706    }
1707
1708    /// Get the serial number of the revoked certificate
1709    #[corresponds(X509_REVOKED_get0_serialNumber)]
1710    pub fn serial_number(&self) -> &Asn1IntegerRef {
1711        unsafe {
1712            let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1713            assert!(!r.is_null());
1714            Asn1IntegerRef::from_ptr(r as *mut _)
1715        }
1716    }
1717
1718    /// Get the criticality and value of an extension.
1719    ///
1720    /// This returns None if the extension is not present or occurs multiple times.
1721    #[corresponds(X509_REVOKED_get_ext_d2i)]
1722    pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1723        let mut critical = -1;
1724        let out = unsafe {
1725            // SAFETY: self.as_ptr() is a valid pointer to an X509_REVOKED.
1726            let ext = ffi::X509_REVOKED_get_ext_d2i(
1727                self.as_ptr(),
1728                T::NID.as_raw(),
1729                &mut critical as *mut _,
1730                ptr::null_mut(),
1731            );
1732            // SAFETY: Extensions's contract promises that the type returned by
1733            // OpenSSL here is T::Output.
1734            T::Output::from_ptr_opt(ext as *mut _)
1735        };
1736        match (critical, out) {
1737            (0, Some(out)) => Ok(Some((false, out))),
1738            (1, Some(out)) => Ok(Some((true, out))),
1739            // -1 means the extension wasn't found, -2 means multiple were found.
1740            (-1 | -2, _) => Ok(None),
1741            // A critical value of 0 or 1 suggests success, but a null pointer
1742            // was returned so something went wrong.
1743            (0 | 1, None) => Err(ErrorStack::get()),
1744            (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1745        }
1746    }
1747}
1748
1749/// The CRL entry extension identifying the reason for revocation see [`CrlReason`],
1750/// this is as defined in RFC 5280 Section 5.3.1.
1751pub enum ReasonCode {}
1752
1753// SAFETY: ReasonCode is defined to be an Asn1Enumerated in the RFC
1754// and in OpenSSL.
1755unsafe impl ExtensionType for ReasonCode {
1756    const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1757
1758    type Output = Asn1Enumerated;
1759}
1760
1761/// The CRL entry extension identifying the issuer of a certificate used in
1762/// indirect CRLs, as defined in RFC 5280 Section 5.3.3.
1763pub enum CertificateIssuer {}
1764
1765// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC
1766// and in OpenSSL.
1767unsafe impl ExtensionType for CertificateIssuer {
1768    const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1769
1770    type Output = Stack<GeneralName>;
1771}
1772
1773/// The CRL extension identifying how to access information and services for the issuer of the CRL
1774pub enum AuthorityInformationAccess {}
1775
1776// SAFETY: AuthorityInformationAccess is defined to be a stack of AccessDescription in the RFC
1777// and in OpenSSL.
1778unsafe impl ExtensionType for AuthorityInformationAccess {
1779    const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1780
1781    type Output = Stack<AccessDescription>;
1782}
1783
1784// SAFETY: CrlNumber is defined to be an Asn1Integer in the RFC
1785// and in OpenSSL.
1786unsafe impl ExtensionType for CrlNumber {
1787    const NID: Nid = Nid::CRL_NUMBER;
1788
1789    type Output = Asn1Integer;
1790}
1791
1792/// A builder used to construct a version 2 `X509Crl`.
1793pub struct X509CrlBuilder(X509Crl);
1794
1795impl X509CrlBuilder {
1796    /// Creates a new builder.
1797    #[corresponds(X509_CRL_new)]
1798    pub fn new() -> Result<Self, ErrorStack> {
1799        unsafe {
1800            ffi::init();
1801            let ptr = cvt_p(ffi::X509_CRL_new())?;
1802            cvt(ffi::X509_CRL_set_version(ptr, 1)).map(|_| ())?;
1803
1804            Ok(Self(X509Crl(ptr)))
1805        }
1806    }
1807
1808    /// Set the issuer name of the CRL.
1809    #[corresponds(X509_CRL_set_issuer_name)]
1810    pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
1811        unsafe {
1812            cvt(ffi::X509_CRL_set_issuer_name(
1813                self.0.as_ptr(),
1814                issuer_name.as_ptr(),
1815            ))
1816            .map(|_| ())
1817        }
1818    }
1819
1820    /// Set the lastUpdate (thisUpdate) time indicating when the CRL was issued.
1821    #[corresponds(X509_CRL_set1_lastUpdate)]
1822    pub fn set_last_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
1823        unsafe { cvt(ffi::X509_CRL_set1_lastUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
1824    }
1825
1826    /// Set the nextUpdate timestamp indicating when a newer CRL is expected.
1827    #[corresponds(X509_CRL_set1_nextUpdate)]
1828    pub fn set_next_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
1829        unsafe { cvt(ffi::X509_CRL_set1_nextUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
1830    }
1831
1832    /// Add an X509 extension value to the CRL.
1833    ///
1834    /// This works just as `append_extension` except it takes ownership of the `X509Extension`.
1835    pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
1836        self.append_extension2(&extension)
1837    }
1838
1839    /// Add an X509 extension value to the CRL.
1840    #[corresponds(X509_CRL_add_ext)]
1841    pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
1842        unsafe {
1843            cvt(ffi::X509_CRL_add_ext(
1844                self.0.as_ptr(),
1845                extension.as_ptr(),
1846                -1,
1847            ))
1848            .map(|_| ())
1849        }
1850    }
1851
1852    /// Add a revoked certificate to the CRL.
1853    #[corresponds(X509_CRL_add0_revoked)]
1854    pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
1855        unsafe {
1856            let r = cvt(ffi::X509_CRL_add0_revoked(
1857                self.0.as_ptr(),
1858                revoked.as_ptr(),
1859            ))
1860            .map(|_| ());
1861            std::mem::forget(revoked);
1862            r
1863        }
1864    }
1865
1866    /// Sort the CRL.
1867    #[corresponds(X509_CRL_sort)]
1868    pub fn sort(&mut self) -> Result<(), ErrorStack> {
1869        unsafe { cvt(ffi::X509_CRL_sort(self.0.as_ptr())).map(|_| ()) }
1870    }
1871
1872    /// Signs the CRL with a private key.
1873    #[corresponds(X509_CRL_sign)]
1874    pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1875    where
1876        T: HasPrivate,
1877    {
1878        unsafe {
1879            cvt(ffi::X509_CRL_sign(
1880                self.0.as_ptr(),
1881                key.as_ptr(),
1882                hash.as_ptr(),
1883            ))
1884            .map(|_| ())
1885        }
1886    }
1887
1888    /// Consumes the builder, returning the CRL.
1889    ///
1890    /// # Panics
1891    ///
1892    /// Panics if any of nextUpdate, revoked, AuthorityKeyIdentifier or CrlNumber is missing
1893    pub fn build(self) -> Result<X509Crl, ErrorStack> {
1894        unsafe {
1895            let loc = ffi::X509_CRL_get_ext_by_NID(
1896                self.0.as_ptr(),
1897                Nid::AUTHORITY_KEY_IDENTIFIER.as_raw(),
1898                -1,
1899            );
1900            assert!(
1901                loc >= 0,
1902                "CRL must have an Authority Key Identifier extension"
1903            );
1904            let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
1905            assert_eq!(
1906                ffi::X509_EXTENSION_get_critical(ext),
1907                0,
1908                "Authority Key Identifier extension must not be critical"
1909            );
1910
1911            let loc = ffi::X509_CRL_get_ext_by_NID(self.0.as_ptr(), Nid::CRL_NUMBER.as_raw(), -1);
1912            assert!(loc >= 0, "CRL must have a Crl Number extension");
1913            let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
1914            assert_eq!(
1915                ffi::X509_EXTENSION_get_critical(ext),
1916                0,
1917                "Crl Number extension must not be critical"
1918            );
1919
1920            assert!(
1921                !X509_CRL_get0_nextUpdate(self.0.as_ptr()).is_null(),
1922                "CRL must have nextUpdate time set"
1923            );
1924            let revoked = self.0.get_revoked();
1925            assert!(
1926                // XXX - switch to is_none_or() once MSRV is 1.82.
1927                revoked.is_none() || revoked.is_some_and(|r| !r.is_empty()),
1928                "Revoked must be absent or non-empty"
1929            );
1930        }
1931
1932        Ok(self.0)
1933    }
1934}
1935
1936foreign_type_and_impl_send_sync! {
1937    type CType = ffi::X509_CRL;
1938    fn drop = ffi::X509_CRL_free;
1939
1940    /// An `X509` certificate revocation list.
1941    pub struct X509Crl;
1942    /// Reference to `X509Crl`.
1943    pub struct X509CrlRef;
1944}
1945
1946/// The status of a certificate in a revoction list
1947///
1948/// Corresponds to the return value from the [`X509_CRL_get0_by_*`] methods.
1949///
1950/// [`X509_CRL_get0_by_*`]: https://docs.openssl.org/master/man3/X509_CRL_get0_by_serial/
1951pub enum CrlStatus<'a> {
1952    /// The certificate is not present in the list
1953    NotRevoked,
1954    /// The certificate is in the list and is revoked
1955    Revoked(&'a X509RevokedRef),
1956    /// The certificate is in the list, but has the "removeFromCrl" status.
1957    ///
1958    /// This can occur if the certificate was revoked with the "CertificateHold"
1959    /// reason, and has since been unrevoked.
1960    RemoveFromCrl(&'a X509RevokedRef),
1961}
1962
1963impl<'a> CrlStatus<'a> {
1964    // Helper used by the X509_CRL_get0_by_* methods to convert their return
1965    // value to the status enum.
1966    // Safety note: the returned CrlStatus must not outlive the owner of the
1967    // revoked_entry pointer.
1968    unsafe fn from_ffi_status(
1969        status: c_int,
1970        revoked_entry: *mut ffi::X509_REVOKED,
1971    ) -> CrlStatus<'a> {
1972        match status {
1973            0 => CrlStatus::NotRevoked,
1974            1 => {
1975                assert!(!revoked_entry.is_null());
1976                CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1977            }
1978            2 => {
1979                assert!(!revoked_entry.is_null());
1980                CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1981            }
1982            _ => unreachable!(
1983                "{}",
1984                "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1985            ),
1986        }
1987    }
1988}
1989
1990impl X509Crl {
1991    from_pem! {
1992        /// Deserializes a PEM-encoded Certificate Revocation List
1993        ///
1994        /// The input should have a header of `-----BEGIN X509 CRL-----`.
1995        #[corresponds(PEM_read_bio_X509_CRL)]
1996        from_pem,
1997        X509Crl,
1998        ffi::PEM_read_bio_X509_CRL
1999    }
2000
2001    from_der! {
2002        /// Deserializes a DER-encoded Certificate Revocation List
2003        #[corresponds(d2i_X509_CRL)]
2004        from_der,
2005        X509Crl,
2006        ffi::d2i_X509_CRL
2007    }
2008}
2009
2010impl X509CrlRef {
2011    to_pem! {
2012        /// Serializes the certificate request to a PEM-encoded Certificate Revocation List.
2013        ///
2014        /// The output will have a header of `-----BEGIN X509 CRL-----`.
2015        #[corresponds(PEM_write_bio_X509_CRL)]
2016        to_pem,
2017        ffi::PEM_write_bio_X509_CRL
2018    }
2019
2020    to_der! {
2021        /// Serializes the certificate request to a DER-encoded Certificate Revocation List.
2022        #[corresponds(i2d_X509_CRL)]
2023        to_der,
2024        ffi::i2d_X509_CRL
2025    }
2026
2027    /// Get the stack of revocation entries
2028    pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2029        unsafe {
2030            let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2031            if revoked.is_null() {
2032                None
2033            } else {
2034                Some(StackRef::from_ptr(revoked))
2035            }
2036        }
2037    }
2038
2039    /// Returns the CRL's `lastUpdate` time.
2040    #[corresponds(X509_CRL_get0_lastUpdate)]
2041    pub fn last_update(&self) -> &Asn1TimeRef {
2042        unsafe {
2043            let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2044            assert!(!date.is_null());
2045            Asn1TimeRef::from_ptr(date as *mut _)
2046        }
2047    }
2048
2049    /// Returns the CRL's `nextUpdate` time.
2050    ///
2051    /// If the `nextUpdate` field is missing, returns `None`.
2052    #[corresponds(X509_CRL_get0_nextUpdate)]
2053    pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2054        unsafe {
2055            let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2056            Asn1TimeRef::from_const_ptr_opt(date)
2057        }
2058    }
2059
2060    /// Get the revocation status of a certificate by its serial number
2061    #[corresponds(X509_CRL_get0_by_serial)]
2062    pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2063        unsafe {
2064            let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2065            let status =
2066                ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2067            CrlStatus::from_ffi_status(status, ret)
2068        }
2069    }
2070
2071    /// Get the revocation status of a certificate
2072    #[corresponds(X509_CRL_get0_by_cert)]
2073    pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2074        unsafe {
2075            let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2076            let status =
2077                ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2078            CrlStatus::from_ffi_status(status, ret)
2079        }
2080    }
2081
2082    /// Get the issuer name from the revocation list.
2083    #[corresponds(X509_CRL_get_issuer)]
2084    pub fn issuer_name(&self) -> &X509NameRef {
2085        unsafe {
2086            let name = X509_CRL_get_issuer(self.as_ptr());
2087            assert!(!name.is_null());
2088            X509NameRef::from_ptr(name as *mut _)
2089        }
2090    }
2091
2092    /// Check if the CRL is signed using the given public key.
2093    ///
2094    /// Only the signature is checked: no other checks (such as certificate chain validity)
2095    /// are performed.
2096    ///
2097    /// Returns `true` if verification succeeds.
2098    #[corresponds(X509_CRL_verify)]
2099    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2100    where
2101        T: HasPublic,
2102    {
2103        unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2104    }
2105
2106    /// Get the criticality and value of an extension.
2107    ///
2108    /// This returns None if the extension is not present or occurs multiple times.
2109    #[corresponds(X509_CRL_get_ext_d2i)]
2110    pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2111        let mut critical = -1;
2112        let out = unsafe {
2113            // SAFETY: self.as_ptr() is a valid pointer to an X509_CRL.
2114            let ext = ffi::X509_CRL_get_ext_d2i(
2115                self.as_ptr(),
2116                T::NID.as_raw(),
2117                &mut critical as *mut _,
2118                ptr::null_mut(),
2119            );
2120            // SAFETY: Extensions's contract promises that the type returned by
2121            // OpenSSL here is T::Output.
2122            T::Output::from_ptr_opt(ext as *mut _)
2123        };
2124        match (critical, out) {
2125            (0, Some(out)) => Ok(Some((false, out))),
2126            (1, Some(out)) => Ok(Some((true, out))),
2127            // -1 means the extension wasn't found, -2 means multiple were found.
2128            (-1 | -2, _) => Ok(None),
2129            // A critical value of 0 or 1 suggests success, but a null pointer
2130            // was returned so something went wrong.
2131            (0 | 1, None) => Err(ErrorStack::get()),
2132            (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2133        }
2134    }
2135}
2136
2137/// The result of peer certificate verification.
2138#[derive(Copy, Clone, PartialEq, Eq)]
2139pub struct X509VerifyResult(c_int);
2140
2141impl fmt::Debug for X509VerifyResult {
2142    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2143        fmt.debug_struct("X509VerifyResult")
2144            .field("code", &self.0)
2145            .field("error", &self.error_string())
2146            .finish()
2147    }
2148}
2149
2150impl fmt::Display for X509VerifyResult {
2151    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2152        fmt.write_str(self.error_string())
2153    }
2154}
2155
2156impl Error for X509VerifyResult {}
2157
2158impl X509VerifyResult {
2159    /// Creates an `X509VerifyResult` from a raw error number.
2160    ///
2161    /// # Safety
2162    ///
2163    /// Some methods on `X509VerifyResult` are not thread safe if the error
2164    /// number is invalid.
2165    pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2166        X509VerifyResult(err)
2167    }
2168
2169    /// Return the integer representation of an `X509VerifyResult`.
2170    #[allow(clippy::trivially_copy_pass_by_ref)]
2171    pub fn as_raw(&self) -> c_int {
2172        self.0
2173    }
2174
2175    /// Return a human readable error string from the verification error.
2176    #[corresponds(X509_verify_cert_error_string)]
2177    #[allow(clippy::trivially_copy_pass_by_ref)]
2178    pub fn error_string(&self) -> &'static str {
2179        ffi::init();
2180
2181        unsafe {
2182            let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2183            str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2184        }
2185    }
2186
2187    /// Successful peer certificate verification.
2188    pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2189    /// Application verification failure.
2190    pub const APPLICATION_VERIFICATION: X509VerifyResult =
2191        X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2192}
2193
2194foreign_type_and_impl_send_sync! {
2195    type CType = ffi::GENERAL_NAME;
2196    fn drop = ffi::GENERAL_NAME_free;
2197
2198    /// An `X509` certificate alternative names.
2199    pub struct GeneralName;
2200    /// Reference to `GeneralName`.
2201    pub struct GeneralNameRef;
2202}
2203
2204impl GeneralName {
2205    unsafe fn new(
2206        type_: c_int,
2207        asn1_type: Asn1Type,
2208        value: &[u8],
2209    ) -> Result<GeneralName, ErrorStack> {
2210        ffi::init();
2211        let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2212        (*gn.as_ptr()).type_ = type_;
2213        let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2214        ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2215
2216        #[cfg(any(boringssl, awslc))]
2217        {
2218            (*gn.as_ptr()).d.ptr = s.cast();
2219        }
2220        #[cfg(not(any(boringssl, awslc)))]
2221        {
2222            (*gn.as_ptr()).d = s.cast();
2223        }
2224
2225        Ok(gn)
2226    }
2227
2228    pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2229        unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2230    }
2231
2232    pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2233        unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2234    }
2235
2236    pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2237        unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2238    }
2239
2240    pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2241        match ip {
2242            IpAddr::V4(addr) => unsafe {
2243                GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2244            },
2245            IpAddr::V6(addr) => unsafe {
2246                GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2247            },
2248        }
2249    }
2250
2251    pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2252        unsafe {
2253            ffi::init();
2254            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2255            (*gn).type_ = ffi::GEN_RID;
2256
2257            #[cfg(any(boringssl, awslc))]
2258            {
2259                (*gn).d.registeredID = oid.as_ptr();
2260            }
2261            #[cfg(not(any(boringssl, awslc)))]
2262            {
2263                (*gn).d = oid.as_ptr().cast();
2264            }
2265
2266            mem::forget(oid);
2267
2268            Ok(GeneralName::from_ptr(gn))
2269        }
2270    }
2271
2272    pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2273        unsafe {
2274            ffi::init();
2275
2276            let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2277                ptr::null_mut(),
2278                &mut value.as_ptr().cast(),
2279                value.len().try_into().unwrap(),
2280            ))?;
2281
2282            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2283            (*gn).type_ = ffi::GEN_OTHERNAME;
2284
2285            if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2286                gn,
2287                oid.as_ptr().cast(),
2288                typ,
2289            )) {
2290                ffi::GENERAL_NAME_free(gn);
2291                return Err(e);
2292            }
2293
2294            mem::forget(oid);
2295
2296            Ok(GeneralName::from_ptr(gn))
2297        }
2298    }
2299
2300    pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2301        unsafe {
2302            ffi::init();
2303            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2304            (*gn).type_ = ffi::GEN_DIRNAME;
2305
2306            let dup = match name.to_owned() {
2307                Ok(dup) => dup,
2308                Err(e) => {
2309                    ffi::GENERAL_NAME_free(gn);
2310                    return Err(e);
2311                }
2312            };
2313
2314            #[cfg(any(boringssl, awslc))]
2315            {
2316                (*gn).d.directoryName = dup.as_ptr();
2317            }
2318            #[cfg(not(any(boringssl, awslc)))]
2319            {
2320                (*gn).d = dup.as_ptr().cast();
2321            }
2322
2323            std::mem::forget(dup);
2324
2325            Ok(GeneralName::from_ptr(gn))
2326        }
2327    }
2328}
2329
2330impl GeneralNameRef {
2331    fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2332        unsafe {
2333            if (*self.as_ptr()).type_ != ffi_type {
2334                return None;
2335            }
2336
2337            #[cfg(any(boringssl, awslc))]
2338            let d = (*self.as_ptr()).d.ptr;
2339            #[cfg(not(any(boringssl, awslc)))]
2340            let d = (*self.as_ptr()).d;
2341
2342            let ptr = ASN1_STRING_get0_data(d as *mut _);
2343            let len = ffi::ASN1_STRING_length(d as *mut _);
2344
2345            #[allow(clippy::unnecessary_cast)]
2346            let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2347            // IA5Strings are stated to be ASCII (specifically IA5). Hopefully
2348            // OpenSSL checks that when loading a certificate but if not we'll
2349            // use this instead of from_utf8_unchecked just in case.
2350            str::from_utf8(slice).ok()
2351        }
2352    }
2353
2354    /// Returns the contents of this `GeneralName` if it is an `rfc822Name`.
2355    pub fn email(&self) -> Option<&str> {
2356        self.ia5_string(ffi::GEN_EMAIL)
2357    }
2358
2359    /// Returns the contents of this `GeneralName` if it is a `directoryName`.
2360    pub fn directory_name(&self) -> Option<&X509NameRef> {
2361        unsafe {
2362            if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2363                return None;
2364            }
2365
2366            #[cfg(any(boringssl, awslc))]
2367            let d = (*self.as_ptr()).d.ptr;
2368            #[cfg(not(any(boringssl, awslc)))]
2369            let d = (*self.as_ptr()).d;
2370
2371            Some(X509NameRef::from_const_ptr(d as *const _))
2372        }
2373    }
2374
2375    /// Returns the contents of this `GeneralName` if it is a `dNSName`.
2376    pub fn dnsname(&self) -> Option<&str> {
2377        self.ia5_string(ffi::GEN_DNS)
2378    }
2379
2380    /// Returns the contents of this `GeneralName` if it is an `uniformResourceIdentifier`.
2381    pub fn uri(&self) -> Option<&str> {
2382        self.ia5_string(ffi::GEN_URI)
2383    }
2384
2385    /// Returns the contents of this `GeneralName` if it is an `iPAddress`.
2386    pub fn ipaddress(&self) -> Option<&[u8]> {
2387        unsafe {
2388            if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2389                return None;
2390            }
2391            #[cfg(any(boringssl, awslc))]
2392            let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2393            #[cfg(not(any(boringssl, awslc)))]
2394            let d = (*self.as_ptr()).d;
2395
2396            let ptr = ASN1_STRING_get0_data(d as *mut _);
2397            let len = ffi::ASN1_STRING_length(d as *mut _);
2398
2399            #[allow(clippy::unnecessary_cast)]
2400            Some(util::from_raw_parts(ptr as *const u8, len as usize))
2401        }
2402    }
2403}
2404
2405impl fmt::Debug for GeneralNameRef {
2406    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2407        if let Some(email) = self.email() {
2408            formatter.write_str(email)
2409        } else if let Some(dnsname) = self.dnsname() {
2410            formatter.write_str(dnsname)
2411        } else if let Some(uri) = self.uri() {
2412            formatter.write_str(uri)
2413        } else if let Some(ipaddress) = self.ipaddress() {
2414            let address = <[u8; 16]>::try_from(ipaddress)
2415                .map(IpAddr::from)
2416                .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2417            match address {
2418                Ok(a) => fmt::Debug::fmt(&a, formatter),
2419                Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2420            }
2421        } else {
2422            formatter.write_str("(empty)")
2423        }
2424    }
2425}
2426
2427impl Stackable for GeneralName {
2428    type StackType = ffi::stack_st_GENERAL_NAME;
2429}
2430
2431foreign_type_and_impl_send_sync! {
2432    type CType = ffi::DIST_POINT;
2433    fn drop = ffi::DIST_POINT_free;
2434
2435    /// A `X509` distribution point.
2436    pub struct DistPoint;
2437    /// Reference to `DistPoint`.
2438    pub struct DistPointRef;
2439}
2440
2441impl DistPointRef {
2442    /// Returns the name of this distribution point if it exists
2443    pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2444        unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2445    }
2446}
2447
2448foreign_type_and_impl_send_sync! {
2449    type CType = ffi::DIST_POINT_NAME;
2450    fn drop = ffi::DIST_POINT_NAME_free;
2451
2452    /// A `X509` distribution point.
2453    pub struct DistPointName;
2454    /// Reference to `DistPointName`.
2455    pub struct DistPointNameRef;
2456}
2457
2458impl DistPointNameRef {
2459    /// Returns the contents of this DistPointName if it is a fullname.
2460    pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2461        unsafe {
2462            if (*self.as_ptr()).type_ != 0 {
2463                return None;
2464            }
2465            StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2466        }
2467    }
2468}
2469
2470impl Stackable for DistPoint {
2471    type StackType = ffi::stack_st_DIST_POINT;
2472}
2473
2474foreign_type_and_impl_send_sync! {
2475    type CType = ffi::ACCESS_DESCRIPTION;
2476    fn drop = ffi::ACCESS_DESCRIPTION_free;
2477
2478    /// `AccessDescription` of certificate authority information.
2479    pub struct AccessDescription;
2480    /// Reference to `AccessDescription`.
2481    pub struct AccessDescriptionRef;
2482}
2483
2484impl AccessDescriptionRef {
2485    /// Returns the access method OID.
2486    pub fn method(&self) -> &Asn1ObjectRef {
2487        unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2488    }
2489
2490    // Returns the access location.
2491    pub fn location(&self) -> &GeneralNameRef {
2492        unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2493    }
2494}
2495
2496impl Stackable for AccessDescription {
2497    type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2498}
2499
2500foreign_type_and_impl_send_sync! {
2501    type CType = ffi::X509_ALGOR;
2502    fn drop = ffi::X509_ALGOR_free;
2503
2504    /// An `X509` certificate signature algorithm.
2505    pub struct X509Algorithm;
2506    /// Reference to `X509Algorithm`.
2507    pub struct X509AlgorithmRef;
2508}
2509
2510impl X509AlgorithmRef {
2511    /// Returns the ASN.1 OID of this algorithm.
2512    pub fn object(&self) -> &Asn1ObjectRef {
2513        unsafe {
2514            let mut oid = ptr::null();
2515            X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2516            Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2517        }
2518    }
2519}
2520
2521foreign_type_and_impl_send_sync! {
2522    type CType = ffi::X509_OBJECT;
2523    fn drop = X509_OBJECT_free;
2524
2525    /// An `X509` or an X509 certificate revocation list.
2526    pub struct X509Object;
2527    /// Reference to `X509Object`
2528    pub struct X509ObjectRef;
2529}
2530
2531impl X509ObjectRef {
2532    pub fn x509(&self) -> Option<&X509Ref> {
2533        unsafe {
2534            let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2535            X509Ref::from_const_ptr_opt(ptr)
2536        }
2537    }
2538}
2539
2540impl Stackable for X509Object {
2541    type StackType = ffi::stack_st_X509_OBJECT;
2542}
2543
2544use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2545
2546use ffi::{
2547    ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2548    X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2549};
2550
2551use ffi::X509_OBJECT_free;
2552use ffi::X509_OBJECT_get0_X509;
2553
2554use ffi::{
2555    X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2556    X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2557};
2558
2559#[derive(Copy, Clone, PartialEq, Eq)]
2560pub struct X509PurposeId(c_int);
2561
2562impl X509PurposeId {
2563    pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2564    pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2565    pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2566    pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2567    pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2568    pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2569    pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2570    pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2571    pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2572    #[cfg(ossl320)]
2573    pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2574
2575    /// Constructs an `X509PurposeId` from a raw OpenSSL value.
2576    pub fn from_raw(id: c_int) -> Self {
2577        X509PurposeId(id)
2578    }
2579
2580    /// Returns the raw OpenSSL value represented by this type.
2581    pub fn as_raw(&self) -> c_int {
2582        self.0
2583    }
2584}
2585
2586/// A reference to an [`X509_PURPOSE`].
2587pub struct X509PurposeRef(Opaque);
2588
2589/// Implements a wrapper type for the static `X509_PURPOSE` table in OpenSSL.
2590impl ForeignTypeRef for X509PurposeRef {
2591    type CType = ffi::X509_PURPOSE;
2592}
2593
2594impl X509PurposeRef {
2595    /// Get the internal table index of an X509_PURPOSE for a given short name. Valid short
2596    /// names include
2597    ///  - "sslclient",
2598    ///  - "sslserver",
2599    ///  - "nssslserver",
2600    ///  - "smimesign",
2601    ///  - "smimeencrypt",
2602    ///  - "crlsign",
2603    ///  - "any",
2604    ///  - "ocsphelper",
2605    ///  - "timestampsign"
2606    ///
2607    /// The index can be used with `X509PurposeRef::from_idx()` to get the purpose.
2608    #[allow(clippy::unnecessary_cast)]
2609    pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2610        unsafe {
2611            let sname = CString::new(sname).unwrap();
2612            let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2613            Ok(purpose)
2614        }
2615    }
2616    /// Get an `X509PurposeRef` for a given index value. The index can be obtained from e.g.
2617    /// `X509PurposeRef::get_by_sname()`.
2618    #[corresponds(X509_PURPOSE_get0)]
2619    pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2620        unsafe {
2621            let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2622            Ok(X509PurposeRef::from_const_ptr(ptr))
2623        }
2624    }
2625
2626    /// Get the purpose value from an X509Purpose structure. This value is one of
2627    /// - `X509_PURPOSE_SSL_CLIENT`
2628    /// - `X509_PURPOSE_SSL_SERVER`
2629    /// - `X509_PURPOSE_NS_SSL_SERVER`
2630    /// - `X509_PURPOSE_SMIME_SIGN`
2631    /// - `X509_PURPOSE_SMIME_ENCRYPT`
2632    /// - `X509_PURPOSE_CRL_SIGN`
2633    /// - `X509_PURPOSE_ANY`
2634    /// - `X509_PURPOSE_OCSP_HELPER`
2635    /// - `X509_PURPOSE_TIMESTAMP_SIGN`
2636    pub fn purpose(&self) -> X509PurposeId {
2637        unsafe {
2638            let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2639            X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2640        }
2641    }
2642}