1use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
11use libc::{c_int, c_long, 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, Asn1Integer, 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 use crate::x509::extension::CrlNumber;
43
44pub mod verify;
45
46pub mod extension;
47pub mod store;
48
49#[cfg(test)]
50mod tests;
51
52pub unsafe trait ExtensionType {
58 const NID: Nid;
59 type Output: ForeignType;
60}
61
62foreign_type_and_impl_send_sync! {
63 type CType = ffi::X509_STORE_CTX;
64 fn drop = ffi::X509_STORE_CTX_free;
65
66 pub struct X509StoreContext;
68
69 pub struct X509StoreContextRef;
71}
72
73impl X509StoreContext {
74 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
77 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
78 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
79 }
80
81 #[corresponds(X509_STORE_CTX_new)]
83 pub fn new() -> Result<X509StoreContext, ErrorStack> {
84 unsafe {
85 ffi::init();
86 cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
87 }
88 }
89}
90
91impl X509StoreContextRef {
92 #[corresponds(X509_STORE_CTX_get_ex_data)]
94 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
95 unsafe {
96 let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
97 if data.is_null() {
98 None
99 } else {
100 Some(&*(data as *const T))
101 }
102 }
103 }
104
105 #[corresponds(X509_STORE_CTX_get_error)]
107 pub fn error(&self) -> X509VerifyResult {
108 unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
109 }
110
111 pub fn init<F, T>(
127 &mut self,
128 trust: &store::X509StoreRef,
129 cert: &X509Ref,
130 cert_chain: &StackRef<X509>,
131 with_context: F,
132 ) -> Result<T, ErrorStack>
133 where
134 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
135 {
136 struct Cleanup<'a>(&'a mut X509StoreContextRef);
137
138 impl Drop for Cleanup<'_> {
139 fn drop(&mut self) {
140 unsafe {
141 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
142 }
143 }
144 }
145
146 unsafe {
147 cvt(ffi::X509_STORE_CTX_init(
148 self.as_ptr(),
149 trust.as_ptr(),
150 cert.as_ptr(),
151 cert_chain.as_ptr(),
152 ))?;
153
154 let cleanup = Cleanup(self);
155 with_context(cleanup.0)
156 }
157 }
158
159 #[corresponds(X509_verify_cert)]
166 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
167 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
168 }
169
170 #[corresponds(X509_STORE_CTX_set_error)]
172 pub fn set_error(&mut self, result: X509VerifyResult) {
173 unsafe {
174 ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
175 }
176 }
177
178 #[corresponds(X509_STORE_CTX_get_current_cert)]
181 pub fn current_cert(&self) -> Option<&X509Ref> {
182 unsafe {
183 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
184 X509Ref::from_const_ptr_opt(ptr)
185 }
186 }
187
188 #[corresponds(X509_STORE_CTX_get_error_depth)]
193 pub fn error_depth(&self) -> u32 {
194 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
195 }
196
197 #[corresponds(X509_STORE_CTX_get0_chain)]
199 pub fn chain(&self) -> Option<&StackRef<X509>> {
200 unsafe {
201 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
202
203 if chain.is_null() {
204 None
205 } else {
206 Some(StackRef::from_ptr(chain))
207 }
208 }
209 }
210}
211
212pub struct X509Builder(X509);
214
215impl X509Builder {
216 #[corresponds(X509_new)]
218 pub fn new() -> Result<X509Builder, ErrorStack> {
219 unsafe {
220 ffi::init();
221 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
222 }
223 }
224
225 #[corresponds(X509_set1_notAfter)]
227 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
228 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
229 }
230
231 #[corresponds(X509_set1_notBefore)]
233 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
234 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
235 }
236
237 #[corresponds(X509_set_version)]
242 #[allow(clippy::useless_conversion)]
243 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
244 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
245 }
246
247 #[corresponds(X509_set_serialNumber)]
249 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
250 unsafe {
251 cvt(ffi::X509_set_serialNumber(
252 self.0.as_ptr(),
253 serial_number.as_ptr(),
254 ))
255 .map(|_| ())
256 }
257 }
258
259 #[corresponds(X509_set_issuer_name)]
261 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
262 unsafe {
263 cvt(ffi::X509_set_issuer_name(
264 self.0.as_ptr(),
265 issuer_name.as_ptr(),
266 ))
267 .map(|_| ())
268 }
269 }
270
271 #[corresponds(X509_set_subject_name)]
290 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
291 unsafe {
292 cvt(ffi::X509_set_subject_name(
293 self.0.as_ptr(),
294 subject_name.as_ptr(),
295 ))
296 .map(|_| ())
297 }
298 }
299
300 #[corresponds(X509_set_pubkey)]
302 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
303 where
304 T: HasPublic,
305 {
306 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
307 }
308
309 #[corresponds(X509V3_set_ctx)]
313 pub fn x509v3_context<'a>(
314 &'a self,
315 issuer: Option<&'a X509Ref>,
316 conf: Option<&'a ConfRef>,
317 ) -> X509v3Context<'a> {
318 unsafe {
319 let mut ctx = mem::zeroed();
320
321 let issuer = match issuer {
322 Some(issuer) => issuer.as_ptr(),
323 None => self.0.as_ptr(),
324 };
325 let subject = self.0.as_ptr();
326 ffi::X509V3_set_ctx(
327 &mut ctx,
328 issuer,
329 subject,
330 ptr::null_mut(),
331 ptr::null_mut(),
332 0,
333 );
334
335 if let Some(conf) = conf {
337 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
338 }
339
340 X509v3Context(ctx, PhantomData)
341 }
342 }
343
344 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
348 self.append_extension2(&extension)
349 }
350
351 #[corresponds(X509_add_ext)]
353 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
354 unsafe {
355 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
356 Ok(())
357 }
358 }
359
360 #[corresponds(X509_sign)]
362 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
363 where
364 T: HasPrivate,
365 {
366 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
367 }
368
369 pub fn build(self) -> X509 {
371 self.0
372 }
373}
374
375foreign_type_and_impl_send_sync! {
376 type CType = ffi::X509;
377 fn drop = ffi::X509_free;
378
379 pub struct X509;
381 pub struct X509Ref;
383}
384
385impl X509Ref {
386 #[corresponds(X509_get_subject_name)]
388 pub fn subject_name(&self) -> &X509NameRef {
389 unsafe {
390 let name = ffi::X509_get_subject_name(self.as_ptr());
391 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
392 }
393 }
394
395 #[corresponds(X509_subject_name_hash)]
397 pub fn subject_name_hash(&self) -> u32 {
398 #[allow(clippy::unnecessary_cast)]
399 unsafe {
400 ffi::X509_subject_name_hash(self.as_ptr()) as u32
401 }
402 }
403
404 #[corresponds(X509_get_issuer_name)]
406 pub fn issuer_name(&self) -> &X509NameRef {
407 unsafe {
408 let name = ffi::X509_get_issuer_name(self.as_ptr());
409 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
410 }
411 }
412
413 #[corresponds(X509_issuer_name_hash)]
415 pub fn issuer_name_hash(&self) -> u32 {
416 #[allow(clippy::unnecessary_cast)]
417 unsafe {
418 ffi::X509_issuer_name_hash(self.as_ptr()) as u32
419 }
420 }
421
422 #[corresponds(X509_get_ext_d2i)]
424 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
425 unsafe {
426 let stack = ffi::X509_get_ext_d2i(
427 self.as_ptr(),
428 ffi::NID_subject_alt_name,
429 ptr::null_mut(),
430 ptr::null_mut(),
431 );
432 Stack::from_ptr_opt(stack as *mut _)
433 }
434 }
435
436 #[corresponds(X509_get_ext_d2i)]
438 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
439 unsafe {
440 let stack = ffi::X509_get_ext_d2i(
441 self.as_ptr(),
442 ffi::NID_crl_distribution_points,
443 ptr::null_mut(),
444 ptr::null_mut(),
445 );
446 Stack::from_ptr_opt(stack as *mut _)
447 }
448 }
449
450 #[corresponds(X509_get_ext_d2i)]
452 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
453 unsafe {
454 let stack = ffi::X509_get_ext_d2i(
455 self.as_ptr(),
456 ffi::NID_issuer_alt_name,
457 ptr::null_mut(),
458 ptr::null_mut(),
459 );
460 Stack::from_ptr_opt(stack as *mut _)
461 }
462 }
463
464 #[corresponds(X509_get_ext_d2i)]
468 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
469 unsafe {
470 let stack = ffi::X509_get_ext_d2i(
471 self.as_ptr(),
472 ffi::NID_info_access,
473 ptr::null_mut(),
474 ptr::null_mut(),
475 );
476 Stack::from_ptr_opt(stack as *mut _)
477 }
478 }
479
480 #[corresponds(X509_get_pathlen)]
482 #[cfg(any(ossl110, boringssl, awslc))]
483 pub fn pathlen(&self) -> Option<u32> {
484 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
485 u32::try_from(v).ok()
486 }
487
488 #[corresponds(X509_get0_subject_key_id)]
490 #[cfg(any(ossl110, boringssl, awslc))]
491 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
492 unsafe {
493 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
494 Asn1OctetStringRef::from_const_ptr_opt(data)
495 }
496 }
497
498 #[corresponds(X509_get0_authority_key_id)]
500 #[cfg(any(ossl110, boringssl, awslc))]
501 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
502 unsafe {
503 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
504 Asn1OctetStringRef::from_const_ptr_opt(data)
505 }
506 }
507
508 #[corresponds(X509_get0_authority_issuer)]
510 #[cfg(ossl111d)]
511 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
512 unsafe {
513 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
514 StackRef::from_const_ptr_opt(stack)
515 }
516 }
517
518 #[corresponds(X509_get0_authority_serial)]
520 #[cfg(ossl111d)]
521 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
522 unsafe {
523 let r = ffi::X509_get0_authority_serial(self.as_ptr());
524 Asn1IntegerRef::from_const_ptr_opt(r)
525 }
526 }
527
528 #[corresponds(X509_get_pubkey)]
529 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
530 unsafe {
531 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
532 Ok(PKey::from_ptr(pkey))
533 }
534 }
535
536 #[corresponds(X509_digest)]
538 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
539 unsafe {
540 let mut digest = DigestBytes {
541 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
542 len: ffi::EVP_MAX_MD_SIZE as usize,
543 };
544 let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
545 cvt(ffi::X509_digest(
546 self.as_ptr(),
547 hash_type.as_ptr(),
548 digest.buf.as_mut_ptr() as *mut _,
549 &mut len,
550 ))?;
551 digest.len = len as usize;
552
553 Ok(digest)
554 }
555 }
556
557 #[deprecated(since = "0.10.9", note = "renamed to digest")]
558 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
559 self.digest(hash_type).map(|b| b.to_vec())
560 }
561
562 #[corresponds(X509_getm_notAfter)]
564 pub fn not_after(&self) -> &Asn1TimeRef {
565 unsafe {
566 let date = X509_getm_notAfter(self.as_ptr());
567 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
568 }
569 }
570
571 #[corresponds(X509_getm_notBefore)]
573 pub fn not_before(&self) -> &Asn1TimeRef {
574 unsafe {
575 let date = X509_getm_notBefore(self.as_ptr());
576 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
577 }
578 }
579
580 #[corresponds(X509_get0_signature)]
582 pub fn signature(&self) -> &Asn1BitStringRef {
583 unsafe {
584 let mut signature = ptr::null();
585 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
586 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
587 }
588 }
589
590 #[corresponds(X509_get0_signature)]
592 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
593 unsafe {
594 let mut algor = ptr::null();
595 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
596 X509AlgorithmRef::from_const_ptr_opt(algor)
597 .expect("signature algorithm must not be null")
598 }
599 }
600
601 #[corresponds(X509_get1_ocsp)]
607 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
608 unsafe {
609 let stack: Stack<OpensslString> =
610 cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))?;
611 for entry in &stack {
612 let bytes = CStr::from_ptr(entry.as_ptr()).to_bytes();
613 if str::from_utf8(bytes).is_err() {
614 return Err(ErrorStack::internal_error(
615 "OCSP responder URL contained invalid UTF-8",
616 ));
617 }
618 }
619 Ok(stack)
620 }
621 }
622
623 #[corresponds(X509_check_issued)]
625 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
626 unsafe {
627 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
628 X509VerifyResult::from_raw(r)
629 }
630 }
631
632 #[corresponds(X509_get_version)]
637 #[cfg(ossl110)]
638 #[allow(clippy::unnecessary_cast)]
639 pub fn version(&self) -> i32 {
640 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
641 }
642
643 #[corresponds(X509_verify)]
650 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
651 where
652 T: HasPublic,
653 {
654 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
655 }
656
657 #[corresponds(X509_get_serialNumber)]
659 pub fn serial_number(&self) -> &Asn1IntegerRef {
660 unsafe {
661 let r = ffi::X509_get_serialNumber(self.as_ptr());
662 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
663 }
664 }
665
666 #[corresponds(X509_alias_get0)]
672 pub fn alias(&self) -> Option<&[u8]> {
673 unsafe {
674 let mut len = 0;
675 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
676 if ptr.is_null() {
677 None
678 } else {
679 Some(util::from_raw_parts(ptr, len as usize))
680 }
681 }
682 }
683
684 to_pem! {
685 #[corresponds(PEM_write_bio_X509)]
689 to_pem,
690 ffi::PEM_write_bio_X509
691 }
692
693 to_der! {
694 #[corresponds(i2d_X509)]
696 to_der,
697 ffi::i2d_X509
698 }
699
700 to_pem! {
701 #[corresponds(X509_print)]
703 to_text,
704 ffi::X509_print
705 }
706}
707
708impl ToOwned for X509Ref {
709 type Owned = X509;
710
711 fn to_owned(&self) -> X509 {
712 unsafe {
713 X509_up_ref(self.as_ptr());
714 X509::from_ptr(self.as_ptr())
715 }
716 }
717}
718
719impl Ord for X509Ref {
720 fn cmp(&self, other: &Self) -> cmp::Ordering {
721 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
724 cmp.cmp(&0)
725 }
726}
727
728impl PartialOrd for X509Ref {
729 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
730 Some(self.cmp(other))
731 }
732}
733
734impl PartialOrd<X509> for X509Ref {
735 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
736 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
737 }
738}
739
740impl PartialEq for X509Ref {
741 fn eq(&self, other: &Self) -> bool {
742 self.cmp(other) == cmp::Ordering::Equal
743 }
744}
745
746impl PartialEq<X509> for X509Ref {
747 fn eq(&self, other: &X509) -> bool {
748 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
749 }
750}
751
752impl Eq for X509Ref {}
753
754impl X509 {
755 pub fn builder() -> Result<X509Builder, ErrorStack> {
757 X509Builder::new()
758 }
759
760 from_pem! {
761 #[corresponds(PEM_read_bio_X509)]
765 from_pem,
766 X509,
767 ffi::PEM_read_bio_X509
768 }
769
770 from_der! {
771 #[corresponds(d2i_X509)]
773 from_der,
774 X509,
775 ffi::d2i_X509
776 }
777
778 #[corresponds(PEM_read_bio_X509)]
780 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
781 unsafe {
782 ffi::init();
783 let bio = MemBioSlice::new(pem)?;
784
785 let mut certs = vec![];
786 loop {
787 let r =
788 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
789 if r.is_null() {
790 let e = ErrorStack::get();
791
792 if let Some(err) = e.errors().last() {
793 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
794 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
795 {
796 break;
797 }
798 }
799
800 return Err(e);
801 } else {
802 certs.push(X509(r));
803 }
804 }
805
806 Ok(certs)
807 }
808 }
809}
810
811impl Clone for X509 {
812 fn clone(&self) -> X509 {
813 X509Ref::to_owned(self)
814 }
815}
816
817impl fmt::Debug for X509 {
818 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
819 let serial = match &self.serial_number().to_bn() {
820 Ok(bn) => match bn.to_hex_str() {
821 Ok(hex) => hex.to_string(),
822 Err(_) => "".to_string(),
823 },
824 Err(_) => "".to_string(),
825 };
826 let mut debug_struct = formatter.debug_struct("X509");
827 debug_struct.field("serial_number", &serial);
828 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
829 debug_struct.field("issuer", &self.issuer_name());
830 debug_struct.field("subject", &self.subject_name());
831 if let Some(subject_alt_names) = &self.subject_alt_names() {
832 debug_struct.field("subject_alt_names", subject_alt_names);
833 }
834 debug_struct.field("not_before", &self.not_before());
835 debug_struct.field("not_after", &self.not_after());
836
837 if let Ok(public_key) = &self.public_key() {
838 debug_struct.field("public_key", public_key);
839 };
840 debug_struct.finish()
843 }
844}
845
846impl AsRef<X509Ref> for X509Ref {
847 fn as_ref(&self) -> &X509Ref {
848 self
849 }
850}
851
852impl Stackable for X509 {
853 type StackType = ffi::stack_st_X509;
854}
855
856impl Ord for X509 {
857 fn cmp(&self, other: &Self) -> cmp::Ordering {
858 X509Ref::cmp(self, other)
859 }
860}
861
862impl PartialOrd for X509 {
863 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
864 Some(self.cmp(other))
865 }
866}
867
868impl PartialOrd<X509Ref> for X509 {
869 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
870 X509Ref::partial_cmp(self, other)
871 }
872}
873
874impl PartialEq for X509 {
875 fn eq(&self, other: &Self) -> bool {
876 X509Ref::eq(self, other)
877 }
878}
879
880impl PartialEq<X509Ref> for X509 {
881 fn eq(&self, other: &X509Ref) -> bool {
882 X509Ref::eq(self, other)
883 }
884}
885
886impl Eq for X509 {}
887
888pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
890
891impl X509v3Context<'_> {
892 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
893 &self.0 as *const _ as *mut _
894 }
895}
896
897foreign_type_and_impl_send_sync! {
898 type CType = ffi::X509_EXTENSION;
899 fn drop = ffi::X509_EXTENSION_free;
900
901 pub struct X509Extension;
903 pub struct X509ExtensionRef;
905}
906
907impl Stackable for X509Extension {
908 type StackType = ffi::stack_st_X509_EXTENSION;
909}
910
911impl X509Extension {
912 #[deprecated(
926 note = "Use x509::extension types or new_from_der instead",
927 since = "0.10.51"
928 )]
929 pub fn new(
930 conf: Option<&ConfRef>,
931 context: Option<&X509v3Context<'_>>,
932 name: &str,
933 value: &str,
934 ) -> Result<X509Extension, ErrorStack> {
935 let name = CString::new(name).unwrap();
936 let value = CString::new(value).unwrap();
937 let mut ctx;
938 unsafe {
939 ffi::init();
940 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
941 let context_ptr = match context {
942 Some(c) => c.as_ptr(),
943 None => {
944 ctx = mem::zeroed();
945
946 ffi::X509V3_set_ctx(
947 &mut ctx,
948 ptr::null_mut(),
949 ptr::null_mut(),
950 ptr::null_mut(),
951 ptr::null_mut(),
952 0,
953 );
954 &mut ctx
955 }
956 };
957 let name = name.as_ptr() as *mut _;
958 let value = value.as_ptr() as *mut _;
959
960 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
961 }
962 }
963
964 #[deprecated(
978 note = "Use x509::extension types or new_from_der instead",
979 since = "0.10.51"
980 )]
981 pub fn new_nid(
982 conf: Option<&ConfRef>,
983 context: Option<&X509v3Context<'_>>,
984 name: Nid,
985 value: &str,
986 ) -> Result<X509Extension, ErrorStack> {
987 let value = CString::new(value).unwrap();
988 let mut ctx;
989 unsafe {
990 ffi::init();
991 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
992 let context_ptr = match context {
993 Some(c) => c.as_ptr(),
994 None => {
995 ctx = mem::zeroed();
996
997 ffi::X509V3_set_ctx(
998 &mut ctx,
999 ptr::null_mut(),
1000 ptr::null_mut(),
1001 ptr::null_mut(),
1002 ptr::null_mut(),
1003 0,
1004 );
1005 &mut ctx
1006 }
1007 };
1008 let name = name.as_raw();
1009 let value = value.as_ptr() as *mut _;
1010
1011 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1012 }
1013 }
1014
1015 pub fn new_from_der(
1025 oid: &Asn1ObjectRef,
1026 critical: bool,
1027 der_contents: &Asn1OctetStringRef,
1028 ) -> Result<X509Extension, ErrorStack> {
1029 unsafe {
1030 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1031 ptr::null_mut(),
1032 oid.as_ptr(),
1033 critical as _,
1034 der_contents.as_ptr(),
1035 ))
1036 .map(X509Extension)
1037 }
1038 }
1039
1040 pub(crate) unsafe fn new_internal(
1041 nid: Nid,
1042 critical: bool,
1043 value: *mut c_void,
1044 ) -> Result<X509Extension, ErrorStack> {
1045 ffi::init();
1046 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1047 }
1048
1049 #[cfg(not(libressl390))]
1055 #[corresponds(X509V3_EXT_add_alias)]
1056 #[deprecated(
1057 note = "Use x509::extension types or new_from_der and then this is not necessary",
1058 since = "0.10.51"
1059 )]
1060 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1061 ffi::init();
1062 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1063 }
1064}
1065
1066impl X509ExtensionRef {
1067 to_der! {
1068 #[corresponds(i2d_X509_EXTENSION)]
1070 to_der,
1071 ffi::i2d_X509_EXTENSION
1072 }
1073}
1074
1075pub struct X509NameBuilder(X509Name);
1077
1078impl X509NameBuilder {
1079 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1081 unsafe {
1082 ffi::init();
1083 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1084 }
1085 }
1086
1087 #[corresponds(X509_NAME_add_entry)]
1089 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1090 unsafe {
1091 cvt(ffi::X509_NAME_add_entry(
1092 self.0.as_ptr(),
1093 ne.as_ptr(),
1094 -1,
1095 0,
1096 ))
1097 .map(|_| ())
1098 }
1099 }
1100
1101 #[corresponds(X509_NAME_add_entry_by_txt)]
1103 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1104 unsafe {
1105 let field = CString::new(field).unwrap();
1106 assert!(value.len() <= crate::SLenType::MAX as usize);
1107 cvt(ffi::X509_NAME_add_entry_by_txt(
1108 self.0.as_ptr(),
1109 field.as_ptr() as *mut _,
1110 ffi::MBSTRING_UTF8,
1111 value.as_ptr(),
1112 value.len() as crate::SLenType,
1113 -1,
1114 0,
1115 ))
1116 .map(|_| ())
1117 }
1118 }
1119
1120 #[corresponds(X509_NAME_add_entry_by_txt)]
1122 pub fn append_entry_by_text_with_type(
1123 &mut self,
1124 field: &str,
1125 value: &str,
1126 ty: Asn1Type,
1127 ) -> Result<(), ErrorStack> {
1128 unsafe {
1129 let field = CString::new(field).unwrap();
1130 assert!(value.len() <= crate::SLenType::MAX as usize);
1131 cvt(ffi::X509_NAME_add_entry_by_txt(
1132 self.0.as_ptr(),
1133 field.as_ptr() as *mut _,
1134 ty.as_raw(),
1135 value.as_ptr(),
1136 value.len() as crate::SLenType,
1137 -1,
1138 0,
1139 ))
1140 .map(|_| ())
1141 }
1142 }
1143
1144 #[corresponds(X509_NAME_add_entry_by_NID)]
1146 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1147 unsafe {
1148 assert!(value.len() <= crate::SLenType::MAX as usize);
1149 cvt(ffi::X509_NAME_add_entry_by_NID(
1150 self.0.as_ptr(),
1151 field.as_raw(),
1152 ffi::MBSTRING_UTF8,
1153 value.as_ptr() as *mut _,
1154 value.len() as crate::SLenType,
1155 -1,
1156 0,
1157 ))
1158 .map(|_| ())
1159 }
1160 }
1161
1162 #[corresponds(X509_NAME_add_entry_by_NID)]
1164 pub fn append_entry_by_nid_with_type(
1165 &mut self,
1166 field: Nid,
1167 value: &str,
1168 ty: Asn1Type,
1169 ) -> Result<(), ErrorStack> {
1170 unsafe {
1171 assert!(value.len() <= crate::SLenType::MAX as usize);
1172 cvt(ffi::X509_NAME_add_entry_by_NID(
1173 self.0.as_ptr(),
1174 field.as_raw(),
1175 ty.as_raw(),
1176 value.as_ptr() as *mut _,
1177 value.len() as crate::SLenType,
1178 -1,
1179 0,
1180 ))
1181 .map(|_| ())
1182 }
1183 }
1184
1185 pub fn build(self) -> X509Name {
1187 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1191 }
1192}
1193
1194foreign_type_and_impl_send_sync! {
1195 type CType = ffi::X509_NAME;
1196 fn drop = ffi::X509_NAME_free;
1197
1198 pub struct X509Name;
1200 pub struct X509NameRef;
1202}
1203
1204impl X509Name {
1205 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1207 X509NameBuilder::new()
1208 }
1209
1210 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1214 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1215 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1216 }
1217
1218 from_der! {
1219 from_der,
1225 X509Name,
1226 ffi::d2i_X509_NAME
1227 }
1228}
1229
1230impl Stackable for X509Name {
1231 type StackType = ffi::stack_st_X509_NAME;
1232}
1233
1234impl X509NameRef {
1235 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1237 X509NameEntries {
1238 name: self,
1239 nid: Some(nid),
1240 loc: -1,
1241 }
1242 }
1243
1244 pub fn entries(&self) -> X509NameEntries<'_> {
1246 X509NameEntries {
1247 name: self,
1248 nid: None,
1249 loc: -1,
1250 }
1251 }
1252
1253 #[corresponds(X509_NAME_cmp)]
1260 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1261 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1262 if cfg!(ossl300) && cmp == -2 {
1263 return Err(ErrorStack::get());
1264 }
1265 Ok(cmp.cmp(&0))
1266 }
1267
1268 #[corresponds(X509_NAME_dup)]
1270 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1271 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1272 }
1273
1274 to_der! {
1275 to_der,
1281 ffi::i2d_X509_NAME
1282 }
1283}
1284
1285impl fmt::Debug for X509NameRef {
1286 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1287 formatter.debug_list().entries(self.entries()).finish()
1288 }
1289}
1290
1291pub struct X509NameEntries<'a> {
1293 name: &'a X509NameRef,
1294 nid: Option<Nid>,
1295 loc: c_int,
1296}
1297
1298impl<'a> Iterator for X509NameEntries<'a> {
1299 type Item = &'a X509NameEntryRef;
1300
1301 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1302 unsafe {
1303 match self.nid {
1304 Some(nid) => {
1305 self.loc =
1307 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1308 if self.loc == -1 {
1309 return None;
1310 }
1311 }
1312 None => {
1313 self.loc += 1;
1315 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1316 return None;
1317 }
1318 }
1319 }
1320
1321 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1322
1323 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1324 }
1325 }
1326}
1327
1328foreign_type_and_impl_send_sync! {
1329 type CType = ffi::X509_NAME_ENTRY;
1330 fn drop = ffi::X509_NAME_ENTRY_free;
1331
1332 pub struct X509NameEntry;
1334 pub struct X509NameEntryRef;
1336}
1337
1338impl X509NameEntryRef {
1339 #[corresponds(X509_NAME_ENTRY_get_data)]
1341 pub fn data(&self) -> &Asn1StringRef {
1342 unsafe {
1343 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1344 Asn1StringRef::from_ptr(data as *mut _)
1345 }
1346 }
1347
1348 #[corresponds(X509_NAME_ENTRY_get_object)]
1351 pub fn object(&self) -> &Asn1ObjectRef {
1352 unsafe {
1353 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1354 Asn1ObjectRef::from_ptr(object as *mut _)
1355 }
1356 }
1357}
1358
1359impl fmt::Debug for X509NameEntryRef {
1360 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1361 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1362 }
1363}
1364
1365pub struct X509ReqBuilder(X509Req);
1367
1368impl X509ReqBuilder {
1369 #[corresponds(X509_REQ_new)]
1371 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1372 unsafe {
1373 ffi::init();
1374 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1375 }
1376 }
1377
1378 #[corresponds(X509_REQ_set_version)]
1380 #[allow(clippy::useless_conversion)]
1381 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1382 unsafe {
1383 cvt(ffi::X509_REQ_set_version(
1384 self.0.as_ptr(),
1385 version as c_long,
1386 ))
1387 .map(|_| ())
1388 }
1389 }
1390
1391 #[corresponds(X509_REQ_set_subject_name)]
1393 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1394 unsafe {
1395 cvt(ffi::X509_REQ_set_subject_name(
1396 self.0.as_ptr(),
1397 subject_name.as_ptr(),
1398 ))
1399 .map(|_| ())
1400 }
1401 }
1402
1403 #[corresponds(X509_REQ_set_pubkey)]
1405 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1406 where
1407 T: HasPublic,
1408 {
1409 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1410 }
1411
1412 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1415 unsafe {
1416 let mut ctx = mem::zeroed();
1417
1418 ffi::X509V3_set_ctx(
1419 &mut ctx,
1420 ptr::null_mut(),
1421 ptr::null_mut(),
1422 self.0.as_ptr(),
1423 ptr::null_mut(),
1424 0,
1425 );
1426
1427 if let Some(conf) = conf {
1429 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1430 }
1431
1432 X509v3Context(ctx, PhantomData)
1433 }
1434 }
1435
1436 pub fn add_extensions(
1438 &mut self,
1439 extensions: &StackRef<X509Extension>,
1440 ) -> Result<(), ErrorStack> {
1441 unsafe {
1442 cvt(ffi::X509_REQ_add_extensions(
1443 self.0.as_ptr(),
1444 extensions.as_ptr(),
1445 ))
1446 .map(|_| ())
1447 }
1448 }
1449
1450 #[corresponds(X509_REQ_sign)]
1452 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1453 where
1454 T: HasPrivate,
1455 {
1456 unsafe {
1457 cvt(ffi::X509_REQ_sign(
1458 self.0.as_ptr(),
1459 key.as_ptr(),
1460 hash.as_ptr(),
1461 ))
1462 .map(|_| ())
1463 }
1464 }
1465
1466 pub fn build(self) -> X509Req {
1468 self.0
1469 }
1470}
1471
1472foreign_type_and_impl_send_sync! {
1473 type CType = ffi::X509_REQ;
1474 fn drop = ffi::X509_REQ_free;
1475
1476 pub struct X509Req;
1478 pub struct X509ReqRef;
1480}
1481
1482impl X509Req {
1483 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1485 X509ReqBuilder::new()
1486 }
1487
1488 from_pem! {
1489 from_pem,
1497 X509Req,
1498 ffi::PEM_read_bio_X509_REQ
1499 }
1500
1501 from_der! {
1502 from_der,
1508 X509Req,
1509 ffi::d2i_X509_REQ
1510 }
1511}
1512
1513impl X509ReqRef {
1514 to_pem! {
1515 to_pem,
1523 ffi::PEM_write_bio_X509_REQ
1524 }
1525
1526 to_der! {
1527 to_der,
1533 ffi::i2d_X509_REQ
1534 }
1535
1536 to_pem! {
1537 #[corresponds(X509_Req_print)]
1539 to_text,
1540 ffi::X509_REQ_print
1541 }
1542
1543 #[corresponds(X509_REQ_get_version)]
1545 #[allow(clippy::unnecessary_cast)]
1546 pub fn version(&self) -> i32 {
1547 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1548 }
1549
1550 #[corresponds(X509_REQ_get_subject_name)]
1552 pub fn subject_name(&self) -> &X509NameRef {
1553 unsafe {
1554 let name = X509_REQ_get_subject_name(self.as_ptr());
1555 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1556 }
1557 }
1558
1559 #[corresponds(X509_REQ_get_pubkey)]
1561 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1562 unsafe {
1563 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1564 Ok(PKey::from_ptr(key))
1565 }
1566 }
1567
1568 #[corresponds(X509_REQ_verify)]
1572 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1573 where
1574 T: HasPublic,
1575 {
1576 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1577 }
1578
1579 #[corresponds(X509_REQ_get_extensions)]
1581 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1582 unsafe {
1583 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1584 Ok(Stack::from_ptr(extensions))
1585 }
1586 }
1587}
1588
1589#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1591pub struct CrlReason(c_int);
1592
1593#[allow(missing_docs)] impl CrlReason {
1595 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1596 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1597 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1598 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1599 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1600 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1601 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1602 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1603 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1604 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1605
1606 pub const fn from_raw(value: c_int) -> Self {
1608 CrlReason(value)
1609 }
1610
1611 pub const fn as_raw(&self) -> c_int {
1613 self.0
1614 }
1615}
1616
1617pub struct X509RevokedBuilder(X509Revoked);
1619
1620impl X509RevokedBuilder {
1621 #[corresponds(X509_REVOKED_new)]
1623 pub fn new() -> Result<Self, ErrorStack> {
1624 unsafe {
1625 ffi::init();
1626 cvt_p(ffi::X509_REVOKED_new()).map(|p| X509RevokedBuilder(X509Revoked(p)))
1627 }
1628 }
1629
1630 #[corresponds(X509_REVOKED_set_revocationDate)]
1632 pub fn set_revocation_date(&mut self, date: &Asn1TimeRef) -> Result<(), ErrorStack> {
1633 unsafe {
1634 cvt(ffi::X509_REVOKED_set_revocationDate(
1635 self.0.as_ptr(),
1636 date.as_ptr(),
1637 ))
1638 .map(|_| ())
1639 }
1640 }
1641
1642 #[corresponds(X509_REVOKED_set_serialNumber)]
1644 pub fn set_serial_number(&mut self, serial: &Asn1IntegerRef) -> Result<(), ErrorStack> {
1645 unsafe {
1646 cvt(ffi::X509_REVOKED_set_serialNumber(
1647 self.0.as_ptr(),
1648 serial.as_ptr(),
1649 ))
1650 .map(|_| ())
1651 }
1652 }
1653
1654 pub fn build(self) -> X509Revoked {
1656 self.0
1657 }
1658}
1659
1660foreign_type_and_impl_send_sync! {
1661 type CType = ffi::X509_REVOKED;
1662 fn drop = ffi::X509_REVOKED_free;
1663
1664 pub struct X509Revoked;
1666 pub struct X509RevokedRef;
1668}
1669
1670impl Stackable for X509Revoked {
1671 type StackType = ffi::stack_st_X509_REVOKED;
1672}
1673
1674impl X509Revoked {
1675 from_der! {
1676 #[corresponds(d2i_X509_REVOKED)]
1678 from_der,
1679 X509Revoked,
1680 ffi::d2i_X509_REVOKED
1681 }
1682}
1683
1684impl X509RevokedRef {
1685 to_der! {
1686 #[corresponds(d2i_X509_REVOKED)]
1688 to_der,
1689 ffi::i2d_X509_REVOKED
1690 }
1691
1692 #[corresponds(X509_REVOKED_dup)]
1694 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1695 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1696 }
1697
1698 #[corresponds(X509_REVOKED_get0_revocationDate)]
1700 pub fn revocation_date(&self) -> &Asn1TimeRef {
1701 unsafe {
1702 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1703 assert!(!r.is_null());
1704 Asn1TimeRef::from_ptr(r as *mut _)
1705 }
1706 }
1707
1708 #[corresponds(X509_REVOKED_get0_serialNumber)]
1710 pub fn serial_number(&self) -> &Asn1IntegerRef {
1711 unsafe {
1712 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1713 assert!(!r.is_null());
1714 Asn1IntegerRef::from_ptr(r as *mut _)
1715 }
1716 }
1717
1718 #[corresponds(X509_REVOKED_get_ext_d2i)]
1722 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1723 let mut critical = -1;
1724 let out = unsafe {
1725 let ext = ffi::X509_REVOKED_get_ext_d2i(
1727 self.as_ptr(),
1728 T::NID.as_raw(),
1729 &mut critical as *mut _,
1730 ptr::null_mut(),
1731 );
1732 T::Output::from_ptr_opt(ext as *mut _)
1735 };
1736 match (critical, out) {
1737 (0, Some(out)) => Ok(Some((false, out))),
1738 (1, Some(out)) => Ok(Some((true, out))),
1739 (-1 | -2, _) => Ok(None),
1741 (0 | 1, None) => Err(ErrorStack::get()),
1744 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1745 }
1746 }
1747}
1748
1749pub enum ReasonCode {}
1752
1753unsafe impl ExtensionType for ReasonCode {
1756 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1757
1758 type Output = Asn1Enumerated;
1759}
1760
1761pub enum CertificateIssuer {}
1764
1765unsafe impl ExtensionType for CertificateIssuer {
1768 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1769
1770 type Output = Stack<GeneralName>;
1771}
1772
1773pub enum AuthorityInformationAccess {}
1775
1776unsafe impl ExtensionType for AuthorityInformationAccess {
1779 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1780
1781 type Output = Stack<AccessDescription>;
1782}
1783
1784unsafe impl ExtensionType for CrlNumber {
1787 const NID: Nid = Nid::CRL_NUMBER;
1788
1789 type Output = Asn1Integer;
1790}
1791
1792pub struct X509CrlBuilder(X509Crl);
1794
1795impl X509CrlBuilder {
1796 #[corresponds(X509_CRL_new)]
1798 pub fn new() -> Result<Self, ErrorStack> {
1799 unsafe {
1800 ffi::init();
1801 let ptr = cvt_p(ffi::X509_CRL_new())?;
1802 cvt(ffi::X509_CRL_set_version(ptr, 1)).map(|_| ())?;
1803
1804 Ok(Self(X509Crl(ptr)))
1805 }
1806 }
1807
1808 #[corresponds(X509_CRL_set_issuer_name)]
1810 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
1811 unsafe {
1812 cvt(ffi::X509_CRL_set_issuer_name(
1813 self.0.as_ptr(),
1814 issuer_name.as_ptr(),
1815 ))
1816 .map(|_| ())
1817 }
1818 }
1819
1820 #[corresponds(X509_CRL_set1_lastUpdate)]
1822 pub fn set_last_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
1823 unsafe { cvt(ffi::X509_CRL_set1_lastUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
1824 }
1825
1826 #[corresponds(X509_CRL_set1_nextUpdate)]
1828 pub fn set_next_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
1829 unsafe { cvt(ffi::X509_CRL_set1_nextUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
1830 }
1831
1832 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
1836 self.append_extension2(&extension)
1837 }
1838
1839 #[corresponds(X509_CRL_add_ext)]
1841 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
1842 unsafe {
1843 cvt(ffi::X509_CRL_add_ext(
1844 self.0.as_ptr(),
1845 extension.as_ptr(),
1846 -1,
1847 ))
1848 .map(|_| ())
1849 }
1850 }
1851
1852 #[corresponds(X509_CRL_add0_revoked)]
1854 pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
1855 unsafe {
1856 let r = cvt(ffi::X509_CRL_add0_revoked(
1857 self.0.as_ptr(),
1858 revoked.as_ptr(),
1859 ))
1860 .map(|_| ());
1861 std::mem::forget(revoked);
1862 r
1863 }
1864 }
1865
1866 #[corresponds(X509_CRL_sort)]
1868 pub fn sort(&mut self) -> Result<(), ErrorStack> {
1869 unsafe { cvt(ffi::X509_CRL_sort(self.0.as_ptr())).map(|_| ()) }
1870 }
1871
1872 #[corresponds(X509_CRL_sign)]
1874 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1875 where
1876 T: HasPrivate,
1877 {
1878 unsafe {
1879 cvt(ffi::X509_CRL_sign(
1880 self.0.as_ptr(),
1881 key.as_ptr(),
1882 hash.as_ptr(),
1883 ))
1884 .map(|_| ())
1885 }
1886 }
1887
1888 pub fn build(self) -> Result<X509Crl, ErrorStack> {
1894 unsafe {
1895 let loc = ffi::X509_CRL_get_ext_by_NID(
1896 self.0.as_ptr(),
1897 Nid::AUTHORITY_KEY_IDENTIFIER.as_raw(),
1898 -1,
1899 );
1900 assert!(
1901 loc >= 0,
1902 "CRL must have an Authority Key Identifier extension"
1903 );
1904 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
1905 assert_eq!(
1906 ffi::X509_EXTENSION_get_critical(ext),
1907 0,
1908 "Authority Key Identifier extension must not be critical"
1909 );
1910
1911 let loc = ffi::X509_CRL_get_ext_by_NID(self.0.as_ptr(), Nid::CRL_NUMBER.as_raw(), -1);
1912 assert!(loc >= 0, "CRL must have a Crl Number extension");
1913 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
1914 assert_eq!(
1915 ffi::X509_EXTENSION_get_critical(ext),
1916 0,
1917 "Crl Number extension must not be critical"
1918 );
1919
1920 assert!(
1921 !X509_CRL_get0_nextUpdate(self.0.as_ptr()).is_null(),
1922 "CRL must have nextUpdate time set"
1923 );
1924 let revoked = self.0.get_revoked();
1925 assert!(
1926 revoked.is_none() || revoked.is_some_and(|r| !r.is_empty()),
1928 "Revoked must be absent or non-empty"
1929 );
1930 }
1931
1932 Ok(self.0)
1933 }
1934}
1935
1936foreign_type_and_impl_send_sync! {
1937 type CType = ffi::X509_CRL;
1938 fn drop = ffi::X509_CRL_free;
1939
1940 pub struct X509Crl;
1942 pub struct X509CrlRef;
1944}
1945
1946pub enum CrlStatus<'a> {
1952 NotRevoked,
1954 Revoked(&'a X509RevokedRef),
1956 RemoveFromCrl(&'a X509RevokedRef),
1961}
1962
1963impl<'a> CrlStatus<'a> {
1964 unsafe fn from_ffi_status(
1969 status: c_int,
1970 revoked_entry: *mut ffi::X509_REVOKED,
1971 ) -> CrlStatus<'a> {
1972 match status {
1973 0 => CrlStatus::NotRevoked,
1974 1 => {
1975 assert!(!revoked_entry.is_null());
1976 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1977 }
1978 2 => {
1979 assert!(!revoked_entry.is_null());
1980 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1981 }
1982 _ => unreachable!(
1983 "{}",
1984 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1985 ),
1986 }
1987 }
1988}
1989
1990impl X509Crl {
1991 from_pem! {
1992 #[corresponds(PEM_read_bio_X509_CRL)]
1996 from_pem,
1997 X509Crl,
1998 ffi::PEM_read_bio_X509_CRL
1999 }
2000
2001 from_der! {
2002 #[corresponds(d2i_X509_CRL)]
2004 from_der,
2005 X509Crl,
2006 ffi::d2i_X509_CRL
2007 }
2008}
2009
2010impl X509CrlRef {
2011 to_pem! {
2012 #[corresponds(PEM_write_bio_X509_CRL)]
2016 to_pem,
2017 ffi::PEM_write_bio_X509_CRL
2018 }
2019
2020 to_der! {
2021 #[corresponds(i2d_X509_CRL)]
2023 to_der,
2024 ffi::i2d_X509_CRL
2025 }
2026
2027 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2029 unsafe {
2030 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2031 if revoked.is_null() {
2032 None
2033 } else {
2034 Some(StackRef::from_ptr(revoked))
2035 }
2036 }
2037 }
2038
2039 #[corresponds(X509_CRL_get0_lastUpdate)]
2041 pub fn last_update(&self) -> &Asn1TimeRef {
2042 unsafe {
2043 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2044 assert!(!date.is_null());
2045 Asn1TimeRef::from_ptr(date as *mut _)
2046 }
2047 }
2048
2049 #[corresponds(X509_CRL_get0_nextUpdate)]
2053 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2054 unsafe {
2055 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2056 Asn1TimeRef::from_const_ptr_opt(date)
2057 }
2058 }
2059
2060 #[corresponds(X509_CRL_get0_by_serial)]
2062 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2063 unsafe {
2064 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2065 let status =
2066 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2067 CrlStatus::from_ffi_status(status, ret)
2068 }
2069 }
2070
2071 #[corresponds(X509_CRL_get0_by_cert)]
2073 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2074 unsafe {
2075 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2076 let status =
2077 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2078 CrlStatus::from_ffi_status(status, ret)
2079 }
2080 }
2081
2082 #[corresponds(X509_CRL_get_issuer)]
2084 pub fn issuer_name(&self) -> &X509NameRef {
2085 unsafe {
2086 let name = X509_CRL_get_issuer(self.as_ptr());
2087 assert!(!name.is_null());
2088 X509NameRef::from_ptr(name as *mut _)
2089 }
2090 }
2091
2092 #[corresponds(X509_CRL_verify)]
2099 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2100 where
2101 T: HasPublic,
2102 {
2103 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2104 }
2105
2106 #[corresponds(X509_CRL_get_ext_d2i)]
2110 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2111 let mut critical = -1;
2112 let out = unsafe {
2113 let ext = ffi::X509_CRL_get_ext_d2i(
2115 self.as_ptr(),
2116 T::NID.as_raw(),
2117 &mut critical as *mut _,
2118 ptr::null_mut(),
2119 );
2120 T::Output::from_ptr_opt(ext as *mut _)
2123 };
2124 match (critical, out) {
2125 (0, Some(out)) => Ok(Some((false, out))),
2126 (1, Some(out)) => Ok(Some((true, out))),
2127 (-1 | -2, _) => Ok(None),
2129 (0 | 1, None) => Err(ErrorStack::get()),
2132 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2133 }
2134 }
2135}
2136
2137#[derive(Copy, Clone, PartialEq, Eq)]
2139pub struct X509VerifyResult(c_int);
2140
2141impl fmt::Debug for X509VerifyResult {
2142 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2143 fmt.debug_struct("X509VerifyResult")
2144 .field("code", &self.0)
2145 .field("error", &self.error_string())
2146 .finish()
2147 }
2148}
2149
2150impl fmt::Display for X509VerifyResult {
2151 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2152 fmt.write_str(self.error_string())
2153 }
2154}
2155
2156impl Error for X509VerifyResult {}
2157
2158impl X509VerifyResult {
2159 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2166 X509VerifyResult(err)
2167 }
2168
2169 #[allow(clippy::trivially_copy_pass_by_ref)]
2171 pub fn as_raw(&self) -> c_int {
2172 self.0
2173 }
2174
2175 #[corresponds(X509_verify_cert_error_string)]
2177 #[allow(clippy::trivially_copy_pass_by_ref)]
2178 pub fn error_string(&self) -> &'static str {
2179 ffi::init();
2180
2181 unsafe {
2182 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2183 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2184 }
2185 }
2186
2187 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2189 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2191 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2192}
2193
2194foreign_type_and_impl_send_sync! {
2195 type CType = ffi::GENERAL_NAME;
2196 fn drop = ffi::GENERAL_NAME_free;
2197
2198 pub struct GeneralName;
2200 pub struct GeneralNameRef;
2202}
2203
2204impl GeneralName {
2205 unsafe fn new(
2206 type_: c_int,
2207 asn1_type: Asn1Type,
2208 value: &[u8],
2209 ) -> Result<GeneralName, ErrorStack> {
2210 ffi::init();
2211 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2212 (*gn.as_ptr()).type_ = type_;
2213 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2214 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2215
2216 #[cfg(any(boringssl, awslc))]
2217 {
2218 (*gn.as_ptr()).d.ptr = s.cast();
2219 }
2220 #[cfg(not(any(boringssl, awslc)))]
2221 {
2222 (*gn.as_ptr()).d = s.cast();
2223 }
2224
2225 Ok(gn)
2226 }
2227
2228 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2229 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2230 }
2231
2232 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2233 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2234 }
2235
2236 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2237 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2238 }
2239
2240 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2241 match ip {
2242 IpAddr::V4(addr) => unsafe {
2243 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2244 },
2245 IpAddr::V6(addr) => unsafe {
2246 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2247 },
2248 }
2249 }
2250
2251 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2252 unsafe {
2253 ffi::init();
2254 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2255 (*gn).type_ = ffi::GEN_RID;
2256
2257 #[cfg(any(boringssl, awslc))]
2258 {
2259 (*gn).d.registeredID = oid.as_ptr();
2260 }
2261 #[cfg(not(any(boringssl, awslc)))]
2262 {
2263 (*gn).d = oid.as_ptr().cast();
2264 }
2265
2266 mem::forget(oid);
2267
2268 Ok(GeneralName::from_ptr(gn))
2269 }
2270 }
2271
2272 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2273 unsafe {
2274 ffi::init();
2275
2276 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2277 ptr::null_mut(),
2278 &mut value.as_ptr().cast(),
2279 value.len().try_into().unwrap(),
2280 ))?;
2281
2282 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2283 (*gn).type_ = ffi::GEN_OTHERNAME;
2284
2285 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2286 gn,
2287 oid.as_ptr().cast(),
2288 typ,
2289 )) {
2290 ffi::GENERAL_NAME_free(gn);
2291 return Err(e);
2292 }
2293
2294 mem::forget(oid);
2295
2296 Ok(GeneralName::from_ptr(gn))
2297 }
2298 }
2299
2300 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2301 unsafe {
2302 ffi::init();
2303 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2304 (*gn).type_ = ffi::GEN_DIRNAME;
2305
2306 let dup = match name.to_owned() {
2307 Ok(dup) => dup,
2308 Err(e) => {
2309 ffi::GENERAL_NAME_free(gn);
2310 return Err(e);
2311 }
2312 };
2313
2314 #[cfg(any(boringssl, awslc))]
2315 {
2316 (*gn).d.directoryName = dup.as_ptr();
2317 }
2318 #[cfg(not(any(boringssl, awslc)))]
2319 {
2320 (*gn).d = dup.as_ptr().cast();
2321 }
2322
2323 std::mem::forget(dup);
2324
2325 Ok(GeneralName::from_ptr(gn))
2326 }
2327 }
2328}
2329
2330impl GeneralNameRef {
2331 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2332 unsafe {
2333 if (*self.as_ptr()).type_ != ffi_type {
2334 return None;
2335 }
2336
2337 #[cfg(any(boringssl, awslc))]
2338 let d = (*self.as_ptr()).d.ptr;
2339 #[cfg(not(any(boringssl, awslc)))]
2340 let d = (*self.as_ptr()).d;
2341
2342 let ptr = ASN1_STRING_get0_data(d as *mut _);
2343 let len = ffi::ASN1_STRING_length(d as *mut _);
2344
2345 #[allow(clippy::unnecessary_cast)]
2346 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2347 str::from_utf8(slice).ok()
2351 }
2352 }
2353
2354 pub fn email(&self) -> Option<&str> {
2356 self.ia5_string(ffi::GEN_EMAIL)
2357 }
2358
2359 pub fn directory_name(&self) -> Option<&X509NameRef> {
2361 unsafe {
2362 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2363 return None;
2364 }
2365
2366 #[cfg(any(boringssl, awslc))]
2367 let d = (*self.as_ptr()).d.ptr;
2368 #[cfg(not(any(boringssl, awslc)))]
2369 let d = (*self.as_ptr()).d;
2370
2371 Some(X509NameRef::from_const_ptr(d as *const _))
2372 }
2373 }
2374
2375 pub fn dnsname(&self) -> Option<&str> {
2377 self.ia5_string(ffi::GEN_DNS)
2378 }
2379
2380 pub fn uri(&self) -> Option<&str> {
2382 self.ia5_string(ffi::GEN_URI)
2383 }
2384
2385 pub fn ipaddress(&self) -> Option<&[u8]> {
2387 unsafe {
2388 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2389 return None;
2390 }
2391 #[cfg(any(boringssl, awslc))]
2392 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2393 #[cfg(not(any(boringssl, awslc)))]
2394 let d = (*self.as_ptr()).d;
2395
2396 let ptr = ASN1_STRING_get0_data(d as *mut _);
2397 let len = ffi::ASN1_STRING_length(d as *mut _);
2398
2399 #[allow(clippy::unnecessary_cast)]
2400 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2401 }
2402 }
2403}
2404
2405impl fmt::Debug for GeneralNameRef {
2406 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2407 if let Some(email) = self.email() {
2408 formatter.write_str(email)
2409 } else if let Some(dnsname) = self.dnsname() {
2410 formatter.write_str(dnsname)
2411 } else if let Some(uri) = self.uri() {
2412 formatter.write_str(uri)
2413 } else if let Some(ipaddress) = self.ipaddress() {
2414 let address = <[u8; 16]>::try_from(ipaddress)
2415 .map(IpAddr::from)
2416 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2417 match address {
2418 Ok(a) => fmt::Debug::fmt(&a, formatter),
2419 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2420 }
2421 } else {
2422 formatter.write_str("(empty)")
2423 }
2424 }
2425}
2426
2427impl Stackable for GeneralName {
2428 type StackType = ffi::stack_st_GENERAL_NAME;
2429}
2430
2431foreign_type_and_impl_send_sync! {
2432 type CType = ffi::DIST_POINT;
2433 fn drop = ffi::DIST_POINT_free;
2434
2435 pub struct DistPoint;
2437 pub struct DistPointRef;
2439}
2440
2441impl DistPointRef {
2442 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2444 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2445 }
2446}
2447
2448foreign_type_and_impl_send_sync! {
2449 type CType = ffi::DIST_POINT_NAME;
2450 fn drop = ffi::DIST_POINT_NAME_free;
2451
2452 pub struct DistPointName;
2454 pub struct DistPointNameRef;
2456}
2457
2458impl DistPointNameRef {
2459 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2461 unsafe {
2462 if (*self.as_ptr()).type_ != 0 {
2463 return None;
2464 }
2465 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2466 }
2467 }
2468}
2469
2470impl Stackable for DistPoint {
2471 type StackType = ffi::stack_st_DIST_POINT;
2472}
2473
2474foreign_type_and_impl_send_sync! {
2475 type CType = ffi::ACCESS_DESCRIPTION;
2476 fn drop = ffi::ACCESS_DESCRIPTION_free;
2477
2478 pub struct AccessDescription;
2480 pub struct AccessDescriptionRef;
2482}
2483
2484impl AccessDescriptionRef {
2485 pub fn method(&self) -> &Asn1ObjectRef {
2487 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2488 }
2489
2490 pub fn location(&self) -> &GeneralNameRef {
2492 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2493 }
2494}
2495
2496impl Stackable for AccessDescription {
2497 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2498}
2499
2500foreign_type_and_impl_send_sync! {
2501 type CType = ffi::X509_ALGOR;
2502 fn drop = ffi::X509_ALGOR_free;
2503
2504 pub struct X509Algorithm;
2506 pub struct X509AlgorithmRef;
2508}
2509
2510impl X509AlgorithmRef {
2511 pub fn object(&self) -> &Asn1ObjectRef {
2513 unsafe {
2514 let mut oid = ptr::null();
2515 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2516 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2517 }
2518 }
2519}
2520
2521foreign_type_and_impl_send_sync! {
2522 type CType = ffi::X509_OBJECT;
2523 fn drop = X509_OBJECT_free;
2524
2525 pub struct X509Object;
2527 pub struct X509ObjectRef;
2529}
2530
2531impl X509ObjectRef {
2532 pub fn x509(&self) -> Option<&X509Ref> {
2533 unsafe {
2534 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2535 X509Ref::from_const_ptr_opt(ptr)
2536 }
2537 }
2538}
2539
2540impl Stackable for X509Object {
2541 type StackType = ffi::stack_st_X509_OBJECT;
2542}
2543
2544use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2545
2546use ffi::{
2547 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2548 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2549};
2550
2551use ffi::X509_OBJECT_free;
2552use ffi::X509_OBJECT_get0_X509;
2553
2554use ffi::{
2555 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2556 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2557};
2558
2559#[derive(Copy, Clone, PartialEq, Eq)]
2560pub struct X509PurposeId(c_int);
2561
2562impl X509PurposeId {
2563 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2564 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2565 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2566 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2567 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2568 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2569 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2570 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2571 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2572 #[cfg(ossl320)]
2573 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2574
2575 pub fn from_raw(id: c_int) -> Self {
2577 X509PurposeId(id)
2578 }
2579
2580 pub fn as_raw(&self) -> c_int {
2582 self.0
2583 }
2584}
2585
2586pub struct X509PurposeRef(Opaque);
2588
2589impl ForeignTypeRef for X509PurposeRef {
2591 type CType = ffi::X509_PURPOSE;
2592}
2593
2594impl X509PurposeRef {
2595 #[allow(clippy::unnecessary_cast)]
2609 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2610 unsafe {
2611 let sname = CString::new(sname).unwrap();
2612 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2613 Ok(purpose)
2614 }
2615 }
2616 #[corresponds(X509_PURPOSE_get0)]
2619 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2620 unsafe {
2621 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2622 Ok(X509PurposeRef::from_const_ptr(ptr))
2623 }
2624 }
2625
2626 pub fn purpose(&self) -> X509PurposeId {
2637 unsafe {
2638 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2639 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2640 }
2641 }
2642}