1use crate::libc_types::{c_int, c_long, c_void};
11use foreign_types::{ForeignType, ForeignTypeRef};
12use openssl_macros::corresponds;
13use std::convert::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;
23use std::sync::LazyLock;
24
25use crate::asn1::{
26 Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef,
27 Asn1Type,
28};
29use crate::bio::{MemBio, MemBioSlice};
30use crate::conf::ConfRef;
31use crate::error::ErrorStack;
32use crate::ex_data::Index;
33use crate::hash::{DigestBytes, MessageDigest};
34use crate::nid::Nid;
35use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
36use crate::ssl::SslRef;
37use crate::stack::{Stack, StackRef, Stackable};
38use crate::string::OpensslString;
39use crate::util::ForeignTypeRefExt;
40use crate::x509::verify::{X509VerifyParam, X509VerifyParamRef};
41use crate::{cvt, cvt_n, cvt_p, try_int};
42use crate::{ffi, free_data_box};
43
44pub mod extension;
45pub mod store;
46pub mod verify;
47
48#[cfg(test)]
49mod tests;
50
51static STORE_INDEX: LazyLock<Index<X509StoreContext, store::X509Store>> =
52 LazyLock::new(|| X509StoreContext::new_ex_index().unwrap());
53
54static CERT_INDEX: LazyLock<Index<X509StoreContext, X509>> =
55 LazyLock::new(|| X509StoreContext::new_ex_index().unwrap());
56
57static CERT_CHAIN_INDEX: LazyLock<Index<X509StoreContext, Stack<X509>>> =
58 LazyLock::new(|| X509StoreContext::new_ex_index().unwrap());
59
60foreign_type_and_impl_send_sync! {
61 type CType = ffi::X509_STORE_CTX;
62 fn drop = ffi::X509_STORE_CTX_free;
63
64 pub struct X509StoreContext;
66}
67
68impl X509StoreContext {
69 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
72 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
73 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
74 }
75
76 #[corresponds(X509_STORE_CTX_new)]
78 pub fn new() -> Result<X509StoreContext, ErrorStack> {
79 unsafe {
80 ffi::init();
81 cvt_p(ffi::X509_STORE_CTX_new()).map(|p| X509StoreContext::from_ptr(p))
82 }
83 }
84
85 #[corresponds(SSL_CTX_get_ex_new_index)]
90 pub fn new_ex_index<T>() -> Result<Index<X509StoreContext, T>, ErrorStack>
91 where
92 T: 'static + Sync + Send,
93 {
94 unsafe {
95 ffi::init();
96 let idx = cvt_n(get_new_x509_store_ctx_idx(Some(free_data_box::<T>)))?;
97 Ok(Index::from_raw(idx))
98 }
99 }
100}
101
102impl X509StoreContextRef {
103 #[corresponds(X509_STORE_CTX_get_ex_data)]
105 #[must_use]
106 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
107 unsafe {
108 ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw())
109 .cast::<T>()
110 .as_ref()
111 }
112 }
113
114 #[corresponds(X509_STORE_CTX_get_ex_data)]
116 pub fn ex_data_mut<T>(&mut self, index: Index<X509StoreContext, T>) -> Option<&mut T> {
117 unsafe {
118 ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw())
119 .cast::<T>()
120 .as_mut()
121 }
122 }
123
124 #[corresponds(X509_STORE_CTX_set_ex_data)]
129 pub fn set_ex_data<T>(&mut self, index: Index<X509StoreContext, T>, data: T) {
130 if let Some(old) = self.ex_data_mut(index) {
131 *old = data;
132
133 return;
134 }
135
136 unsafe {
137 let data = Box::new(data);
138
139 ffi::X509_STORE_CTX_set_ex_data(
140 self.as_ptr(),
141 index.as_raw(),
142 Box::into_raw(data).cast(),
143 );
144 }
145 }
146
147 #[corresponds(X509_STORE_CTX_get_error)]
149 pub fn verify_result(&self) -> X509VerifyResult {
150 unsafe { X509VerifyError::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
151 }
152
153 #[corresponds(X509_STORE_CTX_init)]
167 pub fn init<F, T>(
168 &mut self,
169 trust: &store::X509StoreRef,
170 cert: &X509Ref,
171 cert_chain: &StackRef<X509>,
172 with_context: F,
173 ) -> Result<T, ErrorStack>
174 where
175 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
176 {
177 struct Cleanup<'a>(&'a mut X509StoreContextRef);
178
179 impl Drop for Cleanup<'_> {
180 fn drop(&mut self) {
181 unsafe {
182 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
183 }
184 }
185 }
186
187 unsafe {
188 let cleanup = Cleanup(self);
189
190 cvt(ffi::X509_STORE_CTX_init(
191 cleanup.0.as_ptr(),
192 trust.as_ptr(),
193 cert.as_ptr(),
194 cert_chain.as_ptr(),
195 ))?;
196
197 with_context(cleanup.0)
198 }
199 }
200
201 #[corresponds(X509_STORE_CTX_init)]
208 pub fn reset_with_context_data(
209 &mut self,
210 trust: store::X509Store,
211 cert: X509,
212 cert_chain: Stack<X509>,
213 ) -> Result<(), ErrorStack> {
214 unsafe {
215 if let Err(e) = cvt(ffi::X509_STORE_CTX_init(
216 self.as_ptr(),
217 trust.as_ptr(),
218 cert.as_ptr(),
219 cert_chain.as_ptr(),
220 )) {
221 ffi::X509_STORE_CTX_cleanup(self.as_ptr());
222
223 return Err(e);
224 }
225 }
226
227 self.set_ex_data(*STORE_INDEX, trust);
228 self.set_ex_data(*CERT_INDEX, cert);
229 self.set_ex_data(*CERT_CHAIN_INDEX, cert_chain);
230
231 Ok(())
232 }
233
234 #[corresponds(X509_STORE_CTX_get0_param)]
236 pub fn verify_param(&mut self) -> &X509VerifyParamRef {
237 unsafe { X509VerifyParamRef::from_ptr(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) }
238 }
239
240 #[corresponds(X509_STORE_CTX_get0_param)]
242 pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef {
243 unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) }
244 }
245
246 #[corresponds(X509_STORE_CTX_set0_param)]
248 pub fn set_verify_param(&mut self, param: X509VerifyParam) {
249 unsafe { ffi::X509_STORE_CTX_set0_param(self.as_ptr(), param.as_ptr()) }
250 }
251
252 #[corresponds(X509_verify_cert)]
259 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
260 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
261 }
262
263 #[corresponds(X509_STORE_CTX_set_error)]
265 pub fn set_error(&mut self, result: X509VerifyResult) {
266 unsafe {
267 ffi::X509_STORE_CTX_set_error(
268 self.as_ptr(),
269 result
270 .err()
271 .as_ref()
272 .map_or(ffi::X509_V_OK, X509VerifyError::as_raw),
273 );
274 }
275 }
276
277 #[corresponds(X509_STORE_CTX_get_current_cert)]
280 #[must_use]
281 pub fn current_cert(&self) -> Option<&X509Ref> {
282 unsafe {
283 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
284 if ptr.is_null() {
285 None
286 } else {
287 Some(X509Ref::from_ptr(ptr))
288 }
289 }
290 }
291
292 #[corresponds(X509_STORE_CTX_get_error_depth)]
297 #[must_use]
298 pub fn error_depth(&self) -> u32 {
299 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
300 }
301
302 #[corresponds(X509_STORE_CTX_get0_chain)]
304 #[must_use]
305 pub fn chain(&self) -> Option<&StackRef<X509>> {
306 unsafe {
307 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
308
309 if chain.is_null() {
310 None
311 } else {
312 Some(StackRef::from_ptr(chain))
313 }
314 }
315 }
316
317 #[corresponds(X509_STORE_CTX_get0_untrusted)]
320 #[must_use]
321 pub fn untrusted(&self) -> Option<&StackRef<X509>> {
322 unsafe {
323 let certs = ffi::X509_STORE_CTX_get0_untrusted(self.as_ptr());
324
325 if certs.is_null() {
326 None
327 } else {
328 Some(StackRef::from_ptr(certs))
329 }
330 }
331 }
332
333 #[corresponds(X509_STORE_CTX_get0_cert)]
336 #[must_use]
337 pub fn cert(&self) -> Option<&X509Ref> {
338 unsafe {
339 let ptr = ffi::X509_STORE_CTX_get0_cert(self.as_ptr());
340 if ptr.is_null() {
341 None
342 } else {
343 Some(X509Ref::from_ptr(ptr))
344 }
345 }
346 }
347}
348
349pub struct X509Builder(X509);
351
352impl X509Builder {
353 #[corresponds(X509_new)]
355 pub fn new() -> Result<X509Builder, ErrorStack> {
356 unsafe {
357 ffi::init();
358 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509::from_ptr(p)))
359 }
360 }
361
362 #[corresponds(X509_set1_notAfter)]
364 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
365 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())) }
366 }
367
368 #[corresponds(X509_set1_notBefore)]
370 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
371 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())) }
372 }
373
374 #[corresponds(X509_set_version)]
379 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
380 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version.into())) }
381 }
382
383 #[corresponds(X509_set_serialNumber)]
385 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
386 unsafe {
387 cvt(ffi::X509_set_serialNumber(
388 self.0.as_ptr(),
389 serial_number.as_ptr(),
390 ))
391 }
392 }
393
394 #[corresponds(X509_set_issuer_name)]
396 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
397 unsafe {
398 cvt(ffi::X509_set_issuer_name(
399 self.0.as_ptr(),
400 issuer_name.as_ptr(),
401 ))
402 }
403 }
404
405 #[corresponds(X509_set_subject_name)]
424 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
425 unsafe {
426 cvt(ffi::X509_set_subject_name(
427 self.0.as_ptr(),
428 subject_name.as_ptr(),
429 ))
430 }
431 }
432
433 #[corresponds(X509_set_pubkey)]
435 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
436 where
437 T: HasPublic,
438 {
439 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())) }
440 }
441
442 #[corresponds(X509V3_set_ctx)]
446 #[must_use]
447 pub fn x509v3_context<'a>(
448 &'a self,
449 issuer: Option<&'a X509Ref>,
450 conf: Option<&'a ConfRef>,
451 ) -> X509v3Context<'a> {
452 unsafe {
453 let mut ctx = mem::zeroed();
454
455 let issuer = match issuer {
456 Some(issuer) => issuer.as_ptr(),
457 None => self.0.as_ptr(),
458 };
459 let subject = self.0.as_ptr();
460 ffi::X509V3_set_ctx(
461 &mut ctx,
462 issuer,
463 subject,
464 ptr::null_mut(),
465 ptr::null_mut(),
466 0,
467 );
468
469 if let Some(conf) = conf {
471 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
472 }
473
474 X509v3Context(ctx, PhantomData)
475 }
476 }
477
478 #[corresponds(X509_add_ext)]
480 pub fn append_extension(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
481 unsafe {
482 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
483 Ok(())
484 }
485 }
486
487 #[corresponds(X509_sign)]
489 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
490 where
491 T: HasPrivate,
492 {
493 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())) }
494 }
495
496 #[must_use]
498 pub fn build(self) -> X509 {
499 self.0
500 }
501}
502
503foreign_type_and_impl_send_sync! {
504 type CType = ffi::X509;
505 fn drop = ffi::X509_free;
506
507 pub struct X509;
509}
510
511impl X509Ref {
512 #[corresponds(X509_get_subject_name)]
514 #[must_use]
515 pub fn subject_name(&self) -> &X509NameRef {
516 unsafe {
517 let name = ffi::X509_get_subject_name(self.as_ptr());
518 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
519 }
520 }
521
522 #[corresponds(X509_subject_name_hash)]
524 #[must_use]
525 pub fn subject_name_hash(&self) -> u32 {
526 unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 }
527 }
528
529 #[corresponds(X509_get_ext_d2i)]
531 #[must_use]
532 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
533 unsafe {
534 let stack = ffi::X509_get_ext_d2i(
535 self.as_ptr(),
536 ffi::NID_subject_alt_name,
537 ptr::null_mut(),
538 ptr::null_mut(),
539 );
540 if stack.is_null() {
541 None
542 } else {
543 Some(Stack::from_ptr(stack.cast()))
544 }
545 }
546 }
547
548 #[corresponds(X509_get_issuer_name)]
550 #[must_use]
551 pub fn issuer_name(&self) -> &X509NameRef {
552 unsafe {
553 let name = ffi::X509_get_issuer_name(self.as_ptr());
554 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
555 }
556 }
557
558 #[corresponds(X509_get_ext_d2i)]
560 #[must_use]
561 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
562 unsafe {
563 let stack = ffi::X509_get_ext_d2i(
564 self.as_ptr(),
565 ffi::NID_issuer_alt_name,
566 ptr::null_mut(),
567 ptr::null_mut(),
568 );
569 if stack.is_null() {
570 None
571 } else {
572 Some(Stack::from_ptr(stack.cast()))
573 }
574 }
575 }
576
577 #[corresponds(X509_get0_subject_key_id)]
579 #[must_use]
580 pub fn subject_key_id(&self) -> Option<&Asn1StringRef> {
581 unsafe {
582 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
583 Asn1StringRef::from_const_ptr_opt(data)
584 }
585 }
586
587 #[corresponds(X509_get0_authority_key_id)]
589 #[must_use]
590 pub fn authority_key_id(&self) -> Option<&Asn1StringRef> {
591 unsafe {
592 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
593 Asn1StringRef::from_const_ptr_opt(data)
594 }
595 }
596
597 #[corresponds(X509_get_pubkey)]
598 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
599 unsafe {
600 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
601 Ok(PKey::from_ptr(pkey))
602 }
603 }
604
605 #[corresponds(X509_digest)]
607 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
608 unsafe {
609 let mut digest = DigestBytes {
610 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
611 len: ffi::EVP_MAX_MD_SIZE as usize,
612 };
613 let mut len = try_int(ffi::EVP_MAX_MD_SIZE)?;
614 cvt(ffi::X509_digest(
615 self.as_ptr(),
616 hash_type.as_ptr(),
617 digest.buf.as_mut_ptr(),
618 &mut len,
619 ))?;
620 digest.len = try_int(len)?;
621
622 Ok(digest)
623 }
624 }
625
626 #[deprecated(since = "0.10.9", note = "renamed to digest")]
627 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
628 self.digest(hash_type).map(|b| b.to_vec())
629 }
630
631 #[corresponds(X509_getm_notAfter)]
633 #[must_use]
634 pub fn not_after(&self) -> &Asn1TimeRef {
635 unsafe {
636 let date = X509_getm_notAfter(self.as_ptr());
637 assert!(!date.is_null());
638 Asn1TimeRef::from_ptr(date)
639 }
640 }
641
642 #[corresponds(X509_getm_notBefore)]
644 #[must_use]
645 pub fn not_before(&self) -> &Asn1TimeRef {
646 unsafe {
647 let date = X509_getm_notBefore(self.as_ptr());
648 assert!(!date.is_null());
649 Asn1TimeRef::from_ptr(date)
650 }
651 }
652
653 #[corresponds(X509_get0_signature)]
655 #[must_use]
656 pub fn signature(&self) -> &Asn1BitStringRef {
657 unsafe {
658 let mut signature = ptr::null();
659 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
660 assert!(!signature.is_null());
661 Asn1BitStringRef::from_ptr(signature.cast_mut())
662 }
663 }
664
665 #[corresponds(X509_get0_signature)]
667 #[must_use]
668 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
669 unsafe {
670 let mut algor = ptr::null();
671 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
672 assert!(!algor.is_null());
673 X509AlgorithmRef::from_ptr(algor.cast_mut())
674 }
675 }
676
677 #[corresponds(X509_get1_ocsp)]
680 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
681 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
682 }
683
684 #[corresponds(X509_check_issued)]
686 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
687 unsafe {
688 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
689 X509VerifyError::from_raw(r)
690 }
691 }
692
693 #[corresponds(X509_verify)]
700 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
701 where
702 T: HasPublic,
703 {
704 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
705 }
706
707 #[corresponds(X509_get_serialNumber)]
709 #[must_use]
710 pub fn serial_number(&self) -> &Asn1IntegerRef {
711 unsafe {
712 let r = ffi::X509_get_serialNumber(self.as_ptr());
713 assert!(!r.is_null());
714 Asn1IntegerRef::from_ptr(r)
715 }
716 }
717
718 pub fn check_host(&self, host: &str) -> Result<bool, ErrorStack> {
719 unsafe {
720 cvt_n(ffi::X509_check_host(
721 self.as_ptr(),
722 host.as_ptr().cast(),
723 host.len(),
724 0,
725 std::ptr::null_mut(),
726 ))
727 .map(|n| n == 1)
728 }
729 }
730
731 #[corresponds(X509_check_ip_asc)]
732 pub fn check_ip_asc(&self, address: &str) -> Result<bool, ErrorStack> {
733 let c_str = CString::new(address).map_err(ErrorStack::internal_error)?;
734
735 unsafe { cvt_n(ffi::X509_check_ip_asc(self.as_ptr(), c_str.as_ptr(), 0)).map(|n| n == 1) }
736 }
737
738 to_pem! {
739 #[corresponds(PEM_write_bio_X509)]
743 to_pem,
744 ffi::PEM_write_bio_X509
745 }
746
747 to_der! {
748 #[corresponds(i2d_X509)]
750 to_der,
751 ffi::i2d_X509
752 }
753}
754
755impl ToOwned for X509Ref {
756 type Owned = X509;
757
758 fn to_owned(&self) -> X509 {
759 unsafe {
760 X509_up_ref(self.as_ptr());
761 X509::from_ptr(self.as_ptr())
762 }
763 }
764}
765
766impl X509 {
767 pub fn builder() -> Result<X509Builder, ErrorStack> {
769 X509Builder::new()
770 }
771
772 from_pem! {
773 #[corresponds(PEM_read_bio_X509)]
777 from_pem,
778 X509,
779 ffi::PEM_read_bio_X509
780 }
781
782 from_der! {
783 #[corresponds(d2i_X509)]
785 from_der,
786 X509,
787 ffi::d2i_X509,
788 crate::libc_types::c_long
789 }
790
791 #[corresponds(PEM_read_bio_X509)]
793 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
794 unsafe {
795 ffi::init();
796 let bio = MemBioSlice::new(pem)?;
797
798 let mut certs = vec![];
799 loop {
800 let r =
801 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
802 if r.is_null() {
803 let err = ffi::ERR_peek_last_error();
804
805 if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM.0.try_into().unwrap()
806 && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
807 {
808 ErrorStack::clear();
809 break;
810 }
811
812 return Err(ErrorStack::get());
813 } else {
814 certs.push(X509::from_ptr(r));
815 }
816 }
817
818 Ok(certs)
819 }
820 }
821}
822
823impl Clone for X509 {
824 fn clone(&self) -> X509 {
825 X509Ref::to_owned(self)
826 }
827}
828
829impl fmt::Debug for X509 {
830 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
831 let serial = match &self.serial_number().to_bn() {
832 Ok(bn) => match bn.to_hex_str() {
833 Ok(hex) => hex.to_string(),
834 Err(_) => "".to_string(),
835 },
836 Err(_) => "".to_string(),
837 };
838 let mut debug_struct = formatter.debug_struct("X509");
839 debug_struct.field("serial_number", &serial);
840 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
841 debug_struct.field("issuer", &self.issuer_name());
842 debug_struct.field("subject", &self.subject_name());
843 if let Some(subject_alt_names) = &self.subject_alt_names() {
844 debug_struct.field("subject_alt_names", subject_alt_names);
845 }
846 debug_struct.field("not_before", &self.not_before());
847 debug_struct.field("not_after", &self.not_after());
848
849 if let Ok(public_key) = &self.public_key() {
850 debug_struct.field("public_key", public_key);
851 }
852 debug_struct.finish()
855 }
856}
857
858impl AsRef<X509Ref> for X509Ref {
859 fn as_ref(&self) -> &X509Ref {
860 self
861 }
862}
863
864impl Stackable for X509 {
865 type StackType = ffi::stack_st_X509;
866}
867
868pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
870
871impl X509v3Context<'_> {
872 #[must_use]
873 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
874 std::ptr::addr_of!(self.0).cast_mut()
875 }
876}
877
878foreign_type_and_impl_send_sync! {
879 type CType = ffi::X509_EXTENSION;
880 fn drop = ffi::X509_EXTENSION_free;
881
882 pub struct X509Extension;
884}
885
886impl Stackable for X509Extension {
887 type StackType = ffi::stack_st_X509_EXTENSION;
888}
889
890impl X509Extension {
891 pub fn new(
902 conf: Option<&ConfRef>,
903 context: Option<&X509v3Context>,
904 name: &str,
905 value: &str,
906 ) -> Result<X509Extension, ErrorStack> {
907 let name = CString::new(name).map_err(ErrorStack::internal_error)?;
908 let value = CString::new(value).map_err(ErrorStack::internal_error)?;
909 let mut ctx;
910 unsafe {
911 ffi::init();
912 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
913 let context_ptr = match context {
914 Some(c) => c.as_ptr(),
915 None => {
916 ctx = mem::zeroed();
917
918 ffi::X509V3_set_ctx(
919 &mut ctx,
920 ptr::null_mut(),
921 ptr::null_mut(),
922 ptr::null_mut(),
923 ptr::null_mut(),
924 0,
925 );
926 &mut ctx
927 }
928 };
929 let name = name.as_ptr().cast_mut();
930 let value = value.as_ptr().cast_mut();
931
932 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value))
933 .map(|p| X509Extension::from_ptr(p))
934 }
935 }
936
937 pub fn new_nid(
948 conf: Option<&ConfRef>,
949 context: Option<&X509v3Context>,
950 name: Nid,
951 value: &str,
952 ) -> Result<X509Extension, ErrorStack> {
953 let value = CString::new(value).map_err(ErrorStack::internal_error)?;
954 let mut ctx;
955 unsafe {
956 ffi::init();
957 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
958 let context_ptr = match context {
959 Some(c) => c.as_ptr(),
960 None => {
961 ctx = mem::zeroed();
962
963 ffi::X509V3_set_ctx(
964 &mut ctx,
965 ptr::null_mut(),
966 ptr::null_mut(),
967 ptr::null_mut(),
968 ptr::null_mut(),
969 0,
970 );
971 &mut ctx
972 }
973 };
974 let name = name.as_raw();
975 let value = value.as_ptr().cast_mut();
976
977 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value))
978 .map(|p| X509Extension::from_ptr(p))
979 }
980 }
981
982 pub(crate) unsafe fn new_internal(
983 nid: Nid,
984 critical: bool,
985 value: *mut c_void,
986 ) -> Result<X509Extension, ErrorStack> {
987 ffi::init();
988 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value))
989 .map(|p| X509Extension::from_ptr(p))
990 }
991}
992
993impl X509ExtensionRef {
994 to_der! {
995 to_der,
997 ffi::i2d_X509_EXTENSION
998 }
999}
1000
1001pub struct X509NameBuilder(X509Name);
1003
1004impl X509NameBuilder {
1005 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1007 unsafe {
1008 ffi::init();
1009 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name::from_ptr(p)))
1010 }
1011 }
1012
1013 #[corresponds(X509_NAME_add_entry_by_txt)]
1015 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1016 unsafe {
1017 let field = CString::new(field).map_err(ErrorStack::internal_error)?;
1018 cvt(ffi::X509_NAME_add_entry_by_txt(
1019 self.0.as_ptr(),
1020 field.as_ptr().cast_mut(),
1021 ffi::MBSTRING_UTF8,
1022 value.as_ptr(),
1023 try_int(value.len())?,
1024 -1,
1025 0,
1026 ))
1027 }
1028 }
1029
1030 #[corresponds(X509_NAME_add_entry_by_txt)]
1032 pub fn append_entry_by_text_with_type(
1033 &mut self,
1034 field: &str,
1035 value: &str,
1036 ty: Asn1Type,
1037 ) -> Result<(), ErrorStack> {
1038 unsafe {
1039 let field = CString::new(field).map_err(ErrorStack::internal_error)?;
1040 cvt(ffi::X509_NAME_add_entry_by_txt(
1041 self.0.as_ptr(),
1042 field.as_ptr().cast_mut(),
1043 ty.as_raw(),
1044 value.as_ptr(),
1045 try_int(value.len())?,
1046 -1,
1047 0,
1048 ))
1049 }
1050 }
1051
1052 #[corresponds(X509_NAME_add_entry_by_NID)]
1054 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1055 unsafe {
1056 cvt(ffi::X509_NAME_add_entry_by_NID(
1057 self.0.as_ptr(),
1058 field.as_raw(),
1059 ffi::MBSTRING_UTF8,
1060 value.as_ptr().cast_mut(),
1061 try_int(value.len())?,
1062 -1,
1063 0,
1064 ))
1065 }
1066 }
1067
1068 #[corresponds(X509_NAME_add_entry_by_NID)]
1070 pub fn append_entry_by_nid_with_type(
1071 &mut self,
1072 field: Nid,
1073 value: &str,
1074 ty: Asn1Type,
1075 ) -> Result<(), ErrorStack> {
1076 unsafe {
1077 cvt(ffi::X509_NAME_add_entry_by_NID(
1078 self.0.as_ptr(),
1079 field.as_raw(),
1080 ty.as_raw(),
1081 value.as_ptr().cast_mut(),
1082 try_int(value.len())?,
1083 -1,
1084 0,
1085 ))
1086 }
1087 }
1088
1089 #[must_use]
1091 pub fn build(self) -> X509Name {
1092 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1096 }
1097}
1098
1099foreign_type_and_impl_send_sync! {
1100 type CType = ffi::X509_NAME;
1101 fn drop = ffi::X509_NAME_free;
1102
1103 pub struct X509Name;
1105}
1106
1107impl X509Name {
1108 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1110 X509NameBuilder::new()
1111 }
1112
1113 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1117 let file = CString::new(file.as_ref().as_os_str().as_encoded_bytes())
1118 .map_err(ErrorStack::internal_error)?;
1119 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1120 }
1121
1122 from_der! {
1123 #[corresponds(d2i_X509_NAME)]
1125 from_der,
1126 X509Name,
1127 ffi::d2i_X509_NAME,
1128 crate::libc_types::c_long
1129 }
1130}
1131
1132impl Stackable for X509Name {
1133 type StackType = ffi::stack_st_X509_NAME;
1134}
1135
1136impl X509NameRef {
1137 #[must_use]
1139 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1140 X509NameEntries {
1141 name: self,
1142 nid: Some(nid),
1143 loc: -1,
1144 }
1145 }
1146
1147 #[must_use]
1149 pub fn entries(&self) -> X509NameEntries<'_> {
1150 X509NameEntries {
1151 name: self,
1152 nid: None,
1153 loc: -1,
1154 }
1155 }
1156
1157 #[corresponds(X509_NAME_print_ex)]
1161 #[must_use]
1162 pub fn print_ex(&self, flags: i32) -> Option<String> {
1163 unsafe {
1164 let bio = MemBio::new().ok()?;
1165 ffi::X509_NAME_print_ex(bio.as_ptr(), self.as_ptr(), 0, flags as _);
1166 let buf = bio.get_buf().to_vec();
1167 let res = String::from_utf8(buf);
1168 res.ok()
1169 }
1170 }
1171
1172 to_der! {
1173 #[corresponds(i2d_X509_NAME)]
1175 to_der,
1176 ffi::i2d_X509_NAME
1177 }
1178}
1179
1180impl fmt::Debug for X509NameRef {
1181 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1182 formatter.debug_list().entries(self.entries()).finish()
1183 }
1184}
1185
1186pub struct X509NameEntries<'a> {
1188 name: &'a X509NameRef,
1189 nid: Option<Nid>,
1190 loc: c_int,
1191}
1192
1193impl<'a> Iterator for X509NameEntries<'a> {
1194 type Item = &'a X509NameEntryRef;
1195
1196 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1197 unsafe {
1198 match self.nid {
1199 Some(nid) => {
1200 self.loc =
1202 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1203 if self.loc == -1 {
1204 return None;
1205 }
1206 }
1207 None => {
1208 self.loc += 1;
1210 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1211 return None;
1212 }
1213 }
1214 }
1215
1216 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1217 assert!(!entry.is_null());
1218
1219 Some(X509NameEntryRef::from_ptr(entry))
1220 }
1221 }
1222}
1223
1224foreign_type_and_impl_send_sync! {
1225 type CType = ffi::X509_NAME_ENTRY;
1226 fn drop = ffi::X509_NAME_ENTRY_free;
1227
1228 pub struct X509NameEntry;
1230}
1231
1232impl X509NameEntryRef {
1233 #[corresponds(X509_NAME_ENTRY_get_data)]
1235 #[must_use]
1236 pub fn data(&self) -> &Asn1StringRef {
1237 unsafe {
1238 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1239 Asn1StringRef::from_ptr(data)
1240 }
1241 }
1242
1243 #[corresponds(X509_NAME_ENTRY_get_object)]
1246 #[must_use]
1247 pub fn object(&self) -> &Asn1ObjectRef {
1248 unsafe {
1249 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1250 Asn1ObjectRef::from_ptr(object)
1251 }
1252 }
1253}
1254
1255impl fmt::Debug for X509NameEntryRef {
1256 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1257 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1258 }
1259}
1260
1261pub struct X509ReqBuilder(X509Req);
1263
1264impl X509ReqBuilder {
1265 #[corresponds(X509_REQ_new)]
1267 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1268 unsafe {
1269 ffi::init();
1270 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req::from_ptr(p)))
1271 }
1272 }
1273
1274 #[corresponds(X509_REQ_set_version)]
1276 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1277 unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version.into())) }
1278 }
1279
1280 #[corresponds(X509_REQ_set_subject_name)]
1282 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1283 unsafe {
1284 cvt(ffi::X509_REQ_set_subject_name(
1285 self.0.as_ptr(),
1286 subject_name.as_ptr(),
1287 ))
1288 }
1289 }
1290
1291 #[corresponds(X509_REQ_set_pubkey)]
1293 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1294 where
1295 T: HasPublic,
1296 {
1297 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())) }
1298 }
1299
1300 #[must_use]
1303 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1304 unsafe {
1305 let mut ctx = mem::zeroed();
1306
1307 ffi::X509V3_set_ctx(
1308 &mut ctx,
1309 ptr::null_mut(),
1310 ptr::null_mut(),
1311 self.0.as_ptr(),
1312 ptr::null_mut(),
1313 0,
1314 );
1315
1316 if let Some(conf) = conf {
1318 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1319 }
1320
1321 X509v3Context(ctx, PhantomData)
1322 }
1323 }
1324
1325 pub fn add_extensions(
1327 &mut self,
1328 extensions: &StackRef<X509Extension>,
1329 ) -> Result<(), ErrorStack> {
1330 unsafe {
1331 cvt(ffi::X509_REQ_add_extensions(
1332 self.0.as_ptr(),
1333 extensions.as_ptr(),
1334 ))
1335 }
1336 }
1337
1338 #[corresponds(X509_REQ_sign)]
1340 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1341 where
1342 T: HasPrivate,
1343 {
1344 unsafe {
1345 cvt(ffi::X509_REQ_sign(
1346 self.0.as_ptr(),
1347 key.as_ptr(),
1348 hash.as_ptr(),
1349 ))
1350 }
1351 }
1352
1353 #[must_use]
1355 pub fn build(self) -> X509Req {
1356 self.0
1357 }
1358}
1359
1360foreign_type_and_impl_send_sync! {
1361 type CType = ffi::X509_REQ;
1362 fn drop = ffi::X509_REQ_free;
1363
1364 pub struct X509Req;
1366}
1367
1368impl X509Req {
1369 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1371 X509ReqBuilder::new()
1372 }
1373
1374 from_pem! {
1375 #[corresponds(PEM_read_bio_X509_REQ)]
1379 from_pem,
1380 X509Req,
1381 ffi::PEM_read_bio_X509_REQ
1382 }
1383
1384 from_der! {
1385 #[corresponds(d2i_X509_REQ)]
1387 from_der,
1388 X509Req,
1389 ffi::d2i_X509_REQ,
1390 crate::libc_types::c_long
1391 }
1392}
1393
1394impl X509ReqRef {
1395 to_pem! {
1396 #[corresponds(PEM_write_bio_X509_REQ)]
1400 to_pem,
1401 ffi::PEM_write_bio_X509_REQ
1402 }
1403
1404 to_der! {
1405 #[corresponds(i2d_X509_REQ)]
1407 to_der,
1408 ffi::i2d_X509_REQ
1409 }
1410
1411 #[corresponds(X509_REQ_get_version)]
1413 #[must_use]
1414 pub fn version(&self) -> i32 {
1415 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1416 }
1417
1418 #[corresponds(X509_REQ_get_subject_name)]
1420 #[must_use]
1421 pub fn subject_name(&self) -> &X509NameRef {
1422 unsafe {
1423 let name = X509_REQ_get_subject_name(self.as_ptr());
1424 assert!(!name.is_null());
1425 X509NameRef::from_ptr(name)
1426 }
1427 }
1428
1429 #[corresponds(X509_REQ_get_pubkey)]
1431 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1432 unsafe {
1433 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1434 Ok(PKey::from_ptr(key))
1435 }
1436 }
1437
1438 #[corresponds(X509_REQ_verify)]
1442 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1443 where
1444 T: HasPublic,
1445 {
1446 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1447 }
1448
1449 #[corresponds(X509_REQ_get_extensions)]
1451 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1452 unsafe {
1453 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1454 Ok(Stack::from_ptr(extensions))
1455 }
1456 }
1457}
1458
1459pub type X509VerifyResult = Result<(), X509VerifyError>;
1461
1462#[derive(Copy, Clone, PartialEq, Eq)]
1463pub struct X509VerifyError(c_int);
1464
1465impl fmt::Debug for X509VerifyError {
1466 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1467 fmt.debug_struct("X509VerifyError")
1468 .field("code", &self.0)
1469 .field("error", &self.error_string())
1470 .finish()
1471 }
1472}
1473
1474impl fmt::Display for X509VerifyError {
1475 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1476 fmt.write_str(self.error_string())
1477 }
1478}
1479
1480impl Error for X509VerifyError {}
1481
1482impl X509VerifyError {
1483 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
1490 if err == ffi::X509_V_OK {
1491 Ok(())
1492 } else {
1493 Err(X509VerifyError(err))
1494 }
1495 }
1496
1497 #[allow(clippy::trivially_copy_pass_by_ref)]
1499 #[must_use]
1500 pub fn as_raw(&self) -> c_int {
1501 self.0
1502 }
1503
1504 #[corresponds(X509_verify_cert_error_string)]
1508 #[allow(clippy::trivially_copy_pass_by_ref)]
1509 #[must_use]
1510 pub fn error_string(&self) -> &'static str {
1511 ffi::init();
1512
1513 unsafe {
1514 let s = ffi::X509_verify_cert_error_string(c_long::from(self.0));
1515 CStr::from_ptr(s).to_str().unwrap_or_default()
1516 }
1517 }
1518}
1519
1520#[allow(missing_docs)] impl X509VerifyError {
1522 pub const UNSPECIFIED: Self = Self(ffi::X509_V_ERR_UNSPECIFIED);
1523 pub const UNABLE_TO_GET_ISSUER_CERT: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT);
1524 pub const UNABLE_TO_GET_CRL: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL);
1525 pub const UNABLE_TO_DECRYPT_CERT_SIGNATURE: Self =
1526 Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
1527 pub const UNABLE_TO_DECRYPT_CRL_SIGNATURE: Self =
1528 Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE);
1529 pub const UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: Self =
1530 Self(ffi::X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
1531 pub const CERT_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CERT_SIGNATURE_FAILURE);
1532 pub const CRL_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CRL_SIGNATURE_FAILURE);
1533 pub const CERT_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CERT_NOT_YET_VALID);
1534 pub const CERT_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CERT_HAS_EXPIRED);
1535 pub const CRL_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CRL_NOT_YET_VALID);
1536 pub const CRL_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CRL_HAS_EXPIRED);
1537 pub const ERROR_IN_CERT_NOT_BEFORE_FIELD: Self =
1538 Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
1539 pub const ERROR_IN_CERT_NOT_AFTER_FIELD: Self =
1540 Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
1541 pub const ERROR_IN_CRL_LAST_UPDATE_FIELD: Self =
1542 Self(ffi::X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD);
1543 pub const ERROR_IN_CRL_NEXT_UPDATE_FIELD: Self =
1544 Self(ffi::X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
1545 pub const OUT_OF_MEM: Self = Self(ffi::X509_V_ERR_OUT_OF_MEM);
1546 pub const DEPTH_ZERO_SELF_SIGNED_CERT: Self = Self(ffi::X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
1547 pub const SELF_SIGNED_CERT_IN_CHAIN: Self = Self(ffi::X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
1548 pub const UNABLE_TO_GET_ISSUER_CERT_LOCALLY: Self =
1549 Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
1550 pub const UNABLE_TO_VERIFY_LEAF_SIGNATURE: Self =
1551 Self(ffi::X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
1552 pub const CERT_CHAIN_TOO_LONG: Self = Self(ffi::X509_V_ERR_CERT_CHAIN_TOO_LONG);
1553 pub const CERT_REVOKED: Self = Self(ffi::X509_V_ERR_CERT_REVOKED);
1554 pub const INVALID_CA: Self = Self(ffi::X509_V_ERR_INVALID_CA);
1555 pub const PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PATH_LENGTH_EXCEEDED);
1556 pub const INVALID_PURPOSE: Self = Self(ffi::X509_V_ERR_INVALID_PURPOSE);
1557 pub const CERT_UNTRUSTED: Self = Self(ffi::X509_V_ERR_CERT_UNTRUSTED);
1558 pub const CERT_REJECTED: Self = Self(ffi::X509_V_ERR_CERT_REJECTED);
1559 pub const SUBJECT_ISSUER_MISMATCH: Self = Self(ffi::X509_V_ERR_SUBJECT_ISSUER_MISMATCH);
1560 pub const AKID_SKID_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_SKID_MISMATCH);
1561 pub const AKID_ISSUER_SERIAL_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
1562 pub const KEYUSAGE_NO_CERTSIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CERTSIGN);
1563 pub const UNABLE_TO_GET_CRL_ISSUER: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER);
1564 pub const UNHANDLED_CRITICAL_EXTENSION: Self =
1565 Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION);
1566 pub const KEYUSAGE_NO_CRL_SIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CRL_SIGN);
1567 pub const UNHANDLED_CRITICAL_CRL_EXTENSION: Self =
1568 Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION);
1569 pub const INVALID_NON_CA: Self = Self(ffi::X509_V_ERR_INVALID_NON_CA);
1570 pub const PROXY_PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED);
1571 pub const KEYUSAGE_NO_DIGITAL_SIGNATURE: Self =
1572 Self(ffi::X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE);
1573 pub const PROXY_CERTIFICATES_NOT_ALLOWED: Self =
1574 Self(ffi::X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED);
1575 pub const INVALID_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_EXTENSION);
1576 pub const INVALID_POLICY_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_POLICY_EXTENSION);
1577 pub const NO_EXPLICIT_POLICY: Self = Self(ffi::X509_V_ERR_NO_EXPLICIT_POLICY);
1578 pub const DIFFERENT_CRL_SCOPE: Self = Self(ffi::X509_V_ERR_DIFFERENT_CRL_SCOPE);
1579 pub const UNSUPPORTED_EXTENSION_FEATURE: Self =
1580 Self(ffi::X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
1581 pub const UNNESTED_RESOURCE: Self = Self(ffi::X509_V_ERR_UNNESTED_RESOURCE);
1582 pub const PERMITTED_VIOLATION: Self = Self(ffi::X509_V_ERR_PERMITTED_VIOLATION);
1583 pub const EXCLUDED_VIOLATION: Self = Self(ffi::X509_V_ERR_EXCLUDED_VIOLATION);
1584 pub const SUBTREE_MINMAX: Self = Self(ffi::X509_V_ERR_SUBTREE_MINMAX);
1585 pub const APPLICATION_VERIFICATION: Self = Self(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
1586 pub const UNSUPPORTED_CONSTRAINT_TYPE: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
1587 pub const UNSUPPORTED_CONSTRAINT_SYNTAX: Self =
1588 Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
1589 pub const UNSUPPORTED_NAME_SYNTAX: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_NAME_SYNTAX);
1590 pub const CRL_PATH_VALIDATION_ERROR: Self = Self(ffi::X509_V_ERR_CRL_PATH_VALIDATION_ERROR);
1591 pub const HOSTNAME_MISMATCH: Self = Self(ffi::X509_V_ERR_HOSTNAME_MISMATCH);
1592 pub const EMAIL_MISMATCH: Self = Self(ffi::X509_V_ERR_EMAIL_MISMATCH);
1593 pub const IP_ADDRESS_MISMATCH: Self = Self(ffi::X509_V_ERR_IP_ADDRESS_MISMATCH);
1594 pub const INVALID_CALL: Self = Self(ffi::X509_V_ERR_INVALID_CALL);
1595 pub const STORE_LOOKUP: Self = Self(ffi::X509_V_ERR_STORE_LOOKUP);
1596 pub const NAME_CONSTRAINTS_WITHOUT_SANS: Self =
1597 Self(ffi::X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS);
1598}
1599
1600foreign_type_and_impl_send_sync! {
1601 type CType = ffi::GENERAL_NAME;
1602 fn drop = ffi::GENERAL_NAME_free;
1603
1604 pub struct GeneralName;
1606}
1607
1608impl GeneralName {
1609 unsafe fn new(
1610 type_: c_int,
1611 asn1_type: Asn1Type,
1612 value: &[u8],
1613 ) -> Result<GeneralName, ErrorStack> {
1614 ffi::init();
1615 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
1616 (*gn.as_ptr()).type_ = type_;
1617 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
1618 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
1619
1620 (*gn.as_ptr()).d.ptr = s.cast();
1621
1622 Ok(gn)
1623 }
1624
1625 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
1626 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
1627 }
1628
1629 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
1630 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
1631 }
1632
1633 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
1634 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
1635 }
1636
1637 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
1638 match ip {
1639 IpAddr::V4(addr) => unsafe {
1640 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
1641 },
1642 IpAddr::V6(addr) => unsafe {
1643 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
1644 },
1645 }
1646 }
1647
1648 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
1649 unsafe {
1650 ffi::init();
1651 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
1652 (*gn).type_ = ffi::GEN_RID;
1653 (*gn).d.registeredID = oid.into_ptr();
1654
1655 Ok(GeneralName::from_ptr(gn))
1656 }
1657 }
1658}
1659
1660impl GeneralNameRef {
1661 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
1662 unsafe {
1663 if (*self.as_ptr()).type_ != ffi_type {
1664 return None;
1665 }
1666
1667 let asn = Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ia5);
1668
1669 asn.to_str()
1673 }
1674 }
1675
1676 #[must_use]
1678 pub fn email(&self) -> Option<&str> {
1679 self.ia5_string(ffi::GEN_EMAIL)
1680 }
1681
1682 #[must_use]
1684 pub fn dnsname(&self) -> Option<&str> {
1685 self.ia5_string(ffi::GEN_DNS)
1686 }
1687
1688 #[must_use]
1690 pub fn uri(&self) -> Option<&str> {
1691 self.ia5_string(ffi::GEN_URI)
1692 }
1693
1694 #[must_use]
1696 pub fn ipaddress(&self) -> Option<&[u8]> {
1697 unsafe {
1698 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
1699 return None;
1700 }
1701
1702 Some(Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ip).as_slice())
1703 }
1704 }
1705}
1706
1707impl fmt::Debug for GeneralNameRef {
1708 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1709 if let Some(email) = self.email() {
1710 formatter.write_str(email)
1711 } else if let Some(dnsname) = self.dnsname() {
1712 formatter.write_str(dnsname)
1713 } else if let Some(uri) = self.uri() {
1714 formatter.write_str(uri)
1715 } else if let Some(ipaddress) = self.ipaddress() {
1716 let result = String::from_utf8_lossy(ipaddress);
1717 formatter.write_str(&result)
1718 } else {
1719 formatter.write_str("(empty)")
1720 }
1721 }
1722}
1723
1724impl Stackable for GeneralName {
1725 type StackType = ffi::stack_st_GENERAL_NAME;
1726}
1727
1728foreign_type_and_impl_send_sync! {
1729 type CType = ffi::X509_ALGOR;
1730 fn drop = ffi::X509_ALGOR_free;
1731
1732 pub struct X509Algorithm;
1734}
1735
1736impl X509AlgorithmRef {
1737 #[must_use]
1739 pub fn object(&self) -> &Asn1ObjectRef {
1740 unsafe {
1741 let mut oid = ptr::null();
1742 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
1743 assert!(!oid.is_null());
1744 Asn1ObjectRef::from_ptr(oid.cast_mut())
1745 }
1746 }
1747}
1748
1749foreign_type_and_impl_send_sync! {
1750 type CType = ffi::X509_OBJECT;
1751 fn drop = X509_OBJECT_free;
1752
1753 pub struct X509Object;
1755}
1756
1757impl X509ObjectRef {
1758 #[must_use]
1759 pub fn x509(&self) -> Option<&X509Ref> {
1760 unsafe {
1761 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
1762 if ptr.is_null() {
1763 None
1764 } else {
1765 Some(X509Ref::from_ptr(ptr))
1766 }
1767 }
1768 }
1769}
1770
1771impl Stackable for X509Object {
1772 type StackType = ffi::stack_st_X509_OBJECT;
1773}
1774
1775use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
1776
1777use crate::ffi::{
1778 X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, X509_STORE_CTX_get0_chain,
1779 X509_set1_notAfter, X509_set1_notBefore,
1780};
1781
1782use crate::ffi::X509_OBJECT_get0_X509;
1783
1784#[allow(bad_style)]
1785unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
1786 ffi::X509_OBJECT_free_contents(x);
1787 ffi::OPENSSL_free(x.cast());
1788}
1789
1790unsafe fn get_new_x509_store_ctx_idx(f: ffi::CRYPTO_EX_free) -> c_int {
1791 ffi::X509_STORE_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f)
1792}