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)]
717 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
718 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
719 }
720
721 #[corresponds(X509_check_issued)]
723 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
724 unsafe {
725 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
726 X509VerifyResult::from_raw(r)
727 }
728 }
729
730 #[corresponds(X509_get_version)]
735 #[cfg(any(ossl110, libressl, boringssl, awslc))]
736 #[allow(clippy::unnecessary_cast)]
737 pub fn version(&self) -> i32 {
738 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
739 }
740
741 #[corresponds(X509_verify)]
748 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
749 where
750 T: HasPublic,
751 {
752 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
753 }
754
755 #[corresponds(X509_get_serialNumber)]
757 pub fn serial_number(&self) -> &Asn1IntegerRef {
758 unsafe {
759 let r = ffi::X509_get_serialNumber(self.as_ptr());
760 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
761 }
762 }
763
764 #[corresponds(X509_alias_get0)]
770 pub fn alias(&self) -> Option<&[u8]> {
771 unsafe {
772 let mut len = 0;
773 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
774 if ptr.is_null() {
775 None
776 } else {
777 Some(util::from_raw_parts(ptr, len as usize))
778 }
779 }
780 }
781
782 to_pem! {
783 #[corresponds(PEM_write_bio_X509)]
787 to_pem,
788 ffi::PEM_write_bio_X509
789 }
790
791 to_der! {
792 #[corresponds(i2d_X509)]
794 to_der,
795 ffi::i2d_X509
796 }
797
798 to_pem! {
799 #[corresponds(X509_print)]
801 to_text,
802 ffi::X509_print
803 }
804}
805
806impl ToOwned for X509Ref {
807 type Owned = X509;
808
809 fn to_owned(&self) -> X509 {
810 unsafe {
811 X509_up_ref(self.as_ptr());
812 X509::from_ptr(self.as_ptr())
813 }
814 }
815}
816
817impl Ord for X509Ref {
818 fn cmp(&self, other: &Self) -> cmp::Ordering {
819 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
822 cmp.cmp(&0)
823 }
824}
825
826impl PartialOrd for X509Ref {
827 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
828 Some(self.cmp(other))
829 }
830}
831
832impl PartialOrd<X509> for X509Ref {
833 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
834 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
835 }
836}
837
838impl PartialEq for X509Ref {
839 fn eq(&self, other: &Self) -> bool {
840 self.cmp(other) == cmp::Ordering::Equal
841 }
842}
843
844impl PartialEq<X509> for X509Ref {
845 fn eq(&self, other: &X509) -> bool {
846 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
847 }
848}
849
850impl Eq for X509Ref {}
851
852impl X509 {
853 pub fn builder() -> Result<X509Builder, ErrorStack> {
855 X509Builder::new()
856 }
857
858 from_pem! {
859 #[corresponds(PEM_read_bio_X509)]
863 from_pem,
864 X509,
865 ffi::PEM_read_bio_X509
866 }
867
868 from_der! {
869 #[corresponds(d2i_X509)]
871 from_der,
872 X509,
873 ffi::d2i_X509
874 }
875
876 #[corresponds(PEM_read_bio_X509)]
878 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
879 unsafe {
880 ffi::init();
881 let bio = MemBioSlice::new(pem)?;
882
883 let mut certs = vec![];
884 loop {
885 let r =
886 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
887 if r.is_null() {
888 let e = ErrorStack::get();
889
890 if let Some(err) = e.errors().last() {
891 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
892 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
893 {
894 break;
895 }
896 }
897
898 return Err(e);
899 } else {
900 certs.push(X509(r));
901 }
902 }
903
904 Ok(certs)
905 }
906 }
907}
908
909impl Clone for X509 {
910 fn clone(&self) -> X509 {
911 X509Ref::to_owned(self)
912 }
913}
914
915impl fmt::Debug for X509 {
916 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
917 let serial = match &self.serial_number().to_bn() {
918 Ok(bn) => match bn.to_hex_str() {
919 Ok(hex) => hex.to_string(),
920 Err(_) => "".to_string(),
921 },
922 Err(_) => "".to_string(),
923 };
924 let mut debug_struct = formatter.debug_struct("X509");
925 debug_struct.field("serial_number", &serial);
926 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
927 debug_struct.field("issuer", &self.issuer_name());
928 debug_struct.field("subject", &self.subject_name());
929 if let Some(subject_alt_names) = &self.subject_alt_names() {
930 debug_struct.field("subject_alt_names", subject_alt_names);
931 }
932 debug_struct.field("not_before", &self.not_before());
933 debug_struct.field("not_after", &self.not_after());
934
935 if let Ok(public_key) = &self.public_key() {
936 debug_struct.field("public_key", public_key);
937 };
938 debug_struct.finish()
941 }
942}
943
944impl AsRef<X509Ref> for X509Ref {
945 fn as_ref(&self) -> &X509Ref {
946 self
947 }
948}
949
950impl Stackable for X509 {
951 type StackType = ffi::stack_st_X509;
952}
953
954impl Ord for X509 {
955 fn cmp(&self, other: &Self) -> cmp::Ordering {
956 X509Ref::cmp(self, other)
957 }
958}
959
960impl PartialOrd for X509 {
961 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
962 Some(self.cmp(other))
963 }
964}
965
966impl PartialOrd<X509Ref> for X509 {
967 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
968 X509Ref::partial_cmp(self, other)
969 }
970}
971
972impl PartialEq for X509 {
973 fn eq(&self, other: &Self) -> bool {
974 X509Ref::eq(self, other)
975 }
976}
977
978impl PartialEq<X509Ref> for X509 {
979 fn eq(&self, other: &X509Ref) -> bool {
980 X509Ref::eq(self, other)
981 }
982}
983
984impl Eq for X509 {}
985
986pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
988
989impl X509v3Context<'_> {
990 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
991 &self.0 as *const _ as *mut _
992 }
993}
994
995foreign_type_and_impl_send_sync! {
996 type CType = ffi::X509_EXTENSION;
997 fn drop = ffi::X509_EXTENSION_free;
998
999 pub struct X509Extension;
1001 pub struct X509ExtensionRef;
1003}
1004
1005impl Stackable for X509Extension {
1006 type StackType = ffi::stack_st_X509_EXTENSION;
1007}
1008
1009impl X509Extension {
1010 #[deprecated(
1024 note = "Use x509::extension types or new_from_der instead",
1025 since = "0.10.51"
1026 )]
1027 pub fn new(
1028 conf: Option<&ConfRef>,
1029 context: Option<&X509v3Context<'_>>,
1030 name: &str,
1031 value: &str,
1032 ) -> Result<X509Extension, ErrorStack> {
1033 let name = CString::new(name).unwrap();
1034 let value = CString::new(value).unwrap();
1035 let mut ctx;
1036 unsafe {
1037 ffi::init();
1038 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1039 let context_ptr = match context {
1040 Some(c) => c.as_ptr(),
1041 None => {
1042 ctx = mem::zeroed();
1043
1044 ffi::X509V3_set_ctx(
1045 &mut ctx,
1046 ptr::null_mut(),
1047 ptr::null_mut(),
1048 ptr::null_mut(),
1049 ptr::null_mut(),
1050 0,
1051 );
1052 &mut ctx
1053 }
1054 };
1055 let name = name.as_ptr() as *mut _;
1056 let value = value.as_ptr() as *mut _;
1057
1058 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
1059 }
1060 }
1061
1062 #[deprecated(
1076 note = "Use x509::extension types or new_from_der instead",
1077 since = "0.10.51"
1078 )]
1079 pub fn new_nid(
1080 conf: Option<&ConfRef>,
1081 context: Option<&X509v3Context<'_>>,
1082 name: Nid,
1083 value: &str,
1084 ) -> Result<X509Extension, ErrorStack> {
1085 let value = CString::new(value).unwrap();
1086 let mut ctx;
1087 unsafe {
1088 ffi::init();
1089 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1090 let context_ptr = match context {
1091 Some(c) => c.as_ptr(),
1092 None => {
1093 ctx = mem::zeroed();
1094
1095 ffi::X509V3_set_ctx(
1096 &mut ctx,
1097 ptr::null_mut(),
1098 ptr::null_mut(),
1099 ptr::null_mut(),
1100 ptr::null_mut(),
1101 0,
1102 );
1103 &mut ctx
1104 }
1105 };
1106 let name = name.as_raw();
1107 let value = value.as_ptr() as *mut _;
1108
1109 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1110 }
1111 }
1112
1113 pub fn new_from_der(
1123 oid: &Asn1ObjectRef,
1124 critical: bool,
1125 der_contents: &Asn1OctetStringRef,
1126 ) -> Result<X509Extension, ErrorStack> {
1127 unsafe {
1128 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1129 ptr::null_mut(),
1130 oid.as_ptr(),
1131 critical as _,
1132 der_contents.as_ptr(),
1133 ))
1134 .map(X509Extension)
1135 }
1136 }
1137
1138 pub fn new_subject_alt_name(
1140 stack: Stack<GeneralName>,
1141 critical: bool,
1142 ) -> Result<X509Extension, ErrorStack> {
1143 unsafe { Self::new_internal(Nid::SUBJECT_ALT_NAME, critical, stack.as_ptr().cast()) }
1144 }
1145
1146 pub(crate) unsafe fn new_internal(
1147 nid: Nid,
1148 critical: bool,
1149 value: *mut c_void,
1150 ) -> Result<X509Extension, ErrorStack> {
1151 ffi::init();
1152 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1153 }
1154
1155 #[cfg(not(libressl390))]
1161 #[corresponds(X509V3_EXT_add_alias)]
1162 #[deprecated(
1163 note = "Use x509::extension types or new_from_der and then this is not necessary",
1164 since = "0.10.51"
1165 )]
1166 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1167 ffi::init();
1168 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1169 }
1170}
1171
1172impl X509ExtensionRef {
1173 to_der! {
1174 #[corresponds(i2d_X509_EXTENSION)]
1176 to_der,
1177 ffi::i2d_X509_EXTENSION
1178 }
1179}
1180
1181pub struct X509NameBuilder(X509Name);
1183
1184impl X509NameBuilder {
1185 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1187 unsafe {
1188 ffi::init();
1189 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1190 }
1191 }
1192
1193 #[corresponds(X509_NAME_add_entry)]
1195 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1196 unsafe {
1197 cvt(ffi::X509_NAME_add_entry(
1198 self.0.as_ptr(),
1199 ne.as_ptr(),
1200 -1,
1201 0,
1202 ))
1203 .map(|_| ())
1204 }
1205 }
1206
1207 #[corresponds(X509_NAME_add_entry_by_txt)]
1209 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1210 unsafe {
1211 let field = CString::new(field).unwrap();
1212 assert!(value.len() <= crate::SLenType::MAX as usize);
1213 cvt(ffi::X509_NAME_add_entry_by_txt(
1214 self.0.as_ptr(),
1215 field.as_ptr() as *mut _,
1216 ffi::MBSTRING_UTF8,
1217 value.as_ptr(),
1218 value.len() as crate::SLenType,
1219 -1,
1220 0,
1221 ))
1222 .map(|_| ())
1223 }
1224 }
1225
1226 #[corresponds(X509_NAME_add_entry_by_txt)]
1228 pub fn append_entry_by_text_with_type(
1229 &mut self,
1230 field: &str,
1231 value: &str,
1232 ty: Asn1Type,
1233 ) -> Result<(), ErrorStack> {
1234 unsafe {
1235 let field = CString::new(field).unwrap();
1236 assert!(value.len() <= crate::SLenType::MAX as usize);
1237 cvt(ffi::X509_NAME_add_entry_by_txt(
1238 self.0.as_ptr(),
1239 field.as_ptr() as *mut _,
1240 ty.as_raw(),
1241 value.as_ptr(),
1242 value.len() as crate::SLenType,
1243 -1,
1244 0,
1245 ))
1246 .map(|_| ())
1247 }
1248 }
1249
1250 #[corresponds(X509_NAME_add_entry_by_NID)]
1252 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1253 unsafe {
1254 assert!(value.len() <= crate::SLenType::MAX as usize);
1255 cvt(ffi::X509_NAME_add_entry_by_NID(
1256 self.0.as_ptr(),
1257 field.as_raw(),
1258 ffi::MBSTRING_UTF8,
1259 value.as_ptr() as *mut _,
1260 value.len() as crate::SLenType,
1261 -1,
1262 0,
1263 ))
1264 .map(|_| ())
1265 }
1266 }
1267
1268 #[corresponds(X509_NAME_add_entry_by_NID)]
1270 pub fn append_entry_by_nid_with_type(
1271 &mut self,
1272 field: Nid,
1273 value: &str,
1274 ty: Asn1Type,
1275 ) -> Result<(), ErrorStack> {
1276 unsafe {
1277 assert!(value.len() <= crate::SLenType::MAX as usize);
1278 cvt(ffi::X509_NAME_add_entry_by_NID(
1279 self.0.as_ptr(),
1280 field.as_raw(),
1281 ty.as_raw(),
1282 value.as_ptr() as *mut _,
1283 value.len() as crate::SLenType,
1284 -1,
1285 0,
1286 ))
1287 .map(|_| ())
1288 }
1289 }
1290
1291 pub fn build(self) -> X509Name {
1293 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1297 }
1298}
1299
1300foreign_type_and_impl_send_sync! {
1301 type CType = ffi::X509_NAME;
1302 fn drop = ffi::X509_NAME_free;
1303
1304 pub struct X509Name;
1306 pub struct X509NameRef;
1308}
1309
1310impl X509Name {
1311 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1313 X509NameBuilder::new()
1314 }
1315
1316 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1320 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1321 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1322 }
1323
1324 from_der! {
1325 from_der,
1331 X509Name,
1332 ffi::d2i_X509_NAME
1333 }
1334}
1335
1336impl Stackable for X509Name {
1337 type StackType = ffi::stack_st_X509_NAME;
1338}
1339
1340impl X509NameRef {
1341 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1343 X509NameEntries {
1344 name: self,
1345 nid: Some(nid),
1346 loc: -1,
1347 }
1348 }
1349
1350 pub fn entries(&self) -> X509NameEntries<'_> {
1352 X509NameEntries {
1353 name: self,
1354 nid: None,
1355 loc: -1,
1356 }
1357 }
1358
1359 #[corresponds(X509_NAME_cmp)]
1366 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1367 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1368 if cfg!(ossl300) && cmp == -2 {
1369 return Err(ErrorStack::get());
1370 }
1371 Ok(cmp.cmp(&0))
1372 }
1373
1374 #[corresponds(X509_NAME_dup)]
1376 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1377 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1378 }
1379
1380 to_der! {
1381 to_der,
1387 ffi::i2d_X509_NAME
1388 }
1389
1390 digest! {
1391 digest,
1397 ffi::X509_NAME_digest
1398 }
1399}
1400
1401impl fmt::Debug for X509NameRef {
1402 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1403 formatter.debug_list().entries(self.entries()).finish()
1404 }
1405}
1406
1407pub struct X509NameEntries<'a> {
1409 name: &'a X509NameRef,
1410 nid: Option<Nid>,
1411 loc: c_int,
1412}
1413
1414impl<'a> Iterator for X509NameEntries<'a> {
1415 type Item = &'a X509NameEntryRef;
1416
1417 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1418 unsafe {
1419 match self.nid {
1420 Some(nid) => {
1421 self.loc =
1423 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1424 if self.loc == -1 {
1425 return None;
1426 }
1427 }
1428 None => {
1429 self.loc += 1;
1431 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1432 return None;
1433 }
1434 }
1435 }
1436
1437 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1438
1439 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1440 }
1441 }
1442}
1443
1444foreign_type_and_impl_send_sync! {
1445 type CType = ffi::X509_NAME_ENTRY;
1446 fn drop = ffi::X509_NAME_ENTRY_free;
1447
1448 pub struct X509NameEntry;
1450 pub struct X509NameEntryRef;
1452}
1453
1454impl X509NameEntryRef {
1455 #[corresponds(X509_NAME_ENTRY_get_data)]
1457 pub fn data(&self) -> &Asn1StringRef {
1458 unsafe {
1459 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1460 Asn1StringRef::from_ptr(data as *mut _)
1461 }
1462 }
1463
1464 #[corresponds(X509_NAME_ENTRY_get_object)]
1467 pub fn object(&self) -> &Asn1ObjectRef {
1468 unsafe {
1469 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1470 Asn1ObjectRef::from_ptr(object as *mut _)
1471 }
1472 }
1473}
1474
1475impl fmt::Debug for X509NameEntryRef {
1476 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1477 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1478 }
1479}
1480
1481foreign_type_and_impl_send_sync! {
1482 type CType = ffi::X509_PUBKEY;
1483 fn drop = ffi::X509_PUBKEY_free;
1484
1485 pub struct X509Pubkey;
1487 pub struct X509PubkeyRef;
1489}
1490
1491impl X509Pubkey {
1492 from_der! {
1493 from_der,
1499 X509Pubkey,
1500 ffi::d2i_X509_PUBKEY
1501 }
1502
1503 pub fn from_pubkey<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
1509 where
1510 T: HasPublic,
1511 {
1512 let mut p = ptr::null_mut();
1513 unsafe {
1514 cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?;
1515 }
1516 Ok(X509Pubkey(p))
1517 }
1518}
1519
1520impl X509PubkeyRef {
1521 #[corresponds(X509_PUBKEY_dup)]
1523 #[cfg(ossl300)]
1524 pub fn to_owned(&self) -> Result<X509Pubkey, ErrorStack> {
1525 unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) }
1526 }
1527
1528 to_der! {
1529 to_der,
1535 ffi::i2d_X509_PUBKEY
1536 }
1537
1538 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1544 unsafe {
1545 let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
1546 Ok(PKey::from_ptr(key))
1547 }
1548 }
1549
1550 pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> {
1556 unsafe {
1557 let mut pk = ptr::null_mut() as *const c_uchar;
1558 let mut pkt_len: c_int = 0;
1559 cvt(ffi::X509_PUBKEY_get0_param(
1560 ptr::null_mut(),
1561 &mut pk as *mut _,
1562 &mut pkt_len as *mut _,
1563 ptr::null_mut(),
1564 self.as_ptr(),
1565 ))?;
1566
1567 Ok(util::from_raw_parts(pk, pkt_len as usize))
1568 }
1569 }
1570}
1571
1572pub struct X509ReqBuilder(X509Req);
1574
1575impl X509ReqBuilder {
1576 #[corresponds(X509_REQ_new)]
1578 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1579 unsafe {
1580 ffi::init();
1581 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1582 }
1583 }
1584
1585 #[corresponds(X509_REQ_set_version)]
1587 #[allow(clippy::useless_conversion)]
1588 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1589 unsafe {
1590 cvt(ffi::X509_REQ_set_version(
1591 self.0.as_ptr(),
1592 version as c_long,
1593 ))
1594 .map(|_| ())
1595 }
1596 }
1597
1598 #[corresponds(X509_REQ_set_subject_name)]
1600 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1601 unsafe {
1602 cvt(ffi::X509_REQ_set_subject_name(
1603 self.0.as_ptr(),
1604 subject_name.as_ptr(),
1605 ))
1606 .map(|_| ())
1607 }
1608 }
1609
1610 #[corresponds(X509_REQ_set_pubkey)]
1612 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1613 where
1614 T: HasPublic,
1615 {
1616 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1617 }
1618
1619 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1622 unsafe {
1623 let mut ctx = mem::zeroed();
1624
1625 ffi::X509V3_set_ctx(
1626 &mut ctx,
1627 ptr::null_mut(),
1628 ptr::null_mut(),
1629 self.0.as_ptr(),
1630 ptr::null_mut(),
1631 0,
1632 );
1633
1634 if let Some(conf) = conf {
1636 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1637 }
1638
1639 X509v3Context(ctx, PhantomData)
1640 }
1641 }
1642
1643 pub fn add_extensions(
1645 &mut self,
1646 extensions: &StackRef<X509Extension>,
1647 ) -> Result<(), ErrorStack> {
1648 unsafe {
1649 cvt(ffi::X509_REQ_add_extensions(
1650 self.0.as_ptr(),
1651 extensions.as_ptr(),
1652 ))
1653 .map(|_| ())
1654 }
1655 }
1656
1657 #[corresponds(X509_REQ_sign)]
1659 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1660 where
1661 T: HasPrivate,
1662 {
1663 unsafe {
1664 cvt(ffi::X509_REQ_sign(
1665 self.0.as_ptr(),
1666 key.as_ptr(),
1667 hash.as_ptr(),
1668 ))
1669 .map(|_| ())
1670 }
1671 }
1672
1673 pub fn build(self) -> X509Req {
1675 self.0
1676 }
1677}
1678
1679foreign_type_and_impl_send_sync! {
1680 type CType = ffi::X509_REQ;
1681 fn drop = ffi::X509_REQ_free;
1682
1683 pub struct X509Req;
1685 pub struct X509ReqRef;
1687}
1688
1689impl X509Req {
1690 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1692 X509ReqBuilder::new()
1693 }
1694
1695 from_pem! {
1696 from_pem,
1704 X509Req,
1705 ffi::PEM_read_bio_X509_REQ
1706 }
1707
1708 from_der! {
1709 from_der,
1715 X509Req,
1716 ffi::d2i_X509_REQ
1717 }
1718}
1719
1720impl X509ReqRef {
1721 to_pem! {
1722 to_pem,
1730 ffi::PEM_write_bio_X509_REQ
1731 }
1732
1733 to_der! {
1734 to_der,
1740 ffi::i2d_X509_REQ
1741 }
1742
1743 to_pem! {
1744 #[corresponds(X509_Req_print)]
1746 to_text,
1747 ffi::X509_REQ_print
1748 }
1749
1750 digest! {
1751 digest,
1757 ffi::X509_REQ_digest
1758 }
1759
1760 #[corresponds(X509_REQ_get_version)]
1762 #[allow(clippy::unnecessary_cast)]
1763 pub fn version(&self) -> i32 {
1764 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1765 }
1766
1767 #[corresponds(X509_REQ_get_subject_name)]
1769 pub fn subject_name(&self) -> &X509NameRef {
1770 unsafe {
1771 let name = X509_REQ_get_subject_name(self.as_ptr());
1772 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1773 }
1774 }
1775
1776 #[corresponds(X509_REQ_get_pubkey)]
1778 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1779 unsafe {
1780 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1781 Ok(PKey::from_ptr(key))
1782 }
1783 }
1784
1785 #[cfg(ossl110)]
1791 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
1792 unsafe {
1793 let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?;
1794 Ok(X509PubkeyRef::from_ptr(key))
1795 }
1796 }
1797
1798 #[corresponds(X509_REQ_verify)]
1802 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1803 where
1804 T: HasPublic,
1805 {
1806 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1807 }
1808
1809 #[corresponds(X509_REQ_get_extensions)]
1811 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1812 unsafe {
1813 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1814 Ok(Stack::from_ptr(extensions))
1815 }
1816 }
1817}
1818
1819#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1821pub struct CrlReason(c_int);
1822
1823#[allow(missing_docs)] impl CrlReason {
1825 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1826 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1827 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1828 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1829 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1830 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1831 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1832 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1833 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1834 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1835
1836 pub const fn from_raw(value: c_int) -> Self {
1838 CrlReason(value)
1839 }
1840
1841 pub const fn as_raw(&self) -> c_int {
1843 self.0
1844 }
1845}
1846
1847foreign_type_and_impl_send_sync! {
1848 type CType = ffi::X509_REVOKED;
1849 fn drop = ffi::X509_REVOKED_free;
1850
1851 pub struct X509Revoked;
1853 pub struct X509RevokedRef;
1855}
1856
1857impl Stackable for X509Revoked {
1858 type StackType = ffi::stack_st_X509_REVOKED;
1859}
1860
1861impl X509Revoked {
1862 from_der! {
1863 #[corresponds(d2i_X509_REVOKED)]
1865 from_der,
1866 X509Revoked,
1867 ffi::d2i_X509_REVOKED
1868 }
1869}
1870
1871impl X509RevokedRef {
1872 to_der! {
1873 #[corresponds(d2i_X509_REVOKED)]
1875 to_der,
1876 ffi::i2d_X509_REVOKED
1877 }
1878
1879 #[corresponds(X509_REVOKED_dup)]
1881 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1882 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1883 }
1884
1885 #[corresponds(X509_REVOKED_get0_revocationDate)]
1887 pub fn revocation_date(&self) -> &Asn1TimeRef {
1888 unsafe {
1889 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1890 assert!(!r.is_null());
1891 Asn1TimeRef::from_ptr(r as *mut _)
1892 }
1893 }
1894
1895 #[corresponds(X509_REVOKED_get0_serialNumber)]
1897 pub fn serial_number(&self) -> &Asn1IntegerRef {
1898 unsafe {
1899 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1900 assert!(!r.is_null());
1901 Asn1IntegerRef::from_ptr(r as *mut _)
1902 }
1903 }
1904
1905 #[corresponds(X509_REVOKED_get_ext_d2i)]
1909 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1910 let mut critical = -1;
1911 let out = unsafe {
1912 let ext = ffi::X509_REVOKED_get_ext_d2i(
1914 self.as_ptr(),
1915 T::NID.as_raw(),
1916 &mut critical as *mut _,
1917 ptr::null_mut(),
1918 );
1919 T::Output::from_ptr_opt(ext as *mut _)
1922 };
1923 match (critical, out) {
1924 (0, Some(out)) => Ok(Some((false, out))),
1925 (1, Some(out)) => Ok(Some((true, out))),
1926 (-1 | -2, _) => Ok(None),
1928 (0 | 1, None) => Err(ErrorStack::get()),
1931 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1932 }
1933 }
1934}
1935
1936pub enum ReasonCode {}
1939
1940unsafe impl ExtensionType for ReasonCode {
1943 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1944
1945 type Output = Asn1Enumerated;
1946}
1947
1948pub enum CertificateIssuer {}
1951
1952unsafe impl ExtensionType for CertificateIssuer {
1955 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1956
1957 type Output = Stack<GeneralName>;
1958}
1959
1960pub enum AuthorityInformationAccess {}
1962
1963unsafe impl ExtensionType for AuthorityInformationAccess {
1966 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1967
1968 type Output = Stack<AccessDescription>;
1969}
1970
1971foreign_type_and_impl_send_sync! {
1972 type CType = ffi::X509_CRL;
1973 fn drop = ffi::X509_CRL_free;
1974
1975 pub struct X509Crl;
1977 pub struct X509CrlRef;
1979}
1980
1981pub enum CrlStatus<'a> {
1987 NotRevoked,
1989 Revoked(&'a X509RevokedRef),
1991 RemoveFromCrl(&'a X509RevokedRef),
1996}
1997
1998impl<'a> CrlStatus<'a> {
1999 unsafe fn from_ffi_status(
2004 status: c_int,
2005 revoked_entry: *mut ffi::X509_REVOKED,
2006 ) -> CrlStatus<'a> {
2007 match status {
2008 0 => CrlStatus::NotRevoked,
2009 1 => {
2010 assert!(!revoked_entry.is_null());
2011 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
2012 }
2013 2 => {
2014 assert!(!revoked_entry.is_null());
2015 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
2016 }
2017 _ => unreachable!(
2018 "{}",
2019 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
2020 ),
2021 }
2022 }
2023}
2024
2025impl X509Crl {
2026 from_pem! {
2027 #[corresponds(PEM_read_bio_X509_CRL)]
2031 from_pem,
2032 X509Crl,
2033 ffi::PEM_read_bio_X509_CRL
2034 }
2035
2036 from_der! {
2037 #[corresponds(d2i_X509_CRL)]
2039 from_der,
2040 X509Crl,
2041 ffi::d2i_X509_CRL
2042 }
2043}
2044
2045impl X509CrlRef {
2046 to_pem! {
2047 #[corresponds(PEM_write_bio_X509_CRL)]
2051 to_pem,
2052 ffi::PEM_write_bio_X509_CRL
2053 }
2054
2055 to_der! {
2056 #[corresponds(i2d_X509_CRL)]
2058 to_der,
2059 ffi::i2d_X509_CRL
2060 }
2061
2062 digest! {
2063 digest,
2069 ffi::X509_CRL_digest
2070 }
2071
2072 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2074 unsafe {
2075 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2076 if revoked.is_null() {
2077 None
2078 } else {
2079 Some(StackRef::from_ptr(revoked))
2080 }
2081 }
2082 }
2083
2084 #[corresponds(X509_CRL_get0_lastUpdate)]
2086 pub fn last_update(&self) -> &Asn1TimeRef {
2087 unsafe {
2088 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2089 assert!(!date.is_null());
2090 Asn1TimeRef::from_ptr(date as *mut _)
2091 }
2092 }
2093
2094 #[corresponds(X509_CRL_get0_nextUpdate)]
2098 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2099 unsafe {
2100 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2101 Asn1TimeRef::from_const_ptr_opt(date)
2102 }
2103 }
2104
2105 #[corresponds(X509_CRL_get0_by_serial)]
2107 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2108 unsafe {
2109 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2110 let status =
2111 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2112 CrlStatus::from_ffi_status(status, ret)
2113 }
2114 }
2115
2116 #[corresponds(X509_CRL_get0_by_cert)]
2118 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2119 unsafe {
2120 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2121 let status =
2122 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2123 CrlStatus::from_ffi_status(status, ret)
2124 }
2125 }
2126
2127 #[corresponds(X509_CRL_get_issuer)]
2129 pub fn issuer_name(&self) -> &X509NameRef {
2130 unsafe {
2131 let name = X509_CRL_get_issuer(self.as_ptr());
2132 assert!(!name.is_null());
2133 X509NameRef::from_ptr(name as *mut _)
2134 }
2135 }
2136
2137 #[corresponds(X509_CRL_verify)]
2144 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2145 where
2146 T: HasPublic,
2147 {
2148 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2149 }
2150
2151 #[corresponds(X509_CRL_get_ext_d2i)]
2155 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2156 let mut critical = -1;
2157 let out = unsafe {
2158 let ext = ffi::X509_CRL_get_ext_d2i(
2160 self.as_ptr(),
2161 T::NID.as_raw(),
2162 &mut critical as *mut _,
2163 ptr::null_mut(),
2164 );
2165 T::Output::from_ptr_opt(ext as *mut _)
2168 };
2169 match (critical, out) {
2170 (0, Some(out)) => Ok(Some((false, out))),
2171 (1, Some(out)) => Ok(Some((true, out))),
2172 (-1 | -2, _) => Ok(None),
2174 (0 | 1, None) => Err(ErrorStack::get()),
2177 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2178 }
2179 }
2180}
2181
2182#[derive(Copy, Clone, PartialEq, Eq)]
2184pub struct X509VerifyResult(c_int);
2185
2186impl fmt::Debug for X509VerifyResult {
2187 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2188 fmt.debug_struct("X509VerifyResult")
2189 .field("code", &self.0)
2190 .field("error", &self.error_string())
2191 .finish()
2192 }
2193}
2194
2195impl fmt::Display for X509VerifyResult {
2196 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2197 fmt.write_str(self.error_string())
2198 }
2199}
2200
2201impl Error for X509VerifyResult {}
2202
2203impl X509VerifyResult {
2204 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2211 X509VerifyResult(err)
2212 }
2213
2214 #[allow(clippy::trivially_copy_pass_by_ref)]
2216 pub fn as_raw(&self) -> c_int {
2217 self.0
2218 }
2219
2220 #[corresponds(X509_verify_cert_error_string)]
2222 #[allow(clippy::trivially_copy_pass_by_ref)]
2223 pub fn error_string(&self) -> &'static str {
2224 ffi::init();
2225
2226 unsafe {
2227 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2228 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2229 }
2230 }
2231
2232 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2234 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2236 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2237}
2238
2239foreign_type_and_impl_send_sync! {
2240 type CType = ffi::GENERAL_NAME;
2241 fn drop = ffi::GENERAL_NAME_free;
2242
2243 pub struct GeneralName;
2245 pub struct GeneralNameRef;
2247}
2248
2249impl GeneralName {
2250 unsafe fn new(
2251 type_: c_int,
2252 asn1_type: Asn1Type,
2253 value: &[u8],
2254 ) -> Result<GeneralName, ErrorStack> {
2255 ffi::init();
2256 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2257 (*gn.as_ptr()).type_ = type_;
2258 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2259 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2260
2261 #[cfg(any(boringssl, awslc))]
2262 {
2263 (*gn.as_ptr()).d.ptr = s.cast();
2264 }
2265 #[cfg(not(any(boringssl, awslc)))]
2266 {
2267 (*gn.as_ptr()).d = s.cast();
2268 }
2269
2270 Ok(gn)
2271 }
2272
2273 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2274 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2275 }
2276
2277 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2278 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2279 }
2280
2281 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2282 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2283 }
2284
2285 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2286 match ip {
2287 IpAddr::V4(addr) => unsafe {
2288 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2289 },
2290 IpAddr::V6(addr) => unsafe {
2291 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2292 },
2293 }
2294 }
2295
2296 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2297 unsafe {
2298 ffi::init();
2299 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2300 (*gn).type_ = ffi::GEN_RID;
2301
2302 #[cfg(any(boringssl, awslc))]
2303 {
2304 (*gn).d.registeredID = oid.as_ptr();
2305 }
2306 #[cfg(not(any(boringssl, awslc)))]
2307 {
2308 (*gn).d = oid.as_ptr().cast();
2309 }
2310
2311 mem::forget(oid);
2312
2313 Ok(GeneralName::from_ptr(gn))
2314 }
2315 }
2316
2317 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2318 unsafe {
2319 ffi::init();
2320
2321 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2322 ptr::null_mut(),
2323 &mut value.as_ptr().cast(),
2324 value.len().try_into().unwrap(),
2325 ))?;
2326
2327 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2328 (*gn).type_ = ffi::GEN_OTHERNAME;
2329
2330 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2331 gn,
2332 oid.as_ptr().cast(),
2333 typ,
2334 )) {
2335 ffi::GENERAL_NAME_free(gn);
2336 return Err(e);
2337 }
2338
2339 mem::forget(oid);
2340
2341 Ok(GeneralName::from_ptr(gn))
2342 }
2343 }
2344
2345 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2346 unsafe {
2347 ffi::init();
2348 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2349 (*gn).type_ = ffi::GEN_DIRNAME;
2350
2351 let dup = match name.to_owned() {
2352 Ok(dup) => dup,
2353 Err(e) => {
2354 ffi::GENERAL_NAME_free(gn);
2355 return Err(e);
2356 }
2357 };
2358
2359 #[cfg(any(boringssl, awslc))]
2360 {
2361 (*gn).d.directoryName = dup.as_ptr();
2362 }
2363 #[cfg(not(any(boringssl, awslc)))]
2364 {
2365 (*gn).d = dup.as_ptr().cast();
2366 }
2367
2368 std::mem::forget(dup);
2369
2370 Ok(GeneralName::from_ptr(gn))
2371 }
2372 }
2373}
2374
2375impl GeneralNameRef {
2376 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2377 unsafe {
2378 if (*self.as_ptr()).type_ != ffi_type {
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 let ptr = ASN1_STRING_get0_data(d as *mut _);
2388 let len = ffi::ASN1_STRING_length(d as *mut _);
2389
2390 #[allow(clippy::unnecessary_cast)]
2391 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2392 str::from_utf8(slice).ok()
2396 }
2397 }
2398
2399 pub fn email(&self) -> Option<&str> {
2401 self.ia5_string(ffi::GEN_EMAIL)
2402 }
2403
2404 pub fn directory_name(&self) -> Option<&X509NameRef> {
2406 unsafe {
2407 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2408 return None;
2409 }
2410
2411 #[cfg(any(boringssl, awslc))]
2412 let d = (*self.as_ptr()).d.ptr;
2413 #[cfg(not(any(boringssl, awslc)))]
2414 let d = (*self.as_ptr()).d;
2415
2416 Some(X509NameRef::from_const_ptr(d as *const _))
2417 }
2418 }
2419
2420 pub fn dnsname(&self) -> Option<&str> {
2422 self.ia5_string(ffi::GEN_DNS)
2423 }
2424
2425 pub fn uri(&self) -> Option<&str> {
2427 self.ia5_string(ffi::GEN_URI)
2428 }
2429
2430 pub fn ipaddress(&self) -> Option<&[u8]> {
2432 unsafe {
2433 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2434 return None;
2435 }
2436 #[cfg(any(boringssl, awslc))]
2437 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2438 #[cfg(not(any(boringssl, awslc)))]
2439 let d = (*self.as_ptr()).d;
2440
2441 let ptr = ASN1_STRING_get0_data(d as *mut _);
2442 let len = ffi::ASN1_STRING_length(d as *mut _);
2443
2444 #[allow(clippy::unnecessary_cast)]
2445 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2446 }
2447 }
2448}
2449
2450impl fmt::Debug for GeneralNameRef {
2451 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2452 if let Some(email) = self.email() {
2453 formatter.write_str(email)
2454 } else if let Some(dnsname) = self.dnsname() {
2455 formatter.write_str(dnsname)
2456 } else if let Some(uri) = self.uri() {
2457 formatter.write_str(uri)
2458 } else if let Some(ipaddress) = self.ipaddress() {
2459 let address = <[u8; 16]>::try_from(ipaddress)
2460 .map(IpAddr::from)
2461 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2462 match address {
2463 Ok(a) => fmt::Debug::fmt(&a, formatter),
2464 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2465 }
2466 } else {
2467 formatter.write_str("(empty)")
2468 }
2469 }
2470}
2471
2472impl Stackable for GeneralName {
2473 type StackType = ffi::stack_st_GENERAL_NAME;
2474}
2475
2476foreign_type_and_impl_send_sync! {
2477 type CType = ffi::DIST_POINT;
2478 fn drop = ffi::DIST_POINT_free;
2479
2480 pub struct DistPoint;
2482 pub struct DistPointRef;
2484}
2485
2486impl DistPointRef {
2487 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2489 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2490 }
2491}
2492
2493foreign_type_and_impl_send_sync! {
2494 type CType = ffi::DIST_POINT_NAME;
2495 fn drop = ffi::DIST_POINT_NAME_free;
2496
2497 pub struct DistPointName;
2499 pub struct DistPointNameRef;
2501}
2502
2503impl DistPointNameRef {
2504 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2506 unsafe {
2507 if (*self.as_ptr()).type_ != 0 {
2508 return None;
2509 }
2510 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2511 }
2512 }
2513}
2514
2515impl Stackable for DistPoint {
2516 type StackType = ffi::stack_st_DIST_POINT;
2517}
2518
2519foreign_type_and_impl_send_sync! {
2520 type CType = ffi::BASIC_CONSTRAINTS;
2521 fn drop = ffi::BASIC_CONSTRAINTS_free;
2522
2523 pub struct BasicConstraints;
2525 pub struct BasicConstraintsRef;
2527}
2528
2529impl BasicConstraintsRef {
2530 pub fn ca(&self) -> bool {
2531 unsafe { (*(self.as_ptr())).ca != 0 }
2532 }
2533
2534 pub fn pathlen(&self) -> Option<&Asn1IntegerRef> {
2535 if !self.ca() {
2536 return None;
2537 }
2538 unsafe {
2539 let data = (*(self.as_ptr())).pathlen;
2540 Asn1IntegerRef::from_const_ptr_opt(data as _)
2541 }
2542 }
2543}
2544
2545foreign_type_and_impl_send_sync! {
2546 type CType = ffi::ACCESS_DESCRIPTION;
2547 fn drop = ffi::ACCESS_DESCRIPTION_free;
2548
2549 pub struct AccessDescription;
2551 pub struct AccessDescriptionRef;
2553}
2554
2555impl AccessDescriptionRef {
2556 pub fn method(&self) -> &Asn1ObjectRef {
2558 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2559 }
2560
2561 pub fn location(&self) -> &GeneralNameRef {
2563 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2564 }
2565}
2566
2567impl Stackable for AccessDescription {
2568 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2569}
2570
2571foreign_type_and_impl_send_sync! {
2572 type CType = ffi::X509_ALGOR;
2573 fn drop = ffi::X509_ALGOR_free;
2574
2575 pub struct X509Algorithm;
2577 pub struct X509AlgorithmRef;
2579}
2580
2581impl X509AlgorithmRef {
2582 pub fn object(&self) -> &Asn1ObjectRef {
2584 unsafe {
2585 let mut oid = ptr::null();
2586 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2587 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2588 }
2589 }
2590}
2591
2592foreign_type_and_impl_send_sync! {
2593 type CType = ffi::X509_OBJECT;
2594 fn drop = X509_OBJECT_free;
2595
2596 pub struct X509Object;
2598 pub struct X509ObjectRef;
2600}
2601
2602impl X509ObjectRef {
2603 pub fn x509(&self) -> Option<&X509Ref> {
2604 unsafe {
2605 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2606 X509Ref::from_const_ptr_opt(ptr)
2607 }
2608 }
2609}
2610
2611impl Stackable for X509Object {
2612 type StackType = ffi::stack_st_X509_OBJECT;
2613}
2614
2615use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2616
2617use ffi::{
2618 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2619 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2620};
2621
2622use ffi::X509_OBJECT_free;
2623use ffi::X509_OBJECT_get0_X509;
2624
2625use ffi::{
2626 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2627 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2628};
2629
2630#[derive(Copy, Clone, PartialEq, Eq)]
2631pub struct X509PurposeId(c_int);
2632
2633impl X509PurposeId {
2634 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2635 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2636 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2637 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2638 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2639 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2640 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2641 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2642 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2643 #[cfg(ossl320)]
2644 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2645
2646 pub fn from_raw(id: c_int) -> Self {
2648 X509PurposeId(id)
2649 }
2650
2651 pub fn as_raw(&self) -> c_int {
2653 self.0
2654 }
2655}
2656
2657pub struct X509PurposeRef(Opaque);
2659
2660impl ForeignTypeRef for X509PurposeRef {
2662 type CType = ffi::X509_PURPOSE;
2663}
2664
2665impl X509PurposeRef {
2666 #[allow(clippy::unnecessary_cast)]
2680 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2681 unsafe {
2682 let sname = CString::new(sname).unwrap();
2683 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2684 Ok(purpose)
2685 }
2686 }
2687 #[corresponds(X509_PURPOSE_get0)]
2690 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2691 unsafe {
2692 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2693 Ok(X509PurposeRef::from_const_ptr(ptr))
2694 }
2695 }
2696
2697 pub fn purpose(&self) -> X509PurposeId {
2708 unsafe {
2709 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2710 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2711 }
2712 }
2713}