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