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