1use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
11use libc::{c_int, c_long, c_uint, c_void};
12use std::cmp::{self, Ordering};
13use std::convert::{TryFrom, TryInto};
14use std::error::Error;
15use std::ffi::{CStr, CString};
16use std::fmt;
17use std::marker::PhantomData;
18use std::mem;
19use std::net::IpAddr;
20use std::path::Path;
21use std::ptr;
22use std::str;
23
24use crate::asn1::{
25 Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
26 Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
27};
28use crate::bio::MemBioSlice;
29use crate::conf::ConfRef;
30use crate::error::ErrorStack;
31use crate::ex_data::Index;
32use crate::hash::{DigestBytes, MessageDigest};
33use crate::nid::Nid;
34use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
35use crate::ssl::SslRef;
36use crate::stack::{Stack, StackRef, Stackable};
37use crate::string::OpensslString;
38use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
39use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
40use openssl_macros::corresponds;
41
42pub mod verify;
43
44pub mod extension;
45pub mod store;
46
47#[cfg(test)]
48mod tests;
49
50pub unsafe trait ExtensionType {
56 const NID: Nid;
57 type Output: ForeignType;
58}
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 pub struct X509StoreContextRef;
69}
70
71impl X509StoreContext {
72 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
75 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
76 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
77 }
78
79 #[corresponds(X509_STORE_CTX_new)]
81 pub fn new() -> Result<X509StoreContext, ErrorStack> {
82 unsafe {
83 ffi::init();
84 cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
85 }
86 }
87}
88
89impl X509StoreContextRef {
90 #[corresponds(X509_STORE_CTX_get_ex_data)]
92 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
93 unsafe {
94 let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
95 if data.is_null() {
96 None
97 } else {
98 Some(&*(data as *const T))
99 }
100 }
101 }
102
103 #[corresponds(X509_STORE_CTX_get_error)]
105 pub fn error(&self) -> X509VerifyResult {
106 unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
107 }
108
109 pub fn init<F, T>(
125 &mut self,
126 trust: &store::X509StoreRef,
127 cert: &X509Ref,
128 cert_chain: &StackRef<X509>,
129 with_context: F,
130 ) -> Result<T, ErrorStack>
131 where
132 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
133 {
134 struct Cleanup<'a>(&'a mut X509StoreContextRef);
135
136 impl Drop for Cleanup<'_> {
137 fn drop(&mut self) {
138 unsafe {
139 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
140 }
141 }
142 }
143
144 unsafe {
145 cvt(ffi::X509_STORE_CTX_init(
146 self.as_ptr(),
147 trust.as_ptr(),
148 cert.as_ptr(),
149 cert_chain.as_ptr(),
150 ))?;
151
152 let cleanup = Cleanup(self);
153 with_context(cleanup.0)
154 }
155 }
156
157 #[corresponds(X509_verify_cert)]
164 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
165 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
166 }
167
168 #[corresponds(X509_STORE_CTX_set_error)]
170 pub fn set_error(&mut self, result: X509VerifyResult) {
171 unsafe {
172 ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
173 }
174 }
175
176 #[corresponds(X509_STORE_CTX_get_current_cert)]
179 pub fn current_cert(&self) -> Option<&X509Ref> {
180 unsafe {
181 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
182 X509Ref::from_const_ptr_opt(ptr)
183 }
184 }
185
186 #[corresponds(X509_STORE_CTX_get_error_depth)]
191 pub fn error_depth(&self) -> u32 {
192 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
193 }
194
195 #[corresponds(X509_STORE_CTX_get0_chain)]
197 pub fn chain(&self) -> Option<&StackRef<X509>> {
198 unsafe {
199 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
200
201 if chain.is_null() {
202 None
203 } else {
204 Some(StackRef::from_ptr(chain))
205 }
206 }
207 }
208}
209
210pub struct X509Builder(X509);
212
213impl X509Builder {
214 #[corresponds(X509_new)]
216 pub fn new() -> Result<X509Builder, ErrorStack> {
217 unsafe {
218 ffi::init();
219 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
220 }
221 }
222
223 #[corresponds(X509_set1_notAfter)]
225 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
226 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
227 }
228
229 #[corresponds(X509_set1_notBefore)]
231 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
232 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
233 }
234
235 #[corresponds(X509_set_version)]
240 #[allow(clippy::useless_conversion)]
241 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
242 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
243 }
244
245 #[corresponds(X509_set_serialNumber)]
247 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
248 unsafe {
249 cvt(ffi::X509_set_serialNumber(
250 self.0.as_ptr(),
251 serial_number.as_ptr(),
252 ))
253 .map(|_| ())
254 }
255 }
256
257 #[corresponds(X509_set_issuer_name)]
259 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
260 unsafe {
261 cvt(ffi::X509_set_issuer_name(
262 self.0.as_ptr(),
263 issuer_name.as_ptr(),
264 ))
265 .map(|_| ())
266 }
267 }
268
269 #[corresponds(X509_set_subject_name)]
288 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
289 unsafe {
290 cvt(ffi::X509_set_subject_name(
291 self.0.as_ptr(),
292 subject_name.as_ptr(),
293 ))
294 .map(|_| ())
295 }
296 }
297
298 #[corresponds(X509_set_pubkey)]
300 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
301 where
302 T: HasPublic,
303 {
304 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
305 }
306
307 #[corresponds(X509V3_set_ctx)]
311 pub fn x509v3_context<'a>(
312 &'a self,
313 issuer: Option<&'a X509Ref>,
314 conf: Option<&'a ConfRef>,
315 ) -> X509v3Context<'a> {
316 unsafe {
317 let mut ctx = mem::zeroed();
318
319 let issuer = match issuer {
320 Some(issuer) => issuer.as_ptr(),
321 None => self.0.as_ptr(),
322 };
323 let subject = self.0.as_ptr();
324 ffi::X509V3_set_ctx(
325 &mut ctx,
326 issuer,
327 subject,
328 ptr::null_mut(),
329 ptr::null_mut(),
330 0,
331 );
332
333 if let Some(conf) = conf {
335 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
336 }
337
338 X509v3Context(ctx, PhantomData)
339 }
340 }
341
342 pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
346 self.append_extension2(&extension)
347 }
348
349 #[corresponds(X509_add_ext)]
351 pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
352 unsafe {
353 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
354 Ok(())
355 }
356 }
357
358 #[corresponds(X509_sign)]
360 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
361 where
362 T: HasPrivate,
363 {
364 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
365 }
366
367 pub fn build(self) -> X509 {
369 self.0
370 }
371}
372
373foreign_type_and_impl_send_sync! {
374 type CType = ffi::X509;
375 fn drop = ffi::X509_free;
376
377 pub struct X509;
379 pub struct X509Ref;
381}
382
383impl X509Ref {
384 #[corresponds(X509_get_subject_name)]
386 pub fn subject_name(&self) -> &X509NameRef {
387 unsafe {
388 let name = ffi::X509_get_subject_name(self.as_ptr());
389 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
390 }
391 }
392
393 #[corresponds(X509_subject_name_hash)]
395 pub fn subject_name_hash(&self) -> u32 {
396 #[allow(clippy::unnecessary_cast)]
397 unsafe {
398 ffi::X509_subject_name_hash(self.as_ptr()) as u32
399 }
400 }
401
402 #[corresponds(X509_get_issuer_name)]
404 pub fn issuer_name(&self) -> &X509NameRef {
405 unsafe {
406 let name = ffi::X509_get_issuer_name(self.as_ptr());
407 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
408 }
409 }
410
411 #[corresponds(X509_issuer_name_hash)]
413 pub fn issuer_name_hash(&self) -> u32 {
414 #[allow(clippy::unnecessary_cast)]
415 unsafe {
416 ffi::X509_issuer_name_hash(self.as_ptr()) as u32
417 }
418 }
419
420 #[corresponds(X509_get_ext_d2i)]
422 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
423 unsafe {
424 let stack = ffi::X509_get_ext_d2i(
425 self.as_ptr(),
426 ffi::NID_subject_alt_name,
427 ptr::null_mut(),
428 ptr::null_mut(),
429 );
430 Stack::from_ptr_opt(stack as *mut _)
431 }
432 }
433
434 #[corresponds(X509_get_ext_d2i)]
436 pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
437 unsafe {
438 let stack = ffi::X509_get_ext_d2i(
439 self.as_ptr(),
440 ffi::NID_crl_distribution_points,
441 ptr::null_mut(),
442 ptr::null_mut(),
443 );
444 Stack::from_ptr_opt(stack as *mut _)
445 }
446 }
447
448 #[corresponds(X509_get_ext_d2i)]
450 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
451 unsafe {
452 let stack = ffi::X509_get_ext_d2i(
453 self.as_ptr(),
454 ffi::NID_issuer_alt_name,
455 ptr::null_mut(),
456 ptr::null_mut(),
457 );
458 Stack::from_ptr_opt(stack as *mut _)
459 }
460 }
461
462 #[corresponds(X509_get_ext_d2i)]
466 pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
467 unsafe {
468 let stack = ffi::X509_get_ext_d2i(
469 self.as_ptr(),
470 ffi::NID_info_access,
471 ptr::null_mut(),
472 ptr::null_mut(),
473 );
474 Stack::from_ptr_opt(stack as *mut _)
475 }
476 }
477
478 #[corresponds(X509_get_pathlen)]
480 #[cfg(any(ossl110, boringssl, awslc))]
481 pub fn pathlen(&self) -> Option<u32> {
482 let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
483 u32::try_from(v).ok()
484 }
485
486 #[corresponds(X509_get0_subject_key_id)]
488 #[cfg(any(ossl110, boringssl, awslc))]
489 pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
490 unsafe {
491 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
492 Asn1OctetStringRef::from_const_ptr_opt(data)
493 }
494 }
495
496 #[corresponds(X509_get0_authority_key_id)]
498 #[cfg(any(ossl110, boringssl, awslc))]
499 pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
500 unsafe {
501 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
502 Asn1OctetStringRef::from_const_ptr_opt(data)
503 }
504 }
505
506 #[corresponds(X509_get0_authority_issuer)]
508 #[cfg(ossl111d)]
509 pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
510 unsafe {
511 let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
512 StackRef::from_const_ptr_opt(stack)
513 }
514 }
515
516 #[corresponds(X509_get0_authority_serial)]
518 #[cfg(ossl111d)]
519 pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
520 unsafe {
521 let r = ffi::X509_get0_authority_serial(self.as_ptr());
522 Asn1IntegerRef::from_const_ptr_opt(r)
523 }
524 }
525
526 #[corresponds(X509_get_pubkey)]
527 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
528 unsafe {
529 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
530 Ok(PKey::from_ptr(pkey))
531 }
532 }
533
534 #[corresponds(X509_digest)]
536 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
537 unsafe {
538 let mut digest = DigestBytes {
539 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
540 len: ffi::EVP_MAX_MD_SIZE as usize,
541 };
542 let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
543 cvt(ffi::X509_digest(
544 self.as_ptr(),
545 hash_type.as_ptr(),
546 digest.buf.as_mut_ptr() as *mut _,
547 &mut len,
548 ))?;
549 digest.len = len as usize;
550
551 Ok(digest)
552 }
553 }
554
555 #[deprecated(since = "0.10.9", note = "renamed to digest")]
556 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
557 self.digest(hash_type).map(|b| b.to_vec())
558 }
559
560 #[corresponds(X509_getm_notAfter)]
562 pub fn not_after(&self) -> &Asn1TimeRef {
563 unsafe {
564 let date = X509_getm_notAfter(self.as_ptr());
565 Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
566 }
567 }
568
569 #[corresponds(X509_getm_notBefore)]
571 pub fn not_before(&self) -> &Asn1TimeRef {
572 unsafe {
573 let date = X509_getm_notBefore(self.as_ptr());
574 Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
575 }
576 }
577
578 #[corresponds(X509_get0_signature)]
580 pub fn signature(&self) -> &Asn1BitStringRef {
581 unsafe {
582 let mut signature = ptr::null();
583 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
584 Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
585 }
586 }
587
588 #[corresponds(X509_get0_signature)]
590 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
591 unsafe {
592 let mut algor = ptr::null();
593 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
594 X509AlgorithmRef::from_const_ptr_opt(algor)
595 .expect("signature algorithm must not be null")
596 }
597 }
598
599 #[corresponds(X509_get1_ocsp)]
605 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
606 unsafe {
607 let stack: Stack<OpensslString> =
608 cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))?;
609 for entry in &stack {
610 let bytes = CStr::from_ptr(entry.as_ptr()).to_bytes();
611 if str::from_utf8(bytes).is_err() {
612 return Err(ErrorStack::internal_error(
613 "OCSP responder URL contained invalid UTF-8",
614 ));
615 }
616 }
617 Ok(stack)
618 }
619 }
620
621 #[corresponds(X509_check_issued)]
623 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
624 unsafe {
625 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
626 X509VerifyResult::from_raw(r)
627 }
628 }
629
630 #[corresponds(X509_get_version)]
635 #[cfg(ossl110)]
636 #[allow(clippy::unnecessary_cast)]
637 pub fn version(&self) -> i32 {
638 unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
639 }
640
641 #[corresponds(X509_verify)]
648 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
649 where
650 T: HasPublic,
651 {
652 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
653 }
654
655 #[corresponds(X509_get_serialNumber)]
657 pub fn serial_number(&self) -> &Asn1IntegerRef {
658 unsafe {
659 let r = ffi::X509_get_serialNumber(self.as_ptr());
660 Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
661 }
662 }
663
664 #[corresponds(X509_alias_get0)]
670 pub fn alias(&self) -> Option<&[u8]> {
671 unsafe {
672 let mut len = 0;
673 let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
674 if ptr.is_null() {
675 None
676 } else {
677 Some(util::from_raw_parts(ptr, len as usize))
678 }
679 }
680 }
681
682 to_pem! {
683 #[corresponds(PEM_write_bio_X509)]
687 to_pem,
688 ffi::PEM_write_bio_X509
689 }
690
691 to_der! {
692 #[corresponds(i2d_X509)]
694 to_der,
695 ffi::i2d_X509
696 }
697
698 to_pem! {
699 #[corresponds(X509_print)]
701 to_text,
702 ffi::X509_print
703 }
704}
705
706impl ToOwned for X509Ref {
707 type Owned = X509;
708
709 fn to_owned(&self) -> X509 {
710 unsafe {
711 X509_up_ref(self.as_ptr());
712 X509::from_ptr(self.as_ptr())
713 }
714 }
715}
716
717impl Ord for X509Ref {
718 fn cmp(&self, other: &Self) -> cmp::Ordering {
719 let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
722 cmp.cmp(&0)
723 }
724}
725
726impl PartialOrd for X509Ref {
727 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
728 Some(self.cmp(other))
729 }
730}
731
732impl PartialOrd<X509> for X509Ref {
733 fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
734 <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
735 }
736}
737
738impl PartialEq for X509Ref {
739 fn eq(&self, other: &Self) -> bool {
740 self.cmp(other) == cmp::Ordering::Equal
741 }
742}
743
744impl PartialEq<X509> for X509Ref {
745 fn eq(&self, other: &X509) -> bool {
746 <X509Ref as PartialEq<X509Ref>>::eq(self, other)
747 }
748}
749
750impl Eq for X509Ref {}
751
752impl X509 {
753 pub fn builder() -> Result<X509Builder, ErrorStack> {
755 X509Builder::new()
756 }
757
758 from_pem! {
759 #[corresponds(PEM_read_bio_X509)]
763 from_pem,
764 X509,
765 ffi::PEM_read_bio_X509
766 }
767
768 from_der! {
769 #[corresponds(d2i_X509)]
771 from_der,
772 X509,
773 ffi::d2i_X509
774 }
775
776 #[corresponds(PEM_read_bio_X509)]
778 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
779 unsafe {
780 ffi::init();
781 let bio = MemBioSlice::new(pem)?;
782
783 let mut certs = vec![];
784 loop {
785 let r =
786 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
787 if r.is_null() {
788 let e = ErrorStack::get();
789
790 if let Some(err) = e.errors().last() {
791 if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
792 && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
793 {
794 break;
795 }
796 }
797
798 return Err(e);
799 } else {
800 certs.push(X509(r));
801 }
802 }
803
804 Ok(certs)
805 }
806 }
807}
808
809impl Clone for X509 {
810 fn clone(&self) -> X509 {
811 X509Ref::to_owned(self)
812 }
813}
814
815impl fmt::Debug for X509 {
816 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
817 let serial = match &self.serial_number().to_bn() {
818 Ok(bn) => match bn.to_hex_str() {
819 Ok(hex) => hex.to_string(),
820 Err(_) => "".to_string(),
821 },
822 Err(_) => "".to_string(),
823 };
824 let mut debug_struct = formatter.debug_struct("X509");
825 debug_struct.field("serial_number", &serial);
826 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
827 debug_struct.field("issuer", &self.issuer_name());
828 debug_struct.field("subject", &self.subject_name());
829 if let Some(subject_alt_names) = &self.subject_alt_names() {
830 debug_struct.field("subject_alt_names", subject_alt_names);
831 }
832 debug_struct.field("not_before", &self.not_before());
833 debug_struct.field("not_after", &self.not_after());
834
835 if let Ok(public_key) = &self.public_key() {
836 debug_struct.field("public_key", public_key);
837 };
838 debug_struct.finish()
841 }
842}
843
844impl AsRef<X509Ref> for X509Ref {
845 fn as_ref(&self) -> &X509Ref {
846 self
847 }
848}
849
850impl Stackable for X509 {
851 type StackType = ffi::stack_st_X509;
852}
853
854impl Ord for X509 {
855 fn cmp(&self, other: &Self) -> cmp::Ordering {
856 X509Ref::cmp(self, other)
857 }
858}
859
860impl PartialOrd for X509 {
861 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
862 Some(self.cmp(other))
863 }
864}
865
866impl PartialOrd<X509Ref> for X509 {
867 fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
868 X509Ref::partial_cmp(self, other)
869 }
870}
871
872impl PartialEq for X509 {
873 fn eq(&self, other: &Self) -> bool {
874 X509Ref::eq(self, other)
875 }
876}
877
878impl PartialEq<X509Ref> for X509 {
879 fn eq(&self, other: &X509Ref) -> bool {
880 X509Ref::eq(self, other)
881 }
882}
883
884impl Eq for X509 {}
885
886pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
888
889impl X509v3Context<'_> {
890 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
891 &self.0 as *const _ as *mut _
892 }
893}
894
895foreign_type_and_impl_send_sync! {
896 type CType = ffi::X509_EXTENSION;
897 fn drop = ffi::X509_EXTENSION_free;
898
899 pub struct X509Extension;
901 pub struct X509ExtensionRef;
903}
904
905impl Stackable for X509Extension {
906 type StackType = ffi::stack_st_X509_EXTENSION;
907}
908
909impl X509Extension {
910 #[deprecated(
924 note = "Use x509::extension types or new_from_der instead",
925 since = "0.10.51"
926 )]
927 pub fn new(
928 conf: Option<&ConfRef>,
929 context: Option<&X509v3Context<'_>>,
930 name: &str,
931 value: &str,
932 ) -> Result<X509Extension, ErrorStack> {
933 let name = CString::new(name).unwrap();
934 let value = CString::new(value).unwrap();
935 let mut ctx;
936 unsafe {
937 ffi::init();
938 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
939 let context_ptr = match context {
940 Some(c) => c.as_ptr(),
941 None => {
942 ctx = mem::zeroed();
943
944 ffi::X509V3_set_ctx(
945 &mut ctx,
946 ptr::null_mut(),
947 ptr::null_mut(),
948 ptr::null_mut(),
949 ptr::null_mut(),
950 0,
951 );
952 &mut ctx
953 }
954 };
955 let name = name.as_ptr() as *mut _;
956 let value = value.as_ptr() as *mut _;
957
958 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
959 }
960 }
961
962 #[deprecated(
976 note = "Use x509::extension types or new_from_der instead",
977 since = "0.10.51"
978 )]
979 pub fn new_nid(
980 conf: Option<&ConfRef>,
981 context: Option<&X509v3Context<'_>>,
982 name: Nid,
983 value: &str,
984 ) -> Result<X509Extension, ErrorStack> {
985 let value = CString::new(value).unwrap();
986 let mut ctx;
987 unsafe {
988 ffi::init();
989 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
990 let context_ptr = match context {
991 Some(c) => c.as_ptr(),
992 None => {
993 ctx = mem::zeroed();
994
995 ffi::X509V3_set_ctx(
996 &mut ctx,
997 ptr::null_mut(),
998 ptr::null_mut(),
999 ptr::null_mut(),
1000 ptr::null_mut(),
1001 0,
1002 );
1003 &mut ctx
1004 }
1005 };
1006 let name = name.as_raw();
1007 let value = value.as_ptr() as *mut _;
1008
1009 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
1010 }
1011 }
1012
1013 pub fn new_from_der(
1023 oid: &Asn1ObjectRef,
1024 critical: bool,
1025 der_contents: &Asn1OctetStringRef,
1026 ) -> Result<X509Extension, ErrorStack> {
1027 unsafe {
1028 cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1029 ptr::null_mut(),
1030 oid.as_ptr(),
1031 critical as _,
1032 der_contents.as_ptr(),
1033 ))
1034 .map(X509Extension)
1035 }
1036 }
1037
1038 pub(crate) unsafe fn new_internal(
1039 nid: Nid,
1040 critical: bool,
1041 value: *mut c_void,
1042 ) -> Result<X509Extension, ErrorStack> {
1043 ffi::init();
1044 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1045 }
1046
1047 #[cfg(not(libressl390))]
1053 #[corresponds(X509V3_EXT_add_alias)]
1054 #[deprecated(
1055 note = "Use x509::extension types or new_from_der and then this is not necessary",
1056 since = "0.10.51"
1057 )]
1058 pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1059 ffi::init();
1060 cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1061 }
1062}
1063
1064impl X509ExtensionRef {
1065 to_der! {
1066 #[corresponds(i2d_X509_EXTENSION)]
1068 to_der,
1069 ffi::i2d_X509_EXTENSION
1070 }
1071}
1072
1073pub struct X509NameBuilder(X509Name);
1075
1076impl X509NameBuilder {
1077 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1079 unsafe {
1080 ffi::init();
1081 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1082 }
1083 }
1084
1085 #[corresponds(X509_NAME_add_entry)]
1087 pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1088 unsafe {
1089 cvt(ffi::X509_NAME_add_entry(
1090 self.0.as_ptr(),
1091 ne.as_ptr(),
1092 -1,
1093 0,
1094 ))
1095 .map(|_| ())
1096 }
1097 }
1098
1099 #[corresponds(X509_NAME_add_entry_by_txt)]
1101 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1102 unsafe {
1103 let field = CString::new(field).unwrap();
1104 assert!(value.len() <= crate::SLenType::MAX as usize);
1105 cvt(ffi::X509_NAME_add_entry_by_txt(
1106 self.0.as_ptr(),
1107 field.as_ptr() as *mut _,
1108 ffi::MBSTRING_UTF8,
1109 value.as_ptr(),
1110 value.len() as crate::SLenType,
1111 -1,
1112 0,
1113 ))
1114 .map(|_| ())
1115 }
1116 }
1117
1118 #[corresponds(X509_NAME_add_entry_by_txt)]
1120 pub fn append_entry_by_text_with_type(
1121 &mut self,
1122 field: &str,
1123 value: &str,
1124 ty: Asn1Type,
1125 ) -> Result<(), ErrorStack> {
1126 unsafe {
1127 let field = CString::new(field).unwrap();
1128 assert!(value.len() <= crate::SLenType::MAX as usize);
1129 cvt(ffi::X509_NAME_add_entry_by_txt(
1130 self.0.as_ptr(),
1131 field.as_ptr() as *mut _,
1132 ty.as_raw(),
1133 value.as_ptr(),
1134 value.len() as crate::SLenType,
1135 -1,
1136 0,
1137 ))
1138 .map(|_| ())
1139 }
1140 }
1141
1142 #[corresponds(X509_NAME_add_entry_by_NID)]
1144 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1145 unsafe {
1146 assert!(value.len() <= crate::SLenType::MAX as usize);
1147 cvt(ffi::X509_NAME_add_entry_by_NID(
1148 self.0.as_ptr(),
1149 field.as_raw(),
1150 ffi::MBSTRING_UTF8,
1151 value.as_ptr() as *mut _,
1152 value.len() as crate::SLenType,
1153 -1,
1154 0,
1155 ))
1156 .map(|_| ())
1157 }
1158 }
1159
1160 #[corresponds(X509_NAME_add_entry_by_NID)]
1162 pub fn append_entry_by_nid_with_type(
1163 &mut self,
1164 field: Nid,
1165 value: &str,
1166 ty: Asn1Type,
1167 ) -> Result<(), ErrorStack> {
1168 unsafe {
1169 assert!(value.len() <= crate::SLenType::MAX as usize);
1170 cvt(ffi::X509_NAME_add_entry_by_NID(
1171 self.0.as_ptr(),
1172 field.as_raw(),
1173 ty.as_raw(),
1174 value.as_ptr() as *mut _,
1175 value.len() as crate::SLenType,
1176 -1,
1177 0,
1178 ))
1179 .map(|_| ())
1180 }
1181 }
1182
1183 pub fn build(self) -> X509Name {
1185 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1189 }
1190}
1191
1192foreign_type_and_impl_send_sync! {
1193 type CType = ffi::X509_NAME;
1194 fn drop = ffi::X509_NAME_free;
1195
1196 pub struct X509Name;
1198 pub struct X509NameRef;
1200}
1201
1202impl X509Name {
1203 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1205 X509NameBuilder::new()
1206 }
1207
1208 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1212 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1213 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1214 }
1215
1216 from_der! {
1217 from_der,
1223 X509Name,
1224 ffi::d2i_X509_NAME
1225 }
1226}
1227
1228impl Stackable for X509Name {
1229 type StackType = ffi::stack_st_X509_NAME;
1230}
1231
1232impl X509NameRef {
1233 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1235 X509NameEntries {
1236 name: self,
1237 nid: Some(nid),
1238 loc: -1,
1239 }
1240 }
1241
1242 pub fn entries(&self) -> X509NameEntries<'_> {
1244 X509NameEntries {
1245 name: self,
1246 nid: None,
1247 loc: -1,
1248 }
1249 }
1250
1251 #[corresponds(X509_NAME_cmp)]
1258 pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1259 let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1260 if cfg!(ossl300) && cmp == -2 {
1261 return Err(ErrorStack::get());
1262 }
1263 Ok(cmp.cmp(&0))
1264 }
1265
1266 #[corresponds(X509_NAME_dup)]
1268 pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1269 unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1270 }
1271
1272 to_der! {
1273 to_der,
1279 ffi::i2d_X509_NAME
1280 }
1281}
1282
1283impl fmt::Debug for X509NameRef {
1284 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1285 formatter.debug_list().entries(self.entries()).finish()
1286 }
1287}
1288
1289pub struct X509NameEntries<'a> {
1291 name: &'a X509NameRef,
1292 nid: Option<Nid>,
1293 loc: c_int,
1294}
1295
1296impl<'a> Iterator for X509NameEntries<'a> {
1297 type Item = &'a X509NameEntryRef;
1298
1299 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1300 unsafe {
1301 match self.nid {
1302 Some(nid) => {
1303 self.loc =
1305 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1306 if self.loc == -1 {
1307 return None;
1308 }
1309 }
1310 None => {
1311 self.loc += 1;
1313 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1314 return None;
1315 }
1316 }
1317 }
1318
1319 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1320
1321 Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1322 }
1323 }
1324}
1325
1326foreign_type_and_impl_send_sync! {
1327 type CType = ffi::X509_NAME_ENTRY;
1328 fn drop = ffi::X509_NAME_ENTRY_free;
1329
1330 pub struct X509NameEntry;
1332 pub struct X509NameEntryRef;
1334}
1335
1336impl X509NameEntryRef {
1337 #[corresponds(X509_NAME_ENTRY_get_data)]
1339 pub fn data(&self) -> &Asn1StringRef {
1340 unsafe {
1341 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1342 Asn1StringRef::from_ptr(data as *mut _)
1343 }
1344 }
1345
1346 #[corresponds(X509_NAME_ENTRY_get_object)]
1349 pub fn object(&self) -> &Asn1ObjectRef {
1350 unsafe {
1351 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1352 Asn1ObjectRef::from_ptr(object as *mut _)
1353 }
1354 }
1355}
1356
1357impl fmt::Debug for X509NameEntryRef {
1358 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1359 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1360 }
1361}
1362
1363pub struct X509ReqBuilder(X509Req);
1365
1366impl X509ReqBuilder {
1367 #[corresponds(X509_REQ_new)]
1369 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1370 unsafe {
1371 ffi::init();
1372 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1373 }
1374 }
1375
1376 #[corresponds(X509_REQ_set_version)]
1378 #[allow(clippy::useless_conversion)]
1379 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1380 unsafe {
1381 cvt(ffi::X509_REQ_set_version(
1382 self.0.as_ptr(),
1383 version as c_long,
1384 ))
1385 .map(|_| ())
1386 }
1387 }
1388
1389 #[corresponds(X509_REQ_set_subject_name)]
1391 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1392 unsafe {
1393 cvt(ffi::X509_REQ_set_subject_name(
1394 self.0.as_ptr(),
1395 subject_name.as_ptr(),
1396 ))
1397 .map(|_| ())
1398 }
1399 }
1400
1401 #[corresponds(X509_REQ_set_pubkey)]
1403 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1404 where
1405 T: HasPublic,
1406 {
1407 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1408 }
1409
1410 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1413 unsafe {
1414 let mut ctx = mem::zeroed();
1415
1416 ffi::X509V3_set_ctx(
1417 &mut ctx,
1418 ptr::null_mut(),
1419 ptr::null_mut(),
1420 self.0.as_ptr(),
1421 ptr::null_mut(),
1422 0,
1423 );
1424
1425 if let Some(conf) = conf {
1427 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1428 }
1429
1430 X509v3Context(ctx, PhantomData)
1431 }
1432 }
1433
1434 pub fn add_extensions(
1436 &mut self,
1437 extensions: &StackRef<X509Extension>,
1438 ) -> Result<(), ErrorStack> {
1439 unsafe {
1440 cvt(ffi::X509_REQ_add_extensions(
1441 self.0.as_ptr(),
1442 extensions.as_ptr(),
1443 ))
1444 .map(|_| ())
1445 }
1446 }
1447
1448 #[corresponds(X509_REQ_sign)]
1450 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1451 where
1452 T: HasPrivate,
1453 {
1454 unsafe {
1455 cvt(ffi::X509_REQ_sign(
1456 self.0.as_ptr(),
1457 key.as_ptr(),
1458 hash.as_ptr(),
1459 ))
1460 .map(|_| ())
1461 }
1462 }
1463
1464 pub fn build(self) -> X509Req {
1466 self.0
1467 }
1468}
1469
1470foreign_type_and_impl_send_sync! {
1471 type CType = ffi::X509_REQ;
1472 fn drop = ffi::X509_REQ_free;
1473
1474 pub struct X509Req;
1476 pub struct X509ReqRef;
1478}
1479
1480impl X509Req {
1481 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1483 X509ReqBuilder::new()
1484 }
1485
1486 from_pem! {
1487 from_pem,
1495 X509Req,
1496 ffi::PEM_read_bio_X509_REQ
1497 }
1498
1499 from_der! {
1500 from_der,
1506 X509Req,
1507 ffi::d2i_X509_REQ
1508 }
1509}
1510
1511impl X509ReqRef {
1512 to_pem! {
1513 to_pem,
1521 ffi::PEM_write_bio_X509_REQ
1522 }
1523
1524 to_der! {
1525 to_der,
1531 ffi::i2d_X509_REQ
1532 }
1533
1534 to_pem! {
1535 #[corresponds(X509_Req_print)]
1537 to_text,
1538 ffi::X509_REQ_print
1539 }
1540
1541 #[corresponds(X509_REQ_get_version)]
1543 #[allow(clippy::unnecessary_cast)]
1544 pub fn version(&self) -> i32 {
1545 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1546 }
1547
1548 #[corresponds(X509_REQ_get_subject_name)]
1550 pub fn subject_name(&self) -> &X509NameRef {
1551 unsafe {
1552 let name = X509_REQ_get_subject_name(self.as_ptr());
1553 X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1554 }
1555 }
1556
1557 #[corresponds(X509_REQ_get_pubkey)]
1559 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1560 unsafe {
1561 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1562 Ok(PKey::from_ptr(key))
1563 }
1564 }
1565
1566 #[corresponds(X509_REQ_verify)]
1570 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1571 where
1572 T: HasPublic,
1573 {
1574 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1575 }
1576
1577 #[corresponds(X509_REQ_get_extensions)]
1579 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1580 unsafe {
1581 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1582 Ok(Stack::from_ptr(extensions))
1583 }
1584 }
1585}
1586
1587#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1589pub struct CrlReason(c_int);
1590
1591#[allow(missing_docs)] impl CrlReason {
1593 pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1594 pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1595 pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1596 pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1597 pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1598 pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1599 pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1600 pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1601 pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1602 pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1603
1604 pub const fn from_raw(value: c_int) -> Self {
1606 CrlReason(value)
1607 }
1608
1609 pub const fn as_raw(&self) -> c_int {
1611 self.0
1612 }
1613}
1614
1615foreign_type_and_impl_send_sync! {
1616 type CType = ffi::X509_REVOKED;
1617 fn drop = ffi::X509_REVOKED_free;
1618
1619 pub struct X509Revoked;
1621 pub struct X509RevokedRef;
1623}
1624
1625impl Stackable for X509Revoked {
1626 type StackType = ffi::stack_st_X509_REVOKED;
1627}
1628
1629impl X509Revoked {
1630 from_der! {
1631 #[corresponds(d2i_X509_REVOKED)]
1633 from_der,
1634 X509Revoked,
1635 ffi::d2i_X509_REVOKED
1636 }
1637}
1638
1639impl X509RevokedRef {
1640 to_der! {
1641 #[corresponds(d2i_X509_REVOKED)]
1643 to_der,
1644 ffi::i2d_X509_REVOKED
1645 }
1646
1647 #[corresponds(X509_REVOKED_dup)]
1649 pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1650 unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1651 }
1652
1653 #[corresponds(X509_REVOKED_get0_revocationDate)]
1655 pub fn revocation_date(&self) -> &Asn1TimeRef {
1656 unsafe {
1657 let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1658 assert!(!r.is_null());
1659 Asn1TimeRef::from_ptr(r as *mut _)
1660 }
1661 }
1662
1663 #[corresponds(X509_REVOKED_get0_serialNumber)]
1665 pub fn serial_number(&self) -> &Asn1IntegerRef {
1666 unsafe {
1667 let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1668 assert!(!r.is_null());
1669 Asn1IntegerRef::from_ptr(r as *mut _)
1670 }
1671 }
1672
1673 #[corresponds(X509_REVOKED_get_ext_d2i)]
1677 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1678 let mut critical = -1;
1679 let out = unsafe {
1680 let ext = ffi::X509_REVOKED_get_ext_d2i(
1682 self.as_ptr(),
1683 T::NID.as_raw(),
1684 &mut critical as *mut _,
1685 ptr::null_mut(),
1686 );
1687 T::Output::from_ptr_opt(ext as *mut _)
1690 };
1691 match (critical, out) {
1692 (0, Some(out)) => Ok(Some((false, out))),
1693 (1, Some(out)) => Ok(Some((true, out))),
1694 (-1 | -2, _) => Ok(None),
1696 (0 | 1, None) => Err(ErrorStack::get()),
1699 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1700 }
1701 }
1702}
1703
1704pub enum ReasonCode {}
1707
1708unsafe impl ExtensionType for ReasonCode {
1711 const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1712
1713 type Output = Asn1Enumerated;
1714}
1715
1716pub enum CertificateIssuer {}
1719
1720unsafe impl ExtensionType for CertificateIssuer {
1723 const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1724
1725 type Output = Stack<GeneralName>;
1726}
1727
1728pub enum AuthorityInformationAccess {}
1730
1731unsafe impl ExtensionType for AuthorityInformationAccess {
1734 const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1735
1736 type Output = Stack<AccessDescription>;
1737}
1738
1739foreign_type_and_impl_send_sync! {
1740 type CType = ffi::X509_CRL;
1741 fn drop = ffi::X509_CRL_free;
1742
1743 pub struct X509Crl;
1745 pub struct X509CrlRef;
1747}
1748
1749pub enum CrlStatus<'a> {
1755 NotRevoked,
1757 Revoked(&'a X509RevokedRef),
1759 RemoveFromCrl(&'a X509RevokedRef),
1764}
1765
1766impl<'a> CrlStatus<'a> {
1767 unsafe fn from_ffi_status(
1772 status: c_int,
1773 revoked_entry: *mut ffi::X509_REVOKED,
1774 ) -> CrlStatus<'a> {
1775 match status {
1776 0 => CrlStatus::NotRevoked,
1777 1 => {
1778 assert!(!revoked_entry.is_null());
1779 CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1780 }
1781 2 => {
1782 assert!(!revoked_entry.is_null());
1783 CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1784 }
1785 _ => unreachable!(
1786 "{}",
1787 "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1788 ),
1789 }
1790 }
1791}
1792
1793impl X509Crl {
1794 from_pem! {
1795 #[corresponds(PEM_read_bio_X509_CRL)]
1799 from_pem,
1800 X509Crl,
1801 ffi::PEM_read_bio_X509_CRL
1802 }
1803
1804 from_der! {
1805 #[corresponds(d2i_X509_CRL)]
1807 from_der,
1808 X509Crl,
1809 ffi::d2i_X509_CRL
1810 }
1811}
1812
1813impl X509CrlRef {
1814 to_pem! {
1815 #[corresponds(PEM_write_bio_X509_CRL)]
1819 to_pem,
1820 ffi::PEM_write_bio_X509_CRL
1821 }
1822
1823 to_der! {
1824 #[corresponds(i2d_X509_CRL)]
1826 to_der,
1827 ffi::i2d_X509_CRL
1828 }
1829
1830 pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
1832 unsafe {
1833 let revoked = X509_CRL_get_REVOKED(self.as_ptr());
1834 if revoked.is_null() {
1835 None
1836 } else {
1837 Some(StackRef::from_ptr(revoked))
1838 }
1839 }
1840 }
1841
1842 #[corresponds(X509_CRL_get0_lastUpdate)]
1844 pub fn last_update(&self) -> &Asn1TimeRef {
1845 unsafe {
1846 let date = X509_CRL_get0_lastUpdate(self.as_ptr());
1847 assert!(!date.is_null());
1848 Asn1TimeRef::from_ptr(date as *mut _)
1849 }
1850 }
1851
1852 #[corresponds(X509_CRL_get0_nextUpdate)]
1856 pub fn next_update(&self) -> Option<&Asn1TimeRef> {
1857 unsafe {
1858 let date = X509_CRL_get0_nextUpdate(self.as_ptr());
1859 Asn1TimeRef::from_const_ptr_opt(date)
1860 }
1861 }
1862
1863 #[corresponds(X509_CRL_get0_by_serial)]
1865 pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
1866 unsafe {
1867 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
1868 let status =
1869 ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
1870 CrlStatus::from_ffi_status(status, ret)
1871 }
1872 }
1873
1874 #[corresponds(X509_CRL_get0_by_cert)]
1876 pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
1877 unsafe {
1878 let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
1879 let status =
1880 ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
1881 CrlStatus::from_ffi_status(status, ret)
1882 }
1883 }
1884
1885 #[corresponds(X509_CRL_get_issuer)]
1887 pub fn issuer_name(&self) -> &X509NameRef {
1888 unsafe {
1889 let name = X509_CRL_get_issuer(self.as_ptr());
1890 assert!(!name.is_null());
1891 X509NameRef::from_ptr(name as *mut _)
1892 }
1893 }
1894
1895 #[corresponds(X509_CRL_verify)]
1902 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1903 where
1904 T: HasPublic,
1905 {
1906 unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1907 }
1908
1909 #[corresponds(X509_CRL_get_ext_d2i)]
1913 pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1914 let mut critical = -1;
1915 let out = unsafe {
1916 let ext = ffi::X509_CRL_get_ext_d2i(
1918 self.as_ptr(),
1919 T::NID.as_raw(),
1920 &mut critical as *mut _,
1921 ptr::null_mut(),
1922 );
1923 T::Output::from_ptr_opt(ext as *mut _)
1926 };
1927 match (critical, out) {
1928 (0, Some(out)) => Ok(Some((false, out))),
1929 (1, Some(out)) => Ok(Some((true, out))),
1930 (-1 | -2, _) => Ok(None),
1932 (0 | 1, None) => Err(ErrorStack::get()),
1935 (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1936 }
1937 }
1938}
1939
1940#[derive(Copy, Clone, PartialEq, Eq)]
1942pub struct X509VerifyResult(c_int);
1943
1944impl fmt::Debug for X509VerifyResult {
1945 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1946 fmt.debug_struct("X509VerifyResult")
1947 .field("code", &self.0)
1948 .field("error", &self.error_string())
1949 .finish()
1950 }
1951}
1952
1953impl fmt::Display for X509VerifyResult {
1954 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1955 fmt.write_str(self.error_string())
1956 }
1957}
1958
1959impl Error for X509VerifyResult {}
1960
1961impl X509VerifyResult {
1962 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
1969 X509VerifyResult(err)
1970 }
1971
1972 #[allow(clippy::trivially_copy_pass_by_ref)]
1974 pub fn as_raw(&self) -> c_int {
1975 self.0
1976 }
1977
1978 #[corresponds(X509_verify_cert_error_string)]
1980 #[allow(clippy::trivially_copy_pass_by_ref)]
1981 pub fn error_string(&self) -> &'static str {
1982 ffi::init();
1983
1984 unsafe {
1985 let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
1986 str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
1987 }
1988 }
1989
1990 pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
1992 pub const APPLICATION_VERIFICATION: X509VerifyResult =
1994 X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
1995}
1996
1997foreign_type_and_impl_send_sync! {
1998 type CType = ffi::GENERAL_NAME;
1999 fn drop = ffi::GENERAL_NAME_free;
2000
2001 pub struct GeneralName;
2003 pub struct GeneralNameRef;
2005}
2006
2007impl GeneralName {
2008 unsafe fn new(
2009 type_: c_int,
2010 asn1_type: Asn1Type,
2011 value: &[u8],
2012 ) -> Result<GeneralName, ErrorStack> {
2013 ffi::init();
2014 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2015 (*gn.as_ptr()).type_ = type_;
2016 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2017 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2018
2019 #[cfg(any(boringssl, awslc))]
2020 {
2021 (*gn.as_ptr()).d.ptr = s.cast();
2022 }
2023 #[cfg(not(any(boringssl, awslc)))]
2024 {
2025 (*gn.as_ptr()).d = s.cast();
2026 }
2027
2028 Ok(gn)
2029 }
2030
2031 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2032 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2033 }
2034
2035 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2036 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2037 }
2038
2039 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2040 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2041 }
2042
2043 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2044 match ip {
2045 IpAddr::V4(addr) => unsafe {
2046 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2047 },
2048 IpAddr::V6(addr) => unsafe {
2049 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2050 },
2051 }
2052 }
2053
2054 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2055 unsafe {
2056 ffi::init();
2057 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2058 (*gn).type_ = ffi::GEN_RID;
2059
2060 #[cfg(any(boringssl, awslc))]
2061 {
2062 (*gn).d.registeredID = oid.as_ptr();
2063 }
2064 #[cfg(not(any(boringssl, awslc)))]
2065 {
2066 (*gn).d = oid.as_ptr().cast();
2067 }
2068
2069 mem::forget(oid);
2070
2071 Ok(GeneralName::from_ptr(gn))
2072 }
2073 }
2074
2075 pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2076 unsafe {
2077 ffi::init();
2078
2079 let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2080 ptr::null_mut(),
2081 &mut value.as_ptr().cast(),
2082 value.len().try_into().unwrap(),
2083 ))?;
2084
2085 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2086 (*gn).type_ = ffi::GEN_OTHERNAME;
2087
2088 if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2089 gn,
2090 oid.as_ptr().cast(),
2091 typ,
2092 )) {
2093 ffi::GENERAL_NAME_free(gn);
2094 return Err(e);
2095 }
2096
2097 mem::forget(oid);
2098
2099 Ok(GeneralName::from_ptr(gn))
2100 }
2101 }
2102
2103 pub(crate) fn new_dir_name(name: &X509NameRef) -> Result<GeneralName, ErrorStack> {
2104 unsafe {
2105 ffi::init();
2106 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2107 (*gn).type_ = ffi::GEN_DIRNAME;
2108
2109 let dup = match name.to_owned() {
2110 Ok(dup) => dup,
2111 Err(e) => {
2112 ffi::GENERAL_NAME_free(gn);
2113 return Err(e);
2114 }
2115 };
2116
2117 #[cfg(any(boringssl, awslc))]
2118 {
2119 (*gn).d.directoryName = dup.as_ptr();
2120 }
2121 #[cfg(not(any(boringssl, awslc)))]
2122 {
2123 (*gn).d = dup.as_ptr().cast();
2124 }
2125
2126 std::mem::forget(dup);
2127
2128 Ok(GeneralName::from_ptr(gn))
2129 }
2130 }
2131}
2132
2133impl GeneralNameRef {
2134 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2135 unsafe {
2136 if (*self.as_ptr()).type_ != ffi_type {
2137 return None;
2138 }
2139
2140 #[cfg(any(boringssl, awslc))]
2141 let d = (*self.as_ptr()).d.ptr;
2142 #[cfg(not(any(boringssl, awslc)))]
2143 let d = (*self.as_ptr()).d;
2144
2145 let ptr = ASN1_STRING_get0_data(d as *mut _);
2146 let len = ffi::ASN1_STRING_length(d as *mut _);
2147
2148 #[allow(clippy::unnecessary_cast)]
2149 let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2150 str::from_utf8(slice).ok()
2154 }
2155 }
2156
2157 pub fn email(&self) -> Option<&str> {
2159 self.ia5_string(ffi::GEN_EMAIL)
2160 }
2161
2162 pub fn directory_name(&self) -> Option<&X509NameRef> {
2164 unsafe {
2165 if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2166 return None;
2167 }
2168
2169 #[cfg(any(boringssl, awslc))]
2170 let d = (*self.as_ptr()).d.ptr;
2171 #[cfg(not(any(boringssl, awslc)))]
2172 let d = (*self.as_ptr()).d;
2173
2174 Some(X509NameRef::from_const_ptr(d as *const _))
2175 }
2176 }
2177
2178 pub fn dnsname(&self) -> Option<&str> {
2180 self.ia5_string(ffi::GEN_DNS)
2181 }
2182
2183 pub fn uri(&self) -> Option<&str> {
2185 self.ia5_string(ffi::GEN_URI)
2186 }
2187
2188 pub fn ipaddress(&self) -> Option<&[u8]> {
2190 unsafe {
2191 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2192 return None;
2193 }
2194 #[cfg(any(boringssl, awslc))]
2195 let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2196 #[cfg(not(any(boringssl, awslc)))]
2197 let d = (*self.as_ptr()).d;
2198
2199 let ptr = ASN1_STRING_get0_data(d as *mut _);
2200 let len = ffi::ASN1_STRING_length(d as *mut _);
2201
2202 #[allow(clippy::unnecessary_cast)]
2203 Some(util::from_raw_parts(ptr as *const u8, len as usize))
2204 }
2205 }
2206}
2207
2208impl fmt::Debug for GeneralNameRef {
2209 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2210 if let Some(email) = self.email() {
2211 formatter.write_str(email)
2212 } else if let Some(dnsname) = self.dnsname() {
2213 formatter.write_str(dnsname)
2214 } else if let Some(uri) = self.uri() {
2215 formatter.write_str(uri)
2216 } else if let Some(ipaddress) = self.ipaddress() {
2217 let address = <[u8; 16]>::try_from(ipaddress)
2218 .map(IpAddr::from)
2219 .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2220 match address {
2221 Ok(a) => fmt::Debug::fmt(&a, formatter),
2222 Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2223 }
2224 } else {
2225 formatter.write_str("(empty)")
2226 }
2227 }
2228}
2229
2230impl Stackable for GeneralName {
2231 type StackType = ffi::stack_st_GENERAL_NAME;
2232}
2233
2234foreign_type_and_impl_send_sync! {
2235 type CType = ffi::DIST_POINT;
2236 fn drop = ffi::DIST_POINT_free;
2237
2238 pub struct DistPoint;
2240 pub struct DistPointRef;
2242}
2243
2244impl DistPointRef {
2245 pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2247 unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2248 }
2249}
2250
2251foreign_type_and_impl_send_sync! {
2252 type CType = ffi::DIST_POINT_NAME;
2253 fn drop = ffi::DIST_POINT_NAME_free;
2254
2255 pub struct DistPointName;
2257 pub struct DistPointNameRef;
2259}
2260
2261impl DistPointNameRef {
2262 pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2264 unsafe {
2265 if (*self.as_ptr()).type_ != 0 {
2266 return None;
2267 }
2268 StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2269 }
2270 }
2271}
2272
2273impl Stackable for DistPoint {
2274 type StackType = ffi::stack_st_DIST_POINT;
2275}
2276
2277foreign_type_and_impl_send_sync! {
2278 type CType = ffi::ACCESS_DESCRIPTION;
2279 fn drop = ffi::ACCESS_DESCRIPTION_free;
2280
2281 pub struct AccessDescription;
2283 pub struct AccessDescriptionRef;
2285}
2286
2287impl AccessDescriptionRef {
2288 pub fn method(&self) -> &Asn1ObjectRef {
2290 unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2291 }
2292
2293 pub fn location(&self) -> &GeneralNameRef {
2295 unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2296 }
2297}
2298
2299impl Stackable for AccessDescription {
2300 type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2301}
2302
2303foreign_type_and_impl_send_sync! {
2304 type CType = ffi::X509_ALGOR;
2305 fn drop = ffi::X509_ALGOR_free;
2306
2307 pub struct X509Algorithm;
2309 pub struct X509AlgorithmRef;
2311}
2312
2313impl X509AlgorithmRef {
2314 pub fn object(&self) -> &Asn1ObjectRef {
2316 unsafe {
2317 let mut oid = ptr::null();
2318 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2319 Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2320 }
2321 }
2322}
2323
2324foreign_type_and_impl_send_sync! {
2325 type CType = ffi::X509_OBJECT;
2326 fn drop = X509_OBJECT_free;
2327
2328 pub struct X509Object;
2330 pub struct X509ObjectRef;
2332}
2333
2334impl X509ObjectRef {
2335 pub fn x509(&self) -> Option<&X509Ref> {
2336 unsafe {
2337 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2338 X509Ref::from_const_ptr_opt(ptr)
2339 }
2340 }
2341}
2342
2343impl Stackable for X509Object {
2344 type StackType = ffi::stack_st_X509_OBJECT;
2345}
2346
2347use ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
2348
2349use ffi::{
2350 ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
2351 X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
2352};
2353
2354use ffi::X509_OBJECT_free;
2355use ffi::X509_OBJECT_get0_X509;
2356
2357use ffi::{
2358 X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_get_REVOKED, X509_CRL_get_issuer,
2359 X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2360};
2361
2362#[derive(Copy, Clone, PartialEq, Eq)]
2363pub struct X509PurposeId(c_int);
2364
2365impl X509PurposeId {
2366 pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2367 pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2368 pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2369 pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2370 pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2371 pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2372 pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2373 pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2374 pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2375 #[cfg(ossl320)]
2376 pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2377
2378 pub fn from_raw(id: c_int) -> Self {
2380 X509PurposeId(id)
2381 }
2382
2383 pub fn as_raw(&self) -> c_int {
2385 self.0
2386 }
2387}
2388
2389pub struct X509PurposeRef(Opaque);
2391
2392impl ForeignTypeRef for X509PurposeRef {
2394 type CType = ffi::X509_PURPOSE;
2395}
2396
2397impl X509PurposeRef {
2398 #[allow(clippy::unnecessary_cast)]
2412 pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2413 unsafe {
2414 let sname = CString::new(sname).unwrap();
2415 let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2416 Ok(purpose)
2417 }
2418 }
2419 #[corresponds(X509_PURPOSE_get0)]
2422 pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2423 unsafe {
2424 let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2425 Ok(X509PurposeRef::from_const_ptr(ptr))
2426 }
2427 }
2428
2429 pub fn purpose(&self) -> X509PurposeId {
2440 unsafe {
2441 let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2442 X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2443 }
2444 }
2445}