1#![deny(missing_docs)]
2
3use cfg_if::cfg_if;
28use foreign_types::{ForeignType, ForeignTypeRef};
29use libc::{c_char, c_int, c_long, time_t};
30use std::cmp::Ordering;
31use std::convert::TryInto;
32use std::ffi::CString;
33use std::fmt;
34use std::ptr;
35use std::str;
36
37use crate::bio::MemBio;
38use crate::bn::{BigNum, BigNumRef};
39use crate::error::ErrorStack;
40use crate::nid::Nid;
41use crate::stack::Stackable;
42use crate::string::OpensslString;
43use crate::{cvt, cvt_p, util};
44use openssl_macros::corresponds;
45
46foreign_type_and_impl_send_sync! {
47 type CType = ffi::ASN1_GENERALIZEDTIME;
48 fn drop = ffi::ASN1_GENERALIZEDTIME_free;
49
50 pub struct Asn1GeneralizedTime;
62 pub struct Asn1GeneralizedTimeRef;
66}
67
68impl fmt::Display for Asn1GeneralizedTimeRef {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 unsafe {
71 let mem_bio = match MemBio::new() {
72 Err(_) => return f.write_str("error"),
73 Ok(m) => m,
74 };
75 let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
76 mem_bio.as_ptr(),
77 self.as_ptr(),
78 ));
79 match print_result {
80 Err(_) => f.write_str("error"),
81 Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
82 }
83 }
84 }
85}
86
87impl Asn1GeneralizedTime {
88 #[corresponds(ASN1_GENERALIZEDTIME_set_string)]
91 #[allow(clippy::should_implement_trait)]
92 pub fn from_str(s: &str) -> Result<Asn1GeneralizedTime, ErrorStack> {
93 unsafe {
94 ffi::init();
95
96 let time_str = CString::new(s).unwrap();
97 let ptr = cvt_p(ffi::ASN1_GENERALIZEDTIME_new())?;
98 let time = Asn1GeneralizedTime::from_ptr(ptr);
99
100 cvt(ffi::ASN1_GENERALIZEDTIME_set_string(
101 time.as_ptr(),
102 time_str.as_ptr(),
103 ))?;
104
105 Ok(time)
106 }
107 }
108}
109
110#[derive(Debug, Copy, Clone, PartialEq, Eq)]
112pub struct Asn1Type(c_int);
113
114#[allow(missing_docs)] impl Asn1Type {
116 pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC);
117
118 pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN);
119
120 pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER);
121
122 pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING);
123
124 pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING);
125
126 pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL);
127
128 pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT);
129
130 pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR);
131
132 pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL);
133
134 pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL);
135
136 pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED);
137
138 pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING);
139
140 pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE);
141
142 pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET);
143
144 pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING);
145
146 pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING);
147
148 pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING);
149
150 pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING);
151
152 pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING);
153
154 pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING);
155
156 pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME);
157
158 pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME);
159
160 pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING);
161
162 pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING);
163
164 pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING);
165
166 pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING);
167
168 pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING);
169
170 pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING);
171
172 pub fn from_raw(value: c_int) -> Self {
174 Asn1Type(value)
175 }
176
177 pub fn as_raw(&self) -> c_int {
179 self.0
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
191pub struct TimeDiff {
192 pub days: c_int,
194 pub secs: c_int,
198}
199
200foreign_type_and_impl_send_sync! {
201 type CType = ffi::ASN1_TIME;
202 fn drop = ffi::ASN1_TIME_free;
203 pub struct Asn1Time;
214 pub struct Asn1TimeRef;
218}
219
220impl Asn1TimeRef {
221 #[corresponds(ASN1_TIME_diff)]
223 pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
224 let mut days = 0;
225 let mut secs = 0;
226 let other = compare.as_ptr();
227
228 let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) };
229
230 match err {
231 0 => Err(ErrorStack::get()),
232 _ => Ok(TimeDiff { days, secs }),
233 }
234 }
235
236 #[corresponds(ASN1_TIME_compare)]
238 pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
239 let d = self.diff(other)?;
240 if d.days > 0 || d.secs > 0 {
241 return Ok(Ordering::Less);
242 }
243 if d.days < 0 || d.secs < 0 {
244 return Ok(Ordering::Greater);
245 }
246
247 Ok(Ordering::Equal)
248 }
249}
250
251impl PartialEq for Asn1TimeRef {
252 fn eq(&self, other: &Asn1TimeRef) -> bool {
253 self.diff(other)
254 .map(|t| t.days == 0 && t.secs == 0)
255 .unwrap_or(false)
256 }
257}
258
259impl PartialEq<Asn1Time> for Asn1TimeRef {
260 fn eq(&self, other: &Asn1Time) -> bool {
261 self.diff(other)
262 .map(|t| t.days == 0 && t.secs == 0)
263 .unwrap_or(false)
264 }
265}
266
267impl PartialEq<Asn1Time> for &Asn1TimeRef {
268 fn eq(&self, other: &Asn1Time) -> bool {
269 self.diff(other)
270 .map(|t| t.days == 0 && t.secs == 0)
271 .unwrap_or(false)
272 }
273}
274
275impl PartialOrd for Asn1TimeRef {
276 fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
277 self.compare(other).ok()
278 }
279}
280
281impl PartialOrd<Asn1Time> for Asn1TimeRef {
282 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
283 self.compare(other).ok()
284 }
285}
286
287impl PartialOrd<Asn1Time> for &Asn1TimeRef {
288 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
289 self.compare(other).ok()
290 }
291}
292
293impl fmt::Display for Asn1TimeRef {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 unsafe {
296 let mem_bio = match MemBio::new() {
297 Err(_) => return f.write_str("error"),
298 Ok(m) => m,
299 };
300 let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()));
301 match print_result {
302 Err(_) => f.write_str("error"),
303 Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
304 }
305 }
306 }
307}
308
309impl fmt::Debug for Asn1TimeRef {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 f.write_str(&self.to_string())
312 }
313}
314
315impl Asn1Time {
316 #[corresponds(ASN1_TIME_new)]
317 fn new() -> Result<Asn1Time, ErrorStack> {
318 ffi::init();
319
320 unsafe {
321 let handle = cvt_p(ffi::ASN1_TIME_new())?;
322 Ok(Asn1Time::from_ptr(handle))
323 }
324 }
325
326 #[corresponds(X509_gmtime_adj)]
327 fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
328 ffi::init();
329
330 unsafe {
331 let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?;
332 Ok(Asn1Time::from_ptr(handle))
333 }
334 }
335
336 pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
338 Asn1Time::from_period(days as c_long * 60 * 60 * 24)
339 }
340
341 #[corresponds(ASN1_TIME_set)]
343 pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
344 ffi::init();
345
346 unsafe {
347 let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
348 Ok(Asn1Time::from_ptr(handle))
349 }
350 }
351
352 #[corresponds(ASN1_TIME_set_string)]
354 #[allow(clippy::should_implement_trait)]
355 pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
356 unsafe {
357 let s = CString::new(s).unwrap();
358
359 let time = Asn1Time::new()?;
360 cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
361
362 Ok(time)
363 }
364 }
365
366 #[corresponds(ASN1_TIME_set_string_X509)]
370 #[cfg(any(ossl111, boringssl, awslc))]
371 pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> {
372 unsafe {
373 let s = CString::new(s).unwrap();
374
375 let time = Asn1Time::new()?;
376 cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?;
377
378 Ok(time)
379 }
380 }
381}
382
383impl PartialEq for Asn1Time {
384 fn eq(&self, other: &Asn1Time) -> bool {
385 self.diff(other)
386 .map(|t| t.days == 0 && t.secs == 0)
387 .unwrap_or(false)
388 }
389}
390
391impl PartialEq<Asn1TimeRef> for Asn1Time {
392 fn eq(&self, other: &Asn1TimeRef) -> bool {
393 self.diff(other)
394 .map(|t| t.days == 0 && t.secs == 0)
395 .unwrap_or(false)
396 }
397}
398
399impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
400 fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
401 self.diff(other)
402 .map(|t| t.days == 0 && t.secs == 0)
403 .unwrap_or(false)
404 }
405}
406
407impl PartialOrd for Asn1Time {
408 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
409 self.compare(other).ok()
410 }
411}
412
413impl PartialOrd<Asn1TimeRef> for Asn1Time {
414 fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
415 self.compare(other).ok()
416 }
417}
418
419impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
420 fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
421 self.compare(other).ok()
422 }
423}
424
425foreign_type_and_impl_send_sync! {
426 type CType = ffi::ASN1_STRING;
427 fn drop = ffi::ASN1_STRING_free;
428 pub struct Asn1String;
436 pub struct Asn1StringRef;
438}
439
440impl Asn1StringRef {
441 #[corresponds(ASN1_STRING_to_UTF8)]
447 pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
448 unsafe {
449 let mut ptr = ptr::null_mut();
450 let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
451 if len < 0 {
452 return Err(ErrorStack::get());
453 }
454
455 Ok(OpensslString::from_ptr(ptr as *mut c_char))
456 }
457 }
458
459 #[corresponds(ASN1_STRING_get0_data)]
466 pub fn as_slice(&self) -> &[u8] {
467 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
468 }
469
470 #[corresponds(ASN1_STRING_length)]
472 pub fn len(&self) -> usize {
473 unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
474 }
475
476 pub fn is_empty(&self) -> bool {
478 self.len() == 0
479 }
480}
481
482impl fmt::Debug for Asn1StringRef {
483 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
484 match self.as_utf8() {
485 Ok(openssl_string) => openssl_string.fmt(fmt),
486 Err(_) => fmt.write_str("error"),
487 }
488 }
489}
490
491foreign_type_and_impl_send_sync! {
492 type CType = ffi::ASN1_INTEGER;
493 fn drop = ffi::ASN1_INTEGER_free;
494
495 pub struct Asn1Integer;
505 pub struct Asn1IntegerRef;
507}
508
509impl Asn1Integer {
510 pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
518 bn.to_asn1_integer()
519 }
520}
521
522impl Ord for Asn1Integer {
523 fn cmp(&self, other: &Self) -> Ordering {
524 Asn1IntegerRef::cmp(self, other)
525 }
526}
527impl PartialOrd for Asn1Integer {
528 fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> {
529 Some(self.cmp(other))
530 }
531}
532impl Eq for Asn1Integer {}
533impl PartialEq for Asn1Integer {
534 fn eq(&self, other: &Asn1Integer) -> bool {
535 Asn1IntegerRef::eq(self, other)
536 }
537}
538
539impl Asn1IntegerRef {
540 #[allow(missing_docs, clippy::unnecessary_cast)]
541 #[deprecated(since = "0.10.6", note = "use to_bn instead")]
542 pub fn get(&self) -> i64 {
543 unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
544 }
545
546 #[corresponds(ASN1_INTEGER_to_BN)]
548 pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
549 unsafe {
550 cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut()))
551 .map(|p| BigNum::from_ptr(p))
552 }
553 }
554
555 #[corresponds(ASN1_INTEGER_set)]
560 pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
561 unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
562 }
563
564 #[corresponds(ASN1_INTEGER_dup)]
566 pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> {
567 unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) }
568 }
569}
570
571impl Ord for Asn1IntegerRef {
572 fn cmp(&self, other: &Self) -> Ordering {
573 let res = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), other.as_ptr()) };
574 res.cmp(&0)
575 }
576}
577impl PartialOrd for Asn1IntegerRef {
578 fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> {
579 Some(self.cmp(other))
580 }
581}
582impl Eq for Asn1IntegerRef {}
583impl PartialEq for Asn1IntegerRef {
584 fn eq(&self, other: &Asn1IntegerRef) -> bool {
585 self.cmp(other) == Ordering::Equal
586 }
587}
588
589foreign_type_and_impl_send_sync! {
590 type CType = ffi::ASN1_BIT_STRING;
591 fn drop = ffi::ASN1_BIT_STRING_free;
592 pub struct Asn1BitString;
599 pub struct Asn1BitStringRef;
601}
602
603impl Asn1BitStringRef {
604 #[corresponds(ASN1_STRING_get0_data)]
606 pub fn as_slice(&self) -> &[u8] {
607 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
608 }
609
610 #[corresponds(ASN1_STRING_length)]
612 pub fn len(&self) -> usize {
613 unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
614 }
615
616 pub fn is_empty(&self) -> bool {
618 self.len() == 0
619 }
620}
621
622foreign_type_and_impl_send_sync! {
623 type CType = ffi::ASN1_OCTET_STRING;
624 fn drop = ffi::ASN1_OCTET_STRING_free;
625 pub struct Asn1OctetString;
627 pub struct Asn1OctetStringRef;
629}
630
631impl Asn1OctetString {
632 pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> {
634 ffi::init();
635 unsafe {
636 let s = cvt_p(ffi::ASN1_OCTET_STRING_new())?;
637 ffi::ASN1_OCTET_STRING_set(s, value.as_ptr(), value.len().try_into().unwrap());
638 Ok(Self::from_ptr(s))
639 }
640 }
641}
642
643impl Asn1OctetStringRef {
644 #[corresponds(ASN1_STRING_get0_data)]
646 pub fn as_slice(&self) -> &[u8] {
647 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) }
648 }
649
650 #[corresponds(ASN1_STRING_length)]
652 pub fn len(&self) -> usize {
653 unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize }
654 }
655
656 pub fn is_empty(&self) -> bool {
658 self.len() == 0
659 }
660}
661
662foreign_type_and_impl_send_sync! {
663 type CType = ffi::ASN1_OBJECT;
664 fn drop = ffi::ASN1_OBJECT_free;
665 fn clone = ffi::OBJ_dup;
666
667 pub struct Asn1Object;
681 pub struct Asn1ObjectRef;
683}
684
685impl Stackable for Asn1Object {
686 type StackType = ffi::stack_st_ASN1_OBJECT;
687}
688
689impl Asn1Object {
690 #[corresponds(OBJ_txt2obj)]
692 #[allow(clippy::should_implement_trait)]
693 pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> {
694 unsafe {
695 ffi::init();
696 let txt = CString::new(txt).unwrap();
697 let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?;
698 Ok(Asn1Object::from_ptr(obj))
699 }
700 }
701
702 #[corresponds(OBJ_get0_data)]
707 #[cfg(ossl111)]
708 pub fn as_slice(&self) -> &[u8] {
709 unsafe {
710 let len = ffi::OBJ_length(self.as_ptr());
711 util::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len)
712 }
713 }
714}
715
716impl Asn1ObjectRef {
717 pub fn nid(&self) -> Nid {
719 unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
720 }
721}
722
723impl fmt::Display for Asn1ObjectRef {
724 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
725 unsafe {
726 let mut buf = [0; 80];
727 let len = ffi::OBJ_obj2txt(
728 buf.as_mut_ptr() as *mut _,
729 buf.len() as c_int,
730 self.as_ptr(),
731 0,
732 );
733 match str::from_utf8(&buf[..len as usize]) {
734 Err(_) => fmt.write_str("error"),
735 Ok(s) => fmt.write_str(s),
736 }
737 }
738 }
739}
740
741impl fmt::Debug for Asn1ObjectRef {
742 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
743 fmt.write_str(self.to_string().as_str())
744 }
745}
746
747cfg_if! {
748 if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
749 use ffi::ASN1_STRING_get0_data;
750 } else {
751 #[allow(bad_style)]
752 unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar {
753 ffi::ASN1_STRING_data(s)
754 }
755 }
756}
757
758foreign_type_and_impl_send_sync! {
759 type CType = ffi::ASN1_ENUMERATED;
760 fn drop = ffi::ASN1_ENUMERATED_free;
761
762 pub struct Asn1Enumerated;
764 pub struct Asn1EnumeratedRef;
766}
767
768impl Asn1EnumeratedRef {
769 #[corresponds(ASN1_ENUMERATED_get_int64)]
771 #[cfg(ossl110)]
772 pub fn get_i64(&self) -> Result<i64, ErrorStack> {
773 let mut crl_reason = 0;
774 unsafe {
775 cvt(ffi::ASN1_ENUMERATED_get_int64(
776 &mut crl_reason,
777 self.as_ptr(),
778 ))?;
779 }
780 Ok(crl_reason)
781 }
782}
783
784#[cfg(test)]
785mod tests {
786 use super::*;
787
788 use crate::bn::BigNum;
789 use crate::nid::Nid;
790
791 #[test]
793 fn bn_cvt() {
794 fn roundtrip(bn: BigNum) {
795 let large = Asn1Integer::from_bn(&bn).unwrap();
796 assert_eq!(large.to_bn().unwrap(), bn);
797 }
798
799 roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
800 roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
801 roundtrip(BigNum::from_u32(1234).unwrap());
802 roundtrip(-BigNum::from_u32(1234).unwrap());
803 }
804
805 #[test]
806 fn time_from_str() {
807 Asn1Time::from_str("99991231235959Z").unwrap();
808 #[cfg(ossl111)]
809 Asn1Time::from_str_x509("99991231235959Z").unwrap();
810 }
811
812 #[test]
813 fn generalized_time_from_str() {
814 let time = Asn1GeneralizedTime::from_str("99991231235959Z").unwrap();
815 assert_eq!("Dec 31 23:59:59 9999 GMT", time.to_string());
816 }
817
818 #[test]
819 fn time_from_unix() {
820 let t = Asn1Time::from_unix(0).unwrap();
821 assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string());
822 }
823
824 #[test]
825 fn time_eq() {
826 let a = Asn1Time::from_str("99991231235959Z").unwrap();
827 let b = Asn1Time::from_str("99991231235959Z").unwrap();
828 let c = Asn1Time::from_str("99991231235958Z").unwrap();
829 let a_ref = a.as_ref();
830 let b_ref = b.as_ref();
831 let c_ref = c.as_ref();
832 assert!(a == b);
833 assert!(a != c);
834 assert!(a == b_ref);
835 assert!(a != c_ref);
836 assert!(b_ref == a);
837 assert!(c_ref != a);
838 assert!(a_ref == b_ref);
839 assert!(a_ref != c_ref);
840 }
841
842 #[test]
843 fn time_ord() {
844 let a = Asn1Time::from_str("99991231235959Z").unwrap();
845 let b = Asn1Time::from_str("99991231235959Z").unwrap();
846 let c = Asn1Time::from_str("99991231235958Z").unwrap();
847 let a_ref = a.as_ref();
848 let b_ref = b.as_ref();
849 let c_ref = c.as_ref();
850 assert!(a >= b);
851 assert!(a > c);
852 assert!(b <= a);
853 assert!(c < a);
854
855 assert!(a_ref >= b);
856 assert!(a_ref > c);
857 assert!(b_ref <= a);
858 assert!(c_ref < a);
859
860 assert!(a >= b_ref);
861 assert!(a > c_ref);
862 assert!(b <= a_ref);
863 assert!(c < a_ref);
864
865 assert!(a_ref >= b_ref);
866 assert!(a_ref > c_ref);
867 assert!(b_ref <= a_ref);
868 assert!(c_ref < a_ref);
869 }
870
871 #[test]
872 fn integer_to_owned() {
873 let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
874 let b = a.to_owned().unwrap();
875 assert_eq!(
876 a.to_bn().unwrap().to_dec_str().unwrap().to_string(),
877 b.to_bn().unwrap().to_dec_str().unwrap().to_string(),
878 );
879 assert_ne!(a.as_ptr(), b.as_ptr());
880 }
881
882 #[test]
883 fn integer_cmp() {
884 let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
885 let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
886 let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43").unwrap()).unwrap();
887 assert!(a == b);
888 assert!(a != c);
889 assert!(a < c);
890 assert!(c > b);
891 }
892
893 #[test]
894 fn object_from_str() {
895 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
896 assert_eq!(object.nid(), Nid::SHA256);
897 }
898
899 #[test]
900 fn object_from_str_with_invalid_input() {
901 Asn1Object::from_str("NOT AN OID")
902 .map(|object| object.to_string())
903 .expect_err("parsing invalid OID should fail");
904 }
905
906 #[test]
907 #[cfg(ossl111)]
908 fn object_to_slice() {
909 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
910 assert_eq!(
911 object.as_slice(),
912 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01],
913 );
914 }
915
916 #[test]
917 fn asn1_octet_string() {
918 let octet_string = Asn1OctetString::new_from_bytes(b"hello world").unwrap();
919 assert_eq!(octet_string.as_slice(), b"hello world");
920 assert_eq!(octet_string.len(), 11);
921 }
922}