1use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
11use libc::{c_int, c_long, c_uchar, c_uint, c_void};
12use std::cmp::{self, Ordering};
13use std::convert::{TryFrom, TryInto};
14use std::error::Error;
15use std::ffi::{CStr, CString};
16use std::fmt;
17use std::marker::PhantomData;
18use std::mem;
19use std::net::IpAddr;
20use std::path::Path;
21use std::ptr;
22use std::str;
23
24use crate::asn1::{
25 Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
26 Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
27};
28use crate::bio::MemBioSlice;
29use crate::conf::ConfRef;
30use crate::error::ErrorStack;
31use crate::ex_data::Index;
32use crate::hash::{DigestBytes, MessageDigest};
33use crate::nid::Nid;
34use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
35use crate::ssl::SslRef;
36use crate::stack::{Stack, StackRef, Stackable};
37use crate::string::OpensslString;
38use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
39use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
40use openssl_macros::corresponds;
41
42pub mod verify;
43
44pub mod extension;
45pub mod store;
46
47#[cfg(test)]
48mod tests;
49
50pub unsafe trait ExtensionType {
56 const NID: Nid;
57 type Output: ForeignType;
58}
59
60foreign_type_and_impl_send_sync! {
61 type CType = ffi::X509_STORE_CTX;
62 fn drop = ffi::X509_STORE_CTX_free;
63
64 pub struct X509StoreContext;
66
67 pub struct X509StoreContextRef;
69}
70
71impl X509StoreContext {
72 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
75 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
76 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
77 }
78
79 #[corresponds(X509_STORE_CTX_new)]
81 pub fn new() -> Result<X509StoreContext, ErrorStack> {
82 unsafe {
83 ffi::init();
84 cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
85 }
86 }
87}
88
89impl X509StoreContextRef {
90 #[corresponds(X509_STORE_CTX_get_ex_data)]
92 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
93 unsafe {
94 let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
95 if data.is_null() {
96 None
97 } else {
98 Some(&*(data as *const T))
99 }
100 }
101 }
102
103 #[corresponds(X509_STORE_CTX_get_error)]
105 pub fn error(&self) -> X509VerifyResult {
106 unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
107 }
108
109 pub fn init<F, T>(
125 &mut self,
126 trust: &store::X509StoreRef,
127 cert: &X509Ref,
128 cert_chain: &StackRef<X509>,
129 with_context: F,
130 ) -> Result<T, ErrorStack>
131 where
132 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
133 {
134 struct Cleanup<'a>(&'a mut X509StoreContextRef);
135
136 impl Drop for Cleanup<'_> {
137 fn drop(&mut self) {
138 unsafe {
139 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
140 }
141 }
142 }
143
144 unsafe {
145 cvt(ffi::X509_STORE_CTX_init(
146 self.as_ptr(),
147 trust.as_ptr(),
148 cert.as_ptr(),
149 cert_chain.as_ptr(),
150 ))?;
151
152 let cleanup = Cleanup(self);
153 with_context(cleanup.0)
154 }
155 }
156
157 #[corresponds(X509_verify_cert)]
164 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
165 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
166 }
167
168 #[corresponds(X509_STORE_CTX_set_error)]
170 pub fn set_error(&mut self, result: X509VerifyResult) {
171 unsafe {
172 ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
173 }
174 }
175
176 #[corresponds(X509_STORE_CTX_get_current_cert)]
179 pub fn current_cert(&self) -> Option<&X509Ref> {
180 unsafe {
181 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
182 X509Ref::from_const_ptr_opt(ptr)
183 }
184 }
185
186 #[corresponds(X509_STORE_CTX_get_error_depth)]
191 pub fn error_depth(&self) -> u32 {
192 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
193 }
194
195 #[corresponds(X509_STORE_CTX_get0_chain)]
197 pub fn chain(&self) -> Option<&StackRef<X509>> {
198 unsafe {
199 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
200
201 if chain.is_null() {
202 None
203 } else {
204 Some(StackRef::from_ptr(chain))
205 }
206 }
207 }
208}
209
210pub struct X509Builder(X509);
212
213impl X509Builder {
214 #[corresponds(X509_new)]
216 pub fn new() -> Result<X509Builder, ErrorStack> {
217 unsafe {
218 ffi::init();
219 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
220 }
221 }
222
223 #[corresponds(X509_set1_notAfter)]
225 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
226 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
227 }
228
229 #[corresponds(X509_set1_notBefore)]
231 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
232 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
233 }
234
235 #[corresponds(X509_set_version)]
240 #[allow(clippy::useless_conversion)]
241 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
242 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
243 }
244
245 #[corresponds(X509_set_serialNumber)]
247 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
248 unsafe {
249 cvt(ffi::X509_set_serialNumber(
250 self.0.as_ptr(),
251 serial_number.as_ptr(),
252 ))
253 .map(|_| ())
254 }
255 }
256
257 #[corresponds(X509_set_issuer_name)]
259 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
260 unsafe {
261 cvt(ffi::X509_set_issuer_name(
262 self.0.as_ptr(),
263 issuer_name.as_ptr(),
264 ))
265 .map(|_| ())
266 }
267 }
268
269 #[corresponds(X509_set_subject_name)]
288 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
289 unsafe {
290 cvt(ffi::X509_set_subject_name(
291 self.0.as_ptr(),
292 subject_name.as_ptr(),
293 ))
294 .map(|_| ())
295 }
296 }
297
298 #[corresponds(X509_set_pubkey)]
300 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
301 where
302 T: HasPublic,
303 {
304 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
305 }
306
307 #[corresponds(X509V3_set_ctx)]
311 pub fn x509v3_context<'a>(
312 &'a self,
313 issuer: Option<&'a X509Ref>,
314 conf: Option<&'a ConfRef>,
315 ) -> X509v3Context<'a> {
316 unsafe {
317 let mut ctx = mem::zeroed();
318
319 let issuer = match issuer {
320 Some(issuer) => issuer.as_ptr(),
321 None => self.0.as_ptr(),
322 };
323 let subject = self.0.as_ptr();
324 ffi::X509V3_set_ctx(
325 &mut ctx,
326 issuer,
327 subject,
328 ptr::null_mut(),
329 ptr::null_mut(),
330 0,
331 );
332
333 if let Some(conf) = conf {
335 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
336 }
337
338 X509v3Context(ctx, PhantomData)
339 }
340 }
341
342 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
346 self.append_extension2(&extension)
347 }
348
349 #[corresponds(X509_add_ext)]
351 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
352 unsafe {
353 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
354 Ok(())
355 }
356 }
357
358 #[corresponds(X509_sign)]
360 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
361 where
362 T: HasPrivate,
363 {
364 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
365 }
366
367 pub fn build(self) -> X509 {
369 self.0
370 }
371}
372
373foreign_type_and_impl_send_sync! {
374 type CType = ffi::X509;
375 fn drop = ffi::X509_free;
376
377 pub struct X509;
379 pub struct X509Ref;
381}
382
383impl X509Ref {
384 #[corresponds(X509_get_subject_name)]
386 pub fn subject_name(&self) -> &X509NameRef {
387 unsafe {
388 let name = ffi::X509_get_subject_name(self.as_ptr());
389 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
390 }
391 }
392
393 #[corresponds(X509_subject_name_hash)]
395 pub fn subject_name_hash(&self) -> u32 {
396 #[allow(clippy::unnecessary_cast)]
397 unsafe {
398 ffi::X509_subject_name_hash(self.as_ptr()) as u32
399 }
400 }
401
402 #[corresponds(X509_get_issuer_name)]
404 pub fn issuer_name(&self) -> &X509NameRef {
405 unsafe {
406 let name = ffi::X509_get_issuer_name(self.as_ptr());
407 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
408 }
409 }
410
411 #[corresponds(X509_issuer_name_hash)]
413 pub fn issuer_name_hash(&self) -> u32 {
414 #[allow(clippy::unnecessary_cast)]
415 unsafe {
416 ffi::X509_issuer_name_hash(self.as_ptr()) as u32
417 }
418 }
419
420 #[corresponds(X509_get0_extensions)]
422 #[cfg(any(ossl111, libressl, boringssl, awslc))]
423 pub fn extensions(&self) -> Option<&StackRef<X509Extension>> {
424 unsafe {
425 let extensions = ffi::X509_get0_extensions(self.as_ptr());
426 StackRef::from_const_ptr_opt(extensions)
427 }
428 }
429
430 #[corresponds(X509_get0_ext_by_NID)]
432 #[cfg(any(ossl111, libressl, boringssl, awslc))]
433 pub fn get_extension_location(&self, nid: Nid, lastpos: Option<i32>) -> Option<i32> {
434 let lastpos = lastpos.unwrap_or(-1);
435 unsafe {
436 let id = ffi::X509_get_ext_by_NID(self.as_ptr(), nid.as_raw(), lastpos as _);
437 if id == -1 {
438 None
439 } else {
440 Some(id)
441 }
442 }
443 }
444
445 #[corresponds(X509_get_ext)]
447 #[cfg(any(all(ossl111, not(ossl400)), libressl, boringssl, awslc))]
448 pub fn get_extension(&self, loc: i32) -> Result<&X509ExtensionRef, ErrorStack> {
449 unsafe {
450 let ext = cvt_p(ffi::X509_get_ext(self.as_ptr(), loc as _))?;
451 Ok(X509ExtensionRef::from_ptr(ext))
452 }
453 }
454
455 #[corresponds(X509_get_ext)]
457 #[cfg(ossl400)]
458 pub fn get_extension(&self, loc: i32) -> Result<&X509ExtensionRef, ErrorStack> {
459 unsafe {
460 let ext = cvt_p_const(ffi::X509_get_ext(self.as_ptr(), loc as _))?;
461 Ok(X509ExtensionRef::from_ptr(ext as *mut _))
462 }
463 }
464
465 #[corresponds(X509_get_key_usage)]
467 #[cfg(any(ossl110, libressl, boringssl, awslc))]
468 pub fn key_usage(&self) -> Option<u32> {
469 let flags = unsafe { ffi::X509_get_key_usage(self.as_ptr()) };
470 if flags == u32::MAX {
471 None
472 } else {
473 Some(flags)
474 }
475 }
476
477 #[corresponds(X509_get_ext_d2i)]
479 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
480 unsafe {
481 let stack = ffi::X509_get_ext_d2i(
482 self.as_ptr(),
483 ffi::NID_subject_alt_name,
484 ptr::null_mut(),
485 ptr::null_mut(),
486 );
487 Stack::from_ptr_opt(stack as *mut _)
488 }
489 }
490
491 #[corresponds(X509_get_ext_d2i)]
493 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
494 unsafe {
495 let stack = ffi::X509_get_ext_d2i(
496 self.as_ptr(),
497 ffi::NID_crl_distribution_points,
498 ptr::null_mut(),
499 ptr::null_mut(),
500 );
501 Stack::from_ptr_opt(stack as *mut _)
502 }
503 }
504
505 #[corresponds(X509_get_ext_d2i)]
507 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
508 unsafe {
509 let stack = ffi::X509_get_ext_d2i(
510 self.as_ptr(),
511 ffi::NID_issuer_alt_name,
512 ptr::null_mut(),
513 ptr::null_mut(),
514 );
515 Stack::from_ptr_opt(stack as *mut _)
516 }
517 }
518
519 #[corresponds(X509_get_ext_d2i)]
523 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
524 unsafe {
525 let stack = ffi::X509_get_ext_d2i(
526 self.as_ptr(),
527 ffi::NID_info_access,
528 ptr::null_mut(),
529 ptr::null_mut(),
530 );
531 Stack::from_ptr_opt(stack as *mut _)
532 }
533 }
534
535 #[corresponds(X509_get_pathlen)]
537 #[cfg(any(ossl110, boringssl, awslc))]
538 pub fn pathlen(&self) -> Option<u32> {
539 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
540 u32::try_from(v).ok()
541 }
542
543 #[allow(deprecated)]
545 #[cfg(libressl)]
546 pub fn pathlen(&self) -> Option<u32> {
547 let bs = self.basic_constraints()?;
548 let pathlen = bs.pathlen()?;
549 u32::try_from(pathlen.get()).ok()
550 }
551
552 pub fn basic_constraints(&self) -> Option<BasicConstraints> {
554 unsafe {
555 let data = ffi::X509_get_ext_d2i(
556 self.as_ptr(),
557 ffi::NID_basic_constraints,
558 ptr::null_mut(),
559 ptr::null_mut(),
560 );
561 BasicConstraints::from_ptr_opt(data as _)
562 }
563 }
564
565 #[corresponds(X509_get0_subject_key_id)]
567 #[cfg(any(ossl110, boringssl, awslc))]
568 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
569 unsafe {
570 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
571 Asn1OctetStringRef::from_const_ptr_opt(data)
572 }
573 }
574
575 #[cfg(libressl)]
577 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
578 unsafe {
579 let data = ffi::X509_get_ext_d2i(
580 self.as_ptr(),
581 ffi::NID_subject_key_identifier,
582 ptr::null_mut(),
583 ptr::null_mut(),
584 );
585 Asn1OctetStringRef::from_const_ptr_opt(data as _)
586 }
587 }
588
589 #[corresponds(X509_get0_authority_key_id)]
591 #[cfg(any(ossl110, boringssl, awslc))]
592 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
593 unsafe {
594 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
595 Asn1OctetStringRef::from_const_ptr_opt(data)
596 }
597 }
598
599 #[corresponds(X509_get0_authority_issuer)]
601 #[cfg(any(ossl111d, boringssl, awslc))]
602 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
603 unsafe {
604 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
605 StackRef::from_const_ptr_opt(stack)
606 }
607 }
608
609 #[corresponds(X509_get0_authority_serial)]
611 #[cfg(any(ossl111d, boringssl, awslc))]
612 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
613 unsafe {
614 let r = ffi::X509_get0_authority_serial(self.as_ptr());
615 Asn1IntegerRef::from_const_ptr_opt(r)
616 }
617 }
618
619 #[corresponds(X509_get_pubkey)]
620 #[cfg(not(ossl400))]
621 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
622 unsafe {
623 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
624 Ok(PKey::from_ptr(pkey))
625 }
626 }
627
628 #[corresponds(X509_get_pubkey)]
629 #[cfg(ossl400)]
630 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
631 unsafe {
632 let pkey = cvt_p_const(ffi::X509_get_pubkey(self.as_ptr()))?;
633 Ok(PKey::from_ptr(pkey as *mut _))
634 }
635 }
636
637 #[corresponds(X509_get_X509_PUBKEY)]
638 #[cfg(any(all(ossl110, not(ossl400)), libressl, boringssl, awslc))]
639 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
640 unsafe {
641 let key = cvt_p(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?;
642 Ok(X509PubkeyRef::from_ptr(key))
643 }
644 }
645
646 #[corresponds(X509_get_X509_PUBKEY)]
647 #[cfg(ossl400)]
648 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
649 unsafe {
650 let key = cvt_p_const(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?;
651 Ok(X509PubkeyRef::from_ptr(key as *mut _))
652 }
653 }
654
655 digest! {
656 digest,
662 ffi::X509_digest
663 }
664
665 digest! {
666 pubkey_digest,
672 ffi::X509_pubkey_digest
673 }
674
675 #[corresponds(X509_getm_notAfter)]
677 pub fn not_after(&self) -> &Asn1TimeRef {
678 unsafe {
679 let date = X509_getm_notAfter(self.as_ptr());
680 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
681 }
682 }
683
684 #[corresponds(X509_getm_notBefore)]
686 pub fn not_before(&self) -> &Asn1TimeRef {
687 unsafe {
688 let date = X509_getm_notBefore(self.as_ptr());
689 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
690 }
691 }
692
693 #[corresponds(X509_get0_signature)]
695 pub fn signature(&self) -> &Asn1BitStringRef {
696 unsafe {
697 let mut signature = ptr::null();
698 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
699 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
700 }
701 }
702
703 #[corresponds(X509_get0_signature)]
705 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
706 unsafe {
707 let mut algor = ptr::null();
708 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
709 X509AlgorithmRef::from_const_ptr_opt(algor)
710 .expect("signature algorithm must not be null")
711 }
712 }
713
714 #[corresponds(X509_get1_ocsp)]
720 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
721 unsafe {
722 let stack: Stack<OpensslString> =
723 cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))?;
724 for entry in &stack {
725 let bytes = CStr::from_ptr(entry.as_ptr()).to_bytes();
726 if str::from_utf8(bytes).is_err() {
727 return Err(ErrorStack::internal_error(
728 "OCSP responder URL contained invalid UTF-8",
729 ));
730 }
731 }
732 Ok(stack)
733 }
734 }
735
736 #[corresponds(X509_check_issued)]
738 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
739 unsafe {
740 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
741 X509VerifyResult::from_raw(r)
742 }
743 }
744
745 #[corresponds(X509_get_version)]
750 #[cfg(any(ossl110, libressl, boringssl, awslc))]
751 #[allow(clippy::unnecessary_cast)]
752 pub fn version(&self) -> i32 {
753 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
754 }
755
756 #[corresponds(X509_verify)]
763 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
764 where
765 T: HasPublic,
766 {
767 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
768 }
769
770 #[corresponds(X509_get_serialNumber)]
772 pub fn serial_number(&self) -> &Asn1IntegerRef {
773 unsafe {
774 let r = ffi::X509_get_serialNumber(self.as_ptr());
775 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
776 }
777 }
778
779 #[corresponds(X509_alias_get0)]
785 pub fn alias(&self) -> Option<&[u8]> {
786 unsafe {
787 let mut len = 0;
788 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
789 if ptr.is_null() {
790 None
791 } else {
792 Some(util::from_raw_parts(ptr, len as usize))
793 }
794 }
795 }
796
797 to_pem! {
798 #[corresponds(PEM_write_bio_X509)]
802 to_pem,
803 ffi::PEM_write_bio_X509
804 }
805
806 to_der! {
807 #[corresponds(i2d_X509)]
809 to_der,
810 ffi::i2d_X509
811 }
812
813 to_pem! {
814 #[corresponds(X509_print)]
816 to_text,
817 ffi::X509_print
818 }
819}
820
821impl ToOwned for X509Ref {
822 type Owned = X509;
823
824 fn to_owned(&self) -> X509 {
825 unsafe {
826 X509_up_ref(self.as_ptr());
827 X509::from_ptr(self.as_ptr())
828 }
829 }
830}
831
832impl Ord for X509Ref {
833 fn cmp(&self, other: &Self) -> cmp::Ordering {
834 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
837 cmp.cmp(&0)
838 }
839}
840
841impl PartialOrd for X509Ref {
842 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
843 Some(self.cmp(other))
844 }
845}
846
847impl PartialOrd<X509> for X509Ref {
848 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
849 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
850 }
851}
852
853impl PartialEq for X509Ref {
854 fn eq(&self, other: &Self) -> bool {
855 self.cmp(other) == cmp::Ordering::Equal
856 }
857}
858
859impl PartialEq<X509> for X509Ref {
860 fn eq(&self, other: &X509) -> bool {
861 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
862 }
863}
864
865impl Eq for X509Ref {}
866
867impl X509 {
868 pub fn builder() -> Result<X509Builder, ErrorStack> {
870 X509Builder::new()
871 }
872
873 from_pem! {
874 #[corresponds(PEM_read_bio_X509)]
878 from_pem,
879 X509,
880 ffi::PEM_read_bio_X509
881 }
882
883 from_der! {
884 #[corresponds(d2i_X509)]
886 from_der,
887 X509,
888 ffi::d2i_X509
889 }
890
891 #[corresponds(PEM_read_bio_X509)]
893 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
894 unsafe {
895 ffi::init();
896 let bio = MemBioSlice::new(pem)?;
897
898 let mut certs = vec![];
899 loop {
900 let r =
901 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
902 if r.is_null() {
903 let e = ErrorStack::get();
904
905 if let Some(err) = e.errors().last() {
906 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
907 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
908 {
909 break;
910 }
911 }
912
913 return Err(e);
914 } else {
915 certs.push(X509(r));
916 }
917 }
918
919 Ok(certs)
920 }
921 }
922}
923
924impl Clone for X509 {
925 fn clone(&self) -> X509 {
926 X509Ref::to_owned(self)
927 }
928}
929
930impl fmt::Debug for X509 {
931 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
932 let serial = match &self.serial_number().to_bn() {
933 Ok(bn) => match bn.to_hex_str() {
934 Ok(hex) => hex.to_string(),
935 Err(_) => "".to_string(),
936 },
937 Err(_) => "".to_string(),
938 };
939 let mut debug_struct = formatter.debug_struct("X509");
940 debug_struct.field("serial_number", &serial);
941 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
942 debug_struct.field("issuer", &self.issuer_name());
943 debug_struct.field("subject", &self.subject_name());
944 if let Some(subject_alt_names) = &self.subject_alt_names() {
945 debug_struct.field("subject_alt_names", subject_alt_names);
946 }
947 debug_struct.field("not_before", &self.not_before());
948 debug_struct.field("not_after", &self.not_after());
949
950 if let Ok(public_key) = &self.public_key() {
951 debug_struct.field("public_key", public_key);
952 };
953 debug_struct.finish()
956 }
957}
958
959impl AsRef<X509Ref> for X509Ref {
960 fn as_ref(&self) -> &X509Ref {
961 self
962 }
963}
964
965impl Stackable for X509 {
966 type StackType = ffi::stack_st_X509;
967}
968
969impl Ord for X509 {
970 fn cmp(&self, other: &Self) -> cmp::Ordering {
971 X509Ref::cmp(self, other)
972 }
973}
974
975impl PartialOrd for X509 {
976 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
977 Some(self.cmp(other))
978 }
979}
980
981impl PartialOrd<X509Ref> for X509 {
982 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
983 X509Ref::partial_cmp(self, other)
984 }
985}
986
987impl PartialEq for X509 {
988 fn eq(&self, other: &Self) -> bool {
989 X509Ref::eq(self, other)
990 }
991}
992
993impl PartialEq<X509Ref> for X509 {
994 fn eq(&self, other: &X509Ref) -> bool {
995 X509Ref::eq(self, other)
996 }
997}
998
999impl Eq for X509 {}
1000
1001pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
1003
1004impl X509v3Context<'_> {
1005 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
1006 &self.0 as *const _ as *mut _
1007 }
1008}
1009
1010foreign_type_and_impl_send_sync! {
1011 type CType = ffi::X509_EXTENSION;
1012 fn drop = ffi::X509_EXTENSION_free;
1013
1014 pub struct X509Extension;
1016 pub struct X509ExtensionRef;
1018}
1019
1020impl Stackable for X509Extension {
1021 type StackType = ffi::stack_st_X509_EXTENSION;
1022}
1023
1024impl X509Extension {
1025 #[deprecated(
1039 note = "Use x509::extension types or new_from_der instead",
1040 since = "0.10.51"
1041 )]
1042 pub fn new(
1043 conf: Option<&ConfRef>,
1044 context: Option<&X509v3Context<'_>>,
1045 name: &str,
1046 value: &str,
1047 ) -> Result<X509Extension, ErrorStack> {
1048 let name = CString::new(name).unwrap();
1049 let value = CString::new(value).unwrap();
1050 let mut ctx;
1051 unsafe {
1052 ffi::init();
1053 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1054 let context_ptr = match context {
1055 Some(c) => c.as_ptr(),
1056 None => {
1057 ctx = mem::zeroed();
1058
1059 ffi::X509V3_set_ctx(
1060 &mut ctx,
1061 ptr::null_mut(),
1062 ptr::null_mut(),
1063 ptr::null_mut(),
1064 ptr::null_mut(),
1065 0,
1066 );
1067 &mut ctx
1068 }
1069 };
1070 let name = name.as_ptr() as *mut _;
1071 let value = value.as_ptr() as *mut _;
1072
1073 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
1074 }
1075 }
1076
1077 #[deprecated(
1091 note = "Use x509::extension types or new_from_der instead",
1092 since = "0.10.51"
1093 )]
1094 pub fn new_nid(
1095 conf: Option<&ConfRef>,
1096 context: Option<&X509v3Context<'_>>,
1097 name: Nid,
1098 value: &str,
1099 ) -> Result<X509Extension, ErrorStack> {
1100 let value = CString::new(value).unwrap();
1101 let mut ctx;
1102 unsafe {
1103 ffi::init();
1104 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1105 let context_ptr = match context {
1106 Some(c) => c.as_ptr(),
1107 None => {
1108 ctx = mem::zeroed();
1109
1110 ffi::X509V3_set_ctx(
1111 &mut ctx,
1112 ptr::null_mut(),
1113 ptr::null_mut(),
1114 ptr::null_mut(),
1115 ptr::null_mut(),
1116 0,
1117 );
1118 &mut ctx
1119 }
1120 };
1121 let name = name.as_raw();
1122 let value = value.as_ptr() as *mut _;
1123
1124 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1125 }
1126 }
1127
1128 pub fn new_from_der(
1138 oid: &Asn1ObjectRef,
1139 critical: bool,
1140 der_contents: &Asn1OctetStringRef,
1141 ) -> Result<X509Extension, ErrorStack> {
1142 unsafe {
1143 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1144 ptr::null_mut(),
1145 oid.as_ptr(),
1146 critical as _,
1147 der_contents.as_ptr(),
1148 ))
1149 .map(X509Extension)
1150 }
1151 }
1152
1153 pub fn new_subject_alt_name(
1155 stack: Stack<GeneralName>,
1156 critical: bool,
1157 ) -> Result<X509Extension, ErrorStack> {
1158 unsafe { Self::new_internal(Nid::SUBJECT_ALT_NAME, critical, stack.as_ptr().cast()) }
1159 }
1160
1161 pub(crate) unsafe fn new_internal(
1162 nid: Nid,
1163 critical: bool,
1164 value: *mut c_void,
1165 ) -> Result<X509Extension, ErrorStack> {
1166 ffi::init();
1167 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1168 }
1169
1170 #[cfg(not(libressl390))]
1176 #[corresponds(X509V3_EXT_add_alias)]
1177 #[deprecated(
1178 note = "Use x509::extension types or new_from_der and then this is not necessary",
1179 since = "0.10.51"
1180 )]
1181 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1182 ffi::init();
1183 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1184 }
1185}
1186
1187impl X509ExtensionRef {
1188 to_der! {
1189 #[corresponds(i2d_X509_EXTENSION)]
1191 to_der,
1192 ffi::i2d_X509_EXTENSION
1193 }
1194}
1195
1196pub struct X509NameBuilder(X509Name);
1198
1199impl X509NameBuilder {
1200 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1202 unsafe {
1203 ffi::init();
1204 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1205 }
1206 }
1207
1208 #[corresponds(X509_NAME_add_entry)]
1210 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1211 unsafe {
1212 cvt(ffi::X509_NAME_add_entry(
1213 self.0.as_ptr(),
1214 ne.as_ptr(),
1215 -1,
1216 0,
1217 ))
1218 .map(|_| ())
1219 }
1220 }
1221
1222 #[corresponds(X509_NAME_add_entry_by_txt)]
1224 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1225 unsafe {
1226 let field = CString::new(field).unwrap();
1227 assert!(value.len() <= crate::SLenType::MAX as usize);
1228 cvt(ffi::X509_NAME_add_entry_by_txt(
1229 self.0.as_ptr(),
1230 field.as_ptr() as *mut _,
1231 ffi::MBSTRING_UTF8,
1232 value.as_ptr(),
1233 value.len() as crate::SLenType,
1234 -1,
1235 0,
1236 ))
1237 .map(|_| ())
1238 }
1239 }
1240
1241 #[corresponds(X509_NAME_add_entry_by_txt)]
1243 pub fn append_entry_by_text_with_type(
1244 &mut self,
1245 field: &str,
1246 value: &str,
1247 ty: Asn1Type,
1248 ) -> Result<(), ErrorStack> {
1249 unsafe {
1250 let field = CString::new(field).unwrap();
1251 assert!(value.len() <= crate::SLenType::MAX as usize);
1252 cvt(ffi::X509_NAME_add_entry_by_txt(
1253 self.0.as_ptr(),
1254 field.as_ptr() as *mut _,
1255 ty.as_raw(),
1256 value.as_ptr(),
1257 value.len() as crate::SLenType,
1258 -1,
1259 0,
1260 ))
1261 .map(|_| ())
1262 }
1263 }
1264
1265 #[corresponds(X509_NAME_add_entry_by_NID)]
1267 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1268 unsafe {
1269 assert!(value.len() <= crate::SLenType::MAX as usize);
1270 cvt(ffi::X509_NAME_add_entry_by_NID(
1271 self.0.as_ptr(),
1272 field.as_raw(),
1273 ffi::MBSTRING_UTF8,
1274 value.as_ptr() as *mut _,
1275 value.len() as crate::SLenType,
1276 -1,
1277 0,
1278 ))
1279 .map(|_| ())
1280 }
1281 }
1282
1283 #[corresponds(X509_NAME_add_entry_by_NID)]
1285 pub fn append_entry_by_nid_with_type(
1286 &mut self,
1287 field: Nid,
1288 value: &str,
1289 ty: Asn1Type,
1290 ) -> Result<(), ErrorStack> {
1291 unsafe {
1292 assert!(value.len() <= crate::SLenType::MAX as usize);
1293 cvt(ffi::X509_NAME_add_entry_by_NID(
1294 self.0.as_ptr(),
1295 field.as_raw(),
1296 ty.as_raw(),
1297 value.as_ptr() as *mut _,
1298 value.len() as crate::SLenType,
1299 -1,
1300 0,
1301 ))
1302 .map(|_| ())
1303 }
1304 }
1305
1306 pub fn build(self) -> X509Name {
1308 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1312 }
1313}
1314
1315foreign_type_and_impl_send_sync! {
1316 type CType = ffi::X509_NAME;
1317 fn drop = ffi::X509_NAME_free;
1318
1319 pub struct X509Name;
1321 pub struct X509NameRef;
1323}
1324
1325impl X509Name {
1326 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1328 X509NameBuilder::new()
1329 }
1330
1331 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1335 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1336 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1337 }
1338
1339 from_der! {
1340 from_der,
1346 X509Name,
1347 ffi::d2i_X509_NAME
1348 }
1349}
1350
1351impl Stackable for X509Name {
1352 type StackType = ffi::stack_st_X509_NAME;
1353}
1354
1355impl X509NameRef {
1356 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1358 X509NameEntries {
1359 name: self,
1360 nid: Some(nid),
1361 loc: -1,
1362 }
1363 }
1364
1365 pub fn entries(&self) -> X509NameEntries<'_> {
1367 X509NameEntries {
1368 name: self,
1369 nid: None,
1370 loc: -1,
1371 }
1372 }
1373
1374 #[corresponds(X509_NAME_cmp)]
1381 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1382 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1383 if cfg!(ossl300) && cmp == -2 {
1384 return Err(ErrorStack::get());
1385 }
1386 Ok(cmp.cmp(&0))
1387 }
1388
1389 #[corresponds(X509_NAME_dup)]
1391 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1392 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1393 }
1394
1395 to_der! {
1396 to_der,
1402 ffi::i2d_X509_NAME
1403 }
1404
1405 digest! {
1406 digest,
1412 ffi::X509_NAME_digest
1413 }
1414}
1415
1416impl fmt::Debug for X509NameRef {
1417 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1418 formatter.debug_list().entries(self.entries()).finish()
1419 }
1420}
1421
1422pub struct X509NameEntries<'a> {
1424 name: &'a X509NameRef,
1425 nid: Option<Nid>,
1426 loc: c_int,
1427}
1428
1429impl<'a> Iterator for X509NameEntries<'a> {
1430 type Item = &'a X509NameEntryRef;
1431
1432 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1433 unsafe {
1434 match self.nid {
1435 Some(nid) => {
1436 self.loc =
1438 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1439 if self.loc == -1 {
1440 return None;
1441 }
1442 }
1443 None => {
1444 self.loc += 1;
1446 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1447 return None;
1448 }
1449 }
1450 }
1451
1452 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1453
1454 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1455 }
1456 }
1457}
1458
1459foreign_type_and_impl_send_sync! {
1460 type CType = ffi::X509_NAME_ENTRY;
1461 fn drop = ffi::X509_NAME_ENTRY_free;
1462
1463 pub struct X509NameEntry;
1465 pub struct X509NameEntryRef;
1467}
1468
1469impl X509NameEntryRef {
1470 #[corresponds(X509_NAME_ENTRY_get_data)]
1472 pub fn data(&self) -> &Asn1StringRef {
1473 unsafe {
1474 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1475 Asn1StringRef::from_ptr(data as *mut _)
1476 }
1477 }
1478
1479 #[corresponds(X509_NAME_ENTRY_get_object)]
1482 pub fn object(&self) -> &Asn1ObjectRef {
1483 unsafe {
1484 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1485 Asn1ObjectRef::from_ptr(object as *mut _)
1486 }
1487 }
1488}
1489
1490impl fmt::Debug for X509NameEntryRef {
1491 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1492 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1493 }
1494}
1495
1496foreign_type_and_impl_send_sync! {
1497 type CType = ffi::X509_PUBKEY;
1498 fn drop = ffi::X509_PUBKEY_free;
1499
1500 pub struct X509Pubkey;
1502 pub struct X509PubkeyRef;
1504}
1505
1506impl X509Pubkey {
1507 from_der! {
1508 from_der,
1514 X509Pubkey,
1515 ffi::d2i_X509_PUBKEY
1516 }
1517
1518 pub fn from_pubkey<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
1524 where
1525 T: HasPublic,
1526 {
1527 let mut p = ptr::null_mut();
1528 unsafe {
1529 cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?;
1530 }
1531 Ok(X509Pubkey(p))
1532 }
1533}
1534
1535impl X509PubkeyRef {
1536 #[corresponds(X509_PUBKEY_dup)]
1538 #[cfg(ossl300)]
1539 pub fn to_owned(&self) -> Result<X509Pubkey, ErrorStack> {
1540 unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) }
1541 }
1542
1543 to_der! {
1544 to_der,
1550 ffi::i2d_X509_PUBKEY
1551 }
1552
1553 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1559 unsafe {
1560 let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
1561 Ok(PKey::from_ptr(key))
1562 }
1563 }
1564
1565 pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> {
1571 unsafe {
1572 let mut pk = ptr::null_mut() as *const c_uchar;
1573 let mut pkt_len: c_int = 0;
1574 cvt(ffi::X509_PUBKEY_get0_param(
1575 ptr::null_mut(),
1576 &mut pk as *mut _,
1577 &mut pkt_len as *mut _,
1578 ptr::null_mut(),
1579 self.as_ptr(),
1580 ))?;
1581
1582 Ok(util::from_raw_parts(pk, pkt_len as usize))
1583 }
1584 }
1585}
1586
1587pub struct X509ReqBuilder(X509Req);
1589
1590impl X509ReqBuilder {
1591 #[corresponds(X509_REQ_new)]
1593 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1594 unsafe {
1595 ffi::init();
1596 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1597 }
1598 }
1599
1600 #[corresponds(X509_REQ_set_version)]
1602 #[allow(clippy::useless_conversion)]
1603 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1604 unsafe {
1605 cvt(ffi::X509_REQ_set_version(
1606 self.0.as_ptr(),
1607 version as c_long,
1608 ))
1609 .map(|_| ())
1610 }
1611 }
1612
1613 #[corresponds(X509_REQ_set_subject_name)]
1615 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1616 unsafe {
1617 cvt(ffi::X509_REQ_set_subject_name(
1618 self.0.as_ptr(),
1619 subject_name.as_ptr(),
1620 ))
1621 .map(|_| ())
1622 }
1623 }
1624
1625 #[corresponds(X509_REQ_set_pubkey)]
1627 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1628 where
1629 T: HasPublic,
1630 {
1631 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1632 }
1633
1634 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1637 unsafe {
1638 let mut ctx = mem::zeroed();
1639
1640 ffi::X509V3_set_ctx(
1641 &mut ctx,
1642 ptr::null_mut(),
1643 ptr::null_mut(),
1644 self.0.as_ptr(),
1645 ptr::null_mut(),
1646 0,
1647 );
1648
1649 if let Some(conf) = conf {
1651 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1652 }
1653
1654 X509v3Context(ctx, PhantomData)
1655 }
1656 }
1657
1658 pub fn add_extensions(
1660 &mut self,
1661 extensions: &StackRef<X509Extension>,
1662 ) -> Result<(), ErrorStack> {
1663 unsafe {
1664 cvt(ffi::X509_REQ_add_extensions(
1665 self.0.as_ptr(),
1666 extensions.as_ptr(),
1667 ))
1668 .map(|_| ())
1669 }
1670 }
1671
1672 #[corresponds(X509_REQ_sign)]
1674 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1675 where
1676 T: HasPrivate,
1677 {
1678 unsafe {
1679 cvt(ffi::X509_REQ_sign(
1680 self.0.as_ptr(),
1681 key.as_ptr(),
1682 hash.as_ptr(),
1683 ))
1684 .map(|_| ())
1685 }
1686 }
1687
1688 pub fn build(self) -> X509Req {
1690 self.0
1691 }
1692}
1693
1694foreign_type_and_impl_send_sync! {
1695 type CType = ffi::X509_REQ;
1696 fn drop = ffi::X509_REQ_free;
1697
1698 pub struct X509Req;
1700 pub struct X509ReqRef;
1702}
1703
1704impl X509Req {
1705 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1707 X509ReqBuilder::new()
1708 }
1709
1710 from_pem! {
1711 from_pem,
1719 X509Req,
1720 ffi::PEM_read_bio_X509_REQ
1721 }
1722
1723 from_der! {
1724 from_der,
1730 X509Req,
1731 ffi::d2i_X509_REQ
1732 }
1733}
1734
1735impl X509ReqRef {
1736 to_pem! {
1737 to_pem,
1745 ffi::PEM_write_bio_X509_REQ
1746 }
1747
1748 to_der! {
1749 to_der,
1755 ffi::i2d_X509_REQ
1756 }
1757
1758 to_pem! {
1759 #[corresponds(X509_Req_print)]
1761 to_text,
1762 ffi::X509_REQ_print
1763 }
1764
1765 digest! {
1766 digest,
1772 ffi::X509_REQ_digest
1773 }
1774
1775 #[corresponds(X509_REQ_get_version)]
1777 #[allow(clippy::unnecessary_cast)]
1778 pub fn version(&self) -> i32 {
1779 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1780 }
1781
1782 #[corresponds(X509_REQ_get_subject_name)]
1784 pub fn subject_name(&self) -> &X509NameRef {
1785 unsafe {
1786 let name = X509_REQ_get_subject_name(self.as_ptr());
1787 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1788 }
1789 }
1790
1791 #[corresponds(X509_REQ_get_pubkey)]
1793 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1794 unsafe {
1795 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1796 Ok(PKey::from_ptr(key))
1797 }
1798 }
1799
1800 #[cfg(ossl110)]
1806 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
1807 unsafe {
1808 let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?;
1809 Ok(X509PubkeyRef::from_ptr(key))
1810 }
1811 }
1812
1813 #[corresponds(X509_REQ_verify)]
1817 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1818 where
1819 T: HasPublic,
1820 {
1821 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1822 }
1823
1824 #[corresponds(X509_REQ_get_extensions)]
1826 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1827 unsafe {
1828 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1829 Ok(Stack::from_ptr(extensions))
1830 }
1831 }
1832}
1833
1834#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1836pub struct CrlReason(c_int);
1837
1838#[allow(missing_docs)] impl CrlReason {
1840 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1841 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1842 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1843 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1844 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1845 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1846 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1847 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1848 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1849 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1850
1851 pub const fn from_raw(value: c_int) -> Self {
1853 CrlReason(value)
1854 }
1855
1856 pub const fn as_raw(&self) -> c_int {
1858 self.0
1859 }
1860}
1861
1862foreign_type_and_impl_send_sync! {
1863 type CType = ffi::X509_REVOKED;
1864 fn drop = ffi::X509_REVOKED_free;
1865
1866 pub struct X509Revoked;
1868 pub struct X509RevokedRef;
1870}
1871
1872impl Stackable for X509Revoked {
1873 type StackType = ffi::stack_st_X509_REVOKED;
1874}
1875
1876impl X509Revoked {
1877 from_der! {
1878 #[corresponds(d2i_X509_REVOKED)]
1880 from_der,
1881 X509Revoked,
1882 ffi::d2i_X509_REVOKED
1883 }
1884}
1885
1886impl X509RevokedRef {
1887 to_der! {
1888 #[corresponds(d2i_X509_REVOKED)]
1890 to_der,
1891 ffi::i2d_X509_REVOKED
1892 }
1893
1894 #[corresponds(X509_REVOKED_dup)]
1896 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1897 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1898 }
1899
1900 #[corresponds(X509_REVOKED_get0_revocationDate)]
1902 pub fn revocation_date(&self) -> &Asn1TimeRef {
1903 unsafe {
1904 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1905 assert!(!r.is_null());
1906 Asn1TimeRef::from_ptr(r as *mut _)
1907 }
1908 }
1909
1910 #[corresponds(X509_REVOKED_get0_serialNumber)]
1912 pub fn serial_number(&self) -> &Asn1IntegerRef {
1913 unsafe {
1914 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1915 assert!(!r.is_null());
1916 Asn1IntegerRef::from_ptr(r as *mut _)
1917 }
1918 }
1919
1920 #[corresponds(X509_REVOKED_get_ext_d2i)]
1924 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1925 let mut critical = -1;
1926 let out = unsafe {
1927 let ext = ffi::X509_REVOKED_get_ext_d2i(
1929 self.as_ptr(),
1930 T::NID.as_raw(),
1931 &mut critical as *mut _,
1932 ptr::null_mut(),
1933 );
1934 T::Output::from_ptr_opt(ext as *mut _)
1937 };
1938 match (critical, out) {
1939 (0, Some(out)) => Ok(Some((false, out))),
1940 (1, Some(out)) => Ok(Some((true, out))),
1941 (-1 | -2, _) => Ok(None),
1943 (0 | 1, None) => Err(ErrorStack::get()),
1946 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1947 }
1948 }
1949}
1950
1951pub enum ReasonCode {}
1954
1955unsafe impl ExtensionType for ReasonCode {
1958 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1959
1960 type Output = Asn1Enumerated;
1961}
1962
1963pub enum CertificateIssuer {}
1966
1967unsafe impl ExtensionType for CertificateIssuer {
1970 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1971
1972 type Output = Stack<GeneralName>;
1973}
1974
1975pub enum AuthorityInformationAccess {}
1977
1978unsafe impl ExtensionType for AuthorityInformationAccess {
1981 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1982
1983 type Output = Stack<AccessDescription>;
1984}
1985
1986foreign_type_and_impl_send_sync! {
1987 type CType = ffi::X509_CRL;
1988 fn drop = ffi::X509_CRL_free;
1989
1990 pub struct X509Crl;
1992 pub struct X509CrlRef;
1994}
1995
1996pub enum CrlStatus<'a> {
2002 NotRevoked,
2004 Revoked(&'a X509RevokedRef),
2006 RemoveFromCrl(&'a X509RevokedRef),
2011}
2012
2013impl<'a> CrlStatus<'a> {
2014 unsafe fn from_ffi_status(
2019 status: c_int,
2020 revoked_entry: *mut ffi::X509_REVOKED,
2021 ) -> CrlStatus<'a> {
2022 match status {
2023 0 => CrlStatus::NotRevoked,
2024 1 => {
2025 assert!(!revoked_entry.is_null());
2026 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
2027 }
2028 2 => {
2029 assert!(!revoked_entry.is_null());
2030 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
2031 }
2032 _ => unreachable!(
2033 "{}",
2034 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
2035 ),
2036 }
2037 }
2038}
2039
2040impl X509Crl {
2041 from_pem! {
2042 #[corresponds(PEM_read_bio_X509_CRL)]
2046 from_pem,
2047 X509Crl,
2048 ffi::PEM_read_bio_X509_CRL
2049 }
2050
2051 from_der! {
2052 #[corresponds(d2i_X509_CRL)]
2054 from_der,
2055 X509Crl,
2056 ffi::d2i_X509_CRL
2057 }
2058}
2059
2060impl X509CrlRef {
2061 to_pem! {
2062 #[corresponds(PEM_write_bio_X509_CRL)]
2066 to_pem,
2067 ffi::PEM_write_bio_X509_CRL
2068 }
2069
2070 to_der! {
2071 #[corresponds(i2d_X509_CRL)]
2073 to_der,
2074 ffi::i2d_X509_CRL
2075 }
2076
2077 digest! {
2078 digest,
2084 ffi::X509_CRL_digest
2085 }
2086
2087 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2089 unsafe {
2090 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2091 if revoked.is_null() {
2092 None
2093 } else {
2094 Some(StackRef::from_ptr(revoked))
2095 }
2096 }
2097 }
2098
2099 #[corresponds(X509_CRL_get0_lastUpdate)]
2101 pub fn last_update(&self) -> &Asn1TimeRef {
2102 unsafe {
2103 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2104 assert!(!date.is_null());
2105 Asn1TimeRef::from_ptr(date as *mut _)
2106 }
2107 }
2108
2109 #[corresponds(X509_CRL_get0_nextUpdate)]
2113 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2114 unsafe {
2115 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2116 Asn1TimeRef::from_const_ptr_opt(date)
2117 }
2118 }
2119
2120 #[corresponds(X509_CRL_get0_by_serial)]
2122 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2123 unsafe {
2124 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2125 let status =
2126 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2127 CrlStatus::from_ffi_status(status, ret)
2128 }
2129 }
2130
2131 #[corresponds(X509_CRL_get0_by_cert)]
2133 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2134 unsafe {
2135 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2136 let status =
2137 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2138 CrlStatus::from_ffi_status(status, ret)
2139 }
2140 }
2141
2142 #[corresponds(X509_CRL_get_issuer)]
2144 pub fn issuer_name(&self) -> &X509NameRef {
2145 unsafe {
2146 let name = X509_CRL_get_issuer(self.as_ptr());
2147 assert!(!name.is_null());
2148 X509NameRef::from_ptr(name as *mut _)
2149 }
2150 }
2151
2152 #[corresponds(X509_CRL_verify)]
2159 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2160 where
2161 T: HasPublic,
2162 {
2163 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2164 }
2165
2166 #[corresponds(X509_CRL_get_ext_d2i)]
2170 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2171 let mut critical = -1;
2172 let out = unsafe {
2173 let ext = ffi::X509_CRL_get_ext_d2i(
2175 self.as_ptr(),
2176 T::NID.as_raw(),
2177 &mut critical as *mut _,
2178 ptr::null_mut(),
2179 );
2180 T::Output::from_ptr_opt(ext as *mut _)
2183 };
2184 match (critical, out) {
2185 (0, Some(out)) => Ok(Some((false, out))),
2186 (1, Some(out)) => Ok(Some((true, out))),
2187 (-1 | -2, _) => Ok(None),
2189 (0 | 1, None) => Err(ErrorStack::get()),
2192 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2193 }
2194 }
2195}
2196
2197#[derive(Copy, Clone, PartialEq, Eq)]
2199pub struct X509VerifyResult(c_int);
2200
2201impl fmt::Debug for X509VerifyResult {
2202 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2203 fmt.debug_struct("X509VerifyResult")
2204 .field("code", &self.0)
2205 .field("error", &self.error_string())
2206 .finish()
2207 }
2208}
2209
2210impl fmt::Display for X509VerifyResult {
2211 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2212 fmt.write_str(self.error_string())
2213 }
2214}
2215
2216impl Error for X509VerifyResult {}
2217
2218impl X509VerifyResult {
2219 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2226 X509VerifyResult(err)
2227 }
2228
2229 #[allow(clippy::trivially_copy_pass_by_ref)]
2231 pub fn as_raw(&self) -> c_int {
2232 self.0
2233 }
2234
2235 #[corresponds(X509_verify_cert_error_string)]
2237 #[allow(clippy::trivially_copy_pass_by_ref)]
2238 pub fn error_string(&self) -> &'static str {
2239 ffi::init();
2240
2241 unsafe {
2242 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2243 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2244 }
2245 }
2246
2247 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2249 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2251 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2252}
2253
2254foreign_type_and_impl_send_sync! {
2255 type CType = ffi::GENERAL_NAME;
2256 fn drop = ffi::GENERAL_NAME_free;
2257
2258 pub struct GeneralName;
2260 pub struct GeneralNameRef;
2262}
2263
2264impl GeneralName {
2265 unsafe fn new(
2266 type_: c_int,
2267 asn1_type: Asn1Type,
2268 value: &[u8],
2269 ) -> Result<GeneralName, ErrorStack> {
2270 ffi::init();
2271 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2272 (*gn.as_ptr()).type_ = type_;
2273 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2274 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2275
2276 #[cfg(any(boringssl, awslc))]
2277 {
2278 (*gn.as_ptr()).d.ptr = s.cast();
2279 }
2280 #[cfg(not(any(boringssl, awslc)))]
2281 {
2282 (*gn.as_ptr()).d = s.cast();
2283 }
2284
2285 Ok(gn)
2286 }
2287
2288 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2289 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2290 }
2291
2292 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2293 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2294 }
2295
2296 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2297 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2298 }
2299
2300 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2301 match ip {
2302 IpAddr::V4(addr) => unsafe {
2303 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2304 },
2305 IpAddr::V6(addr) => unsafe {
2306 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2307 },
2308 }
2309 }
2310
2311 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2312 unsafe {
2313 ffi::init();
2314 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2315 (*gn).type_ = ffi::GEN_RID;
2316
2317 #[cfg(any(boringssl, awslc))]
2318 {
2319 (*gn).d.registeredID = oid.as_ptr();
2320 }
2321 #[cfg(not(any(boringssl, awslc)))]
2322 {
2323 (*gn).d = oid.as_ptr().cast();
2324 }
2325
2326 mem::forget(oid);
2327
2328 Ok(GeneralName::from_ptr(gn))
2329 }
2330 }
2331
2332 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2333 unsafe {
2334 ffi::init();
2335
2336 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2337 ptr::null_mut(),
2338 &mut value.as_ptr().cast(),
2339 value.len().try_into().unwrap(),
2340 ))?;
2341
2342 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2343 (*gn).type_ = ffi::GEN_OTHERNAME;
2344
2345 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2346 gn,
2347 oid.as_ptr().cast(),
2348 typ,
2349 )) {
2350 ffi::GENERAL_NAME_free(gn);
2351 return Err(e);
2352 }
2353
2354 mem::forget(oid);
2355
2356 Ok(GeneralName::from_ptr(gn))
2357 }
2358 }
2359
2360 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2361 unsafe {
2362 ffi::init();
2363 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2364 (*gn).type_ = ffi::GEN_DIRNAME;
2365
2366 let dup = match name.to_owned() {
2367 Ok(dup) => dup,
2368 Err(e) => {
2369 ffi::GENERAL_NAME_free(gn);
2370 return Err(e);
2371 }
2372 };
2373
2374 #[cfg(any(boringssl, awslc))]
2375 {
2376 (*gn).d.directoryName = dup.as_ptr();
2377 }
2378 #[cfg(not(any(boringssl, awslc)))]
2379 {
2380 (*gn).d = dup.as_ptr().cast();
2381 }
2382
2383 std::mem::forget(dup);
2384
2385 Ok(GeneralName::from_ptr(gn))
2386 }
2387 }
2388}
2389
2390impl GeneralNameRef {
2391 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2392 unsafe {
2393 if (*self.as_ptr()).type_ != ffi_type {
2394 return None;
2395 }
2396
2397 #[cfg(any(boringssl, awslc))]
2398 let d = (*self.as_ptr()).d.ptr;
2399 #[cfg(not(any(boringssl, awslc)))]
2400 let d = (*self.as_ptr()).d;
2401
2402 let ptr = ASN1_STRING_get0_data(d as *mut _);
2403 let len = ffi::ASN1_STRING_length(d as *mut _);
2404
2405 #[allow(clippy::unnecessary_cast)]
2406 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2407 str::from_utf8(slice).ok()
2411 }
2412 }
2413
2414 pub fn email(&self) -> Option<&str> {
2416 self.ia5_string(ffi::GEN_EMAIL)
2417 }
2418
2419 pub fn directory_name(&self) -> Option<&X509NameRef> {
2421 unsafe {
2422 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2423 return None;
2424 }
2425
2426 #[cfg(any(boringssl, awslc))]
2427 let d = (*self.as_ptr()).d.ptr;
2428 #[cfg(not(any(boringssl, awslc)))]
2429 let d = (*self.as_ptr()).d;
2430
2431 Some(X509NameRef::from_const_ptr(d as *const _))
2432 }
2433 }
2434
2435 pub fn dnsname(&self) -> Option<&str> {
2437 self.ia5_string(ffi::GEN_DNS)
2438 }
2439
2440 pub fn uri(&self) -> Option<&str> {
2442 self.ia5_string(ffi::GEN_URI)
2443 }
2444
2445 pub fn ipaddress(&self) -> Option<&[u8]> {
2447 unsafe {
2448 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2449 return None;
2450 }
2451 #[cfg(any(boringssl, awslc))]
2452 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2453 #[cfg(not(any(boringssl, awslc)))]
2454 let d = (*self.as_ptr()).d;
2455
2456 let ptr = ASN1_STRING_get0_data(d as *mut _);
2457 let len = ffi::ASN1_STRING_length(d as *mut _);
2458
2459 #[allow(clippy::unnecessary_cast)]
2460 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2461 }
2462 }
2463}
2464
2465impl fmt::Debug for GeneralNameRef {
2466 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2467 if let Some(email) = self.email() {
2468 formatter.write_str(email)
2469 } else if let Some(dnsname) = self.dnsname() {
2470 formatter.write_str(dnsname)
2471 } else if let Some(uri) = self.uri() {
2472 formatter.write_str(uri)
2473 } else if let Some(ipaddress) = self.ipaddress() {
2474 let address = <[u8; 16]>::try_from(ipaddress)
2475 .map(IpAddr::from)
2476 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2477 match address {
2478 Ok(a) => fmt::Debug::fmt(&a, formatter),
2479 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2480 }
2481 } else {
2482 formatter.write_str("(empty)")
2483 }
2484 }
2485}
2486
2487impl Stackable for GeneralName {
2488 type StackType = ffi::stack_st_GENERAL_NAME;
2489}
2490
2491foreign_type_and_impl_send_sync! {
2492 type CType = ffi::DIST_POINT;
2493 fn drop = ffi::DIST_POINT_free;
2494
2495 pub struct DistPoint;
2497 pub struct DistPointRef;
2499}
2500
2501impl DistPointRef {
2502 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2504 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2505 }
2506}
2507
2508foreign_type_and_impl_send_sync! {
2509 type CType = ffi::DIST_POINT_NAME;
2510 fn drop = ffi::DIST_POINT_NAME_free;
2511
2512 pub struct DistPointName;
2514 pub struct DistPointNameRef;
2516}
2517
2518impl DistPointNameRef {
2519 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2521 unsafe {
2522 if (*self.as_ptr()).type_ != 0 {
2523 return None;
2524 }
2525 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2526 }
2527 }
2528}
2529
2530impl Stackable for DistPoint {
2531 type StackType = ffi::stack_st_DIST_POINT;
2532}
2533
2534foreign_type_and_impl_send_sync! {
2535 type CType = ffi::BASIC_CONSTRAINTS;
2536 fn drop = ffi::BASIC_CONSTRAINTS_free;
2537
2538 pub struct BasicConstraints;
2540 pub struct BasicConstraintsRef;
2542}
2543
2544impl BasicConstraintsRef {
2545 pub fn ca(&self) -> bool {
2546 unsafe { (*(self.as_ptr())).ca != 0 }
2547 }
2548
2549 pub fn pathlen(&self) -> Option<&Asn1IntegerRef> {
2550 if !self.ca() {
2551 return None;
2552 }
2553 unsafe {
2554 let data = (*(self.as_ptr())).pathlen;
2555 Asn1IntegerRef::from_const_ptr_opt(data as _)
2556 }
2557 }
2558}
2559
2560foreign_type_and_impl_send_sync! {
2561 type CType = ffi::ACCESS_DESCRIPTION;
2562 fn drop = ffi::ACCESS_DESCRIPTION_free;
2563
2564 pub struct AccessDescription;
2566 pub struct AccessDescriptionRef;
2568}
2569
2570impl AccessDescriptionRef {
2571 pub fn method(&self) -> &Asn1ObjectRef {
2573 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2574 }
2575
2576 pub fn location(&self) -> &GeneralNameRef {
2578 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2579 }
2580}
2581
2582impl Stackable for AccessDescription {
2583 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2584}
2585
2586foreign_type_and_impl_send_sync! {
2587 type CType = ffi::X509_ALGOR;
2588 fn drop = ffi::X509_ALGOR_free;
2589
2590 pub struct X509Algorithm;
2592 pub struct X509AlgorithmRef;
2594}
2595
2596impl X509AlgorithmRef {
2597 pub fn object(&self) -> &Asn1ObjectRef {
2599 unsafe {
2600 let mut oid = ptr::null();
2601 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2602 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2603 }
2604 }
2605}
2606
2607foreign_type_and_impl_send_sync! {
2608 type CType = ffi::X509_OBJECT;
2609 fn drop = X509_OBJECT_free;
2610
2611 pub struct X509Object;
2613 pub struct X509ObjectRef;
2615}
2616
2617impl X509ObjectRef {
2618 pub fn x509(&self) -> Option<&X509Ref> {
2619 unsafe {
2620 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2621 X509Ref::from_const_ptr_opt(ptr)
2622 }
2623 }
2624}
2625
2626impl Stackable for X509Object {
2627 type StackType = ffi::stack_st_X509_OBJECT;
2628}
2629
2630use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2631
2632use ffi::{
2633 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2634 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2635};
2636
2637use ffi::X509_OBJECT_free;
2638use ffi::X509_OBJECT_get0_X509;
2639
2640use ffi::{
2641 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2642 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2643};
2644
2645#[derive(Copy, Clone, PartialEq, Eq)]
2646pub struct X509PurposeId(c_int);
2647
2648impl X509PurposeId {
2649 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2650 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2651 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2652 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2653 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2654 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2655 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2656 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2657 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2658 #[cfg(ossl320)]
2659 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2660
2661 pub fn from_raw(id: c_int) -> Self {
2663 X509PurposeId(id)
2664 }
2665
2666 pub fn as_raw(&self) -> c_int {
2668 self.0
2669 }
2670}
2671
2672pub struct X509PurposeRef(Opaque);
2674
2675impl ForeignTypeRef for X509PurposeRef {
2677 type CType = ffi::X509_PURPOSE;
2678}
2679
2680impl X509PurposeRef {
2681 #[allow(clippy::unnecessary_cast)]
2695 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2696 unsafe {
2697 let sname = CString::new(sname).unwrap();
2698 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2699 Ok(purpose)
2700 }
2701 }
2702 #[corresponds(X509_PURPOSE_get0)]
2705 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2706 unsafe {
2707 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2708 Ok(X509PurposeRef::from_const_ptr(ptr))
2709 }
2710 }
2711
2712 pub fn purpose(&self) -> X509PurposeId {
2723 unsafe {
2724 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2725 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2726 }
2727 }
2728}