1#![deny(missing_docs)]
2
3use foreign_types::{ForeignType, ForeignTypeRef};
28use libc::{c_char, c_int, c_long, c_void, time_t};
29use std::cmp::Ordering;
30use std::convert::TryInto;
31use std::ffi::CString;
32use std::fmt;
33use std::ptr;
34use std::str;
35
36use crate::bio::MemBio;
37use crate::bn::{BigNum, BigNumRef};
38use crate::error::ErrorStack;
39use crate::nid::Nid;
40use crate::stack::Stackable;
41use crate::string::OpensslString;
42use crate::{cvt, cvt_p, util};
43use openssl_macros::corresponds;
44
45foreign_type_and_impl_send_sync! {
46 type CType = ffi::ASN1_GENERALIZEDTIME;
47 fn drop = ffi::ASN1_GENERALIZEDTIME_free;
48
49 pub struct Asn1GeneralizedTime;
61 pub struct Asn1GeneralizedTimeRef;
65}
66
67impl fmt::Display for Asn1GeneralizedTimeRef {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 unsafe {
70 let mem_bio = match MemBio::new() {
71 Err(_) => return f.write_str("error"),
72 Ok(m) => m,
73 };
74 let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
75 mem_bio.as_ptr(),
76 self.as_ptr(),
77 ));
78 match print_result {
79 Err(_) => f.write_str("error"),
80 Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
81 }
82 }
83 }
84}
85
86impl Asn1GeneralizedTime {
87 #[corresponds(ASN1_GENERALIZEDTIME_set_string)]
90 #[allow(clippy::should_implement_trait)]
91 pub fn from_str(s: &str) -> Result<Asn1GeneralizedTime, ErrorStack> {
92 unsafe {
93 ffi::init();
94
95 let time_str = CString::new(s).unwrap();
96 let ptr = cvt_p(ffi::ASN1_GENERALIZEDTIME_new())?;
97 let time = Asn1GeneralizedTime::from_ptr(ptr);
98
99 cvt(ffi::ASN1_GENERALIZEDTIME_set_string(
100 time.as_ptr(),
101 time_str.as_ptr(),
102 ))?;
103
104 Ok(time)
105 }
106 }
107}
108
109#[derive(Debug, Copy, Clone, PartialEq, Eq)]
111pub struct Asn1Type(c_int);
112
113#[allow(missing_docs)] impl Asn1Type {
115 pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC);
116
117 pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN);
118
119 pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER);
120
121 pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING);
122
123 pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING);
124
125 pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL);
126
127 pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT);
128
129 pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR);
130
131 pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL);
132
133 pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL);
134
135 pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED);
136
137 pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING);
138
139 pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE);
140
141 pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET);
142
143 pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING);
144
145 pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING);
146
147 pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING);
148
149 pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING);
150
151 pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING);
152
153 pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING);
154
155 pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME);
156
157 pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME);
158
159 pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING);
160
161 pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING);
162
163 pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING);
164
165 pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING);
166
167 pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING);
168
169 pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING);
170
171 pub fn from_raw(value: c_int) -> Self {
173 Asn1Type(value)
174 }
175
176 pub fn as_raw(&self) -> c_int {
178 self.0
179 }
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190pub struct TimeDiff {
191 pub days: c_int,
193 pub secs: c_int,
197}
198
199foreign_type_and_impl_send_sync! {
200 type CType = ffi::ASN1_TIME;
201 fn drop = ffi::ASN1_TIME_free;
202 pub struct Asn1Time;
213 pub struct Asn1TimeRef;
217}
218
219impl Asn1TimeRef {
220 #[corresponds(ASN1_TIME_diff)]
222 pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
223 let mut days = 0;
224 let mut secs = 0;
225 let other = compare.as_ptr();
226
227 let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) };
228
229 match err {
230 0 => Err(ErrorStack::get()),
231 _ => Ok(TimeDiff { days, secs }),
232 }
233 }
234
235 #[corresponds(ASN1_TIME_compare)]
237 pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
238 let d = self.diff(other)?;
239 if d.days > 0 || d.secs > 0 {
240 return Ok(Ordering::Less);
241 }
242 if d.days < 0 || d.secs < 0 {
243 return Ok(Ordering::Greater);
244 }
245
246 Ok(Ordering::Equal)
247 }
248}
249
250impl PartialEq for Asn1TimeRef {
251 fn eq(&self, other: &Asn1TimeRef) -> bool {
252 self.diff(other)
253 .map(|t| t.days == 0 && t.secs == 0)
254 .unwrap_or(false)
255 }
256}
257
258impl PartialEq<Asn1Time> for Asn1TimeRef {
259 fn eq(&self, other: &Asn1Time) -> bool {
260 self.diff(other)
261 .map(|t| t.days == 0 && t.secs == 0)
262 .unwrap_or(false)
263 }
264}
265
266impl PartialEq<Asn1Time> for &Asn1TimeRef {
267 fn eq(&self, other: &Asn1Time) -> bool {
268 self.diff(other)
269 .map(|t| t.days == 0 && t.secs == 0)
270 .unwrap_or(false)
271 }
272}
273
274impl PartialOrd for Asn1TimeRef {
275 fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
276 self.compare(other).ok()
277 }
278}
279
280impl PartialOrd<Asn1Time> for Asn1TimeRef {
281 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
282 self.compare(other).ok()
283 }
284}
285
286impl PartialOrd<Asn1Time> for &Asn1TimeRef {
287 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
288 self.compare(other).ok()
289 }
290}
291
292impl fmt::Display for Asn1TimeRef {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 unsafe {
295 let mem_bio = match MemBio::new() {
296 Err(_) => return f.write_str("error"),
297 Ok(m) => m,
298 };
299 let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()));
300 match print_result {
301 Err(_) => f.write_str("error"),
302 Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
303 }
304 }
305 }
306}
307
308impl fmt::Debug for Asn1TimeRef {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 f.write_str(&self.to_string())
311 }
312}
313
314impl Asn1Time {
315 #[corresponds(ASN1_TIME_new)]
316 fn new() -> Result<Asn1Time, ErrorStack> {
317 ffi::init();
318
319 unsafe {
320 let handle = cvt_p(ffi::ASN1_TIME_new())?;
321 Ok(Asn1Time::from_ptr(handle))
322 }
323 }
324
325 #[corresponds(X509_gmtime_adj)]
326 fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
327 ffi::init();
328
329 unsafe {
330 let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?;
331 Ok(Asn1Time::from_ptr(handle))
332 }
333 }
334
335 pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
337 Asn1Time::from_period(days as c_long * 60 * 60 * 24)
338 }
339
340 #[corresponds(ASN1_TIME_set)]
342 pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
343 ffi::init();
344
345 unsafe {
346 let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
347 Ok(Asn1Time::from_ptr(handle))
348 }
349 }
350
351 #[corresponds(ASN1_TIME_set_string)]
353 #[allow(clippy::should_implement_trait)]
354 pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
355 unsafe {
356 let s = CString::new(s).unwrap();
357
358 let time = Asn1Time::new()?;
359 cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
360
361 Ok(time)
362 }
363 }
364
365 #[corresponds(ASN1_TIME_set_string_X509)]
369 #[cfg(any(ossl111, boringssl, libressl360, awslc))]
370 pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> {
371 unsafe {
372 let s = CString::new(s).unwrap();
373
374 let time = Asn1Time::new()?;
375 cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?;
376
377 Ok(time)
378 }
379 }
380}
381
382impl PartialEq for Asn1Time {
383 fn eq(&self, other: &Asn1Time) -> bool {
384 self.diff(other)
385 .map(|t| t.days == 0 && t.secs == 0)
386 .unwrap_or(false)
387 }
388}
389
390impl PartialEq<Asn1TimeRef> for Asn1Time {
391 fn eq(&self, other: &Asn1TimeRef) -> bool {
392 self.diff(other)
393 .map(|t| t.days == 0 && t.secs == 0)
394 .unwrap_or(false)
395 }
396}
397
398impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
399 fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
400 self.diff(other)
401 .map(|t| t.days == 0 && t.secs == 0)
402 .unwrap_or(false)
403 }
404}
405
406impl PartialOrd for Asn1Time {
407 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
408 self.compare(other).ok()
409 }
410}
411
412impl PartialOrd<Asn1TimeRef> for Asn1Time {
413 fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
414 self.compare(other).ok()
415 }
416}
417
418impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
419 fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
420 self.compare(other).ok()
421 }
422}
423
424foreign_type_and_impl_send_sync! {
425 type CType = ffi::ASN1_STRING;
426 fn drop = ffi::ASN1_STRING_free;
427 pub struct Asn1String;
435 pub struct Asn1StringRef;
437}
438
439impl Asn1StringRef {
440 #[corresponds(ASN1_STRING_to_UTF8)]
446 #[deprecated(
447 since = "0.10.81",
448 note = "truncates at the first interior NUL byte; use `to_string` instead"
449 )]
450 pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
451 unsafe {
452 let mut ptr = ptr::null_mut();
453 let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
454 if len < 0 {
455 return Err(ErrorStack::get());
456 }
457
458 Ok(OpensslString::from_ptr(ptr as *mut c_char))
459 }
460 }
461
462 #[corresponds(ASN1_STRING_to_UTF8)]
472 pub fn to_string(&self) -> Result<String, ErrorStack> {
473 match unsafe { ffi::ASN1_STRING_type(self.as_ptr()) } {
477 ffi::V_ASN1_UTF8STRING => {
478 if let Ok(s) = str::from_utf8(self.as_slice()) {
479 return Ok(s.to_owned());
480 }
481 }
482 ffi::V_ASN1_NUMERICSTRING
484 | ffi::V_ASN1_PRINTABLESTRING
485 | ffi::V_ASN1_T61STRING
486 | ffi::V_ASN1_IA5STRING
487 | ffi::V_ASN1_VISIBLESTRING => {
488 let slice = self.as_slice();
489 if slice.is_ascii() {
490 return Ok(unsafe { str::from_utf8_unchecked(slice) }.to_owned());
492 }
493 }
494 _ => {}
495 }
496
497 unsafe {
498 let mut ptr = ptr::null_mut();
499 let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
500 if len < 0 {
501 return Err(ErrorStack::get());
502 }
503
504 let s = String::from_utf8_lossy(util::from_raw_parts(ptr, len as usize)).into_owned();
509 openssl_free(ptr.cast());
510 Ok(s)
511 }
512 }
513
514 #[corresponds(ASN1_STRING_get0_data)]
521 pub fn as_slice(&self) -> &[u8] {
522 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
523 }
524
525 #[corresponds(ASN1_STRING_length)]
527 pub fn len(&self) -> usize {
528 unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
529 }
530
531 pub fn is_empty(&self) -> bool {
533 self.len() == 0
534 }
535}
536
537impl fmt::Debug for Asn1StringRef {
538 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
539 match self.to_string() {
540 Ok(string) => string.fmt(fmt),
541 Err(_) => fmt.write_str("error"),
542 }
543 }
544}
545
546#[inline]
547#[cfg(not(any(boringssl, awslc)))]
548unsafe fn openssl_free(buf: *mut c_void) {
549 ffi::OPENSSL_free(buf);
550}
551
552#[inline]
553#[cfg(any(boringssl, awslc))]
554unsafe fn openssl_free(buf: *mut c_void) {
555 ffi::CRYPTO_free(
556 buf,
557 concat!(file!(), "\0").as_ptr() as *const c_char,
558 line!() as c_int,
559 );
560}
561
562foreign_type_and_impl_send_sync! {
563 type CType = ffi::ASN1_INTEGER;
564 fn drop = ffi::ASN1_INTEGER_free;
565
566 pub struct Asn1Integer;
576 pub struct Asn1IntegerRef;
578}
579
580impl Asn1Integer {
581 pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
589 bn.to_asn1_integer()
590 }
591}
592
593impl Ord for Asn1Integer {
594 fn cmp(&self, other: &Self) -> Ordering {
595 Asn1IntegerRef::cmp(self, other)
596 }
597}
598impl PartialOrd for Asn1Integer {
599 fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> {
600 Some(self.cmp(other))
601 }
602}
603impl Eq for Asn1Integer {}
604impl PartialEq for Asn1Integer {
605 fn eq(&self, other: &Asn1Integer) -> bool {
606 Asn1IntegerRef::eq(self, other)
607 }
608}
609
610impl Asn1IntegerRef {
611 #[allow(missing_docs, clippy::unnecessary_cast)]
612 #[deprecated(since = "0.10.6", note = "use to_bn instead")]
613 pub fn get(&self) -> i64 {
614 unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
615 }
616
617 #[corresponds(ASN1_INTEGER_to_BN)]
619 pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
620 unsafe {
621 cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut()))
622 .map(|p| BigNum::from_ptr(p))
623 }
624 }
625
626 #[corresponds(ASN1_INTEGER_set)]
631 pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
632 unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
633 }
634
635 #[corresponds(ASN1_INTEGER_dup)]
637 pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> {
638 unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) }
639 }
640}
641
642impl Ord for Asn1IntegerRef {
643 fn cmp(&self, other: &Self) -> Ordering {
644 let res = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), other.as_ptr()) };
645 res.cmp(&0)
646 }
647}
648impl PartialOrd for Asn1IntegerRef {
649 fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> {
650 Some(self.cmp(other))
651 }
652}
653impl Eq for Asn1IntegerRef {}
654impl PartialEq for Asn1IntegerRef {
655 fn eq(&self, other: &Asn1IntegerRef) -> bool {
656 self.cmp(other) == Ordering::Equal
657 }
658}
659
660foreign_type_and_impl_send_sync! {
661 type CType = ffi::ASN1_BIT_STRING;
662 fn drop = ffi::ASN1_BIT_STRING_free;
663 pub struct Asn1BitString;
670 pub struct Asn1BitStringRef;
672}
673
674impl Asn1BitStringRef {
675 #[corresponds(ASN1_STRING_get0_data)]
677 pub fn as_slice(&self) -> &[u8] {
678 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
679 }
680
681 #[corresponds(ASN1_STRING_length)]
683 pub fn len(&self) -> usize {
684 unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
685 }
686
687 pub fn is_empty(&self) -> bool {
689 self.len() == 0
690 }
691}
692
693foreign_type_and_impl_send_sync! {
694 type CType = ffi::ASN1_OCTET_STRING;
695 fn drop = ffi::ASN1_OCTET_STRING_free;
696 pub struct Asn1OctetString;
698 pub struct Asn1OctetStringRef;
700}
701
702impl Asn1OctetString {
703 pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> {
705 ffi::init();
706 unsafe {
707 let s = cvt_p(ffi::ASN1_OCTET_STRING_new())?;
708 ffi::ASN1_OCTET_STRING_set(s, value.as_ptr(), value.len().try_into().unwrap());
709 Ok(Self::from_ptr(s))
710 }
711 }
712}
713
714impl Asn1OctetStringRef {
715 #[corresponds(ASN1_STRING_get0_data)]
717 pub fn as_slice(&self) -> &[u8] {
718 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) }
719 }
720
721 #[corresponds(ASN1_STRING_length)]
723 pub fn len(&self) -> usize {
724 unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize }
725 }
726
727 pub fn is_empty(&self) -> bool {
729 self.len() == 0
730 }
731}
732
733foreign_type_and_impl_send_sync! {
734 type CType = ffi::ASN1_OBJECT;
735 fn drop = ffi::ASN1_OBJECT_free;
736 fn clone = ffi::OBJ_dup;
737
738 pub struct Asn1Object;
752 pub struct Asn1ObjectRef;
754}
755
756impl Stackable for Asn1Object {
757 type StackType = ffi::stack_st_ASN1_OBJECT;
758}
759
760impl Asn1Object {
761 #[corresponds(OBJ_txt2obj)]
763 #[allow(clippy::should_implement_trait)]
764 pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> {
765 unsafe {
766 ffi::init();
767 let txt = CString::new(txt).unwrap();
768 let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?;
769 Ok(Asn1Object::from_ptr(obj))
770 }
771 }
772
773 #[corresponds(OBJ_get0_data)]
778 #[cfg(ossl111)]
779 pub fn as_slice(&self) -> &[u8] {
780 unsafe {
781 let len = ffi::OBJ_length(self.as_ptr());
782 util::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len)
783 }
784 }
785}
786
787impl Asn1ObjectRef {
788 pub fn nid(&self) -> Nid {
790 unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
791 }
792}
793
794impl fmt::Display for Asn1ObjectRef {
795 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
796 unsafe {
797 let mut buf = [0; 80];
798 let mut clamped = false;
799 let mut len = ffi::OBJ_obj2txt(
800 buf.as_mut_ptr() as *mut _,
801 buf.len() as c_int,
802 self.as_ptr(),
803 0,
804 );
805 if len <= 0 {
806 return fmt.write_str("OBJ_obj2txt error");
807 }
808 if len > buf.len() as i32 {
809 len = (buf.len() - 1) as i32;
811 clamped = true;
812 }
813 match str::from_utf8(&buf[..len as usize]) {
814 Err(_) => fmt.write_str("error"),
815 Ok(s) => {
816 if clamped {
817 fmt.write_str(&(s.to_owned() + "..."))
818 } else {
819 fmt.write_str(s)
820 }
821 }
822 }
823 }
824 }
825}
826
827impl fmt::Debug for Asn1ObjectRef {
828 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
829 fmt.write_str(self.to_string().as_str())
830 }
831}
832
833use ffi::ASN1_STRING_get0_data;
834
835foreign_type_and_impl_send_sync! {
836 type CType = ffi::ASN1_ENUMERATED;
837 fn drop = ffi::ASN1_ENUMERATED_free;
838
839 pub struct Asn1Enumerated;
841 pub struct Asn1EnumeratedRef;
843}
844
845impl Asn1EnumeratedRef {
846 #[corresponds(ASN1_ENUMERATED_get_int64)]
848 #[cfg(ossl110)]
849 pub fn get_i64(&self) -> Result<i64, ErrorStack> {
850 let mut crl_reason = 0;
851 unsafe {
852 cvt(ffi::ASN1_ENUMERATED_get_int64(
853 &mut crl_reason,
854 self.as_ptr(),
855 ))?;
856 }
857 Ok(crl_reason)
858 }
859}
860
861#[cfg(test)]
862mod tests {
863 use super::*;
864
865 use crate::bn::BigNum;
866 use crate::nid::Nid;
867
868 #[test]
870 fn bn_cvt() {
871 fn roundtrip(bn: BigNum) {
872 let large = Asn1Integer::from_bn(&bn).unwrap();
873 assert_eq!(large.to_bn().unwrap(), bn);
874 }
875
876 roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
877 roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
878 roundtrip(BigNum::from_u32(1234).unwrap());
879 roundtrip(-BigNum::from_u32(1234).unwrap());
880 }
881
882 #[test]
884 fn string_with_interior_nul() {
885 fn make_string(typ: c_int, data: &[u8]) -> Asn1String {
886 unsafe {
887 let ptr = cvt_p(ffi::ASN1_STRING_type_new(typ)).unwrap();
888 let s = Asn1String::from_ptr(ptr);
889 cvt(ffi::ASN1_STRING_set(
890 s.as_ptr(),
891 data.as_ptr().cast(),
892 data.len().try_into().unwrap(),
893 ))
894 .unwrap();
895 s
896 }
897 }
898
899 let s = make_string(ffi::V_ASN1_UTF8STRING, b"foo\0bar.com");
901 assert_eq!(s.as_slice(), b"foo\0bar.com");
902 assert_eq!(s.to_string().unwrap(), "foo\0bar.com");
903
904 let s = make_string(ffi::V_ASN1_IA5STRING, b"foo\0bar.com");
905 assert_eq!(s.to_string().unwrap(), "foo\0bar.com");
906
907 let s = make_string(ffi::V_ASN1_BMPSTRING, b"\0f\0\0\0o");
909 assert_eq!(s.to_string().unwrap(), "f\0o");
910 }
911
912 #[test]
913 fn time_from_str() {
914 Asn1Time::from_str("99991231235959Z").unwrap();
915 #[cfg(any(ossl111, boringssl, libressl360, awslc))]
916 Asn1Time::from_str_x509("99991231235959Z").unwrap();
917 }
918
919 #[test]
920 fn generalized_time_from_str() {
921 let time = Asn1GeneralizedTime::from_str("99991231235959Z").unwrap();
922 assert_eq!("Dec 31 23:59:59 9999 GMT", time.to_string());
923 }
924
925 #[test]
926 fn time_from_unix() {
927 let t = Asn1Time::from_unix(0).unwrap();
928 assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string());
929 }
930
931 #[test]
932 fn time_eq() {
933 let a = Asn1Time::from_str("99991231235959Z").unwrap();
934 let b = Asn1Time::from_str("99991231235959Z").unwrap();
935 let c = Asn1Time::from_str("99991231235958Z").unwrap();
936 let a_ref = a.as_ref();
937 let b_ref = b.as_ref();
938 let c_ref = c.as_ref();
939 assert!(a == b);
940 assert!(a != c);
941 assert!(a == b_ref);
942 assert!(a != c_ref);
943 assert!(b_ref == a);
944 assert!(c_ref != a);
945 assert!(a_ref == b_ref);
946 assert!(a_ref != c_ref);
947 }
948
949 #[test]
950 fn time_ord() {
951 let a = Asn1Time::from_str("99991231235959Z").unwrap();
952 let b = Asn1Time::from_str("99991231235959Z").unwrap();
953 let c = Asn1Time::from_str("99991231235958Z").unwrap();
954 let a_ref = a.as_ref();
955 let b_ref = b.as_ref();
956 let c_ref = c.as_ref();
957 assert!(a >= b);
958 assert!(a > c);
959 assert!(b <= a);
960 assert!(c < a);
961
962 assert!(a_ref >= b);
963 assert!(a_ref > c);
964 assert!(b_ref <= a);
965 assert!(c_ref < a);
966
967 assert!(a >= b_ref);
968 assert!(a > c_ref);
969 assert!(b <= a_ref);
970 assert!(c < a_ref);
971
972 assert!(a_ref >= b_ref);
973 assert!(a_ref > c_ref);
974 assert!(b_ref <= a_ref);
975 assert!(c_ref < a_ref);
976 }
977
978 #[test]
979 fn integer_to_owned() {
980 let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
981 let b = a.to_owned().unwrap();
982 assert_eq!(
983 a.to_bn().unwrap().to_dec_str().unwrap().to_string(),
984 b.to_bn().unwrap().to_dec_str().unwrap().to_string(),
985 );
986 assert_ne!(a.as_ptr(), b.as_ptr());
987 }
988
989 #[test]
990 fn integer_cmp() {
991 let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
992 let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
993 let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43").unwrap()).unwrap();
994 assert!(a == b);
995 assert!(a != c);
996 assert!(a < c);
997 assert!(c > b);
998 }
999
1000 #[test]
1001 fn object_from_str() {
1002 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
1003 assert_eq!(object.nid(), Nid::SHA256);
1004 }
1005
1006 #[test]
1007 fn object_from_str_with_invalid_input() {
1008 Asn1Object::from_str("NOT AN OID")
1009 .map(|object| object.to_string())
1010 .expect_err("parsing invalid OID should fail");
1011 }
1012
1013 #[test]
1014 fn very_long_object() {
1015 let fifty_ones = "1.".repeat(49) + "1";
1016 let object = Asn1Object::from_str(&fifty_ones).unwrap();
1017 assert_eq!(object.as_ref().to_string(), "1.".repeat(40) + "..");
1018 }
1019
1020 #[test]
1021 #[cfg(ossl111)]
1022 fn object_to_slice() {
1023 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
1024 assert_eq!(
1025 object.as_slice(),
1026 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01],
1027 );
1028 }
1029
1030 #[test]
1031 fn asn1_octet_string() {
1032 let octet_string = Asn1OctetString::new_from_bytes(b"hello world").unwrap();
1033 assert_eq!(octet_string.as_slice(), b"hello world");
1034 assert_eq!(octet_string.len(), 11);
1035 }
1036}