openssl/
asn1.rs

1#![deny(missing_docs)]
2
3//! Defines the format of certificates
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//! ```
27use 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    /// 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 implementation.  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://docs.openssl.org/master/man3/ASN1_GENERALIZEDTIME_set/
61    pub struct Asn1GeneralizedTime;
62    /// Reference to a [`Asn1GeneralizedTime`]
63    ///
64    /// [`Asn1GeneralizedTime`]: struct.Asn1GeneralizedTime.html
65    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    /// Creates a new generalized time corresponding to the specified ASN1 time
89    /// string.
90    #[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/// The type of an ASN.1 value.
111#[derive(Debug, Copy, Clone, PartialEq, Eq)]
112pub struct Asn1Type(c_int);
113
114#[allow(missing_docs)] // no need to document the constants
115impl 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    /// Constructs an `Asn1Type` from a raw OpenSSL value.
173    pub fn from_raw(value: c_int) -> Self {
174        Asn1Type(value)
175    }
176
177    /// Returns the raw OpenSSL value represented by this type.
178    pub fn as_raw(&self) -> c_int {
179        self.0
180    }
181}
182
183/// Difference between two ASN1 times.
184///
185/// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its
186/// documentation for more.
187///
188/// [`diff`]: struct.Asn1TimeRef.html#method.diff
189/// [`Asn1TimeRef`]: struct.Asn1TimeRef.html
190#[derive(Debug, Clone, PartialEq, Eq, Hash)]
191pub struct TimeDiff {
192    /// Difference in days
193    pub days: c_int,
194    /// Difference in seconds.
195    ///
196    /// This is always less than the number of seconds in a day.
197    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    /// Time storage and comparison
204    ///
205    /// Asn1Time should be used to store and share time information
206    /// using certificates.  If Asn1Time is set using a string, it must
207    /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format.
208    ///
209    /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation
210    /// used by OpenSSL.
211    ///
212    /// [ASN_TIME_set]: https://docs.openssl.org/master/man3/ASN1_TIME_set/
213    pub struct Asn1Time;
214    /// Reference to an [`Asn1Time`]
215    ///
216    /// [`Asn1Time`]: struct.Asn1Time.html
217    pub struct Asn1TimeRef;
218}
219
220impl Asn1TimeRef {
221    /// Find difference between two times
222    #[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    /// Compare two times
237    #[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    /// Creates a new time on specified interval in days from now
337    pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
338        Asn1Time::from_period(days as c_long * 60 * 60 * 24)
339    }
340
341    /// Creates a new time from the specified `time_t` value
342    #[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    /// Creates a new time corresponding to the specified ASN1 time string.
353    #[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    /// Creates a new time corresponding to the specified X509 time string.
367    ///
368    /// Requires BoringSSL or OpenSSL 1.1.1 or newer.
369    #[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    /// Primary ASN.1 type used by OpenSSL
429    ///
430    /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING
431    /// structures.  This implementation uses [ASN1_STRING-to_UTF8] to preserve
432    /// compatibility with Rust's String.
433    ///
434    /// [ASN1_STRING-to_UTF8]: https://docs.openssl.org/master/man3/ASN1_STRING_to_UTF8/
435    pub struct Asn1String;
436    /// A reference to an [`Asn1String`].
437    pub struct Asn1StringRef;
438}
439
440impl Asn1StringRef {
441    /// Converts the ASN.1 underlying format to UTF8
442    ///
443    /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8.  This is important to
444    /// consume the string in a meaningful way without knowing the underlying
445    /// format.
446    #[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    /// Return the string as an array of bytes.
460    ///
461    /// The bytes do not directly correspond to UTF-8 encoding.  To interact with
462    /// strings in rust, it is preferable to use [`as_utf8`]
463    ///
464    /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8
465    #[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    /// Returns the number of bytes in the string.
471    #[corresponds(ASN1_STRING_length)]
472    pub fn len(&self) -> usize {
473        unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
474    }
475
476    /// Determines if the string is empty.
477    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    /// Numeric representation
496    ///
497    /// Integers in ASN.1 may include BigNum, int64 or uint64.  BigNum implementation
498    /// can be found within [`bn`] module.
499    ///
500    /// OpenSSL documentation includes [`ASN1_INTEGER_set`].
501    ///
502    /// [`bn`]: ../bn/index.html
503    /// [`ASN1_INTEGER_set`]: https://docs.openssl.org/master/man3/ASN1_INTEGER_set/
504    pub struct Asn1Integer;
505    /// A reference to an [`Asn1Integer`].
506    pub struct Asn1IntegerRef;
507}
508
509impl Asn1Integer {
510    /// Converts a bignum to an `Asn1Integer`.
511    ///
512    /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see
513    /// [`BigNumRef::to_asn1_integer`].
514    ///
515    /// [`BN_to_ASN1_INTEGER`]: https://docs.openssl.org/master/man3/BN_to_ASN1_INTEGER/
516    /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
517    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    /// Converts the integer to a `BigNum`.
547    #[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    /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers
556    /// see [`bn`].
557    ///
558    /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
559    #[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    /// Creates a new Asn1Integer with the same value.
565    #[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    /// Sequence of bytes
593    ///
594    /// Asn1BitString is used in [`x509`] certificates for the signature.
595    /// The bit string acts as a collection of bytes.
596    ///
597    /// [`x509`]: ../x509/struct.X509.html#method.signature
598    pub struct Asn1BitString;
599    /// A reference to an [`Asn1BitString`].
600    pub struct Asn1BitStringRef;
601}
602
603impl Asn1BitStringRef {
604    /// Returns the Asn1BitString as a slice.
605    #[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    /// Returns the number of bytes in the string.
611    #[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    /// Determines if the string is empty.
617    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    /// ASN.1 OCTET STRING type
626    pub struct Asn1OctetString;
627    /// A reference to an [`Asn1OctetString`].
628    pub struct Asn1OctetStringRef;
629}
630
631impl Asn1OctetString {
632    /// Creates an Asn1OctetString from bytes
633    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    /// Returns the octet string as an array of bytes.
645    #[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    /// Returns the number of bytes in the octet string.
651    #[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    /// Determines if the string is empty.
657    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    /// Object Identifier
668    ///
669    /// Represents an ASN.1 Object.  Typically, NIDs, or numeric identifiers
670    /// are stored as a table within the [`Nid`] module.  These constants are
671    /// used to determine attributes of a certificate, such as mapping the
672    /// attribute "CommonName" to "CN" which is represented as the OID of 13.
673    /// This attribute is a constant in the [`nid::COMMONNAME`].
674    ///
675    /// OpenSSL documentation at [`OBJ_nid2obj`]
676    ///
677    /// [`Nid`]: ../nid/index.html
678    /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html
679    /// [`OBJ_nid2obj`]: https://docs.openssl.org/master/man3/OBJ_obj2nid/
680    pub struct Asn1Object;
681    /// A reference to an [`Asn1Object`].
682    pub struct Asn1ObjectRef;
683}
684
685impl Stackable for Asn1Object {
686    type StackType = ffi::stack_st_ASN1_OBJECT;
687}
688
689impl Asn1Object {
690    /// Constructs an ASN.1 Object Identifier from a string representation of the OID.
691    #[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    /// Return the OID as an DER encoded array of bytes. This is the ASN.1
703    /// value, not including tag or length.
704    ///
705    /// Requires OpenSSL 1.1.1 or newer.
706    #[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    /// Returns the NID associated with this OID.
718    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    /// An ASN.1 enumerated.
763    pub struct Asn1Enumerated;
764    /// A reference to an [`Asn1Enumerated`].
765    pub struct Asn1EnumeratedRef;
766}
767
768impl Asn1EnumeratedRef {
769    /// Get the value, if it fits in the required bounds.
770    #[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    /// Tests conversion between BigNum and Asn1Integer.
792    #[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}