Skip to main content

variant_ssl/
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 foreign_types::{ForeignType, ForeignTypeRef};
28use libc::{c_char, c_int, c_long, c_void, time_t};
29use std::cmp::Ordering;
30use std::convert::TryInto;
31use std::ffi::CString;
32use std::fmt;
33use std::ptr;
34use std::str;
35
36use crate::bio::MemBio;
37use crate::bn::{BigNum, BigNumRef};
38use crate::error::ErrorStack;
39use crate::nid::Nid;
40use crate::stack::Stackable;
41use crate::string::OpensslString;
42use crate::{cvt, cvt_p, util};
43use openssl_macros::corresponds;
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 implementation.  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://docs.openssl.org/master/man3/ASN1_GENERALIZEDTIME_set/
60    pub struct Asn1GeneralizedTime;
61    /// Reference to a [`Asn1GeneralizedTime`]
62    ///
63    /// [`Asn1GeneralizedTime`]: struct.Asn1GeneralizedTime.html
64    pub struct Asn1GeneralizedTimeRef;
65}
66
67impl fmt::Display for Asn1GeneralizedTimeRef {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        unsafe {
70            let mem_bio = match MemBio::new() {
71                Err(_) => return f.write_str("error"),
72                Ok(m) => m,
73            };
74            let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
75                mem_bio.as_ptr(),
76                self.as_ptr(),
77            ));
78            match print_result {
79                Err(_) => f.write_str("error"),
80                Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
81            }
82        }
83    }
84}
85
86impl Asn1GeneralizedTime {
87    /// Creates a new generalized time corresponding to the specified ASN1 time
88    /// string.
89    #[corresponds(ASN1_GENERALIZEDTIME_set_string)]
90    #[allow(clippy::should_implement_trait)]
91    pub fn from_str(s: &str) -> Result<Asn1GeneralizedTime, ErrorStack> {
92        unsafe {
93            ffi::init();
94
95            let time_str = CString::new(s).unwrap();
96            let ptr = cvt_p(ffi::ASN1_GENERALIZEDTIME_new())?;
97            let time = Asn1GeneralizedTime::from_ptr(ptr);
98
99            cvt(ffi::ASN1_GENERALIZEDTIME_set_string(
100                time.as_ptr(),
101                time_str.as_ptr(),
102            ))?;
103
104            Ok(time)
105        }
106    }
107}
108
109/// The type of an ASN.1 value.
110#[derive(Debug, Copy, Clone, PartialEq, Eq)]
111pub struct Asn1Type(c_int);
112
113#[allow(missing_docs)] // no need to document the constants
114impl Asn1Type {
115    pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC);
116
117    pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN);
118
119    pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER);
120
121    pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING);
122
123    pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING);
124
125    pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL);
126
127    pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT);
128
129    pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR);
130
131    pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL);
132
133    pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL);
134
135    pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED);
136
137    pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING);
138
139    pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE);
140
141    pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET);
142
143    pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING);
144
145    pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING);
146
147    pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING);
148
149    pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING);
150
151    pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING);
152
153    pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING);
154
155    pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME);
156
157    pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME);
158
159    pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING);
160
161    pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING);
162
163    pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING);
164
165    pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING);
166
167    pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING);
168
169    pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING);
170
171    /// Constructs an `Asn1Type` from a raw OpenSSL value.
172    pub fn from_raw(value: c_int) -> Self {
173        Asn1Type(value)
174    }
175
176    /// Returns the raw OpenSSL value represented by this type.
177    pub fn as_raw(&self) -> c_int {
178        self.0
179    }
180}
181
182/// Difference between two ASN1 times.
183///
184/// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its
185/// documentation for more.
186///
187/// [`diff`]: struct.Asn1TimeRef.html#method.diff
188/// [`Asn1TimeRef`]: struct.Asn1TimeRef.html
189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190pub struct TimeDiff {
191    /// Difference in days
192    pub days: c_int,
193    /// Difference in seconds.
194    ///
195    /// This is always less than the number of seconds in a day.
196    pub secs: c_int,
197}
198
199foreign_type_and_impl_send_sync! {
200    type CType = ffi::ASN1_TIME;
201    fn drop = ffi::ASN1_TIME_free;
202    /// Time storage and comparison
203    ///
204    /// Asn1Time should be used to store and share time information
205    /// using certificates.  If Asn1Time is set using a string, it must
206    /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format.
207    ///
208    /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation
209    /// used by OpenSSL.
210    ///
211    /// [ASN_TIME_set]: https://docs.openssl.org/master/man3/ASN1_TIME_set/
212    pub struct Asn1Time;
213    /// Reference to an [`Asn1Time`]
214    ///
215    /// [`Asn1Time`]: struct.Asn1Time.html
216    pub struct Asn1TimeRef;
217}
218
219impl Asn1TimeRef {
220    /// Find difference between two times
221    #[corresponds(ASN1_TIME_diff)]
222    pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
223        let mut days = 0;
224        let mut secs = 0;
225        let other = compare.as_ptr();
226
227        let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) };
228
229        match err {
230            0 => Err(ErrorStack::get()),
231            _ => Ok(TimeDiff { days, secs }),
232        }
233    }
234
235    /// Compare two times
236    #[corresponds(ASN1_TIME_compare)]
237    pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
238        let d = self.diff(other)?;
239        if d.days > 0 || d.secs > 0 {
240            return Ok(Ordering::Less);
241        }
242        if d.days < 0 || d.secs < 0 {
243            return Ok(Ordering::Greater);
244        }
245
246        Ok(Ordering::Equal)
247    }
248}
249
250impl PartialEq for Asn1TimeRef {
251    fn eq(&self, other: &Asn1TimeRef) -> bool {
252        self.diff(other)
253            .map(|t| t.days == 0 && t.secs == 0)
254            .unwrap_or(false)
255    }
256}
257
258impl PartialEq<Asn1Time> for Asn1TimeRef {
259    fn eq(&self, other: &Asn1Time) -> bool {
260        self.diff(other)
261            .map(|t| t.days == 0 && t.secs == 0)
262            .unwrap_or(false)
263    }
264}
265
266impl PartialEq<Asn1Time> for &Asn1TimeRef {
267    fn eq(&self, other: &Asn1Time) -> bool {
268        self.diff(other)
269            .map(|t| t.days == 0 && t.secs == 0)
270            .unwrap_or(false)
271    }
272}
273
274impl PartialOrd for Asn1TimeRef {
275    fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
276        self.compare(other).ok()
277    }
278}
279
280impl PartialOrd<Asn1Time> for Asn1TimeRef {
281    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
282        self.compare(other).ok()
283    }
284}
285
286impl PartialOrd<Asn1Time> for &Asn1TimeRef {
287    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
288        self.compare(other).ok()
289    }
290}
291
292impl fmt::Display for Asn1TimeRef {
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294        unsafe {
295            let mem_bio = match MemBio::new() {
296                Err(_) => return f.write_str("error"),
297                Ok(m) => m,
298            };
299            let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()));
300            match print_result {
301                Err(_) => f.write_str("error"),
302                Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
303            }
304        }
305    }
306}
307
308impl fmt::Debug for Asn1TimeRef {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        f.write_str(&self.to_string())
311    }
312}
313
314impl Asn1Time {
315    #[corresponds(ASN1_TIME_new)]
316    fn new() -> Result<Asn1Time, ErrorStack> {
317        ffi::init();
318
319        unsafe {
320            let handle = cvt_p(ffi::ASN1_TIME_new())?;
321            Ok(Asn1Time::from_ptr(handle))
322        }
323    }
324
325    #[corresponds(X509_gmtime_adj)]
326    fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
327        ffi::init();
328
329        unsafe {
330            let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?;
331            Ok(Asn1Time::from_ptr(handle))
332        }
333    }
334
335    /// Creates a new time on specified interval in days from now
336    pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
337        Asn1Time::from_period(days as c_long * 60 * 60 * 24)
338    }
339
340    /// Creates a new time from the specified `time_t` value
341    #[corresponds(ASN1_TIME_set)]
342    pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
343        ffi::init();
344
345        unsafe {
346            let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
347            Ok(Asn1Time::from_ptr(handle))
348        }
349    }
350
351    /// Creates a new time corresponding to the specified ASN1 time string.
352    #[corresponds(ASN1_TIME_set_string)]
353    #[allow(clippy::should_implement_trait)]
354    pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
355        unsafe {
356            let s = CString::new(s).unwrap();
357
358            let time = Asn1Time::new()?;
359            cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
360
361            Ok(time)
362        }
363    }
364
365    /// Creates a new time corresponding to the specified X509 time string.
366    ///
367    /// Requires BoringSSL, AWS-LC, OpenSSL 1.1.1, LibreSSL 3.6.0, or newer.
368    #[corresponds(ASN1_TIME_set_string_X509)]
369    #[cfg(any(ossl111, boringssl, libressl360, awslc))]
370    pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> {
371        unsafe {
372            let s = CString::new(s).unwrap();
373
374            let time = Asn1Time::new()?;
375            cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?;
376
377            Ok(time)
378        }
379    }
380}
381
382impl PartialEq for Asn1Time {
383    fn eq(&self, other: &Asn1Time) -> bool {
384        self.diff(other)
385            .map(|t| t.days == 0 && t.secs == 0)
386            .unwrap_or(false)
387    }
388}
389
390impl PartialEq<Asn1TimeRef> for Asn1Time {
391    fn eq(&self, other: &Asn1TimeRef) -> bool {
392        self.diff(other)
393            .map(|t| t.days == 0 && t.secs == 0)
394            .unwrap_or(false)
395    }
396}
397
398impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
399    fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
400        self.diff(other)
401            .map(|t| t.days == 0 && t.secs == 0)
402            .unwrap_or(false)
403    }
404}
405
406impl PartialOrd for Asn1Time {
407    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
408        self.compare(other).ok()
409    }
410}
411
412impl PartialOrd<Asn1TimeRef> for Asn1Time {
413    fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
414        self.compare(other).ok()
415    }
416}
417
418impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
419    fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
420        self.compare(other).ok()
421    }
422}
423
424foreign_type_and_impl_send_sync! {
425    type CType = ffi::ASN1_STRING;
426    fn drop = ffi::ASN1_STRING_free;
427    /// Primary ASN.1 type used by OpenSSL
428    ///
429    /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING
430    /// structures.  This implementation uses [ASN1_STRING-to_UTF8] to preserve
431    /// compatibility with Rust's String.
432    ///
433    /// [ASN1_STRING-to_UTF8]: https://docs.openssl.org/master/man3/ASN1_STRING_to_UTF8/
434    pub struct Asn1String;
435    /// A reference to an [`Asn1String`].
436    pub struct Asn1StringRef;
437}
438
439impl Asn1StringRef {
440    /// Converts the ASN.1 underlying format to UTF8
441    ///
442    /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8.  This is important to
443    /// consume the string in a meaningful way without knowing the underlying
444    /// format.
445    #[corresponds(ASN1_STRING_to_UTF8)]
446    #[deprecated(
447        since = "0.10.81",
448        note = "truncates at the first interior NUL byte; use `to_string` instead"
449    )]
450    pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
451        unsafe {
452            let mut ptr = ptr::null_mut();
453            let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
454            if len < 0 {
455                return Err(ErrorStack::get());
456            }
457
458            Ok(OpensslString::from_ptr(ptr as *mut c_char))
459        }
460    }
461
462    /// Converts the ASN.1 underlying format to a UTF-8 string.
463    ///
464    /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8.  This is important to
465    /// consume the string in a meaningful way without knowing the underlying
466    /// format.
467    ///
468    /// The full contents of the string are preserved, including any interior
469    /// NUL bytes. Any bytes that do not form valid UTF-8 after conversion are
470    /// replaced with U+FFFD.
471    #[corresponds(ASN1_STRING_to_UTF8)]
472    pub fn to_string(&self) -> Result<String, ErrorStack> {
473        // For string types whose conversion to UTF-8 is the identity
474        // function, copy directly out of the underlying buffer, avoiding
475        // ASN1_STRING_to_UTF8's intermediate allocation of it.
476        match unsafe { ffi::ASN1_STRING_type(self.as_ptr()) } {
477            ffi::V_ASN1_UTF8STRING => {
478                if let Ok(s) = str::from_utf8(self.as_slice()) {
479                    return Ok(s.to_owned());
480                }
481            }
482            // Latin-1 types, whose UTF-8 conversion is the identity on ASCII.
483            ffi::V_ASN1_NUMERICSTRING
484            | ffi::V_ASN1_PRINTABLESTRING
485            | ffi::V_ASN1_T61STRING
486            | ffi::V_ASN1_IA5STRING
487            | ffi::V_ASN1_VISIBLESTRING => {
488                let slice = self.as_slice();
489                if slice.is_ascii() {
490                    // SAFETY: ASCII is valid UTF-8.
491                    return Ok(unsafe { str::from_utf8_unchecked(slice) }.to_owned());
492                }
493            }
494            _ => {}
495        }
496
497        unsafe {
498            let mut ptr = ptr::null_mut();
499            let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
500            if len < 0 {
501                return Err(ErrorStack::get());
502            }
503
504            // This copies the buffer exactly once: for valid UTF-8,
505            // from_utf8_lossy is a no-op returning Cow::Borrowed and
506            // into_owned performs the copy; for invalid UTF-8, from_utf8_lossy
507            // copies with replacements and into_owned is a no-op.
508            let s = String::from_utf8_lossy(util::from_raw_parts(ptr, len as usize)).into_owned();
509            openssl_free(ptr.cast());
510            Ok(s)
511        }
512    }
513
514    /// Return the string as an array of bytes.
515    ///
516    /// The bytes do not directly correspond to UTF-8 encoding.  To interact with
517    /// strings in rust, it is preferable to use [`to_string`]
518    ///
519    /// [`to_string`]: struct.Asn1StringRef.html#method.to_string
520    #[corresponds(ASN1_STRING_get0_data)]
521    pub fn as_slice(&self) -> &[u8] {
522        unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
523    }
524
525    /// Returns the number of bytes in the string.
526    #[corresponds(ASN1_STRING_length)]
527    pub fn len(&self) -> usize {
528        unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
529    }
530
531    /// Determines if the string is empty.
532    pub fn is_empty(&self) -> bool {
533        self.len() == 0
534    }
535}
536
537impl fmt::Debug for Asn1StringRef {
538    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
539        match self.to_string() {
540            Ok(string) => string.fmt(fmt),
541            Err(_) => fmt.write_str("error"),
542        }
543    }
544}
545
546#[inline]
547#[cfg(not(any(boringssl, awslc)))]
548unsafe fn openssl_free(buf: *mut c_void) {
549    ffi::OPENSSL_free(buf);
550}
551
552#[inline]
553#[cfg(any(boringssl, awslc))]
554unsafe fn openssl_free(buf: *mut c_void) {
555    ffi::CRYPTO_free(
556        buf,
557        concat!(file!(), "\0").as_ptr() as *const c_char,
558        line!() as c_int,
559    );
560}
561
562foreign_type_and_impl_send_sync! {
563    type CType = ffi::ASN1_INTEGER;
564    fn drop = ffi::ASN1_INTEGER_free;
565
566    /// Numeric representation
567    ///
568    /// Integers in ASN.1 may include BigNum, int64 or uint64.  BigNum implementation
569    /// can be found within [`bn`] module.
570    ///
571    /// OpenSSL documentation includes [`ASN1_INTEGER_set`].
572    ///
573    /// [`bn`]: ../bn/index.html
574    /// [`ASN1_INTEGER_set`]: https://docs.openssl.org/master/man3/ASN1_INTEGER_set/
575    pub struct Asn1Integer;
576    /// A reference to an [`Asn1Integer`].
577    pub struct Asn1IntegerRef;
578}
579
580impl Asn1Integer {
581    /// Converts a bignum to an `Asn1Integer`.
582    ///
583    /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see
584    /// [`BigNumRef::to_asn1_integer`].
585    ///
586    /// [`BN_to_ASN1_INTEGER`]: https://docs.openssl.org/master/man3/BN_to_ASN1_INTEGER/
587    /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
588    pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
589        bn.to_asn1_integer()
590    }
591}
592
593impl Ord for Asn1Integer {
594    fn cmp(&self, other: &Self) -> Ordering {
595        Asn1IntegerRef::cmp(self, other)
596    }
597}
598impl PartialOrd for Asn1Integer {
599    fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> {
600        Some(self.cmp(other))
601    }
602}
603impl Eq for Asn1Integer {}
604impl PartialEq for Asn1Integer {
605    fn eq(&self, other: &Asn1Integer) -> bool {
606        Asn1IntegerRef::eq(self, other)
607    }
608}
609
610impl Asn1IntegerRef {
611    #[allow(missing_docs, clippy::unnecessary_cast)]
612    #[deprecated(since = "0.10.6", note = "use to_bn instead")]
613    pub fn get(&self) -> i64 {
614        unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
615    }
616
617    /// Converts the integer to a `BigNum`.
618    #[corresponds(ASN1_INTEGER_to_BN)]
619    pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
620        unsafe {
621            cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut()))
622                .map(|p| BigNum::from_ptr(p))
623        }
624    }
625
626    /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers
627    /// see [`bn`].
628    ///
629    /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
630    #[corresponds(ASN1_INTEGER_set)]
631    pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
632        unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
633    }
634
635    /// Creates a new Asn1Integer with the same value.
636    #[corresponds(ASN1_INTEGER_dup)]
637    pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> {
638        unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) }
639    }
640}
641
642impl Ord for Asn1IntegerRef {
643    fn cmp(&self, other: &Self) -> Ordering {
644        let res = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), other.as_ptr()) };
645        res.cmp(&0)
646    }
647}
648impl PartialOrd for Asn1IntegerRef {
649    fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> {
650        Some(self.cmp(other))
651    }
652}
653impl Eq for Asn1IntegerRef {}
654impl PartialEq for Asn1IntegerRef {
655    fn eq(&self, other: &Asn1IntegerRef) -> bool {
656        self.cmp(other) == Ordering::Equal
657    }
658}
659
660foreign_type_and_impl_send_sync! {
661    type CType = ffi::ASN1_BIT_STRING;
662    fn drop = ffi::ASN1_BIT_STRING_free;
663    /// Sequence of bytes
664    ///
665    /// Asn1BitString is used in [`x509`] certificates for the signature.
666    /// The bit string acts as a collection of bytes.
667    ///
668    /// [`x509`]: ../x509/struct.X509.html#method.signature
669    pub struct Asn1BitString;
670    /// A reference to an [`Asn1BitString`].
671    pub struct Asn1BitStringRef;
672}
673
674impl Asn1BitStringRef {
675    /// Returns the Asn1BitString as a slice.
676    #[corresponds(ASN1_STRING_get0_data)]
677    pub fn as_slice(&self) -> &[u8] {
678        unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
679    }
680
681    /// Returns the number of bytes in the string.
682    #[corresponds(ASN1_STRING_length)]
683    pub fn len(&self) -> usize {
684        unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
685    }
686
687    /// Determines if the string is empty.
688    pub fn is_empty(&self) -> bool {
689        self.len() == 0
690    }
691}
692
693foreign_type_and_impl_send_sync! {
694    type CType = ffi::ASN1_OCTET_STRING;
695    fn drop = ffi::ASN1_OCTET_STRING_free;
696    /// ASN.1 OCTET STRING type
697    pub struct Asn1OctetString;
698    /// A reference to an [`Asn1OctetString`].
699    pub struct Asn1OctetStringRef;
700}
701
702impl Asn1OctetString {
703    /// Creates an Asn1OctetString from bytes
704    pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> {
705        ffi::init();
706        unsafe {
707            let s = cvt_p(ffi::ASN1_OCTET_STRING_new())?;
708            ffi::ASN1_OCTET_STRING_set(s, value.as_ptr(), value.len().try_into().unwrap());
709            Ok(Self::from_ptr(s))
710        }
711    }
712}
713
714impl Asn1OctetStringRef {
715    /// Returns the octet string as an array of bytes.
716    #[corresponds(ASN1_STRING_get0_data)]
717    pub fn as_slice(&self) -> &[u8] {
718        unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) }
719    }
720
721    /// Returns the number of bytes in the octet string.
722    #[corresponds(ASN1_STRING_length)]
723    pub fn len(&self) -> usize {
724        unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize }
725    }
726
727    /// Determines if the string is empty.
728    pub fn is_empty(&self) -> bool {
729        self.len() == 0
730    }
731}
732
733foreign_type_and_impl_send_sync! {
734    type CType = ffi::ASN1_OBJECT;
735    fn drop = ffi::ASN1_OBJECT_free;
736    fn clone = ffi::OBJ_dup;
737
738    /// Object Identifier
739    ///
740    /// Represents an ASN.1 Object.  Typically, NIDs, or numeric identifiers
741    /// are stored as a table within the [`Nid`] module.  These constants are
742    /// used to determine attributes of a certificate, such as mapping the
743    /// attribute "CommonName" to "CN" which is represented as the OID of 13.
744    /// This attribute is a constant in the [`nid::COMMONNAME`].
745    ///
746    /// OpenSSL documentation at [`OBJ_nid2obj`]
747    ///
748    /// [`Nid`]: ../nid/index.html
749    /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html
750    /// [`OBJ_nid2obj`]: https://docs.openssl.org/master/man3/OBJ_obj2nid/
751    pub struct Asn1Object;
752    /// A reference to an [`Asn1Object`].
753    pub struct Asn1ObjectRef;
754}
755
756impl Stackable for Asn1Object {
757    type StackType = ffi::stack_st_ASN1_OBJECT;
758}
759
760impl Asn1Object {
761    /// Constructs an ASN.1 Object Identifier from a string representation of the OID.
762    #[corresponds(OBJ_txt2obj)]
763    #[allow(clippy::should_implement_trait)]
764    pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> {
765        unsafe {
766            ffi::init();
767            let txt = CString::new(txt).unwrap();
768            let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?;
769            Ok(Asn1Object::from_ptr(obj))
770        }
771    }
772
773    /// Return the OID as an DER encoded array of bytes. This is the ASN.1
774    /// value, not including tag or length.
775    ///
776    /// Requires OpenSSL 1.1.1 or newer.
777    #[corresponds(OBJ_get0_data)]
778    #[cfg(ossl111)]
779    pub fn as_slice(&self) -> &[u8] {
780        unsafe {
781            let len = ffi::OBJ_length(self.as_ptr());
782            util::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len)
783        }
784    }
785}
786
787impl Asn1ObjectRef {
788    /// Returns the NID associated with this OID.
789    pub fn nid(&self) -> Nid {
790        unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
791    }
792}
793
794impl fmt::Display for Asn1ObjectRef {
795    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
796        unsafe {
797            let mut buf = [0; 80];
798            let mut clamped = false;
799            let mut len = ffi::OBJ_obj2txt(
800                buf.as_mut_ptr() as *mut _,
801                buf.len() as c_int,
802                self.as_ptr(),
803                0,
804            );
805            if len <= 0 {
806                return fmt.write_str("OBJ_obj2txt error");
807            }
808            if len > buf.len() as i32 {
809                // omit trailing NUL
810                len = (buf.len() - 1) as i32;
811                clamped = true;
812            }
813            match str::from_utf8(&buf[..len as usize]) {
814                Err(_) => fmt.write_str("error"),
815                Ok(s) => {
816                    if clamped {
817                        fmt.write_str(&(s.to_owned() + "..."))
818                    } else {
819                        fmt.write_str(s)
820                    }
821                }
822            }
823        }
824    }
825}
826
827impl fmt::Debug for Asn1ObjectRef {
828    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
829        fmt.write_str(self.to_string().as_str())
830    }
831}
832
833use ffi::ASN1_STRING_get0_data;
834
835foreign_type_and_impl_send_sync! {
836    type CType = ffi::ASN1_ENUMERATED;
837    fn drop = ffi::ASN1_ENUMERATED_free;
838
839    /// An ASN.1 enumerated.
840    pub struct Asn1Enumerated;
841    /// A reference to an [`Asn1Enumerated`].
842    pub struct Asn1EnumeratedRef;
843}
844
845impl Asn1EnumeratedRef {
846    /// Get the value, if it fits in the required bounds.
847    #[corresponds(ASN1_ENUMERATED_get_int64)]
848    #[cfg(ossl110)]
849    pub fn get_i64(&self) -> Result<i64, ErrorStack> {
850        let mut crl_reason = 0;
851        unsafe {
852            cvt(ffi::ASN1_ENUMERATED_get_int64(
853                &mut crl_reason,
854                self.as_ptr(),
855            ))?;
856        }
857        Ok(crl_reason)
858    }
859}
860
861#[cfg(test)]
862mod tests {
863    use super::*;
864
865    use crate::bn::BigNum;
866    use crate::nid::Nid;
867
868    /// Tests conversion between BigNum and Asn1Integer.
869    #[test]
870    fn bn_cvt() {
871        fn roundtrip(bn: BigNum) {
872            let large = Asn1Integer::from_bn(&bn).unwrap();
873            assert_eq!(large.to_bn().unwrap(), bn);
874        }
875
876        roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
877        roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
878        roundtrip(BigNum::from_u32(1234).unwrap());
879        roundtrip(-BigNum::from_u32(1234).unwrap());
880    }
881
882    /// Tests that interior NUL bytes are preserved when converting to UTF-8.
883    #[test]
884    fn string_with_interior_nul() {
885        fn make_string(typ: c_int, data: &[u8]) -> Asn1String {
886            unsafe {
887                let ptr = cvt_p(ffi::ASN1_STRING_type_new(typ)).unwrap();
888                let s = Asn1String::from_ptr(ptr);
889                cvt(ffi::ASN1_STRING_set(
890                    s.as_ptr(),
891                    data.as_ptr().cast(),
892                    data.len().try_into().unwrap(),
893                ))
894                .unwrap();
895                s
896            }
897        }
898
899        // Copied directly out of the underlying buffer.
900        let s = make_string(ffi::V_ASN1_UTF8STRING, b"foo\0bar.com");
901        assert_eq!(s.as_slice(), b"foo\0bar.com");
902        assert_eq!(s.to_string().unwrap(), "foo\0bar.com");
903
904        let s = make_string(ffi::V_ASN1_IA5STRING, b"foo\0bar.com");
905        assert_eq!(s.to_string().unwrap(), "foo\0bar.com");
906
907        // Converted through ASN1_STRING_to_UTF8.
908        let s = make_string(ffi::V_ASN1_BMPSTRING, b"\0f\0\0\0o");
909        assert_eq!(s.to_string().unwrap(), "f\0o");
910    }
911
912    #[test]
913    fn time_from_str() {
914        Asn1Time::from_str("99991231235959Z").unwrap();
915        #[cfg(any(ossl111, boringssl, libressl360, awslc))]
916        Asn1Time::from_str_x509("99991231235959Z").unwrap();
917    }
918
919    #[test]
920    fn generalized_time_from_str() {
921        let time = Asn1GeneralizedTime::from_str("99991231235959Z").unwrap();
922        assert_eq!("Dec 31 23:59:59 9999 GMT", time.to_string());
923    }
924
925    #[test]
926    fn time_from_unix() {
927        let t = Asn1Time::from_unix(0).unwrap();
928        assert_eq!("Jan  1 00:00:00 1970 GMT", t.to_string());
929    }
930
931    #[test]
932    fn time_eq() {
933        let a = Asn1Time::from_str("99991231235959Z").unwrap();
934        let b = Asn1Time::from_str("99991231235959Z").unwrap();
935        let c = Asn1Time::from_str("99991231235958Z").unwrap();
936        let a_ref = a.as_ref();
937        let b_ref = b.as_ref();
938        let c_ref = c.as_ref();
939        assert!(a == b);
940        assert!(a != c);
941        assert!(a == b_ref);
942        assert!(a != c_ref);
943        assert!(b_ref == a);
944        assert!(c_ref != a);
945        assert!(a_ref == b_ref);
946        assert!(a_ref != c_ref);
947    }
948
949    #[test]
950    fn time_ord() {
951        let a = Asn1Time::from_str("99991231235959Z").unwrap();
952        let b = Asn1Time::from_str("99991231235959Z").unwrap();
953        let c = Asn1Time::from_str("99991231235958Z").unwrap();
954        let a_ref = a.as_ref();
955        let b_ref = b.as_ref();
956        let c_ref = c.as_ref();
957        assert!(a >= b);
958        assert!(a > c);
959        assert!(b <= a);
960        assert!(c < a);
961
962        assert!(a_ref >= b);
963        assert!(a_ref > c);
964        assert!(b_ref <= a);
965        assert!(c_ref < a);
966
967        assert!(a >= b_ref);
968        assert!(a > c_ref);
969        assert!(b <= a_ref);
970        assert!(c < a_ref);
971
972        assert!(a_ref >= b_ref);
973        assert!(a_ref > c_ref);
974        assert!(b_ref <= a_ref);
975        assert!(c_ref < a_ref);
976    }
977
978    #[test]
979    fn integer_to_owned() {
980        let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
981        let b = a.to_owned().unwrap();
982        assert_eq!(
983            a.to_bn().unwrap().to_dec_str().unwrap().to_string(),
984            b.to_bn().unwrap().to_dec_str().unwrap().to_string(),
985        );
986        assert_ne!(a.as_ptr(), b.as_ptr());
987    }
988
989    #[test]
990    fn integer_cmp() {
991        let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
992        let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
993        let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43").unwrap()).unwrap();
994        assert!(a == b);
995        assert!(a != c);
996        assert!(a < c);
997        assert!(c > b);
998    }
999
1000    #[test]
1001    fn object_from_str() {
1002        let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
1003        assert_eq!(object.nid(), Nid::SHA256);
1004    }
1005
1006    #[test]
1007    fn object_from_str_with_invalid_input() {
1008        Asn1Object::from_str("NOT AN OID")
1009            .map(|object| object.to_string())
1010            .expect_err("parsing invalid OID should fail");
1011    }
1012
1013    #[test]
1014    fn very_long_object() {
1015        let fifty_ones = "1.".repeat(49) + "1";
1016        let object = Asn1Object::from_str(&fifty_ones).unwrap();
1017        assert_eq!(object.as_ref().to_string(), "1.".repeat(40) + "..");
1018    }
1019
1020    #[test]
1021    #[cfg(ossl111)]
1022    fn object_to_slice() {
1023        let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
1024        assert_eq!(
1025            object.as_slice(),
1026            &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01],
1027        );
1028    }
1029
1030    #[test]
1031    fn asn1_octet_string() {
1032        let octet_string = Asn1OctetString::new_from_bytes(b"hello world").unwrap();
1033        assert_eq!(octet_string.as_slice(), b"hello world");
1034        assert_eq!(octet_string.len(), 11);
1035    }
1036}