boring_imp/
asn1.rs

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 boring::asn1::Asn1Time;
25//! let tomorrow = Asn1Time::days_from_now(1);
26//! ```
27use crate::ffi;
28use foreign_types::{ForeignType, ForeignTypeRef};
29use libc::{c_char, c_int, c_long, time_t};
30use std::cmp::Ordering;
31use std::ffi::CString;
32use std::fmt;
33use std::ptr;
34use std::slice;
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};
44
45foreign_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}
62
63impl fmt::Display for Asn1GeneralizedTimeRef {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        unsafe {
66            let mem_bio = match MemBio::new() {
67                Err(_) => return f.write_str("error"),
68                Ok(m) => m,
69            };
70            let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
71                mem_bio.as_ptr(),
72                self.as_ptr(),
73            ));
74            match print_result {
75                Err(_) => f.write_str("error"),
76                Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
77            }
78        }
79    }
80}
81
82/// The type of an ASN.1 value.
83#[derive(Debug, Copy, Clone, PartialEq, Eq)]
84pub struct Asn1Type(c_int);
85
86#[allow(missing_docs)] // no need to document the constants
87impl Asn1Type {
88    pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC);
89
90    pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN);
91
92    pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER);
93
94    pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING);
95
96    pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING);
97
98    pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL);
99
100    pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT);
101
102    pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR);
103
104    pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL);
105
106    pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL);
107
108    pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED);
109
110    pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING);
111
112    pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE);
113
114    pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET);
115
116    pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING);
117
118    pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING);
119
120    pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING);
121
122    pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING);
123
124    pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING);
125
126    pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING);
127
128    pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME);
129
130    pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME);
131
132    pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING);
133
134    pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING);
135
136    pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING);
137
138    pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING);
139
140    pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING);
141
142    pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING);
143
144    /// Constructs an `Asn1Type` from a raw OpenSSL value.
145    pub fn from_raw(value: c_int) -> Self {
146        Asn1Type(value)
147    }
148
149    /// Returns the raw OpenSSL value represented by this type.
150    pub fn as_raw(&self) -> c_int {
151        self.0
152    }
153}
154
155/// Difference between two ASN1 times.
156///
157/// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its
158/// documentation for more.
159///
160/// [`diff`]: struct.Asn1TimeRef.html#method.diff
161/// [`Asn1TimeRef`]: struct.Asn1TimeRef.html
162#[derive(Debug, Clone, PartialEq, Eq, Hash)]
163pub struct TimeDiff {
164    /// Difference in days
165    pub days: c_int,
166    /// Difference in seconds.
167    ///
168    /// This is always less than the number of seconds in a day.
169    pub secs: c_int,
170}
171
172foreign_type_and_impl_send_sync! {
173    type CType = ffi::ASN1_TIME;
174    fn drop = ffi::ASN1_TIME_free;
175    /// Time storage and comparison
176    ///
177    /// Asn1Time should be used to store and share time information
178    /// using certificates.  If Asn1Time is set using a string, it must
179    /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format.
180    ///
181    /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation
182    /// used by OpenSSL.
183    ///
184    /// [ASN_TIME_set]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_set.html
185    pub struct Asn1Time;
186}
187
188impl Asn1TimeRef {
189    /// Find difference between two times
190    ///
191    /// This corresponds to [`ASN1_TIME_diff`].
192    ///
193    /// [`ASN1_TIME_diff`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_diff.html
194    pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
195        let mut days = 0;
196        let mut secs = 0;
197        let other = compare.as_ptr();
198
199        let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) };
200
201        match err {
202            0 => Err(ErrorStack::get()),
203            _ => Ok(TimeDiff { days, secs }),
204        }
205    }
206
207    /// Compare two times
208    ///
209    /// This corresponds to [`ASN1_TIME_compare`] but is implemented using [`diff`] so that it is
210    /// also supported on older versions of OpenSSL.
211    ///
212    /// [`ASN1_TIME_compare`]: https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_compare.html
213    /// [`diff`]: struct.Asn1TimeRef.html#method.diff
214    pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
215        let d = self.diff(other)?;
216        if d.days > 0 || d.secs > 0 {
217            return Ok(Ordering::Less);
218        }
219        if d.days < 0 || d.secs < 0 {
220            return Ok(Ordering::Greater);
221        }
222
223        Ok(Ordering::Equal)
224    }
225}
226
227impl PartialEq for Asn1TimeRef {
228    fn eq(&self, other: &Asn1TimeRef) -> bool {
229        self.diff(other)
230            .map(|t| t.days == 0 && t.secs == 0)
231            .unwrap_or(false)
232    }
233}
234
235impl PartialEq<Asn1Time> for Asn1TimeRef {
236    fn eq(&self, other: &Asn1Time) -> bool {
237        self.diff(other)
238            .map(|t| t.days == 0 && t.secs == 0)
239            .unwrap_or(false)
240    }
241}
242
243impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
244    fn eq(&self, other: &Asn1Time) -> bool {
245        self.diff(other)
246            .map(|t| t.days == 0 && t.secs == 0)
247            .unwrap_or(false)
248    }
249}
250
251impl PartialOrd for Asn1TimeRef {
252    fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
253        self.compare(other).ok()
254    }
255}
256
257impl PartialOrd<Asn1Time> for Asn1TimeRef {
258    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
259        self.compare(other).ok()
260    }
261}
262
263impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
264    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
265        self.compare(other).ok()
266    }
267}
268
269impl fmt::Display for Asn1TimeRef {
270    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
271        unsafe {
272            let mem_bio = match MemBio::new() {
273                Err(_) => return f.write_str("error"),
274                Ok(m) => m,
275            };
276            let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()));
277            match print_result {
278                Err(_) => f.write_str("error"),
279                Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
280            }
281        }
282    }
283}
284
285impl fmt::Debug for Asn1TimeRef {
286    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287        f.write_str(&self.to_string())
288    }
289}
290
291impl Asn1Time {
292    fn new() -> Result<Asn1Time, ErrorStack> {
293        ffi::init();
294
295        unsafe {
296            let handle = cvt_p(ffi::ASN1_TIME_new())?;
297            Ok(Asn1Time::from_ptr(handle))
298        }
299    }
300
301    fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
302        ffi::init();
303
304        unsafe {
305            let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?;
306            Ok(Asn1Time::from_ptr(handle))
307        }
308    }
309
310    /// Creates a new time on specified interval in days from now
311    pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
312        Asn1Time::from_period(days as c_long * 60 * 60 * 24)
313    }
314
315    /// Creates a new time from the specified `time_t` value
316    pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
317        ffi::init();
318
319        unsafe {
320            let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
321            Ok(Asn1Time::from_ptr(handle))
322        }
323    }
324
325    /// Creates a new time corresponding to the specified ASN1 time string.
326    ///
327    /// This corresponds to [`ASN1_TIME_set_string`].
328    ///
329    /// [`ASN1_TIME_set_string`]: https://www.openssl.org/docs/manmaster/man3/ASN1_TIME_set_string.html
330    #[allow(clippy::should_implement_trait)]
331    pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
332        unsafe {
333            let s = CString::new(s).unwrap();
334
335            let time = Asn1Time::new()?;
336            cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
337
338            Ok(time)
339        }
340    }
341}
342
343impl PartialEq for Asn1Time {
344    fn eq(&self, other: &Asn1Time) -> bool {
345        self.diff(other)
346            .map(|t| t.days == 0 && t.secs == 0)
347            .unwrap_or(false)
348    }
349}
350
351impl PartialEq<Asn1TimeRef> for Asn1Time {
352    fn eq(&self, other: &Asn1TimeRef) -> bool {
353        self.diff(other)
354            .map(|t| t.days == 0 && t.secs == 0)
355            .unwrap_or(false)
356    }
357}
358
359impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
360    fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
361        self.diff(other)
362            .map(|t| t.days == 0 && t.secs == 0)
363            .unwrap_or(false)
364    }
365}
366
367impl PartialOrd for Asn1Time {
368    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
369        self.compare(other).ok()
370    }
371}
372
373impl PartialOrd<Asn1TimeRef> for Asn1Time {
374    fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
375        self.compare(other).ok()
376    }
377}
378
379impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
380    fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
381        self.compare(other).ok()
382    }
383}
384
385foreign_type_and_impl_send_sync! {
386    type CType = ffi::ASN1_STRING;
387    fn drop = ffi::ASN1_STRING_free;
388    /// Primary ASN.1 type used by OpenSSL
389    ///
390    /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING
391    /// structures.  This implementation uses [ASN1_STRING-to_UTF8] to preserve
392    /// compatibility with Rust's String.
393    ///
394    /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_STRING_to_UTF8.html
395    pub struct Asn1String;
396}
397
398impl Asn1StringRef {
399    /// Converts the ASN.1 underlying format to UTF8
400    ///
401    /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8.  This is important to
402    /// consume the string in a meaningful way without knowing the underlying
403    /// format.
404    pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
405        unsafe {
406            let mut ptr = ptr::null_mut();
407            let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
408            if len < 0 {
409                return Err(ErrorStack::get());
410            }
411
412            Ok(OpensslString::from_ptr(ptr as *mut c_char))
413        }
414    }
415
416    /// Return the string as an array of bytes.
417    ///
418    /// The bytes do not directly correspond to UTF-8 encoding.  To interact with
419    /// strings in rust, it is preferable to use [`as_utf8`]
420    ///
421    /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8
422    pub fn as_slice(&self) -> &[u8] {
423        unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
424    }
425
426    /// Returns the number of bytes in the string.
427    pub fn len(&self) -> usize {
428        unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
429    }
430
431    /// Determines if the string is empty.
432    pub fn is_empty(&self) -> bool {
433        self.len() == 0
434    }
435}
436
437impl fmt::Debug for Asn1StringRef {
438    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
439        match self.as_utf8() {
440            Ok(openssl_string) => openssl_string.fmt(fmt),
441            Err(_) => fmt.write_str("error"),
442        }
443    }
444}
445
446foreign_type_and_impl_send_sync! {
447    type CType = ffi::ASN1_INTEGER;
448    fn drop = ffi::ASN1_INTEGER_free;
449
450    /// Numeric representation
451    ///
452    /// Integers in ASN.1 may include BigNum, int64 or uint64.  BigNum implementation
453    /// can be found within [`bn`] module.
454    ///
455    /// OpenSSL documentation includes [`ASN1_INTEGER_set`].
456    ///
457    /// [`bn`]: ../bn/index.html
458    /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html
459    pub struct Asn1Integer;
460}
461
462impl Asn1Integer {
463    /// Converts a bignum to an `Asn1Integer`.
464    ///
465    /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see
466    /// [`BigNumRef::to_asn1_integer`].
467    ///
468    /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_to_ASN1_INTEGER.html
469    /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
470    pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
471        bn.to_asn1_integer()
472    }
473}
474
475impl Asn1IntegerRef {
476    #[allow(clippy::unnecessary_cast)]
477    #[allow(missing_docs)]
478    #[deprecated(since = "0.10.6", note = "use to_bn instead")]
479    pub fn get(&self) -> i64 {
480        unsafe { crate::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
481    }
482
483    /// Converts the integer to a `BigNum`.
484    ///
485    /// This corresponds to [`ASN1_INTEGER_to_BN`].
486    ///
487    /// [`ASN1_INTEGER_to_BN`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_get.html
488    pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
489        unsafe {
490            cvt_p(crate::ffi::ASN1_INTEGER_to_BN(
491                self.as_ptr(),
492                ptr::null_mut(),
493            ))
494            .map(|p| BigNum::from_ptr(p))
495        }
496    }
497
498    /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers
499    /// see [`bn`].
500    ///
501    /// OpenSSL documentation at [`ASN1_INTEGER_set`]
502    ///
503    /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
504    /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html
505    pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
506        unsafe { cvt(crate::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
507    }
508}
509
510foreign_type_and_impl_send_sync! {
511    type CType = ffi::ASN1_BIT_STRING;
512    fn drop = ffi::ASN1_BIT_STRING_free;
513    /// Sequence of bytes
514    ///
515    /// Asn1BitString is used in [`x509`] certificates for the signature.
516    /// The bit string acts as a collection of bytes.
517    ///
518    /// [`x509`]: ../x509/struct.X509.html#method.signature
519    pub struct Asn1BitString;
520}
521
522impl Asn1BitStringRef {
523    /// Returns the Asn1BitString as a slice.
524    pub fn as_slice(&self) -> &[u8] {
525        unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
526    }
527
528    /// Returns the number of bytes in the string.
529    pub fn len(&self) -> usize {
530        unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
531    }
532
533    /// Determines if the string is empty.
534    pub fn is_empty(&self) -> bool {
535        self.len() == 0
536    }
537}
538
539foreign_type_and_impl_send_sync! {
540    type CType = ffi::ASN1_OBJECT;
541    fn drop = ffi::ASN1_OBJECT_free;
542
543    /// Object Identifier
544    ///
545    /// Represents an ASN.1 Object.  Typically, NIDs, or numeric identifiers
546    /// are stored as a table within the [`Nid`] module.  These constants are
547    /// used to determine attributes of a certificate, such as mapping the
548    /// attribute "CommonName" to "CN" which is represented as the OID of 13.
549    /// This attribute is a constant in the [`nid::COMMONNAME`].
550    ///
551    /// OpenSSL documentation at [`OBJ_nid2obj`]
552    ///
553    /// [`Nid`]: ../nid/index.html
554    /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html
555    /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_obj2nid.html
556    pub struct Asn1Object;
557}
558
559impl Stackable for Asn1Object {
560    type StackType = ffi::stack_st_ASN1_OBJECT;
561}
562
563impl Asn1Object {
564    /// Constructs an ASN.1 Object Identifier from a string representation of
565    /// the OID.
566    ///
567    /// This corresponds to [`OBJ_txt2obj`].
568    ///
569    /// [`OBJ_txt2obj`]: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html
570    #[allow(clippy::should_implement_trait)]
571    pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> {
572        unsafe {
573            ffi::init();
574            let txt = CString::new(txt).unwrap();
575            let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?;
576            Ok(Asn1Object::from_ptr(obj))
577        }
578    }
579}
580
581impl Asn1ObjectRef {
582    /// Returns the NID associated with this OID.
583    pub fn nid(&self) -> Nid {
584        unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
585    }
586}
587
588impl fmt::Display for Asn1ObjectRef {
589    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
590        unsafe {
591            let mut buf = [0; 80];
592            let len = ffi::OBJ_obj2txt(
593                buf.as_mut_ptr() as *mut _,
594                buf.len() as c_int,
595                self.as_ptr(),
596                0,
597            );
598            match str::from_utf8(&buf[..len as usize]) {
599                Err(_) => fmt.write_str("error"),
600                Ok(s) => fmt.write_str(s),
601            }
602        }
603    }
604}
605
606impl fmt::Debug for Asn1ObjectRef {
607    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
608        fmt.write_str(self.to_string().as_str())
609    }
610}
611
612use crate::ffi::ASN1_STRING_get0_data;
613
614#[cfg(test)]
615mod tests {
616    use super::*;
617
618    use crate::bn::BigNum;
619    use crate::nid::Nid;
620
621    /// Tests conversion between BigNum and Asn1Integer.
622    #[test]
623    fn bn_cvt() {
624        fn roundtrip(bn: BigNum) {
625            let large = Asn1Integer::from_bn(&bn).unwrap();
626            assert_eq!(large.to_bn().unwrap(), bn);
627        }
628
629        roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
630        roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
631        roundtrip(BigNum::from_u32(1234).unwrap());
632        roundtrip(-BigNum::from_u32(1234).unwrap());
633    }
634
635    #[test]
636    fn time_from_str() {
637        Asn1Time::from_str("99991231235959Z").unwrap();
638    }
639
640    #[test]
641    fn time_from_unix() {
642        let t = Asn1Time::from_unix(0).unwrap();
643        assert_eq!("Jan  1 00:00:00 1970 GMT", t.to_string());
644    }
645
646    #[test]
647    fn time_eq() {
648        let a = Asn1Time::from_str("99991231235959Z").unwrap();
649        let b = Asn1Time::from_str("99991231235959Z").unwrap();
650        let c = Asn1Time::from_str("99991231235958Z").unwrap();
651        let a_ref = a.as_ref();
652        let b_ref = b.as_ref();
653        let c_ref = c.as_ref();
654        assert!(a == b);
655        assert!(a != c);
656        assert!(a == b_ref);
657        assert!(a != c_ref);
658        assert!(b_ref == a);
659        assert!(c_ref != a);
660        assert!(a_ref == b_ref);
661        assert!(a_ref != c_ref);
662    }
663
664    #[test]
665    fn time_ord() {
666        let a = Asn1Time::from_str("99991231235959Z").unwrap();
667        let b = Asn1Time::from_str("99991231235959Z").unwrap();
668        let c = Asn1Time::from_str("99991231235958Z").unwrap();
669        let a_ref = a.as_ref();
670        let b_ref = b.as_ref();
671        let c_ref = c.as_ref();
672        assert!(a >= b);
673        assert!(a > c);
674        assert!(b <= a);
675        assert!(c < a);
676
677        assert!(a_ref >= b);
678        assert!(a_ref > c);
679        assert!(b_ref <= a);
680        assert!(c_ref < a);
681
682        assert!(a >= b_ref);
683        assert!(a > c_ref);
684        assert!(b <= a_ref);
685        assert!(c < a_ref);
686
687        assert!(a_ref >= b_ref);
688        assert!(a_ref > c_ref);
689        assert!(b_ref <= a_ref);
690        assert!(c_ref < a_ref);
691    }
692
693    #[test]
694    fn object_from_str() {
695        let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
696        assert_eq!(object.nid(), Nid::SHA256);
697    }
698
699    #[test]
700    fn object_from_str_with_invalid_input() {
701        Asn1Object::from_str("NOT AN OID")
702            .map(|object| object.to_string())
703            .expect_err("parsing invalid OID should fail");
704    }
705}