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