Skip to main content

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