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, Asn1String, Asn1StringRef,
27 Asn1TimeRef, 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 pub fn append_extension_der_payload(
491 &mut self,
492 object: &Asn1ObjectRef,
493 critical: bool,
494 der_payload: &[u8],
495 ) -> Result<(), ErrorStack> {
496 let extension = X509Extension::from_der_payload(object, critical, der_payload)?;
497 self.append_extension(extension.as_ref())
498 }
499
500 #[corresponds(X509_add_ext)]
504 pub fn append_extensions_from_cert(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
505 for ext in cert.extensions() {
506 self.append_extension(ext)?;
507 }
508 Ok(())
509 }
510
511 #[corresponds(X509_sign)]
513 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
514 where
515 T: HasPrivate,
516 {
517 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())) }
518 }
519
520 #[must_use]
522 pub fn build(self) -> X509 {
523 self.0
524 }
525}
526
527foreign_type_and_impl_send_sync! {
528 type CType = ffi::X509;
529 fn drop = ffi::X509_free;
530
531 pub struct X509;
533}
534
535impl X509Ref {
536 #[corresponds(X509_get_subject_name)]
538 #[must_use]
539 pub fn subject_name(&self) -> &X509NameRef {
540 unsafe {
541 let name = ffi::X509_get_subject_name(self.as_ptr());
542 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
543 }
544 }
545
546 #[corresponds(X509_subject_name_hash)]
548 #[must_use]
549 pub fn subject_name_hash(&self) -> u32 {
550 unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 }
551 }
552
553 #[corresponds(X509_get_ext_count)]
555 #[must_use]
556 pub fn extension_count(&self) -> usize {
557 unsafe { ffi::X509_get_ext_count(self.as_ptr()) as usize }
558 }
559
560 #[corresponds(X509_get_ext)]
564 #[must_use]
565 pub fn extension(&self, index: usize) -> Option<&X509ExtensionRef> {
566 if index >= self.extension_count() {
567 return None;
568 }
569
570 unsafe {
571 let ext = ffi::X509_get_ext(self.as_ptr(), index as c_int);
572 X509ExtensionRef::from_const_ptr_opt(ext.cast_const())
573 }
574 }
575
576 #[must_use]
578 pub fn extensions(&self) -> X509ExtensionIter<'_> {
579 X509ExtensionIter {
580 cert: self,
581 index: 0,
582 len: self.extension_count(),
583 }
584 }
585
586 #[corresponds(X509_get_ext_d2i)]
588 #[must_use]
589 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
590 unsafe {
591 let stack = ffi::X509_get_ext_d2i(
592 self.as_ptr(),
593 ffi::NID_subject_alt_name,
594 ptr::null_mut(),
595 ptr::null_mut(),
596 );
597 if stack.is_null() {
598 None
599 } else {
600 Some(Stack::from_ptr(stack.cast()))
601 }
602 }
603 }
604
605 #[corresponds(X509_get_issuer_name)]
607 #[must_use]
608 pub fn issuer_name(&self) -> &X509NameRef {
609 unsafe {
610 let name = ffi::X509_get_issuer_name(self.as_ptr());
611 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
612 }
613 }
614
615 #[corresponds(X509_get_ext_d2i)]
617 #[must_use]
618 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
619 unsafe {
620 let stack = ffi::X509_get_ext_d2i(
621 self.as_ptr(),
622 ffi::NID_issuer_alt_name,
623 ptr::null_mut(),
624 ptr::null_mut(),
625 );
626 if stack.is_null() {
627 None
628 } else {
629 Some(Stack::from_ptr(stack.cast()))
630 }
631 }
632 }
633
634 #[corresponds(X509_get0_subject_key_id)]
636 #[must_use]
637 pub fn subject_key_id(&self) -> Option<&Asn1StringRef> {
638 unsafe {
639 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
640 Asn1StringRef::from_const_ptr_opt(data)
641 }
642 }
643
644 #[corresponds(X509_get0_authority_key_id)]
646 #[must_use]
647 pub fn authority_key_id(&self) -> Option<&Asn1StringRef> {
648 unsafe {
649 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
650 Asn1StringRef::from_const_ptr_opt(data)
651 }
652 }
653
654 #[corresponds(X509_get_pubkey)]
655 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
656 unsafe {
657 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
658 Ok(PKey::from_ptr(pkey))
659 }
660 }
661
662 #[corresponds(X509_digest)]
664 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
665 unsafe {
666 let mut digest = DigestBytes {
667 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
668 len: ffi::EVP_MAX_MD_SIZE as usize,
669 };
670 let mut len = try_int(ffi::EVP_MAX_MD_SIZE)?;
671 cvt(ffi::X509_digest(
672 self.as_ptr(),
673 hash_type.as_ptr(),
674 digest.buf.as_mut_ptr(),
675 &mut len,
676 ))?;
677 digest.len = try_int(len)?;
678
679 Ok(digest)
680 }
681 }
682
683 #[corresponds(X509_pubkey_digest)]
689 pub fn pubkey_digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
690 unsafe {
691 let mut digest = DigestBytes {
692 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
693 len: ffi::EVP_MAX_MD_SIZE as usize,
694 };
695 let mut len = try_int(ffi::EVP_MAX_MD_SIZE)?;
696 cvt(ffi::X509_pubkey_digest(
697 self.as_ptr(),
698 hash_type.as_ptr(),
699 digest.buf.as_mut_ptr(),
700 &mut len,
701 ))?;
702 digest.len = try_int(len)?;
703
704 Ok(digest)
705 }
706 }
707
708 #[deprecated(since = "0.10.9", note = "renamed to digest")]
709 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
710 self.digest(hash_type).map(|b| b.to_vec())
711 }
712
713 #[corresponds(X509_getm_notAfter)]
715 #[must_use]
716 pub fn not_after(&self) -> &Asn1TimeRef {
717 unsafe {
718 let date = X509_getm_notAfter(self.as_ptr());
719 assert!(!date.is_null());
720 Asn1TimeRef::from_ptr(date)
721 }
722 }
723
724 #[corresponds(X509_getm_notBefore)]
726 #[must_use]
727 pub fn not_before(&self) -> &Asn1TimeRef {
728 unsafe {
729 let date = X509_getm_notBefore(self.as_ptr());
730 assert!(!date.is_null());
731 Asn1TimeRef::from_ptr(date)
732 }
733 }
734
735 #[corresponds(X509_get0_signature)]
737 #[must_use]
738 pub fn signature(&self) -> &Asn1BitStringRef {
739 unsafe {
740 let mut signature = ptr::null();
741 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
742 assert!(!signature.is_null());
743 Asn1BitStringRef::from_ptr(signature.cast_mut())
744 }
745 }
746
747 #[corresponds(X509_get0_signature)]
749 #[must_use]
750 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
751 unsafe {
752 let mut algor = ptr::null();
753 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
754 assert!(!algor.is_null());
755 X509AlgorithmRef::from_ptr(algor.cast_mut())
756 }
757 }
758
759 #[corresponds(X509_get1_ocsp)]
762 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
763 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
764 }
765
766 #[corresponds(X509_check_issued)]
768 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
769 unsafe {
770 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
771 X509VerifyError::from_raw(r)
772 }
773 }
774
775 #[corresponds(X509_verify)]
782 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
783 where
784 T: HasPublic,
785 {
786 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
787 }
788
789 #[corresponds(X509_get_serialNumber)]
791 #[must_use]
792 pub fn serial_number(&self) -> &Asn1IntegerRef {
793 unsafe {
794 let r = ffi::X509_get_serialNumber(self.as_ptr());
795 assert!(!r.is_null());
796 Asn1IntegerRef::from_ptr(r)
797 }
798 }
799
800 pub fn check_host(&self, host: &str) -> Result<bool, ErrorStack> {
801 unsafe {
802 cvt_n(ffi::X509_check_host(
803 self.as_ptr(),
804 host.as_ptr().cast(),
805 host.len(),
806 0,
807 std::ptr::null_mut(),
808 ))
809 .map(|n| n == 1)
810 }
811 }
812
813 #[corresponds(X509_check_ip_asc)]
814 pub fn check_ip_asc(&self, address: &str) -> Result<bool, ErrorStack> {
815 let c_str = CString::new(address).map_err(ErrorStack::internal_error)?;
816
817 unsafe { cvt_n(ffi::X509_check_ip_asc(self.as_ptr(), c_str.as_ptr(), 0)).map(|n| n == 1) }
818 }
819
820 to_pem! {
821 #[corresponds(PEM_write_bio_X509)]
825 to_pem,
826 ffi::PEM_write_bio_X509
827 }
828
829 to_der! {
830 #[corresponds(i2d_X509)]
832 to_der,
833 ffi::i2d_X509
834 }
835}
836
837pub struct X509ExtensionIter<'a> {
839 cert: &'a X509Ref,
840 index: usize,
841 len: usize,
842}
843
844impl<'a> Iterator for X509ExtensionIter<'a> {
845 type Item = &'a X509ExtensionRef;
846
847 fn next(&mut self) -> Option<Self::Item> {
848 if self.index >= self.len {
849 return None;
850 }
851 let item = self.cert.extension(self.index);
852 self.index += 1;
853 item
854 }
855
856 fn size_hint(&self) -> (usize, Option<usize>) {
857 let remaining = self.len.saturating_sub(self.index);
858 (remaining, Some(remaining))
859 }
860}
861
862impl ExactSizeIterator for X509ExtensionIter<'_> {}
863
864impl ToOwned for X509Ref {
865 type Owned = X509;
866
867 fn to_owned(&self) -> X509 {
868 unsafe {
869 X509_up_ref(self.as_ptr());
870 X509::from_ptr(self.as_ptr())
871 }
872 }
873}
874
875impl X509 {
876 pub fn builder() -> Result<X509Builder, ErrorStack> {
878 X509Builder::new()
879 }
880
881 from_pem! {
882 #[corresponds(PEM_read_bio_X509)]
886 from_pem,
887 X509,
888 ffi::PEM_read_bio_X509
889 }
890
891 from_der! {
892 #[corresponds(d2i_X509)]
894 from_der,
895 X509,
896 ffi::d2i_X509,
897 crate::libc_types::c_long
898 }
899
900 #[corresponds(PEM_read_bio_X509)]
902 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
903 unsafe {
904 ffi::init();
905 let bio = MemBioSlice::new(pem)?;
906
907 let mut certs = vec![];
908 loop {
909 let r =
910 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
911 if r.is_null() {
912 let err = ffi::ERR_peek_last_error();
913
914 if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM.0.try_into().unwrap()
915 && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
916 {
917 ErrorStack::clear();
918 break;
919 }
920
921 return Err(ErrorStack::get());
922 } else {
923 certs.push(X509::from_ptr(r));
924 }
925 }
926
927 Ok(certs)
928 }
929 }
930}
931
932impl Clone for X509 {
933 fn clone(&self) -> X509 {
934 X509Ref::to_owned(self)
935 }
936}
937
938impl fmt::Debug for X509 {
939 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
940 let serial = match &self.serial_number().to_bn() {
941 Ok(bn) => match bn.to_hex_str() {
942 Ok(hex) => hex.to_string(),
943 Err(_) => "".to_string(),
944 },
945 Err(_) => "".to_string(),
946 };
947 let mut debug_struct = formatter.debug_struct("X509");
948 debug_struct.field("serial_number", &serial);
949 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
950 debug_struct.field("issuer", &self.issuer_name());
951 debug_struct.field("subject", &self.subject_name());
952 if let Some(subject_alt_names) = &self.subject_alt_names() {
953 debug_struct.field("subject_alt_names", subject_alt_names);
954 }
955 debug_struct.field("not_before", &self.not_before());
956 debug_struct.field("not_after", &self.not_after());
957
958 if let Ok(public_key) = &self.public_key() {
959 debug_struct.field("public_key", public_key);
960 }
961 debug_struct.finish()
964 }
965}
966
967impl AsRef<X509Ref> for X509Ref {
968 fn as_ref(&self) -> &X509Ref {
969 self
970 }
971}
972
973impl Stackable for X509 {
974 type StackType = ffi::stack_st_X509;
975}
976
977pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
979
980impl X509v3Context<'_> {
981 #[must_use]
982 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
983 std::ptr::addr_of!(self.0).cast_mut()
984 }
985}
986
987foreign_type_and_impl_send_sync! {
988 type CType = ffi::X509_EXTENSION;
989 fn drop = ffi::X509_EXTENSION_free;
990
991 pub struct X509Extension;
993}
994
995impl Stackable for X509Extension {
996 type StackType = ffi::stack_st_X509_EXTENSION;
997}
998
999impl X509Extension {
1000 pub fn new(
1011 conf: Option<&ConfRef>,
1012 context: Option<&X509v3Context>,
1013 name: &str,
1014 value: &str,
1015 ) -> Result<X509Extension, ErrorStack> {
1016 let name = CString::new(name).map_err(ErrorStack::internal_error)?;
1017 let value = CString::new(value).map_err(ErrorStack::internal_error)?;
1018 let mut ctx;
1019 unsafe {
1020 ffi::init();
1021 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1022 let context_ptr = match context {
1023 Some(c) => c.as_ptr(),
1024 None => {
1025 ctx = mem::zeroed();
1026
1027 ffi::X509V3_set_ctx(
1028 &mut ctx,
1029 ptr::null_mut(),
1030 ptr::null_mut(),
1031 ptr::null_mut(),
1032 ptr::null_mut(),
1033 0,
1034 );
1035 &mut ctx
1036 }
1037 };
1038 let name = name.as_ptr().cast_mut();
1039 let value = value.as_ptr().cast_mut();
1040
1041 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value))
1042 .map(|p| X509Extension::from_ptr(p))
1043 }
1044 }
1045
1046 pub fn new_nid(
1057 conf: Option<&ConfRef>,
1058 context: Option<&X509v3Context>,
1059 name: Nid,
1060 value: &str,
1061 ) -> Result<X509Extension, ErrorStack> {
1062 let value = CString::new(value).map_err(ErrorStack::internal_error)?;
1063 let mut ctx;
1064 unsafe {
1065 ffi::init();
1066 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
1067 let context_ptr = match context {
1068 Some(c) => c.as_ptr(),
1069 None => {
1070 ctx = mem::zeroed();
1071
1072 ffi::X509V3_set_ctx(
1073 &mut ctx,
1074 ptr::null_mut(),
1075 ptr::null_mut(),
1076 ptr::null_mut(),
1077 ptr::null_mut(),
1078 0,
1079 );
1080 &mut ctx
1081 }
1082 };
1083 let name = name.as_raw();
1084 let value = value.as_ptr().cast_mut();
1085
1086 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value))
1087 .map(|p| X509Extension::from_ptr(p))
1088 }
1089 }
1090
1091 pub(crate) unsafe fn new_internal(
1092 nid: Nid,
1093 critical: bool,
1094 value: *mut c_void,
1095 ) -> Result<X509Extension, ErrorStack> {
1096 ffi::init();
1097 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value))
1098 .map(|p| X509Extension::from_ptr(p))
1099 }
1100
1101 #[corresponds(X509_EXTENSION_create_by_OBJ)]
1106 pub fn from_der_payload(
1107 object: &Asn1ObjectRef,
1108 critical: bool,
1109 der_payload: &[u8],
1110 ) -> Result<X509Extension, ErrorStack> {
1111 unsafe {
1112 ffi::init();
1113 let octet = Asn1String::from_ptr(cvt_p(ffi::ASN1_OCTET_STRING_new())?.cast());
1114 cvt(ffi::ASN1_STRING_set(
1115 octet.as_ptr(),
1116 der_payload.as_ptr().cast(),
1117 try_int(der_payload.len())?,
1118 ))?;
1119 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1120 ptr::null_mut(),
1121 object.as_ptr(),
1122 critical as c_int,
1123 octet.as_ptr().cast(),
1124 ))
1125 .map(|p| X509Extension::from_ptr(p))
1126 }
1127 }
1128}
1129
1130impl X509ExtensionRef {
1131 to_der! {
1132 to_der,
1134 ffi::i2d_X509_EXTENSION
1135 }
1136
1137 #[corresponds(X509_EXTENSION_get_object)]
1139 #[must_use]
1140 pub fn object(&self) -> &Asn1ObjectRef {
1141 unsafe {
1142 let object = ffi::X509_EXTENSION_get_object(self.as_ptr());
1143 Asn1ObjectRef::from_const_ptr_opt(object)
1144 .expect("x509 extension object must not be null")
1145 }
1146 }
1147
1148 #[corresponds(X509_EXTENSION_get_critical)]
1150 #[must_use]
1151 pub fn critical(&self) -> bool {
1152 unsafe { ffi::X509_EXTENSION_get_critical(self.as_ptr()) != 0 }
1153 }
1154
1155 #[corresponds(X509_EXTENSION_get_data)]
1159 #[must_use]
1160 pub fn data(&self) -> &Asn1StringRef {
1161 unsafe {
1162 let data = ffi::X509_EXTENSION_get_data(self.as_ptr());
1163 Asn1StringRef::from_const_ptr_opt(data.cast_const())
1164 .expect("x509 extension data must not be null")
1165 }
1166 }
1167}
1168
1169pub struct X509NameBuilder(X509Name);
1171
1172impl X509NameBuilder {
1173 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1175 unsafe {
1176 ffi::init();
1177 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name::from_ptr(p)))
1178 }
1179 }
1180
1181 #[corresponds(X509_NAME_add_entry_by_txt)]
1183 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1184 unsafe {
1185 let field = CString::new(field).map_err(ErrorStack::internal_error)?;
1186 cvt(ffi::X509_NAME_add_entry_by_txt(
1187 self.0.as_ptr(),
1188 field.as_ptr().cast_mut(),
1189 ffi::MBSTRING_UTF8,
1190 value.as_ptr(),
1191 try_int(value.len())?,
1192 -1,
1193 0,
1194 ))
1195 }
1196 }
1197
1198 #[corresponds(X509_NAME_add_entry_by_txt)]
1200 pub fn append_entry_by_text_with_type(
1201 &mut self,
1202 field: &str,
1203 value: &str,
1204 ty: Asn1Type,
1205 ) -> Result<(), ErrorStack> {
1206 unsafe {
1207 let field = CString::new(field).map_err(ErrorStack::internal_error)?;
1208 cvt(ffi::X509_NAME_add_entry_by_txt(
1209 self.0.as_ptr(),
1210 field.as_ptr().cast_mut(),
1211 ty.as_raw(),
1212 value.as_ptr(),
1213 try_int(value.len())?,
1214 -1,
1215 0,
1216 ))
1217 }
1218 }
1219
1220 #[corresponds(X509_NAME_add_entry_by_NID)]
1222 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1223 unsafe {
1224 cvt(ffi::X509_NAME_add_entry_by_NID(
1225 self.0.as_ptr(),
1226 field.as_raw(),
1227 ffi::MBSTRING_UTF8,
1228 value.as_ptr().cast_mut(),
1229 try_int(value.len())?,
1230 -1,
1231 0,
1232 ))
1233 }
1234 }
1235
1236 #[corresponds(X509_NAME_add_entry_by_NID)]
1238 pub fn append_entry_by_nid_with_type(
1239 &mut self,
1240 field: Nid,
1241 value: &str,
1242 ty: Asn1Type,
1243 ) -> Result<(), ErrorStack> {
1244 unsafe {
1245 cvt(ffi::X509_NAME_add_entry_by_NID(
1246 self.0.as_ptr(),
1247 field.as_raw(),
1248 ty.as_raw(),
1249 value.as_ptr().cast_mut(),
1250 try_int(value.len())?,
1251 -1,
1252 0,
1253 ))
1254 }
1255 }
1256
1257 #[must_use]
1259 pub fn build(self) -> X509Name {
1260 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1264 }
1265}
1266
1267foreign_type_and_impl_send_sync! {
1268 type CType = ffi::X509_NAME;
1269 fn drop = ffi::X509_NAME_free;
1270
1271 pub struct X509Name;
1273}
1274
1275impl X509Name {
1276 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1278 X509NameBuilder::new()
1279 }
1280
1281 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1285 let file = CString::new(file.as_ref().as_os_str().as_encoded_bytes())
1286 .map_err(ErrorStack::internal_error)?;
1287 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1288 }
1289
1290 from_der! {
1291 #[corresponds(d2i_X509_NAME)]
1293 from_der,
1294 X509Name,
1295 ffi::d2i_X509_NAME,
1296 crate::libc_types::c_long
1297 }
1298}
1299
1300impl Stackable for X509Name {
1301 type StackType = ffi::stack_st_X509_NAME;
1302}
1303
1304impl X509NameRef {
1305 #[must_use]
1307 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1308 X509NameEntries {
1309 name: self,
1310 nid: Some(nid),
1311 loc: -1,
1312 }
1313 }
1314
1315 #[must_use]
1317 pub fn entries(&self) -> X509NameEntries<'_> {
1318 X509NameEntries {
1319 name: self,
1320 nid: None,
1321 loc: -1,
1322 }
1323 }
1324
1325 #[corresponds(X509_NAME_print_ex)]
1329 #[must_use]
1330 pub fn print_ex(&self, flags: i32) -> Option<String> {
1331 unsafe {
1332 let bio = MemBio::new().ok()?;
1333 ffi::X509_NAME_print_ex(bio.as_ptr(), self.as_ptr(), 0, flags as _);
1334 let buf = bio.get_buf().to_vec();
1335 let res = String::from_utf8(buf);
1336 res.ok()
1337 }
1338 }
1339
1340 to_der! {
1341 #[corresponds(i2d_X509_NAME)]
1343 to_der,
1344 ffi::i2d_X509_NAME
1345 }
1346}
1347
1348impl fmt::Debug for X509NameRef {
1349 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1350 formatter.debug_list().entries(self.entries()).finish()
1351 }
1352}
1353
1354pub struct X509NameEntries<'a> {
1356 name: &'a X509NameRef,
1357 nid: Option<Nid>,
1358 loc: c_int,
1359}
1360
1361impl<'a> Iterator for X509NameEntries<'a> {
1362 type Item = &'a X509NameEntryRef;
1363
1364 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1365 unsafe {
1366 match self.nid {
1367 Some(nid) => {
1368 self.loc =
1370 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1371 if self.loc == -1 {
1372 return None;
1373 }
1374 }
1375 None => {
1376 self.loc += 1;
1378 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1379 return None;
1380 }
1381 }
1382 }
1383
1384 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1385 assert!(!entry.is_null());
1386
1387 Some(X509NameEntryRef::from_ptr(entry))
1388 }
1389 }
1390}
1391
1392foreign_type_and_impl_send_sync! {
1393 type CType = ffi::X509_NAME_ENTRY;
1394 fn drop = ffi::X509_NAME_ENTRY_free;
1395
1396 pub struct X509NameEntry;
1398}
1399
1400impl X509NameEntryRef {
1401 #[corresponds(X509_NAME_ENTRY_get_data)]
1403 #[must_use]
1404 pub fn data(&self) -> &Asn1StringRef {
1405 unsafe {
1406 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1407 Asn1StringRef::from_ptr(data)
1408 }
1409 }
1410
1411 #[corresponds(X509_NAME_ENTRY_get_object)]
1414 #[must_use]
1415 pub fn object(&self) -> &Asn1ObjectRef {
1416 unsafe {
1417 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1418 Asn1ObjectRef::from_ptr(object)
1419 }
1420 }
1421}
1422
1423impl fmt::Debug for X509NameEntryRef {
1424 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1425 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1426 }
1427}
1428
1429pub struct X509ReqBuilder(X509Req);
1431
1432impl X509ReqBuilder {
1433 #[corresponds(X509_REQ_new)]
1435 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1436 unsafe {
1437 ffi::init();
1438 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req::from_ptr(p)))
1439 }
1440 }
1441
1442 #[corresponds(X509_REQ_set_version)]
1444 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1445 unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version.into())) }
1446 }
1447
1448 #[corresponds(X509_REQ_set_subject_name)]
1450 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1451 unsafe {
1452 cvt(ffi::X509_REQ_set_subject_name(
1453 self.0.as_ptr(),
1454 subject_name.as_ptr(),
1455 ))
1456 }
1457 }
1458
1459 #[corresponds(X509_REQ_set_pubkey)]
1461 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1462 where
1463 T: HasPublic,
1464 {
1465 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())) }
1466 }
1467
1468 #[must_use]
1471 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1472 unsafe {
1473 let mut ctx = mem::zeroed();
1474
1475 ffi::X509V3_set_ctx(
1476 &mut ctx,
1477 ptr::null_mut(),
1478 ptr::null_mut(),
1479 self.0.as_ptr(),
1480 ptr::null_mut(),
1481 0,
1482 );
1483
1484 if let Some(conf) = conf {
1486 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1487 }
1488
1489 X509v3Context(ctx, PhantomData)
1490 }
1491 }
1492
1493 pub fn add_extensions(
1495 &mut self,
1496 extensions: &StackRef<X509Extension>,
1497 ) -> Result<(), ErrorStack> {
1498 unsafe {
1499 cvt(ffi::X509_REQ_add_extensions(
1500 self.0.as_ptr(),
1501 extensions.as_ptr(),
1502 ))
1503 }
1504 }
1505
1506 #[corresponds(X509_REQ_sign)]
1508 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1509 where
1510 T: HasPrivate,
1511 {
1512 unsafe {
1513 cvt(ffi::X509_REQ_sign(
1514 self.0.as_ptr(),
1515 key.as_ptr(),
1516 hash.as_ptr(),
1517 ))
1518 }
1519 }
1520
1521 #[must_use]
1523 pub fn build(self) -> X509Req {
1524 self.0
1525 }
1526}
1527
1528foreign_type_and_impl_send_sync! {
1529 type CType = ffi::X509_REQ;
1530 fn drop = ffi::X509_REQ_free;
1531
1532 pub struct X509Req;
1534}
1535
1536impl X509Req {
1537 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1539 X509ReqBuilder::new()
1540 }
1541
1542 from_pem! {
1543 #[corresponds(PEM_read_bio_X509_REQ)]
1547 from_pem,
1548 X509Req,
1549 ffi::PEM_read_bio_X509_REQ
1550 }
1551
1552 from_der! {
1553 #[corresponds(d2i_X509_REQ)]
1555 from_der,
1556 X509Req,
1557 ffi::d2i_X509_REQ,
1558 crate::libc_types::c_long
1559 }
1560}
1561
1562impl X509ReqRef {
1563 to_pem! {
1564 #[corresponds(PEM_write_bio_X509_REQ)]
1568 to_pem,
1569 ffi::PEM_write_bio_X509_REQ
1570 }
1571
1572 to_der! {
1573 #[corresponds(i2d_X509_REQ)]
1575 to_der,
1576 ffi::i2d_X509_REQ
1577 }
1578
1579 #[corresponds(X509_REQ_get_version)]
1581 #[must_use]
1582 pub fn version(&self) -> i32 {
1583 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1584 }
1585
1586 #[corresponds(X509_REQ_get_subject_name)]
1588 #[must_use]
1589 pub fn subject_name(&self) -> &X509NameRef {
1590 unsafe {
1591 let name = X509_REQ_get_subject_name(self.as_ptr());
1592 assert!(!name.is_null());
1593 X509NameRef::from_ptr(name)
1594 }
1595 }
1596
1597 #[corresponds(X509_REQ_get_pubkey)]
1599 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1600 unsafe {
1601 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1602 Ok(PKey::from_ptr(key))
1603 }
1604 }
1605
1606 #[corresponds(X509_REQ_verify)]
1610 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1611 where
1612 T: HasPublic,
1613 {
1614 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1615 }
1616
1617 #[corresponds(X509_REQ_get_extensions)]
1619 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1620 unsafe {
1621 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1622 Ok(Stack::from_ptr(extensions))
1623 }
1624 }
1625}
1626
1627pub type X509VerifyResult = Result<(), X509VerifyError>;
1629
1630#[derive(Copy, Clone, PartialEq, Eq)]
1631pub struct X509VerifyError(c_int);
1632
1633impl fmt::Debug for X509VerifyError {
1634 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1635 fmt.debug_struct("X509VerifyError")
1636 .field("code", &self.0)
1637 .field("error", &self.error_string())
1638 .finish()
1639 }
1640}
1641
1642impl fmt::Display for X509VerifyError {
1643 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1644 fmt.write_str(self.error_string())
1645 }
1646}
1647
1648impl Error for X509VerifyError {}
1649
1650impl X509VerifyError {
1651 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
1658 if err == ffi::X509_V_OK {
1659 Ok(())
1660 } else {
1661 Err(X509VerifyError(err))
1662 }
1663 }
1664
1665 #[allow(clippy::trivially_copy_pass_by_ref)]
1667 #[must_use]
1668 pub fn as_raw(&self) -> c_int {
1669 self.0
1670 }
1671
1672 #[corresponds(X509_verify_cert_error_string)]
1676 #[allow(clippy::trivially_copy_pass_by_ref)]
1677 #[must_use]
1678 pub fn error_string(&self) -> &'static str {
1679 ffi::init();
1680
1681 unsafe {
1682 let s = ffi::X509_verify_cert_error_string(c_long::from(self.0));
1683 CStr::from_ptr(s).to_str().unwrap_or_default()
1684 }
1685 }
1686}
1687
1688#[allow(missing_docs)] impl X509VerifyError {
1690 pub const UNSPECIFIED: Self = Self(ffi::X509_V_ERR_UNSPECIFIED);
1691 pub const UNABLE_TO_GET_ISSUER_CERT: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT);
1692 pub const UNABLE_TO_GET_CRL: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL);
1693 pub const UNABLE_TO_DECRYPT_CERT_SIGNATURE: Self =
1694 Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
1695 pub const UNABLE_TO_DECRYPT_CRL_SIGNATURE: Self =
1696 Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE);
1697 pub const UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: Self =
1698 Self(ffi::X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
1699 pub const CERT_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CERT_SIGNATURE_FAILURE);
1700 pub const CRL_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CRL_SIGNATURE_FAILURE);
1701 pub const CERT_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CERT_NOT_YET_VALID);
1702 pub const CERT_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CERT_HAS_EXPIRED);
1703 pub const CRL_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CRL_NOT_YET_VALID);
1704 pub const CRL_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CRL_HAS_EXPIRED);
1705 pub const ERROR_IN_CERT_NOT_BEFORE_FIELD: Self =
1706 Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
1707 pub const ERROR_IN_CERT_NOT_AFTER_FIELD: Self =
1708 Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
1709 pub const ERROR_IN_CRL_LAST_UPDATE_FIELD: Self =
1710 Self(ffi::X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD);
1711 pub const ERROR_IN_CRL_NEXT_UPDATE_FIELD: Self =
1712 Self(ffi::X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
1713 pub const OUT_OF_MEM: Self = Self(ffi::X509_V_ERR_OUT_OF_MEM);
1714 pub const DEPTH_ZERO_SELF_SIGNED_CERT: Self = Self(ffi::X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
1715 pub const SELF_SIGNED_CERT_IN_CHAIN: Self = Self(ffi::X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
1716 pub const UNABLE_TO_GET_ISSUER_CERT_LOCALLY: Self =
1717 Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
1718 pub const UNABLE_TO_VERIFY_LEAF_SIGNATURE: Self =
1719 Self(ffi::X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
1720 pub const CERT_CHAIN_TOO_LONG: Self = Self(ffi::X509_V_ERR_CERT_CHAIN_TOO_LONG);
1721 pub const CERT_REVOKED: Self = Self(ffi::X509_V_ERR_CERT_REVOKED);
1722 pub const INVALID_CA: Self = Self(ffi::X509_V_ERR_INVALID_CA);
1723 pub const PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PATH_LENGTH_EXCEEDED);
1724 pub const INVALID_PURPOSE: Self = Self(ffi::X509_V_ERR_INVALID_PURPOSE);
1725 pub const CERT_UNTRUSTED: Self = Self(ffi::X509_V_ERR_CERT_UNTRUSTED);
1726 pub const CERT_REJECTED: Self = Self(ffi::X509_V_ERR_CERT_REJECTED);
1727 pub const SUBJECT_ISSUER_MISMATCH: Self = Self(ffi::X509_V_ERR_SUBJECT_ISSUER_MISMATCH);
1728 pub const AKID_SKID_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_SKID_MISMATCH);
1729 pub const AKID_ISSUER_SERIAL_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
1730 pub const KEYUSAGE_NO_CERTSIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CERTSIGN);
1731 pub const UNABLE_TO_GET_CRL_ISSUER: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER);
1732 pub const UNHANDLED_CRITICAL_EXTENSION: Self =
1733 Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION);
1734 pub const KEYUSAGE_NO_CRL_SIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CRL_SIGN);
1735 pub const UNHANDLED_CRITICAL_CRL_EXTENSION: Self =
1736 Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION);
1737 pub const INVALID_NON_CA: Self = Self(ffi::X509_V_ERR_INVALID_NON_CA);
1738 pub const PROXY_PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED);
1739 pub const KEYUSAGE_NO_DIGITAL_SIGNATURE: Self =
1740 Self(ffi::X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE);
1741 pub const PROXY_CERTIFICATES_NOT_ALLOWED: Self =
1742 Self(ffi::X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED);
1743 pub const INVALID_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_EXTENSION);
1744 pub const INVALID_POLICY_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_POLICY_EXTENSION);
1745 pub const NO_EXPLICIT_POLICY: Self = Self(ffi::X509_V_ERR_NO_EXPLICIT_POLICY);
1746 pub const DIFFERENT_CRL_SCOPE: Self = Self(ffi::X509_V_ERR_DIFFERENT_CRL_SCOPE);
1747 pub const UNSUPPORTED_EXTENSION_FEATURE: Self =
1748 Self(ffi::X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
1749 pub const UNNESTED_RESOURCE: Self = Self(ffi::X509_V_ERR_UNNESTED_RESOURCE);
1750 pub const PERMITTED_VIOLATION: Self = Self(ffi::X509_V_ERR_PERMITTED_VIOLATION);
1751 pub const EXCLUDED_VIOLATION: Self = Self(ffi::X509_V_ERR_EXCLUDED_VIOLATION);
1752 pub const SUBTREE_MINMAX: Self = Self(ffi::X509_V_ERR_SUBTREE_MINMAX);
1753 pub const APPLICATION_VERIFICATION: Self = Self(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
1754 pub const UNSUPPORTED_CONSTRAINT_TYPE: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
1755 pub const UNSUPPORTED_CONSTRAINT_SYNTAX: Self =
1756 Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
1757 pub const UNSUPPORTED_NAME_SYNTAX: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_NAME_SYNTAX);
1758 pub const CRL_PATH_VALIDATION_ERROR: Self = Self(ffi::X509_V_ERR_CRL_PATH_VALIDATION_ERROR);
1759 pub const HOSTNAME_MISMATCH: Self = Self(ffi::X509_V_ERR_HOSTNAME_MISMATCH);
1760 pub const EMAIL_MISMATCH: Self = Self(ffi::X509_V_ERR_EMAIL_MISMATCH);
1761 pub const IP_ADDRESS_MISMATCH: Self = Self(ffi::X509_V_ERR_IP_ADDRESS_MISMATCH);
1762 pub const INVALID_CALL: Self = Self(ffi::X509_V_ERR_INVALID_CALL);
1763 pub const STORE_LOOKUP: Self = Self(ffi::X509_V_ERR_STORE_LOOKUP);
1764 pub const NAME_CONSTRAINTS_WITHOUT_SANS: Self =
1765 Self(ffi::X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS);
1766}
1767
1768foreign_type_and_impl_send_sync! {
1769 type CType = ffi::GENERAL_NAME;
1770 fn drop = ffi::GENERAL_NAME_free;
1771
1772 pub struct GeneralName;
1774}
1775
1776impl GeneralName {
1777 unsafe fn new(
1778 type_: c_int,
1779 asn1_type: Asn1Type,
1780 value: &[u8],
1781 ) -> Result<GeneralName, ErrorStack> {
1782 ffi::init();
1783 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
1784 (*gn.as_ptr()).type_ = type_;
1785 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
1786 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
1787
1788 (*gn.as_ptr()).d.ptr = s.cast();
1789
1790 Ok(gn)
1791 }
1792
1793 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
1794 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
1795 }
1796
1797 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
1798 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
1799 }
1800
1801 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
1802 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
1803 }
1804
1805 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
1806 match ip {
1807 IpAddr::V4(addr) => unsafe {
1808 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
1809 },
1810 IpAddr::V6(addr) => unsafe {
1811 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
1812 },
1813 }
1814 }
1815
1816 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
1817 unsafe {
1818 ffi::init();
1819 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
1820 (*gn).type_ = ffi::GEN_RID;
1821 (*gn).d.registeredID = oid.into_ptr();
1822
1823 Ok(GeneralName::from_ptr(gn))
1824 }
1825 }
1826}
1827
1828impl GeneralNameRef {
1829 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
1830 unsafe {
1831 if (*self.as_ptr()).type_ != ffi_type {
1832 return None;
1833 }
1834
1835 let asn = Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ia5);
1836
1837 asn.to_str()
1841 }
1842 }
1843
1844 #[must_use]
1846 pub fn email(&self) -> Option<&str> {
1847 self.ia5_string(ffi::GEN_EMAIL)
1848 }
1849
1850 #[must_use]
1852 pub fn dnsname(&self) -> Option<&str> {
1853 self.ia5_string(ffi::GEN_DNS)
1854 }
1855
1856 #[must_use]
1858 pub fn uri(&self) -> Option<&str> {
1859 self.ia5_string(ffi::GEN_URI)
1860 }
1861
1862 #[must_use]
1864 pub fn ipaddress(&self) -> Option<&[u8]> {
1865 unsafe {
1866 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
1867 return None;
1868 }
1869
1870 Some(Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ip).as_slice())
1871 }
1872 }
1873}
1874
1875impl fmt::Debug for GeneralNameRef {
1876 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1877 if let Some(email) = self.email() {
1878 formatter.write_str(email)
1879 } else if let Some(dnsname) = self.dnsname() {
1880 formatter.write_str(dnsname)
1881 } else if let Some(uri) = self.uri() {
1882 formatter.write_str(uri)
1883 } else if let Some(ipaddress) = self.ipaddress() {
1884 let result = String::from_utf8_lossy(ipaddress);
1885 formatter.write_str(&result)
1886 } else {
1887 formatter.write_str("(empty)")
1888 }
1889 }
1890}
1891
1892impl Stackable for GeneralName {
1893 type StackType = ffi::stack_st_GENERAL_NAME;
1894}
1895
1896foreign_type_and_impl_send_sync! {
1897 type CType = ffi::X509_ALGOR;
1898 fn drop = ffi::X509_ALGOR_free;
1899
1900 pub struct X509Algorithm;
1902}
1903
1904impl X509AlgorithmRef {
1905 #[must_use]
1907 pub fn object(&self) -> &Asn1ObjectRef {
1908 unsafe {
1909 let mut oid = ptr::null();
1910 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
1911 assert!(!oid.is_null());
1912 Asn1ObjectRef::from_ptr(oid.cast_mut())
1913 }
1914 }
1915}
1916
1917foreign_type_and_impl_send_sync! {
1918 type CType = ffi::X509_OBJECT;
1919 fn drop = X509_OBJECT_free;
1920
1921 pub struct X509Object;
1923}
1924
1925impl X509ObjectRef {
1926 #[must_use]
1927 pub fn x509(&self) -> Option<&X509Ref> {
1928 unsafe {
1929 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
1930 if ptr.is_null() {
1931 None
1932 } else {
1933 Some(X509Ref::from_ptr(ptr))
1934 }
1935 }
1936 }
1937}
1938
1939impl Stackable for X509Object {
1940 type StackType = ffi::stack_st_X509_OBJECT;
1941}
1942
1943use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
1944
1945use crate::ffi::{
1946 X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, X509_STORE_CTX_get0_chain,
1947 X509_set1_notAfter, X509_set1_notBefore,
1948};
1949
1950use crate::ffi::X509_OBJECT_get0_X509;
1951
1952#[allow(bad_style)]
1953unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
1954 ffi::X509_OBJECT_free_contents(x);
1955 ffi::OPENSSL_free(x.cast());
1956}
1957
1958unsafe fn get_new_x509_store_ctx_idx(f: ffi::CRYPTO_EX_free) -> c_int {
1959 ffi::X509_STORE_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f)
1960}