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