1use bitflags::bitflags;
11use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
12use libc::{c_int, c_long, c_uint, c_void};
13use std::cmp::{self, Ordering};
14use std::convert::{TryFrom, TryInto};
15use std::error::Error;
16use std::ffi::{CStr, CString};
17use std::fmt;
18use std::marker::PhantomData;
19use std::mem;
20use std::net::IpAddr;
21use std::path::Path;
22use std::ptr;
23use std::str;
24
25use crate::asn1::{
26 Asn1BitStringRef, Asn1Enumerated, Asn1Integer, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
27 Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
28};
29use crate::bio::MemBioSlice;
30use crate::conf::ConfRef;
31use crate::error::ErrorStack;
32use crate::ex_data::Index;
33use crate::hash::{DigestBytes, MessageDigest};
34use crate::nid::Nid;
35use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
36use crate::ssl::SslRef;
37use crate::stack::{Stack, StackRef, Stackable};
38use crate::string::OpensslString;
39use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
40use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
41use openssl_macros::corresponds;
42
43pub use crate::x509::extension::CrlNumber;
44
45pub mod verify;
46
47pub mod extension;
48pub mod store;
49
50#[cfg(test)]
51mod tests;
52
53pub 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 pub struct X509StoreContext;
69
70 pub struct X509StoreContextRef;
72}
73
74impl X509StoreContext {
75 #[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 #[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 #[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 #[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 pub fn init<F, T>(
128 &mut self,
129 trust: &store::X509StoreRef,
130 cert: &X509Ref,
131 cert_chain: &StackRef<X509>,
132 with_context: F,
133 ) -> Result<T, ErrorStack>
134 where
135 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
136 {
137 struct Cleanup<'a>(&'a mut X509StoreContextRef);
138
139 impl Drop for Cleanup<'_> {
140 fn drop(&mut self) {
141 unsafe {
142 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
143 #[cfg(not(any(boringssl, awslc)))]
149 {
150 ffi::X509_STORE_CTX_set_cert(self.0.as_ptr(), ptr::null_mut());
151 ffi::X509_STORE_CTX_set_current_cert(self.0.as_ptr(), ptr::null_mut());
152 }
153 }
154 }
155 }
156
157 unsafe {
158 cvt(ffi::X509_STORE_CTX_init(
159 self.as_ptr(),
160 trust.as_ptr(),
161 cert.as_ptr(),
162 cert_chain.as_ptr(),
163 ))?;
164
165 let cleanup = Cleanup(self);
166 with_context(cleanup.0)
167 }
168 }
169
170 #[corresponds(X509_verify_cert)]
178 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
179 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
180 }
181
182 #[corresponds(X509_STORE_CTX_set_error)]
184 pub fn set_error(&mut self, result: X509VerifyResult) {
185 unsafe {
186 ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
187 }
188 }
189
190 #[corresponds(X509_STORE_CTX_get_current_cert)]
193 pub fn current_cert(&self) -> Option<&X509Ref> {
194 unsafe {
195 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
196 X509Ref::from_const_ptr_opt(ptr)
197 }
198 }
199
200 #[corresponds(X509_STORE_CTX_get_error_depth)]
205 pub fn error_depth(&self) -> u32 {
206 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
207 }
208
209 #[corresponds(X509_STORE_CTX_get0_chain)]
211 pub fn chain(&self) -> Option<&StackRef<X509>> {
212 unsafe {
213 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
214
215 if chain.is_null() {
216 None
217 } else {
218 Some(StackRef::from_ptr(chain))
219 }
220 }
221 }
222}
223
224pub struct X509Builder(X509);
226
227impl X509Builder {
228 #[corresponds(X509_new)]
230 pub fn new() -> Result<X509Builder, ErrorStack> {
231 unsafe {
232 ffi::init();
233 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
234 }
235 }
236
237 #[corresponds(X509_set1_notAfter)]
239 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
240 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
241 }
242
243 #[corresponds(X509_set1_notBefore)]
245 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
246 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
247 }
248
249 #[corresponds(X509_set_version)]
254 #[allow(clippy::useless_conversion)]
255 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
256 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
257 }
258
259 #[corresponds(X509_set_serialNumber)]
261 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
262 unsafe {
263 cvt(ffi::X509_set_serialNumber(
264 self.0.as_ptr(),
265 serial_number.as_ptr(),
266 ))
267 .map(|_| ())
268 }
269 }
270
271 #[corresponds(X509_set_issuer_name)]
273 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
274 unsafe {
275 cvt(ffi::X509_set_issuer_name(
276 self.0.as_ptr(),
277 issuer_name.as_ptr(),
278 ))
279 .map(|_| ())
280 }
281 }
282
283 #[corresponds(X509_set_subject_name)]
302 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
303 unsafe {
304 cvt(ffi::X509_set_subject_name(
305 self.0.as_ptr(),
306 subject_name.as_ptr(),
307 ))
308 .map(|_| ())
309 }
310 }
311
312 #[corresponds(X509_set_pubkey)]
314 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
315 where
316 T: HasPublic,
317 {
318 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
319 }
320
321 #[corresponds(X509V3_set_ctx)]
325 pub fn x509v3_context<'a>(
326 &'a self,
327 issuer: Option<&'a X509Ref>,
328 conf: Option<&'a ConfRef>,
329 ) -> X509v3Context<'a> {
330 unsafe {
331 let mut ctx = mem::zeroed();
332
333 let issuer = match issuer {
334 Some(issuer) => issuer.as_ptr(),
335 None => self.0.as_ptr(),
336 };
337 let subject = self.0.as_ptr();
338 ffi::X509V3_set_ctx(
339 &mut ctx,
340 issuer,
341 subject,
342 ptr::null_mut(),
343 ptr::null_mut(),
344 0,
345 );
346
347 if let Some(conf) = conf {
349 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
350 }
351
352 X509v3Context(ctx, PhantomData)
353 }
354 }
355
356 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
360 self.append_extension2(&extension)
361 }
362
363 #[corresponds(X509_add_ext)]
365 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
366 unsafe {
367 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
368 Ok(())
369 }
370 }
371
372 #[corresponds(X509_sign)]
374 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
375 where
376 T: HasPrivate,
377 {
378 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
379 }
380
381 pub fn build(self) -> X509 {
383 self.0
384 }
385}
386
387bitflags! {
388 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
390 #[repr(transparent)]
391 pub struct X509KeyUsage: u32 {
392 const DIGITAL_SIGNATURE = ffi::X509v3_KU_DIGITAL_SIGNATURE as _;
393 const NON_REPUDIATION = ffi::X509v3_KU_NON_REPUDIATION as _;
394 const KEY_ENCIPHERMENT = ffi::X509v3_KU_KEY_ENCIPHERMENT as _;
395 const DATA_ENCIPHERMENT = ffi::X509v3_KU_DATA_ENCIPHERMENT as _;
396 const KEY_AGREEMENT = ffi::X509v3_KU_KEY_AGREEMENT as _;
397 const KEY_CERT_SIGN = ffi::X509v3_KU_KEY_CERT_SIGN as _;
398 const CRL_SIGN = ffi::X509v3_KU_CRL_SIGN as _;
399 const ENCIPHER_ONLY = ffi::X509v3_KU_ENCIPHER_ONLY as _;
400 const DECIPHER_ONLY = ffi::X509v3_KU_DECIPHER_ONLY as _;
401 }
402}
403
404bitflags! {
405 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
407 #[repr(transparent)]
408 pub struct X509ExtendedKeyUsage: u32 {
409 const SSL_SERVER = ffi::XKU_SSL_SERVER as _;
410 const SSL_CLIENT = ffi::XKU_SSL_CLIENT as _;
411 const SMIME = ffi::XKU_SMIME as _;
412 const CODE_SIGN = ffi::XKU_CODE_SIGN as _;
413 const SGC = ffi::XKU_SGC as _;
414 const OCSP_SIGN = ffi::XKU_OCSP_SIGN as _;
415 const TIMESTAMP = ffi::XKU_TIMESTAMP as _;
416 const DVCS = ffi::XKU_DVCS as _;
417 const ANYEKU = ffi::XKU_ANYEKU as _;
418 }
419}
420
421foreign_type_and_impl_send_sync! {
422 type CType = ffi::X509;
423 fn drop = ffi::X509_free;
424
425 pub struct X509;
427 pub struct X509Ref;
429}
430
431impl X509Ref {
432 #[corresponds(X509_get_subject_name)]
434 pub fn subject_name(&self) -> &X509NameRef {
435 unsafe {
436 let name = ffi::X509_get_subject_name(self.as_ptr());
437 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
438 }
439 }
440
441 #[corresponds(X509_subject_name_hash)]
443 pub fn subject_name_hash(&self) -> u32 {
444 #[allow(clippy::unnecessary_cast)]
445 unsafe {
446 ffi::X509_subject_name_hash(self.as_ptr()) as u32
447 }
448 }
449
450 #[corresponds(X509_get_issuer_name)]
452 pub fn issuer_name(&self) -> &X509NameRef {
453 unsafe {
454 let name = ffi::X509_get_issuer_name(self.as_ptr());
455 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
456 }
457 }
458
459 #[corresponds(X509_issuer_name_hash)]
461 pub fn issuer_name_hash(&self) -> u32 {
462 #[allow(clippy::unnecessary_cast)]
463 unsafe {
464 ffi::X509_issuer_name_hash(self.as_ptr()) as u32
465 }
466 }
467
468 #[corresponds(X509_get_ext_d2i)]
470 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
471 unsafe {
472 let stack = ffi::X509_get_ext_d2i(
473 self.as_ptr(),
474 ffi::NID_subject_alt_name,
475 ptr::null_mut(),
476 ptr::null_mut(),
477 );
478 Stack::from_ptr_opt(stack as *mut _)
479 }
480 }
481
482 #[corresponds(X509_get_ext_d2i)]
484 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
485 unsafe {
486 let stack = ffi::X509_get_ext_d2i(
487 self.as_ptr(),
488 ffi::NID_crl_distribution_points,
489 ptr::null_mut(),
490 ptr::null_mut(),
491 );
492 Stack::from_ptr_opt(stack as *mut _)
493 }
494 }
495
496 #[corresponds(X509_get_ext_d2i)]
498 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
499 unsafe {
500 let stack = ffi::X509_get_ext_d2i(
501 self.as_ptr(),
502 ffi::NID_issuer_alt_name,
503 ptr::null_mut(),
504 ptr::null_mut(),
505 );
506 Stack::from_ptr_opt(stack as *mut _)
507 }
508 }
509
510 #[corresponds(X509_get_ext_d2i)]
514 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
515 unsafe {
516 let stack = ffi::X509_get_ext_d2i(
517 self.as_ptr(),
518 ffi::NID_info_access,
519 ptr::null_mut(),
520 ptr::null_mut(),
521 );
522 Stack::from_ptr_opt(stack as *mut _)
523 }
524 }
525
526 #[corresponds(X509_get_pathlen)]
528 #[cfg(any(ossl110, boringssl, awslc))]
529 pub fn pathlen(&self) -> Option<u32> {
530 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
531 u32::try_from(v).ok()
532 }
533
534 #[corresponds(X509_get0_subject_key_id)]
536 #[cfg(any(ossl110, boringssl, awslc))]
537 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
538 unsafe {
539 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
540 Asn1OctetStringRef::from_const_ptr_opt(data)
541 }
542 }
543
544 #[corresponds(X509_get0_authority_key_id)]
546 #[cfg(any(ossl110, boringssl, awslc))]
547 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
548 unsafe {
549 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
550 Asn1OctetStringRef::from_const_ptr_opt(data)
551 }
552 }
553
554 #[corresponds(X509_get0_authority_issuer)]
556 #[cfg(ossl111d)]
557 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
558 unsafe {
559 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
560 StackRef::from_const_ptr_opt(stack)
561 }
562 }
563
564 #[corresponds(X509_get0_authority_serial)]
566 #[cfg(ossl111d)]
567 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
568 unsafe {
569 let r = ffi::X509_get0_authority_serial(self.as_ptr());
570 Asn1IntegerRef::from_const_ptr_opt(r)
571 }
572 }
573
574 #[corresponds(X509_get_key_usage)]
580 pub fn key_usage(&self) -> Option<X509KeyUsage> {
581 let bits = unsafe { ffi::X509_get_key_usage(self.as_ptr()) };
582 if bits == u32::MAX {
583 None
584 } else {
585 Some(X509KeyUsage::from_bits_retain(bits))
586 }
587 }
588
589 #[corresponds(X509_get_extended_key_usage)]
598 pub fn extended_key_usage(&self) -> Option<X509ExtendedKeyUsage> {
599 let bits = unsafe { ffi::X509_get_extended_key_usage(self.as_ptr()) };
600 if bits == u32::MAX {
601 None
602 } else {
603 Some(X509ExtendedKeyUsage::from_bits_retain(bits))
604 }
605 }
606
607 #[corresponds(X509_get_pubkey)]
608 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
609 unsafe {
610 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
611 Ok(PKey::from_ptr(pkey))
612 }
613 }
614
615 #[corresponds(X509_digest)]
617 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
618 unsafe {
619 let mut digest = DigestBytes {
620 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
621 len: ffi::EVP_MAX_MD_SIZE as usize,
622 };
623 let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
624 cvt(ffi::X509_digest(
625 self.as_ptr(),
626 hash_type.as_ptr(),
627 digest.buf.as_mut_ptr() as *mut _,
628 &mut len,
629 ))?;
630 digest.len = len as usize;
631
632 Ok(digest)
633 }
634 }
635
636 #[deprecated(since = "0.10.9", note = "renamed to digest")]
637 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
638 self.digest(hash_type).map(|b| b.to_vec())
639 }
640
641 #[corresponds(X509_getm_notAfter)]
643 pub fn not_after(&self) -> &Asn1TimeRef {
644 unsafe {
645 let date = X509_getm_notAfter(self.as_ptr());
646 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
647 }
648 }
649
650 #[corresponds(X509_getm_notBefore)]
652 pub fn not_before(&self) -> &Asn1TimeRef {
653 unsafe {
654 let date = X509_getm_notBefore(self.as_ptr());
655 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
656 }
657 }
658
659 #[corresponds(X509_get0_signature)]
661 pub fn signature(&self) -> &Asn1BitStringRef {
662 unsafe {
663 let mut signature = ptr::null();
664 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
665 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
666 }
667 }
668
669 #[corresponds(X509_get0_signature)]
671 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
672 unsafe {
673 let mut algor = ptr::null();
674 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
675 X509AlgorithmRef::from_const_ptr_opt(algor)
676 .expect("signature algorithm must not be null")
677 }
678 }
679
680 #[corresponds(X509_get1_ocsp)]
686 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
687 unsafe {
688 let stack: Stack<OpensslString> =
689 cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))?;
690 for entry in &stack {
691 let bytes = CStr::from_ptr(entry.as_ptr()).to_bytes();
692 if str::from_utf8(bytes).is_err() {
693 return Err(ErrorStack::internal_error(
694 "OCSP responder URL contained invalid UTF-8",
695 ));
696 }
697 }
698 Ok(stack)
699 }
700 }
701
702 #[corresponds(X509_check_issued)]
704 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
705 unsafe {
706 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
707 X509VerifyResult::from_raw(r)
708 }
709 }
710
711 #[corresponds(X509_get_version)]
716 #[cfg(ossl110)]
717 #[allow(clippy::unnecessary_cast)]
718 pub fn version(&self) -> i32 {
719 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
720 }
721
722 #[corresponds(X509_verify)]
729 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
730 where
731 T: HasPublic,
732 {
733 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
734 }
735
736 #[corresponds(X509_get_serialNumber)]
738 pub fn serial_number(&self) -> &Asn1IntegerRef {
739 unsafe {
740 let r = ffi::X509_get_serialNumber(self.as_ptr());
741 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
742 }
743 }
744
745 #[corresponds(X509_alias_get0)]
751 pub fn alias(&self) -> Option<&[u8]> {
752 unsafe {
753 let mut len = 0;
754 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
755 if ptr.is_null() {
756 None
757 } else {
758 Some(util::from_raw_parts(ptr, len as usize))
759 }
760 }
761 }
762
763 to_pem! {
764 #[corresponds(PEM_write_bio_X509)]
768 to_pem,
769 ffi::PEM_write_bio_X509
770 }
771
772 to_der! {
773 #[corresponds(i2d_X509)]
775 to_der,
776 ffi::i2d_X509
777 }
778
779 to_pem! {
780 #[corresponds(X509_print)]
782 to_text,
783 ffi::X509_print
784 }
785}
786
787impl ToOwned for X509Ref {
788 type Owned = X509;
789
790 fn to_owned(&self) -> X509 {
791 unsafe {
792 let r = X509_up_ref(self.as_ptr());
793 assert!(r == 1);
794 X509::from_ptr(self.as_ptr())
795 }
796 }
797}
798
799impl Ord for X509Ref {
800 fn cmp(&self, other: &Self) -> cmp::Ordering {
801 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
804 cmp.cmp(&0)
805 }
806}
807
808impl PartialOrd for X509Ref {
809 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
810 Some(self.cmp(other))
811 }
812}
813
814impl PartialOrd<X509> for X509Ref {
815 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
816 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
817 }
818}
819
820impl PartialEq for X509Ref {
821 fn eq(&self, other: &Self) -> bool {
822 self.cmp(other) == cmp::Ordering::Equal
823 }
824}
825
826impl PartialEq<X509> for X509Ref {
827 fn eq(&self, other: &X509) -> bool {
828 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
829 }
830}
831
832impl Eq for X509Ref {}
833
834impl X509 {
835 pub fn builder() -> Result<X509Builder, ErrorStack> {
837 X509Builder::new()
838 }
839
840 from_pem! {
841 #[corresponds(PEM_read_bio_X509)]
845 from_pem,
846 X509,
847 ffi::PEM_read_bio_X509
848 }
849
850 from_der! {
851 #[corresponds(d2i_X509)]
853 from_der,
854 X509,
855 ffi::d2i_X509
856 }
857
858 #[corresponds(PEM_read_bio_X509)]
860 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
861 unsafe {
862 ffi::init();
863 let bio = MemBioSlice::new(pem)?;
864
865 let mut certs = vec![];
866 loop {
867 let r =
868 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
869 if r.is_null() {
870 let e = ErrorStack::get();
871
872 if let Some(err) = e.errors().last() {
873 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
874 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
875 {
876 break;
877 }
878 }
879
880 return Err(e);
881 } else {
882 certs.push(X509(r));
883 }
884 }
885
886 Ok(certs)
887 }
888 }
889}
890
891impl Clone for X509 {
892 fn clone(&self) -> X509 {
893 X509Ref::to_owned(self)
894 }
895}
896
897impl fmt::Debug for X509 {
898 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
899 let serial = match &self.serial_number().to_bn() {
900 Ok(bn) => match bn.to_hex_str() {
901 Ok(hex) => hex.to_string(),
902 Err(_) => "".to_string(),
903 },
904 Err(_) => "".to_string(),
905 };
906 let mut debug_struct = formatter.debug_struct("X509");
907 debug_struct.field("serial_number", &serial);
908 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
909 debug_struct.field("issuer", &self.issuer_name());
910 debug_struct.field("subject", &self.subject_name());
911 if let Some(subject_alt_names) = &self.subject_alt_names() {
912 debug_struct.field("subject_alt_names", subject_alt_names);
913 }
914 debug_struct.field("not_before", &self.not_before());
915 debug_struct.field("not_after", &self.not_after());
916
917 if let Ok(public_key) = &self.public_key() {
918 debug_struct.field("public_key", public_key);
919 };
920 debug_struct.finish()
923 }
924}
925
926impl AsRef<X509Ref> for X509Ref {
927 fn as_ref(&self) -> &X509Ref {
928 self
929 }
930}
931
932impl Stackable for X509 {
933 type StackType = ffi::stack_st_X509;
934}
935
936impl Ord for X509 {
937 fn cmp(&self, other: &Self) -> cmp::Ordering {
938 X509Ref::cmp(self, other)
939 }
940}
941
942impl PartialOrd for X509 {
943 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
944 Some(self.cmp(other))
945 }
946}
947
948impl PartialOrd<X509Ref> for X509 {
949 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
950 X509Ref::partial_cmp(self, other)
951 }
952}
953
954impl PartialEq for X509 {
955 fn eq(&self, other: &Self) -> bool {
956 X509Ref::eq(self, other)
957 }
958}
959
960impl PartialEq<X509Ref> for X509 {
961 fn eq(&self, other: &X509Ref) -> bool {
962 X509Ref::eq(self, other)
963 }
964}
965
966impl Eq for X509 {}
967
968pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
970
971impl X509v3Context<'_> {
972 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
973 &self.0 as *const _ as *mut _
974 }
975}
976
977foreign_type_and_impl_send_sync! {
978 type CType = ffi::X509_EXTENSION;
979 fn drop = ffi::X509_EXTENSION_free;
980
981 pub struct X509Extension;
983 pub struct X509ExtensionRef;
985}
986
987impl Stackable for X509Extension {
988 type StackType = ffi::stack_st_X509_EXTENSION;
989}
990
991impl X509Extension {
992 #[deprecated(
1006 note = "Use x509::extension types or new_from_der instead",
1007 since = "0.10.51"
1008 )]
1009 pub fn new(
1010 conf: Option<&ConfRef>,
1011 context: Option<&X509v3Context<'_>>,
1012 name: &str,
1013 value: &str,
1014 ) -> Result<X509Extension, ErrorStack> {
1015 let name = CString::new(name).unwrap();
1016 let value = CString::new(value).unwrap();
1017 let mut ctx;
1018 unsafe {
1019 ffi::init();
1020 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1021 let context_ptr = match context {
1022 Some(c) => c.as_ptr(),
1023 None => {
1024 ctx = mem::zeroed();
1025
1026 ffi::X509V3_set_ctx(
1027 &mut ctx,
1028 ptr::null_mut(),
1029 ptr::null_mut(),
1030 ptr::null_mut(),
1031 ptr::null_mut(),
1032 0,
1033 );
1034 &mut ctx
1035 }
1036 };
1037 let name = name.as_ptr() as *mut _;
1038 let value = value.as_ptr() as *mut _;
1039
1040 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
1041 }
1042 }
1043
1044 #[deprecated(
1058 note = "Use x509::extension types or new_from_der instead",
1059 since = "0.10.51"
1060 )]
1061 pub fn new_nid(
1062 conf: Option<&ConfRef>,
1063 context: Option<&X509v3Context<'_>>,
1064 name: Nid,
1065 value: &str,
1066 ) -> Result<X509Extension, ErrorStack> {
1067 let value = CString::new(value).unwrap();
1068 let mut ctx;
1069 unsafe {
1070 ffi::init();
1071 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1072 let context_ptr = match context {
1073 Some(c) => c.as_ptr(),
1074 None => {
1075 ctx = mem::zeroed();
1076
1077 ffi::X509V3_set_ctx(
1078 &mut ctx,
1079 ptr::null_mut(),
1080 ptr::null_mut(),
1081 ptr::null_mut(),
1082 ptr::null_mut(),
1083 0,
1084 );
1085 &mut ctx
1086 }
1087 };
1088 let name = name.as_raw();
1089 let value = value.as_ptr() as *mut _;
1090
1091 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1092 }
1093 }
1094
1095 pub fn new_from_der(
1105 oid: &Asn1ObjectRef,
1106 critical: bool,
1107 der_contents: &Asn1OctetStringRef,
1108 ) -> Result<X509Extension, ErrorStack> {
1109 unsafe {
1110 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1111 ptr::null_mut(),
1112 oid.as_ptr(),
1113 critical as _,
1114 der_contents.as_ptr(),
1115 ))
1116 .map(X509Extension)
1117 }
1118 }
1119
1120 pub(crate) unsafe fn new_internal(
1121 nid: Nid,
1122 critical: bool,
1123 value: *mut c_void,
1124 ) -> Result<X509Extension, ErrorStack> {
1125 ffi::init();
1126 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1127 }
1128
1129 #[cfg(not(libressl390))]
1135 #[corresponds(X509V3_EXT_add_alias)]
1136 #[deprecated(
1137 note = "Use x509::extension types or new_from_der and then this is not necessary",
1138 since = "0.10.51"
1139 )]
1140 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1141 ffi::init();
1142 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1143 }
1144}
1145
1146impl X509ExtensionRef {
1147 to_der! {
1148 #[corresponds(i2d_X509_EXTENSION)]
1150 to_der,
1151 ffi::i2d_X509_EXTENSION
1152 }
1153}
1154
1155pub struct X509NameBuilder(X509Name);
1157
1158impl X509NameBuilder {
1159 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1161 unsafe {
1162 ffi::init();
1163 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1164 }
1165 }
1166
1167 #[corresponds(X509_NAME_add_entry)]
1169 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1170 unsafe {
1171 cvt(ffi::X509_NAME_add_entry(
1172 self.0.as_ptr(),
1173 ne.as_ptr(),
1174 -1,
1175 0,
1176 ))
1177 .map(|_| ())
1178 }
1179 }
1180
1181 #[corresponds(X509_NAME_add_entry_by_txt)]
1183 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1184 unsafe {
1185 let field = CString::new(field).unwrap();
1186 assert!(value.len() <= crate::SLenType::MAX as usize);
1187 cvt(ffi::X509_NAME_add_entry_by_txt(
1188 self.0.as_ptr(),
1189 field.as_ptr() as *mut _,
1190 ffi::MBSTRING_UTF8,
1191 value.as_ptr(),
1192 value.len() as crate::SLenType,
1193 -1,
1194 0,
1195 ))
1196 .map(|_| ())
1197 }
1198 }
1199
1200 #[corresponds(X509_NAME_add_entry_by_txt)]
1202 pub fn append_entry_by_text_with_type(
1203 &mut self,
1204 field: &str,
1205 value: &str,
1206 ty: Asn1Type,
1207 ) -> Result<(), ErrorStack> {
1208 unsafe {
1209 let field = CString::new(field).unwrap();
1210 assert!(value.len() <= crate::SLenType::MAX as usize);
1211 cvt(ffi::X509_NAME_add_entry_by_txt(
1212 self.0.as_ptr(),
1213 field.as_ptr() as *mut _,
1214 ty.as_raw(),
1215 value.as_ptr(),
1216 value.len() as crate::SLenType,
1217 -1,
1218 0,
1219 ))
1220 .map(|_| ())
1221 }
1222 }
1223
1224 #[corresponds(X509_NAME_add_entry_by_NID)]
1226 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1227 unsafe {
1228 assert!(value.len() <= crate::SLenType::MAX as usize);
1229 cvt(ffi::X509_NAME_add_entry_by_NID(
1230 self.0.as_ptr(),
1231 field.as_raw(),
1232 ffi::MBSTRING_UTF8,
1233 value.as_ptr() as *mut _,
1234 value.len() as crate::SLenType,
1235 -1,
1236 0,
1237 ))
1238 .map(|_| ())
1239 }
1240 }
1241
1242 #[corresponds(X509_NAME_add_entry_by_NID)]
1244 pub fn append_entry_by_nid_with_type(
1245 &mut self,
1246 field: Nid,
1247 value: &str,
1248 ty: Asn1Type,
1249 ) -> Result<(), ErrorStack> {
1250 unsafe {
1251 assert!(value.len() <= crate::SLenType::MAX as usize);
1252 cvt(ffi::X509_NAME_add_entry_by_NID(
1253 self.0.as_ptr(),
1254 field.as_raw(),
1255 ty.as_raw(),
1256 value.as_ptr() as *mut _,
1257 value.len() as crate::SLenType,
1258 -1,
1259 0,
1260 ))
1261 .map(|_| ())
1262 }
1263 }
1264
1265 pub fn build(self) -> X509Name {
1267 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1271 }
1272}
1273
1274foreign_type_and_impl_send_sync! {
1275 type CType = ffi::X509_NAME;
1276 fn drop = ffi::X509_NAME_free;
1277
1278 pub struct X509Name;
1280 pub struct X509NameRef;
1282}
1283
1284impl X509Name {
1285 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1287 X509NameBuilder::new()
1288 }
1289
1290 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1294 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1295 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1296 }
1297
1298 from_der! {
1299 from_der,
1305 X509Name,
1306 ffi::d2i_X509_NAME
1307 }
1308}
1309
1310impl Stackable for X509Name {
1311 type StackType = ffi::stack_st_X509_NAME;
1312}
1313
1314impl X509NameRef {
1315 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1317 X509NameEntries {
1318 name: self,
1319 nid: Some(nid),
1320 loc: -1,
1321 }
1322 }
1323
1324 pub fn entries(&self) -> X509NameEntries<'_> {
1326 X509NameEntries {
1327 name: self,
1328 nid: None,
1329 loc: -1,
1330 }
1331 }
1332
1333 #[corresponds(X509_NAME_cmp)]
1340 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1341 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1342 if cfg!(ossl300) && cmp == -2 {
1343 return Err(ErrorStack::get());
1344 }
1345 Ok(cmp.cmp(&0))
1346 }
1347
1348 #[corresponds(X509_NAME_dup)]
1350 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1351 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1352 }
1353
1354 to_der! {
1355 to_der,
1361 ffi::i2d_X509_NAME
1362 }
1363}
1364
1365impl fmt::Debug for X509NameRef {
1366 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1367 formatter.debug_list().entries(self.entries()).finish()
1368 }
1369}
1370
1371pub struct X509NameEntries<'a> {
1373 name: &'a X509NameRef,
1374 nid: Option<Nid>,
1375 loc: c_int,
1376}
1377
1378impl<'a> Iterator for X509NameEntries<'a> {
1379 type Item = &'a X509NameEntryRef;
1380
1381 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1382 unsafe {
1383 match self.nid {
1384 Some(nid) => {
1385 self.loc =
1387 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1388 if self.loc == -1 {
1389 return None;
1390 }
1391 }
1392 None => {
1393 self.loc += 1;
1395 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1396 return None;
1397 }
1398 }
1399 }
1400
1401 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1402
1403 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1404 }
1405 }
1406}
1407
1408foreign_type_and_impl_send_sync! {
1409 type CType = ffi::X509_NAME_ENTRY;
1410 fn drop = ffi::X509_NAME_ENTRY_free;
1411
1412 pub struct X509NameEntry;
1414 pub struct X509NameEntryRef;
1416}
1417
1418impl X509NameEntryRef {
1419 #[corresponds(X509_NAME_ENTRY_get_data)]
1421 pub fn data(&self) -> &Asn1StringRef {
1422 unsafe {
1423 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1424 Asn1StringRef::from_ptr(data as *mut _)
1425 }
1426 }
1427
1428 #[corresponds(X509_NAME_ENTRY_get_object)]
1431 pub fn object(&self) -> &Asn1ObjectRef {
1432 unsafe {
1433 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1434 Asn1ObjectRef::from_ptr(object as *mut _)
1435 }
1436 }
1437}
1438
1439impl fmt::Debug for X509NameEntryRef {
1440 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1441 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1442 }
1443}
1444
1445pub struct X509ReqBuilder(X509Req);
1447
1448impl X509ReqBuilder {
1449 #[corresponds(X509_REQ_new)]
1451 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1452 unsafe {
1453 ffi::init();
1454 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1455 }
1456 }
1457
1458 #[corresponds(X509_REQ_set_version)]
1460 #[allow(clippy::useless_conversion)]
1461 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1462 unsafe {
1463 cvt(ffi::X509_REQ_set_version(
1464 self.0.as_ptr(),
1465 version as c_long,
1466 ))
1467 .map(|_| ())
1468 }
1469 }
1470
1471 #[corresponds(X509_REQ_set_subject_name)]
1473 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1474 unsafe {
1475 cvt(ffi::X509_REQ_set_subject_name(
1476 self.0.as_ptr(),
1477 subject_name.as_ptr(),
1478 ))
1479 .map(|_| ())
1480 }
1481 }
1482
1483 #[corresponds(X509_REQ_set_pubkey)]
1485 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1486 where
1487 T: HasPublic,
1488 {
1489 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1490 }
1491
1492 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1495 unsafe {
1496 let mut ctx = mem::zeroed();
1497
1498 ffi::X509V3_set_ctx(
1499 &mut ctx,
1500 ptr::null_mut(),
1501 ptr::null_mut(),
1502 self.0.as_ptr(),
1503 ptr::null_mut(),
1504 0,
1505 );
1506
1507 if let Some(conf) = conf {
1509 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1510 }
1511
1512 X509v3Context(ctx, PhantomData)
1513 }
1514 }
1515
1516 pub fn add_extensions(
1518 &mut self,
1519 extensions: &StackRef<X509Extension>,
1520 ) -> Result<(), ErrorStack> {
1521 unsafe {
1522 cvt(ffi::X509_REQ_add_extensions(
1523 self.0.as_ptr(),
1524 extensions.as_ptr(),
1525 ))
1526 .map(|_| ())
1527 }
1528 }
1529
1530 #[corresponds(X509_REQ_sign)]
1532 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1533 where
1534 T: HasPrivate,
1535 {
1536 unsafe {
1537 cvt(ffi::X509_REQ_sign(
1538 self.0.as_ptr(),
1539 key.as_ptr(),
1540 hash.as_ptr(),
1541 ))
1542 .map(|_| ())
1543 }
1544 }
1545
1546 pub fn build(self) -> X509Req {
1548 self.0
1549 }
1550}
1551
1552foreign_type_and_impl_send_sync! {
1553 type CType = ffi::X509_REQ;
1554 fn drop = ffi::X509_REQ_free;
1555
1556 pub struct X509Req;
1558 pub struct X509ReqRef;
1560}
1561
1562impl X509Req {
1563 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1565 X509ReqBuilder::new()
1566 }
1567
1568 from_pem! {
1569 from_pem,
1577 X509Req,
1578 ffi::PEM_read_bio_X509_REQ
1579 }
1580
1581 from_der! {
1582 from_der,
1588 X509Req,
1589 ffi::d2i_X509_REQ
1590 }
1591}
1592
1593impl X509ReqRef {
1594 to_pem! {
1595 to_pem,
1603 ffi::PEM_write_bio_X509_REQ
1604 }
1605
1606 to_der! {
1607 to_der,
1613 ffi::i2d_X509_REQ
1614 }
1615
1616 to_pem! {
1617 #[corresponds(X509_Req_print)]
1619 to_text,
1620 ffi::X509_REQ_print
1621 }
1622
1623 #[corresponds(X509_REQ_get_version)]
1625 #[allow(clippy::unnecessary_cast)]
1626 pub fn version(&self) -> i32 {
1627 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1628 }
1629
1630 #[corresponds(X509_REQ_get_subject_name)]
1632 pub fn subject_name(&self) -> &X509NameRef {
1633 unsafe {
1634 let name = X509_REQ_get_subject_name(self.as_ptr());
1635 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1636 }
1637 }
1638
1639 #[corresponds(X509_REQ_get_pubkey)]
1641 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1642 unsafe {
1643 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1644 Ok(PKey::from_ptr(key))
1645 }
1646 }
1647
1648 #[corresponds(X509_REQ_verify)]
1652 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1653 where
1654 T: HasPublic,
1655 {
1656 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1657 }
1658
1659 #[corresponds(X509_REQ_get_extensions)]
1661 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1662 unsafe {
1663 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1664 Ok(Stack::from_ptr(extensions))
1665 }
1666 }
1667}
1668
1669#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1671pub struct CrlReason(c_int);
1672
1673#[allow(missing_docs)] impl CrlReason {
1675 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1676 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1677 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1678 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1679 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1680 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1681 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1682 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1683 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1684 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1685
1686 pub const fn from_raw(value: c_int) -> Self {
1688 CrlReason(value)
1689 }
1690
1691 pub const fn as_raw(&self) -> c_int {
1693 self.0
1694 }
1695}
1696
1697pub struct X509RevokedBuilder(X509Revoked);
1699
1700impl X509RevokedBuilder {
1701 #[corresponds(X509_REVOKED_new)]
1703 pub fn new() -> Result<Self, ErrorStack> {
1704 unsafe {
1705 ffi::init();
1706 cvt_p(ffi::X509_REVOKED_new()).map(|p| X509RevokedBuilder(X509Revoked(p)))
1707 }
1708 }
1709
1710 #[corresponds(X509_REVOKED_set_revocationDate)]
1712 pub fn set_revocation_date(&mut self, date: &Asn1TimeRef) -> Result<(), ErrorStack> {
1713 unsafe {
1714 cvt(ffi::X509_REVOKED_set_revocationDate(
1715 self.0.as_ptr(),
1716 date.as_ptr(),
1717 ))
1718 .map(|_| ())
1719 }
1720 }
1721
1722 #[corresponds(X509_REVOKED_set_serialNumber)]
1724 pub fn set_serial_number(&mut self, serial: &Asn1IntegerRef) -> Result<(), ErrorStack> {
1725 unsafe {
1726 cvt(ffi::X509_REVOKED_set_serialNumber(
1727 self.0.as_ptr(),
1728 serial.as_ptr(),
1729 ))
1730 .map(|_| ())
1731 }
1732 }
1733
1734 pub fn build(self) -> X509Revoked {
1736 self.0
1737 }
1738}
1739
1740foreign_type_and_impl_send_sync! {
1741 type CType = ffi::X509_REVOKED;
1742 fn drop = ffi::X509_REVOKED_free;
1743
1744 pub struct X509Revoked;
1746 pub struct X509RevokedRef;
1748}
1749
1750impl Stackable for X509Revoked {
1751 type StackType = ffi::stack_st_X509_REVOKED;
1752}
1753
1754impl X509Revoked {
1755 from_der! {
1756 #[corresponds(d2i_X509_REVOKED)]
1758 from_der,
1759 X509Revoked,
1760 ffi::d2i_X509_REVOKED
1761 }
1762}
1763
1764impl X509RevokedRef {
1765 to_der! {
1766 #[corresponds(d2i_X509_REVOKED)]
1768 to_der,
1769 ffi::i2d_X509_REVOKED
1770 }
1771
1772 #[corresponds(X509_REVOKED_dup)]
1774 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1775 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1776 }
1777
1778 #[corresponds(X509_REVOKED_get0_revocationDate)]
1780 pub fn revocation_date(&self) -> &Asn1TimeRef {
1781 unsafe {
1782 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1783 assert!(!r.is_null());
1784 Asn1TimeRef::from_ptr(r as *mut _)
1785 }
1786 }
1787
1788 #[corresponds(X509_REVOKED_get0_serialNumber)]
1790 pub fn serial_number(&self) -> &Asn1IntegerRef {
1791 unsafe {
1792 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1793 assert!(!r.is_null());
1794 Asn1IntegerRef::from_ptr(r as *mut _)
1795 }
1796 }
1797
1798 #[corresponds(X509_REVOKED_get_ext_d2i)]
1802 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1803 let mut critical = -1;
1804 let out = unsafe {
1805 let ext = ffi::X509_REVOKED_get_ext_d2i(
1807 self.as_ptr(),
1808 T::NID.as_raw(),
1809 &mut critical as *mut _,
1810 ptr::null_mut(),
1811 );
1812 T::Output::from_ptr_opt(ext as *mut _)
1815 };
1816 match (critical, out) {
1817 (0, Some(out)) => Ok(Some((false, out))),
1818 (1, Some(out)) => Ok(Some((true, out))),
1819 (-1 | -2, _) => Ok(None),
1821 (0 | 1, None) => Err(ErrorStack::get()),
1824 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1825 }
1826 }
1827}
1828
1829pub enum ReasonCode {}
1832
1833unsafe impl ExtensionType for ReasonCode {
1836 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1837
1838 type Output = Asn1Enumerated;
1839}
1840
1841pub enum CertificateIssuer {}
1844
1845unsafe impl ExtensionType for CertificateIssuer {
1848 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1849
1850 type Output = Stack<GeneralName>;
1851}
1852
1853pub enum AuthorityInformationAccess {}
1855
1856unsafe impl ExtensionType for AuthorityInformationAccess {
1859 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1860
1861 type Output = Stack<AccessDescription>;
1862}
1863
1864unsafe impl ExtensionType for CrlNumber {
1867 const NID: Nid = Nid::CRL_NUMBER;
1868
1869 type Output = Asn1Integer;
1870}
1871
1872pub struct X509CrlBuilder(X509Crl);
1874
1875impl X509CrlBuilder {
1876 #[corresponds(X509_CRL_new)]
1878 pub fn new() -> Result<Self, ErrorStack> {
1879 unsafe {
1880 ffi::init();
1881 let ptr = cvt_p(ffi::X509_CRL_new())?;
1882 cvt(ffi::X509_CRL_set_version(ptr, 1)).map(|_| ())?;
1883
1884 Ok(Self(X509Crl(ptr)))
1885 }
1886 }
1887
1888 #[corresponds(X509_CRL_set_issuer_name)]
1890 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
1891 unsafe {
1892 cvt(ffi::X509_CRL_set_issuer_name(
1893 self.0.as_ptr(),
1894 issuer_name.as_ptr(),
1895 ))
1896 .map(|_| ())
1897 }
1898 }
1899
1900 #[corresponds(X509_CRL_set1_lastUpdate)]
1902 pub fn set_last_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
1903 unsafe { cvt(ffi::X509_CRL_set1_lastUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
1904 }
1905
1906 #[corresponds(X509_CRL_set1_nextUpdate)]
1908 pub fn set_next_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
1909 unsafe { cvt(ffi::X509_CRL_set1_nextUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
1910 }
1911
1912 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
1916 self.append_extension2(&extension)
1917 }
1918
1919 #[corresponds(X509_CRL_add_ext)]
1921 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
1922 unsafe {
1923 cvt(ffi::X509_CRL_add_ext(
1924 self.0.as_ptr(),
1925 extension.as_ptr(),
1926 -1,
1927 ))
1928 .map(|_| ())
1929 }
1930 }
1931
1932 #[corresponds(X509_CRL_add0_revoked)]
1934 pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
1935 unsafe {
1936 let r = cvt(ffi::X509_CRL_add0_revoked(
1937 self.0.as_ptr(),
1938 revoked.as_ptr(),
1939 ))
1940 .map(|_| ());
1941 std::mem::forget(revoked);
1942 r
1943 }
1944 }
1945
1946 #[corresponds(X509_CRL_sort)]
1948 pub fn sort(&mut self) -> Result<(), ErrorStack> {
1949 unsafe { cvt(ffi::X509_CRL_sort(self.0.as_ptr())).map(|_| ()) }
1950 }
1951
1952 #[corresponds(X509_CRL_sign)]
1954 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1955 where
1956 T: HasPrivate,
1957 {
1958 unsafe {
1959 cvt(ffi::X509_CRL_sign(
1960 self.0.as_ptr(),
1961 key.as_ptr(),
1962 hash.as_ptr(),
1963 ))
1964 .map(|_| ())
1965 }
1966 }
1967
1968 pub fn build(self) -> Result<X509Crl, ErrorStack> {
1974 unsafe {
1975 let loc = ffi::X509_CRL_get_ext_by_NID(
1976 self.0.as_ptr(),
1977 Nid::AUTHORITY_KEY_IDENTIFIER.as_raw(),
1978 -1,
1979 );
1980 if loc < 0 {
1981 return Err(ErrorStack::internal_error(
1982 "CRL must have an Authority Key Identifier extension",
1983 ));
1984 }
1985 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
1986 if ffi::X509_EXTENSION_get_critical(ext) != 0 {
1987 return Err(ErrorStack::internal_error(
1988 "Authority Key Identifier extension must not be critical",
1989 ));
1990 }
1991
1992 let loc = ffi::X509_CRL_get_ext_by_NID(self.0.as_ptr(), Nid::CRL_NUMBER.as_raw(), -1);
1993 if loc < 0 {
1994 return Err(ErrorStack::internal_error(
1995 "CRL must have a Crl Number extension",
1996 ));
1997 }
1998 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
1999 if ffi::X509_EXTENSION_get_critical(ext) != 0 {
2000 return Err(ErrorStack::internal_error(
2001 "Crl Number extension must not be critical",
2002 ));
2003 }
2004
2005 if X509_CRL_get0_nextUpdate(self.0.as_ptr()).is_null() {
2006 return Err(ErrorStack::internal_error(
2007 "CRL must have nextUpdate time set",
2008 ));
2009 }
2010 let revoked = self.0.get_revoked();
2011 if !(revoked.is_none() || revoked.is_some_and(|r| !r.is_empty())) {
2013 return Err(ErrorStack::internal_error(
2014 "Revoked must be absent or non-empty",
2015 ));
2016 }
2017 }
2018
2019 Ok(self.0)
2020 }
2021}
2022
2023foreign_type_and_impl_send_sync! {
2024 type CType = ffi::X509_CRL;
2025 fn drop = ffi::X509_CRL_free;
2026
2027 pub struct X509Crl;
2029 pub struct X509CrlRef;
2031}
2032
2033pub enum CrlStatus<'a> {
2039 NotRevoked,
2041 Revoked(&'a X509RevokedRef),
2043 RemoveFromCrl(&'a X509RevokedRef),
2048}
2049
2050impl<'a> CrlStatus<'a> {
2051 unsafe fn from_ffi_status(
2056 status: c_int,
2057 revoked_entry: *mut ffi::X509_REVOKED,
2058 ) -> CrlStatus<'a> {
2059 match status {
2060 0 => CrlStatus::NotRevoked,
2061 1 => {
2062 assert!(!revoked_entry.is_null());
2063 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
2064 }
2065 2 => {
2066 assert!(!revoked_entry.is_null());
2067 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
2068 }
2069 _ => unreachable!(
2070 "{}",
2071 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
2072 ),
2073 }
2074 }
2075}
2076
2077impl X509Crl {
2078 from_pem! {
2079 #[corresponds(PEM_read_bio_X509_CRL)]
2083 from_pem,
2084 X509Crl,
2085 ffi::PEM_read_bio_X509_CRL
2086 }
2087
2088 from_der! {
2089 #[corresponds(d2i_X509_CRL)]
2091 from_der,
2092 X509Crl,
2093 ffi::d2i_X509_CRL
2094 }
2095}
2096
2097impl X509CrlRef {
2098 to_pem! {
2099 #[corresponds(PEM_write_bio_X509_CRL)]
2103 to_pem,
2104 ffi::PEM_write_bio_X509_CRL
2105 }
2106
2107 to_der! {
2108 #[corresponds(i2d_X509_CRL)]
2110 to_der,
2111 ffi::i2d_X509_CRL
2112 }
2113
2114 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2116 unsafe {
2117 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2118 if revoked.is_null() {
2119 None
2120 } else {
2121 Some(StackRef::from_ptr(revoked))
2122 }
2123 }
2124 }
2125
2126 #[corresponds(X509_CRL_get0_lastUpdate)]
2128 pub fn last_update(&self) -> &Asn1TimeRef {
2129 unsafe {
2130 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2131 assert!(!date.is_null());
2132 Asn1TimeRef::from_ptr(date as *mut _)
2133 }
2134 }
2135
2136 #[corresponds(X509_CRL_get0_nextUpdate)]
2140 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2141 unsafe {
2142 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2143 Asn1TimeRef::from_const_ptr_opt(date)
2144 }
2145 }
2146
2147 #[corresponds(X509_CRL_get0_by_serial)]
2149 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2150 unsafe {
2151 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2152 let status =
2153 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2154 CrlStatus::from_ffi_status(status, ret)
2155 }
2156 }
2157
2158 #[corresponds(X509_CRL_get0_by_cert)]
2160 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2161 unsafe {
2162 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2163 let status =
2164 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2165 CrlStatus::from_ffi_status(status, ret)
2166 }
2167 }
2168
2169 #[corresponds(X509_CRL_get_issuer)]
2171 pub fn issuer_name(&self) -> &X509NameRef {
2172 unsafe {
2173 let name = X509_CRL_get_issuer(self.as_ptr());
2174 assert!(!name.is_null());
2175 X509NameRef::from_ptr(name as *mut _)
2176 }
2177 }
2178
2179 #[corresponds(X509_CRL_verify)]
2186 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2187 where
2188 T: HasPublic,
2189 {
2190 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2191 }
2192
2193 #[corresponds(X509_CRL_get_ext_d2i)]
2197 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2198 let mut critical = -1;
2199 let out = unsafe {
2200 let ext = ffi::X509_CRL_get_ext_d2i(
2202 self.as_ptr(),
2203 T::NID.as_raw(),
2204 &mut critical as *mut _,
2205 ptr::null_mut(),
2206 );
2207 T::Output::from_ptr_opt(ext as *mut _)
2210 };
2211 match (critical, out) {
2212 (0, Some(out)) => Ok(Some((false, out))),
2213 (1, Some(out)) => Ok(Some((true, out))),
2214 (-1 | -2, _) => Ok(None),
2216 (0 | 1, None) => Err(ErrorStack::get()),
2219 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2220 }
2221 }
2222}
2223
2224#[derive(Copy, Clone, PartialEq, Eq)]
2226pub struct X509VerifyResult(c_int);
2227
2228impl fmt::Debug for X509VerifyResult {
2229 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2230 fmt.debug_struct("X509VerifyResult")
2231 .field("code", &self.0)
2232 .field("error", &self.error_string())
2233 .finish()
2234 }
2235}
2236
2237impl fmt::Display for X509VerifyResult {
2238 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2239 fmt.write_str(self.error_string())
2240 }
2241}
2242
2243impl Error for X509VerifyResult {}
2244
2245impl X509VerifyResult {
2246 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2253 X509VerifyResult(err)
2254 }
2255
2256 #[allow(clippy::trivially_copy_pass_by_ref)]
2258 pub fn as_raw(&self) -> c_int {
2259 self.0
2260 }
2261
2262 #[corresponds(X509_verify_cert_error_string)]
2264 #[allow(clippy::trivially_copy_pass_by_ref)]
2265 pub fn error_string(&self) -> &'static str {
2266 ffi::init();
2267
2268 unsafe {
2269 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2270 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2271 }
2272 }
2273
2274 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2276 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2278 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2279}
2280
2281foreign_type_and_impl_send_sync! {
2282 type CType = ffi::GENERAL_NAME;
2283 fn drop = ffi::GENERAL_NAME_free;
2284
2285 pub struct GeneralName;
2287 pub struct GeneralNameRef;
2289}
2290
2291impl GeneralName {
2292 unsafe fn new(
2293 type_: c_int,
2294 asn1_type: Asn1Type,
2295 value: &[u8],
2296 ) -> Result<GeneralName, ErrorStack> {
2297 ffi::init();
2298 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2299 (*gn.as_ptr()).type_ = type_;
2300 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2301 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2302
2303 #[cfg(any(boringssl, awslc))]
2304 {
2305 (*gn.as_ptr()).d.ptr = s.cast();
2306 }
2307 #[cfg(not(any(boringssl, awslc)))]
2308 {
2309 (*gn.as_ptr()).d = s.cast();
2310 }
2311
2312 Ok(gn)
2313 }
2314
2315 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2316 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2317 }
2318
2319 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2320 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2321 }
2322
2323 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2324 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2325 }
2326
2327 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2328 match ip {
2329 IpAddr::V4(addr) => unsafe {
2330 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2331 },
2332 IpAddr::V6(addr) => unsafe {
2333 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2334 },
2335 }
2336 }
2337
2338 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2339 unsafe {
2340 ffi::init();
2341 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2342 (*gn).type_ = ffi::GEN_RID;
2343
2344 #[cfg(any(boringssl, awslc))]
2345 {
2346 (*gn).d.registeredID = oid.as_ptr();
2347 }
2348 #[cfg(not(any(boringssl, awslc)))]
2349 {
2350 (*gn).d = oid.as_ptr().cast();
2351 }
2352
2353 mem::forget(oid);
2354
2355 Ok(GeneralName::from_ptr(gn))
2356 }
2357 }
2358
2359 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2360 unsafe {
2361 ffi::init();
2362
2363 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2364 ptr::null_mut(),
2365 &mut value.as_ptr().cast(),
2366 value.len().try_into().unwrap(),
2367 ))?;
2368
2369 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2370 (*gn).type_ = ffi::GEN_OTHERNAME;
2371
2372 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2373 gn,
2374 oid.as_ptr().cast(),
2375 typ,
2376 )) {
2377 ffi::GENERAL_NAME_free(gn);
2378 return Err(e);
2379 }
2380
2381 mem::forget(oid);
2382
2383 Ok(GeneralName::from_ptr(gn))
2384 }
2385 }
2386
2387 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2388 unsafe {
2389 ffi::init();
2390 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2391 (*gn).type_ = ffi::GEN_DIRNAME;
2392
2393 let dup = match name.to_owned() {
2394 Ok(dup) => dup,
2395 Err(e) => {
2396 ffi::GENERAL_NAME_free(gn);
2397 return Err(e);
2398 }
2399 };
2400
2401 #[cfg(any(boringssl, awslc))]
2402 {
2403 (*gn).d.directoryName = dup.as_ptr();
2404 }
2405 #[cfg(not(any(boringssl, awslc)))]
2406 {
2407 (*gn).d = dup.as_ptr().cast();
2408 }
2409
2410 std::mem::forget(dup);
2411
2412 Ok(GeneralName::from_ptr(gn))
2413 }
2414 }
2415}
2416
2417impl GeneralNameRef {
2418 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2419 unsafe {
2420 if (*self.as_ptr()).type_ != ffi_type {
2421 return None;
2422 }
2423
2424 #[cfg(any(boringssl, awslc))]
2425 let d = (*self.as_ptr()).d.ptr;
2426 #[cfg(not(any(boringssl, awslc)))]
2427 let d = (*self.as_ptr()).d;
2428
2429 let ptr = ASN1_STRING_get0_data(d as *mut _);
2430 let len = ffi::ASN1_STRING_length(d as *mut _);
2431
2432 #[allow(clippy::unnecessary_cast)]
2433 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2434 str::from_utf8(slice).ok()
2438 }
2439 }
2440
2441 pub fn email(&self) -> Option<&str> {
2443 self.ia5_string(ffi::GEN_EMAIL)
2444 }
2445
2446 pub fn directory_name(&self) -> Option<&X509NameRef> {
2448 unsafe {
2449 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2450 return None;
2451 }
2452
2453 #[cfg(any(boringssl, awslc))]
2454 let d = (*self.as_ptr()).d.ptr;
2455 #[cfg(not(any(boringssl, awslc)))]
2456 let d = (*self.as_ptr()).d;
2457
2458 Some(X509NameRef::from_const_ptr(d as *const _))
2459 }
2460 }
2461
2462 pub fn dnsname(&self) -> Option<&str> {
2464 self.ia5_string(ffi::GEN_DNS)
2465 }
2466
2467 pub fn uri(&self) -> Option<&str> {
2469 self.ia5_string(ffi::GEN_URI)
2470 }
2471
2472 pub fn ipaddress(&self) -> Option<&[u8]> {
2474 unsafe {
2475 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2476 return None;
2477 }
2478 #[cfg(any(boringssl, awslc))]
2479 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2480 #[cfg(not(any(boringssl, awslc)))]
2481 let d = (*self.as_ptr()).d;
2482
2483 let ptr = ASN1_STRING_get0_data(d as *mut _);
2484 let len = ffi::ASN1_STRING_length(d as *mut _);
2485
2486 #[allow(clippy::unnecessary_cast)]
2487 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2488 }
2489 }
2490}
2491
2492impl fmt::Debug for GeneralNameRef {
2493 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2494 if let Some(email) = self.email() {
2495 formatter.write_str(email)
2496 } else if let Some(dnsname) = self.dnsname() {
2497 formatter.write_str(dnsname)
2498 } else if let Some(uri) = self.uri() {
2499 formatter.write_str(uri)
2500 } else if let Some(ipaddress) = self.ipaddress() {
2501 let address = <[u8; 16]>::try_from(ipaddress)
2502 .map(IpAddr::from)
2503 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2504 match address {
2505 Ok(a) => fmt::Debug::fmt(&a, formatter),
2506 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2507 }
2508 } else {
2509 formatter.write_str("(empty)")
2510 }
2511 }
2512}
2513
2514impl Stackable for GeneralName {
2515 type StackType = ffi::stack_st_GENERAL_NAME;
2516}
2517
2518foreign_type_and_impl_send_sync! {
2519 type CType = ffi::DIST_POINT;
2520 fn drop = ffi::DIST_POINT_free;
2521
2522 pub struct DistPoint;
2524 pub struct DistPointRef;
2526}
2527
2528impl DistPointRef {
2529 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2531 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2532 }
2533}
2534
2535foreign_type_and_impl_send_sync! {
2536 type CType = ffi::DIST_POINT_NAME;
2537 fn drop = ffi::DIST_POINT_NAME_free;
2538
2539 pub struct DistPointName;
2541 pub struct DistPointNameRef;
2543}
2544
2545impl DistPointNameRef {
2546 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2548 unsafe {
2549 if (*self.as_ptr()).type_ != 0 {
2550 return None;
2551 }
2552 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2553 }
2554 }
2555}
2556
2557impl Stackable for DistPoint {
2558 type StackType = ffi::stack_st_DIST_POINT;
2559}
2560
2561foreign_type_and_impl_send_sync! {
2562 type CType = ffi::ACCESS_DESCRIPTION;
2563 fn drop = ffi::ACCESS_DESCRIPTION_free;
2564
2565 pub struct AccessDescription;
2567 pub struct AccessDescriptionRef;
2569}
2570
2571impl AccessDescriptionRef {
2572 pub fn method(&self) -> &Asn1ObjectRef {
2574 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2575 }
2576
2577 pub fn location(&self) -> &GeneralNameRef {
2579 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2580 }
2581}
2582
2583impl Stackable for AccessDescription {
2584 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2585}
2586
2587foreign_type_and_impl_send_sync! {
2588 type CType = ffi::X509_ALGOR;
2589 fn drop = ffi::X509_ALGOR_free;
2590
2591 pub struct X509Algorithm;
2593 pub struct X509AlgorithmRef;
2595}
2596
2597impl X509AlgorithmRef {
2598 pub fn object(&self) -> &Asn1ObjectRef {
2600 unsafe {
2601 let mut oid = ptr::null();
2602 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2603 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2604 }
2605 }
2606}
2607
2608foreign_type_and_impl_send_sync! {
2609 type CType = ffi::X509_OBJECT;
2610 fn drop = X509_OBJECT_free;
2611
2612 pub struct X509Object;
2614 pub struct X509ObjectRef;
2616}
2617
2618impl X509ObjectRef {
2619 pub fn x509(&self) -> Option<&X509Ref> {
2620 unsafe {
2621 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2622 X509Ref::from_const_ptr_opt(ptr)
2623 }
2624 }
2625}
2626
2627impl Stackable for X509Object {
2628 type StackType = ffi::stack_st_X509_OBJECT;
2629}
2630
2631use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2632
2633use ffi::{
2634 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2635 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2636};
2637
2638use ffi::X509_OBJECT_free;
2639use ffi::X509_OBJECT_get0_X509;
2640
2641use ffi::{
2642 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2643 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2644};
2645
2646#[derive(Copy, Clone, PartialEq, Eq)]
2647pub struct X509PurposeId(c_int);
2648
2649impl X509PurposeId {
2650 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2651 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2652 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2653 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2654 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2655 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2656 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2657 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2658 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2659 #[cfg(ossl320)]
2660 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2661
2662 pub fn from_raw(id: c_int) -> Self {
2664 X509PurposeId(id)
2665 }
2666
2667 pub fn as_raw(&self) -> c_int {
2669 self.0
2670 }
2671}
2672
2673pub struct X509PurposeRef(Opaque);
2677
2678impl ForeignTypeRef for X509PurposeRef {
2680 type CType = ffi::X509_PURPOSE;
2681}
2682
2683impl X509PurposeRef {
2684 #[allow(clippy::unnecessary_cast)]
2698 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2699 unsafe {
2700 let sname = CString::new(sname).unwrap();
2701 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2702 Ok(purpose)
2703 }
2704 }
2705 #[corresponds(X509_PURPOSE_get0)]
2708 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2709 unsafe {
2710 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2711 Ok(X509PurposeRef::from_const_ptr(ptr))
2712 }
2713 }
2714
2715 pub fn purpose(&self) -> X509PurposeId {
2726 unsafe {
2727 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2728 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2729 }
2730 }
2731}