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, 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_get0_extensions)]
424 #[cfg(any(ossl111, libressl, boringssl, awslc))]
425 pub fn extensions(&self) -> Option<&StackRef<X509Extension>> {
426 unsafe {
427 let extensions = ffi::X509_get0_extensions(self.as_ptr());
428 StackRef::from_const_ptr_opt(extensions)
429 }
430 }
431
432 #[corresponds(X509_get0_ext_by_NID)]
434 #[cfg(any(ossl111, libressl, boringssl, awslc))]
435 pub fn get_extension_location(&self, nid: Nid, lastpos: Option<i32>) -> Option<i32> {
436 let lastpos = lastpos.unwrap_or(-1);
437 unsafe {
438 let id = ffi::X509_get_ext_by_NID(self.as_ptr(), nid.as_raw(), lastpos as _);
439 if id == -1 {
440 None
441 } else {
442 Some(id)
443 }
444 }
445 }
446
447 #[corresponds(X509_get_ext)]
449 #[cfg(any(all(ossl111, not(ossl400)), libressl, boringssl, awslc))]
450 pub fn get_extension(&self, loc: i32) -> Result<&X509ExtensionRef, ErrorStack> {
451 unsafe {
452 let ext = cvt_p(ffi::X509_get_ext(self.as_ptr(), loc as _))?;
453 Ok(X509ExtensionRef::from_ptr(ext))
454 }
455 }
456
457 #[corresponds(X509_get_ext)]
459 #[cfg(ossl400)]
460 pub fn get_extension(&self, loc: i32) -> Result<&X509ExtensionRef, ErrorStack> {
461 unsafe {
462 let ext = cvt_p_const(ffi::X509_get_ext(self.as_ptr(), loc as _))?;
463 Ok(X509ExtensionRef::from_ptr(ext as *mut _))
464 }
465 }
466
467 #[corresponds(X509_get_key_usage)]
469 #[cfg(any(ossl110, libressl, boringssl, awslc))]
470 pub fn key_usage(&self) -> Option<u32> {
471 let flags = unsafe { ffi::X509_get_key_usage(self.as_ptr()) };
472 if flags == u32::MAX {
473 None
474 } else {
475 Some(flags)
476 }
477 }
478
479 #[corresponds(X509_get_ext_d2i)]
481 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
482 unsafe {
483 let stack = ffi::X509_get_ext_d2i(
484 self.as_ptr(),
485 ffi::NID_subject_alt_name,
486 ptr::null_mut(),
487 ptr::null_mut(),
488 );
489 Stack::from_ptr_opt(stack as *mut _)
490 }
491 }
492
493 #[corresponds(X509_get_ext_d2i)]
495 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
496 unsafe {
497 let stack = ffi::X509_get_ext_d2i(
498 self.as_ptr(),
499 ffi::NID_crl_distribution_points,
500 ptr::null_mut(),
501 ptr::null_mut(),
502 );
503 Stack::from_ptr_opt(stack as *mut _)
504 }
505 }
506
507 #[corresponds(X509_get_ext_d2i)]
509 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
510 unsafe {
511 let stack = ffi::X509_get_ext_d2i(
512 self.as_ptr(),
513 ffi::NID_issuer_alt_name,
514 ptr::null_mut(),
515 ptr::null_mut(),
516 );
517 Stack::from_ptr_opt(stack as *mut _)
518 }
519 }
520
521 #[corresponds(X509_get_ext_d2i)]
525 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
526 unsafe {
527 let stack = ffi::X509_get_ext_d2i(
528 self.as_ptr(),
529 ffi::NID_info_access,
530 ptr::null_mut(),
531 ptr::null_mut(),
532 );
533 Stack::from_ptr_opt(stack as *mut _)
534 }
535 }
536
537 #[corresponds(X509_get_pathlen)]
539 #[cfg(any(ossl110, boringssl, awslc))]
540 pub fn pathlen(&self) -> Option<u32> {
541 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
542 u32::try_from(v).ok()
543 }
544
545 #[allow(deprecated)]
547 #[cfg(libressl)]
548 pub fn pathlen(&self) -> Option<u32> {
549 let bs = self.basic_constraints()?;
550 let pathlen = bs.pathlen()?;
551 u32::try_from(pathlen.get()).ok()
552 }
553
554 pub fn basic_constraints(&self) -> Option<BasicConstraints> {
556 unsafe {
557 let data = ffi::X509_get_ext_d2i(
558 self.as_ptr(),
559 ffi::NID_basic_constraints,
560 ptr::null_mut(),
561 ptr::null_mut(),
562 );
563 BasicConstraints::from_ptr_opt(data as _)
564 }
565 }
566
567 #[corresponds(X509_get0_subject_key_id)]
569 #[cfg(any(ossl110, boringssl, awslc))]
570 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
571 unsafe {
572 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
573 Asn1OctetStringRef::from_const_ptr_opt(data)
574 }
575 }
576
577 #[cfg(libressl)]
579 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
580 unsafe {
581 let data = ffi::X509_get_ext_d2i(
582 self.as_ptr(),
583 ffi::NID_subject_key_identifier,
584 ptr::null_mut(),
585 ptr::null_mut(),
586 );
587 Asn1OctetStringRef::from_const_ptr_opt(data as _)
588 }
589 }
590
591 #[corresponds(X509_get0_authority_key_id)]
593 #[cfg(any(ossl110, boringssl, awslc))]
594 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
595 unsafe {
596 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
597 Asn1OctetStringRef::from_const_ptr_opt(data)
598 }
599 }
600
601 #[corresponds(X509_get0_authority_issuer)]
603 #[cfg(any(ossl111d, boringssl, awslc))]
604 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
605 unsafe {
606 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
607 StackRef::from_const_ptr_opt(stack)
608 }
609 }
610
611 #[corresponds(X509_get0_authority_serial)]
613 #[cfg(any(ossl111d, boringssl, awslc))]
614 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
615 unsafe {
616 let r = ffi::X509_get0_authority_serial(self.as_ptr());
617 Asn1IntegerRef::from_const_ptr_opt(r)
618 }
619 }
620
621 #[corresponds(X509_get_pubkey)]
622 #[cfg(not(ossl400))]
623 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
624 unsafe {
625 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
626 Ok(PKey::from_ptr(pkey))
627 }
628 }
629
630 #[corresponds(X509_get_pubkey)]
631 #[cfg(ossl400)]
632 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
633 unsafe {
634 let pkey = cvt_p_const(ffi::X509_get_pubkey(self.as_ptr()))?;
635 Ok(PKey::from_ptr(pkey as *mut _))
636 }
637 }
638
639 #[corresponds(X509_get_X509_PUBKEY)]
640 #[cfg(any(all(ossl110, not(ossl400)), libressl, boringssl, awslc))]
641 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
642 unsafe {
643 let key = cvt_p(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?;
644 Ok(X509PubkeyRef::from_ptr(key))
645 }
646 }
647
648 #[corresponds(X509_get_X509_PUBKEY)]
649 #[cfg(ossl400)]
650 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
651 unsafe {
652 let key = cvt_p_const(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?;
653 Ok(X509PubkeyRef::from_ptr(key as *mut _))
654 }
655 }
656
657 digest! {
658 digest,
664 ffi::X509_digest
665 }
666
667 digest! {
668 pubkey_digest,
674 ffi::X509_pubkey_digest
675 }
676
677 #[corresponds(X509_getm_notAfter)]
679 pub fn not_after(&self) -> &Asn1TimeRef {
680 unsafe {
681 let date = X509_getm_notAfter(self.as_ptr());
682 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
683 }
684 }
685
686 #[corresponds(X509_getm_notBefore)]
688 pub fn not_before(&self) -> &Asn1TimeRef {
689 unsafe {
690 let date = X509_getm_notBefore(self.as_ptr());
691 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
692 }
693 }
694
695 #[corresponds(X509_get0_signature)]
697 pub fn signature(&self) -> &Asn1BitStringRef {
698 unsafe {
699 let mut signature = ptr::null();
700 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
701 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
702 }
703 }
704
705 #[corresponds(X509_get0_signature)]
707 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
708 unsafe {
709 let mut algor = ptr::null();
710 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
711 X509AlgorithmRef::from_const_ptr_opt(algor)
712 .expect("signature algorithm must not be null")
713 }
714 }
715
716 #[corresponds(X509_get1_ocsp)]
722 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
723 unsafe {
724 let stack: Stack<OpensslString> =
725 cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))?;
726 for entry in &stack {
727 let bytes = CStr::from_ptr(entry.as_ptr()).to_bytes();
728 if str::from_utf8(bytes).is_err() {
729 return Err(ErrorStack::internal_error(
730 "OCSP responder URL contained invalid UTF-8",
731 ));
732 }
733 }
734 Ok(stack)
735 }
736 }
737
738 #[corresponds(X509_check_issued)]
740 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
741 unsafe {
742 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
743 X509VerifyResult::from_raw(r)
744 }
745 }
746
747 #[corresponds(X509_get_version)]
752 #[cfg(any(ossl110, libressl, boringssl, awslc))]
753 #[allow(clippy::unnecessary_cast)]
754 pub fn version(&self) -> i32 {
755 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
756 }
757
758 #[corresponds(X509_verify)]
765 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
766 where
767 T: HasPublic,
768 {
769 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
770 }
771
772 #[corresponds(X509_get_serialNumber)]
774 pub fn serial_number(&self) -> &Asn1IntegerRef {
775 unsafe {
776 let r = ffi::X509_get_serialNumber(self.as_ptr());
777 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
778 }
779 }
780
781 #[corresponds(X509_alias_get0)]
787 pub fn alias(&self) -> Option<&[u8]> {
788 unsafe {
789 let mut len = 0;
790 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
791 if ptr.is_null() {
792 None
793 } else {
794 Some(util::from_raw_parts(ptr, len as usize))
795 }
796 }
797 }
798
799 to_pem! {
800 #[corresponds(PEM_write_bio_X509)]
804 to_pem,
805 ffi::PEM_write_bio_X509
806 }
807
808 to_der! {
809 #[corresponds(i2d_X509)]
811 to_der,
812 ffi::i2d_X509
813 }
814
815 to_pem! {
816 #[corresponds(X509_print)]
818 to_text,
819 ffi::X509_print
820 }
821}
822
823impl ToOwned for X509Ref {
824 type Owned = X509;
825
826 fn to_owned(&self) -> X509 {
827 unsafe {
828 X509_up_ref(self.as_ptr());
829 X509::from_ptr(self.as_ptr())
830 }
831 }
832}
833
834impl Ord for X509Ref {
835 fn cmp(&self, other: &Self) -> cmp::Ordering {
836 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
839 cmp.cmp(&0)
840 }
841}
842
843impl PartialOrd for X509Ref {
844 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
845 Some(self.cmp(other))
846 }
847}
848
849impl PartialOrd<X509> for X509Ref {
850 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
851 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
852 }
853}
854
855impl PartialEq for X509Ref {
856 fn eq(&self, other: &Self) -> bool {
857 self.cmp(other) == cmp::Ordering::Equal
858 }
859}
860
861impl PartialEq<X509> for X509Ref {
862 fn eq(&self, other: &X509) -> bool {
863 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
864 }
865}
866
867impl Eq for X509Ref {}
868
869impl X509 {
870 pub fn builder() -> Result<X509Builder, ErrorStack> {
872 X509Builder::new()
873 }
874
875 from_pem! {
876 #[corresponds(PEM_read_bio_X509)]
880 from_pem,
881 X509,
882 ffi::PEM_read_bio_X509
883 }
884
885 from_der! {
886 #[corresponds(d2i_X509)]
888 from_der,
889 X509,
890 ffi::d2i_X509
891 }
892
893 #[corresponds(PEM_read_bio_X509)]
895 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
896 unsafe {
897 ffi::init();
898 let bio = MemBioSlice::new(pem)?;
899
900 let mut certs = vec![];
901 loop {
902 let r =
903 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
904 if r.is_null() {
905 let e = ErrorStack::get();
906
907 if let Some(err) = e.errors().last() {
908 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
909 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
910 {
911 break;
912 }
913 }
914
915 return Err(e);
916 } else {
917 certs.push(X509(r));
918 }
919 }
920
921 Ok(certs)
922 }
923 }
924}
925
926impl Clone for X509 {
927 fn clone(&self) -> X509 {
928 X509Ref::to_owned(self)
929 }
930}
931
932impl fmt::Debug for X509 {
933 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
934 let serial = match &self.serial_number().to_bn() {
935 Ok(bn) => match bn.to_hex_str() {
936 Ok(hex) => hex.to_string(),
937 Err(_) => "".to_string(),
938 },
939 Err(_) => "".to_string(),
940 };
941 let mut debug_struct = formatter.debug_struct("X509");
942 debug_struct.field("serial_number", &serial);
943 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
944 debug_struct.field("issuer", &self.issuer_name());
945 debug_struct.field("subject", &self.subject_name());
946 if let Some(subject_alt_names) = &self.subject_alt_names() {
947 debug_struct.field("subject_alt_names", subject_alt_names);
948 }
949 debug_struct.field("not_before", &self.not_before());
950 debug_struct.field("not_after", &self.not_after());
951
952 if let Ok(public_key) = &self.public_key() {
953 debug_struct.field("public_key", public_key);
954 };
955 debug_struct.finish()
958 }
959}
960
961impl AsRef<X509Ref> for X509Ref {
962 fn as_ref(&self) -> &X509Ref {
963 self
964 }
965}
966
967impl Stackable for X509 {
968 type StackType = ffi::stack_st_X509;
969}
970
971impl Ord for X509 {
972 fn cmp(&self, other: &Self) -> cmp::Ordering {
973 X509Ref::cmp(self, other)
974 }
975}
976
977impl PartialOrd for X509 {
978 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
979 Some(self.cmp(other))
980 }
981}
982
983impl PartialOrd<X509Ref> for X509 {
984 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
985 X509Ref::partial_cmp(self, other)
986 }
987}
988
989impl PartialEq for X509 {
990 fn eq(&self, other: &Self) -> bool {
991 X509Ref::eq(self, other)
992 }
993}
994
995impl PartialEq<X509Ref> for X509 {
996 fn eq(&self, other: &X509Ref) -> bool {
997 X509Ref::eq(self, other)
998 }
999}
1000
1001impl Eq for X509 {}
1002
1003pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
1005
1006impl X509v3Context<'_> {
1007 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
1008 &self.0 as *const _ as *mut _
1009 }
1010}
1011
1012foreign_type_and_impl_send_sync! {
1013 type CType = ffi::X509_EXTENSION;
1014 fn drop = ffi::X509_EXTENSION_free;
1015
1016 pub struct X509Extension;
1018 pub struct X509ExtensionRef;
1020}
1021
1022impl Stackable for X509Extension {
1023 type StackType = ffi::stack_st_X509_EXTENSION;
1024}
1025
1026impl X509Extension {
1027 #[deprecated(
1041 note = "Use x509::extension types or new_from_der instead",
1042 since = "0.10.51"
1043 )]
1044 pub fn new(
1045 conf: Option<&ConfRef>,
1046 context: Option<&X509v3Context<'_>>,
1047 name: &str,
1048 value: &str,
1049 ) -> Result<X509Extension, ErrorStack> {
1050 let name = CString::new(name).unwrap();
1051 let value = CString::new(value).unwrap();
1052 let mut ctx;
1053 unsafe {
1054 ffi::init();
1055 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1056 let context_ptr = match context {
1057 Some(c) => c.as_ptr(),
1058 None => {
1059 ctx = mem::zeroed();
1060
1061 ffi::X509V3_set_ctx(
1062 &mut ctx,
1063 ptr::null_mut(),
1064 ptr::null_mut(),
1065 ptr::null_mut(),
1066 ptr::null_mut(),
1067 0,
1068 );
1069 &mut ctx
1070 }
1071 };
1072 let name = name.as_ptr() as *mut _;
1073 let value = value.as_ptr() as *mut _;
1074
1075 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
1076 }
1077 }
1078
1079 #[deprecated(
1093 note = "Use x509::extension types or new_from_der instead",
1094 since = "0.10.51"
1095 )]
1096 pub fn new_nid(
1097 conf: Option<&ConfRef>,
1098 context: Option<&X509v3Context<'_>>,
1099 name: Nid,
1100 value: &str,
1101 ) -> Result<X509Extension, ErrorStack> {
1102 let value = CString::new(value).unwrap();
1103 let mut ctx;
1104 unsafe {
1105 ffi::init();
1106 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1107 let context_ptr = match context {
1108 Some(c) => c.as_ptr(),
1109 None => {
1110 ctx = mem::zeroed();
1111
1112 ffi::X509V3_set_ctx(
1113 &mut ctx,
1114 ptr::null_mut(),
1115 ptr::null_mut(),
1116 ptr::null_mut(),
1117 ptr::null_mut(),
1118 0,
1119 );
1120 &mut ctx
1121 }
1122 };
1123 let name = name.as_raw();
1124 let value = value.as_ptr() as *mut _;
1125
1126 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1127 }
1128 }
1129
1130 pub fn new_from_der(
1140 oid: &Asn1ObjectRef,
1141 critical: bool,
1142 der_contents: &Asn1OctetStringRef,
1143 ) -> Result<X509Extension, ErrorStack> {
1144 unsafe {
1145 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1146 ptr::null_mut(),
1147 oid.as_ptr(),
1148 critical as _,
1149 der_contents.as_ptr(),
1150 ))
1151 .map(X509Extension)
1152 }
1153 }
1154
1155 pub fn new_subject_alt_name(
1157 stack: Stack<GeneralName>,
1158 critical: bool,
1159 ) -> Result<X509Extension, ErrorStack> {
1160 unsafe { Self::new_internal(Nid::SUBJECT_ALT_NAME, critical, stack.as_ptr().cast()) }
1161 }
1162
1163 pub(crate) unsafe fn new_internal(
1164 nid: Nid,
1165 critical: bool,
1166 value: *mut c_void,
1167 ) -> Result<X509Extension, ErrorStack> {
1168 ffi::init();
1169 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1170 }
1171
1172 #[cfg(not(libressl390))]
1178 #[corresponds(X509V3_EXT_add_alias)]
1179 #[deprecated(
1180 note = "Use x509::extension types or new_from_der and then this is not necessary",
1181 since = "0.10.51"
1182 )]
1183 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1184 ffi::init();
1185 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1186 }
1187}
1188
1189impl X509ExtensionRef {
1190 to_der! {
1191 #[corresponds(i2d_X509_EXTENSION)]
1193 to_der,
1194 ffi::i2d_X509_EXTENSION
1195 }
1196}
1197
1198pub struct X509NameBuilder(X509Name);
1200
1201impl X509NameBuilder {
1202 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1204 unsafe {
1205 ffi::init();
1206 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1207 }
1208 }
1209
1210 #[corresponds(X509_NAME_add_entry)]
1212 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1213 unsafe {
1214 cvt(ffi::X509_NAME_add_entry(
1215 self.0.as_ptr(),
1216 ne.as_ptr(),
1217 -1,
1218 0,
1219 ))
1220 .map(|_| ())
1221 }
1222 }
1223
1224 #[corresponds(X509_NAME_add_entry_by_txt)]
1226 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1227 unsafe {
1228 let field = CString::new(field).unwrap();
1229 assert!(value.len() <= crate::SLenType::MAX as usize);
1230 cvt(ffi::X509_NAME_add_entry_by_txt(
1231 self.0.as_ptr(),
1232 field.as_ptr() as *mut _,
1233 ffi::MBSTRING_UTF8,
1234 value.as_ptr(),
1235 value.len() as crate::SLenType,
1236 -1,
1237 0,
1238 ))
1239 .map(|_| ())
1240 }
1241 }
1242
1243 #[corresponds(X509_NAME_add_entry_by_txt)]
1245 pub fn append_entry_by_text_with_type(
1246 &mut self,
1247 field: &str,
1248 value: &str,
1249 ty: Asn1Type,
1250 ) -> Result<(), ErrorStack> {
1251 unsafe {
1252 let field = CString::new(field).unwrap();
1253 assert!(value.len() <= crate::SLenType::MAX as usize);
1254 cvt(ffi::X509_NAME_add_entry_by_txt(
1255 self.0.as_ptr(),
1256 field.as_ptr() as *mut _,
1257 ty.as_raw(),
1258 value.as_ptr(),
1259 value.len() as crate::SLenType,
1260 -1,
1261 0,
1262 ))
1263 .map(|_| ())
1264 }
1265 }
1266
1267 #[corresponds(X509_NAME_add_entry_by_NID)]
1269 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1270 unsafe {
1271 assert!(value.len() <= crate::SLenType::MAX as usize);
1272 cvt(ffi::X509_NAME_add_entry_by_NID(
1273 self.0.as_ptr(),
1274 field.as_raw(),
1275 ffi::MBSTRING_UTF8,
1276 value.as_ptr() as *mut _,
1277 value.len() as crate::SLenType,
1278 -1,
1279 0,
1280 ))
1281 .map(|_| ())
1282 }
1283 }
1284
1285 #[corresponds(X509_NAME_add_entry_by_NID)]
1287 pub fn append_entry_by_nid_with_type(
1288 &mut self,
1289 field: Nid,
1290 value: &str,
1291 ty: Asn1Type,
1292 ) -> Result<(), ErrorStack> {
1293 unsafe {
1294 assert!(value.len() <= crate::SLenType::MAX as usize);
1295 cvt(ffi::X509_NAME_add_entry_by_NID(
1296 self.0.as_ptr(),
1297 field.as_raw(),
1298 ty.as_raw(),
1299 value.as_ptr() as *mut _,
1300 value.len() as crate::SLenType,
1301 -1,
1302 0,
1303 ))
1304 .map(|_| ())
1305 }
1306 }
1307
1308 pub fn build(self) -> X509Name {
1310 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1314 }
1315}
1316
1317foreign_type_and_impl_send_sync! {
1318 type CType = ffi::X509_NAME;
1319 fn drop = ffi::X509_NAME_free;
1320
1321 pub struct X509Name;
1323 pub struct X509NameRef;
1325}
1326
1327impl X509Name {
1328 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1330 X509NameBuilder::new()
1331 }
1332
1333 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1337 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1338 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1339 }
1340
1341 from_der! {
1342 from_der,
1348 X509Name,
1349 ffi::d2i_X509_NAME
1350 }
1351}
1352
1353impl Stackable for X509Name {
1354 type StackType = ffi::stack_st_X509_NAME;
1355}
1356
1357impl X509NameRef {
1358 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1360 X509NameEntries {
1361 name: self,
1362 nid: Some(nid),
1363 loc: -1,
1364 }
1365 }
1366
1367 pub fn entries(&self) -> X509NameEntries<'_> {
1369 X509NameEntries {
1370 name: self,
1371 nid: None,
1372 loc: -1,
1373 }
1374 }
1375
1376 #[corresponds(X509_NAME_cmp)]
1383 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1384 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1385 if cfg!(ossl300) && cmp == -2 {
1386 return Err(ErrorStack::get());
1387 }
1388 Ok(cmp.cmp(&0))
1389 }
1390
1391 #[corresponds(X509_NAME_dup)]
1393 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1394 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1395 }
1396
1397 to_der! {
1398 to_der,
1404 ffi::i2d_X509_NAME
1405 }
1406
1407 digest! {
1408 digest,
1414 ffi::X509_NAME_digest
1415 }
1416}
1417
1418impl fmt::Debug for X509NameRef {
1419 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1420 formatter.debug_list().entries(self.entries()).finish()
1421 }
1422}
1423
1424pub struct X509NameEntries<'a> {
1426 name: &'a X509NameRef,
1427 nid: Option<Nid>,
1428 loc: c_int,
1429}
1430
1431impl<'a> Iterator for X509NameEntries<'a> {
1432 type Item = &'a X509NameEntryRef;
1433
1434 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1435 unsafe {
1436 match self.nid {
1437 Some(nid) => {
1438 self.loc =
1440 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1441 if self.loc == -1 {
1442 return None;
1443 }
1444 }
1445 None => {
1446 self.loc += 1;
1448 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1449 return None;
1450 }
1451 }
1452 }
1453
1454 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1455
1456 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1457 }
1458 }
1459}
1460
1461foreign_type_and_impl_send_sync! {
1462 type CType = ffi::X509_NAME_ENTRY;
1463 fn drop = ffi::X509_NAME_ENTRY_free;
1464
1465 pub struct X509NameEntry;
1467 pub struct X509NameEntryRef;
1469}
1470
1471impl X509NameEntryRef {
1472 #[corresponds(X509_NAME_ENTRY_get_data)]
1474 pub fn data(&self) -> &Asn1StringRef {
1475 unsafe {
1476 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1477 Asn1StringRef::from_ptr(data as *mut _)
1478 }
1479 }
1480
1481 #[corresponds(X509_NAME_ENTRY_get_object)]
1484 pub fn object(&self) -> &Asn1ObjectRef {
1485 unsafe {
1486 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1487 Asn1ObjectRef::from_ptr(object as *mut _)
1488 }
1489 }
1490}
1491
1492impl fmt::Debug for X509NameEntryRef {
1493 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1494 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1495 }
1496}
1497
1498foreign_type_and_impl_send_sync! {
1499 type CType = ffi::X509_PUBKEY;
1500 fn drop = ffi::X509_PUBKEY_free;
1501
1502 pub struct X509Pubkey;
1504 pub struct X509PubkeyRef;
1506}
1507
1508impl X509Pubkey {
1509 from_der! {
1510 from_der,
1516 X509Pubkey,
1517 ffi::d2i_X509_PUBKEY
1518 }
1519
1520 pub fn from_pubkey<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
1526 where
1527 T: HasPublic,
1528 {
1529 let mut p = ptr::null_mut();
1530 unsafe {
1531 cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?;
1532 }
1533 Ok(X509Pubkey(p))
1534 }
1535}
1536
1537impl X509PubkeyRef {
1538 #[corresponds(X509_PUBKEY_dup)]
1540 #[cfg(ossl300)]
1541 pub fn to_owned(&self) -> Result<X509Pubkey, ErrorStack> {
1542 unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) }
1543 }
1544
1545 to_der! {
1546 to_der,
1552 ffi::i2d_X509_PUBKEY
1553 }
1554
1555 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1561 unsafe {
1562 let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
1563 Ok(PKey::from_ptr(key))
1564 }
1565 }
1566
1567 pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> {
1573 unsafe {
1574 let mut pk = ptr::null_mut() as *const c_uchar;
1575 let mut pkt_len: c_int = 0;
1576 cvt(ffi::X509_PUBKEY_get0_param(
1577 ptr::null_mut(),
1578 &mut pk as *mut _,
1579 &mut pkt_len as *mut _,
1580 ptr::null_mut(),
1581 self.as_ptr(),
1582 ))?;
1583
1584 Ok(util::from_raw_parts(pk, pkt_len as usize))
1585 }
1586 }
1587}
1588
1589pub struct X509ReqBuilder(X509Req);
1591
1592impl X509ReqBuilder {
1593 #[corresponds(X509_REQ_new)]
1595 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1596 unsafe {
1597 ffi::init();
1598 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1599 }
1600 }
1601
1602 #[corresponds(X509_REQ_set_version)]
1604 #[allow(clippy::useless_conversion)]
1605 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1606 unsafe {
1607 cvt(ffi::X509_REQ_set_version(
1608 self.0.as_ptr(),
1609 version as c_long,
1610 ))
1611 .map(|_| ())
1612 }
1613 }
1614
1615 #[corresponds(X509_REQ_set_subject_name)]
1617 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1618 unsafe {
1619 cvt(ffi::X509_REQ_set_subject_name(
1620 self.0.as_ptr(),
1621 subject_name.as_ptr(),
1622 ))
1623 .map(|_| ())
1624 }
1625 }
1626
1627 #[corresponds(X509_REQ_set_pubkey)]
1629 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1630 where
1631 T: HasPublic,
1632 {
1633 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1634 }
1635
1636 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1639 unsafe {
1640 let mut ctx = mem::zeroed();
1641
1642 ffi::X509V3_set_ctx(
1643 &mut ctx,
1644 ptr::null_mut(),
1645 ptr::null_mut(),
1646 self.0.as_ptr(),
1647 ptr::null_mut(),
1648 0,
1649 );
1650
1651 if let Some(conf) = conf {
1653 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1654 }
1655
1656 X509v3Context(ctx, PhantomData)
1657 }
1658 }
1659
1660 pub fn add_extensions(
1662 &mut self,
1663 extensions: &StackRef<X509Extension>,
1664 ) -> Result<(), ErrorStack> {
1665 unsafe {
1666 cvt(ffi::X509_REQ_add_extensions(
1667 self.0.as_ptr(),
1668 extensions.as_ptr(),
1669 ))
1670 .map(|_| ())
1671 }
1672 }
1673
1674 #[corresponds(X509_REQ_sign)]
1676 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1677 where
1678 T: HasPrivate,
1679 {
1680 unsafe {
1681 cvt(ffi::X509_REQ_sign(
1682 self.0.as_ptr(),
1683 key.as_ptr(),
1684 hash.as_ptr(),
1685 ))
1686 .map(|_| ())
1687 }
1688 }
1689
1690 pub fn build(self) -> X509Req {
1692 self.0
1693 }
1694}
1695
1696foreign_type_and_impl_send_sync! {
1697 type CType = ffi::X509_REQ;
1698 fn drop = ffi::X509_REQ_free;
1699
1700 pub struct X509Req;
1702 pub struct X509ReqRef;
1704}
1705
1706impl X509Req {
1707 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1709 X509ReqBuilder::new()
1710 }
1711
1712 from_pem! {
1713 from_pem,
1721 X509Req,
1722 ffi::PEM_read_bio_X509_REQ
1723 }
1724
1725 from_der! {
1726 from_der,
1732 X509Req,
1733 ffi::d2i_X509_REQ
1734 }
1735}
1736
1737impl X509ReqRef {
1738 to_pem! {
1739 to_pem,
1747 ffi::PEM_write_bio_X509_REQ
1748 }
1749
1750 to_der! {
1751 to_der,
1757 ffi::i2d_X509_REQ
1758 }
1759
1760 to_pem! {
1761 #[corresponds(X509_Req_print)]
1763 to_text,
1764 ffi::X509_REQ_print
1765 }
1766
1767 digest! {
1768 digest,
1774 ffi::X509_REQ_digest
1775 }
1776
1777 #[corresponds(X509_REQ_get_version)]
1779 #[allow(clippy::unnecessary_cast)]
1780 pub fn version(&self) -> i32 {
1781 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1782 }
1783
1784 #[corresponds(X509_REQ_get_subject_name)]
1786 pub fn subject_name(&self) -> &X509NameRef {
1787 unsafe {
1788 let name = X509_REQ_get_subject_name(self.as_ptr());
1789 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1790 }
1791 }
1792
1793 #[corresponds(X509_REQ_get_pubkey)]
1795 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1796 unsafe {
1797 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1798 Ok(PKey::from_ptr(key))
1799 }
1800 }
1801
1802 #[cfg(ossl110)]
1808 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
1809 unsafe {
1810 let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?;
1811 Ok(X509PubkeyRef::from_ptr(key))
1812 }
1813 }
1814
1815 #[corresponds(X509_REQ_verify)]
1819 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1820 where
1821 T: HasPublic,
1822 {
1823 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1824 }
1825
1826 #[corresponds(X509_REQ_get_extensions)]
1828 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1829 unsafe {
1830 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1831 Ok(Stack::from_ptr(extensions))
1832 }
1833 }
1834}
1835
1836#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1838pub struct CrlReason(c_int);
1839
1840#[allow(missing_docs)] impl CrlReason {
1842 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1843 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1844 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1845 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1846 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1847 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1848 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1849 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1850 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1851 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1852
1853 pub const fn from_raw(value: c_int) -> Self {
1855 CrlReason(value)
1856 }
1857
1858 pub const fn as_raw(&self) -> c_int {
1860 self.0
1861 }
1862}
1863
1864pub struct X509RevokedBuilder(X509Revoked);
1866
1867impl X509RevokedBuilder {
1868 #[corresponds(X509_REVOKED_new)]
1870 pub fn new() -> Result<Self, ErrorStack> {
1871 unsafe {
1872 ffi::init();
1873 cvt_p(ffi::X509_REVOKED_new()).map(|p| X509RevokedBuilder(X509Revoked(p)))
1874 }
1875 }
1876
1877 #[corresponds(X509_REVOKED_set_revocationDate)]
1879 pub fn set_revocation_date(&mut self, date: &Asn1TimeRef) -> Result<(), ErrorStack> {
1880 unsafe {
1881 cvt(ffi::X509_REVOKED_set_revocationDate(
1882 self.0.as_ptr(),
1883 date.as_ptr(),
1884 ))
1885 .map(|_| ())
1886 }
1887 }
1888
1889 #[corresponds(X509_REVOKED_set_serialNumber)]
1891 pub fn set_serial_number(&mut self, serial: &Asn1IntegerRef) -> Result<(), ErrorStack> {
1892 unsafe {
1893 cvt(ffi::X509_REVOKED_set_serialNumber(
1894 self.0.as_ptr(),
1895 serial.as_ptr(),
1896 ))
1897 .map(|_| ())
1898 }
1899 }
1900
1901 pub fn build(self) -> X509Revoked {
1903 self.0
1904 }
1905}
1906
1907foreign_type_and_impl_send_sync! {
1908 type CType = ffi::X509_REVOKED;
1909 fn drop = ffi::X509_REVOKED_free;
1910
1911 pub struct X509Revoked;
1913 pub struct X509RevokedRef;
1915}
1916
1917impl Stackable for X509Revoked {
1918 type StackType = ffi::stack_st_X509_REVOKED;
1919}
1920
1921impl X509Revoked {
1922 from_der! {
1923 #[corresponds(d2i_X509_REVOKED)]
1925 from_der,
1926 X509Revoked,
1927 ffi::d2i_X509_REVOKED
1928 }
1929}
1930
1931impl X509RevokedRef {
1932 to_der! {
1933 #[corresponds(d2i_X509_REVOKED)]
1935 to_der,
1936 ffi::i2d_X509_REVOKED
1937 }
1938
1939 #[corresponds(X509_REVOKED_dup)]
1941 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1942 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1943 }
1944
1945 #[corresponds(X509_REVOKED_get0_revocationDate)]
1947 pub fn revocation_date(&self) -> &Asn1TimeRef {
1948 unsafe {
1949 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1950 assert!(!r.is_null());
1951 Asn1TimeRef::from_ptr(r as *mut _)
1952 }
1953 }
1954
1955 #[corresponds(X509_REVOKED_get0_serialNumber)]
1957 pub fn serial_number(&self) -> &Asn1IntegerRef {
1958 unsafe {
1959 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1960 assert!(!r.is_null());
1961 Asn1IntegerRef::from_ptr(r as *mut _)
1962 }
1963 }
1964
1965 #[corresponds(X509_REVOKED_get_ext_d2i)]
1969 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1970 let mut critical = -1;
1971 let out = unsafe {
1972 let ext = ffi::X509_REVOKED_get_ext_d2i(
1974 self.as_ptr(),
1975 T::NID.as_raw(),
1976 &mut critical as *mut _,
1977 ptr::null_mut(),
1978 );
1979 T::Output::from_ptr_opt(ext as *mut _)
1982 };
1983 match (critical, out) {
1984 (0, Some(out)) => Ok(Some((false, out))),
1985 (1, Some(out)) => Ok(Some((true, out))),
1986 (-1 | -2, _) => Ok(None),
1988 (0 | 1, None) => Err(ErrorStack::get()),
1991 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1992 }
1993 }
1994}
1995
1996pub enum ReasonCode {}
1999
2000unsafe impl ExtensionType for ReasonCode {
2003 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
2004
2005 type Output = Asn1Enumerated;
2006}
2007
2008pub enum CertificateIssuer {}
2011
2012unsafe impl ExtensionType for CertificateIssuer {
2015 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
2016
2017 type Output = Stack<GeneralName>;
2018}
2019
2020pub enum AuthorityInformationAccess {}
2022
2023unsafe impl ExtensionType for AuthorityInformationAccess {
2026 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
2027
2028 type Output = Stack<AccessDescription>;
2029}
2030
2031unsafe impl ExtensionType for CrlNumber {
2034 const NID: Nid = Nid::CRL_NUMBER;
2035
2036 type Output = Asn1Integer;
2037}
2038
2039pub struct X509CrlBuilder(X509Crl);
2041
2042impl X509CrlBuilder {
2043 #[corresponds(X509_CRL_new)]
2045 pub fn new() -> Result<Self, ErrorStack> {
2046 unsafe {
2047 ffi::init();
2048 let ptr = cvt_p(ffi::X509_CRL_new())?;
2049 cvt(ffi::X509_CRL_set_version(ptr, 1)).map(|_| ())?;
2050
2051 Ok(Self(X509Crl(ptr)))
2052 }
2053 }
2054
2055 #[corresponds(X509_CRL_set_issuer_name)]
2057 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
2058 unsafe {
2059 cvt(ffi::X509_CRL_set_issuer_name(
2060 self.0.as_ptr(),
2061 issuer_name.as_ptr(),
2062 ))
2063 .map(|_| ())
2064 }
2065 }
2066
2067 #[corresponds(X509_CRL_set1_lastUpdate)]
2069 pub fn set_last_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
2070 unsafe { cvt(ffi::X509_CRL_set1_lastUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
2071 }
2072
2073 #[corresponds(X509_CRL_set1_nextUpdate)]
2075 pub fn set_next_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
2076 unsafe { cvt(ffi::X509_CRL_set1_nextUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
2077 }
2078
2079 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
2083 self.append_extension2(&extension)
2084 }
2085
2086 #[corresponds(X509_CRL_add_ext)]
2088 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
2089 unsafe {
2090 cvt(ffi::X509_CRL_add_ext(
2091 self.0.as_ptr(),
2092 extension.as_ptr(),
2093 -1,
2094 ))
2095 .map(|_| ())
2096 }
2097 }
2098
2099 #[corresponds(X509_CRL_add0_revoked)]
2101 pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
2102 unsafe {
2103 let r = cvt(ffi::X509_CRL_add0_revoked(
2104 self.0.as_ptr(),
2105 revoked.as_ptr(),
2106 ))
2107 .map(|_| ());
2108 std::mem::forget(revoked);
2109 r
2110 }
2111 }
2112
2113 #[corresponds(X509_CRL_sort)]
2115 pub fn sort(&mut self) -> Result<(), ErrorStack> {
2116 unsafe { cvt(ffi::X509_CRL_sort(self.0.as_ptr())).map(|_| ()) }
2117 }
2118
2119 #[corresponds(X509_CRL_sign)]
2121 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
2122 where
2123 T: HasPrivate,
2124 {
2125 unsafe {
2126 cvt(ffi::X509_CRL_sign(
2127 self.0.as_ptr(),
2128 key.as_ptr(),
2129 hash.as_ptr(),
2130 ))
2131 .map(|_| ())
2132 }
2133 }
2134
2135 pub fn build(self) -> Result<X509Crl, ErrorStack> {
2141 unsafe {
2142 let loc = ffi::X509_CRL_get_ext_by_NID(
2143 self.0.as_ptr(),
2144 Nid::AUTHORITY_KEY_IDENTIFIER.as_raw(),
2145 -1,
2146 );
2147 assert!(
2148 loc >= 0,
2149 "CRL must have an Authority Key Identifier extension"
2150 );
2151 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
2152 assert_eq!(
2153 ffi::X509_EXTENSION_get_critical(ext),
2154 0,
2155 "Authority Key Identifier extension must not be critical"
2156 );
2157
2158 let loc = ffi::X509_CRL_get_ext_by_NID(self.0.as_ptr(), Nid::CRL_NUMBER.as_raw(), -1);
2159 assert!(loc >= 0, "CRL must have a Crl Number extension");
2160 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
2161 assert_eq!(
2162 ffi::X509_EXTENSION_get_critical(ext),
2163 0,
2164 "Crl Number extension must not be critical"
2165 );
2166
2167 assert!(
2168 !X509_CRL_get0_nextUpdate(self.0.as_ptr()).is_null(),
2169 "CRL must have nextUpdate time set"
2170 );
2171 let revoked = self.0.get_revoked();
2172 assert!(
2173 revoked.is_none() || revoked.is_some_and(|r| !r.is_empty()),
2175 "Revoked must be absent or non-empty"
2176 );
2177 }
2178
2179 Ok(self.0)
2180 }
2181}
2182
2183foreign_type_and_impl_send_sync! {
2184 type CType = ffi::X509_CRL;
2185 fn drop = ffi::X509_CRL_free;
2186
2187 pub struct X509Crl;
2189 pub struct X509CrlRef;
2191}
2192
2193pub enum CrlStatus<'a> {
2199 NotRevoked,
2201 Revoked(&'a X509RevokedRef),
2203 RemoveFromCrl(&'a X509RevokedRef),
2208}
2209
2210impl<'a> CrlStatus<'a> {
2211 unsafe fn from_ffi_status(
2216 status: c_int,
2217 revoked_entry: *mut ffi::X509_REVOKED,
2218 ) -> CrlStatus<'a> {
2219 match status {
2220 0 => CrlStatus::NotRevoked,
2221 1 => {
2222 assert!(!revoked_entry.is_null());
2223 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
2224 }
2225 2 => {
2226 assert!(!revoked_entry.is_null());
2227 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
2228 }
2229 _ => unreachable!(
2230 "{}",
2231 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
2232 ),
2233 }
2234 }
2235}
2236
2237impl X509Crl {
2238 from_pem! {
2239 #[corresponds(PEM_read_bio_X509_CRL)]
2243 from_pem,
2244 X509Crl,
2245 ffi::PEM_read_bio_X509_CRL
2246 }
2247
2248 from_der! {
2249 #[corresponds(d2i_X509_CRL)]
2251 from_der,
2252 X509Crl,
2253 ffi::d2i_X509_CRL
2254 }
2255}
2256
2257impl X509CrlRef {
2258 to_pem! {
2259 #[corresponds(PEM_write_bio_X509_CRL)]
2263 to_pem,
2264 ffi::PEM_write_bio_X509_CRL
2265 }
2266
2267 to_der! {
2268 #[corresponds(i2d_X509_CRL)]
2270 to_der,
2271 ffi::i2d_X509_CRL
2272 }
2273
2274 digest! {
2275 digest,
2281 ffi::X509_CRL_digest
2282 }
2283
2284 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2286 unsafe {
2287 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2288 if revoked.is_null() {
2289 None
2290 } else {
2291 Some(StackRef::from_ptr(revoked))
2292 }
2293 }
2294 }
2295
2296 #[corresponds(X509_CRL_get0_lastUpdate)]
2298 pub fn last_update(&self) -> &Asn1TimeRef {
2299 unsafe {
2300 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2301 assert!(!date.is_null());
2302 Asn1TimeRef::from_ptr(date as *mut _)
2303 }
2304 }
2305
2306 #[corresponds(X509_CRL_get0_nextUpdate)]
2310 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2311 unsafe {
2312 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2313 Asn1TimeRef::from_const_ptr_opt(date)
2314 }
2315 }
2316
2317 #[corresponds(X509_CRL_get0_by_serial)]
2319 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2320 unsafe {
2321 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2322 let status =
2323 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2324 CrlStatus::from_ffi_status(status, ret)
2325 }
2326 }
2327
2328 #[corresponds(X509_CRL_get0_by_cert)]
2330 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2331 unsafe {
2332 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2333 let status =
2334 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2335 CrlStatus::from_ffi_status(status, ret)
2336 }
2337 }
2338
2339 #[corresponds(X509_CRL_get_issuer)]
2341 pub fn issuer_name(&self) -> &X509NameRef {
2342 unsafe {
2343 let name = X509_CRL_get_issuer(self.as_ptr());
2344 assert!(!name.is_null());
2345 X509NameRef::from_ptr(name as *mut _)
2346 }
2347 }
2348
2349 #[corresponds(X509_CRL_verify)]
2356 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2357 where
2358 T: HasPublic,
2359 {
2360 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2361 }
2362
2363 #[corresponds(X509_CRL_get_ext_d2i)]
2367 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2368 let mut critical = -1;
2369 let out = unsafe {
2370 let ext = ffi::X509_CRL_get_ext_d2i(
2372 self.as_ptr(),
2373 T::NID.as_raw(),
2374 &mut critical as *mut _,
2375 ptr::null_mut(),
2376 );
2377 T::Output::from_ptr_opt(ext as *mut _)
2380 };
2381 match (critical, out) {
2382 (0, Some(out)) => Ok(Some((false, out))),
2383 (1, Some(out)) => Ok(Some((true, out))),
2384 (-1 | -2, _) => Ok(None),
2386 (0 | 1, None) => Err(ErrorStack::get()),
2389 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2390 }
2391 }
2392}
2393
2394#[derive(Copy, Clone, PartialEq, Eq)]
2396pub struct X509VerifyResult(c_int);
2397
2398impl fmt::Debug for X509VerifyResult {
2399 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2400 fmt.debug_struct("X509VerifyResult")
2401 .field("code", &self.0)
2402 .field("error", &self.error_string())
2403 .finish()
2404 }
2405}
2406
2407impl fmt::Display for X509VerifyResult {
2408 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2409 fmt.write_str(self.error_string())
2410 }
2411}
2412
2413impl Error for X509VerifyResult {}
2414
2415impl X509VerifyResult {
2416 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2423 X509VerifyResult(err)
2424 }
2425
2426 #[allow(clippy::trivially_copy_pass_by_ref)]
2428 pub fn as_raw(&self) -> c_int {
2429 self.0
2430 }
2431
2432 #[corresponds(X509_verify_cert_error_string)]
2434 #[allow(clippy::trivially_copy_pass_by_ref)]
2435 pub fn error_string(&self) -> &'static str {
2436 ffi::init();
2437
2438 unsafe {
2439 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2440 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2441 }
2442 }
2443
2444 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2446 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2448 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2449}
2450
2451foreign_type_and_impl_send_sync! {
2452 type CType = ffi::GENERAL_NAME;
2453 fn drop = ffi::GENERAL_NAME_free;
2454
2455 pub struct GeneralName;
2457 pub struct GeneralNameRef;
2459}
2460
2461impl GeneralName {
2462 unsafe fn new(
2463 type_: c_int,
2464 asn1_type: Asn1Type,
2465 value: &[u8],
2466 ) -> Result<GeneralName, ErrorStack> {
2467 ffi::init();
2468 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2469 (*gn.as_ptr()).type_ = type_;
2470 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2471 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2472
2473 #[cfg(any(boringssl, awslc))]
2474 {
2475 (*gn.as_ptr()).d.ptr = s.cast();
2476 }
2477 #[cfg(not(any(boringssl, awslc)))]
2478 {
2479 (*gn.as_ptr()).d = s.cast();
2480 }
2481
2482 Ok(gn)
2483 }
2484
2485 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2486 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2487 }
2488
2489 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2490 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2491 }
2492
2493 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2494 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2495 }
2496
2497 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2498 match ip {
2499 IpAddr::V4(addr) => unsafe {
2500 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2501 },
2502 IpAddr::V6(addr) => unsafe {
2503 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2504 },
2505 }
2506 }
2507
2508 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2509 unsafe {
2510 ffi::init();
2511 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2512 (*gn).type_ = ffi::GEN_RID;
2513
2514 #[cfg(any(boringssl, awslc))]
2515 {
2516 (*gn).d.registeredID = oid.as_ptr();
2517 }
2518 #[cfg(not(any(boringssl, awslc)))]
2519 {
2520 (*gn).d = oid.as_ptr().cast();
2521 }
2522
2523 mem::forget(oid);
2524
2525 Ok(GeneralName::from_ptr(gn))
2526 }
2527 }
2528
2529 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2530 unsafe {
2531 ffi::init();
2532
2533 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2534 ptr::null_mut(),
2535 &mut value.as_ptr().cast(),
2536 value.len().try_into().unwrap(),
2537 ))?;
2538
2539 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2540 (*gn).type_ = ffi::GEN_OTHERNAME;
2541
2542 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2543 gn,
2544 oid.as_ptr().cast(),
2545 typ,
2546 )) {
2547 ffi::GENERAL_NAME_free(gn);
2548 return Err(e);
2549 }
2550
2551 mem::forget(oid);
2552
2553 Ok(GeneralName::from_ptr(gn))
2554 }
2555 }
2556
2557 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2558 unsafe {
2559 ffi::init();
2560 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2561 (*gn).type_ = ffi::GEN_DIRNAME;
2562
2563 let dup = match name.to_owned() {
2564 Ok(dup) => dup,
2565 Err(e) => {
2566 ffi::GENERAL_NAME_free(gn);
2567 return Err(e);
2568 }
2569 };
2570
2571 #[cfg(any(boringssl, awslc))]
2572 {
2573 (*gn).d.directoryName = dup.as_ptr();
2574 }
2575 #[cfg(not(any(boringssl, awslc)))]
2576 {
2577 (*gn).d = dup.as_ptr().cast();
2578 }
2579
2580 std::mem::forget(dup);
2581
2582 Ok(GeneralName::from_ptr(gn))
2583 }
2584 }
2585}
2586
2587impl GeneralNameRef {
2588 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2589 unsafe {
2590 if (*self.as_ptr()).type_ != ffi_type {
2591 return None;
2592 }
2593
2594 #[cfg(any(boringssl, awslc))]
2595 let d = (*self.as_ptr()).d.ptr;
2596 #[cfg(not(any(boringssl, awslc)))]
2597 let d = (*self.as_ptr()).d;
2598
2599 let ptr = ASN1_STRING_get0_data(d as *mut _);
2600 let len = ffi::ASN1_STRING_length(d as *mut _);
2601
2602 #[allow(clippy::unnecessary_cast)]
2603 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2604 str::from_utf8(slice).ok()
2608 }
2609 }
2610
2611 pub fn email(&self) -> Option<&str> {
2613 self.ia5_string(ffi::GEN_EMAIL)
2614 }
2615
2616 pub fn directory_name(&self) -> Option<&X509NameRef> {
2618 unsafe {
2619 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2620 return None;
2621 }
2622
2623 #[cfg(any(boringssl, awslc))]
2624 let d = (*self.as_ptr()).d.ptr;
2625 #[cfg(not(any(boringssl, awslc)))]
2626 let d = (*self.as_ptr()).d;
2627
2628 Some(X509NameRef::from_const_ptr(d as *const _))
2629 }
2630 }
2631
2632 pub fn dnsname(&self) -> Option<&str> {
2634 self.ia5_string(ffi::GEN_DNS)
2635 }
2636
2637 pub fn uri(&self) -> Option<&str> {
2639 self.ia5_string(ffi::GEN_URI)
2640 }
2641
2642 pub fn ipaddress(&self) -> Option<&[u8]> {
2644 unsafe {
2645 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2646 return None;
2647 }
2648 #[cfg(any(boringssl, awslc))]
2649 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2650 #[cfg(not(any(boringssl, awslc)))]
2651 let d = (*self.as_ptr()).d;
2652
2653 let ptr = ASN1_STRING_get0_data(d as *mut _);
2654 let len = ffi::ASN1_STRING_length(d as *mut _);
2655
2656 #[allow(clippy::unnecessary_cast)]
2657 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2658 }
2659 }
2660}
2661
2662impl fmt::Debug for GeneralNameRef {
2663 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2664 if let Some(email) = self.email() {
2665 formatter.write_str(email)
2666 } else if let Some(dnsname) = self.dnsname() {
2667 formatter.write_str(dnsname)
2668 } else if let Some(uri) = self.uri() {
2669 formatter.write_str(uri)
2670 } else if let Some(ipaddress) = self.ipaddress() {
2671 let address = <[u8; 16]>::try_from(ipaddress)
2672 .map(IpAddr::from)
2673 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2674 match address {
2675 Ok(a) => fmt::Debug::fmt(&a, formatter),
2676 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2677 }
2678 } else {
2679 formatter.write_str("(empty)")
2680 }
2681 }
2682}
2683
2684impl Stackable for GeneralName {
2685 type StackType = ffi::stack_st_GENERAL_NAME;
2686}
2687
2688foreign_type_and_impl_send_sync! {
2689 type CType = ffi::DIST_POINT;
2690 fn drop = ffi::DIST_POINT_free;
2691
2692 pub struct DistPoint;
2694 pub struct DistPointRef;
2696}
2697
2698impl DistPointRef {
2699 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2701 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2702 }
2703}
2704
2705foreign_type_and_impl_send_sync! {
2706 type CType = ffi::DIST_POINT_NAME;
2707 fn drop = ffi::DIST_POINT_NAME_free;
2708
2709 pub struct DistPointName;
2711 pub struct DistPointNameRef;
2713}
2714
2715impl DistPointNameRef {
2716 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2718 unsafe {
2719 if (*self.as_ptr()).type_ != 0 {
2720 return None;
2721 }
2722 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2723 }
2724 }
2725}
2726
2727impl Stackable for DistPoint {
2728 type StackType = ffi::stack_st_DIST_POINT;
2729}
2730
2731foreign_type_and_impl_send_sync! {
2732 type CType = ffi::BASIC_CONSTRAINTS;
2733 fn drop = ffi::BASIC_CONSTRAINTS_free;
2734
2735 pub struct BasicConstraints;
2737 pub struct BasicConstraintsRef;
2739}
2740
2741impl BasicConstraintsRef {
2742 pub fn ca(&self) -> bool {
2743 unsafe { (*(self.as_ptr())).ca != 0 }
2744 }
2745
2746 pub fn pathlen(&self) -> Option<&Asn1IntegerRef> {
2747 if !self.ca() {
2748 return None;
2749 }
2750 unsafe {
2751 let data = (*(self.as_ptr())).pathlen;
2752 Asn1IntegerRef::from_const_ptr_opt(data as _)
2753 }
2754 }
2755}
2756
2757foreign_type_and_impl_send_sync! {
2758 type CType = ffi::ACCESS_DESCRIPTION;
2759 fn drop = ffi::ACCESS_DESCRIPTION_free;
2760
2761 pub struct AccessDescription;
2763 pub struct AccessDescriptionRef;
2765}
2766
2767impl AccessDescriptionRef {
2768 pub fn method(&self) -> &Asn1ObjectRef {
2770 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2771 }
2772
2773 pub fn location(&self) -> &GeneralNameRef {
2775 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2776 }
2777}
2778
2779impl Stackable for AccessDescription {
2780 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2781}
2782
2783foreign_type_and_impl_send_sync! {
2784 type CType = ffi::X509_ALGOR;
2785 fn drop = ffi::X509_ALGOR_free;
2786
2787 pub struct X509Algorithm;
2789 pub struct X509AlgorithmRef;
2791}
2792
2793impl X509AlgorithmRef {
2794 pub fn object(&self) -> &Asn1ObjectRef {
2796 unsafe {
2797 let mut oid = ptr::null();
2798 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2799 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2800 }
2801 }
2802}
2803
2804foreign_type_and_impl_send_sync! {
2805 type CType = ffi::X509_OBJECT;
2806 fn drop = X509_OBJECT_free;
2807
2808 pub struct X509Object;
2810 pub struct X509ObjectRef;
2812}
2813
2814impl X509ObjectRef {
2815 pub fn x509(&self) -> Option<&X509Ref> {
2816 unsafe {
2817 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2818 X509Ref::from_const_ptr_opt(ptr)
2819 }
2820 }
2821}
2822
2823impl Stackable for X509Object {
2824 type StackType = ffi::stack_st_X509_OBJECT;
2825}
2826
2827use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2828
2829use ffi::{
2830 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2831 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2832};
2833
2834use ffi::X509_OBJECT_free;
2835use ffi::X509_OBJECT_get0_X509;
2836
2837use ffi::{
2838 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2839 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2840};
2841
2842#[derive(Copy, Clone, PartialEq, Eq)]
2843pub struct X509PurposeId(c_int);
2844
2845impl X509PurposeId {
2846 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2847 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2848 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2849 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2850 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2851 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2852 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2853 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2854 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2855 #[cfg(ossl320)]
2856 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2857
2858 pub fn from_raw(id: c_int) -> Self {
2860 X509PurposeId(id)
2861 }
2862
2863 pub fn as_raw(&self) -> c_int {
2865 self.0
2866 }
2867}
2868
2869pub struct X509PurposeRef(Opaque);
2871
2872impl ForeignTypeRef for X509PurposeRef {
2874 type CType = ffi::X509_PURPOSE;
2875}
2876
2877impl X509PurposeRef {
2878 #[allow(clippy::unnecessary_cast)]
2892 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2893 unsafe {
2894 let sname = CString::new(sname).unwrap();
2895 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2896 Ok(purpose)
2897 }
2898 }
2899 #[corresponds(X509_PURPOSE_get0)]
2902 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2903 unsafe {
2904 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2905 Ok(X509PurposeRef::from_const_ptr(ptr))
2906 }
2907 }
2908
2909 pub fn purpose(&self) -> X509PurposeId {
2920 unsafe {
2921 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2922 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2923 }
2924 }
2925}