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 let r = X509_up_ref(self.as_ptr());
829 assert!(r == 1);
830 X509::from_ptr(self.as_ptr())
831 }
832 }
833}
834
835impl Ord for X509Ref {
836 fn cmp(&self, other: &Self) -> cmp::Ordering {
837 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
840 cmp.cmp(&0)
841 }
842}
843
844impl PartialOrd for X509Ref {
845 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
846 Some(self.cmp(other))
847 }
848}
849
850impl PartialOrd<X509> for X509Ref {
851 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
852 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
853 }
854}
855
856impl PartialEq for X509Ref {
857 fn eq(&self, other: &Self) -> bool {
858 self.cmp(other) == cmp::Ordering::Equal
859 }
860}
861
862impl PartialEq<X509> for X509Ref {
863 fn eq(&self, other: &X509) -> bool {
864 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
865 }
866}
867
868impl Eq for X509Ref {}
869
870impl X509 {
871 pub fn builder() -> Result<X509Builder, ErrorStack> {
873 X509Builder::new()
874 }
875
876 from_pem! {
877 #[corresponds(PEM_read_bio_X509)]
881 from_pem,
882 X509,
883 ffi::PEM_read_bio_X509
884 }
885
886 from_der! {
887 #[corresponds(d2i_X509)]
889 from_der,
890 X509,
891 ffi::d2i_X509
892 }
893
894 #[corresponds(PEM_read_bio_X509)]
896 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
897 unsafe {
898 ffi::init();
899 let bio = MemBioSlice::new(pem)?;
900
901 let mut certs = vec![];
902 loop {
903 let r =
904 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
905 if r.is_null() {
906 let e = ErrorStack::get();
907
908 if let Some(err) = e.errors().last() {
909 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
910 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
911 {
912 break;
913 }
914 }
915
916 return Err(e);
917 } else {
918 certs.push(X509(r));
919 }
920 }
921
922 Ok(certs)
923 }
924 }
925}
926
927impl Clone for X509 {
928 fn clone(&self) -> X509 {
929 X509Ref::to_owned(self)
930 }
931}
932
933impl fmt::Debug for X509 {
934 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
935 let serial = match &self.serial_number().to_bn() {
936 Ok(bn) => match bn.to_hex_str() {
937 Ok(hex) => hex.to_string(),
938 Err(_) => "".to_string(),
939 },
940 Err(_) => "".to_string(),
941 };
942 let mut debug_struct = formatter.debug_struct("X509");
943 debug_struct.field("serial_number", &serial);
944 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
945 debug_struct.field("issuer", &self.issuer_name());
946 debug_struct.field("subject", &self.subject_name());
947 if let Some(subject_alt_names) = &self.subject_alt_names() {
948 debug_struct.field("subject_alt_names", subject_alt_names);
949 }
950 debug_struct.field("not_before", &self.not_before());
951 debug_struct.field("not_after", &self.not_after());
952
953 if let Ok(public_key) = &self.public_key() {
954 debug_struct.field("public_key", public_key);
955 };
956 debug_struct.finish()
959 }
960}
961
962impl AsRef<X509Ref> for X509Ref {
963 fn as_ref(&self) -> &X509Ref {
964 self
965 }
966}
967
968impl Stackable for X509 {
969 type StackType = ffi::stack_st_X509;
970}
971
972impl Ord for X509 {
973 fn cmp(&self, other: &Self) -> cmp::Ordering {
974 X509Ref::cmp(self, other)
975 }
976}
977
978impl PartialOrd for X509 {
979 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
980 Some(self.cmp(other))
981 }
982}
983
984impl PartialOrd<X509Ref> for X509 {
985 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
986 X509Ref::partial_cmp(self, other)
987 }
988}
989
990impl PartialEq for X509 {
991 fn eq(&self, other: &Self) -> bool {
992 X509Ref::eq(self, other)
993 }
994}
995
996impl PartialEq<X509Ref> for X509 {
997 fn eq(&self, other: &X509Ref) -> bool {
998 X509Ref::eq(self, other)
999 }
1000}
1001
1002impl Eq for X509 {}
1003
1004pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
1006
1007impl X509v3Context<'_> {
1008 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
1009 &self.0 as *const _ as *mut _
1010 }
1011}
1012
1013foreign_type_and_impl_send_sync! {
1014 type CType = ffi::X509_EXTENSION;
1015 fn drop = ffi::X509_EXTENSION_free;
1016
1017 pub struct X509Extension;
1019 pub struct X509ExtensionRef;
1021}
1022
1023impl Stackable for X509Extension {
1024 type StackType = ffi::stack_st_X509_EXTENSION;
1025}
1026
1027impl X509Extension {
1028 #[deprecated(
1042 note = "Use x509::extension types or new_from_der instead",
1043 since = "0.10.51"
1044 )]
1045 pub fn new(
1046 conf: Option<&ConfRef>,
1047 context: Option<&X509v3Context<'_>>,
1048 name: &str,
1049 value: &str,
1050 ) -> Result<X509Extension, ErrorStack> {
1051 let name = CString::new(name).unwrap();
1052 let value = CString::new(value).unwrap();
1053 let mut ctx;
1054 unsafe {
1055 ffi::init();
1056 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1057 let context_ptr = match context {
1058 Some(c) => c.as_ptr(),
1059 None => {
1060 ctx = mem::zeroed();
1061
1062 ffi::X509V3_set_ctx(
1063 &mut ctx,
1064 ptr::null_mut(),
1065 ptr::null_mut(),
1066 ptr::null_mut(),
1067 ptr::null_mut(),
1068 0,
1069 );
1070 &mut ctx
1071 }
1072 };
1073 let name = name.as_ptr() as *mut _;
1074 let value = value.as_ptr() as *mut _;
1075
1076 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
1077 }
1078 }
1079
1080 #[deprecated(
1094 note = "Use x509::extension types or new_from_der instead",
1095 since = "0.10.51"
1096 )]
1097 pub fn new_nid(
1098 conf: Option<&ConfRef>,
1099 context: Option<&X509v3Context<'_>>,
1100 name: Nid,
1101 value: &str,
1102 ) -> Result<X509Extension, ErrorStack> {
1103 let value = CString::new(value).unwrap();
1104 let mut ctx;
1105 unsafe {
1106 ffi::init();
1107 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1108 let context_ptr = match context {
1109 Some(c) => c.as_ptr(),
1110 None => {
1111 ctx = mem::zeroed();
1112
1113 ffi::X509V3_set_ctx(
1114 &mut ctx,
1115 ptr::null_mut(),
1116 ptr::null_mut(),
1117 ptr::null_mut(),
1118 ptr::null_mut(),
1119 0,
1120 );
1121 &mut ctx
1122 }
1123 };
1124 let name = name.as_raw();
1125 let value = value.as_ptr() as *mut _;
1126
1127 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1128 }
1129 }
1130
1131 pub fn new_from_der(
1141 oid: &Asn1ObjectRef,
1142 critical: bool,
1143 der_contents: &Asn1OctetStringRef,
1144 ) -> Result<X509Extension, ErrorStack> {
1145 unsafe {
1146 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1147 ptr::null_mut(),
1148 oid.as_ptr(),
1149 critical as _,
1150 der_contents.as_ptr(),
1151 ))
1152 .map(X509Extension)
1153 }
1154 }
1155
1156 pub fn new_subject_alt_name(
1158 stack: Stack<GeneralName>,
1159 critical: bool,
1160 ) -> Result<X509Extension, ErrorStack> {
1161 unsafe { Self::new_internal(Nid::SUBJECT_ALT_NAME, critical, stack.as_ptr().cast()) }
1162 }
1163
1164 pub(crate) unsafe fn new_internal(
1165 nid: Nid,
1166 critical: bool,
1167 value: *mut c_void,
1168 ) -> Result<X509Extension, ErrorStack> {
1169 ffi::init();
1170 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1171 }
1172
1173 #[cfg(not(libressl390))]
1179 #[corresponds(X509V3_EXT_add_alias)]
1180 #[deprecated(
1181 note = "Use x509::extension types or new_from_der and then this is not necessary",
1182 since = "0.10.51"
1183 )]
1184 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1185 ffi::init();
1186 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1187 }
1188}
1189
1190impl X509ExtensionRef {
1191 to_der! {
1192 #[corresponds(i2d_X509_EXTENSION)]
1194 to_der,
1195 ffi::i2d_X509_EXTENSION
1196 }
1197}
1198
1199pub struct X509NameBuilder(X509Name);
1201
1202impl X509NameBuilder {
1203 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1205 unsafe {
1206 ffi::init();
1207 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1208 }
1209 }
1210
1211 #[corresponds(X509_NAME_add_entry)]
1213 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1214 unsafe {
1215 cvt(ffi::X509_NAME_add_entry(
1216 self.0.as_ptr(),
1217 ne.as_ptr(),
1218 -1,
1219 0,
1220 ))
1221 .map(|_| ())
1222 }
1223 }
1224
1225 #[corresponds(X509_NAME_add_entry_by_txt)]
1227 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1228 unsafe {
1229 let field = CString::new(field).unwrap();
1230 assert!(value.len() <= crate::SLenType::MAX as usize);
1231 cvt(ffi::X509_NAME_add_entry_by_txt(
1232 self.0.as_ptr(),
1233 field.as_ptr() as *mut _,
1234 ffi::MBSTRING_UTF8,
1235 value.as_ptr(),
1236 value.len() as crate::SLenType,
1237 -1,
1238 0,
1239 ))
1240 .map(|_| ())
1241 }
1242 }
1243
1244 #[corresponds(X509_NAME_add_entry_by_txt)]
1246 pub fn append_entry_by_text_with_type(
1247 &mut self,
1248 field: &str,
1249 value: &str,
1250 ty: Asn1Type,
1251 ) -> Result<(), ErrorStack> {
1252 unsafe {
1253 let field = CString::new(field).unwrap();
1254 assert!(value.len() <= crate::SLenType::MAX as usize);
1255 cvt(ffi::X509_NAME_add_entry_by_txt(
1256 self.0.as_ptr(),
1257 field.as_ptr() as *mut _,
1258 ty.as_raw(),
1259 value.as_ptr(),
1260 value.len() as crate::SLenType,
1261 -1,
1262 0,
1263 ))
1264 .map(|_| ())
1265 }
1266 }
1267
1268 #[corresponds(X509_NAME_add_entry_by_NID)]
1270 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1271 unsafe {
1272 assert!(value.len() <= crate::SLenType::MAX as usize);
1273 cvt(ffi::X509_NAME_add_entry_by_NID(
1274 self.0.as_ptr(),
1275 field.as_raw(),
1276 ffi::MBSTRING_UTF8,
1277 value.as_ptr() as *mut _,
1278 value.len() as crate::SLenType,
1279 -1,
1280 0,
1281 ))
1282 .map(|_| ())
1283 }
1284 }
1285
1286 #[corresponds(X509_NAME_add_entry_by_NID)]
1288 pub fn append_entry_by_nid_with_type(
1289 &mut self,
1290 field: Nid,
1291 value: &str,
1292 ty: Asn1Type,
1293 ) -> Result<(), ErrorStack> {
1294 unsafe {
1295 assert!(value.len() <= crate::SLenType::MAX as usize);
1296 cvt(ffi::X509_NAME_add_entry_by_NID(
1297 self.0.as_ptr(),
1298 field.as_raw(),
1299 ty.as_raw(),
1300 value.as_ptr() as *mut _,
1301 value.len() as crate::SLenType,
1302 -1,
1303 0,
1304 ))
1305 .map(|_| ())
1306 }
1307 }
1308
1309 pub fn build(self) -> X509Name {
1311 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1315 }
1316}
1317
1318foreign_type_and_impl_send_sync! {
1319 type CType = ffi::X509_NAME;
1320 fn drop = ffi::X509_NAME_free;
1321
1322 pub struct X509Name;
1324 pub struct X509NameRef;
1326}
1327
1328impl X509Name {
1329 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1331 X509NameBuilder::new()
1332 }
1333
1334 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1338 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1339 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1340 }
1341
1342 from_der! {
1343 from_der,
1349 X509Name,
1350 ffi::d2i_X509_NAME
1351 }
1352}
1353
1354impl Stackable for X509Name {
1355 type StackType = ffi::stack_st_X509_NAME;
1356}
1357
1358impl X509NameRef {
1359 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1361 X509NameEntries {
1362 name: self,
1363 nid: Some(nid),
1364 loc: -1,
1365 }
1366 }
1367
1368 pub fn entries(&self) -> X509NameEntries<'_> {
1370 X509NameEntries {
1371 name: self,
1372 nid: None,
1373 loc: -1,
1374 }
1375 }
1376
1377 #[corresponds(X509_NAME_cmp)]
1384 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1385 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1386 if cfg!(ossl300) && cmp == -2 {
1387 return Err(ErrorStack::get());
1388 }
1389 Ok(cmp.cmp(&0))
1390 }
1391
1392 #[corresponds(X509_NAME_dup)]
1394 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1395 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1396 }
1397
1398 to_der! {
1399 to_der,
1405 ffi::i2d_X509_NAME
1406 }
1407
1408 digest! {
1409 digest,
1415 ffi::X509_NAME_digest
1416 }
1417}
1418
1419impl fmt::Debug for X509NameRef {
1420 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1421 formatter.debug_list().entries(self.entries()).finish()
1422 }
1423}
1424
1425pub struct X509NameEntries<'a> {
1427 name: &'a X509NameRef,
1428 nid: Option<Nid>,
1429 loc: c_int,
1430}
1431
1432impl<'a> Iterator for X509NameEntries<'a> {
1433 type Item = &'a X509NameEntryRef;
1434
1435 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1436 unsafe {
1437 match self.nid {
1438 Some(nid) => {
1439 self.loc =
1441 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1442 if self.loc == -1 {
1443 return None;
1444 }
1445 }
1446 None => {
1447 self.loc += 1;
1449 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1450 return None;
1451 }
1452 }
1453 }
1454
1455 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1456
1457 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1458 }
1459 }
1460}
1461
1462foreign_type_and_impl_send_sync! {
1463 type CType = ffi::X509_NAME_ENTRY;
1464 fn drop = ffi::X509_NAME_ENTRY_free;
1465
1466 pub struct X509NameEntry;
1468 pub struct X509NameEntryRef;
1470}
1471
1472impl X509NameEntryRef {
1473 #[corresponds(X509_NAME_ENTRY_get_data)]
1475 pub fn data(&self) -> &Asn1StringRef {
1476 unsafe {
1477 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1478 Asn1StringRef::from_ptr(data as *mut _)
1479 }
1480 }
1481
1482 #[corresponds(X509_NAME_ENTRY_get_object)]
1485 pub fn object(&self) -> &Asn1ObjectRef {
1486 unsafe {
1487 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1488 Asn1ObjectRef::from_ptr(object as *mut _)
1489 }
1490 }
1491}
1492
1493impl fmt::Debug for X509NameEntryRef {
1494 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1495 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1496 }
1497}
1498
1499foreign_type_and_impl_send_sync! {
1500 type CType = ffi::X509_PUBKEY;
1501 fn drop = ffi::X509_PUBKEY_free;
1502
1503 pub struct X509Pubkey;
1505 pub struct X509PubkeyRef;
1507}
1508
1509impl X509Pubkey {
1510 from_der! {
1511 from_der,
1517 X509Pubkey,
1518 ffi::d2i_X509_PUBKEY
1519 }
1520
1521 pub fn from_pubkey<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
1527 where
1528 T: HasPublic,
1529 {
1530 let mut p = ptr::null_mut();
1531 unsafe {
1532 cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?;
1533 }
1534 Ok(X509Pubkey(p))
1535 }
1536}
1537
1538impl X509PubkeyRef {
1539 #[corresponds(X509_PUBKEY_dup)]
1541 #[cfg(ossl300)]
1542 pub fn to_owned(&self) -> Result<X509Pubkey, ErrorStack> {
1543 unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) }
1544 }
1545
1546 to_der! {
1547 to_der,
1553 ffi::i2d_X509_PUBKEY
1554 }
1555
1556 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1562 unsafe {
1563 let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
1564 Ok(PKey::from_ptr(key))
1565 }
1566 }
1567
1568 pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> {
1574 unsafe {
1575 let mut pk = ptr::null_mut() as *const c_uchar;
1576 let mut pkt_len: c_int = 0;
1577 cvt(ffi::X509_PUBKEY_get0_param(
1578 ptr::null_mut(),
1579 &mut pk as *mut _,
1580 &mut pkt_len as *mut _,
1581 ptr::null_mut(),
1582 self.as_ptr(),
1583 ))?;
1584
1585 Ok(util::from_raw_parts(pk, pkt_len as usize))
1586 }
1587 }
1588}
1589
1590pub struct X509ReqBuilder(X509Req);
1592
1593impl X509ReqBuilder {
1594 #[corresponds(X509_REQ_new)]
1596 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1597 unsafe {
1598 ffi::init();
1599 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1600 }
1601 }
1602
1603 #[corresponds(X509_REQ_set_version)]
1605 #[allow(clippy::useless_conversion)]
1606 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1607 unsafe {
1608 cvt(ffi::X509_REQ_set_version(
1609 self.0.as_ptr(),
1610 version as c_long,
1611 ))
1612 .map(|_| ())
1613 }
1614 }
1615
1616 #[corresponds(X509_REQ_set_subject_name)]
1618 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1619 unsafe {
1620 cvt(ffi::X509_REQ_set_subject_name(
1621 self.0.as_ptr(),
1622 subject_name.as_ptr(),
1623 ))
1624 .map(|_| ())
1625 }
1626 }
1627
1628 #[corresponds(X509_REQ_set_pubkey)]
1630 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1631 where
1632 T: HasPublic,
1633 {
1634 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1635 }
1636
1637 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1640 unsafe {
1641 let mut ctx = mem::zeroed();
1642
1643 ffi::X509V3_set_ctx(
1644 &mut ctx,
1645 ptr::null_mut(),
1646 ptr::null_mut(),
1647 self.0.as_ptr(),
1648 ptr::null_mut(),
1649 0,
1650 );
1651
1652 if let Some(conf) = conf {
1654 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1655 }
1656
1657 X509v3Context(ctx, PhantomData)
1658 }
1659 }
1660
1661 pub fn add_extensions(
1663 &mut self,
1664 extensions: &StackRef<X509Extension>,
1665 ) -> Result<(), ErrorStack> {
1666 unsafe {
1667 cvt(ffi::X509_REQ_add_extensions(
1668 self.0.as_ptr(),
1669 extensions.as_ptr(),
1670 ))
1671 .map(|_| ())
1672 }
1673 }
1674
1675 #[corresponds(X509_REQ_sign)]
1677 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1678 where
1679 T: HasPrivate,
1680 {
1681 unsafe {
1682 cvt(ffi::X509_REQ_sign(
1683 self.0.as_ptr(),
1684 key.as_ptr(),
1685 hash.as_ptr(),
1686 ))
1687 .map(|_| ())
1688 }
1689 }
1690
1691 pub fn build(self) -> X509Req {
1693 self.0
1694 }
1695}
1696
1697foreign_type_and_impl_send_sync! {
1698 type CType = ffi::X509_REQ;
1699 fn drop = ffi::X509_REQ_free;
1700
1701 pub struct X509Req;
1703 pub struct X509ReqRef;
1705}
1706
1707impl X509Req {
1708 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1710 X509ReqBuilder::new()
1711 }
1712
1713 from_pem! {
1714 from_pem,
1722 X509Req,
1723 ffi::PEM_read_bio_X509_REQ
1724 }
1725
1726 from_der! {
1727 from_der,
1733 X509Req,
1734 ffi::d2i_X509_REQ
1735 }
1736}
1737
1738impl X509ReqRef {
1739 to_pem! {
1740 to_pem,
1748 ffi::PEM_write_bio_X509_REQ
1749 }
1750
1751 to_der! {
1752 to_der,
1758 ffi::i2d_X509_REQ
1759 }
1760
1761 to_pem! {
1762 #[corresponds(X509_Req_print)]
1764 to_text,
1765 ffi::X509_REQ_print
1766 }
1767
1768 digest! {
1769 digest,
1775 ffi::X509_REQ_digest
1776 }
1777
1778 #[corresponds(X509_REQ_get_version)]
1780 #[allow(clippy::unnecessary_cast)]
1781 pub fn version(&self) -> i32 {
1782 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1783 }
1784
1785 #[corresponds(X509_REQ_get_subject_name)]
1787 pub fn subject_name(&self) -> &X509NameRef {
1788 unsafe {
1789 let name = X509_REQ_get_subject_name(self.as_ptr());
1790 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1791 }
1792 }
1793
1794 #[corresponds(X509_REQ_get_pubkey)]
1796 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1797 unsafe {
1798 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1799 Ok(PKey::from_ptr(key))
1800 }
1801 }
1802
1803 #[cfg(ossl110)]
1809 pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> {
1810 unsafe {
1811 let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?;
1812 Ok(X509PubkeyRef::from_ptr(key))
1813 }
1814 }
1815
1816 #[corresponds(X509_REQ_verify)]
1820 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1821 where
1822 T: HasPublic,
1823 {
1824 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1825 }
1826
1827 #[corresponds(X509_REQ_get_extensions)]
1829 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1830 unsafe {
1831 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1832 Ok(Stack::from_ptr(extensions))
1833 }
1834 }
1835}
1836
1837#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1839pub struct CrlReason(c_int);
1840
1841#[allow(missing_docs)] impl CrlReason {
1843 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1844 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1845 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1846 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1847 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1848 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1849 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1850 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1851 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1852 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1853
1854 pub const fn from_raw(value: c_int) -> Self {
1856 CrlReason(value)
1857 }
1858
1859 pub const fn as_raw(&self) -> c_int {
1861 self.0
1862 }
1863}
1864
1865pub struct X509RevokedBuilder(X509Revoked);
1867
1868impl X509RevokedBuilder {
1869 #[corresponds(X509_REVOKED_new)]
1871 pub fn new() -> Result<Self, ErrorStack> {
1872 unsafe {
1873 ffi::init();
1874 cvt_p(ffi::X509_REVOKED_new()).map(|p| X509RevokedBuilder(X509Revoked(p)))
1875 }
1876 }
1877
1878 #[corresponds(X509_REVOKED_set_revocationDate)]
1880 pub fn set_revocation_date(&mut self, date: &Asn1TimeRef) -> Result<(), ErrorStack> {
1881 unsafe {
1882 cvt(ffi::X509_REVOKED_set_revocationDate(
1883 self.0.as_ptr(),
1884 date.as_ptr(),
1885 ))
1886 .map(|_| ())
1887 }
1888 }
1889
1890 #[corresponds(X509_REVOKED_set_serialNumber)]
1892 pub fn set_serial_number(&mut self, serial: &Asn1IntegerRef) -> Result<(), ErrorStack> {
1893 unsafe {
1894 cvt(ffi::X509_REVOKED_set_serialNumber(
1895 self.0.as_ptr(),
1896 serial.as_ptr(),
1897 ))
1898 .map(|_| ())
1899 }
1900 }
1901
1902 pub fn build(self) -> X509Revoked {
1904 self.0
1905 }
1906}
1907
1908foreign_type_and_impl_send_sync! {
1909 type CType = ffi::X509_REVOKED;
1910 fn drop = ffi::X509_REVOKED_free;
1911
1912 pub struct X509Revoked;
1914 pub struct X509RevokedRef;
1916}
1917
1918impl Stackable for X509Revoked {
1919 type StackType = ffi::stack_st_X509_REVOKED;
1920}
1921
1922impl X509Revoked {
1923 from_der! {
1924 #[corresponds(d2i_X509_REVOKED)]
1926 from_der,
1927 X509Revoked,
1928 ffi::d2i_X509_REVOKED
1929 }
1930}
1931
1932impl X509RevokedRef {
1933 to_der! {
1934 #[corresponds(d2i_X509_REVOKED)]
1936 to_der,
1937 ffi::i2d_X509_REVOKED
1938 }
1939
1940 #[corresponds(X509_REVOKED_dup)]
1942 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1943 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1944 }
1945
1946 #[corresponds(X509_REVOKED_get0_revocationDate)]
1948 pub fn revocation_date(&self) -> &Asn1TimeRef {
1949 unsafe {
1950 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1951 assert!(!r.is_null());
1952 Asn1TimeRef::from_ptr(r as *mut _)
1953 }
1954 }
1955
1956 #[corresponds(X509_REVOKED_get0_serialNumber)]
1958 pub fn serial_number(&self) -> &Asn1IntegerRef {
1959 unsafe {
1960 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1961 assert!(!r.is_null());
1962 Asn1IntegerRef::from_ptr(r as *mut _)
1963 }
1964 }
1965
1966 #[corresponds(X509_REVOKED_get_ext_d2i)]
1970 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1971 let mut critical = -1;
1972 let out = unsafe {
1973 let ext = ffi::X509_REVOKED_get_ext_d2i(
1975 self.as_ptr(),
1976 T::NID.as_raw(),
1977 &mut critical as *mut _,
1978 ptr::null_mut(),
1979 );
1980 T::Output::from_ptr_opt(ext as *mut _)
1983 };
1984 match (critical, out) {
1985 (0, Some(out)) => Ok(Some((false, out))),
1986 (1, Some(out)) => Ok(Some((true, out))),
1987 (-1 | -2, _) => Ok(None),
1989 (0 | 1, None) => Err(ErrorStack::get()),
1992 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1993 }
1994 }
1995}
1996
1997pub enum ReasonCode {}
2000
2001unsafe impl ExtensionType for ReasonCode {
2004 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
2005
2006 type Output = Asn1Enumerated;
2007}
2008
2009pub enum CertificateIssuer {}
2012
2013unsafe impl ExtensionType for CertificateIssuer {
2016 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
2017
2018 type Output = Stack<GeneralName>;
2019}
2020
2021pub enum AuthorityInformationAccess {}
2023
2024unsafe impl ExtensionType for AuthorityInformationAccess {
2027 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
2028
2029 type Output = Stack<AccessDescription>;
2030}
2031
2032unsafe impl ExtensionType for CrlNumber {
2035 const NID: Nid = Nid::CRL_NUMBER;
2036
2037 type Output = Asn1Integer;
2038}
2039
2040pub struct X509CrlBuilder(X509Crl);
2042
2043impl X509CrlBuilder {
2044 #[corresponds(X509_CRL_new)]
2046 pub fn new() -> Result<Self, ErrorStack> {
2047 unsafe {
2048 ffi::init();
2049 let ptr = cvt_p(ffi::X509_CRL_new())?;
2050 cvt(ffi::X509_CRL_set_version(ptr, 1)).map(|_| ())?;
2051
2052 Ok(Self(X509Crl(ptr)))
2053 }
2054 }
2055
2056 #[corresponds(X509_CRL_set_issuer_name)]
2058 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
2059 unsafe {
2060 cvt(ffi::X509_CRL_set_issuer_name(
2061 self.0.as_ptr(),
2062 issuer_name.as_ptr(),
2063 ))
2064 .map(|_| ())
2065 }
2066 }
2067
2068 #[corresponds(X509_CRL_set1_lastUpdate)]
2070 pub fn set_last_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
2071 unsafe { cvt(ffi::X509_CRL_set1_lastUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
2072 }
2073
2074 #[corresponds(X509_CRL_set1_nextUpdate)]
2076 pub fn set_next_update(&mut self, t: &Asn1TimeRef) -> Result<(), ErrorStack> {
2077 unsafe { cvt(ffi::X509_CRL_set1_nextUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) }
2078 }
2079
2080 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
2084 self.append_extension2(&extension)
2085 }
2086
2087 #[corresponds(X509_CRL_add_ext)]
2089 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
2090 unsafe {
2091 cvt(ffi::X509_CRL_add_ext(
2092 self.0.as_ptr(),
2093 extension.as_ptr(),
2094 -1,
2095 ))
2096 .map(|_| ())
2097 }
2098 }
2099
2100 #[corresponds(X509_CRL_add0_revoked)]
2102 pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
2103 unsafe {
2104 let r = cvt(ffi::X509_CRL_add0_revoked(
2105 self.0.as_ptr(),
2106 revoked.as_ptr(),
2107 ))
2108 .map(|_| ());
2109 std::mem::forget(revoked);
2110 r
2111 }
2112 }
2113
2114 #[corresponds(X509_CRL_sort)]
2116 pub fn sort(&mut self) -> Result<(), ErrorStack> {
2117 unsafe { cvt(ffi::X509_CRL_sort(self.0.as_ptr())).map(|_| ()) }
2118 }
2119
2120 #[corresponds(X509_CRL_sign)]
2122 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
2123 where
2124 T: HasPrivate,
2125 {
2126 unsafe {
2127 cvt(ffi::X509_CRL_sign(
2128 self.0.as_ptr(),
2129 key.as_ptr(),
2130 hash.as_ptr(),
2131 ))
2132 .map(|_| ())
2133 }
2134 }
2135
2136 pub fn build(self) -> Result<X509Crl, ErrorStack> {
2142 unsafe {
2143 let loc = ffi::X509_CRL_get_ext_by_NID(
2144 self.0.as_ptr(),
2145 Nid::AUTHORITY_KEY_IDENTIFIER.as_raw(),
2146 -1,
2147 );
2148 assert!(
2149 loc >= 0,
2150 "CRL must have an Authority Key Identifier extension"
2151 );
2152 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
2153 assert_eq!(
2154 ffi::X509_EXTENSION_get_critical(ext),
2155 0,
2156 "Authority Key Identifier extension must not be critical"
2157 );
2158
2159 let loc = ffi::X509_CRL_get_ext_by_NID(self.0.as_ptr(), Nid::CRL_NUMBER.as_raw(), -1);
2160 assert!(loc >= 0, "CRL must have a Crl Number extension");
2161 let ext = ffi::X509_CRL_get_ext(self.0.as_ptr(), loc);
2162 assert_eq!(
2163 ffi::X509_EXTENSION_get_critical(ext),
2164 0,
2165 "Crl Number extension must not be critical"
2166 );
2167
2168 assert!(
2169 !X509_CRL_get0_nextUpdate(self.0.as_ptr()).is_null(),
2170 "CRL must have nextUpdate time set"
2171 );
2172 let revoked = self.0.get_revoked();
2173 assert!(
2174 revoked.is_none() || revoked.is_some_and(|r| !r.is_empty()),
2176 "Revoked must be absent or non-empty"
2177 );
2178 }
2179
2180 Ok(self.0)
2181 }
2182}
2183
2184foreign_type_and_impl_send_sync! {
2185 type CType = ffi::X509_CRL;
2186 fn drop = ffi::X509_CRL_free;
2187
2188 pub struct X509Crl;
2190 pub struct X509CrlRef;
2192}
2193
2194pub enum CrlStatus<'a> {
2200 NotRevoked,
2202 Revoked(&'a X509RevokedRef),
2204 RemoveFromCrl(&'a X509RevokedRef),
2209}
2210
2211impl<'a> CrlStatus<'a> {
2212 unsafe fn from_ffi_status(
2217 status: c_int,
2218 revoked_entry: *mut ffi::X509_REVOKED,
2219 ) -> CrlStatus<'a> {
2220 match status {
2221 0 => CrlStatus::NotRevoked,
2222 1 => {
2223 assert!(!revoked_entry.is_null());
2224 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
2225 }
2226 2 => {
2227 assert!(!revoked_entry.is_null());
2228 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
2229 }
2230 _ => unreachable!(
2231 "{}",
2232 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
2233 ),
2234 }
2235 }
2236}
2237
2238impl X509Crl {
2239 from_pem! {
2240 #[corresponds(PEM_read_bio_X509_CRL)]
2244 from_pem,
2245 X509Crl,
2246 ffi::PEM_read_bio_X509_CRL
2247 }
2248
2249 from_der! {
2250 #[corresponds(d2i_X509_CRL)]
2252 from_der,
2253 X509Crl,
2254 ffi::d2i_X509_CRL
2255 }
2256}
2257
2258impl X509CrlRef {
2259 to_pem! {
2260 #[corresponds(PEM_write_bio_X509_CRL)]
2264 to_pem,
2265 ffi::PEM_write_bio_X509_CRL
2266 }
2267
2268 to_der! {
2269 #[corresponds(i2d_X509_CRL)]
2271 to_der,
2272 ffi::i2d_X509_CRL
2273 }
2274
2275 digest! {
2276 digest,
2282 ffi::X509_CRL_digest
2283 }
2284
2285 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
2287 unsafe {
2288 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
2289 if revoked.is_null() {
2290 None
2291 } else {
2292 Some(StackRef::from_ptr(revoked))
2293 }
2294 }
2295 }
2296
2297 #[corresponds(X509_CRL_get0_lastUpdate)]
2299 pub fn last_update(&self) -> &Asn1TimeRef {
2300 unsafe {
2301 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
2302 assert!(!date.is_null());
2303 Asn1TimeRef::from_ptr(date as *mut _)
2304 }
2305 }
2306
2307 #[corresponds(X509_CRL_get0_nextUpdate)]
2311 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
2312 unsafe {
2313 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
2314 Asn1TimeRef::from_const_ptr_opt(date)
2315 }
2316 }
2317
2318 #[corresponds(X509_CRL_get0_by_serial)]
2320 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
2321 unsafe {
2322 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2323 let status =
2324 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
2325 CrlStatus::from_ffi_status(status, ret)
2326 }
2327 }
2328
2329 #[corresponds(X509_CRL_get0_by_cert)]
2331 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
2332 unsafe {
2333 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
2334 let status =
2335 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
2336 CrlStatus::from_ffi_status(status, ret)
2337 }
2338 }
2339
2340 #[corresponds(X509_CRL_get_issuer)]
2342 pub fn issuer_name(&self) -> &X509NameRef {
2343 unsafe {
2344 let name = X509_CRL_get_issuer(self.as_ptr());
2345 assert!(!name.is_null());
2346 X509NameRef::from_ptr(name as *mut _)
2347 }
2348 }
2349
2350 #[corresponds(X509_CRL_verify)]
2357 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
2358 where
2359 T: HasPublic,
2360 {
2361 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
2362 }
2363
2364 #[corresponds(X509_CRL_get_ext_d2i)]
2368 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
2369 let mut critical = -1;
2370 let out = unsafe {
2371 let ext = ffi::X509_CRL_get_ext_d2i(
2373 self.as_ptr(),
2374 T::NID.as_raw(),
2375 &mut critical as *mut _,
2376 ptr::null_mut(),
2377 );
2378 T::Output::from_ptr_opt(ext as *mut _)
2381 };
2382 match (critical, out) {
2383 (0, Some(out)) => Ok(Some((false, out))),
2384 (1, Some(out)) => Ok(Some((true, out))),
2385 (-1 | -2, _) => Ok(None),
2387 (0 | 1, None) => Err(ErrorStack::get()),
2390 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
2391 }
2392 }
2393}
2394
2395#[derive(Copy, Clone, PartialEq, Eq)]
2397pub struct X509VerifyResult(c_int);
2398
2399impl fmt::Debug for X509VerifyResult {
2400 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2401 fmt.debug_struct("X509VerifyResult")
2402 .field("code", &self.0)
2403 .field("error", &self.error_string())
2404 .finish()
2405 }
2406}
2407
2408impl fmt::Display for X509VerifyResult {
2409 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2410 fmt.write_str(self.error_string())
2411 }
2412}
2413
2414impl Error for X509VerifyResult {}
2415
2416impl X509VerifyResult {
2417 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
2424 X509VerifyResult(err)
2425 }
2426
2427 #[allow(clippy::trivially_copy_pass_by_ref)]
2429 pub fn as_raw(&self) -> c_int {
2430 self.0
2431 }
2432
2433 #[corresponds(X509_verify_cert_error_string)]
2435 #[allow(clippy::trivially_copy_pass_by_ref)]
2436 pub fn error_string(&self) -> &'static str {
2437 ffi::init();
2438
2439 unsafe {
2440 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
2441 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
2442 }
2443 }
2444
2445 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
2447 pub const APPLICATION_VERIFICATION: X509VerifyResult =
2449 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
2450}
2451
2452foreign_type_and_impl_send_sync! {
2453 type CType = ffi::GENERAL_NAME;
2454 fn drop = ffi::GENERAL_NAME_free;
2455
2456 pub struct GeneralName;
2458 pub struct GeneralNameRef;
2460}
2461
2462impl GeneralName {
2463 unsafe fn new(
2464 type_: c_int,
2465 asn1_type: Asn1Type,
2466 value: &[u8],
2467 ) -> Result<GeneralName, ErrorStack> {
2468 ffi::init();
2469 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2470 (*gn.as_ptr()).type_ = type_;
2471 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2472 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2473
2474 #[cfg(any(boringssl, awslc))]
2475 {
2476 (*gn.as_ptr()).d.ptr = s.cast();
2477 }
2478 #[cfg(not(any(boringssl, awslc)))]
2479 {
2480 (*gn.as_ptr()).d = s.cast();
2481 }
2482
2483 Ok(gn)
2484 }
2485
2486 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2487 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2488 }
2489
2490 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2491 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2492 }
2493
2494 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2495 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2496 }
2497
2498 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2499 match ip {
2500 IpAddr::V4(addr) => unsafe {
2501 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2502 },
2503 IpAddr::V6(addr) => unsafe {
2504 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2505 },
2506 }
2507 }
2508
2509 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2510 unsafe {
2511 ffi::init();
2512 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2513 (*gn).type_ = ffi::GEN_RID;
2514
2515 #[cfg(any(boringssl, awslc))]
2516 {
2517 (*gn).d.registeredID = oid.as_ptr();
2518 }
2519 #[cfg(not(any(boringssl, awslc)))]
2520 {
2521 (*gn).d = oid.as_ptr().cast();
2522 }
2523
2524 mem::forget(oid);
2525
2526 Ok(GeneralName::from_ptr(gn))
2527 }
2528 }
2529
2530 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2531 unsafe {
2532 ffi::init();
2533
2534 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2535 ptr::null_mut(),
2536 &mut value.as_ptr().cast(),
2537 value.len().try_into().unwrap(),
2538 ))?;
2539
2540 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2541 (*gn).type_ = ffi::GEN_OTHERNAME;
2542
2543 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2544 gn,
2545 oid.as_ptr().cast(),
2546 typ,
2547 )) {
2548 ffi::GENERAL_NAME_free(gn);
2549 return Err(e);
2550 }
2551
2552 mem::forget(oid);
2553
2554 Ok(GeneralName::from_ptr(gn))
2555 }
2556 }
2557
2558 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2559 unsafe {
2560 ffi::init();
2561 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2562 (*gn).type_ = ffi::GEN_DIRNAME;
2563
2564 let dup = match name.to_owned() {
2565 Ok(dup) => dup,
2566 Err(e) => {
2567 ffi::GENERAL_NAME_free(gn);
2568 return Err(e);
2569 }
2570 };
2571
2572 #[cfg(any(boringssl, awslc))]
2573 {
2574 (*gn).d.directoryName = dup.as_ptr();
2575 }
2576 #[cfg(not(any(boringssl, awslc)))]
2577 {
2578 (*gn).d = dup.as_ptr().cast();
2579 }
2580
2581 std::mem::forget(dup);
2582
2583 Ok(GeneralName::from_ptr(gn))
2584 }
2585 }
2586}
2587
2588impl GeneralNameRef {
2589 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2590 unsafe {
2591 if (*self.as_ptr()).type_ != ffi_type {
2592 return None;
2593 }
2594
2595 #[cfg(any(boringssl, awslc))]
2596 let d = (*self.as_ptr()).d.ptr;
2597 #[cfg(not(any(boringssl, awslc)))]
2598 let d = (*self.as_ptr()).d;
2599
2600 let ptr = ASN1_STRING_get0_data(d as *mut _);
2601 let len = ffi::ASN1_STRING_length(d as *mut _);
2602
2603 #[allow(clippy::unnecessary_cast)]
2604 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2605 str::from_utf8(slice).ok()
2609 }
2610 }
2611
2612 pub fn email(&self) -> Option<&str> {
2614 self.ia5_string(ffi::GEN_EMAIL)
2615 }
2616
2617 pub fn directory_name(&self) -> Option<&X509NameRef> {
2619 unsafe {
2620 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2621 return None;
2622 }
2623
2624 #[cfg(any(boringssl, awslc))]
2625 let d = (*self.as_ptr()).d.ptr;
2626 #[cfg(not(any(boringssl, awslc)))]
2627 let d = (*self.as_ptr()).d;
2628
2629 Some(X509NameRef::from_const_ptr(d as *const _))
2630 }
2631 }
2632
2633 pub fn dnsname(&self) -> Option<&str> {
2635 self.ia5_string(ffi::GEN_DNS)
2636 }
2637
2638 pub fn uri(&self) -> Option<&str> {
2640 self.ia5_string(ffi::GEN_URI)
2641 }
2642
2643 pub fn ipaddress(&self) -> Option<&[u8]> {
2645 unsafe {
2646 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2647 return None;
2648 }
2649 #[cfg(any(boringssl, awslc))]
2650 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2651 #[cfg(not(any(boringssl, awslc)))]
2652 let d = (*self.as_ptr()).d;
2653
2654 let ptr = ASN1_STRING_get0_data(d as *mut _);
2655 let len = ffi::ASN1_STRING_length(d as *mut _);
2656
2657 #[allow(clippy::unnecessary_cast)]
2658 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2659 }
2660 }
2661}
2662
2663impl fmt::Debug for GeneralNameRef {
2664 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2665 if let Some(email) = self.email() {
2666 formatter.write_str(email)
2667 } else if let Some(dnsname) = self.dnsname() {
2668 formatter.write_str(dnsname)
2669 } else if let Some(uri) = self.uri() {
2670 formatter.write_str(uri)
2671 } else if let Some(ipaddress) = self.ipaddress() {
2672 let address = <[u8; 16]>::try_from(ipaddress)
2673 .map(IpAddr::from)
2674 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2675 match address {
2676 Ok(a) => fmt::Debug::fmt(&a, formatter),
2677 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2678 }
2679 } else {
2680 formatter.write_str("(empty)")
2681 }
2682 }
2683}
2684
2685impl Stackable for GeneralName {
2686 type StackType = ffi::stack_st_GENERAL_NAME;
2687}
2688
2689foreign_type_and_impl_send_sync! {
2690 type CType = ffi::DIST_POINT;
2691 fn drop = ffi::DIST_POINT_free;
2692
2693 pub struct DistPoint;
2695 pub struct DistPointRef;
2697}
2698
2699impl DistPointRef {
2700 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2702 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2703 }
2704}
2705
2706foreign_type_and_impl_send_sync! {
2707 type CType = ffi::DIST_POINT_NAME;
2708 fn drop = ffi::DIST_POINT_NAME_free;
2709
2710 pub struct DistPointName;
2712 pub struct DistPointNameRef;
2714}
2715
2716impl DistPointNameRef {
2717 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2719 unsafe {
2720 if (*self.as_ptr()).type_ != 0 {
2721 return None;
2722 }
2723 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2724 }
2725 }
2726}
2727
2728impl Stackable for DistPoint {
2729 type StackType = ffi::stack_st_DIST_POINT;
2730}
2731
2732foreign_type_and_impl_send_sync! {
2733 type CType = ffi::BASIC_CONSTRAINTS;
2734 fn drop = ffi::BASIC_CONSTRAINTS_free;
2735
2736 pub struct BasicConstraints;
2738 pub struct BasicConstraintsRef;
2740}
2741
2742impl BasicConstraintsRef {
2743 pub fn ca(&self) -> bool {
2744 unsafe { (*(self.as_ptr())).ca != 0 }
2745 }
2746
2747 pub fn pathlen(&self) -> Option<&Asn1IntegerRef> {
2748 if !self.ca() {
2749 return None;
2750 }
2751 unsafe {
2752 let data = (*(self.as_ptr())).pathlen;
2753 Asn1IntegerRef::from_const_ptr_opt(data as _)
2754 }
2755 }
2756}
2757
2758foreign_type_and_impl_send_sync! {
2759 type CType = ffi::ACCESS_DESCRIPTION;
2760 fn drop = ffi::ACCESS_DESCRIPTION_free;
2761
2762 pub struct AccessDescription;
2764 pub struct AccessDescriptionRef;
2766}
2767
2768impl AccessDescriptionRef {
2769 pub fn method(&self) -> &Asn1ObjectRef {
2771 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2772 }
2773
2774 pub fn location(&self) -> &GeneralNameRef {
2776 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2777 }
2778}
2779
2780impl Stackable for AccessDescription {
2781 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2782}
2783
2784foreign_type_and_impl_send_sync! {
2785 type CType = ffi::X509_ALGOR;
2786 fn drop = ffi::X509_ALGOR_free;
2787
2788 pub struct X509Algorithm;
2790 pub struct X509AlgorithmRef;
2792}
2793
2794impl X509AlgorithmRef {
2795 pub fn object(&self) -> &Asn1ObjectRef {
2797 unsafe {
2798 let mut oid = ptr::null();
2799 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2800 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2801 }
2802 }
2803}
2804
2805foreign_type_and_impl_send_sync! {
2806 type CType = ffi::X509_OBJECT;
2807 fn drop = X509_OBJECT_free;
2808
2809 pub struct X509Object;
2811 pub struct X509ObjectRef;
2813}
2814
2815impl X509ObjectRef {
2816 pub fn x509(&self) -> Option<&X509Ref> {
2817 unsafe {
2818 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2819 X509Ref::from_const_ptr_opt(ptr)
2820 }
2821 }
2822}
2823
2824impl Stackable for X509Object {
2825 type StackType = ffi::stack_st_X509_OBJECT;
2826}
2827
2828use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2829
2830use ffi::{
2831 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2832 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2833};
2834
2835use ffi::X509_OBJECT_free;
2836use ffi::X509_OBJECT_get0_X509;
2837
2838use ffi::{
2839 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2840 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2841};
2842
2843#[derive(Copy, Clone, PartialEq, Eq)]
2844pub struct X509PurposeId(c_int);
2845
2846impl X509PurposeId {
2847 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2848 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2849 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2850 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2851 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2852 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2853 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2854 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2855 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2856 #[cfg(ossl320)]
2857 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2858
2859 pub fn from_raw(id: c_int) -> Self {
2861 X509PurposeId(id)
2862 }
2863
2864 pub fn as_raw(&self) -> c_int {
2866 self.0
2867 }
2868}
2869
2870pub struct X509PurposeRef(Opaque);
2872
2873impl ForeignTypeRef for X509PurposeRef {
2875 type CType = ffi::X509_PURPOSE;
2876}
2877
2878impl X509PurposeRef {
2879 #[allow(clippy::unnecessary_cast)]
2893 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2894 unsafe {
2895 let sname = CString::new(sname).unwrap();
2896 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2897 Ok(purpose)
2898 }
2899 }
2900 #[corresponds(X509_PURPOSE_get0)]
2903 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2904 unsafe {
2905 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2906 Ok(X509PurposeRef::from_const_ptr(ptr))
2907 }
2908 }
2909
2910 pub fn purpose(&self) -> X509PurposeId {
2921 unsafe {
2922 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2923 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2924 }
2925 }
2926}