1use cfg_if::cfg_if;
11use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
12use libc::{c_int, c_long, c_uint, c_void};
13use std::cmp::{self, Ordering};
14use std::convert::{TryFrom, TryInto};
15use std::error::Error;
16use std::ffi::{CStr, CString};
17use std::fmt;
18use std::marker::PhantomData;
19use std::mem;
20use std::net::IpAddr;
21use std::path::Path;
22use std::ptr;
23use std::str;
24
25use crate::asn1::{
26 Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
27 Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
28};
29use crate::bio::MemBioSlice;
30use crate::conf::ConfRef;
31use crate::error::ErrorStack;
32use crate::ex_data::Index;
33use crate::hash::{DigestBytes, MessageDigest};
34use crate::nid::Nid;
35use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
36use crate::ssl::SslRef;
37use crate::stack::{Stack, StackRef, Stackable};
38use crate::string::OpensslString;
39use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
40use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
41use openssl_macros::corresponds;
42
43pub mod verify;
44
45pub mod extension;
46pub mod store;
47
48#[cfg(test)]
49mod tests;
50
51pub unsafe trait ExtensionType {
57 const NID: Nid;
58 type Output: ForeignType;
59}
60
61foreign_type_and_impl_send_sync! {
62 type CType = ffi::X509_STORE_CTX;
63 fn drop = ffi::X509_STORE_CTX_free;
64
65 pub struct X509StoreContext;
67
68 pub struct X509StoreContextRef;
70}
71
72impl X509StoreContext {
73 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
76 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
77 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
78 }
79
80 #[corresponds(X509_STORE_CTX_new)]
82 pub fn new() -> Result<X509StoreContext, ErrorStack> {
83 unsafe {
84 ffi::init();
85 cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
86 }
87 }
88}
89
90impl X509StoreContextRef {
91 #[corresponds(X509_STORE_CTX_get_ex_data)]
93 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
94 unsafe {
95 let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
96 if data.is_null() {
97 None
98 } else {
99 Some(&*(data as *const T))
100 }
101 }
102 }
103
104 #[corresponds(X509_STORE_CTX_get_error)]
106 pub fn error(&self) -> X509VerifyResult {
107 unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
108 }
109
110 pub fn init<F, T>(
126 &mut self,
127 trust: &store::X509StoreRef,
128 cert: &X509Ref,
129 cert_chain: &StackRef<X509>,
130 with_context: F,
131 ) -> Result<T, ErrorStack>
132 where
133 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
134 {
135 struct Cleanup<'a>(&'a mut X509StoreContextRef);
136
137 impl Drop for Cleanup<'_> {
138 fn drop(&mut self) {
139 unsafe {
140 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
141 }
142 }
143 }
144
145 unsafe {
146 cvt(ffi::X509_STORE_CTX_init(
147 self.as_ptr(),
148 trust.as_ptr(),
149 cert.as_ptr(),
150 cert_chain.as_ptr(),
151 ))?;
152
153 let cleanup = Cleanup(self);
154 with_context(cleanup.0)
155 }
156 }
157
158 #[corresponds(X509_verify_cert)]
165 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
166 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
167 }
168
169 #[corresponds(X509_STORE_CTX_set_error)]
171 pub fn set_error(&mut self, result: X509VerifyResult) {
172 unsafe {
173 ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
174 }
175 }
176
177 #[corresponds(X509_STORE_CTX_get_current_cert)]
180 pub fn current_cert(&self) -> Option<&X509Ref> {
181 unsafe {
182 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
183 X509Ref::from_const_ptr_opt(ptr)
184 }
185 }
186
187 #[corresponds(X509_STORE_CTX_get_error_depth)]
192 pub fn error_depth(&self) -> u32 {
193 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
194 }
195
196 #[corresponds(X509_STORE_CTX_get0_chain)]
198 pub fn chain(&self) -> Option<&StackRef<X509>> {
199 unsafe {
200 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
201
202 if chain.is_null() {
203 None
204 } else {
205 Some(StackRef::from_ptr(chain))
206 }
207 }
208 }
209}
210
211pub struct X509Builder(X509);
213
214impl X509Builder {
215 #[corresponds(X509_new)]
217 pub fn new() -> Result<X509Builder, ErrorStack> {
218 unsafe {
219 ffi::init();
220 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
221 }
222 }
223
224 #[corresponds(X509_set1_notAfter)]
226 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
227 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
228 }
229
230 #[corresponds(X509_set1_notBefore)]
232 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
233 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
234 }
235
236 #[corresponds(X509_set_version)]
241 #[allow(clippy::useless_conversion)]
242 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
243 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
244 }
245
246 #[corresponds(X509_set_serialNumber)]
248 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
249 unsafe {
250 cvt(ffi::X509_set_serialNumber(
251 self.0.as_ptr(),
252 serial_number.as_ptr(),
253 ))
254 .map(|_| ())
255 }
256 }
257
258 #[corresponds(X509_set_issuer_name)]
260 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
261 unsafe {
262 cvt(ffi::X509_set_issuer_name(
263 self.0.as_ptr(),
264 issuer_name.as_ptr(),
265 ))
266 .map(|_| ())
267 }
268 }
269
270 #[corresponds(X509_set_subject_name)]
289 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
290 unsafe {
291 cvt(ffi::X509_set_subject_name(
292 self.0.as_ptr(),
293 subject_name.as_ptr(),
294 ))
295 .map(|_| ())
296 }
297 }
298
299 #[corresponds(X509_set_pubkey)]
301 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
302 where
303 T: HasPublic,
304 {
305 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
306 }
307
308 #[corresponds(X509V3_set_ctx)]
312 pub fn x509v3_context<'a>(
313 &'a self,
314 issuer: Option<&'a X509Ref>,
315 conf: Option<&'a ConfRef>,
316 ) -> X509v3Context<'a> {
317 unsafe {
318 let mut ctx = mem::zeroed();
319
320 let issuer = match issuer {
321 Some(issuer) => issuer.as_ptr(),
322 None => self.0.as_ptr(),
323 };
324 let subject = self.0.as_ptr();
325 ffi::X509V3_set_ctx(
326 &mut ctx,
327 issuer,
328 subject,
329 ptr::null_mut(),
330 ptr::null_mut(),
331 0,
332 );
333
334 if let Some(conf) = conf {
336 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
337 }
338
339 X509v3Context(ctx, PhantomData)
340 }
341 }
342
343 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
347 self.append_extension2(&extension)
348 }
349
350 #[corresponds(X509_add_ext)]
352 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
353 unsafe {
354 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
355 Ok(())
356 }
357 }
358
359 #[corresponds(X509_sign)]
361 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
362 where
363 T: HasPrivate,
364 {
365 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
366 }
367
368 pub fn build(self) -> X509 {
370 self.0
371 }
372}
373
374foreign_type_and_impl_send_sync! {
375 type CType = ffi::X509;
376 fn drop = ffi::X509_free;
377
378 pub struct X509;
380 pub struct X509Ref;
382}
383
384impl X509Ref {
385 #[corresponds(X509_get_subject_name)]
387 pub fn subject_name(&self) -> &X509NameRef {
388 unsafe {
389 let name = ffi::X509_get_subject_name(self.as_ptr());
390 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
391 }
392 }
393
394 #[corresponds(X509_subject_name_hash)]
396 pub fn subject_name_hash(&self) -> u32 {
397 #[allow(clippy::unnecessary_cast)]
398 unsafe {
399 ffi::X509_subject_name_hash(self.as_ptr()) as u32
400 }
401 }
402
403 #[corresponds(X509_get_issuer_name)]
405 pub fn issuer_name(&self) -> &X509NameRef {
406 unsafe {
407 let name = ffi::X509_get_issuer_name(self.as_ptr());
408 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
409 }
410 }
411
412 #[corresponds(X509_issuer_name_hash)]
414 pub fn issuer_name_hash(&self) -> u32 {
415 #[allow(clippy::unnecessary_cast)]
416 unsafe {
417 ffi::X509_issuer_name_hash(self.as_ptr()) as u32
418 }
419 }
420
421 #[corresponds(X509_get_ext_d2i)]
423 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
424 unsafe {
425 let stack = ffi::X509_get_ext_d2i(
426 self.as_ptr(),
427 ffi::NID_subject_alt_name,
428 ptr::null_mut(),
429 ptr::null_mut(),
430 );
431 Stack::from_ptr_opt(stack as *mut _)
432 }
433 }
434
435 #[corresponds(X509_get_ext_d2i)]
437 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
438 unsafe {
439 let stack = ffi::X509_get_ext_d2i(
440 self.as_ptr(),
441 ffi::NID_crl_distribution_points,
442 ptr::null_mut(),
443 ptr::null_mut(),
444 );
445 Stack::from_ptr_opt(stack as *mut _)
446 }
447 }
448
449 #[corresponds(X509_get_ext_d2i)]
451 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
452 unsafe {
453 let stack = ffi::X509_get_ext_d2i(
454 self.as_ptr(),
455 ffi::NID_issuer_alt_name,
456 ptr::null_mut(),
457 ptr::null_mut(),
458 );
459 Stack::from_ptr_opt(stack as *mut _)
460 }
461 }
462
463 #[corresponds(X509_get_ext_d2i)]
467 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
468 unsafe {
469 let stack = ffi::X509_get_ext_d2i(
470 self.as_ptr(),
471 ffi::NID_info_access,
472 ptr::null_mut(),
473 ptr::null_mut(),
474 );
475 Stack::from_ptr_opt(stack as *mut _)
476 }
477 }
478
479 #[corresponds(X509_get_pathlen)]
481 #[cfg(any(ossl110, boringssl, awslc))]
482 pub fn pathlen(&self) -> Option<u32> {
483 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
484 u32::try_from(v).ok()
485 }
486
487 #[corresponds(X509_get0_subject_key_id)]
489 #[cfg(any(ossl110, boringssl, awslc))]
490 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
491 unsafe {
492 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
493 Asn1OctetStringRef::from_const_ptr_opt(data)
494 }
495 }
496
497 #[corresponds(X509_get0_authority_key_id)]
499 #[cfg(any(ossl110, boringssl, awslc))]
500 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
501 unsafe {
502 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
503 Asn1OctetStringRef::from_const_ptr_opt(data)
504 }
505 }
506
507 #[corresponds(X509_get0_authority_issuer)]
509 #[cfg(ossl111d)]
510 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
511 unsafe {
512 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
513 StackRef::from_const_ptr_opt(stack)
514 }
515 }
516
517 #[corresponds(X509_get0_authority_serial)]
519 #[cfg(ossl111d)]
520 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
521 unsafe {
522 let r = ffi::X509_get0_authority_serial(self.as_ptr());
523 Asn1IntegerRef::from_const_ptr_opt(r)
524 }
525 }
526
527 #[corresponds(X509_get_pubkey)]
528 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
529 unsafe {
530 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
531 Ok(PKey::from_ptr(pkey))
532 }
533 }
534
535 #[corresponds(X509_digest)]
537 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
538 unsafe {
539 let mut digest = DigestBytes {
540 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
541 len: ffi::EVP_MAX_MD_SIZE as usize,
542 };
543 let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
544 cvt(ffi::X509_digest(
545 self.as_ptr(),
546 hash_type.as_ptr(),
547 digest.buf.as_mut_ptr() as *mut _,
548 &mut len,
549 ))?;
550 digest.len = len as usize;
551
552 Ok(digest)
553 }
554 }
555
556 #[deprecated(since = "0.10.9", note = "renamed to digest")]
557 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
558 self.digest(hash_type).map(|b| b.to_vec())
559 }
560
561 #[corresponds(X509_getm_notAfter)]
563 pub fn not_after(&self) -> &Asn1TimeRef {
564 unsafe {
565 let date = X509_getm_notAfter(self.as_ptr());
566 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
567 }
568 }
569
570 #[corresponds(X509_getm_notBefore)]
572 pub fn not_before(&self) -> &Asn1TimeRef {
573 unsafe {
574 let date = X509_getm_notBefore(self.as_ptr());
575 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
576 }
577 }
578
579 #[corresponds(X509_get0_signature)]
581 pub fn signature(&self) -> &Asn1BitStringRef {
582 unsafe {
583 let mut signature = ptr::null();
584 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
585 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
586 }
587 }
588
589 #[corresponds(X509_get0_signature)]
591 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
592 unsafe {
593 let mut algor = ptr::null();
594 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
595 X509AlgorithmRef::from_const_ptr_opt(algor)
596 .expect("signature algorithm must not be null")
597 }
598 }
599
600 #[corresponds(X509_get1_ocsp)]
603 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
604 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
605 }
606
607 #[corresponds(X509_check_issued)]
609 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
610 unsafe {
611 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
612 X509VerifyResult::from_raw(r)
613 }
614 }
615
616 #[corresponds(X509_get_version)]
621 #[cfg(ossl110)]
622 #[allow(clippy::unnecessary_cast)]
623 pub fn version(&self) -> i32 {
624 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
625 }
626
627 #[corresponds(X509_verify)]
634 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
635 where
636 T: HasPublic,
637 {
638 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
639 }
640
641 #[corresponds(X509_get_serialNumber)]
643 pub fn serial_number(&self) -> &Asn1IntegerRef {
644 unsafe {
645 let r = ffi::X509_get_serialNumber(self.as_ptr());
646 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
647 }
648 }
649
650 #[corresponds(X509_alias_get0)]
656 pub fn alias(&self) -> Option<&[u8]> {
657 unsafe {
658 let mut len = 0;
659 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
660 if ptr.is_null() {
661 None
662 } else {
663 Some(util::from_raw_parts(ptr, len as usize))
664 }
665 }
666 }
667
668 to_pem! {
669 #[corresponds(PEM_write_bio_X509)]
673 to_pem,
674 ffi::PEM_write_bio_X509
675 }
676
677 to_der! {
678 #[corresponds(i2d_X509)]
680 to_der,
681 ffi::i2d_X509
682 }
683
684 to_pem! {
685 #[corresponds(X509_print)]
687 to_text,
688 ffi::X509_print
689 }
690}
691
692impl ToOwned for X509Ref {
693 type Owned = X509;
694
695 fn to_owned(&self) -> X509 {
696 unsafe {
697 X509_up_ref(self.as_ptr());
698 X509::from_ptr(self.as_ptr())
699 }
700 }
701}
702
703impl Ord for X509Ref {
704 fn cmp(&self, other: &Self) -> cmp::Ordering {
705 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
708 cmp.cmp(&0)
709 }
710}
711
712impl PartialOrd for X509Ref {
713 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
714 Some(self.cmp(other))
715 }
716}
717
718impl PartialOrd<X509> for X509Ref {
719 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
720 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
721 }
722}
723
724impl PartialEq for X509Ref {
725 fn eq(&self, other: &Self) -> bool {
726 self.cmp(other) == cmp::Ordering::Equal
727 }
728}
729
730impl PartialEq<X509> for X509Ref {
731 fn eq(&self, other: &X509) -> bool {
732 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
733 }
734}
735
736impl Eq for X509Ref {}
737
738impl X509 {
739 pub fn builder() -> Result<X509Builder, ErrorStack> {
741 X509Builder::new()
742 }
743
744 from_pem! {
745 #[corresponds(PEM_read_bio_X509)]
749 from_pem,
750 X509,
751 ffi::PEM_read_bio_X509
752 }
753
754 from_der! {
755 #[corresponds(d2i_X509)]
757 from_der,
758 X509,
759 ffi::d2i_X509
760 }
761
762 #[corresponds(PEM_read_bio_X509)]
764 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
765 unsafe {
766 ffi::init();
767 let bio = MemBioSlice::new(pem)?;
768
769 let mut certs = vec![];
770 loop {
771 let r =
772 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
773 if r.is_null() {
774 let e = ErrorStack::get();
775
776 if let Some(err) = e.errors().last() {
777 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
778 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
779 {
780 break;
781 }
782 }
783
784 return Err(e);
785 } else {
786 certs.push(X509(r));
787 }
788 }
789
790 Ok(certs)
791 }
792 }
793}
794
795impl Clone for X509 {
796 fn clone(&self) -> X509 {
797 X509Ref::to_owned(self)
798 }
799}
800
801impl fmt::Debug for X509 {
802 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
803 let serial = match &self.serial_number().to_bn() {
804 Ok(bn) => match bn.to_hex_str() {
805 Ok(hex) => hex.to_string(),
806 Err(_) => "".to_string(),
807 },
808 Err(_) => "".to_string(),
809 };
810 let mut debug_struct = formatter.debug_struct("X509");
811 debug_struct.field("serial_number", &serial);
812 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
813 debug_struct.field("issuer", &self.issuer_name());
814 debug_struct.field("subject", &self.subject_name());
815 if let Some(subject_alt_names) = &self.subject_alt_names() {
816 debug_struct.field("subject_alt_names", subject_alt_names);
817 }
818 debug_struct.field("not_before", &self.not_before());
819 debug_struct.field("not_after", &self.not_after());
820
821 if let Ok(public_key) = &self.public_key() {
822 debug_struct.field("public_key", public_key);
823 };
824 debug_struct.finish()
827 }
828}
829
830impl AsRef<X509Ref> for X509Ref {
831 fn as_ref(&self) -> &X509Ref {
832 self
833 }
834}
835
836impl Stackable for X509 {
837 type StackType = ffi::stack_st_X509;
838}
839
840impl Ord for X509 {
841 fn cmp(&self, other: &Self) -> cmp::Ordering {
842 X509Ref::cmp(self, other)
843 }
844}
845
846impl PartialOrd for X509 {
847 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
848 Some(self.cmp(other))
849 }
850}
851
852impl PartialOrd<X509Ref> for X509 {
853 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
854 X509Ref::partial_cmp(self, other)
855 }
856}
857
858impl PartialEq for X509 {
859 fn eq(&self, other: &Self) -> bool {
860 X509Ref::eq(self, other)
861 }
862}
863
864impl PartialEq<X509Ref> for X509 {
865 fn eq(&self, other: &X509Ref) -> bool {
866 X509Ref::eq(self, other)
867 }
868}
869
870impl Eq for X509 {}
871
872pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
874
875impl X509v3Context<'_> {
876 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
877 &self.0 as *const _ as *mut _
878 }
879}
880
881foreign_type_and_impl_send_sync! {
882 type CType = ffi::X509_EXTENSION;
883 fn drop = ffi::X509_EXTENSION_free;
884
885 pub struct X509Extension;
887 pub struct X509ExtensionRef;
889}
890
891impl Stackable for X509Extension {
892 type StackType = ffi::stack_st_X509_EXTENSION;
893}
894
895impl X509Extension {
896 #[deprecated(
910 note = "Use x509::extension types or new_from_der instead",
911 since = "0.10.51"
912 )]
913 pub fn new(
914 conf: Option<&ConfRef>,
915 context: Option<&X509v3Context<'_>>,
916 name: &str,
917 value: &str,
918 ) -> Result<X509Extension, ErrorStack> {
919 let name = CString::new(name).unwrap();
920 let value = CString::new(value).unwrap();
921 let mut ctx;
922 unsafe {
923 ffi::init();
924 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
925 let context_ptr = match context {
926 Some(c) => c.as_ptr(),
927 None => {
928 ctx = mem::zeroed();
929
930 ffi::X509V3_set_ctx(
931 &mut ctx,
932 ptr::null_mut(),
933 ptr::null_mut(),
934 ptr::null_mut(),
935 ptr::null_mut(),
936 0,
937 );
938 &mut ctx
939 }
940 };
941 let name = name.as_ptr() as *mut _;
942 let value = value.as_ptr() as *mut _;
943
944 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
945 }
946 }
947
948 #[deprecated(
962 note = "Use x509::extension types or new_from_der instead",
963 since = "0.10.51"
964 )]
965 pub fn new_nid(
966 conf: Option<&ConfRef>,
967 context: Option<&X509v3Context<'_>>,
968 name: Nid,
969 value: &str,
970 ) -> Result<X509Extension, ErrorStack> {
971 let value = CString::new(value).unwrap();
972 let mut ctx;
973 unsafe {
974 ffi::init();
975 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
976 let context_ptr = match context {
977 Some(c) => c.as_ptr(),
978 None => {
979 ctx = mem::zeroed();
980
981 ffi::X509V3_set_ctx(
982 &mut ctx,
983 ptr::null_mut(),
984 ptr::null_mut(),
985 ptr::null_mut(),
986 ptr::null_mut(),
987 0,
988 );
989 &mut ctx
990 }
991 };
992 let name = name.as_raw();
993 let value = value.as_ptr() as *mut _;
994
995 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
996 }
997 }
998
999 pub fn new_from_der(
1009 oid: &Asn1ObjectRef,
1010 critical: bool,
1011 der_contents: &Asn1OctetStringRef,
1012 ) -> Result<X509Extension, ErrorStack> {
1013 unsafe {
1014 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1015 ptr::null_mut(),
1016 oid.as_ptr(),
1017 critical as _,
1018 der_contents.as_ptr(),
1019 ))
1020 .map(X509Extension)
1021 }
1022 }
1023
1024 pub(crate) unsafe fn new_internal(
1025 nid: Nid,
1026 critical: bool,
1027 value: *mut c_void,
1028 ) -> Result<X509Extension, ErrorStack> {
1029 ffi::init();
1030 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1031 }
1032
1033 #[cfg(not(libressl390))]
1039 #[corresponds(X509V3_EXT_add_alias)]
1040 #[deprecated(
1041 note = "Use x509::extension types or new_from_der and then this is not necessary",
1042 since = "0.10.51"
1043 )]
1044 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1045 ffi::init();
1046 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1047 }
1048}
1049
1050impl X509ExtensionRef {
1051 to_der! {
1052 #[corresponds(i2d_X509_EXTENSION)]
1054 to_der,
1055 ffi::i2d_X509_EXTENSION
1056 }
1057}
1058
1059pub struct X509NameBuilder(X509Name);
1061
1062impl X509NameBuilder {
1063 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1065 unsafe {
1066 ffi::init();
1067 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1068 }
1069 }
1070
1071 #[corresponds(X509_NAME_add_entry)]
1073 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1074 unsafe {
1075 cvt(ffi::X509_NAME_add_entry(
1076 self.0.as_ptr(),
1077 ne.as_ptr(),
1078 -1,
1079 0,
1080 ))
1081 .map(|_| ())
1082 }
1083 }
1084
1085 #[corresponds(X509_NAME_add_entry_by_txt)]
1087 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1088 unsafe {
1089 let field = CString::new(field).unwrap();
1090 assert!(value.len() <= crate::SLenType::MAX as usize);
1091 cvt(ffi::X509_NAME_add_entry_by_txt(
1092 self.0.as_ptr(),
1093 field.as_ptr() as *mut _,
1094 ffi::MBSTRING_UTF8,
1095 value.as_ptr(),
1096 value.len() as crate::SLenType,
1097 -1,
1098 0,
1099 ))
1100 .map(|_| ())
1101 }
1102 }
1103
1104 #[corresponds(X509_NAME_add_entry_by_txt)]
1106 pub fn append_entry_by_text_with_type(
1107 &mut self,
1108 field: &str,
1109 value: &str,
1110 ty: Asn1Type,
1111 ) -> Result<(), ErrorStack> {
1112 unsafe {
1113 let field = CString::new(field).unwrap();
1114 assert!(value.len() <= crate::SLenType::MAX as usize);
1115 cvt(ffi::X509_NAME_add_entry_by_txt(
1116 self.0.as_ptr(),
1117 field.as_ptr() as *mut _,
1118 ty.as_raw(),
1119 value.as_ptr(),
1120 value.len() as crate::SLenType,
1121 -1,
1122 0,
1123 ))
1124 .map(|_| ())
1125 }
1126 }
1127
1128 #[corresponds(X509_NAME_add_entry_by_NID)]
1130 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1131 unsafe {
1132 assert!(value.len() <= crate::SLenType::MAX as usize);
1133 cvt(ffi::X509_NAME_add_entry_by_NID(
1134 self.0.as_ptr(),
1135 field.as_raw(),
1136 ffi::MBSTRING_UTF8,
1137 value.as_ptr() as *mut _,
1138 value.len() as crate::SLenType,
1139 -1,
1140 0,
1141 ))
1142 .map(|_| ())
1143 }
1144 }
1145
1146 #[corresponds(X509_NAME_add_entry_by_NID)]
1148 pub fn append_entry_by_nid_with_type(
1149 &mut self,
1150 field: Nid,
1151 value: &str,
1152 ty: Asn1Type,
1153 ) -> Result<(), ErrorStack> {
1154 unsafe {
1155 assert!(value.len() <= crate::SLenType::MAX as usize);
1156 cvt(ffi::X509_NAME_add_entry_by_NID(
1157 self.0.as_ptr(),
1158 field.as_raw(),
1159 ty.as_raw(),
1160 value.as_ptr() as *mut _,
1161 value.len() as crate::SLenType,
1162 -1,
1163 0,
1164 ))
1165 .map(|_| ())
1166 }
1167 }
1168
1169 pub fn build(self) -> X509Name {
1171 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1175 }
1176}
1177
1178foreign_type_and_impl_send_sync! {
1179 type CType = ffi::X509_NAME;
1180 fn drop = ffi::X509_NAME_free;
1181
1182 pub struct X509Name;
1184 pub struct X509NameRef;
1186}
1187
1188impl X509Name {
1189 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1191 X509NameBuilder::new()
1192 }
1193
1194 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1198 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1199 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1200 }
1201
1202 from_der! {
1203 from_der,
1209 X509Name,
1210 ffi::d2i_X509_NAME
1211 }
1212}
1213
1214impl Stackable for X509Name {
1215 type StackType = ffi::stack_st_X509_NAME;
1216}
1217
1218impl X509NameRef {
1219 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1221 X509NameEntries {
1222 name: self,
1223 nid: Some(nid),
1224 loc: -1,
1225 }
1226 }
1227
1228 pub fn entries(&self) -> X509NameEntries<'_> {
1230 X509NameEntries {
1231 name: self,
1232 nid: None,
1233 loc: -1,
1234 }
1235 }
1236
1237 #[corresponds(X509_NAME_cmp)]
1244 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1245 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1246 if cfg!(ossl300) && cmp == -2 {
1247 return Err(ErrorStack::get());
1248 }
1249 Ok(cmp.cmp(&0))
1250 }
1251
1252 #[corresponds(X509_NAME_dup)]
1254 #[cfg(any(boringssl, ossl110, libressl, awslc))]
1255 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1256 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1257 }
1258
1259 to_der! {
1260 to_der,
1266 ffi::i2d_X509_NAME
1267 }
1268}
1269
1270impl fmt::Debug for X509NameRef {
1271 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1272 formatter.debug_list().entries(self.entries()).finish()
1273 }
1274}
1275
1276pub struct X509NameEntries<'a> {
1278 name: &'a X509NameRef,
1279 nid: Option<Nid>,
1280 loc: c_int,
1281}
1282
1283impl<'a> Iterator for X509NameEntries<'a> {
1284 type Item = &'a X509NameEntryRef;
1285
1286 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1287 unsafe {
1288 match self.nid {
1289 Some(nid) => {
1290 self.loc =
1292 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1293 if self.loc == -1 {
1294 return None;
1295 }
1296 }
1297 None => {
1298 self.loc += 1;
1300 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1301 return None;
1302 }
1303 }
1304 }
1305
1306 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1307
1308 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1309 }
1310 }
1311}
1312
1313foreign_type_and_impl_send_sync! {
1314 type CType = ffi::X509_NAME_ENTRY;
1315 fn drop = ffi::X509_NAME_ENTRY_free;
1316
1317 pub struct X509NameEntry;
1319 pub struct X509NameEntryRef;
1321}
1322
1323impl X509NameEntryRef {
1324 #[corresponds(X509_NAME_ENTRY_get_data)]
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 #[corresponds(X509_NAME_ENTRY_get_object)]
1336 pub fn object(&self) -> &Asn1ObjectRef {
1337 unsafe {
1338 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1339 Asn1ObjectRef::from_ptr(object)
1340 }
1341 }
1342}
1343
1344impl fmt::Debug for X509NameEntryRef {
1345 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1346 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1347 }
1348}
1349
1350pub struct X509ReqBuilder(X509Req);
1352
1353impl X509ReqBuilder {
1354 #[corresponds(X509_REQ_new)]
1356 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1357 unsafe {
1358 ffi::init();
1359 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1360 }
1361 }
1362
1363 #[corresponds(X509_REQ_set_version)]
1365 #[allow(clippy::useless_conversion)]
1366 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1367 unsafe {
1368 cvt(ffi::X509_REQ_set_version(
1369 self.0.as_ptr(),
1370 version as c_long,
1371 ))
1372 .map(|_| ())
1373 }
1374 }
1375
1376 #[corresponds(X509_REQ_set_subject_name)]
1378 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1379 unsafe {
1380 cvt(ffi::X509_REQ_set_subject_name(
1381 self.0.as_ptr(),
1382 subject_name.as_ptr(),
1383 ))
1384 .map(|_| ())
1385 }
1386 }
1387
1388 #[corresponds(X509_REQ_set_pubkey)]
1390 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1391 where
1392 T: HasPublic,
1393 {
1394 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1395 }
1396
1397 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1400 unsafe {
1401 let mut ctx = mem::zeroed();
1402
1403 ffi::X509V3_set_ctx(
1404 &mut ctx,
1405 ptr::null_mut(),
1406 ptr::null_mut(),
1407 self.0.as_ptr(),
1408 ptr::null_mut(),
1409 0,
1410 );
1411
1412 if let Some(conf) = conf {
1414 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1415 }
1416
1417 X509v3Context(ctx, PhantomData)
1418 }
1419 }
1420
1421 pub fn add_extensions(
1423 &mut self,
1424 extensions: &StackRef<X509Extension>,
1425 ) -> Result<(), ErrorStack> {
1426 unsafe {
1427 cvt(ffi::X509_REQ_add_extensions(
1428 self.0.as_ptr(),
1429 extensions.as_ptr(),
1430 ))
1431 .map(|_| ())
1432 }
1433 }
1434
1435 #[corresponds(X509_REQ_sign)]
1437 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1438 where
1439 T: HasPrivate,
1440 {
1441 unsafe {
1442 cvt(ffi::X509_REQ_sign(
1443 self.0.as_ptr(),
1444 key.as_ptr(),
1445 hash.as_ptr(),
1446 ))
1447 .map(|_| ())
1448 }
1449 }
1450
1451 pub fn build(self) -> X509Req {
1453 self.0
1454 }
1455}
1456
1457foreign_type_and_impl_send_sync! {
1458 type CType = ffi::X509_REQ;
1459 fn drop = ffi::X509_REQ_free;
1460
1461 pub struct X509Req;
1463 pub struct X509ReqRef;
1465}
1466
1467impl X509Req {
1468 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1470 X509ReqBuilder::new()
1471 }
1472
1473 from_pem! {
1474 from_pem,
1482 X509Req,
1483 ffi::PEM_read_bio_X509_REQ
1484 }
1485
1486 from_der! {
1487 from_der,
1493 X509Req,
1494 ffi::d2i_X509_REQ
1495 }
1496}
1497
1498impl X509ReqRef {
1499 to_pem! {
1500 to_pem,
1508 ffi::PEM_write_bio_X509_REQ
1509 }
1510
1511 to_der! {
1512 to_der,
1518 ffi::i2d_X509_REQ
1519 }
1520
1521 to_pem! {
1522 #[corresponds(X509_Req_print)]
1524 to_text,
1525 ffi::X509_REQ_print
1526 }
1527
1528 #[corresponds(X509_REQ_get_version)]
1530 #[allow(clippy::unnecessary_cast)]
1531 pub fn version(&self) -> i32 {
1532 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1533 }
1534
1535 #[corresponds(X509_REQ_get_subject_name)]
1537 pub fn subject_name(&self) -> &X509NameRef {
1538 unsafe {
1539 let name = X509_REQ_get_subject_name(self.as_ptr());
1540 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1541 }
1542 }
1543
1544 #[corresponds(X509_REQ_get_pubkey)]
1546 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1547 unsafe {
1548 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1549 Ok(PKey::from_ptr(key))
1550 }
1551 }
1552
1553 #[corresponds(X509_REQ_verify)]
1557 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1558 where
1559 T: HasPublic,
1560 {
1561 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1562 }
1563
1564 #[corresponds(X509_REQ_get_extensions)]
1566 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1567 unsafe {
1568 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1569 Ok(Stack::from_ptr(extensions))
1570 }
1571 }
1572}
1573
1574#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1576pub struct CrlReason(c_int);
1577
1578#[allow(missing_docs)] impl CrlReason {
1580 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1581 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1582 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1583 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1584 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1585 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1586 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1587 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1588 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1589 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1590
1591 pub const fn from_raw(value: c_int) -> Self {
1593 CrlReason(value)
1594 }
1595
1596 pub const fn as_raw(&self) -> c_int {
1598 self.0
1599 }
1600}
1601
1602foreign_type_and_impl_send_sync! {
1603 type CType = ffi::X509_REVOKED;
1604 fn drop = ffi::X509_REVOKED_free;
1605
1606 pub struct X509Revoked;
1608 pub struct X509RevokedRef;
1610}
1611
1612impl Stackable for X509Revoked {
1613 type StackType = ffi::stack_st_X509_REVOKED;
1614}
1615
1616impl X509Revoked {
1617 from_der! {
1618 #[corresponds(d2i_X509_REVOKED)]
1620 from_der,
1621 X509Revoked,
1622 ffi::d2i_X509_REVOKED
1623 }
1624}
1625
1626impl X509RevokedRef {
1627 to_der! {
1628 #[corresponds(d2i_X509_REVOKED)]
1630 to_der,
1631 ffi::i2d_X509_REVOKED
1632 }
1633
1634 #[corresponds(X509_NAME_dup)]
1636 #[cfg(any(boringssl, ossl110, libressl, awslc))]
1637 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1638 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1639 }
1640
1641 #[corresponds(X509_REVOKED_get0_revocationDate)]
1643 pub fn revocation_date(&self) -> &Asn1TimeRef {
1644 unsafe {
1645 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1646 assert!(!r.is_null());
1647 Asn1TimeRef::from_ptr(r as *mut _)
1648 }
1649 }
1650
1651 #[corresponds(X509_REVOKED_get0_serialNumber)]
1653 pub fn serial_number(&self) -> &Asn1IntegerRef {
1654 unsafe {
1655 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1656 assert!(!r.is_null());
1657 Asn1IntegerRef::from_ptr(r as *mut _)
1658 }
1659 }
1660
1661 #[corresponds(X509_REVOKED_get_ext_d2i)]
1665 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1666 let mut critical = -1;
1667 let out = unsafe {
1668 let ext = ffi::X509_REVOKED_get_ext_d2i(
1670 self.as_ptr(),
1671 T::NID.as_raw(),
1672 &mut critical as *mut _,
1673 ptr::null_mut(),
1674 );
1675 T::Output::from_ptr_opt(ext as *mut _)
1678 };
1679 match (critical, out) {
1680 (0, Some(out)) => Ok(Some((false, out))),
1681 (1, Some(out)) => Ok(Some((true, out))),
1682 (-1 | -2, _) => Ok(None),
1684 (0 | 1, None) => Err(ErrorStack::get()),
1687 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1688 }
1689 }
1690}
1691
1692pub enum ReasonCode {}
1695
1696unsafe impl ExtensionType for ReasonCode {
1699 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1700
1701 type Output = Asn1Enumerated;
1702}
1703
1704pub enum CertificateIssuer {}
1707
1708unsafe impl ExtensionType for CertificateIssuer {
1711 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1712
1713 type Output = Stack<GeneralName>;
1714}
1715
1716pub enum AuthorityInformationAccess {}
1718
1719unsafe impl ExtensionType for AuthorityInformationAccess {
1722 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1723
1724 type Output = Stack<AccessDescription>;
1725}
1726
1727foreign_type_and_impl_send_sync! {
1728 type CType = ffi::X509_CRL;
1729 fn drop = ffi::X509_CRL_free;
1730
1731 pub struct X509Crl;
1733 pub struct X509CrlRef;
1735}
1736
1737pub enum CrlStatus<'a> {
1743 NotRevoked,
1745 Revoked(&'a X509RevokedRef),
1747 RemoveFromCrl(&'a X509RevokedRef),
1752}
1753
1754impl<'a> CrlStatus<'a> {
1755 unsafe fn from_ffi_status(
1760 status: c_int,
1761 revoked_entry: *mut ffi::X509_REVOKED,
1762 ) -> CrlStatus<'a> {
1763 match status {
1764 0 => CrlStatus::NotRevoked,
1765 1 => {
1766 assert!(!revoked_entry.is_null());
1767 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1768 }
1769 2 => {
1770 assert!(!revoked_entry.is_null());
1771 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1772 }
1773 _ => unreachable!(
1774 "{}",
1775 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1776 ),
1777 }
1778 }
1779}
1780
1781impl X509Crl {
1782 from_pem! {
1783 #[corresponds(PEM_read_bio_X509_CRL)]
1787 from_pem,
1788 X509Crl,
1789 ffi::PEM_read_bio_X509_CRL
1790 }
1791
1792 from_der! {
1793 #[corresponds(d2i_X509_CRL)]
1795 from_der,
1796 X509Crl,
1797 ffi::d2i_X509_CRL
1798 }
1799}
1800
1801impl X509CrlRef {
1802 to_pem! {
1803 #[corresponds(PEM_write_bio_X509_CRL)]
1807 to_pem,
1808 ffi::PEM_write_bio_X509_CRL
1809 }
1810
1811 to_der! {
1812 #[corresponds(i2d_X509_CRL)]
1814 to_der,
1815 ffi::i2d_X509_CRL
1816 }
1817
1818 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
1820 unsafe {
1821 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
1822 if revoked.is_null() {
1823 None
1824 } else {
1825 Some(StackRef::from_ptr(revoked))
1826 }
1827 }
1828 }
1829
1830 #[corresponds(X509_CRL_get0_lastUpdate)]
1832 pub fn last_update(&self) -> &Asn1TimeRef {
1833 unsafe {
1834 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
1835 assert!(!date.is_null());
1836 Asn1TimeRef::from_ptr(date as *mut _)
1837 }
1838 }
1839
1840 #[corresponds(X509_CRL_get0_nextUpdate)]
1844 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
1845 unsafe {
1846 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
1847 Asn1TimeRef::from_const_ptr_opt(date)
1848 }
1849 }
1850
1851 #[corresponds(X509_CRL_get0_by_serial)]
1853 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
1854 unsafe {
1855 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
1856 let status =
1857 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
1858 CrlStatus::from_ffi_status(status, ret)
1859 }
1860 }
1861
1862 #[corresponds(X509_CRL_get0_by_cert)]
1864 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
1865 unsafe {
1866 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
1867 let status =
1868 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
1869 CrlStatus::from_ffi_status(status, ret)
1870 }
1871 }
1872
1873 #[corresponds(X509_CRL_get_issuer)]
1875 pub fn issuer_name(&self) -> &X509NameRef {
1876 unsafe {
1877 let name = X509_CRL_get_issuer(self.as_ptr());
1878 assert!(!name.is_null());
1879 X509NameRef::from_ptr(name)
1880 }
1881 }
1882
1883 #[corresponds(X509_CRL_verify)]
1890 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1891 where
1892 T: HasPublic,
1893 {
1894 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1895 }
1896
1897 #[corresponds(X509_CRL_get_ext_d2i)]
1901 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1902 let mut critical = -1;
1903 let out = unsafe {
1904 let ext = ffi::X509_CRL_get_ext_d2i(
1906 self.as_ptr(),
1907 T::NID.as_raw(),
1908 &mut critical as *mut _,
1909 ptr::null_mut(),
1910 );
1911 T::Output::from_ptr_opt(ext as *mut _)
1914 };
1915 match (critical, out) {
1916 (0, Some(out)) => Ok(Some((false, out))),
1917 (1, Some(out)) => Ok(Some((true, out))),
1918 (-1 | -2, _) => Ok(None),
1920 (0 | 1, None) => Err(ErrorStack::get()),
1923 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1924 }
1925 }
1926}
1927
1928#[derive(Copy, Clone, PartialEq, Eq)]
1930pub struct X509VerifyResult(c_int);
1931
1932impl fmt::Debug for X509VerifyResult {
1933 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1934 fmt.debug_struct("X509VerifyResult")
1935 .field("code", &self.0)
1936 .field("error", &self.error_string())
1937 .finish()
1938 }
1939}
1940
1941impl fmt::Display for X509VerifyResult {
1942 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1943 fmt.write_str(self.error_string())
1944 }
1945}
1946
1947impl Error for X509VerifyResult {}
1948
1949impl X509VerifyResult {
1950 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
1957 X509VerifyResult(err)
1958 }
1959
1960 #[allow(clippy::trivially_copy_pass_by_ref)]
1962 pub fn as_raw(&self) -> c_int {
1963 self.0
1964 }
1965
1966 #[corresponds(X509_verify_cert_error_string)]
1968 #[allow(clippy::trivially_copy_pass_by_ref)]
1969 pub fn error_string(&self) -> &'static str {
1970 ffi::init();
1971
1972 unsafe {
1973 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
1974 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
1975 }
1976 }
1977
1978 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
1980 pub const APPLICATION_VERIFICATION: X509VerifyResult =
1982 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
1983}
1984
1985foreign_type_and_impl_send_sync! {
1986 type CType = ffi::GENERAL_NAME;
1987 fn drop = ffi::GENERAL_NAME_free;
1988
1989 pub struct GeneralName;
1991 pub struct GeneralNameRef;
1993}
1994
1995impl GeneralName {
1996 unsafe fn new(
1997 type_: c_int,
1998 asn1_type: Asn1Type,
1999 value: &[u8],
2000 ) -> Result<GeneralName, ErrorStack> {
2001 ffi::init();
2002 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2003 (*gn.as_ptr()).type_ = type_;
2004 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2005 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2006
2007 #[cfg(any(boringssl, awslc))]
2008 {
2009 (*gn.as_ptr()).d.ptr = s.cast();
2010 }
2011 #[cfg(not(any(boringssl, awslc)))]
2012 {
2013 (*gn.as_ptr()).d = s.cast();
2014 }
2015
2016 Ok(gn)
2017 }
2018
2019 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2020 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2021 }
2022
2023 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2024 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2025 }
2026
2027 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2028 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2029 }
2030
2031 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2032 match ip {
2033 IpAddr::V4(addr) => unsafe {
2034 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2035 },
2036 IpAddr::V6(addr) => unsafe {
2037 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2038 },
2039 }
2040 }
2041
2042 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2043 unsafe {
2044 ffi::init();
2045 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2046 (*gn).type_ = ffi::GEN_RID;
2047
2048 #[cfg(any(boringssl, awslc))]
2049 {
2050 (*gn).d.registeredID = oid.as_ptr();
2051 }
2052 #[cfg(not(any(boringssl, awslc)))]
2053 {
2054 (*gn).d = oid.as_ptr().cast();
2055 }
2056
2057 mem::forget(oid);
2058
2059 Ok(GeneralName::from_ptr(gn))
2060 }
2061 }
2062
2063 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2064 unsafe {
2065 ffi::init();
2066
2067 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2068 ptr::null_mut(),
2069 &mut value.as_ptr().cast(),
2070 value.len().try_into().unwrap(),
2071 ))?;
2072
2073 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2074 (*gn).type_ = ffi::GEN_OTHERNAME;
2075
2076 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2077 gn,
2078 oid.as_ptr().cast(),
2079 typ,
2080 )) {
2081 ffi::GENERAL_NAME_free(gn);
2082 return Err(e);
2083 }
2084
2085 mem::forget(oid);
2086
2087 Ok(GeneralName::from_ptr(gn))
2088 }
2089 }
2090}
2091
2092impl GeneralNameRef {
2093 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2094 unsafe {
2095 if (*self.as_ptr()).type_ != ffi_type {
2096 return None;
2097 }
2098
2099 #[cfg(any(boringssl, awslc))]
2100 let d = (*self.as_ptr()).d.ptr;
2101 #[cfg(not(any(boringssl, awslc)))]
2102 let d = (*self.as_ptr()).d;
2103
2104 let ptr = ASN1_STRING_get0_data(d as *mut _);
2105 let len = ffi::ASN1_STRING_length(d as *mut _);
2106
2107 #[allow(clippy::unnecessary_cast)]
2108 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2109 str::from_utf8(slice).ok()
2113 }
2114 }
2115
2116 pub fn email(&self) -> Option<&str> {
2118 self.ia5_string(ffi::GEN_EMAIL)
2119 }
2120
2121 pub fn directory_name(&self) -> Option<&X509NameRef> {
2123 unsafe {
2124 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2125 return None;
2126 }
2127
2128 #[cfg(any(boringssl, awslc))]
2129 let d = (*self.as_ptr()).d.ptr;
2130 #[cfg(not(any(boringssl, awslc)))]
2131 let d = (*self.as_ptr()).d;
2132
2133 Some(X509NameRef::from_const_ptr(d as *const _))
2134 }
2135 }
2136
2137 pub fn dnsname(&self) -> Option<&str> {
2139 self.ia5_string(ffi::GEN_DNS)
2140 }
2141
2142 pub fn uri(&self) -> Option<&str> {
2144 self.ia5_string(ffi::GEN_URI)
2145 }
2146
2147 pub fn ipaddress(&self) -> Option<&[u8]> {
2149 unsafe {
2150 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2151 return None;
2152 }
2153 #[cfg(any(boringssl, awslc))]
2154 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2155 #[cfg(not(any(boringssl, awslc)))]
2156 let d = (*self.as_ptr()).d;
2157
2158 let ptr = ASN1_STRING_get0_data(d as *mut _);
2159 let len = ffi::ASN1_STRING_length(d as *mut _);
2160
2161 #[allow(clippy::unnecessary_cast)]
2162 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2163 }
2164 }
2165}
2166
2167impl fmt::Debug for GeneralNameRef {
2168 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2169 if let Some(email) = self.email() {
2170 formatter.write_str(email)
2171 } else if let Some(dnsname) = self.dnsname() {
2172 formatter.write_str(dnsname)
2173 } else if let Some(uri) = self.uri() {
2174 formatter.write_str(uri)
2175 } else if let Some(ipaddress) = self.ipaddress() {
2176 let address = <[u8; 16]>::try_from(ipaddress)
2177 .map(IpAddr::from)
2178 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2179 match address {
2180 Ok(a) => fmt::Debug::fmt(&a, formatter),
2181 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2182 }
2183 } else {
2184 formatter.write_str("(empty)")
2185 }
2186 }
2187}
2188
2189impl Stackable for GeneralName {
2190 type StackType = ffi::stack_st_GENERAL_NAME;
2191}
2192
2193foreign_type_and_impl_send_sync! {
2194 type CType = ffi::DIST_POINT;
2195 fn drop = ffi::DIST_POINT_free;
2196
2197 pub struct DistPoint;
2199 pub struct DistPointRef;
2201}
2202
2203impl DistPointRef {
2204 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2206 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2207 }
2208}
2209
2210foreign_type_and_impl_send_sync! {
2211 type CType = ffi::DIST_POINT_NAME;
2212 fn drop = ffi::DIST_POINT_NAME_free;
2213
2214 pub struct DistPointName;
2216 pub struct DistPointNameRef;
2218}
2219
2220impl DistPointNameRef {
2221 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2223 unsafe {
2224 if (*self.as_ptr()).type_ != 0 {
2225 return None;
2226 }
2227 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2228 }
2229 }
2230}
2231
2232impl Stackable for DistPoint {
2233 type StackType = ffi::stack_st_DIST_POINT;
2234}
2235
2236foreign_type_and_impl_send_sync! {
2237 type CType = ffi::ACCESS_DESCRIPTION;
2238 fn drop = ffi::ACCESS_DESCRIPTION_free;
2239
2240 pub struct AccessDescription;
2242 pub struct AccessDescriptionRef;
2244}
2245
2246impl AccessDescriptionRef {
2247 pub fn method(&self) -> &Asn1ObjectRef {
2249 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2250 }
2251
2252 pub fn location(&self) -> &GeneralNameRef {
2254 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2255 }
2256}
2257
2258impl Stackable for AccessDescription {
2259 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2260}
2261
2262foreign_type_and_impl_send_sync! {
2263 type CType = ffi::X509_ALGOR;
2264 fn drop = ffi::X509_ALGOR_free;
2265
2266 pub struct X509Algorithm;
2268 pub struct X509AlgorithmRef;
2270}
2271
2272impl X509AlgorithmRef {
2273 pub fn object(&self) -> &Asn1ObjectRef {
2275 unsafe {
2276 let mut oid = ptr::null();
2277 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2278 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2279 }
2280 }
2281}
2282
2283foreign_type_and_impl_send_sync! {
2284 type CType = ffi::X509_OBJECT;
2285 fn drop = X509_OBJECT_free;
2286
2287 pub struct X509Object;
2289 pub struct X509ObjectRef;
2291}
2292
2293impl X509ObjectRef {
2294 pub fn x509(&self) -> Option<&X509Ref> {
2295 unsafe {
2296 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2297 X509Ref::from_const_ptr_opt(ptr)
2298 }
2299 }
2300}
2301
2302impl Stackable for X509Object {
2303 type StackType = ffi::stack_st_X509_OBJECT;
2304}
2305
2306cfg_if! {
2307 if #[cfg(any(boringssl, ossl110, libressl, awslc))] {
2308 use ffi::{X509_getm_notAfter, X509_getm_notBefore, X509_up_ref, X509_get0_signature};
2309 } else {
2310 #[allow(bad_style)]
2311 unsafe fn X509_getm_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
2312 (*(*(*x).cert_info).validity).notAfter
2313 }
2314
2315 #[allow(bad_style)]
2316 unsafe fn X509_getm_notBefore(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
2317 (*(*(*x).cert_info).validity).notBefore
2318 }
2319
2320 #[allow(bad_style)]
2321 unsafe fn X509_up_ref(x: *mut ffi::X509) {
2322 ffi::CRYPTO_add_lock(
2323 &mut (*x).references,
2324 1,
2325 ffi::CRYPTO_LOCK_X509,
2326 "mod.rs\0".as_ptr() as *const _,
2327 line!() as c_int,
2328 );
2329 }
2330
2331 #[allow(bad_style)]
2332 unsafe fn X509_get0_signature(
2333 psig: *mut *const ffi::ASN1_BIT_STRING,
2334 palg: *mut *const ffi::X509_ALGOR,
2335 x: *const ffi::X509,
2336 ) {
2337 if !psig.is_null() {
2338 *psig = (*x).signature;
2339 }
2340 if !palg.is_null() {
2341 *palg = (*x).sig_alg;
2342 }
2343 }
2344 }
2345}
2346
2347cfg_if! {
2348 if #[cfg(any(boringssl, ossl110, libressl, awslc))] {
2349 use ffi::{
2350 X509_ALGOR_get0, ASN1_STRING_get0_data, X509_STORE_CTX_get0_chain, X509_set1_notAfter,
2351 X509_set1_notBefore, X509_REQ_get_version, X509_REQ_get_subject_name,
2352 };
2353 } else {
2354 use ffi::{
2355 ASN1_STRING_data as ASN1_STRING_get0_data,
2356 X509_STORE_CTX_get_chain as X509_STORE_CTX_get0_chain,
2357 X509_set_notAfter as X509_set1_notAfter,
2358 X509_set_notBefore as X509_set1_notBefore,
2359 };
2360
2361 #[allow(bad_style)]
2362 unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long {
2363 ffi::ASN1_INTEGER_get((*(*x).req_info).version)
2364 }
2365
2366 #[allow(bad_style)]
2367 unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME {
2368 (*(*x).req_info).subject
2369 }
2370
2371 #[allow(bad_style)]
2372 unsafe fn X509_ALGOR_get0(
2373 paobj: *mut *const ffi::ASN1_OBJECT,
2374 pptype: *mut c_int,
2375 pval: *mut *mut ::libc::c_void,
2376 alg: *const ffi::X509_ALGOR,
2377 ) {
2378 if !paobj.is_null() {
2379 *paobj = (*alg).algorithm;
2380 }
2381 assert!(pptype.is_null());
2382 assert!(pval.is_null());
2383 }
2384 }
2385}
2386
2387cfg_if! {
2388 if #[cfg(any(ossl110, boringssl, libressl, awslc))] {
2389 use ffi::X509_OBJECT_get0_X509;
2390 } else {
2391 #[allow(bad_style)]
2392 unsafe fn X509_OBJECT_get0_X509(x: *mut ffi::X509_OBJECT) -> *mut ffi::X509 {
2393 if (*x).type_ == ffi::X509_LU_X509 {
2394 (*x).data.x509
2395 } else {
2396 ptr::null_mut()
2397 }
2398 }
2399 }
2400}
2401
2402cfg_if! {
2403 if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2404 use ffi::X509_OBJECT_free;
2405 } else {
2406 #[allow(bad_style)]
2407 unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
2408 ffi::X509_OBJECT_free_contents(x);
2409 ffi::CRYPTO_free(x as *mut libc::c_void);
2410 }
2411 }
2412}
2413
2414cfg_if! {
2415 if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2416 use ffi::{
2417 X509_CRL_get_issuer, X509_CRL_get0_nextUpdate, X509_CRL_get0_lastUpdate,
2418 X509_CRL_get_REVOKED,
2419 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2420 };
2421 } else {
2422 #[allow(bad_style)]
2423 unsafe fn X509_CRL_get0_lastUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
2424 (*(*x).crl).lastUpdate
2425 }
2426 #[allow(bad_style)]
2427 unsafe fn X509_CRL_get0_nextUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
2428 (*(*x).crl).nextUpdate
2429 }
2430 #[allow(bad_style)]
2431 unsafe fn X509_CRL_get_issuer(x: *const ffi::X509_CRL) -> *mut ffi::X509_NAME {
2432 (*(*x).crl).issuer
2433 }
2434 #[allow(bad_style)]
2435 unsafe fn X509_CRL_get_REVOKED(x: *const ffi::X509_CRL) -> *mut ffi::stack_st_X509_REVOKED {
2436 (*(*x).crl).revoked
2437 }
2438 #[allow(bad_style)]
2439 unsafe fn X509_REVOKED_get0_serialNumber(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_INTEGER {
2440 (*x).serialNumber
2441 }
2442 #[allow(bad_style)]
2443 unsafe fn X509_REVOKED_get0_revocationDate(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_TIME {
2444 (*x).revocationDate
2445 }
2446 }
2447}
2448
2449#[derive(Copy, Clone, PartialEq, Eq)]
2450pub struct X509PurposeId(c_int);
2451
2452impl X509PurposeId {
2453 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2454 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2455 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2456 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2457 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2458 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2459 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2460 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2461 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2462 #[cfg(ossl320)]
2463 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2464
2465 pub fn from_raw(id: c_int) -> Self {
2467 X509PurposeId(id)
2468 }
2469
2470 pub fn as_raw(&self) -> c_int {
2472 self.0
2473 }
2474}
2475
2476pub struct X509PurposeRef(Opaque);
2478
2479impl ForeignTypeRef for X509PurposeRef {
2481 type CType = ffi::X509_PURPOSE;
2482}
2483
2484impl X509PurposeRef {
2485 #[allow(clippy::unnecessary_cast)]
2499 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2500 unsafe {
2501 let sname = CString::new(sname).unwrap();
2502 cfg_if! {
2503 if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2504 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2505 } else {
2506 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *mut _))?;
2507 }
2508 }
2509 Ok(purpose)
2510 }
2511 }
2512 #[corresponds(X509_PURPOSE_get0)]
2515 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2516 unsafe {
2517 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2518 Ok(X509PurposeRef::from_const_ptr(ptr))
2519 }
2520 }
2521
2522 pub fn purpose(&self) -> X509PurposeId {
2533 unsafe {
2534 cfg_if! {
2535 if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2536 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2537 } else {
2538 let x509_purpose = self.as_ptr() as *mut ffi::X509_PURPOSE;
2539 }
2540 }
2541 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2542 }
2543 }
2544}