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