1use crate::bio::{MemBio, MemBioBuf};
15use crate::error::ErrorStack;
16use native_ossl_sys as sys;
17use std::ffi::CStr;
18use std::marker::PhantomData;
19use std::sync::Arc;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct BrokenDownTime {
30 pub year: i32,
32 pub month: u8,
34 pub day: u8,
36 pub hour: u8,
38 pub minute: u8,
40 pub second: u8,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct SignatureInfo {
51 pub md_nid: i32,
56 pub pk_nid: i32,
58 pub security_bits: i32,
60}
61
62pub struct X509 {
68 ptr: *mut sys::X509,
69}
70
71unsafe impl Send for X509 {}
73unsafe impl Sync for X509 {}
74
75impl Clone for X509 {
76 fn clone(&self) -> Self {
77 unsafe { sys::X509_up_ref(self.ptr) };
78 X509 { ptr: self.ptr }
79 }
80}
81
82impl Drop for X509 {
83 fn drop(&mut self) {
84 unsafe { sys::X509_free(self.ptr) };
85 }
86}
87
88impl X509 {
89 pub(crate) unsafe fn from_ptr(ptr: *mut sys::X509) -> Self {
95 X509 { ptr }
96 }
97
98 pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
102 let bio = MemBioBuf::new(pem)?;
103 let ptr = unsafe {
104 sys::PEM_read_bio_X509(
105 bio.as_ptr(),
106 std::ptr::null_mut(),
107 None,
108 std::ptr::null_mut(),
109 )
110 };
111 if ptr.is_null() {
112 return Err(ErrorStack::drain());
113 }
114 Ok(unsafe { Self::from_ptr(ptr) })
115 }
116
117 pub fn from_pem_in(_ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
128 Self::from_pem(pem)
129 }
130
131 pub fn new_in(ctx: &Arc<crate::lib_ctx::LibCtx>) -> Result<Self, ErrorStack> {
142 let ptr = unsafe { sys::X509_new_ex(ctx.as_ptr(), std::ptr::null()) };
146 if ptr.is_null() {
147 return Err(ErrorStack::drain());
148 }
149 Ok(unsafe { Self::from_ptr(ptr) })
150 }
151
152 pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
160 let mut der_ptr = der.as_ptr();
161 let len = i64::try_from(der.len()).map_err(|_| ErrorStack::drain())?;
162 let ptr =
163 unsafe { sys::d2i_X509(std::ptr::null_mut(), std::ptr::addr_of_mut!(der_ptr), len) };
164 if ptr.is_null() {
165 return Err(ErrorStack::drain());
166 }
167 Ok(unsafe { Self::from_ptr(ptr) })
168 }
169
170 pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
174 let mut bio = MemBio::new()?;
175 crate::ossl_call!(sys::PEM_write_bio_X509(bio.as_ptr(), self.ptr))?;
176 Ok(bio.into_vec())
177 }
178
179 pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> {
186 let len = unsafe { sys::i2d_X509(self.ptr, std::ptr::null_mut()) };
187 if len < 0 {
188 return Err(ErrorStack::drain());
189 }
190 let mut buf = vec![0u8; usize::try_from(len).unwrap_or(0)];
191 let mut out_ptr = buf.as_mut_ptr();
192 let written = unsafe { sys::i2d_X509(self.ptr, std::ptr::addr_of_mut!(out_ptr)) };
193 if written < 0 {
194 return Err(ErrorStack::drain());
195 }
196 buf.truncate(usize::try_from(written).unwrap_or(0));
197 Ok(buf)
198 }
199
200 #[must_use]
202 pub fn subject_name(&self) -> X509Name<'_> {
203 let ptr = unsafe { sys::X509_get_subject_name(self.ptr) }.cast();
207 X509Name {
208 ptr,
209 _owner: PhantomData,
210 }
211 }
212
213 #[must_use]
215 pub fn issuer_name(&self) -> X509Name<'_> {
216 let ptr = unsafe { sys::X509_get_issuer_name(self.ptr) }.cast();
217 X509Name {
218 ptr,
219 _owner: PhantomData,
220 }
221 }
222
223 #[must_use]
227 pub fn serial_number(&self) -> Option<i64> {
228 let ai = unsafe { sys::X509_get0_serialNumber(self.ptr) };
229 if ai.is_null() {
230 return None;
231 }
232 let mut n: i64 = 0;
233 let rc = unsafe { sys::ASN1_INTEGER_get_int64(std::ptr::addr_of_mut!(n), ai) };
234 if rc == 1 {
235 Some(n)
236 } else {
237 None
238 }
239 }
240
241 #[must_use]
249 pub fn serial_number_bytes(&self) -> Option<Vec<u8>> {
250 let ai = unsafe { sys::X509_get_serialNumber(self.ptr) };
256 if ai.is_null() {
257 return None;
258 }
259 Some(unsafe { asn1_string_data(ai.cast()) }.to_vec())
263 }
264
265 #[must_use]
270 pub fn not_before_str(&self) -> Option<String> {
271 let t = unsafe { sys::X509_get0_notBefore(self.ptr) };
272 asn1_time_to_str(t)
273 }
274
275 #[must_use]
279 pub fn not_after_str(&self) -> Option<String> {
280 let t = unsafe { sys::X509_get0_notAfter(self.ptr) };
281 asn1_time_to_str(t)
282 }
283
284 #[must_use]
288 pub fn not_before_tm(&self) -> Option<BrokenDownTime> {
289 let t = unsafe { sys::X509_get0_notBefore(self.ptr) };
294 asn1_time_to_broken_down(t)
295 }
296
297 #[must_use]
301 pub fn not_after_tm(&self) -> Option<BrokenDownTime> {
302 let t = unsafe { sys::X509_get0_notAfter(self.ptr) };
304 asn1_time_to_broken_down(t)
305 }
306
307 #[must_use]
309 pub fn is_valid_now(&self) -> bool {
310 let nb = unsafe { sys::X509_get0_notBefore(self.ptr) };
311 let na = unsafe { sys::X509_get0_notAfter(self.ptr) };
312 unsafe {
314 sys::X509_cmp_time(nb, std::ptr::null_mut()) <= 0
315 && sys::X509_cmp_time(na, std::ptr::null_mut()) >= 0
316 }
317 }
318
319 pub fn public_key(&self) -> Result<crate::pkey::Pkey<crate::pkey::Public>, ErrorStack> {
325 let ptr = unsafe { sys::X509_get_pubkey(self.ptr) };
326 if ptr.is_null() {
327 return Err(ErrorStack::drain());
328 }
329 Ok(unsafe { crate::pkey::Pkey::from_ptr(ptr) })
330 }
331
332 #[must_use]
340 pub fn public_key_is_a(&self, alg: &CStr) -> bool {
341 let pkey = unsafe { sys::X509_get0_pubkey(self.ptr) };
347 if pkey.is_null() {
348 return false;
349 }
350 unsafe { sys::EVP_PKEY_is_a(pkey, alg.as_ptr()) == 1 }
353 }
354
355 #[must_use]
361 pub fn public_key_bits(&self) -> Option<u32> {
362 let pkey = unsafe { sys::X509_get0_pubkey(self.ptr) };
367 if pkey.is_null() {
368 return None;
369 }
370 u32::try_from(unsafe { sys::EVP_PKEY_get_bits(pkey) }).ok()
372 }
373
374 pub fn signature_info(&self) -> Result<SignatureInfo, ErrorStack> {
390 let mut md_nid: std::ffi::c_int = 0;
391 let mut pk_nid: std::ffi::c_int = 0;
392 let mut security_bits: std::ffi::c_int = 0;
393 let rc = unsafe {
401 sys::X509_get_signature_info(
402 self.ptr,
403 &raw mut md_nid,
404 &raw mut pk_nid,
405 &raw mut security_bits,
406 std::ptr::null_mut(),
407 )
408 };
409 if rc != 1 {
410 return Err(ErrorStack::drain());
411 }
412 Ok(SignatureInfo {
413 md_nid,
414 pk_nid,
415 security_bits,
416 })
417 }
418
419 pub fn verify(&self, key: &crate::pkey::Pkey<crate::pkey::Public>) -> Result<bool, ErrorStack> {
426 match unsafe { sys::X509_verify(self.ptr, key.as_ptr()) } {
427 1 => Ok(true),
428 0 => Ok(false),
429 _ => Err(ErrorStack::drain()),
430 }
431 }
432
433 #[must_use]
435 pub fn is_self_signed(&self) -> bool {
436 unsafe { sys::X509_self_signed(self.ptr, 0) == 1 }
438 }
439
440 #[must_use]
442 pub fn extension_count(&self) -> usize {
443 let n = unsafe { sys::X509_get_ext_count(self.ptr) };
444 usize::try_from(n).unwrap_or(0)
445 }
446
447 #[must_use]
451 pub fn extension(&self, idx: usize) -> Option<X509Extension<'_>> {
452 let idx_i32 = i32::try_from(idx).ok()?;
453 let ptr = unsafe { sys::X509_get_ext(self.ptr, idx_i32) }.cast::<sys::X509_EXTENSION>();
455 if ptr.is_null() {
456 None
457 } else {
458 Some(X509Extension {
459 ptr,
460 _owner: PhantomData,
461 })
462 }
463 }
464
465 #[must_use]
469 pub fn extension_by_nid(&self, nid: i32) -> Option<X509Extension<'_>> {
470 let idx = unsafe { sys::X509_get_ext_by_NID(self.ptr, nid, -1) };
471 if idx < 0 {
472 return None;
473 }
474 let ptr = unsafe { sys::X509_get_ext(self.ptr, idx) }.cast::<sys::X509_EXTENSION>();
475 if ptr.is_null() {
476 None
477 } else {
478 Some(X509Extension {
479 ptr,
480 _owner: PhantomData,
481 })
482 }
483 }
484
485 #[must_use]
494 pub fn extension_der(&self, nid: i32) -> Option<&[u8]> {
495 let idx = unsafe { sys::X509_get_ext_by_NID(self.ptr, nid, -1) };
497 if idx < 0 {
498 return None;
499 }
500 let ext = unsafe { sys::X509_get_ext(self.ptr, idx) };
502 if ext.is_null() {
503 return None;
504 }
505 let data = unsafe { sys::X509_EXTENSION_get_data(ext) };
508 if data.is_null() {
509 return Some(&[]);
510 }
511 Some(unsafe { asn1_string_data(data.cast()) })
514 }
515
516 #[must_use]
518 #[allow(dead_code)] pub(crate) fn as_ptr(&self) -> *mut sys::X509 {
520 self.ptr
521 }
522}
523
524pub struct X509Name<'cert> {
528 ptr: *mut sys::X509_NAME,
529 _owner: PhantomData<&'cert X509>,
530}
531
532impl X509Name<'_> {
533 #[must_use]
535 pub fn entry_count(&self) -> usize {
536 usize::try_from(unsafe { sys::X509_NAME_entry_count(self.ptr) }).unwrap_or(0)
537 }
538
539 #[must_use]
541 pub fn entry(&self, idx: usize) -> Option<X509NameEntry<'_>> {
542 let idx_i32 = i32::try_from(idx).ok()?;
543 let ptr =
545 unsafe { sys::X509_NAME_get_entry(self.ptr, idx_i32) }.cast::<sys::X509_NAME_ENTRY>();
546 if ptr.is_null() {
547 None
548 } else {
549 Some(X509NameEntry {
550 ptr,
551 _owner: PhantomData,
552 })
553 }
554 }
555
556 #[must_use]
565 pub fn to_oneline(&self) -> Option<String> {
566 let ptr = unsafe { sys::X509_NAME_oneline(self.ptr, std::ptr::null_mut(), 0) };
571 if ptr.is_null() {
572 return None;
573 }
574 let s = unsafe { std::ffi::CStr::from_ptr(ptr) }
576 .to_string_lossy()
577 .into_owned();
578 unsafe { sys::CRYPTO_free(ptr.cast(), c"x509.rs".as_ptr(), 0) };
582 Some(s)
583 }
584
585 #[must_use]
590 pub fn to_string(&self) -> Option<String> {
591 let mut bio = MemBio::new().ok()?;
592 let n = unsafe { sys::X509_NAME_print_ex(bio.as_ptr(), self.ptr, 0, 0) };
594 if n < 0 {
595 return None;
596 }
597 String::from_utf8(bio.into_vec()).ok()
598 }
599}
600
601pub struct X509NameEntry<'name> {
605 ptr: *mut sys::X509_NAME_ENTRY,
606 _owner: PhantomData<&'name ()>,
607}
608
609impl X509NameEntry<'_> {
610 #[must_use]
612 pub fn nid(&self) -> i32 {
613 let obj = unsafe { sys::X509_NAME_ENTRY_get_object(self.ptr) };
614 unsafe { sys::OBJ_obj2nid(obj) }
615 }
616
617 #[must_use]
621 pub fn data(&self) -> &[u8] {
622 let asn1 = unsafe { sys::X509_NAME_ENTRY_get_data(self.ptr) };
623 if asn1.is_null() {
624 return &[];
625 }
626 unsafe { asn1_string_data(asn1) }
628 }
629}
630
631pub struct X509Extension<'cert> {
635 ptr: *mut sys::X509_EXTENSION,
636 _owner: PhantomData<&'cert X509>,
637}
638
639impl X509Extension<'_> {
640 #[must_use]
642 pub fn nid(&self) -> i32 {
643 let obj = unsafe { sys::X509_EXTENSION_get_object(self.ptr) };
644 unsafe { sys::OBJ_obj2nid(obj) }
645 }
646
647 #[must_use]
649 pub fn is_critical(&self) -> bool {
650 unsafe { sys::X509_EXTENSION_get_critical(self.ptr) == 1 }
651 }
652
653 #[must_use]
657 pub fn data(&self) -> &[u8] {
658 let asn1 = unsafe { sys::X509_EXTENSION_get_data(self.ptr) };
659 if asn1.is_null() {
660 return &[];
661 }
662 unsafe { asn1_string_data(asn1.cast()) }
665 }
666}
667
668pub struct X509NameOwned {
674 ptr: *mut sys::X509_NAME,
675}
676
677impl X509NameOwned {
678 pub fn new() -> Result<Self, ErrorStack> {
682 let ptr = unsafe { sys::X509_NAME_new() };
683 if ptr.is_null() {
684 return Err(ErrorStack::drain());
685 }
686 Ok(X509NameOwned { ptr })
687 }
688
689 pub fn add_entry_by_txt(&mut self, field: &CStr, value: &[u8]) -> Result<(), ErrorStack> {
699 let len = i32::try_from(value.len()).map_err(|_| ErrorStack::drain())?;
700 let rc = unsafe {
702 sys::X509_NAME_add_entry_by_txt(
703 self.ptr,
704 field.as_ptr(),
705 0x1000, value.as_ptr(),
707 len,
708 -1, 0,
710 )
711 };
712 if rc != 1 {
713 return Err(ErrorStack::drain());
714 }
715 Ok(())
716 }
717}
718
719impl Drop for X509NameOwned {
720 fn drop(&mut self) {
721 unsafe { sys::X509_NAME_free(self.ptr) };
722 }
723}
724
725pub struct X509Builder {
745 ptr: *mut sys::X509,
746}
747
748impl X509Builder {
749 pub fn new() -> Result<Self, ErrorStack> {
753 let ptr = unsafe { sys::X509_new() };
754 if ptr.is_null() {
755 return Err(ErrorStack::drain());
756 }
757 Ok(X509Builder { ptr })
758 }
759
760 pub fn set_version(self, version: i64) -> Result<Self, ErrorStack> {
764 crate::ossl_call!(sys::X509_set_version(self.ptr, version))?;
765 Ok(self)
766 }
767
768 pub fn set_serial_number(self, n: i64) -> Result<Self, ErrorStack> {
772 let ai = unsafe { sys::ASN1_INTEGER_new() };
773 if ai.is_null() {
774 return Err(ErrorStack::drain());
775 }
776 crate::ossl_call!(sys::ASN1_INTEGER_set_int64(ai, n)).map_err(|e| {
777 unsafe { sys::ASN1_INTEGER_free(ai) };
778 e
779 })?;
780 let rc = unsafe { sys::X509_set_serialNumber(self.ptr, ai) };
781 unsafe { sys::ASN1_INTEGER_free(ai) };
782 if rc != 1 {
783 return Err(ErrorStack::drain());
784 }
785 Ok(self)
786 }
787
788 pub fn set_not_before_offset(self, offset_secs: i64) -> Result<Self, ErrorStack> {
792 let t = unsafe { sys::X509_getm_notBefore(self.ptr) };
793 if unsafe { sys::X509_gmtime_adj(t, offset_secs) }.is_null() {
794 return Err(ErrorStack::drain());
795 }
796 Ok(self)
797 }
798
799 pub fn set_not_after_offset(self, offset_secs: i64) -> Result<Self, ErrorStack> {
803 let t = unsafe { sys::X509_getm_notAfter(self.ptr) };
804 if unsafe { sys::X509_gmtime_adj(t, offset_secs) }.is_null() {
805 return Err(ErrorStack::drain());
806 }
807 Ok(self)
808 }
809
810 pub fn set_subject_name(self, name: &X509NameOwned) -> Result<Self, ErrorStack> {
814 crate::ossl_call!(sys::X509_set_subject_name(self.ptr, name.ptr))?;
815 Ok(self)
816 }
817
818 pub fn set_issuer_name(self, name: &X509NameOwned) -> Result<Self, ErrorStack> {
822 crate::ossl_call!(sys::X509_set_issuer_name(self.ptr, name.ptr))?;
823 Ok(self)
824 }
825
826 pub fn set_public_key<T: crate::pkey::HasPublic>(
830 self,
831 key: &crate::pkey::Pkey<T>,
832 ) -> Result<Self, ErrorStack> {
833 crate::ossl_call!(sys::X509_set_pubkey(self.ptr, key.as_ptr()))?;
834 Ok(self)
835 }
836
837 pub fn sign(
844 self,
845 key: &crate::pkey::Pkey<crate::pkey::Private>,
846 digest: Option<&crate::digest::DigestAlg>,
847 ) -> Result<Self, ErrorStack> {
848 let md_ptr = digest.map_or(std::ptr::null(), crate::digest::DigestAlg::as_ptr);
849 let rc = unsafe { sys::X509_sign(self.ptr, key.as_ptr(), md_ptr) };
851 if rc <= 0 {
852 return Err(ErrorStack::drain());
853 }
854 Ok(self)
855 }
856
857 #[must_use]
859 pub fn build(self) -> X509 {
860 let ptr = self.ptr;
861 std::mem::forget(self);
862 X509 { ptr }
863 }
864}
865
866impl Drop for X509Builder {
867 fn drop(&mut self) {
868 unsafe { sys::X509_free(self.ptr) };
869 }
870}
871
872pub struct X509Store {
878 ptr: *mut sys::X509_STORE,
879}
880
881unsafe impl Send for X509Store {}
882unsafe impl Sync for X509Store {}
883
884impl Clone for X509Store {
885 fn clone(&self) -> Self {
886 unsafe { sys::X509_STORE_up_ref(self.ptr) };
887 X509Store { ptr: self.ptr }
888 }
889}
890
891impl Drop for X509Store {
892 fn drop(&mut self) {
893 unsafe { sys::X509_STORE_free(self.ptr) };
894 }
895}
896
897impl X509Store {
898 pub fn new() -> Result<Self, ErrorStack> {
902 let ptr = unsafe { sys::X509_STORE_new() };
903 if ptr.is_null() {
904 return Err(ErrorStack::drain());
905 }
906 Ok(X509Store { ptr })
907 }
908
909 pub fn add_cert(&mut self, cert: &X509) -> Result<(), ErrorStack> {
915 let rc = unsafe { sys::X509_STORE_add_cert(self.ptr, cert.ptr) };
916 if rc != 1 {
917 return Err(ErrorStack::drain());
918 }
919 Ok(())
920 }
921
922 pub fn add_crl(&mut self, crl: &X509Crl) -> Result<(), ErrorStack> {
926 let rc = unsafe { sys::X509_STORE_add_crl(self.ptr, crl.ptr) };
927 if rc != 1 {
928 return Err(ErrorStack::drain());
929 }
930 Ok(())
931 }
932
933 pub fn set_flags(&mut self, flags: u64) -> Result<(), ErrorStack> {
937 let rc = unsafe { sys::X509_STORE_set_flags(self.ptr, flags) };
938 if rc != 1 {
939 return Err(ErrorStack::drain());
940 }
941 Ok(())
942 }
943
944 #[must_use]
946 pub(crate) fn as_ptr(&self) -> *mut sys::X509_STORE {
947 self.ptr
948 }
949}
950
951pub struct X509StoreCtx {
958 ptr: *mut sys::X509_STORE_CTX,
959}
960
961impl Drop for X509StoreCtx {
962 fn drop(&mut self) {
963 unsafe { sys::X509_STORE_CTX_free(self.ptr) };
964 }
965}
966
967unsafe impl Send for X509StoreCtx {}
968
969impl X509StoreCtx {
970 pub fn new() -> Result<Self, ErrorStack> {
974 let ptr = unsafe { sys::X509_STORE_CTX_new() };
975 if ptr.is_null() {
976 return Err(ErrorStack::drain());
977 }
978 Ok(X509StoreCtx { ptr })
979 }
980
981 pub fn init(&mut self, store: &X509Store, cert: &X509) -> Result<(), ErrorStack> {
987 let rc = unsafe {
988 sys::X509_STORE_CTX_init(self.ptr, store.ptr, cert.ptr, std::ptr::null_mut())
989 };
990 if rc != 1 {
991 return Err(ErrorStack::drain());
992 }
993 Ok(())
994 }
995
996 pub fn verify(&mut self) -> Result<bool, ErrorStack> {
1003 match unsafe { sys::X509_verify_cert(self.ptr) } {
1004 1 => Ok(true),
1005 0 => Ok(false),
1006 _ => Err(ErrorStack::drain()),
1007 }
1008 }
1009
1010 #[must_use]
1015 pub fn error(&self) -> i32 {
1016 unsafe { sys::X509_STORE_CTX_get_error(self.ptr) }
1017 }
1018
1019 #[must_use]
1024 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
1026 pub fn chain(&self) -> Vec<X509> {
1027 let stack = unsafe { sys::X509_STORE_CTX_get0_chain(self.ptr) };
1028 if stack.is_null() {
1029 return Vec::new();
1030 }
1031 let n = unsafe { sys::OPENSSL_sk_num(stack.cast::<sys::OPENSSL_STACK>()) };
1032 let n = usize::try_from(n).unwrap_or(0);
1033 let mut out = Vec::with_capacity(n);
1034 for i in 0..n {
1035 let raw =
1036 unsafe { sys::OPENSSL_sk_value(stack.cast::<sys::OPENSSL_STACK>(), i as i32) };
1037 if raw.is_null() {
1038 continue;
1039 }
1040 let cert_ptr = raw.cast::<sys::X509>();
1042 unsafe { sys::X509_up_ref(cert_ptr) };
1043 out.push(X509 { ptr: cert_ptr });
1044 }
1045 out
1046 }
1047}
1048
1049pub struct X509Crl {
1055 ptr: *mut sys::X509_CRL,
1056}
1057
1058unsafe impl Send for X509Crl {}
1059unsafe impl Sync for X509Crl {}
1060
1061impl Clone for X509Crl {
1062 fn clone(&self) -> Self {
1063 unsafe { sys::X509_CRL_up_ref(self.ptr) };
1064 X509Crl { ptr: self.ptr }
1065 }
1066}
1067
1068impl Drop for X509Crl {
1069 fn drop(&mut self) {
1070 unsafe { sys::X509_CRL_free(self.ptr) };
1071 }
1072}
1073
1074impl X509Crl {
1075 pub(crate) unsafe fn from_ptr(ptr: *mut sys::X509_CRL) -> Self {
1081 X509Crl { ptr }
1082 }
1083
1084 pub fn new() -> Result<Self, ErrorStack> {
1093 let ptr = unsafe { sys::X509_CRL_new() };
1098 if ptr.is_null() {
1099 return Err(ErrorStack::drain());
1100 }
1101 Ok(unsafe { Self::from_ptr(ptr) })
1102 }
1103
1104 pub fn new_in(ctx: &std::sync::Arc<crate::lib_ctx::LibCtx>) -> Result<Self, ErrorStack> {
1113 let ptr = unsafe { sys::X509_CRL_new_ex(ctx.as_ptr(), std::ptr::null()) };
1120 if ptr.is_null() {
1121 return Err(ErrorStack::drain());
1122 }
1123 Ok(unsafe { Self::from_ptr(ptr) })
1124 }
1125
1126 pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
1130 let bio = MemBioBuf::new(pem)?;
1131 let ptr = unsafe {
1132 sys::PEM_read_bio_X509_CRL(
1133 bio.as_ptr(),
1134 std::ptr::null_mut(),
1135 None,
1136 std::ptr::null_mut(),
1137 )
1138 };
1139 if ptr.is_null() {
1140 return Err(ErrorStack::drain());
1141 }
1142 Ok(unsafe { Self::from_ptr(ptr) })
1143 }
1144
1145 pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
1149 let bio = MemBioBuf::new(der)?;
1150 let ptr = unsafe { sys::d2i_X509_CRL_bio(bio.as_ptr(), std::ptr::null_mut()) };
1151 if ptr.is_null() {
1152 return Err(ErrorStack::drain());
1153 }
1154 Ok(unsafe { Self::from_ptr(ptr) })
1155 }
1156
1157 pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
1161 let mut bio = MemBio::new()?;
1162 let rc = unsafe { sys::PEM_write_bio_X509_CRL(bio.as_ptr(), self.ptr) };
1163 if rc != 1 {
1164 return Err(ErrorStack::drain());
1165 }
1166 Ok(bio.into_vec())
1167 }
1168
1169 pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> {
1173 let mut bio = MemBio::new()?;
1174 let rc = unsafe { sys::i2d_X509_CRL_bio(bio.as_ptr(), self.ptr) };
1175 if rc != 1 {
1176 return Err(ErrorStack::drain());
1177 }
1178 Ok(bio.into_vec())
1179 }
1180
1181 #[must_use]
1183 pub fn issuer_name(&self) -> X509Name<'_> {
1184 let ptr = unsafe { sys::X509_CRL_get_issuer(self.ptr) };
1185 X509Name {
1186 ptr: ptr.cast(),
1187 _owner: PhantomData,
1188 }
1189 }
1190
1191 #[must_use]
1193 pub fn last_update_str(&self) -> Option<String> {
1194 let t = unsafe { sys::X509_CRL_get0_lastUpdate(self.ptr) };
1195 asn1_time_to_str(t)
1196 }
1197
1198 #[must_use]
1200 pub fn next_update_str(&self) -> Option<String> {
1201 let t = unsafe { sys::X509_CRL_get0_nextUpdate(self.ptr) };
1202 asn1_time_to_str(t)
1203 }
1204
1205 pub fn verify(&self, key: &crate::pkey::Pkey<crate::pkey::Public>) -> Result<bool, ErrorStack> {
1211 match unsafe { sys::X509_CRL_verify(self.ptr, key.as_ptr()) } {
1212 1 => Ok(true),
1213 0 => Ok(false),
1214 _ => Err(ErrorStack::drain()),
1215 }
1216 }
1217
1218 #[must_use]
1220 #[allow(dead_code)]
1221 pub(crate) fn as_ptr(&self) -> *mut sys::X509_CRL {
1222 self.ptr
1223 }
1224}
1225
1226#[must_use]
1232pub fn nid_from_short_name(sn: &CStr) -> Option<i32> {
1233 let nid = unsafe { sys::OBJ_sn2nid(sn.as_ptr()) };
1239 if nid == 0 {
1240 None
1241 } else {
1242 Some(nid)
1243 }
1244}
1245
1246#[must_use]
1253pub fn nid_from_text(s: &CStr) -> Option<i32> {
1254 let nid = unsafe { sys::OBJ_txt2nid(s.as_ptr()) };
1260 if nid == 0 {
1261 None
1262 } else {
1263 Some(nid)
1264 }
1265}
1266
1267#[must_use]
1277pub fn nid_to_short_name(nid: i32) -> Option<&'static CStr> {
1278 let ptr = unsafe { sys::OBJ_nid2sn(nid) };
1284 if ptr.is_null() {
1285 return None;
1286 }
1287 Some(unsafe { CStr::from_ptr(ptr) })
1290}
1291
1292#[must_use]
1299pub fn nid_to_long_name(nid: i32) -> Option<&'static CStr> {
1300 let ptr = unsafe { sys::OBJ_nid2ln(nid) };
1303 if ptr.is_null() {
1304 return None;
1305 }
1306 Some(unsafe { CStr::from_ptr(ptr) })
1309}
1310
1311fn asn1_time_to_broken_down(t: *const sys::ASN1_TIME) -> Option<BrokenDownTime> {
1315 if t.is_null() {
1316 return None;
1317 }
1318 let mut tm = unsafe { std::mem::zeroed::<sys::tm>() };
1323 let rc = unsafe { sys::ASN1_TIME_to_tm(t, &raw mut tm) };
1324 if rc != 1 {
1325 return None;
1326 }
1327 Some(BrokenDownTime {
1328 year: tm.tm_year + 1900,
1329 month: u8::try_from(tm.tm_mon + 1).unwrap_or(0),
1330 day: u8::try_from(tm.tm_mday).unwrap_or(0),
1331 hour: u8::try_from(tm.tm_hour).unwrap_or(0),
1332 minute: u8::try_from(tm.tm_min).unwrap_or(0),
1333 second: u8::try_from(tm.tm_sec).unwrap_or(0),
1334 })
1335}
1336
1337fn asn1_time_to_str(t: *const sys::ASN1_TIME) -> Option<String> {
1339 if t.is_null() {
1340 return None;
1341 }
1342 let mut bio = MemBio::new().ok()?;
1343 let rc = unsafe { sys::ASN1_TIME_print(bio.as_ptr(), t) };
1344 if rc != 1 {
1345 return None;
1346 }
1347 String::from_utf8(bio.into_vec()).ok()
1348}
1349
1350unsafe fn asn1_string_data<'a>(asn1: *const sys::ASN1_STRING) -> &'a [u8] {
1359 let len = usize::try_from(sys::ASN1_STRING_length(asn1)).unwrap_or(0);
1360 let ptr = sys::ASN1_STRING_get0_data(asn1);
1361 if ptr.is_null() || len == 0 {
1362 return &[];
1363 }
1364 std::slice::from_raw_parts(ptr, len)
1366}
1367
1368#[cfg(test)]
1371mod tests {
1372 use super::*;
1373 use crate::pkey::{KeygenCtx, Pkey, Private, Public};
1374
1375 fn make_self_signed() -> (X509, Pkey<Private>, Pkey<Public>) {
1377 let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1378 let priv_key = kgen.generate().unwrap();
1379 let pub_key = Pkey::<Public>::from(priv_key.clone());
1380
1381 let mut name = X509NameOwned::new().unwrap();
1382 name.add_entry_by_txt(c"CN", b"Test Cert").unwrap();
1383 name.add_entry_by_txt(c"O", b"Example Org").unwrap();
1384
1385 let cert = X509Builder::new()
1386 .unwrap()
1387 .set_version(2)
1388 .unwrap()
1389 .set_serial_number(1)
1390 .unwrap()
1391 .set_not_before_offset(0)
1392 .unwrap()
1393 .set_not_after_offset(365 * 86400)
1394 .unwrap()
1395 .set_subject_name(&name)
1396 .unwrap()
1397 .set_issuer_name(&name)
1398 .unwrap()
1399 .set_public_key(&pub_key)
1400 .unwrap()
1401 .sign(&priv_key, None)
1402 .unwrap()
1403 .build();
1404
1405 (cert, priv_key, pub_key)
1406 }
1407
1408 #[test]
1409 fn build_and_verify_self_signed() {
1410 let (cert, _, pub_key) = make_self_signed();
1411 assert!(cert.verify(&pub_key).unwrap());
1412 assert!(cert.is_self_signed());
1413 }
1414
1415 #[test]
1416 fn pem_round_trip() {
1417 let (cert, _, _) = make_self_signed();
1418 let pem = cert.to_pem().unwrap();
1419 assert!(pem.starts_with(b"-----BEGIN CERTIFICATE-----"));
1420
1421 let cert2 = X509::from_pem(&pem).unwrap();
1422 assert_eq!(cert.to_der().unwrap(), cert2.to_der().unwrap());
1424 }
1425
1426 #[test]
1427 fn der_round_trip() {
1428 let (cert, _, _) = make_self_signed();
1429 let der = cert.to_der().unwrap();
1430 assert!(!der.is_empty());
1431
1432 let cert2 = X509::from_der(&der).unwrap();
1433 assert_eq!(cert2.to_der().unwrap(), der);
1434 }
1435
1436 #[test]
1437 fn subject_name_entries() {
1438 let (cert, _, _) = make_self_signed();
1439 let name = cert.subject_name();
1440
1441 assert_eq!(name.entry_count(), 2);
1442
1443 let e0 = name.entry(0).unwrap();
1445 assert_eq!(e0.nid(), 13); assert!(!e0.data().is_empty());
1447
1448 let s = name.to_string().unwrap();
1450 assert!(s.contains("Test Cert") || s.contains("CN=Test Cert"));
1451 }
1452
1453 #[test]
1454 fn serial_number() {
1455 let (cert, _, _) = make_self_signed();
1456 assert_eq!(cert.serial_number(), Some(1));
1457 }
1458
1459 #[test]
1460 fn validity_strings_present() {
1461 let (cert, _, _) = make_self_signed();
1462 let nb = cert.not_before_str().unwrap();
1463 let na = cert.not_after_str().unwrap();
1464 assert!(nb.contains("GMT"), "not_before_str = {nb:?}");
1466 assert!(na.contains("GMT"), "not_after_str = {na:?}");
1467 }
1468
1469 #[test]
1470 fn is_valid_now() {
1471 let (cert, _, _) = make_self_signed();
1472 assert!(cert.is_valid_now());
1473 }
1474
1475 #[test]
1476 fn public_key_extraction() {
1477 let (cert, _, pub_key) = make_self_signed();
1478 let extracted = cert.public_key().unwrap();
1479 assert!(extracted.is_a(c"ED25519"));
1481 assert_eq!(pub_key.bits(), extracted.bits());
1482 }
1483
1484 #[test]
1485 fn clone_cert() {
1486 let (cert, _, pub_key) = make_self_signed();
1487 let cert2 = cert.clone();
1488 assert_eq!(cert.to_der().unwrap(), cert2.to_der().unwrap());
1490 assert!(cert2.verify(&pub_key).unwrap());
1491 }
1492
1493 #[test]
1494 fn verify_fails_with_wrong_key() {
1495 let (cert, _, _) = make_self_signed();
1496 let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1498 let other_priv = kgen.generate().unwrap();
1499 let other_pub = Pkey::<Public>::from(other_priv);
1500
1501 assert!(!cert.verify(&other_pub).unwrap());
1503 }
1504
1505 #[test]
1508 fn x509_store_add_cert_and_verify() {
1509 let (cert, _, _) = make_self_signed();
1510
1511 let mut store = X509Store::new().unwrap();
1512 store.add_cert(&cert).unwrap();
1513
1514 let mut ctx = X509StoreCtx::new().unwrap();
1515 ctx.init(&store, &cert).unwrap();
1516 assert!(ctx.verify().unwrap());
1518 }
1519
1520 #[test]
1521 fn x509_store_verify_untrusted_fails() {
1522 let (cert, _, _) = make_self_signed();
1523 let store = X509Store::new().unwrap();
1525
1526 let mut ctx = X509StoreCtx::new().unwrap();
1527 ctx.init(&store, &cert).unwrap();
1528 assert!(!ctx.verify().unwrap());
1529 assert_ne!(ctx.error(), 0);
1531 }
1532
1533 #[test]
1534 fn x509_store_ctx_chain_populated_after_verify() {
1535 let (cert, _, _) = make_self_signed();
1536 let mut store = X509Store::new().unwrap();
1537 store.add_cert(&cert).unwrap();
1538
1539 let mut ctx = X509StoreCtx::new().unwrap();
1540 ctx.init(&store, &cert).unwrap();
1541 assert!(ctx.verify().unwrap());
1542
1543 let chain = ctx.chain();
1544 assert!(
1545 !chain.is_empty(),
1546 "verified chain should contain at least the leaf"
1547 );
1548 }
1549
1550 #[test]
1553 fn x509crl_new_roundtrip() {
1554 let crl = X509Crl::new().expect("X509_CRL_new should succeed");
1558 let _result = crl.to_der();
1561 drop(crl);
1562 }
1563
1564 #[test]
1565 fn x509crl_new_in_roundtrip() {
1566 use std::sync::Arc;
1567 let ctx = Arc::new(crate::lib_ctx::LibCtx::new().expect("LibCtx::new should succeed"));
1568 let crl = X509Crl::new_in(&ctx).expect("X509_CRL_new_ex should succeed");
1569 let _result = crl.to_der();
1570 drop(crl);
1571 }
1572
1573 const TEST_CRL_PEM: &[u8] = b"\
1575-----BEGIN X509 CRL-----\n\
1576MIIBVjBAMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBlJTQSBDQRcNMjYwNDE1\n\
1577MTUwNDEzWhcNMjYwNTE1MTUwNDEzWjANBgkqhkiG9w0BAQsFAAOCAQEAi209u0hh\n\
1578Vz42YaqLplQwBoYCjtjETenl4xXRNcFOYU6Y+FmR66XNGkl9HbPClrz3hRMnbBYr\n\
1579OQJfWQOKS9lS0zpEI4qtlH/H1JBNGwiY32HMqf5HULn0w0ARvmoXR4NzsCecK22G\n\
1580gN61k5FCCpPY8HztsuoHMHAQ65W1WfBiTWu8ZH0nCCU0CA4MSaPZUiNt8/mJZzTG\n\
1581UwTGcZ/hcHQMpocBX40nE7ta5opcIpjG+q2uiCWhXwoqmYsLvdJ+Obw20bLirMHt\n\
1582UsmESTw5G+vcRCudoiSw89Z/jzsYq8yuFhRzF9kA/RtqCoQ+ylQSSH5hxzW2+bPd\n\
1583QPHivSGDiUhH6Q==\n\
1584-----END X509 CRL-----\n";
1585
1586 #[test]
1587 fn crl_pem_round_trip() {
1588 let crl = X509Crl::from_pem(TEST_CRL_PEM).unwrap();
1589 let issuer = crl.issuer_name();
1591 assert!(issuer.entry_count() > 0);
1592 assert!(crl.last_update_str().is_some());
1594 assert!(crl.next_update_str().is_some());
1595 let pem = crl.to_pem().unwrap();
1597 assert!(pem.starts_with(b"-----BEGIN X509 CRL-----"));
1598 }
1599
1600 #[test]
1601 fn crl_der_round_trip() {
1602 let crl = X509Crl::from_pem(TEST_CRL_PEM).unwrap();
1603 let der = crl.to_der().unwrap();
1604 assert!(!der.is_empty());
1605 let crl2 = X509Crl::from_der(&der).unwrap();
1606 assert_eq!(crl2.to_der().unwrap(), der);
1607 }
1608
1609 #[test]
1610 fn crl_clone() {
1611 let crl = X509Crl::from_pem(TEST_CRL_PEM).unwrap();
1612 let crl2 = crl.clone();
1613 assert_eq!(crl.to_der().unwrap(), crl2.to_der().unwrap());
1614 }
1615
1616 #[test]
1619 fn x509_nid_from_short_name_known() {
1620 let nid = nid_from_short_name(c"SHA256");
1623 assert!(nid.is_some(), "SHA256 should be a known short name");
1624 assert_eq!(nid_from_short_name(c"CN"), Some(13));
1625 }
1626
1627 #[test]
1628 fn x509_nid_from_short_name_unknown() {
1629 assert_eq!(nid_from_short_name(c"not-a-real-algorithm-xyz"), None);
1630 }
1631
1632 #[test]
1633 fn x509_nid_from_text_dotted_oid() {
1634 assert_eq!(nid_from_text(c"2.5.4.3"), Some(13));
1636 }
1637
1638 #[test]
1639 fn x509_nid_from_text_short_name() {
1640 let via_sn = nid_from_short_name(c"SHA256");
1643 let via_txt = nid_from_text(c"SHA256");
1644 assert_eq!(
1645 via_sn, via_txt,
1646 "short-name lookup must agree between OBJ_sn2nid and OBJ_txt2nid"
1647 );
1648 }
1649
1650 #[test]
1651 fn x509_nid_from_text_unknown() {
1652 assert_eq!(nid_from_text(c"9.9.9.9.9.9.9.9"), None);
1653 }
1654
1655 #[test]
1658 fn x509_serial_number_bytes_small_serial() {
1659 let (cert, _, _) = make_self_signed();
1660 let bytes = cert.serial_number_bytes().unwrap();
1662 assert!(!bytes.is_empty());
1663 assert_eq!(*bytes.last().unwrap(), 1u8);
1664 }
1665
1666 #[test]
1667 fn x509_serial_number_bytes_consistent_with_serial_number() {
1668 let (cert, _, _) = make_self_signed();
1669 let bytes = cert.serial_number_bytes().unwrap();
1670 let n = cert.serial_number().unwrap();
1671 let be = n.to_be_bytes();
1673 let start = be.iter().position(|&b| b != 0).unwrap_or(7);
1675 assert_eq!(bytes, &be[start..]);
1676 }
1677
1678 #[test]
1681 fn x509_not_before_tm_is_some() {
1682 let (cert, _, _) = make_self_signed();
1683 let tm = cert.not_before_tm().expect("notBefore must be parseable");
1684 assert!(tm.year >= 2026, "year must be 2026 or later");
1686 assert!((1..=12).contains(&tm.month));
1687 assert!((1..=31).contains(&tm.day));
1688 }
1689
1690 #[test]
1691 fn x509_not_after_tm_one_year_after_not_before() {
1692 let (cert, _, _) = make_self_signed();
1693 let nb = cert.not_before_tm().unwrap();
1694 let na = cert.not_after_tm().unwrap();
1695 let year_diff = na.year - nb.year;
1698 assert!(year_diff == 0 || year_diff == 1, "year diff must be 0 or 1");
1699 }
1700
1701 #[test]
1702 fn x509_not_before_tm_consistent_with_not_before_str() {
1703 let (cert, _, _) = make_self_signed();
1704 assert!(cert.not_before_tm().is_some());
1706 assert!(cert.not_before_str().is_some());
1707 }
1708
1709 #[test]
1712 fn x509_public_key_is_a_ed25519() {
1713 let (cert, _, _) = make_self_signed();
1714 assert!(cert.public_key_is_a(c"ED25519"));
1715 assert!(!cert.public_key_is_a(c"RSA"));
1716 }
1717
1718 #[test]
1719 fn x509_public_key_bits_ed25519() {
1720 let (cert, _, _) = make_self_signed();
1721 let bits = cert.public_key_bits().unwrap();
1724 assert!(bits > 0, "Ed25519 key must report non-zero bit size");
1725 }
1726
1727 #[test]
1728 fn x509_public_key_bits_agrees_with_public_key_method() {
1729 let (cert, _, _) = make_self_signed();
1730 let owned_bits = cert.public_key().unwrap().bits();
1731 let borrow_bits = cert.public_key_bits().unwrap();
1732 assert_eq!(owned_bits, borrow_bits);
1733 }
1734
1735 #[test]
1738 fn nid_to_short_name_known_nid() {
1739 let sn = nid_to_short_name(13).expect("NID 13 must be known");
1741 assert_eq!(sn.to_bytes(), b"CN");
1742 }
1743
1744 #[test]
1745 fn nid_to_long_name_known_nid() {
1746 let ln = nid_to_long_name(13).expect("NID 13 must be known");
1747 assert_eq!(ln.to_bytes(), b"commonName");
1748 }
1749
1750 #[test]
1751 fn nid_to_short_name_unknown_nid() {
1752 assert!(nid_to_short_name(i32::MAX).is_none());
1754 }
1755
1756 #[test]
1757 fn nid_to_long_name_unknown_nid() {
1758 assert!(nid_to_long_name(i32::MAX).is_none());
1759 }
1760
1761 #[test]
1762 fn nid_to_short_name_sha256() {
1763 let sn = nid_to_short_name(672).expect("NID 672 (SHA256) must be known");
1765 assert_eq!(sn.to_bytes(), b"SHA256");
1766 }
1767
1768 #[test]
1771 fn x509_name_oneline_returns_string() {
1772 let (cert, _, _) = make_self_signed();
1773 let name = cert.subject_name();
1774 let s = name
1775 .to_oneline()
1776 .expect("to_oneline must return Some for a non-empty name");
1777 assert!(
1779 s.contains("CN="),
1780 "to_oneline output should contain CN=: {s:?}"
1781 );
1782 }
1783
1784 #[test]
1787 fn x509_new_in_lib_ctx() {
1788 use crate::lib_ctx::LibCtx;
1789 let ctx = Arc::new(LibCtx::new().expect("LibCtx::new must succeed"));
1790 let cert = X509::new_in(&ctx);
1791 assert!(
1792 cert.is_ok(),
1793 "X509::new_in must succeed with a valid LibCtx"
1794 );
1795 }
1796
1797 #[test]
1800 fn x509_extension_der_absent_nid_returns_none() {
1801 let (cert, _, _) = make_self_signed();
1802 let result = cert.extension_der(85);
1804 assert!(
1805 result.is_none(),
1806 "extension_der must return None for absent extension"
1807 );
1808 }
1809
1810 #[test]
1811 fn x509_extension_der_present_returns_some() {
1812 let (cert, _, _) = make_self_signed();
1815 let count = cert.extension_count();
1818 if count > 0 {
1819 let ext = cert
1820 .extension(0)
1821 .expect("extension(0) must be Some when count > 0");
1822 let nid = ext.nid();
1823 let der = cert
1824 .extension_der(nid)
1825 .expect("extension_der must return Some for a NID that exists in the cert");
1826 assert_eq!(
1828 der,
1829 ext.data(),
1830 "extension_der bytes must match X509Extension::data"
1831 );
1832 }
1833 }
1835}
1836
1837#[cfg(test)]
1838mod signature_info_tests {
1839 use super::*;
1840 use crate::pkey::{KeygenCtx, Pkey, Private, Public};
1841
1842 fn make_ed25519_cert() -> (X509, Pkey<Private>, Pkey<Public>) {
1843 let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1844 let priv_key = kgen.generate().unwrap();
1845 let pub_key = Pkey::<Public>::from(priv_key.clone());
1846
1847 let mut name = X509NameOwned::new().unwrap();
1848 name.add_entry_by_txt(c"CN", b"Ed25519 Sig Info Test")
1849 .unwrap();
1850
1851 let cert = X509Builder::new()
1852 .unwrap()
1853 .set_version(2)
1854 .unwrap()
1855 .set_serial_number(42)
1856 .unwrap()
1857 .set_not_before_offset(0)
1858 .unwrap()
1859 .set_not_after_offset(365 * 86400)
1860 .unwrap()
1861 .set_subject_name(&name)
1862 .unwrap()
1863 .set_issuer_name(&name)
1864 .unwrap()
1865 .set_public_key(&pub_key)
1866 .unwrap()
1867 .sign(&priv_key, None)
1868 .unwrap()
1869 .build();
1870
1871 (cert, priv_key, pub_key)
1872 }
1873
1874 #[test]
1878 fn x509_signature_info_ed25519() {
1879 let (cert, _, _) = make_ed25519_cert();
1880 let info = cert
1881 .signature_info()
1882 .expect("signature_info must succeed for a signed cert");
1883
1884 assert_eq!(
1886 info.md_nid, 0,
1887 "Ed25519 md_nid must be 0 (NID_undef); got {}",
1888 info.md_nid
1889 );
1890
1891 assert_ne!(info.pk_nid, 0, "Ed25519 pk_nid must not be NID_undef");
1893 let sn = nid_to_short_name(info.pk_nid)
1894 .expect("Ed25519 pk_nid must have a short name in OpenSSL's OBJ table");
1895 assert_eq!(
1896 sn.to_bytes(),
1897 b"ED25519",
1898 "pk_nid short name must be ED25519; got {sn:?}"
1899 );
1900
1901 assert_eq!(
1903 info.security_bits, 128,
1904 "Ed25519 security bits must be 128; got {}",
1905 info.security_bits
1906 );
1907 }
1908}