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(ossl111, 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_key_usage)]
457 #[cfg(any(ossl110, libressl, boringssl, awslc))]
458 pub fn key_usage(&self) -> Option<u32> {
459 let flags = unsafe { ffi::X509_get_key_usage(self.as_ptr()) };
460 if flags == u32::MAX {
461 None
462 } else {
463 Some(flags)
464 }
465 }
466
467 #[corresponds(X509_get_ext_d2i)]
469 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
470 unsafe {
471 let stack = ffi::X509_get_ext_d2i(
472 self.as_ptr(),
473 ffi::NID_subject_alt_name,
474 ptr::null_mut(),
475 ptr::null_mut(),
476 );
477 Stack::from_ptr_opt(stack as *mut _)
478 }
479 }
480
481 #[corresponds(X509_get_ext_d2i)]
483 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
484 unsafe {
485 let stack = ffi::X509_get_ext_d2i(
486 self.as_ptr(),
487 ffi::NID_crl_distribution_points,
488 ptr::null_mut(),
489 ptr::null_mut(),
490 );
491 Stack::from_ptr_opt(stack as *mut _)
492 }
493 }
494
495 #[corresponds(X509_get_ext_d2i)]
497 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
498 unsafe {
499 let stack = ffi::X509_get_ext_d2i(
500 self.as_ptr(),
501 ffi::NID_issuer_alt_name,
502 ptr::null_mut(),
503 ptr::null_mut(),
504 );
505 Stack::from_ptr_opt(stack as *mut _)
506 }
507 }
508
509 #[corresponds(X509_get_ext_d2i)]
513 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
514 unsafe {
515 let stack = ffi::X509_get_ext_d2i(
516 self.as_ptr(),
517 ffi::NID_info_access,
518 ptr::null_mut(),
519 ptr::null_mut(),
520 );
521 Stack::from_ptr_opt(stack as *mut _)
522 }
523 }
524
525 #[corresponds(X509_get_pathlen)]
527 #[cfg(any(ossl110, boringssl, awslc))]
528 pub fn pathlen(&self) -> Option<u32> {
529 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
530 u32::try_from(v).ok()
531 }
532
533 #[allow(deprecated)]
535 #[cfg(libressl)]
536 pub fn pathlen(&self) -> Option<u32> {
537 let bs = self.basic_constraints()?;
538 let pathlen = bs.pathlen()?;
539 u32::try_from(pathlen.get()).ok()
540 }
541
542 pub fn basic_constraints(&self) -> Option<BasicConstraints> {
544 unsafe {
545 let data = ffi::X509_get_ext_d2i(
546 self.as_ptr(),
547 ffi::NID_basic_constraints,
548 ptr::null_mut(),
549 ptr::null_mut(),
550 );
551 BasicConstraints::from_ptr_opt(data as _)
552 }
553 }
554
555 #[corresponds(X509_get0_subject_key_id)]
557 #[cfg(any(ossl110, boringssl, awslc))]
558 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
559 unsafe {
560 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
561 Asn1OctetStringRef::from_const_ptr_opt(data)
562 }
563 }
564
565 #[cfg(libressl)]
567 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
568 unsafe {
569 let data = ffi::X509_get_ext_d2i(
570 self.as_ptr(),
571 ffi::NID_subject_key_identifier,
572 ptr::null_mut(),
573 ptr::null_mut(),
574 );
575 Asn1OctetStringRef::from_const_ptr_opt(data as _)
576 }
577 }
578
579 #[corresponds(X509_get0_authority_key_id)]
581 #[cfg(any(ossl110, boringssl, awslc))]
582 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
583 unsafe {
584 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
585 Asn1OctetStringRef::from_const_ptr_opt(data)
586 }
587 }
588
589 #[corresponds(X509_get0_authority_issuer)]
591 #[cfg(any(ossl111d, boringssl, awslc))]
592 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
593 unsafe {
594 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
595 StackRef::from_const_ptr_opt(stack)
596 }
597 }
598
599 #[corresponds(X509_get0_authority_serial)]
601 #[cfg(any(ossl111d, boringssl, awslc))]
602 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
603 unsafe {
604 let r = ffi::X509_get0_authority_serial(self.as_ptr());
605 Asn1IntegerRef::from_const_ptr_opt(r)
606 }
607 }
608
609 #[corresponds(X509_get_pubkey)]
610 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
611 unsafe {
612 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
613 Ok(PKey::from_ptr(pkey))
614 }
615 }
616
617 #[corresponds(X509_get_X509_PUBKEY)]
618 #[cfg(any(ossl110, libressl, boringssl, awslc))]
619 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
620 unsafe {
621 let key = cvt_p(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?;
622 Ok(X509PubkeyRef::from_ptr(key))
623 }
624 }
625
626 digest! {
627 digest,
633 ffi::X509_digest
634 }
635
636 digest! {
637 pubkey_digest,
643 ffi::X509_pubkey_digest
644 }
645
646 #[corresponds(X509_getm_notAfter)]
648 pub fn not_after(&self) -> &Asn1TimeRef {
649 unsafe {
650 let date = X509_getm_notAfter(self.as_ptr());
651 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
652 }
653 }
654
655 #[corresponds(X509_getm_notBefore)]
657 pub fn not_before(&self) -> &Asn1TimeRef {
658 unsafe {
659 let date = X509_getm_notBefore(self.as_ptr());
660 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
661 }
662 }
663
664 #[corresponds(X509_get0_signature)]
666 pub fn signature(&self) -> &Asn1BitStringRef {
667 unsafe {
668 let mut signature = ptr::null();
669 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
670 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
671 }
672 }
673
674 #[corresponds(X509_get0_signature)]
676 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
677 unsafe {
678 let mut algor = ptr::null();
679 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
680 X509AlgorithmRef::from_const_ptr_opt(algor)
681 .expect("signature algorithm must not be null")
682 }
683 }
684
685 #[corresponds(X509_get1_ocsp)]
688 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
689 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
690 }
691
692 #[corresponds(X509_check_issued)]
694 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
695 unsafe {
696 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
697 X509VerifyResult::from_raw(r)
698 }
699 }
700
701 #[corresponds(X509_get_version)]
706 #[cfg(any(ossl110, libressl, boringssl, awslc))]
707 #[allow(clippy::unnecessary_cast)]
708 pub fn version(&self) -> i32 {
709 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
710 }
711
712 #[corresponds(X509_verify)]
719 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
720 where
721 T: HasPublic,
722 {
723 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
724 }
725
726 #[corresponds(X509_get_serialNumber)]
728 pub fn serial_number(&self) -> &Asn1IntegerRef {
729 unsafe {
730 let r = ffi::X509_get_serialNumber(self.as_ptr());
731 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
732 }
733 }
734
735 #[corresponds(X509_alias_get0)]
741 pub fn alias(&self) -> Option<&[u8]> {
742 unsafe {
743 let mut len = 0;
744 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
745 if ptr.is_null() {
746 None
747 } else {
748 Some(util::from_raw_parts(ptr, len as usize))
749 }
750 }
751 }
752
753 to_pem! {
754 #[corresponds(PEM_write_bio_X509)]
758 to_pem,
759 ffi::PEM_write_bio_X509
760 }
761
762 to_der! {
763 #[corresponds(i2d_X509)]
765 to_der,
766 ffi::i2d_X509
767 }
768
769 to_pem! {
770 #[corresponds(X509_print)]
772 to_text,
773 ffi::X509_print
774 }
775}
776
777impl ToOwned for X509Ref {
778 type Owned = X509;
779
780 fn to_owned(&self) -> X509 {
781 unsafe {
782 X509_up_ref(self.as_ptr());
783 X509::from_ptr(self.as_ptr())
784 }
785 }
786}
787
788impl Ord for X509Ref {
789 fn cmp(&self, other: &Self) -> cmp::Ordering {
790 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
793 cmp.cmp(&0)
794 }
795}
796
797impl PartialOrd for X509Ref {
798 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
799 Some(self.cmp(other))
800 }
801}
802
803impl PartialOrd<X509> for X509Ref {
804 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
805 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
806 }
807}
808
809impl PartialEq for X509Ref {
810 fn eq(&self, other: &Self) -> bool {
811 self.cmp(other) == cmp::Ordering::Equal
812 }
813}
814
815impl PartialEq<X509> for X509Ref {
816 fn eq(&self, other: &X509) -> bool {
817 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
818 }
819}
820
821impl Eq for X509Ref {}
822
823impl X509 {
824 pub fn builder() -> Result<X509Builder, ErrorStack> {
826 X509Builder::new()
827 }
828
829 from_pem! {
830 #[corresponds(PEM_read_bio_X509)]
834 from_pem,
835 X509,
836 ffi::PEM_read_bio_X509
837 }
838
839 from_der! {
840 #[corresponds(d2i_X509)]
842 from_der,
843 X509,
844 ffi::d2i_X509
845 }
846
847 #[corresponds(PEM_read_bio_X509)]
849 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
850 unsafe {
851 ffi::init();
852 let bio = MemBioSlice::new(pem)?;
853
854 let mut certs = vec![];
855 loop {
856 let r =
857 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
858 if r.is_null() {
859 let e = ErrorStack::get();
860
861 if let Some(err) = e.errors().last() {
862 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
863 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
864 {
865 break;
866 }
867 }
868
869 return Err(e);
870 } else {
871 certs.push(X509(r));
872 }
873 }
874
875 Ok(certs)
876 }
877 }
878}
879
880impl Clone for X509 {
881 fn clone(&self) -> X509 {
882 X509Ref::to_owned(self)
883 }
884}
885
886impl fmt::Debug for X509 {
887 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
888 let serial = match &self.serial_number().to_bn() {
889 Ok(bn) => match bn.to_hex_str() {
890 Ok(hex) => hex.to_string(),
891 Err(_) => "".to_string(),
892 },
893 Err(_) => "".to_string(),
894 };
895 let mut debug_struct = formatter.debug_struct("X509");
896 debug_struct.field("serial_number", &serial);
897 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
898 debug_struct.field("issuer", &self.issuer_name());
899 debug_struct.field("subject", &self.subject_name());
900 if let Some(subject_alt_names) = &self.subject_alt_names() {
901 debug_struct.field("subject_alt_names", subject_alt_names);
902 }
903 debug_struct.field("not_before", &self.not_before());
904 debug_struct.field("not_after", &self.not_after());
905
906 if let Ok(public_key) = &self.public_key() {
907 debug_struct.field("public_key", public_key);
908 };
909 debug_struct.finish()
912 }
913}
914
915impl AsRef<X509Ref> for X509Ref {
916 fn as_ref(&self) -> &X509Ref {
917 self
918 }
919}
920
921impl Stackable for X509 {
922 type StackType = ffi::stack_st_X509;
923}
924
925impl Ord for X509 {
926 fn cmp(&self, other: &Self) -> cmp::Ordering {
927 X509Ref::cmp(self, other)
928 }
929}
930
931impl PartialOrd for X509 {
932 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
933 Some(self.cmp(other))
934 }
935}
936
937impl PartialOrd<X509Ref> for X509 {
938 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
939 X509Ref::partial_cmp(self, other)
940 }
941}
942
943impl PartialEq for X509 {
944 fn eq(&self, other: &Self) -> bool {
945 X509Ref::eq(self, other)
946 }
947}
948
949impl PartialEq<X509Ref> for X509 {
950 fn eq(&self, other: &X509Ref) -> bool {
951 X509Ref::eq(self, other)
952 }
953}
954
955impl Eq for X509 {}
956
957pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
959
960impl X509v3Context<'_> {
961 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
962 &self.0 as *const _ as *mut _
963 }
964}
965
966foreign_type_and_impl_send_sync! {
967 type CType = ffi::X509_EXTENSION;
968 fn drop = ffi::X509_EXTENSION_free;
969
970 pub struct X509Extension;
972 pub struct X509ExtensionRef;
974}
975
976impl Stackable for X509Extension {
977 type StackType = ffi::stack_st_X509_EXTENSION;
978}
979
980impl X509Extension {
981 #[deprecated(
995 note = "Use x509::extension types or new_from_der instead",
996 since = "0.10.51"
997 )]
998 pub fn new(
999 conf: Option<&ConfRef>,
1000 context: Option<&X509v3Context<'_>>,
1001 name: &str,
1002 value: &str,
1003 ) -> Result<X509Extension, ErrorStack> {
1004 let name = CString::new(name).unwrap();
1005 let value = CString::new(value).unwrap();
1006 let mut ctx;
1007 unsafe {
1008 ffi::init();
1009 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1010 let context_ptr = match context {
1011 Some(c) => c.as_ptr(),
1012 None => {
1013 ctx = mem::zeroed();
1014
1015 ffi::X509V3_set_ctx(
1016 &mut ctx,
1017 ptr::null_mut(),
1018 ptr::null_mut(),
1019 ptr::null_mut(),
1020 ptr::null_mut(),
1021 0,
1022 );
1023 &mut ctx
1024 }
1025 };
1026 let name = name.as_ptr() as *mut _;
1027 let value = value.as_ptr() as *mut _;
1028
1029 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
1030 }
1031 }
1032
1033 #[deprecated(
1047 note = "Use x509::extension types or new_from_der instead",
1048 since = "0.10.51"
1049 )]
1050 pub fn new_nid(
1051 conf: Option<&ConfRef>,
1052 context: Option<&X509v3Context<'_>>,
1053 name: Nid,
1054 value: &str,
1055 ) -> Result<X509Extension, ErrorStack> {
1056 let value = CString::new(value).unwrap();
1057 let mut ctx;
1058 unsafe {
1059 ffi::init();
1060 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1061 let context_ptr = match context {
1062 Some(c) => c.as_ptr(),
1063 None => {
1064 ctx = mem::zeroed();
1065
1066 ffi::X509V3_set_ctx(
1067 &mut ctx,
1068 ptr::null_mut(),
1069 ptr::null_mut(),
1070 ptr::null_mut(),
1071 ptr::null_mut(),
1072 0,
1073 );
1074 &mut ctx
1075 }
1076 };
1077 let name = name.as_raw();
1078 let value = value.as_ptr() as *mut _;
1079
1080 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1081 }
1082 }
1083
1084 pub fn new_from_der(
1094 oid: &Asn1ObjectRef,
1095 critical: bool,
1096 der_contents: &Asn1OctetStringRef,
1097 ) -> Result<X509Extension, ErrorStack> {
1098 unsafe {
1099 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1100 ptr::null_mut(),
1101 oid.as_ptr(),
1102 critical as _,
1103 der_contents.as_ptr(),
1104 ))
1105 .map(X509Extension)
1106 }
1107 }
1108
1109 pub fn new_subject_alt_name(
1111 stack: Stack<GeneralName>,
1112 critical: bool,
1113 ) -> Result<X509Extension, ErrorStack> {
1114 unsafe { Self::new_internal(Nid::SUBJECT_ALT_NAME, critical, stack.as_ptr().cast()) }
1115 }
1116
1117 pub(crate) unsafe fn new_internal(
1118 nid: Nid,
1119 critical: bool,
1120 value: *mut c_void,
1121 ) -> Result<X509Extension, ErrorStack> {
1122 ffi::init();
1123 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1124 }
1125
1126 #[cfg(not(libressl390))]
1132 #[corresponds(X509V3_EXT_add_alias)]
1133 #[deprecated(
1134 note = "Use x509::extension types or new_from_der and then this is not necessary",
1135 since = "0.10.51"
1136 )]
1137 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1138 ffi::init();
1139 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1140 }
1141}
1142
1143impl X509ExtensionRef {
1144 to_der! {
1145 #[corresponds(i2d_X509_EXTENSION)]
1147 to_der,
1148 ffi::i2d_X509_EXTENSION
1149 }
1150}
1151
1152pub struct X509NameBuilder(X509Name);
1154
1155impl X509NameBuilder {
1156 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1158 unsafe {
1159 ffi::init();
1160 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1161 }
1162 }
1163
1164 #[corresponds(X509_NAME_add_entry)]
1166 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1167 unsafe {
1168 cvt(ffi::X509_NAME_add_entry(
1169 self.0.as_ptr(),
1170 ne.as_ptr(),
1171 -1,
1172 0,
1173 ))
1174 .map(|_| ())
1175 }
1176 }
1177
1178 #[corresponds(X509_NAME_add_entry_by_txt)]
1180 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1181 unsafe {
1182 let field = CString::new(field).unwrap();
1183 assert!(value.len() <= crate::SLenType::MAX as usize);
1184 cvt(ffi::X509_NAME_add_entry_by_txt(
1185 self.0.as_ptr(),
1186 field.as_ptr() as *mut _,
1187 ffi::MBSTRING_UTF8,
1188 value.as_ptr(),
1189 value.len() as crate::SLenType,
1190 -1,
1191 0,
1192 ))
1193 .map(|_| ())
1194 }
1195 }
1196
1197 #[corresponds(X509_NAME_add_entry_by_txt)]
1199 pub fn append_entry_by_text_with_type(
1200 &mut self,
1201 field: &str,
1202 value: &str,
1203 ty: Asn1Type,
1204 ) -> Result<(), ErrorStack> {
1205 unsafe {
1206 let field = CString::new(field).unwrap();
1207 assert!(value.len() <= crate::SLenType::MAX as usize);
1208 cvt(ffi::X509_NAME_add_entry_by_txt(
1209 self.0.as_ptr(),
1210 field.as_ptr() as *mut _,
1211 ty.as_raw(),
1212 value.as_ptr(),
1213 value.len() as crate::SLenType,
1214 -1,
1215 0,
1216 ))
1217 .map(|_| ())
1218 }
1219 }
1220
1221 #[corresponds(X509_NAME_add_entry_by_NID)]
1223 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1224 unsafe {
1225 assert!(value.len() <= crate::SLenType::MAX as usize);
1226 cvt(ffi::X509_NAME_add_entry_by_NID(
1227 self.0.as_ptr(),
1228 field.as_raw(),
1229 ffi::MBSTRING_UTF8,
1230 value.as_ptr() as *mut _,
1231 value.len() as crate::SLenType,
1232 -1,
1233 0,
1234 ))
1235 .map(|_| ())
1236 }
1237 }
1238
1239 #[corresponds(X509_NAME_add_entry_by_NID)]
1241 pub fn append_entry_by_nid_with_type(
1242 &mut self,
1243 field: Nid,
1244 value: &str,
1245 ty: Asn1Type,
1246 ) -> Result<(), ErrorStack> {
1247 unsafe {
1248 assert!(value.len() <= crate::SLenType::MAX as usize);
1249 cvt(ffi::X509_NAME_add_entry_by_NID(
1250 self.0.as_ptr(),
1251 field.as_raw(),
1252 ty.as_raw(),
1253 value.as_ptr() as *mut _,
1254 value.len() as crate::SLenType,
1255 -1,
1256 0,
1257 ))
1258 .map(|_| ())
1259 }
1260 }
1261
1262 pub fn build(self) -> X509Name {
1264 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1268 }
1269}
1270
1271foreign_type_and_impl_send_sync! {
1272 type CType = ffi::X509_NAME;
1273 fn drop = ffi::X509_NAME_free;
1274
1275 pub struct X509Name;
1277 pub struct X509NameRef;
1279}
1280
1281impl X509Name {
1282 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1284 X509NameBuilder::new()
1285 }
1286
1287 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1291 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1292 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1293 }
1294
1295 from_der! {
1296 from_der,
1302 X509Name,
1303 ffi::d2i_X509_NAME
1304 }
1305}
1306
1307impl Stackable for X509Name {
1308 type StackType = ffi::stack_st_X509_NAME;
1309}
1310
1311impl X509NameRef {
1312 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1314 X509NameEntries {
1315 name: self,
1316 nid: Some(nid),
1317 loc: -1,
1318 }
1319 }
1320
1321 pub fn entries(&self) -> X509NameEntries<'_> {
1323 X509NameEntries {
1324 name: self,
1325 nid: None,
1326 loc: -1,
1327 }
1328 }
1329
1330 #[corresponds(X509_NAME_cmp)]
1337 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1338 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1339 if cfg!(ossl300) && cmp == -2 {
1340 return Err(ErrorStack::get());
1341 }
1342 Ok(cmp.cmp(&0))
1343 }
1344
1345 #[corresponds(X509_NAME_dup)]
1347 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1348 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1349 }
1350
1351 to_der! {
1352 to_der,
1358 ffi::i2d_X509_NAME
1359 }
1360
1361 digest! {
1362 digest,
1368 ffi::X509_NAME_digest
1369 }
1370}
1371
1372impl fmt::Debug for X509NameRef {
1373 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1374 formatter.debug_list().entries(self.entries()).finish()
1375 }
1376}
1377
1378pub struct X509NameEntries<'a> {
1380 name: &'a X509NameRef,
1381 nid: Option<Nid>,
1382 loc: c_int,
1383}
1384
1385impl<'a> Iterator for X509NameEntries<'a> {
1386 type Item = &'a X509NameEntryRef;
1387
1388 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1389 unsafe {
1390 match self.nid {
1391 Some(nid) => {
1392 self.loc =
1394 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1395 if self.loc == -1 {
1396 return None;
1397 }
1398 }
1399 None => {
1400 self.loc += 1;
1402 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1403 return None;
1404 }
1405 }
1406 }
1407
1408 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1409
1410 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1411 }
1412 }
1413}
1414
1415foreign_type_and_impl_send_sync! {
1416 type CType = ffi::X509_NAME_ENTRY;
1417 fn drop = ffi::X509_NAME_ENTRY_free;
1418
1419 pub struct X509NameEntry;
1421 pub struct X509NameEntryRef;
1423}
1424
1425impl X509NameEntryRef {
1426 #[corresponds(X509_NAME_ENTRY_get_data)]
1428 pub fn data(&self) -> &Asn1StringRef {
1429 unsafe {
1430 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1431 Asn1StringRef::from_ptr(data)
1432 }
1433 }
1434
1435 #[corresponds(X509_NAME_ENTRY_get_object)]
1438 pub fn object(&self) -> &Asn1ObjectRef {
1439 unsafe {
1440 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1441 Asn1ObjectRef::from_ptr(object)
1442 }
1443 }
1444}
1445
1446impl fmt::Debug for X509NameEntryRef {
1447 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1448 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1449 }
1450}
1451
1452foreign_type_and_impl_send_sync! {
1453 type CType = ffi::X509_PUBKEY;
1454 fn drop = ffi::X509_PUBKEY_free;
1455
1456 pub struct X509Pubkey;
1458 pub struct X509PubkeyRef;
1460}
1461
1462impl X509Pubkey {
1463 from_der! {
1464 from_der,
1470 X509Pubkey,
1471 ffi::d2i_X509_PUBKEY
1472 }
1473
1474 pub fn from_pubkey<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
1480 where
1481 T: HasPublic,
1482 {
1483 let mut p = ptr::null_mut();
1484 unsafe {
1485 cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?;
1486 }
1487 Ok(X509Pubkey(p))
1488 }
1489}
1490
1491impl X509PubkeyRef {
1492 #[corresponds(X509_PUBKEY_dup)]
1494 #[cfg(ossl300)]
1495 pub fn to_owned(&self) -> Result<X509Pubkey, ErrorStack> {
1496 unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) }
1497 }
1498
1499 to_der! {
1500 to_der,
1506 ffi::i2d_X509_PUBKEY
1507 }
1508
1509 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1515 unsafe {
1516 let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
1517 Ok(PKey::from_ptr(key))
1518 }
1519 }
1520
1521 pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> {
1527 unsafe {
1528 let mut pk = ptr::null_mut() as *const c_uchar;
1529 let mut pkt_len: c_int = 0;
1530 cvt(ffi::X509_PUBKEY_get0_param(
1531 ptr::null_mut(),
1532 &mut pk as *mut _,
1533 &mut pkt_len as *mut _,
1534 ptr::null_mut(),
1535 self.as_ptr(),
1536 ))?;
1537
1538 Ok(util::from_raw_parts(pk, pkt_len as usize))
1539 }
1540 }
1541}
1542
1543pub struct X509ReqBuilder(X509Req);
1545
1546impl X509ReqBuilder {
1547 #[corresponds(X509_REQ_new)]
1549 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1550 unsafe {
1551 ffi::init();
1552 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1553 }
1554 }
1555
1556 #[corresponds(X509_REQ_set_version)]
1558 #[allow(clippy::useless_conversion)]
1559 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1560 unsafe {
1561 cvt(ffi::X509_REQ_set_version(
1562 self.0.as_ptr(),
1563 version as c_long,
1564 ))
1565 .map(|_| ())
1566 }
1567 }
1568
1569 #[corresponds(X509_REQ_set_subject_name)]
1571 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1572 unsafe {
1573 cvt(ffi::X509_REQ_set_subject_name(
1574 self.0.as_ptr(),
1575 subject_name.as_ptr(),
1576 ))
1577 .map(|_| ())
1578 }
1579 }
1580
1581 #[corresponds(X509_REQ_set_pubkey)]
1583 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1584 where
1585 T: HasPublic,
1586 {
1587 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1588 }
1589
1590 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1593 unsafe {
1594 let mut ctx = mem::zeroed();
1595
1596 ffi::X509V3_set_ctx(
1597 &mut ctx,
1598 ptr::null_mut(),
1599 ptr::null_mut(),
1600 self.0.as_ptr(),
1601 ptr::null_mut(),
1602 0,
1603 );
1604
1605 if let Some(conf) = conf {
1607 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1608 }
1609
1610 X509v3Context(ctx, PhantomData)
1611 }
1612 }
1613
1614 pub fn add_extensions(
1616 &mut self,
1617 extensions: &StackRef<X509Extension>,
1618 ) -> Result<(), ErrorStack> {
1619 unsafe {
1620 cvt(ffi::X509_REQ_add_extensions(
1621 self.0.as_ptr(),
1622 extensions.as_ptr(),
1623 ))
1624 .map(|_| ())
1625 }
1626 }
1627
1628 #[corresponds(X509_REQ_sign)]
1630 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1631 where
1632 T: HasPrivate,
1633 {
1634 unsafe {
1635 cvt(ffi::X509_REQ_sign(
1636 self.0.as_ptr(),
1637 key.as_ptr(),
1638 hash.as_ptr(),
1639 ))
1640 .map(|_| ())
1641 }
1642 }
1643
1644 pub fn build(self) -> X509Req {
1646 self.0
1647 }
1648}
1649
1650foreign_type_and_impl_send_sync! {
1651 type CType = ffi::X509_REQ;
1652 fn drop = ffi::X509_REQ_free;
1653
1654 pub struct X509Req;
1656 pub struct X509ReqRef;
1658}
1659
1660impl X509Req {
1661 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1663 X509ReqBuilder::new()
1664 }
1665
1666 from_pem! {
1667 from_pem,
1675 X509Req,
1676 ffi::PEM_read_bio_X509_REQ
1677 }
1678
1679 from_der! {
1680 from_der,
1686 X509Req,
1687 ffi::d2i_X509_REQ
1688 }
1689}
1690
1691impl X509ReqRef {
1692 to_pem! {
1693 to_pem,
1701 ffi::PEM_write_bio_X509_REQ
1702 }
1703
1704 to_der! {
1705 to_der,
1711 ffi::i2d_X509_REQ
1712 }
1713
1714 to_pem! {
1715 #[corresponds(X509_Req_print)]
1717 to_text,
1718 ffi::X509_REQ_print
1719 }
1720
1721 digest! {
1722 digest,
1728 ffi::X509_REQ_digest
1729 }
1730
1731 #[corresponds(X509_REQ_get_version)]
1733 #[allow(clippy::unnecessary_cast)]
1734 pub fn version(&self) -> i32 {
1735 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1736 }
1737
1738 #[corresponds(X509_REQ_get_subject_name)]
1740 pub fn subject_name(&self) -> &X509NameRef {
1741 unsafe {
1742 let name = X509_REQ_get_subject_name(self.as_ptr());
1743 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1744 }
1745 }
1746
1747 #[corresponds(X509_REQ_get_pubkey)]
1749 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1750 unsafe {
1751 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1752 Ok(PKey::from_ptr(key))
1753 }
1754 }
1755
1756 #[cfg(ossl110)]
1762 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
1763 unsafe {
1764 let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?;
1765 Ok(X509PubkeyRef::from_ptr(key))
1766 }
1767 }
1768
1769 #[corresponds(X509_REQ_verify)]
1773 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1774 where
1775 T: HasPublic,
1776 {
1777 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1778 }
1779
1780 #[corresponds(X509_REQ_get_extensions)]
1782 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1783 unsafe {
1784 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1785 Ok(Stack::from_ptr(extensions))
1786 }
1787 }
1788}
1789
1790#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1792pub struct CrlReason(c_int);
1793
1794#[allow(missing_docs)] impl CrlReason {
1796 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1797 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1798 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1799 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1800 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1801 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1802 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1803 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1804 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1805 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1806
1807 pub const fn from_raw(value: c_int) -> Self {
1809 CrlReason(value)
1810 }
1811
1812 pub const fn as_raw(&self) -> c_int {
1814 self.0
1815 }
1816}
1817
1818foreign_type_and_impl_send_sync! {
1819 type CType = ffi::X509_REVOKED;
1820 fn drop = ffi::X509_REVOKED_free;
1821
1822 pub struct X509Revoked;
1824 pub struct X509RevokedRef;
1826}
1827
1828impl Stackable for X509Revoked {
1829 type StackType = ffi::stack_st_X509_REVOKED;
1830}
1831
1832impl X509Revoked {
1833 from_der! {
1834 #[corresponds(d2i_X509_REVOKED)]
1836 from_der,
1837 X509Revoked,
1838 ffi::d2i_X509_REVOKED
1839 }
1840}
1841
1842impl X509RevokedRef {
1843 to_der! {
1844 #[corresponds(d2i_X509_REVOKED)]
1846 to_der,
1847 ffi::i2d_X509_REVOKED
1848 }
1849
1850 #[corresponds(X509_REVOKED_dup)]
1852 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1853 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1854 }
1855
1856 #[corresponds(X509_REVOKED_get0_revocationDate)]
1858 pub fn revocation_date(&self) -> &Asn1TimeRef {
1859 unsafe {
1860 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1861 assert!(!r.is_null());
1862 Asn1TimeRef::from_ptr(r as *mut _)
1863 }
1864 }
1865
1866 #[corresponds(X509_REVOKED_get0_serialNumber)]
1868 pub fn serial_number(&self) -> &Asn1IntegerRef {
1869 unsafe {
1870 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1871 assert!(!r.is_null());
1872 Asn1IntegerRef::from_ptr(r as *mut _)
1873 }
1874 }
1875
1876 #[corresponds(X509_REVOKED_get_ext_d2i)]
1880 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1881 let mut critical = -1;
1882 let out = unsafe {
1883 let ext = ffi::X509_REVOKED_get_ext_d2i(
1885 self.as_ptr(),
1886 T::NID.as_raw(),
1887 &mut critical as *mut _,
1888 ptr::null_mut(),
1889 );
1890 T::Output::from_ptr_opt(ext as *mut _)
1893 };
1894 match (critical, out) {
1895 (0, Some(out)) => Ok(Some((false, out))),
1896 (1, Some(out)) => Ok(Some((true, out))),
1897 (-1 | -2, _) => Ok(None),
1899 (0 | 1, None) => Err(ErrorStack::get()),
1902 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1903 }
1904 }
1905}
1906
1907pub enum ReasonCode {}
1910
1911unsafe impl ExtensionType for ReasonCode {
1914 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1915
1916 type Output = Asn1Enumerated;
1917}
1918
1919pub enum CertificateIssuer {}
1922
1923unsafe impl ExtensionType for CertificateIssuer {
1926 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1927
1928 type Output = Stack<GeneralName>;
1929}
1930
1931pub enum AuthorityInformationAccess {}
1933
1934unsafe impl ExtensionType for AuthorityInformationAccess {
1937 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1938
1939 type Output = Stack<AccessDescription>;
1940}
1941
1942foreign_type_and_impl_send_sync! {
1943 type CType = ffi::X509_CRL;
1944 fn drop = ffi::X509_CRL_free;
1945
1946 pub struct X509Crl;
1948 pub struct X509CrlRef;
1950}
1951
1952pub enum CrlStatus<'a> {
1958 NotRevoked,
1960 Revoked(&'a X509RevokedRef),
1962 RemoveFromCrl(&'a X509RevokedRef),
1967}
1968
1969impl<'a> CrlStatus<'a> {
1970 unsafe fn from_ffi_status(
1975 status: c_int,
1976 revoked_entry: *mut ffi::X509_REVOKED,
1977 ) -> CrlStatus<'a> {
1978 match status {
1979 0 => CrlStatus::NotRevoked,
1980 1 => {
1981 assert!(!revoked_entry.is_null());
1982 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1983 }
1984 2 => {
1985 assert!(!revoked_entry.is_null());
1986 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1987 }
1988 _ => unreachable!(
1989 "{}",
1990 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1991 ),
1992 }
1993 }
1994}
1995
1996impl X509Crl {
1997 from_pem! {
1998 #[corresponds(PEM_read_bio_X509_CRL)]
2002 from_pem,
2003 X509Crl,
2004 ffi::PEM_read_bio_X509_CRL
2005 }
2006
2007 from_der! {
2008 #[corresponds(d2i_X509_CRL)]
2010 from_der,
2011 X509Crl,
2012 ffi::d2i_X509_CRL
2013 }
2014}
2015
2016impl X509CrlRef {
2017 to_pem! {
2018 #[corresponds(PEM_write_bio_X509_CRL)]
2022 to_pem,
2023 ffi::PEM_write_bio_X509_CRL
2024 }
2025
2026 to_der! {
2027 #[corresponds(i2d_X509_CRL)]
2029 to_der,
2030 ffi::i2d_X509_CRL
2031 }
2032
2033 digest! {
2034 digest,
2040 ffi::X509_CRL_digest
2041 }
2042
2043 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2045 unsafe {
2046 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2047 if revoked.is_null() {
2048 None
2049 } else {
2050 Some(StackRef::from_ptr(revoked))
2051 }
2052 }
2053 }
2054
2055 #[corresponds(X509_CRL_get0_lastUpdate)]
2057 pub fn last_update(&self) -> &Asn1TimeRef {
2058 unsafe {
2059 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2060 assert!(!date.is_null());
2061 Asn1TimeRef::from_ptr(date as *mut _)
2062 }
2063 }
2064
2065 #[corresponds(X509_CRL_get0_nextUpdate)]
2069 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2070 unsafe {
2071 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2072 Asn1TimeRef::from_const_ptr_opt(date)
2073 }
2074 }
2075
2076 #[corresponds(X509_CRL_get0_by_serial)]
2078 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2079 unsafe {
2080 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2081 let status =
2082 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2083 CrlStatus::from_ffi_status(status, ret)
2084 }
2085 }
2086
2087 #[corresponds(X509_CRL_get0_by_cert)]
2089 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2090 unsafe {
2091 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2092 let status =
2093 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2094 CrlStatus::from_ffi_status(status, ret)
2095 }
2096 }
2097
2098 #[corresponds(X509_CRL_get_issuer)]
2100 pub fn issuer_name(&self) -> &X509NameRef {
2101 unsafe {
2102 let name = X509_CRL_get_issuer(self.as_ptr());
2103 assert!(!name.is_null());
2104 X509NameRef::from_ptr(name)
2105 }
2106 }
2107
2108 #[corresponds(X509_CRL_verify)]
2115 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2116 where
2117 T: HasPublic,
2118 {
2119 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2120 }
2121
2122 #[corresponds(X509_CRL_get_ext_d2i)]
2126 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2127 let mut critical = -1;
2128 let out = unsafe {
2129 let ext = ffi::X509_CRL_get_ext_d2i(
2131 self.as_ptr(),
2132 T::NID.as_raw(),
2133 &mut critical as *mut _,
2134 ptr::null_mut(),
2135 );
2136 T::Output::from_ptr_opt(ext as *mut _)
2139 };
2140 match (critical, out) {
2141 (0, Some(out)) => Ok(Some((false, out))),
2142 (1, Some(out)) => Ok(Some((true, out))),
2143 (-1 | -2, _) => Ok(None),
2145 (0 | 1, None) => Err(ErrorStack::get()),
2148 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2149 }
2150 }
2151}
2152
2153#[derive(Copy, Clone, PartialEq, Eq)]
2155pub struct X509VerifyResult(c_int);
2156
2157impl fmt::Debug for X509VerifyResult {
2158 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2159 fmt.debug_struct("X509VerifyResult")
2160 .field("code", &self.0)
2161 .field("error", &self.error_string())
2162 .finish()
2163 }
2164}
2165
2166impl fmt::Display for X509VerifyResult {
2167 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2168 fmt.write_str(self.error_string())
2169 }
2170}
2171
2172impl Error for X509VerifyResult {}
2173
2174impl X509VerifyResult {
2175 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2182 X509VerifyResult(err)
2183 }
2184
2185 #[allow(clippy::trivially_copy_pass_by_ref)]
2187 pub fn as_raw(&self) -> c_int {
2188 self.0
2189 }
2190
2191 #[corresponds(X509_verify_cert_error_string)]
2193 #[allow(clippy::trivially_copy_pass_by_ref)]
2194 pub fn error_string(&self) -> &'static str {
2195 ffi::init();
2196
2197 unsafe {
2198 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2199 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2200 }
2201 }
2202
2203 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2205 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2207 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2208}
2209
2210foreign_type_and_impl_send_sync! {
2211 type CType = ffi::GENERAL_NAME;
2212 fn drop = ffi::GENERAL_NAME_free;
2213
2214 pub struct GeneralName;
2216 pub struct GeneralNameRef;
2218}
2219
2220impl GeneralName {
2221 unsafe fn new(
2222 type_: c_int,
2223 asn1_type: Asn1Type,
2224 value: &[u8],
2225 ) -> Result<GeneralName, ErrorStack> {
2226 ffi::init();
2227 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2228 (*gn.as_ptr()).type_ = type_;
2229 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2230 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2231
2232 #[cfg(any(boringssl, awslc))]
2233 {
2234 (*gn.as_ptr()).d.ptr = s.cast();
2235 }
2236 #[cfg(not(any(boringssl, awslc)))]
2237 {
2238 (*gn.as_ptr()).d = s.cast();
2239 }
2240
2241 Ok(gn)
2242 }
2243
2244 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2245 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2246 }
2247
2248 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2249 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2250 }
2251
2252 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2253 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2254 }
2255
2256 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2257 match ip {
2258 IpAddr::V4(addr) => unsafe {
2259 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2260 },
2261 IpAddr::V6(addr) => unsafe {
2262 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2263 },
2264 }
2265 }
2266
2267 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2268 unsafe {
2269 ffi::init();
2270 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2271 (*gn).type_ = ffi::GEN_RID;
2272
2273 #[cfg(any(boringssl, awslc))]
2274 {
2275 (*gn).d.registeredID = oid.as_ptr();
2276 }
2277 #[cfg(not(any(boringssl, awslc)))]
2278 {
2279 (*gn).d = oid.as_ptr().cast();
2280 }
2281
2282 mem::forget(oid);
2283
2284 Ok(GeneralName::from_ptr(gn))
2285 }
2286 }
2287
2288 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2289 unsafe {
2290 ffi::init();
2291
2292 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2293 ptr::null_mut(),
2294 &mut value.as_ptr().cast(),
2295 value.len().try_into().unwrap(),
2296 ))?;
2297
2298 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2299 (*gn).type_ = ffi::GEN_OTHERNAME;
2300
2301 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2302 gn,
2303 oid.as_ptr().cast(),
2304 typ,
2305 )) {
2306 ffi::GENERAL_NAME_free(gn);
2307 return Err(e);
2308 }
2309
2310 mem::forget(oid);
2311
2312 Ok(GeneralName::from_ptr(gn))
2313 }
2314 }
2315
2316 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2317 unsafe {
2318 ffi::init();
2319 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2320 (*gn).type_ = ffi::GEN_DIRNAME;
2321
2322 let dup = match name.to_owned() {
2323 Ok(dup) => dup,
2324 Err(e) => {
2325 ffi::GENERAL_NAME_free(gn);
2326 return Err(e);
2327 }
2328 };
2329
2330 #[cfg(any(boringssl, awslc))]
2331 {
2332 (*gn).d.directoryName = dup.as_ptr();
2333 }
2334 #[cfg(not(any(boringssl, awslc)))]
2335 {
2336 (*gn).d = dup.as_ptr().cast();
2337 }
2338
2339 std::mem::forget(dup);
2340
2341 Ok(GeneralName::from_ptr(gn))
2342 }
2343 }
2344}
2345
2346impl GeneralNameRef {
2347 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2348 unsafe {
2349 if (*self.as_ptr()).type_ != ffi_type {
2350 return None;
2351 }
2352
2353 #[cfg(any(boringssl, awslc))]
2354 let d = (*self.as_ptr()).d.ptr;
2355 #[cfg(not(any(boringssl, awslc)))]
2356 let d = (*self.as_ptr()).d;
2357
2358 let ptr = ASN1_STRING_get0_data(d as *mut _);
2359 let len = ffi::ASN1_STRING_length(d as *mut _);
2360
2361 #[allow(clippy::unnecessary_cast)]
2362 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2363 str::from_utf8(slice).ok()
2367 }
2368 }
2369
2370 pub fn email(&self) -> Option<&str> {
2372 self.ia5_string(ffi::GEN_EMAIL)
2373 }
2374
2375 pub fn directory_name(&self) -> Option<&X509NameRef> {
2377 unsafe {
2378 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2379 return None;
2380 }
2381
2382 #[cfg(any(boringssl, awslc))]
2383 let d = (*self.as_ptr()).d.ptr;
2384 #[cfg(not(any(boringssl, awslc)))]
2385 let d = (*self.as_ptr()).d;
2386
2387 Some(X509NameRef::from_const_ptr(d as *const _))
2388 }
2389 }
2390
2391 pub fn dnsname(&self) -> Option<&str> {
2393 self.ia5_string(ffi::GEN_DNS)
2394 }
2395
2396 pub fn uri(&self) -> Option<&str> {
2398 self.ia5_string(ffi::GEN_URI)
2399 }
2400
2401 pub fn ipaddress(&self) -> Option<&[u8]> {
2403 unsafe {
2404 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2405 return None;
2406 }
2407 #[cfg(any(boringssl, awslc))]
2408 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2409 #[cfg(not(any(boringssl, awslc)))]
2410 let d = (*self.as_ptr()).d;
2411
2412 let ptr = ASN1_STRING_get0_data(d as *mut _);
2413 let len = ffi::ASN1_STRING_length(d as *mut _);
2414
2415 #[allow(clippy::unnecessary_cast)]
2416 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2417 }
2418 }
2419}
2420
2421impl fmt::Debug for GeneralNameRef {
2422 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2423 if let Some(email) = self.email() {
2424 formatter.write_str(email)
2425 } else if let Some(dnsname) = self.dnsname() {
2426 formatter.write_str(dnsname)
2427 } else if let Some(uri) = self.uri() {
2428 formatter.write_str(uri)
2429 } else if let Some(ipaddress) = self.ipaddress() {
2430 let address = <[u8; 16]>::try_from(ipaddress)
2431 .map(IpAddr::from)
2432 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2433 match address {
2434 Ok(a) => fmt::Debug::fmt(&a, formatter),
2435 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2436 }
2437 } else {
2438 formatter.write_str("(empty)")
2439 }
2440 }
2441}
2442
2443impl Stackable for GeneralName {
2444 type StackType = ffi::stack_st_GENERAL_NAME;
2445}
2446
2447foreign_type_and_impl_send_sync! {
2448 type CType = ffi::DIST_POINT;
2449 fn drop = ffi::DIST_POINT_free;
2450
2451 pub struct DistPoint;
2453 pub struct DistPointRef;
2455}
2456
2457impl DistPointRef {
2458 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2460 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2461 }
2462}
2463
2464foreign_type_and_impl_send_sync! {
2465 type CType = ffi::DIST_POINT_NAME;
2466 fn drop = ffi::DIST_POINT_NAME_free;
2467
2468 pub struct DistPointName;
2470 pub struct DistPointNameRef;
2472}
2473
2474impl DistPointNameRef {
2475 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2477 unsafe {
2478 if (*self.as_ptr()).type_ != 0 {
2479 return None;
2480 }
2481 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2482 }
2483 }
2484}
2485
2486impl Stackable for DistPoint {
2487 type StackType = ffi::stack_st_DIST_POINT;
2488}
2489
2490foreign_type_and_impl_send_sync! {
2491 type CType = ffi::BASIC_CONSTRAINTS;
2492 fn drop = ffi::BASIC_CONSTRAINTS_free;
2493
2494 pub struct BasicConstraints;
2496 pub struct BasicConstraintsRef;
2498}
2499
2500impl BasicConstraintsRef {
2501 pub fn ca(&self) -> bool {
2502 unsafe { (*(self.as_ptr())).ca != 0 }
2503 }
2504
2505 pub fn pathlen(&self) -> Option<&Asn1IntegerRef> {
2506 if !self.ca() {
2507 return None;
2508 }
2509 unsafe {
2510 let data = (*(self.as_ptr())).pathlen;
2511 Asn1IntegerRef::from_const_ptr_opt(data as _)
2512 }
2513 }
2514}
2515
2516foreign_type_and_impl_send_sync! {
2517 type CType = ffi::ACCESS_DESCRIPTION;
2518 fn drop = ffi::ACCESS_DESCRIPTION_free;
2519
2520 pub struct AccessDescription;
2522 pub struct AccessDescriptionRef;
2524}
2525
2526impl AccessDescriptionRef {
2527 pub fn method(&self) -> &Asn1ObjectRef {
2529 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2530 }
2531
2532 pub fn location(&self) -> &GeneralNameRef {
2534 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2535 }
2536}
2537
2538impl Stackable for AccessDescription {
2539 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2540}
2541
2542foreign_type_and_impl_send_sync! {
2543 type CType = ffi::X509_ALGOR;
2544 fn drop = ffi::X509_ALGOR_free;
2545
2546 pub struct X509Algorithm;
2548 pub struct X509AlgorithmRef;
2550}
2551
2552impl X509AlgorithmRef {
2553 pub fn object(&self) -> &Asn1ObjectRef {
2555 unsafe {
2556 let mut oid = ptr::null();
2557 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2558 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2559 }
2560 }
2561}
2562
2563foreign_type_and_impl_send_sync! {
2564 type CType = ffi::X509_OBJECT;
2565 fn drop = X509_OBJECT_free;
2566
2567 pub struct X509Object;
2569 pub struct X509ObjectRef;
2571}
2572
2573impl X509ObjectRef {
2574 pub fn x509(&self) -> Option<&X509Ref> {
2575 unsafe {
2576 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2577 X509Ref::from_const_ptr_opt(ptr)
2578 }
2579 }
2580}
2581
2582impl Stackable for X509Object {
2583 type StackType = ffi::stack_st_X509_OBJECT;
2584}
2585
2586use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2587
2588use ffi::{
2589 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2590 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2591};
2592
2593use ffi::X509_OBJECT_free;
2594use ffi::X509_OBJECT_get0_X509;
2595
2596use ffi::{
2597 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2598 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2599};
2600
2601#[derive(Copy, Clone, PartialEq, Eq)]
2602pub struct X509PurposeId(c_int);
2603
2604impl X509PurposeId {
2605 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2606 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2607 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2608 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2609 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2610 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2611 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2612 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2613 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2614 #[cfg(ossl320)]
2615 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2616
2617 pub fn from_raw(id: c_int) -> Self {
2619 X509PurposeId(id)
2620 }
2621
2622 pub fn as_raw(&self) -> c_int {
2624 self.0
2625 }
2626}
2627
2628pub struct X509PurposeRef(Opaque);
2630
2631impl ForeignTypeRef for X509PurposeRef {
2633 type CType = ffi::X509_PURPOSE;
2634}
2635
2636impl X509PurposeRef {
2637 #[allow(clippy::unnecessary_cast)]
2651 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2652 unsafe {
2653 let sname = CString::new(sname).unwrap();
2654 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2655 Ok(purpose)
2656 }
2657 }
2658 #[corresponds(X509_PURPOSE_get0)]
2661 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2662 unsafe {
2663 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2664 Ok(X509PurposeRef::from_const_ptr(ptr))
2665 }
2666 }
2667
2668 pub fn purpose(&self) -> X509PurposeId {
2679 unsafe {
2680 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2681 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2682 }
2683 }
2684}