1 #![deny(missing_docs)]
2 
3 //! Defines the format of certificiates
4 //!
5 //! This module is used by [`x509`] and other certificate building functions
6 //! to describe time, strings, and objects.
7 //!
8 //! Abstract Syntax Notation One is an interface description language.
9 //! The specification comes from [X.208] by OSI, and rewritten in X.680.
10 //! ASN.1 describes properties of an object with a type set.  Those types
11 //! can be atomic, structured, choice, and other (CHOICE and ANY).  These
12 //! types are expressed as a number and the assignment operator ::=  gives
13 //! the type a name.
14 //!
15 //! The implementation here provides a subset of the ASN.1 types that OpenSSL
16 //! uses, especially in the properties of a certificate used in HTTPS.
17 //!
18 //! [X.208]: https://www.itu.int/rec/T-REC-X.208-198811-W/en
19 //! [`x509`]: ../x509/struct.X509Builder.html
20 //!
21 //! ## Examples
22 //!
23 //! ```
24 //! use openssl::asn1::Asn1Time;
25 //! let tomorrow = Asn1Time::days_from_now(1);
26 //! ```
27 use ffi;
28 use foreign_types::{ForeignType, ForeignTypeRef};
29 use libc::{c_char, c_int, c_long, time_t};
30 #[cfg(ossl102)]
31 use std::cmp::Ordering;
32 use std::ffi::CString;
33 use std::fmt;
34 use std::ptr;
35 use std::slice;
36 use std::str;
37 
38 use bio::MemBio;
39 use bn::{BigNum, BigNumRef};
40 use error::ErrorStack;
41 use nid::Nid;
42 use string::OpensslString;
43 use {cvt, cvt_p};
44 
45 foreign_type_and_impl_send_sync! {
46     type CType = ffi::ASN1_GENERALIZEDTIME;
47     fn drop = ffi::ASN1_GENERALIZEDTIME_free;
48 
49     /// Non-UTC representation of time
50     ///
51     /// If a time can be represented by UTCTime, UTCTime is used
52     /// otherwise, ASN1_GENERALIZEDTIME is used.  This would be, for
53     /// example outside the year range of 1950-2049.
54     ///
55     /// [ASN1_GENERALIZEDTIME_set] documentation from OpenSSL provides
56     /// further details of implmentation.  Note: these docs are from the master
57     /// branch as documentation on the 1.1.0 branch did not include this page.
58     ///
59     /// [ASN1_GENERALIZEDTIME_set]: https://www.openssl.org/docs/manmaster/man3/ASN1_GENERALIZEDTIME_set.html
60     pub struct Asn1GeneralizedTime;
61     /// Reference to a [`Asn1GeneralizedTime`]
62     ///
63     /// [`Asn1GeneralizedTime`]: struct.Asn1GeneralizedTime.html
64     pub struct Asn1GeneralizedTimeRef;
65 }
66 
67 impl fmt::Display for Asn1GeneralizedTimeRef {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result68     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69         unsafe {
70             let mem_bio = MemBio::new()?;
71             cvt(ffi::ASN1_GENERALIZEDTIME_print(
72                 mem_bio.as_ptr(),
73                 self.as_ptr(),
74             ))?;
75             write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf()))
76         }
77     }
78 }
79 
80 /// Difference between two ASN1 times.
81 ///
82 /// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its
83 /// documentation for more.
84 ///
85 /// [`diff`]: struct.Asn1TimeRef.html#method.diff
86 /// [`Asn1TimeRef`]: struct.Asn1TimeRef.html
87 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
88 #[cfg(ossl102)]
89 pub struct TimeDiff {
90     /// Difference in days
91     pub days: c_int,
92     /// Difference in seconds.
93     ///
94     /// This is always less than the number of seconds in a day.
95     pub secs: c_int,
96 }
97 
98 foreign_type_and_impl_send_sync! {
99     type CType = ffi::ASN1_TIME;
100     fn drop = ffi::ASN1_TIME_free;
101     /// Time storage and comparison
102     ///
103     /// Asn1Time should be used to store and share time information
104     /// using certificates.  If Asn1Time is set using a string, it must
105     /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format.
106     ///
107     /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementaiton
108     /// used by OpenSSL.
109     ///
110     /// [ASN_TIME_set]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_set.html
111     pub struct Asn1Time;
112     /// Reference to an [`Asn1Time`]
113     ///
114     /// [`Asn1Time`]: struct.Asn1Time.html
115     pub struct Asn1TimeRef;
116 }
117 
118 impl Asn1TimeRef {
119     /// Find difference between two times
120     ///
121     /// This corresponds to [`ASN1_TIME_diff`].
122     ///
123     /// [`ASN1_TIME_diff`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_diff.html
124     #[cfg(ossl102)]
diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack>125     pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
126         let mut days = 0;
127         let mut seconds = 0;
128         let other = compare.as_ptr();
129 
130         let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut seconds, self.as_ptr(), other) };
131 
132         match err {
133             0 => Err(ErrorStack::get()),
134             _ => Ok(TimeDiff {
135                 days: days,
136                 secs: seconds,
137             }),
138         }
139     }
140 
141     /// Compare two times
142     ///
143     /// This corresponds to [`ASN1_TIME_compare`] but is implemented using [`diff`] so that it is
144     /// also supported on older versions of OpenSSL.
145     ///
146     /// [`ASN1_TIME_compare`]: https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_compare.html
147     /// [`diff`]: struct.Asn1TimeRef.html#method.diff
148     #[cfg(ossl102)]
compare(&self, other: &Self) -> Result<Ordering, ErrorStack>149     pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
150         let d = self.diff(other)?;
151         if d.days > 0 || d.secs > 0 {
152             return Ok(Ordering::Less);
153         }
154         if d.days < 0 || d.secs < 0 {
155             return Ok(Ordering::Greater);
156         }
157 
158         Ok(Ordering::Equal)
159     }
160 }
161 
162 #[cfg(ossl102)]
163 impl PartialEq for Asn1TimeRef {
eq(&self, other: &Asn1TimeRef) -> bool164     fn eq(&self, other: &Asn1TimeRef) -> bool {
165         self.diff(other)
166             .map(|t| t.days == 0 && t.secs == 0)
167             .unwrap_or(false)
168     }
169 }
170 
171 #[cfg(ossl102)]
172 impl PartialEq<Asn1Time> for Asn1TimeRef {
eq(&self, other: &Asn1Time) -> bool173     fn eq(&self, other: &Asn1Time) -> bool {
174         self.diff(other)
175             .map(|t| t.days == 0 && t.secs == 0)
176             .unwrap_or(false)
177     }
178 }
179 
180 #[cfg(ossl102)]
181 impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
eq(&self, other: &Asn1Time) -> bool182     fn eq(&self, other: &Asn1Time) -> bool {
183         self.diff(other)
184             .map(|t| t.days == 0 && t.secs == 0)
185             .unwrap_or(false)
186     }
187 }
188 
189 #[cfg(ossl102)]
190 impl PartialOrd for Asn1TimeRef {
partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering>191     fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
192         self.compare(other).ok()
193     }
194 }
195 
196 #[cfg(ossl102)]
197 impl PartialOrd<Asn1Time> for Asn1TimeRef {
partial_cmp(&self, other: &Asn1Time) -> Option<Ordering>198     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
199         self.compare(other).ok()
200     }
201 }
202 
203 #[cfg(ossl102)]
204 impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
partial_cmp(&self, other: &Asn1Time) -> Option<Ordering>205     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
206         self.compare(other).ok()
207     }
208 }
209 
210 impl fmt::Display for Asn1TimeRef {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result211     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212         unsafe {
213             let mem_bio = MemBio::new()?;
214             cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()))?;
215             write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf()))
216         }
217     }
218 }
219 
220 impl Asn1Time {
new() -> Result<Asn1Time, ErrorStack>221     fn new() -> Result<Asn1Time, ErrorStack> {
222         ffi::init();
223 
224         unsafe {
225             let handle = cvt_p(ffi::ASN1_TIME_new())?;
226             Ok(Asn1Time::from_ptr(handle))
227         }
228     }
229 
from_period(period: c_long) -> Result<Asn1Time, ErrorStack>230     fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
231         ffi::init();
232 
233         unsafe {
234             let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?;
235             Ok(Asn1Time::from_ptr(handle))
236         }
237     }
238 
239     /// Creates a new time on specified interval in days from now
days_from_now(days: u32) -> Result<Asn1Time, ErrorStack>240     pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
241         Asn1Time::from_period(days as c_long * 60 * 60 * 24)
242     }
243 
244     /// Creates a new time from the specified `time_t` value
from_unix(time: time_t) -> Result<Asn1Time, ErrorStack>245     pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
246         ffi::init();
247 
248         unsafe {
249             let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
250             Ok(Asn1Time::from_ptr(handle))
251         }
252     }
253 
254     /// Creates a new time corresponding to the specified ASN1 time string.
255     ///
256     /// This corresponds to [`ASN1_TIME_set_string`].
257     ///
258     /// [`ASN1_TIME_set_string`]: https://www.openssl.org/docs/manmaster/man3/ASN1_TIME_set_string.html
from_str(s: &str) -> Result<Asn1Time, ErrorStack>259     pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
260         unsafe {
261             let s = CString::new(s).unwrap();
262 
263             let time = Asn1Time::new()?;
264             cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
265 
266             Ok(time)
267         }
268     }
269 
270     /// Creates a new time corresponding to the specified X509 time string.
271     ///
272     /// This corresponds to [`ASN1_TIME_set_string_X509`].
273     ///
274     /// Requires OpenSSL 1.1.1 or newer.
275     ///
276     /// [`ASN1_TIME_set_string_X509`]: https://www.openssl.org/docs/manmaster/man3/ASN1_TIME_set_string.html
277     #[cfg(ossl111)]
from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack>278     pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> {
279         unsafe {
280             let s = CString::new(s).unwrap();
281 
282             let time = Asn1Time::new()?;
283             cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?;
284 
285             Ok(time)
286         }
287     }
288 }
289 
290 #[cfg(ossl102)]
291 impl PartialEq for Asn1Time {
eq(&self, other: &Asn1Time) -> bool292     fn eq(&self, other: &Asn1Time) -> bool {
293         self.diff(other)
294             .map(|t| t.days == 0 && t.secs == 0)
295             .unwrap_or(false)
296     }
297 }
298 
299 #[cfg(ossl102)]
300 impl PartialEq<Asn1TimeRef> for Asn1Time {
eq(&self, other: &Asn1TimeRef) -> bool301     fn eq(&self, other: &Asn1TimeRef) -> bool {
302         self.diff(other)
303             .map(|t| t.days == 0 && t.secs == 0)
304             .unwrap_or(false)
305     }
306 }
307 
308 #[cfg(ossl102)]
309 impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
eq(&self, other: &&'a Asn1TimeRef) -> bool310     fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
311         self.diff(other)
312             .map(|t| t.days == 0 && t.secs == 0)
313             .unwrap_or(false)
314     }
315 }
316 
317 #[cfg(ossl102)]
318 impl PartialOrd for Asn1Time {
partial_cmp(&self, other: &Asn1Time) -> Option<Ordering>319     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
320         self.compare(other).ok()
321     }
322 }
323 
324 #[cfg(ossl102)]
325 impl PartialOrd<Asn1TimeRef> for Asn1Time {
partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering>326     fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
327         self.compare(other).ok()
328     }
329 }
330 
331 #[cfg(ossl102)]
332 impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering>333     fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
334         self.compare(other).ok()
335     }
336 }
337 
338 foreign_type_and_impl_send_sync! {
339     type CType = ffi::ASN1_STRING;
340     fn drop = ffi::ASN1_STRING_free;
341     /// Primary ASN.1 type used by OpenSSL
342     ///
343     /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING
344     /// structures.  This implementation uses [ASN1_STRING-to_UTF8] to preserve
345     /// compatibility with Rust's String.
346     ///
347     /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_STRING_to_UTF8.html
348     pub struct Asn1String;
349     /// Reference to [`Asn1String`]
350     ///
351     /// [`Asn1String`]: struct.Asn1String.html
352     pub struct Asn1StringRef;
353 }
354 
355 impl Asn1StringRef {
356     /// Converts the ASN.1 underlying format to UTF8
357     ///
358     /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8.  This is important to
359     /// consume the string in a meaningful way without knowing the underlying
360     /// format.
as_utf8(&self) -> Result<OpensslString, ErrorStack>361     pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
362         unsafe {
363             let mut ptr = ptr::null_mut();
364             let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
365             if len < 0 {
366                 return Err(ErrorStack::get());
367             }
368 
369             Ok(OpensslString::from_ptr(ptr as *mut c_char))
370         }
371     }
372 
373     /// Return the string as an array of bytes
374     ///
375     /// The bytes do not directly corespond to UTF-8 encoding.  To interact with
376     /// strings in rust, it is preferable to use [`as_utf8`]
377     ///
378     /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8
as_slice(&self) -> &[u8]379     pub fn as_slice(&self) -> &[u8] {
380         unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
381     }
382 
383     /// Return the length of the Asn1String (number of bytes)
len(&self) -> usize384     pub fn len(&self) -> usize {
385         unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
386     }
387 }
388 
389 foreign_type_and_impl_send_sync! {
390     type CType = ffi::ASN1_INTEGER;
391     fn drop = ffi::ASN1_INTEGER_free;
392 
393     /// Numeric representation
394     ///
395     /// Integers in ASN.1 may include BigNum, int64 or uint64.  BigNum implementation
396     /// can be found within [`bn`] module.
397     ///
398     /// OpenSSL documentation includes [`ASN1_INTEGER_set`].
399     ///
400     /// [`bn`]: ../bn/index.html
401     /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html
402     pub struct Asn1Integer;
403     /// Reference to [`Asn1Integer`]
404     ///
405     /// [`Asn1Integer`]: struct.Asn1Integer.html
406     pub struct Asn1IntegerRef;
407 }
408 
409 impl Asn1Integer {
410     /// Converts a bignum to an `Asn1Integer`.
411     ///
412     /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see
413     /// [`BigNumRef::to_asn1_integer`].
414     ///
415     /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_to_ASN1_INTEGER.html
416     /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack>417     pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
418         bn.to_asn1_integer()
419     }
420 }
421 
422 impl Asn1IntegerRef {
423     #[allow(missing_docs)]
424     #[deprecated(since = "0.10.6", note = "use to_bn instead")]
get(&self) -> i64425     pub fn get(&self) -> i64 {
426         unsafe { ::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
427     }
428 
429     /// Converts the integer to a `BigNum`.
430     ///
431     /// This corresponds to [`ASN1_INTEGER_to_BN`].
432     ///
433     /// [`ASN1_INTEGER_to_BN`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_get.html
to_bn(&self) -> Result<BigNum, ErrorStack>434     pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
435         unsafe {
436             cvt_p(::ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut()))
437                 .map(|p| BigNum::from_ptr(p))
438         }
439     }
440 
441     /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers
442     /// see [`bn`].
443     ///
444     /// OpenSSL documentation at [`ASN1_INTEGER_set`]
445     ///
446     /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
447     /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html
set(&mut self, value: i32) -> Result<(), ErrorStack>448     pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
449         unsafe { cvt(::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
450     }
451 }
452 
453 foreign_type_and_impl_send_sync! {
454     type CType = ffi::ASN1_BIT_STRING;
455     fn drop = ffi::ASN1_BIT_STRING_free;
456     /// Sequence of bytes
457     ///
458     /// Asn1BitString is used in [`x509`] certificates for the signature.
459     /// The bit string acts as a collection of bytes.
460     ///
461     /// [`x509`]: ../x509/struct.X509.html#method.signature
462     pub struct Asn1BitString;
463     /// Reference to [`Asn1BitString`]
464     ///
465     /// [`Asn1BitString`]: struct.Asn1BitString.html
466     pub struct Asn1BitStringRef;
467 }
468 
469 impl Asn1BitStringRef {
470     /// Returns the Asn1BitString as a slice
as_slice(&self) -> &[u8]471     pub fn as_slice(&self) -> &[u8] {
472         unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
473     }
474     /// Length of Asn1BitString in number of bytes.
len(&self) -> usize475     pub fn len(&self) -> usize {
476         unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
477     }
478 }
479 
480 foreign_type_and_impl_send_sync! {
481     type CType = ffi::ASN1_OBJECT;
482     fn drop = ffi::ASN1_OBJECT_free;
483 
484     /// Object Identifier
485     ///
486     /// Represents an ASN.1 Object.  Typically, NIDs, or numeric identifiers
487     /// are stored as a table within the [`Nid`] module.  These constants are
488     /// used to determine attributes of a certificate, such as mapping the
489     /// attribute "CommonName" to "CN" which is represented as the OID of 13.
490     /// This attribute is a constant in the [`nid::COMMONNAME`].
491     ///
492     /// OpenSSL documentation at [`OBJ_nid2obj`]
493     ///
494     /// [`Nid`]: ../nid/index.html
495     /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html
496     /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_obj2nid.html
497     pub struct Asn1Object;
498     /// Reference to [`Asn1Object`]
499     ///
500     /// [`Asn1Object`]: struct.Asn1Object.html
501     pub struct Asn1ObjectRef;
502 }
503 
504 impl Asn1ObjectRef {
505     /// Returns the NID associated with this OID.
nid(&self) -> Nid506     pub fn nid(&self) -> Nid {
507         unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
508     }
509 }
510 
511 impl fmt::Display for Asn1ObjectRef {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result512     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
513         unsafe {
514             let mut buf = [0; 80];
515             let len = ffi::OBJ_obj2txt(
516                 buf.as_mut_ptr() as *mut _,
517                 buf.len() as c_int,
518                 self.as_ptr(),
519                 0,
520             );
521             let s = str::from_utf8(&buf[..len as usize]).map_err(|_| fmt::Error)?;
522             fmt.write_str(s)
523         }
524     }
525 }
526 
527 cfg_if! {
528     if #[cfg(any(ossl110, libressl273))] {
529         use ffi::ASN1_STRING_get0_data;
530     } else {
531         #[allow(bad_style)]
532         unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar {
533             ffi::ASN1_STRING_data(s)
534         }
535     }
536 }
537 
538 #[cfg(test)]
539 mod tests {
540     use super::*;
541 
542     use bn::BigNum;
543 
544     /// Tests conversion between BigNum and Asn1Integer.
545     #[test]
bn_cvt()546     fn bn_cvt() {
547         fn roundtrip(bn: BigNum) {
548             let large = Asn1Integer::from_bn(&bn).unwrap();
549             assert_eq!(large.to_bn().unwrap(), bn);
550         }
551 
552         roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
553         roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
554         roundtrip(BigNum::from_u32(1234).unwrap());
555         roundtrip(-BigNum::from_u32(1234).unwrap());
556     }
557 
558     #[test]
time_from_str()559     fn time_from_str() {
560         Asn1Time::from_str("99991231235959Z").unwrap();
561         #[cfg(ossl111)]
562         Asn1Time::from_str_x509("99991231235959Z").unwrap();
563     }
564 
565     #[test]
time_from_unix()566     fn time_from_unix() {
567         let t = Asn1Time::from_unix(0).unwrap();
568         assert_eq!("Jan  1 00:00:00 1970 GMT", t.to_string());
569     }
570 
571     #[test]
572     #[cfg(ossl102)]
time_eq()573     fn time_eq() {
574         let a = Asn1Time::from_str("99991231235959Z").unwrap();
575         let b = Asn1Time::from_str("99991231235959Z").unwrap();
576         let c = Asn1Time::from_str("99991231235958Z").unwrap();
577         let a_ref = a.as_ref();
578         let b_ref = b.as_ref();
579         let c_ref = c.as_ref();
580         assert!(a == b);
581         assert!(a != c);
582         assert!(a == b_ref);
583         assert!(a != c_ref);
584         assert!(b_ref == a);
585         assert!(c_ref != a);
586         assert!(a_ref == b_ref);
587         assert!(a_ref != c_ref);
588     }
589 
590     #[test]
591     #[cfg(ossl102)]
time_ord()592     fn time_ord() {
593         let a = Asn1Time::from_str("99991231235959Z").unwrap();
594         let b = Asn1Time::from_str("99991231235959Z").unwrap();
595         let c = Asn1Time::from_str("99991231235958Z").unwrap();
596         let a_ref = a.as_ref();
597         let b_ref = b.as_ref();
598         let c_ref = c.as_ref();
599         assert!(a >= b);
600         assert!(a > c);
601         assert!(b <= a);
602         assert!(c < a);
603 
604         assert!(a_ref >= b);
605         assert!(a_ref > c);
606         assert!(b_ref <= a);
607         assert!(c_ref < a);
608 
609         assert!(a >= b_ref);
610         assert!(a > c_ref);
611         assert!(b <= a_ref);
612         assert!(c < a_ref);
613 
614         assert!(a_ref >= b_ref);
615         assert!(a_ref > c_ref);
616         assert!(b_ref <= a_ref);
617         assert!(c_ref < a_ref);
618     }
619 }
620