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