1use cfg_if::cfg_if;
11use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
12use libc::{c_int, c_long, c_uchar, c_uint, c_void};
13use std::cmp::{self, Ordering};
14use std::convert::{TryFrom, TryInto};
15use std::error::Error;
16use std::ffi::{CStr, CString};
17use std::fmt;
18use std::marker::PhantomData;
19use std::mem;
20use std::net::IpAddr;
21use std::path::Path;
22use std::ptr;
23use std::str;
24
25use crate::asn1::{
26 Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
27 Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
28};
29use crate::bio::MemBioSlice;
30use crate::conf::ConfRef;
31use crate::error::ErrorStack;
32use crate::ex_data::Index;
33use crate::hash::{DigestBytes, MessageDigest};
34use crate::nid::Nid;
35use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
36use crate::ssl::SslRef;
37use crate::stack::{Stack, StackRef, Stackable};
38use crate::string::OpensslString;
39use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
40use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
41use openssl_macros::corresponds;
42
43#[cfg(any(ossl102, boringssl, libressl261, awslc))]
44pub mod verify;
45
46pub mod extension;
47pub mod store;
48
49#[cfg(test)]
50mod tests;
51
52pub unsafe trait ExtensionType {
58 const NID: Nid;
59 type Output: ForeignType;
60}
61
62foreign_type_and_impl_send_sync! {
63 type CType = ffi::X509_STORE_CTX;
64 fn drop = ffi::X509_STORE_CTX_free;
65
66 pub struct X509StoreContext;
68
69 pub struct X509StoreContextRef;
71}
72
73impl X509StoreContext {
74 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
77 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
78 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
79 }
80
81 #[corresponds(X509_STORE_CTX_new)]
83 pub fn new() -> Result<X509StoreContext, ErrorStack> {
84 unsafe {
85 ffi::init();
86 cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
87 }
88 }
89}
90
91impl X509StoreContextRef {
92 #[corresponds(X509_STORE_CTX_get_ex_data)]
94 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
95 unsafe {
96 let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
97 if data.is_null() {
98 None
99 } else {
100 Some(&*(data as *const T))
101 }
102 }
103 }
104
105 #[corresponds(X509_STORE_CTX_get_error)]
107 pub fn error(&self) -> X509VerifyResult {
108 unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
109 }
110
111 pub fn init<F, T>(
127 &mut self,
128 trust: &store::X509StoreRef,
129 cert: &X509Ref,
130 cert_chain: &StackRef<X509>,
131 with_context: F,
132 ) -> Result<T, ErrorStack>
133 where
134 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
135 {
136 struct Cleanup<'a>(&'a mut X509StoreContextRef);
137
138 impl Drop for Cleanup<'_> {
139 fn drop(&mut self) {
140 unsafe {
141 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
142 }
143 }
144 }
145
146 unsafe {
147 cvt(ffi::X509_STORE_CTX_init(
148 self.as_ptr(),
149 trust.as_ptr(),
150 cert.as_ptr(),
151 cert_chain.as_ptr(),
152 ))?;
153
154 let cleanup = Cleanup(self);
155 with_context(cleanup.0)
156 }
157 }
158
159 #[corresponds(X509_verify_cert)]
166 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
167 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
168 }
169
170 #[corresponds(X509_STORE_CTX_set_error)]
172 pub fn set_error(&mut self, result: X509VerifyResult) {
173 unsafe {
174 ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
175 }
176 }
177
178 #[corresponds(X509_STORE_CTX_get_current_cert)]
181 pub fn current_cert(&self) -> Option<&X509Ref> {
182 unsafe {
183 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
184 X509Ref::from_const_ptr_opt(ptr)
185 }
186 }
187
188 #[corresponds(X509_STORE_CTX_get_error_depth)]
193 pub fn error_depth(&self) -> u32 {
194 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
195 }
196
197 #[corresponds(X509_STORE_CTX_get0_chain)]
199 pub fn chain(&self) -> Option<&StackRef<X509>> {
200 unsafe {
201 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
202
203 if chain.is_null() {
204 None
205 } else {
206 Some(StackRef::from_ptr(chain))
207 }
208 }
209 }
210}
211
212pub struct X509Builder(X509);
214
215impl X509Builder {
216 #[corresponds(X509_new)]
218 pub fn new() -> Result<X509Builder, ErrorStack> {
219 unsafe {
220 ffi::init();
221 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
222 }
223 }
224
225 #[corresponds(X509_set1_notAfter)]
227 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
228 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
229 }
230
231 #[corresponds(X509_set1_notBefore)]
233 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
234 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
235 }
236
237 #[corresponds(X509_set_version)]
242 #[allow(clippy::useless_conversion)]
243 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
244 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
245 }
246
247 #[corresponds(X509_set_serialNumber)]
249 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
250 unsafe {
251 cvt(ffi::X509_set_serialNumber(
252 self.0.as_ptr(),
253 serial_number.as_ptr(),
254 ))
255 .map(|_| ())
256 }
257 }
258
259 #[corresponds(X509_set_issuer_name)]
261 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
262 unsafe {
263 cvt(ffi::X509_set_issuer_name(
264 self.0.as_ptr(),
265 issuer_name.as_ptr(),
266 ))
267 .map(|_| ())
268 }
269 }
270
271 #[corresponds(X509_set_subject_name)]
290 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
291 unsafe {
292 cvt(ffi::X509_set_subject_name(
293 self.0.as_ptr(),
294 subject_name.as_ptr(),
295 ))
296 .map(|_| ())
297 }
298 }
299
300 #[corresponds(X509_set_pubkey)]
302 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
303 where
304 T: HasPublic,
305 {
306 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
307 }
308
309 #[corresponds(X509V3_set_ctx)]
313 pub fn x509v3_context<'a>(
314 &'a self,
315 issuer: Option<&'a X509Ref>,
316 conf: Option<&'a ConfRef>,
317 ) -> X509v3Context<'a> {
318 unsafe {
319 let mut ctx = mem::zeroed();
320
321 let issuer = match issuer {
322 Some(issuer) => issuer.as_ptr(),
323 None => self.0.as_ptr(),
324 };
325 let subject = self.0.as_ptr();
326 ffi::X509V3_set_ctx(
327 &mut ctx,
328 issuer,
329 subject,
330 ptr::null_mut(),
331 ptr::null_mut(),
332 0,
333 );
334
335 if let Some(conf) = conf {
337 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
338 }
339
340 X509v3Context(ctx, PhantomData)
341 }
342 }
343
344 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
348 self.append_extension2(&extension)
349 }
350
351 #[corresponds(X509_add_ext)]
353 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
354 unsafe {
355 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
356 Ok(())
357 }
358 }
359
360 #[corresponds(X509_sign)]
362 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
363 where
364 T: HasPrivate,
365 {
366 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
367 }
368
369 pub fn build(self) -> X509 {
371 self.0
372 }
373}
374
375foreign_type_and_impl_send_sync! {
376 type CType = ffi::X509;
377 fn drop = ffi::X509_free;
378
379 pub struct X509;
381 pub struct X509Ref;
383}
384
385impl X509Ref {
386 #[corresponds(X509_get_subject_name)]
388 pub fn subject_name(&self) -> &X509NameRef {
389 unsafe {
390 let name = ffi::X509_get_subject_name(self.as_ptr());
391 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
392 }
393 }
394
395 #[corresponds(X509_subject_name_hash)]
397 pub fn subject_name_hash(&self) -> u32 {
398 #[allow(clippy::unnecessary_cast)]
399 unsafe {
400 ffi::X509_subject_name_hash(self.as_ptr()) as u32
401 }
402 }
403
404 #[corresponds(X509_get_issuer_name)]
406 pub fn issuer_name(&self) -> &X509NameRef {
407 unsafe {
408 let name = ffi::X509_get_issuer_name(self.as_ptr());
409 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
410 }
411 }
412
413 #[corresponds(X509_issuer_name_hash)]
415 pub fn issuer_name_hash(&self) -> u32 {
416 #[allow(clippy::unnecessary_cast)]
417 unsafe {
418 ffi::X509_issuer_name_hash(self.as_ptr()) as u32
419 }
420 }
421
422 #[corresponds(X509_get0_extensions)]
424 #[cfg(any(ossl111, libressl272, boringssl, awslc))]
425 pub fn extensions(&self) -> Option<&StackRef<X509Extension>> {
426 unsafe {
427 let extensions = ffi::X509_get0_extensions(self.as_ptr());
428 StackRef::from_const_ptr_opt(extensions)
429 }
430 }
431
432 #[corresponds(X509_get0_ext_by_NID)]
434 #[cfg(any(ossl111, libressl282, boringssl, awslc))]
435 pub fn get_extension_location(&self, nid: Nid, lastpos: Option<i32>) -> Option<i32> {
436 let lastpos = lastpos.unwrap_or(-1);
437 unsafe {
438 let id = ffi::X509_get_ext_by_NID(self.as_ptr(), nid.as_raw(), lastpos as _);
439 if id == -1 {
440 None
441 } else {
442 Some(id)
443 }
444 }
445 }
446
447 #[corresponds(X509_get_ext)]
449 #[cfg(any(ossl111, libressl282, boringssl, awslc))]
450 pub fn get_extension(&self, loc: i32) -> Result<&X509ExtensionRef, ErrorStack> {
451 unsafe {
452 let ext = cvt_p(ffi::X509_get_ext(self.as_ptr(), loc as _))?;
453 Ok(X509ExtensionRef::from_ptr(ext))
454 }
455 }
456
457 #[corresponds(X509_get_key_usage)]
459 #[cfg(any(ossl110, libressl352, boringssl, awslc))]
460 pub fn key_usage(&self) -> Option<u32> {
461 let flags = unsafe { ffi::X509_get_key_usage(self.as_ptr()) };
462 if flags == u32::MAX {
463 None
464 } else {
465 Some(flags)
466 }
467 }
468
469 #[corresponds(X509_get_ext_d2i)]
471 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
472 unsafe {
473 let stack = ffi::X509_get_ext_d2i(
474 self.as_ptr(),
475 ffi::NID_subject_alt_name,
476 ptr::null_mut(),
477 ptr::null_mut(),
478 );
479 Stack::from_ptr_opt(stack as *mut _)
480 }
481 }
482
483 #[corresponds(X509_get_ext_d2i)]
485 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
486 unsafe {
487 let stack = ffi::X509_get_ext_d2i(
488 self.as_ptr(),
489 ffi::NID_crl_distribution_points,
490 ptr::null_mut(),
491 ptr::null_mut(),
492 );
493 Stack::from_ptr_opt(stack as *mut _)
494 }
495 }
496
497 #[corresponds(X509_get_ext_d2i)]
499 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
500 unsafe {
501 let stack = ffi::X509_get_ext_d2i(
502 self.as_ptr(),
503 ffi::NID_issuer_alt_name,
504 ptr::null_mut(),
505 ptr::null_mut(),
506 );
507 Stack::from_ptr_opt(stack as *mut _)
508 }
509 }
510
511 #[corresponds(X509_get_ext_d2i)]
515 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
516 unsafe {
517 let stack = ffi::X509_get_ext_d2i(
518 self.as_ptr(),
519 ffi::NID_info_access,
520 ptr::null_mut(),
521 ptr::null_mut(),
522 );
523 Stack::from_ptr_opt(stack as *mut _)
524 }
525 }
526
527 #[corresponds(X509_get_pathlen)]
529 #[cfg(any(ossl110, boringssl, awslc))]
530 pub fn pathlen(&self) -> Option<u32> {
531 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
532 u32::try_from(v).ok()
533 }
534
535 #[corresponds(X509_get0_subject_key_id)]
537 #[cfg(any(ossl110, boringssl, awslc))]
538 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
539 unsafe {
540 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
541 Asn1OctetStringRef::from_const_ptr_opt(data)
542 }
543 }
544
545 #[corresponds(X509_get0_authority_key_id)]
547 #[cfg(any(ossl110, boringssl, awslc))]
548 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
549 unsafe {
550 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
551 Asn1OctetStringRef::from_const_ptr_opt(data)
552 }
553 }
554
555 #[corresponds(X509_get0_authority_issuer)]
557 #[cfg(any(ossl111d, boringssl))]
558 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
559 unsafe {
560 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
561 StackRef::from_const_ptr_opt(stack)
562 }
563 }
564
565 #[corresponds(X509_get0_authority_serial)]
567 #[cfg(any(ossl111d, boringssl))]
568 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
569 unsafe {
570 let r = ffi::X509_get0_authority_serial(self.as_ptr());
571 Asn1IntegerRef::from_const_ptr_opt(r)
572 }
573 }
574
575 #[corresponds(X509_get_pubkey)]
576 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
577 unsafe {
578 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
579 Ok(PKey::from_ptr(pkey))
580 }
581 }
582
583 #[corresponds(X509_get_X509_PUBKEY)]
584 #[cfg(any(ossl110, boringssl))]
585 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
586 unsafe {
587 let key = cvt_p(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?;
588 Ok(X509PubkeyRef::from_ptr(key))
589 }
590 }
591
592 digest! {
593 digest,
599 ffi::X509_digest
600 }
601
602 digest! {
603 pubkey_digest,
609 ffi::X509_pubkey_digest
610 }
611
612 #[corresponds(X509_getm_notAfter)]
614 pub fn not_after(&self) -> &Asn1TimeRef {
615 unsafe {
616 let date = X509_getm_notAfter(self.as_ptr());
617 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
618 }
619 }
620
621 #[corresponds(X509_getm_notBefore)]
623 pub fn not_before(&self) -> &Asn1TimeRef {
624 unsafe {
625 let date = X509_getm_notBefore(self.as_ptr());
626 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
627 }
628 }
629
630 #[corresponds(X509_get0_signature)]
632 pub fn signature(&self) -> &Asn1BitStringRef {
633 unsafe {
634 let mut signature = ptr::null();
635 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
636 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
637 }
638 }
639
640 #[corresponds(X509_get0_signature)]
642 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
643 unsafe {
644 let mut algor = ptr::null();
645 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
646 X509AlgorithmRef::from_const_ptr_opt(algor)
647 .expect("signature algorithm must not be null")
648 }
649 }
650
651 #[corresponds(X509_get1_ocsp)]
654 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
655 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
656 }
657
658 #[corresponds(X509_check_issued)]
660 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
661 unsafe {
662 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
663 X509VerifyResult::from_raw(r)
664 }
665 }
666
667 #[corresponds(X509_get_version)]
672 #[cfg(any(ossl110, libressl282, boringssl, awslc))]
673 #[allow(clippy::unnecessary_cast)]
674 pub fn version(&self) -> i32 {
675 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
676 }
677
678 #[corresponds(X509_verify)]
685 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
686 where
687 T: HasPublic,
688 {
689 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
690 }
691
692 #[corresponds(X509_get_serialNumber)]
694 pub fn serial_number(&self) -> &Asn1IntegerRef {
695 unsafe {
696 let r = ffi::X509_get_serialNumber(self.as_ptr());
697 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
698 }
699 }
700
701 #[corresponds(X509_alias_get0)]
707 pub fn alias(&self) -> Option<&[u8]> {
708 unsafe {
709 let mut len = 0;
710 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
711 if ptr.is_null() {
712 None
713 } else {
714 Some(util::from_raw_parts(ptr, len as usize))
715 }
716 }
717 }
718
719 to_pem! {
720 #[corresponds(PEM_write_bio_X509)]
724 to_pem,
725 ffi::PEM_write_bio_X509
726 }
727
728 to_der! {
729 #[corresponds(i2d_X509)]
731 to_der,
732 ffi::i2d_X509
733 }
734
735 to_pem! {
736 #[corresponds(X509_print)]
738 to_text,
739 ffi::X509_print
740 }
741}
742
743impl ToOwned for X509Ref {
744 type Owned = X509;
745
746 fn to_owned(&self) -> X509 {
747 unsafe {
748 X509_up_ref(self.as_ptr());
749 X509::from_ptr(self.as_ptr())
750 }
751 }
752}
753
754impl Ord for X509Ref {
755 fn cmp(&self, other: &Self) -> cmp::Ordering {
756 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
759 cmp.cmp(&0)
760 }
761}
762
763impl PartialOrd for X509Ref {
764 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
765 Some(self.cmp(other))
766 }
767}
768
769impl PartialOrd<X509> for X509Ref {
770 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
771 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
772 }
773}
774
775impl PartialEq for X509Ref {
776 fn eq(&self, other: &Self) -> bool {
777 self.cmp(other) == cmp::Ordering::Equal
778 }
779}
780
781impl PartialEq<X509> for X509Ref {
782 fn eq(&self, other: &X509) -> bool {
783 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
784 }
785}
786
787impl Eq for X509Ref {}
788
789impl X509 {
790 pub fn builder() -> Result<X509Builder, ErrorStack> {
792 X509Builder::new()
793 }
794
795 from_pem! {
796 #[corresponds(PEM_read_bio_X509)]
800 from_pem,
801 X509,
802 ffi::PEM_read_bio_X509
803 }
804
805 from_der! {
806 #[corresponds(d2i_X509)]
808 from_der,
809 X509,
810 ffi::d2i_X509
811 }
812
813 #[corresponds(PEM_read_bio_X509)]
815 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
816 unsafe {
817 ffi::init();
818 let bio = MemBioSlice::new(pem)?;
819
820 let mut certs = vec![];
821 loop {
822 let r =
823 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
824 if r.is_null() {
825 let e = ErrorStack::get();
826
827 if let Some(err) = e.errors().last() {
828 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
829 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
830 {
831 break;
832 }
833 }
834
835 return Err(e);
836 } else {
837 certs.push(X509(r));
838 }
839 }
840
841 Ok(certs)
842 }
843 }
844}
845
846impl Clone for X509 {
847 fn clone(&self) -> X509 {
848 X509Ref::to_owned(self)
849 }
850}
851
852impl fmt::Debug for X509 {
853 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
854 let serial = match &self.serial_number().to_bn() {
855 Ok(bn) => match bn.to_hex_str() {
856 Ok(hex) => hex.to_string(),
857 Err(_) => "".to_string(),
858 },
859 Err(_) => "".to_string(),
860 };
861 let mut debug_struct = formatter.debug_struct("X509");
862 debug_struct.field("serial_number", &serial);
863 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
864 debug_struct.field("issuer", &self.issuer_name());
865 debug_struct.field("subject", &self.subject_name());
866 if let Some(subject_alt_names) = &self.subject_alt_names() {
867 debug_struct.field("subject_alt_names", subject_alt_names);
868 }
869 debug_struct.field("not_before", &self.not_before());
870 debug_struct.field("not_after", &self.not_after());
871
872 if let Ok(public_key) = &self.public_key() {
873 debug_struct.field("public_key", public_key);
874 };
875 debug_struct.finish()
878 }
879}
880
881impl AsRef<X509Ref> for X509Ref {
882 fn as_ref(&self) -> &X509Ref {
883 self
884 }
885}
886
887impl Stackable for X509 {
888 type StackType = ffi::stack_st_X509;
889}
890
891impl Ord for X509 {
892 fn cmp(&self, other: &Self) -> cmp::Ordering {
893 X509Ref::cmp(self, other)
894 }
895}
896
897impl PartialOrd for X509 {
898 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
899 Some(self.cmp(other))
900 }
901}
902
903impl PartialOrd<X509Ref> for X509 {
904 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
905 X509Ref::partial_cmp(self, other)
906 }
907}
908
909impl PartialEq for X509 {
910 fn eq(&self, other: &Self) -> bool {
911 X509Ref::eq(self, other)
912 }
913}
914
915impl PartialEq<X509Ref> for X509 {
916 fn eq(&self, other: &X509Ref) -> bool {
917 X509Ref::eq(self, other)
918 }
919}
920
921impl Eq for X509 {}
922
923pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
925
926impl X509v3Context<'_> {
927 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
928 &self.0 as *const _ as *mut _
929 }
930}
931
932foreign_type_and_impl_send_sync! {
933 type CType = ffi::X509_EXTENSION;
934 fn drop = ffi::X509_EXTENSION_free;
935
936 pub struct X509Extension;
938 pub struct X509ExtensionRef;
940}
941
942impl Stackable for X509Extension {
943 type StackType = ffi::stack_st_X509_EXTENSION;
944}
945
946impl X509Extension {
947 #[deprecated(
961 note = "Use x509::extension types or new_from_der instead",
962 since = "0.10.51"
963 )]
964 pub fn new(
965 conf: Option<&ConfRef>,
966 context: Option<&X509v3Context<'_>>,
967 name: &str,
968 value: &str,
969 ) -> Result<X509Extension, ErrorStack> {
970 let name = CString::new(name).unwrap();
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_ptr() as *mut _;
993 let value = value.as_ptr() as *mut _;
994
995 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
996 }
997 }
998
999 #[deprecated(
1013 note = "Use x509::extension types or new_from_der instead",
1014 since = "0.10.51"
1015 )]
1016 pub fn new_nid(
1017 conf: Option<&ConfRef>,
1018 context: Option<&X509v3Context<'_>>,
1019 name: Nid,
1020 value: &str,
1021 ) -> Result<X509Extension, ErrorStack> {
1022 let value = CString::new(value).unwrap();
1023 let mut ctx;
1024 unsafe {
1025 ffi::init();
1026 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1027 let context_ptr = match context {
1028 Some(c) => c.as_ptr(),
1029 None => {
1030 ctx = mem::zeroed();
1031
1032 ffi::X509V3_set_ctx(
1033 &mut ctx,
1034 ptr::null_mut(),
1035 ptr::null_mut(),
1036 ptr::null_mut(),
1037 ptr::null_mut(),
1038 0,
1039 );
1040 &mut ctx
1041 }
1042 };
1043 let name = name.as_raw();
1044 let value = value.as_ptr() as *mut _;
1045
1046 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1047 }
1048 }
1049
1050 pub fn new_from_der(
1060 oid: &Asn1ObjectRef,
1061 critical: bool,
1062 der_contents: &Asn1OctetStringRef,
1063 ) -> Result<X509Extension, ErrorStack> {
1064 unsafe {
1065 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1066 ptr::null_mut(),
1067 oid.as_ptr(),
1068 critical as _,
1069 der_contents.as_ptr(),
1070 ))
1071 .map(X509Extension)
1072 }
1073 }
1074
1075 pub fn new_subject_alt_name(
1077 stack: Stack<GeneralName>,
1078 critical: bool,
1079 ) -> Result<X509Extension, ErrorStack> {
1080 unsafe { Self::new_internal(Nid::SUBJECT_ALT_NAME, critical, stack.as_ptr().cast()) }
1081 }
1082
1083 pub(crate) unsafe fn new_internal(
1084 nid: Nid,
1085 critical: bool,
1086 value: *mut c_void,
1087 ) -> Result<X509Extension, ErrorStack> {
1088 ffi::init();
1089 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1090 }
1091
1092 #[cfg(not(libressl390))]
1098 #[corresponds(X509V3_EXT_add_alias)]
1099 #[deprecated(
1100 note = "Use x509::extension types or new_from_der and then this is not necessary",
1101 since = "0.10.51"
1102 )]
1103 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1104 ffi::init();
1105 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1106 }
1107}
1108
1109impl X509ExtensionRef {
1110 to_der! {
1111 #[corresponds(i2d_X509_EXTENSION)]
1113 to_der,
1114 ffi::i2d_X509_EXTENSION
1115 }
1116}
1117
1118pub struct X509NameBuilder(X509Name);
1120
1121impl X509NameBuilder {
1122 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1124 unsafe {
1125 ffi::init();
1126 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1127 }
1128 }
1129
1130 #[corresponds(X509_NAME_add_entry)]
1132 #[cfg(any(ossl101, libressl350))]
1133 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1134 unsafe {
1135 cvt(ffi::X509_NAME_add_entry(
1136 self.0.as_ptr(),
1137 ne.as_ptr(),
1138 -1,
1139 0,
1140 ))
1141 .map(|_| ())
1142 }
1143 }
1144
1145 #[corresponds(X509_NAME_add_entry_by_txt)]
1147 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1148 unsafe {
1149 let field = CString::new(field).unwrap();
1150 assert!(value.len() <= crate::SLenType::MAX as usize);
1151 cvt(ffi::X509_NAME_add_entry_by_txt(
1152 self.0.as_ptr(),
1153 field.as_ptr() as *mut _,
1154 ffi::MBSTRING_UTF8,
1155 value.as_ptr(),
1156 value.len() as crate::SLenType,
1157 -1,
1158 0,
1159 ))
1160 .map(|_| ())
1161 }
1162 }
1163
1164 #[corresponds(X509_NAME_add_entry_by_txt)]
1166 pub fn append_entry_by_text_with_type(
1167 &mut self,
1168 field: &str,
1169 value: &str,
1170 ty: Asn1Type,
1171 ) -> Result<(), ErrorStack> {
1172 unsafe {
1173 let field = CString::new(field).unwrap();
1174 assert!(value.len() <= crate::SLenType::MAX as usize);
1175 cvt(ffi::X509_NAME_add_entry_by_txt(
1176 self.0.as_ptr(),
1177 field.as_ptr() as *mut _,
1178 ty.as_raw(),
1179 value.as_ptr(),
1180 value.len() as crate::SLenType,
1181 -1,
1182 0,
1183 ))
1184 .map(|_| ())
1185 }
1186 }
1187
1188 #[corresponds(X509_NAME_add_entry_by_NID)]
1190 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1191 unsafe {
1192 assert!(value.len() <= crate::SLenType::MAX as usize);
1193 cvt(ffi::X509_NAME_add_entry_by_NID(
1194 self.0.as_ptr(),
1195 field.as_raw(),
1196 ffi::MBSTRING_UTF8,
1197 value.as_ptr() as *mut _,
1198 value.len() as crate::SLenType,
1199 -1,
1200 0,
1201 ))
1202 .map(|_| ())
1203 }
1204 }
1205
1206 #[corresponds(X509_NAME_add_entry_by_NID)]
1208 pub fn append_entry_by_nid_with_type(
1209 &mut self,
1210 field: Nid,
1211 value: &str,
1212 ty: Asn1Type,
1213 ) -> Result<(), ErrorStack> {
1214 unsafe {
1215 assert!(value.len() <= crate::SLenType::MAX as usize);
1216 cvt(ffi::X509_NAME_add_entry_by_NID(
1217 self.0.as_ptr(),
1218 field.as_raw(),
1219 ty.as_raw(),
1220 value.as_ptr() as *mut _,
1221 value.len() as crate::SLenType,
1222 -1,
1223 0,
1224 ))
1225 .map(|_| ())
1226 }
1227 }
1228
1229 pub fn build(self) -> X509Name {
1231 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1235 }
1236}
1237
1238foreign_type_and_impl_send_sync! {
1239 type CType = ffi::X509_NAME;
1240 fn drop = ffi::X509_NAME_free;
1241
1242 pub struct X509Name;
1244 pub struct X509NameRef;
1246}
1247
1248impl X509Name {
1249 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1251 X509NameBuilder::new()
1252 }
1253
1254 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1258 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1259 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1260 }
1261
1262 from_der! {
1263 from_der,
1269 X509Name,
1270 ffi::d2i_X509_NAME
1271 }
1272}
1273
1274impl Stackable for X509Name {
1275 type StackType = ffi::stack_st_X509_NAME;
1276}
1277
1278impl X509NameRef {
1279 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1281 X509NameEntries {
1282 name: self,
1283 nid: Some(nid),
1284 loc: -1,
1285 }
1286 }
1287
1288 pub fn entries(&self) -> X509NameEntries<'_> {
1290 X509NameEntries {
1291 name: self,
1292 nid: None,
1293 loc: -1,
1294 }
1295 }
1296
1297 #[corresponds(X509_NAME_cmp)]
1304 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1305 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1306 if cfg!(ossl300) && cmp == -2 {
1307 return Err(ErrorStack::get());
1308 }
1309 Ok(cmp.cmp(&0))
1310 }
1311
1312 #[corresponds(X509_NAME_dup)]
1314 #[cfg(any(boringssl, ossl110, libressl270, awslc))]
1315 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1316 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1317 }
1318
1319 to_der! {
1320 to_der,
1326 ffi::i2d_X509_NAME
1327 }
1328
1329 digest! {
1330 digest,
1336 ffi::X509_NAME_digest
1337 }
1338}
1339
1340impl fmt::Debug for X509NameRef {
1341 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1342 formatter.debug_list().entries(self.entries()).finish()
1343 }
1344}
1345
1346pub struct X509NameEntries<'a> {
1348 name: &'a X509NameRef,
1349 nid: Option<Nid>,
1350 loc: c_int,
1351}
1352
1353impl<'a> Iterator for X509NameEntries<'a> {
1354 type Item = &'a X509NameEntryRef;
1355
1356 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1357 unsafe {
1358 match self.nid {
1359 Some(nid) => {
1360 self.loc =
1362 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1363 if self.loc == -1 {
1364 return None;
1365 }
1366 }
1367 None => {
1368 self.loc += 1;
1370 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1371 return None;
1372 }
1373 }
1374 }
1375
1376 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1377
1378 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1379 }
1380 }
1381}
1382
1383foreign_type_and_impl_send_sync! {
1384 type CType = ffi::X509_NAME_ENTRY;
1385 fn drop = ffi::X509_NAME_ENTRY_free;
1386
1387 pub struct X509NameEntry;
1389 pub struct X509NameEntryRef;
1391}
1392
1393impl X509NameEntryRef {
1394 #[corresponds(X509_NAME_ENTRY_get_data)]
1396 pub fn data(&self) -> &Asn1StringRef {
1397 unsafe {
1398 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1399 Asn1StringRef::from_ptr(data)
1400 }
1401 }
1402
1403 #[corresponds(X509_NAME_ENTRY_get_object)]
1406 pub fn object(&self) -> &Asn1ObjectRef {
1407 unsafe {
1408 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1409 Asn1ObjectRef::from_ptr(object)
1410 }
1411 }
1412}
1413
1414impl fmt::Debug for X509NameEntryRef {
1415 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1416 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1417 }
1418}
1419
1420foreign_type_and_impl_send_sync! {
1421 type CType = ffi::X509_PUBKEY;
1422 fn drop = ffi::X509_PUBKEY_free;
1423
1424 pub struct X509Pubkey;
1426 pub struct X509PubkeyRef;
1428}
1429
1430impl X509Pubkey {
1431 from_der! {
1432 from_der,
1438 X509Pubkey,
1439 ffi::d2i_X509_PUBKEY
1440 }
1441
1442 pub fn from_pubkey<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
1448 where
1449 T: HasPublic,
1450 {
1451 let mut p = ptr::null_mut();
1452 unsafe {
1453 cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?;
1454 }
1455 Ok(X509Pubkey(p))
1456 }
1457}
1458
1459impl X509PubkeyRef {
1460 #[corresponds(X509_PUBKEY_dup)]
1462 #[cfg(ossl300)]
1463 pub fn to_owned(&self) -> Result<X509Pubkey, ErrorStack> {
1464 unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) }
1465 }
1466
1467 to_der! {
1468 to_der,
1474 ffi::i2d_X509_PUBKEY
1475 }
1476
1477 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1483 unsafe {
1484 let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
1485 Ok(PKey::from_ptr(key))
1486 }
1487 }
1488
1489 pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> {
1495 unsafe {
1496 let mut pk = ptr::null_mut() as *const c_uchar;
1497 let mut pkt_len: c_int = 0;
1498 cvt(ffi::X509_PUBKEY_get0_param(
1499 ptr::null_mut(),
1500 &mut pk as *mut _,
1501 &mut pkt_len as *mut _,
1502 ptr::null_mut(),
1503 self.as_ptr(),
1504 ))?;
1505
1506 Ok(util::from_raw_parts(pk, pkt_len as usize))
1507 }
1508 }
1509}
1510
1511pub struct X509ReqBuilder(X509Req);
1513
1514impl X509ReqBuilder {
1515 #[corresponds(X509_REQ_new)]
1517 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1518 unsafe {
1519 ffi::init();
1520 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1521 }
1522 }
1523
1524 #[corresponds(X509_REQ_set_version)]
1526 #[allow(clippy::useless_conversion)]
1527 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1528 unsafe {
1529 cvt(ffi::X509_REQ_set_version(
1530 self.0.as_ptr(),
1531 version as c_long,
1532 ))
1533 .map(|_| ())
1534 }
1535 }
1536
1537 #[corresponds(X509_REQ_set_subject_name)]
1539 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1540 unsafe {
1541 cvt(ffi::X509_REQ_set_subject_name(
1542 self.0.as_ptr(),
1543 subject_name.as_ptr(),
1544 ))
1545 .map(|_| ())
1546 }
1547 }
1548
1549 #[corresponds(X509_REQ_set_pubkey)]
1551 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1552 where
1553 T: HasPublic,
1554 {
1555 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1556 }
1557
1558 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1561 unsafe {
1562 let mut ctx = mem::zeroed();
1563
1564 ffi::X509V3_set_ctx(
1565 &mut ctx,
1566 ptr::null_mut(),
1567 ptr::null_mut(),
1568 self.0.as_ptr(),
1569 ptr::null_mut(),
1570 0,
1571 );
1572
1573 if let Some(conf) = conf {
1575 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1576 }
1577
1578 X509v3Context(ctx, PhantomData)
1579 }
1580 }
1581
1582 pub fn add_extensions(
1584 &mut self,
1585 extensions: &StackRef<X509Extension>,
1586 ) -> Result<(), ErrorStack> {
1587 unsafe {
1588 cvt(ffi::X509_REQ_add_extensions(
1589 self.0.as_ptr(),
1590 extensions.as_ptr(),
1591 ))
1592 .map(|_| ())
1593 }
1594 }
1595
1596 #[corresponds(X509_REQ_sign)]
1598 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1599 where
1600 T: HasPrivate,
1601 {
1602 unsafe {
1603 cvt(ffi::X509_REQ_sign(
1604 self.0.as_ptr(),
1605 key.as_ptr(),
1606 hash.as_ptr(),
1607 ))
1608 .map(|_| ())
1609 }
1610 }
1611
1612 pub fn build(self) -> X509Req {
1614 self.0
1615 }
1616}
1617
1618foreign_type_and_impl_send_sync! {
1619 type CType = ffi::X509_REQ;
1620 fn drop = ffi::X509_REQ_free;
1621
1622 pub struct X509Req;
1624 pub struct X509ReqRef;
1626}
1627
1628impl X509Req {
1629 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1631 X509ReqBuilder::new()
1632 }
1633
1634 from_pem! {
1635 from_pem,
1643 X509Req,
1644 ffi::PEM_read_bio_X509_REQ
1645 }
1646
1647 from_der! {
1648 from_der,
1654 X509Req,
1655 ffi::d2i_X509_REQ
1656 }
1657}
1658
1659impl X509ReqRef {
1660 to_pem! {
1661 to_pem,
1669 ffi::PEM_write_bio_X509_REQ
1670 }
1671
1672 to_der! {
1673 to_der,
1679 ffi::i2d_X509_REQ
1680 }
1681
1682 to_pem! {
1683 #[corresponds(X509_Req_print)]
1685 to_text,
1686 ffi::X509_REQ_print
1687 }
1688
1689 digest! {
1690 digest,
1696 ffi::X509_REQ_digest
1697 }
1698
1699 #[corresponds(X509_REQ_get_version)]
1701 #[allow(clippy::unnecessary_cast)]
1702 pub fn version(&self) -> i32 {
1703 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1704 }
1705
1706 #[corresponds(X509_REQ_get_subject_name)]
1708 pub fn subject_name(&self) -> &X509NameRef {
1709 unsafe {
1710 let name = X509_REQ_get_subject_name(self.as_ptr());
1711 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1712 }
1713 }
1714
1715 #[corresponds(X509_REQ_get_pubkey)]
1717 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1718 unsafe {
1719 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1720 Ok(PKey::from_ptr(key))
1721 }
1722 }
1723
1724 #[cfg(ossl110)]
1730 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
1731 unsafe {
1732 let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?;
1733 Ok(X509PubkeyRef::from_ptr(key))
1734 }
1735 }
1736
1737 #[corresponds(X509_REQ_verify)]
1741 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1742 where
1743 T: HasPublic,
1744 {
1745 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1746 }
1747
1748 #[corresponds(X509_REQ_get_extensions)]
1750 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1751 unsafe {
1752 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1753 Ok(Stack::from_ptr(extensions))
1754 }
1755 }
1756}
1757
1758#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1760pub struct CrlReason(c_int);
1761
1762#[allow(missing_docs)] impl CrlReason {
1764 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1765 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1766 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1767 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1768 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1769 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1770 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1771 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1772 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1773 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1774
1775 pub const fn from_raw(value: c_int) -> Self {
1777 CrlReason(value)
1778 }
1779
1780 pub const fn as_raw(&self) -> c_int {
1782 self.0
1783 }
1784}
1785
1786foreign_type_and_impl_send_sync! {
1787 type CType = ffi::X509_REVOKED;
1788 fn drop = ffi::X509_REVOKED_free;
1789
1790 pub struct X509Revoked;
1792 pub struct X509RevokedRef;
1794}
1795
1796impl Stackable for X509Revoked {
1797 type StackType = ffi::stack_st_X509_REVOKED;
1798}
1799
1800impl X509Revoked {
1801 from_der! {
1802 #[corresponds(d2i_X509_REVOKED)]
1804 from_der,
1805 X509Revoked,
1806 ffi::d2i_X509_REVOKED
1807 }
1808}
1809
1810impl X509RevokedRef {
1811 to_der! {
1812 #[corresponds(d2i_X509_REVOKED)]
1814 to_der,
1815 ffi::i2d_X509_REVOKED
1816 }
1817
1818 #[corresponds(X509_NAME_dup)]
1820 #[cfg(any(boringssl, ossl110, libressl270, awslc))]
1821 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1822 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1823 }
1824
1825 #[corresponds(X509_REVOKED_get0_revocationDate)]
1827 pub fn revocation_date(&self) -> &Asn1TimeRef {
1828 unsafe {
1829 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1830 assert!(!r.is_null());
1831 Asn1TimeRef::from_ptr(r as *mut _)
1832 }
1833 }
1834
1835 #[corresponds(X509_REVOKED_get0_serialNumber)]
1837 pub fn serial_number(&self) -> &Asn1IntegerRef {
1838 unsafe {
1839 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1840 assert!(!r.is_null());
1841 Asn1IntegerRef::from_ptr(r as *mut _)
1842 }
1843 }
1844
1845 #[corresponds(X509_REVOKED_get_ext_d2i)]
1849 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1850 let mut critical = -1;
1851 let out = unsafe {
1852 let ext = ffi::X509_REVOKED_get_ext_d2i(
1854 self.as_ptr(),
1855 T::NID.as_raw(),
1856 &mut critical as *mut _,
1857 ptr::null_mut(),
1858 );
1859 T::Output::from_ptr_opt(ext as *mut _)
1862 };
1863 match (critical, out) {
1864 (0, Some(out)) => Ok(Some((false, out))),
1865 (1, Some(out)) => Ok(Some((true, out))),
1866 (-1 | -2, _) => Ok(None),
1868 (0 | 1, None) => Err(ErrorStack::get()),
1871 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1872 }
1873 }
1874}
1875
1876pub enum ReasonCode {}
1879
1880unsafe impl ExtensionType for ReasonCode {
1883 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1884
1885 type Output = Asn1Enumerated;
1886}
1887
1888pub enum CertificateIssuer {}
1891
1892unsafe impl ExtensionType for CertificateIssuer {
1895 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1896
1897 type Output = Stack<GeneralName>;
1898}
1899
1900pub enum AuthorityInformationAccess {}
1902
1903unsafe impl ExtensionType for AuthorityInformationAccess {
1906 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1907
1908 type Output = Stack<AccessDescription>;
1909}
1910
1911foreign_type_and_impl_send_sync! {
1912 type CType = ffi::X509_CRL;
1913 fn drop = ffi::X509_CRL_free;
1914
1915 pub struct X509Crl;
1917 pub struct X509CrlRef;
1919}
1920
1921pub enum CrlStatus<'a> {
1927 NotRevoked,
1929 Revoked(&'a X509RevokedRef),
1931 RemoveFromCrl(&'a X509RevokedRef),
1936}
1937
1938impl<'a> CrlStatus<'a> {
1939 unsafe fn from_ffi_status(
1944 status: c_int,
1945 revoked_entry: *mut ffi::X509_REVOKED,
1946 ) -> CrlStatus<'a> {
1947 match status {
1948 0 => CrlStatus::NotRevoked,
1949 1 => {
1950 assert!(!revoked_entry.is_null());
1951 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1952 }
1953 2 => {
1954 assert!(!revoked_entry.is_null());
1955 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1956 }
1957 _ => unreachable!(
1958 "{}",
1959 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1960 ),
1961 }
1962 }
1963}
1964
1965impl X509Crl {
1966 from_pem! {
1967 #[corresponds(PEM_read_bio_X509_CRL)]
1971 from_pem,
1972 X509Crl,
1973 ffi::PEM_read_bio_X509_CRL
1974 }
1975
1976 from_der! {
1977 #[corresponds(d2i_X509_CRL)]
1979 from_der,
1980 X509Crl,
1981 ffi::d2i_X509_CRL
1982 }
1983}
1984
1985impl X509CrlRef {
1986 to_pem! {
1987 #[corresponds(PEM_write_bio_X509_CRL)]
1991 to_pem,
1992 ffi::PEM_write_bio_X509_CRL
1993 }
1994
1995 to_der! {
1996 #[corresponds(i2d_X509_CRL)]
1998 to_der,
1999 ffi::i2d_X509_CRL
2000 }
2001
2002 digest! {
2003 digest,
2009 ffi::X509_CRL_digest
2010 }
2011
2012 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2014 unsafe {
2015 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2016 if revoked.is_null() {
2017 None
2018 } else {
2019 Some(StackRef::from_ptr(revoked))
2020 }
2021 }
2022 }
2023
2024 #[corresponds(X509_CRL_get0_lastUpdate)]
2026 pub fn last_update(&self) -> &Asn1TimeRef {
2027 unsafe {
2028 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2029 assert!(!date.is_null());
2030 Asn1TimeRef::from_ptr(date as *mut _)
2031 }
2032 }
2033
2034 #[corresponds(X509_CRL_get0_nextUpdate)]
2038 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2039 unsafe {
2040 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2041 Asn1TimeRef::from_const_ptr_opt(date)
2042 }
2043 }
2044
2045 #[corresponds(X509_CRL_get0_by_serial)]
2047 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2048 unsafe {
2049 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2050 let status =
2051 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2052 CrlStatus::from_ffi_status(status, ret)
2053 }
2054 }
2055
2056 #[corresponds(X509_CRL_get0_by_cert)]
2058 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2059 unsafe {
2060 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2061 let status =
2062 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2063 CrlStatus::from_ffi_status(status, ret)
2064 }
2065 }
2066
2067 #[corresponds(X509_CRL_get_issuer)]
2069 pub fn issuer_name(&self) -> &X509NameRef {
2070 unsafe {
2071 let name = X509_CRL_get_issuer(self.as_ptr());
2072 assert!(!name.is_null());
2073 X509NameRef::from_ptr(name)
2074 }
2075 }
2076
2077 #[corresponds(X509_CRL_verify)]
2084 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2085 where
2086 T: HasPublic,
2087 {
2088 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2089 }
2090
2091 #[corresponds(X509_CRL_get_ext_d2i)]
2095 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2096 let mut critical = -1;
2097 let out = unsafe {
2098 let ext = ffi::X509_CRL_get_ext_d2i(
2100 self.as_ptr(),
2101 T::NID.as_raw(),
2102 &mut critical as *mut _,
2103 ptr::null_mut(),
2104 );
2105 T::Output::from_ptr_opt(ext as *mut _)
2108 };
2109 match (critical, out) {
2110 (0, Some(out)) => Ok(Some((false, out))),
2111 (1, Some(out)) => Ok(Some((true, out))),
2112 (-1 | -2, _) => Ok(None),
2114 (0 | 1, None) => Err(ErrorStack::get()),
2117 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2118 }
2119 }
2120}
2121
2122#[derive(Copy, Clone, PartialEq, Eq)]
2124pub struct X509VerifyResult(c_int);
2125
2126impl fmt::Debug for X509VerifyResult {
2127 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2128 fmt.debug_struct("X509VerifyResult")
2129 .field("code", &self.0)
2130 .field("error", &self.error_string())
2131 .finish()
2132 }
2133}
2134
2135impl fmt::Display for X509VerifyResult {
2136 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2137 fmt.write_str(self.error_string())
2138 }
2139}
2140
2141impl Error for X509VerifyResult {}
2142
2143impl X509VerifyResult {
2144 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2151 X509VerifyResult(err)
2152 }
2153
2154 #[allow(clippy::trivially_copy_pass_by_ref)]
2156 pub fn as_raw(&self) -> c_int {
2157 self.0
2158 }
2159
2160 #[corresponds(X509_verify_cert_error_string)]
2162 #[allow(clippy::trivially_copy_pass_by_ref)]
2163 pub fn error_string(&self) -> &'static str {
2164 ffi::init();
2165
2166 unsafe {
2167 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2168 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2169 }
2170 }
2171
2172 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2174 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2176 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2177}
2178
2179foreign_type_and_impl_send_sync! {
2180 type CType = ffi::GENERAL_NAME;
2181 fn drop = ffi::GENERAL_NAME_free;
2182
2183 pub struct GeneralName;
2185 pub struct GeneralNameRef;
2187}
2188
2189impl GeneralName {
2190 unsafe fn new(
2191 type_: c_int,
2192 asn1_type: Asn1Type,
2193 value: &[u8],
2194 ) -> Result<GeneralName, ErrorStack> {
2195 ffi::init();
2196 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2197 (*gn.as_ptr()).type_ = type_;
2198 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2199 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2200
2201 #[cfg(any(boringssl, awslc))]
2202 {
2203 (*gn.as_ptr()).d.ptr = s.cast();
2204 }
2205 #[cfg(not(any(boringssl, awslc)))]
2206 {
2207 (*gn.as_ptr()).d = s.cast();
2208 }
2209
2210 Ok(gn)
2211 }
2212
2213 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2214 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2215 }
2216
2217 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2218 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2219 }
2220
2221 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2222 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2223 }
2224
2225 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2226 match ip {
2227 IpAddr::V4(addr) => unsafe {
2228 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2229 },
2230 IpAddr::V6(addr) => unsafe {
2231 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2232 },
2233 }
2234 }
2235
2236 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2237 unsafe {
2238 ffi::init();
2239 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2240 (*gn).type_ = ffi::GEN_RID;
2241
2242 #[cfg(any(boringssl, awslc))]
2243 {
2244 (*gn).d.registeredID = oid.as_ptr();
2245 }
2246 #[cfg(not(any(boringssl, awslc)))]
2247 {
2248 (*gn).d = oid.as_ptr().cast();
2249 }
2250
2251 mem::forget(oid);
2252
2253 Ok(GeneralName::from_ptr(gn))
2254 }
2255 }
2256
2257 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2258 unsafe {
2259 ffi::init();
2260
2261 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2262 ptr::null_mut(),
2263 &mut value.as_ptr().cast(),
2264 value.len().try_into().unwrap(),
2265 ))?;
2266
2267 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2268 (*gn).type_ = ffi::GEN_OTHERNAME;
2269
2270 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2271 gn,
2272 oid.as_ptr().cast(),
2273 typ,
2274 )) {
2275 ffi::GENERAL_NAME_free(gn);
2276 return Err(e);
2277 }
2278
2279 mem::forget(oid);
2280
2281 Ok(GeneralName::from_ptr(gn))
2282 }
2283 }
2284}
2285
2286impl GeneralNameRef {
2287 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2288 unsafe {
2289 if (*self.as_ptr()).type_ != ffi_type {
2290 return None;
2291 }
2292
2293 #[cfg(any(boringssl, awslc))]
2294 let d = (*self.as_ptr()).d.ptr;
2295 #[cfg(not(any(boringssl, awslc)))]
2296 let d = (*self.as_ptr()).d;
2297
2298 let ptr = ASN1_STRING_get0_data(d as *mut _);
2299 let len = ffi::ASN1_STRING_length(d as *mut _);
2300
2301 #[allow(clippy::unnecessary_cast)]
2302 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2303 str::from_utf8(slice).ok()
2307 }
2308 }
2309
2310 pub fn email(&self) -> Option<&str> {
2312 self.ia5_string(ffi::GEN_EMAIL)
2313 }
2314
2315 pub fn directory_name(&self) -> Option<&X509NameRef> {
2317 unsafe {
2318 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2319 return None;
2320 }
2321
2322 #[cfg(any(boringssl, awslc))]
2323 let d = (*self.as_ptr()).d.ptr;
2324 #[cfg(not(any(boringssl, awslc)))]
2325 let d = (*self.as_ptr()).d;
2326
2327 Some(X509NameRef::from_const_ptr(d as *const _))
2328 }
2329 }
2330
2331 pub fn dnsname(&self) -> Option<&str> {
2333 self.ia5_string(ffi::GEN_DNS)
2334 }
2335
2336 pub fn uri(&self) -> Option<&str> {
2338 self.ia5_string(ffi::GEN_URI)
2339 }
2340
2341 pub fn ipaddress(&self) -> Option<&[u8]> {
2343 unsafe {
2344 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2345 return None;
2346 }
2347 #[cfg(any(boringssl, awslc))]
2348 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2349 #[cfg(not(any(boringssl, awslc)))]
2350 let d = (*self.as_ptr()).d;
2351
2352 let ptr = ASN1_STRING_get0_data(d as *mut _);
2353 let len = ffi::ASN1_STRING_length(d as *mut _);
2354
2355 #[allow(clippy::unnecessary_cast)]
2356 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2357 }
2358 }
2359}
2360
2361impl fmt::Debug for GeneralNameRef {
2362 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2363 if let Some(email) = self.email() {
2364 formatter.write_str(email)
2365 } else if let Some(dnsname) = self.dnsname() {
2366 formatter.write_str(dnsname)
2367 } else if let Some(uri) = self.uri() {
2368 formatter.write_str(uri)
2369 } else if let Some(ipaddress) = self.ipaddress() {
2370 let address = <[u8; 16]>::try_from(ipaddress)
2371 .map(IpAddr::from)
2372 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2373 match address {
2374 Ok(a) => fmt::Debug::fmt(&a, formatter),
2375 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2376 }
2377 } else {
2378 formatter.write_str("(empty)")
2379 }
2380 }
2381}
2382
2383impl Stackable for GeneralName {
2384 type StackType = ffi::stack_st_GENERAL_NAME;
2385}
2386
2387foreign_type_and_impl_send_sync! {
2388 type CType = ffi::DIST_POINT;
2389 fn drop = ffi::DIST_POINT_free;
2390
2391 pub struct DistPoint;
2393 pub struct DistPointRef;
2395}
2396
2397impl DistPointRef {
2398 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2400 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2401 }
2402}
2403
2404foreign_type_and_impl_send_sync! {
2405 type CType = ffi::DIST_POINT_NAME;
2406 fn drop = ffi::DIST_POINT_NAME_free;
2407
2408 pub struct DistPointName;
2410 pub struct DistPointNameRef;
2412}
2413
2414impl DistPointNameRef {
2415 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2417 unsafe {
2418 if (*self.as_ptr()).type_ != 0 {
2419 return None;
2420 }
2421 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2422 }
2423 }
2424}
2425
2426impl Stackable for DistPoint {
2427 type StackType = ffi::stack_st_DIST_POINT;
2428}
2429
2430foreign_type_and_impl_send_sync! {
2431 type CType = ffi::ACCESS_DESCRIPTION;
2432 fn drop = ffi::ACCESS_DESCRIPTION_free;
2433
2434 pub struct AccessDescription;
2436 pub struct AccessDescriptionRef;
2438}
2439
2440impl AccessDescriptionRef {
2441 pub fn method(&self) -> &Asn1ObjectRef {
2443 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2444 }
2445
2446 pub fn location(&self) -> &GeneralNameRef {
2448 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2449 }
2450}
2451
2452impl Stackable for AccessDescription {
2453 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2454}
2455
2456foreign_type_and_impl_send_sync! {
2457 type CType = ffi::X509_ALGOR;
2458 fn drop = ffi::X509_ALGOR_free;
2459
2460 pub struct X509Algorithm;
2462 pub struct X509AlgorithmRef;
2464}
2465
2466impl X509AlgorithmRef {
2467 pub fn object(&self) -> &Asn1ObjectRef {
2469 unsafe {
2470 let mut oid = ptr::null();
2471 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2472 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2473 }
2474 }
2475}
2476
2477foreign_type_and_impl_send_sync! {
2478 type CType = ffi::X509_OBJECT;
2479 fn drop = X509_OBJECT_free;
2480
2481 pub struct X509Object;
2483 pub struct X509ObjectRef;
2485}
2486
2487impl X509ObjectRef {
2488 pub fn x509(&self) -> Option<&X509Ref> {
2489 unsafe {
2490 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2491 X509Ref::from_const_ptr_opt(ptr)
2492 }
2493 }
2494}
2495
2496impl Stackable for X509Object {
2497 type StackType = ffi::stack_st_X509_OBJECT;
2498}
2499
2500cfg_if! {
2501 if #[cfg(any(boringssl, ossl110, libressl273, awslc))] {
2502 use ffi::{X509_getm_notAfter, X509_getm_notBefore, X509_up_ref, X509_get0_signature};
2503 } else {
2504 #[allow(bad_style)]
2505 unsafe fn X509_getm_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
2506 (*(*(*x).cert_info).validity).notAfter
2507 }
2508
2509 #[allow(bad_style)]
2510 unsafe fn X509_getm_notBefore(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
2511 (*(*(*x).cert_info).validity).notBefore
2512 }
2513
2514 #[allow(bad_style)]
2515 unsafe fn X509_up_ref(x: *mut ffi::X509) {
2516 ffi::CRYPTO_add_lock(
2517 &mut (*x).references,
2518 1,
2519 ffi::CRYPTO_LOCK_X509,
2520 "mod.rs\0".as_ptr() as *const _,
2521 line!() as c_int,
2522 );
2523 }
2524
2525 #[allow(bad_style)]
2526 unsafe fn X509_get0_signature(
2527 psig: *mut *const ffi::ASN1_BIT_STRING,
2528 palg: *mut *const ffi::X509_ALGOR,
2529 x: *const ffi::X509,
2530 ) {
2531 if !psig.is_null() {
2532 *psig = (*x).signature;
2533 }
2534 if !palg.is_null() {
2535 *palg = (*x).sig_alg;
2536 }
2537 }
2538 }
2539}
2540
2541cfg_if! {
2542 if #[cfg(any(boringssl, ossl110, libressl350, awslc))] {
2543 use ffi::{
2544 X509_ALGOR_get0, ASN1_STRING_get0_data, X509_STORE_CTX_get0_chain, X509_set1_notAfter,
2545 X509_set1_notBefore, X509_REQ_get_version, X509_REQ_get_subject_name,
2546 };
2547 } else {
2548 use ffi::{
2549 ASN1_STRING_data as ASN1_STRING_get0_data,
2550 X509_STORE_CTX_get_chain as X509_STORE_CTX_get0_chain,
2551 X509_set_notAfter as X509_set1_notAfter,
2552 X509_set_notBefore as X509_set1_notBefore,
2553 };
2554
2555 #[allow(bad_style)]
2556 unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long {
2557 ffi::ASN1_INTEGER_get((*(*x).req_info).version)
2558 }
2559
2560 #[allow(bad_style)]
2561 unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME {
2562 (*(*x).req_info).subject
2563 }
2564
2565 #[allow(bad_style)]
2566 unsafe fn X509_ALGOR_get0(
2567 paobj: *mut *const ffi::ASN1_OBJECT,
2568 pptype: *mut c_int,
2569 pval: *mut *mut ::libc::c_void,
2570 alg: *const ffi::X509_ALGOR,
2571 ) {
2572 if !paobj.is_null() {
2573 *paobj = (*alg).algorithm;
2574 }
2575 assert!(pptype.is_null());
2576 assert!(pval.is_null());
2577 }
2578 }
2579}
2580
2581cfg_if! {
2582 if #[cfg(any(ossl110, boringssl, libressl270, awslc))] {
2583 use ffi::X509_OBJECT_get0_X509;
2584 } else {
2585 #[allow(bad_style)]
2586 unsafe fn X509_OBJECT_get0_X509(x: *mut ffi::X509_OBJECT) -> *mut ffi::X509 {
2587 if (*x).type_ == ffi::X509_LU_X509 {
2588 (*x).data.x509
2589 } else {
2590 ptr::null_mut()
2591 }
2592 }
2593 }
2594}
2595
2596cfg_if! {
2597 if #[cfg(any(ossl110, libressl350, boringssl, awslc))] {
2598 use ffi::X509_OBJECT_free;
2599 } else {
2600 #[allow(bad_style)]
2601 unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
2602 ffi::X509_OBJECT_free_contents(x);
2603 ffi::CRYPTO_free(x as *mut libc::c_void);
2604 }
2605 }
2606}
2607
2608cfg_if! {
2609 if #[cfg(any(ossl110, libressl350, boringssl, awslc))] {
2610 use ffi::{
2611 X509_CRL_get_issuer, X509_CRL_get0_nextUpdate, X509_CRL_get0_lastUpdate,
2612 X509_CRL_get_REVOKED,
2613 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2614 };
2615 } else {
2616 #[allow(bad_style)]
2617 unsafe fn X509_CRL_get0_lastUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
2618 (*(*x).crl).lastUpdate
2619 }
2620 #[allow(bad_style)]
2621 unsafe fn X509_CRL_get0_nextUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
2622 (*(*x).crl).nextUpdate
2623 }
2624 #[allow(bad_style)]
2625 unsafe fn X509_CRL_get_issuer(x: *const ffi::X509_CRL) -> *mut ffi::X509_NAME {
2626 (*(*x).crl).issuer
2627 }
2628 #[allow(bad_style)]
2629 unsafe fn X509_CRL_get_REVOKED(x: *const ffi::X509_CRL) -> *mut ffi::stack_st_X509_REVOKED {
2630 (*(*x).crl).revoked
2631 }
2632 #[allow(bad_style)]
2633 unsafe fn X509_REVOKED_get0_serialNumber(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_INTEGER {
2634 (*x).serialNumber
2635 }
2636 #[allow(bad_style)]
2637 unsafe fn X509_REVOKED_get0_revocationDate(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_TIME {
2638 (*x).revocationDate
2639 }
2640 }
2641}
2642
2643#[derive(Copy, Clone, PartialEq, Eq)]
2644pub struct X509PurposeId(c_int);
2645
2646impl X509PurposeId {
2647 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2648 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2649 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2650 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2651 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2652 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2653 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2654 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2655 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2656 #[cfg(ossl320)]
2657 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2658
2659 pub fn from_raw(id: c_int) -> Self {
2661 X509PurposeId(id)
2662 }
2663
2664 pub fn as_raw(&self) -> c_int {
2666 self.0
2667 }
2668}
2669
2670pub struct X509PurposeRef(Opaque);
2672
2673impl ForeignTypeRef for X509PurposeRef {
2675 type CType = ffi::X509_PURPOSE;
2676}
2677
2678impl X509PurposeRef {
2679 #[allow(clippy::unnecessary_cast)]
2693 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2694 unsafe {
2695 let sname = CString::new(sname).unwrap();
2696 cfg_if! {
2697 if #[cfg(any(ossl110, libressl280, boringssl, awslc))] {
2698 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2699 } else {
2700 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *mut _))?;
2701 }
2702 }
2703 Ok(purpose)
2704 }
2705 }
2706 #[corresponds(X509_PURPOSE_get0)]
2709 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2710 unsafe {
2711 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2712 Ok(X509PurposeRef::from_const_ptr(ptr))
2713 }
2714 }
2715
2716 pub fn purpose(&self) -> X509PurposeId {
2727 unsafe {
2728 cfg_if! {
2729 if #[cfg(any(ossl110, libressl280, boringssl, awslc))] {
2730 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2731 } else {
2732 let x509_purpose = self.as_ptr() as *mut ffi::X509_PURPOSE;
2733 }
2734 }
2735 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2736 }
2737 }
2738}