• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! ```
27 use cfg_if::cfg_if;
28 use foreign_types::{ForeignType, ForeignTypeRef};
29 use libc::{c_char, c_int, c_long, time_t};
30 #[cfg(ossl102)]
31 use std::cmp::Ordering;
32 use std::ffi::CString;
33 use std::fmt;
34 use std::ptr;
35 use std::slice;
36 use std::str;
37 
38 use crate::bio::MemBio;
39 use crate::bn::{BigNum, BigNumRef};
40 use crate::error::ErrorStack;
41 use crate::nid::Nid;
42 use crate::string::OpensslString;
43 use crate::{cvt, cvt_p};
44 use openssl_macros::corresponds;
45 
46 foreign_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 
68 impl fmt::Display for Asn1GeneralizedTimeRef {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result69     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)]
89 pub struct Asn1Type(c_int);
90 
91 #[allow(missing_docs)] // no need to document the constants
92 impl 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.
from_raw(value: c_int) -> Self150     pub fn from_raw(value: c_int) -> Self {
151         Asn1Type(value)
152     }
153 
154     /// Returns the raw OpenSSL value represented by this type.
as_raw(&self) -> c_int155     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(ossl102)]
169 pub 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 
178 foreign_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/man1.1.0/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 
198 impl Asn1TimeRef {
199     /// Find difference between two times
200     #[corresponds(ASN1_TIME_diff)]
201     #[cfg(ossl102)]
diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack>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(ossl102)]
compare(&self, other: &Self) -> Result<Ordering, ErrorStack>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(ossl102)]
232 impl PartialEq for Asn1TimeRef {
eq(&self, other: &Asn1TimeRef) -> bool233     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(ossl102)]
241 impl PartialEq<Asn1Time> for Asn1TimeRef {
eq(&self, other: &Asn1Time) -> bool242     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(ossl102)]
250 impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
eq(&self, other: &Asn1Time) -> bool251     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(ossl102)]
259 impl PartialOrd for Asn1TimeRef {
partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering>260     fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
261         self.compare(other).ok()
262     }
263 }
264 
265 #[cfg(ossl102)]
266 impl PartialOrd<Asn1Time> for Asn1TimeRef {
partial_cmp(&self, other: &Asn1Time) -> Option<Ordering>267     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
268         self.compare(other).ok()
269     }
270 }
271 
272 #[cfg(ossl102)]
273 impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
partial_cmp(&self, other: &Asn1Time) -> Option<Ordering>274     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
275         self.compare(other).ok()
276     }
277 }
278 
279 impl fmt::Display for Asn1TimeRef {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result280     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 
295 impl fmt::Debug for Asn1TimeRef {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result296     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297         f.write_str(&self.to_string())
298     }
299 }
300 
301 impl Asn1Time {
302     #[corresponds(ASN1_TIME_new)]
new() -> Result<Asn1Time, ErrorStack>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)]
from_period(period: c_long) -> Result<Asn1Time, ErrorStack>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
days_from_now(days: u32) -> Result<Asn1Time, ErrorStack>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)]
from_unix(time: time_t) -> Result<Asn1Time, ErrorStack>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)]
from_str(s: &str) -> Result<Asn1Time, ErrorStack>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 OpenSSL 1.1.1 or newer.
355     #[corresponds(ASN1_TIME_set_string_X509)]
356     #[cfg(ossl111)]
from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack>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(ossl102)]
370 impl PartialEq for Asn1Time {
eq(&self, other: &Asn1Time) -> bool371     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(ossl102)]
379 impl PartialEq<Asn1TimeRef> for Asn1Time {
eq(&self, other: &Asn1TimeRef) -> bool380     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(ossl102)]
388 impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
eq(&self, other: &&'a Asn1TimeRef) -> bool389     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(ossl102)]
397 impl PartialOrd for Asn1Time {
partial_cmp(&self, other: &Asn1Time) -> Option<Ordering>398     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
399         self.compare(other).ok()
400     }
401 }
402 
403 #[cfg(ossl102)]
404 impl PartialOrd<Asn1TimeRef> for Asn1Time {
partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering>405     fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
406         self.compare(other).ok()
407     }
408 }
409 
410 #[cfg(ossl102)]
411 impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering>412     fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
413         self.compare(other).ok()
414     }
415 }
416 
417 foreign_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/man1.1.0/crypto/ASN1_STRING_to_UTF8.html
427     pub struct Asn1String;
428     /// A reference to an [`Asn1String`].
429     pub struct Asn1StringRef;
430 }
431 
432 impl 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)]
as_utf8(&self) -> Result<OpensslString, ErrorStack>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)]
as_slice(&self) -> &[u8]458     pub fn as_slice(&self) -> &[u8] {
459         unsafe { slice::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)]
len(&self) -> usize464     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.
is_empty(&self) -> bool469     pub fn is_empty(&self) -> bool {
470         self.len() == 0
471     }
472 }
473 
474 impl fmt::Debug for Asn1StringRef {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result475     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 
483 foreign_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/man1.1.0/crypto/ASN1_INTEGER_set.html
496     pub struct Asn1Integer;
497     /// A reference to an [`Asn1Integer`].
498     pub struct Asn1IntegerRef;
499 }
500 
501 impl 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/man1.1.0/crypto/BN_to_ASN1_INTEGER.html
508     /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack>509     pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
510         bn.to_asn1_integer()
511     }
512 }
513 
514 impl Asn1IntegerRef {
515     #[allow(missing_docs, clippy::unnecessary_cast)]
516     #[deprecated(since = "0.10.6", note = "use to_bn instead")]
get(&self) -> i64517     pub fn get(&self) -> i64 {
518         unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
519     }
520 
521     /// Converts the integer to a `BigNum`.
522     #[corresponds(ASN1_INTEGER_to_BN)]
to_bn(&self) -> Result<BigNum, ErrorStack>523     pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
524         unsafe {
525             cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut()))
526                 .map(|p| BigNum::from_ptr(p))
527         }
528     }
529 
530     /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers
531     /// see [`bn`].
532     ///
533     /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
534     #[corresponds(ASN1_INTEGER_set)]
set(&mut self, value: i32) -> Result<(), ErrorStack>535     pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
536         unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
537     }
538 }
539 
540 foreign_type_and_impl_send_sync! {
541     type CType = ffi::ASN1_BIT_STRING;
542     fn drop = ffi::ASN1_BIT_STRING_free;
543     /// Sequence of bytes
544     ///
545     /// Asn1BitString is used in [`x509`] certificates for the signature.
546     /// The bit string acts as a collection of bytes.
547     ///
548     /// [`x509`]: ../x509/struct.X509.html#method.signature
549     pub struct Asn1BitString;
550     /// A reference to an [`Asn1BitString`].
551     pub struct Asn1BitStringRef;
552 }
553 
554 impl Asn1BitStringRef {
555     /// Returns the Asn1BitString as a slice.
556     #[corresponds(ASN1_STRING_get0_data)]
as_slice(&self) -> &[u8]557     pub fn as_slice(&self) -> &[u8] {
558         unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
559     }
560 
561     /// Returns the number of bytes in the string.
562     #[corresponds(ASN1_STRING_length)]
len(&self) -> usize563     pub fn len(&self) -> usize {
564         unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
565     }
566 
567     /// Determines if the string is empty.
is_empty(&self) -> bool568     pub fn is_empty(&self) -> bool {
569         self.len() == 0
570     }
571 }
572 
573 foreign_type_and_impl_send_sync! {
574     type CType = ffi::ASN1_OBJECT;
575     fn drop = ffi::ASN1_OBJECT_free;
576 
577     /// Object Identifier
578     ///
579     /// Represents an ASN.1 Object.  Typically, NIDs, or numeric identifiers
580     /// are stored as a table within the [`Nid`] module.  These constants are
581     /// used to determine attributes of a certificate, such as mapping the
582     /// attribute "CommonName" to "CN" which is represented as the OID of 13.
583     /// This attribute is a constant in the [`nid::COMMONNAME`].
584     ///
585     /// OpenSSL documentation at [`OBJ_nid2obj`]
586     ///
587     /// [`Nid`]: ../nid/index.html
588     /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html
589     /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_obj2nid.html
590     pub struct Asn1Object;
591     /// A reference to an [`Asn1Object`].
592     pub struct Asn1ObjectRef;
593 }
594 
595 impl Asn1Object {
596     /// Constructs an ASN.1 Object Identifier from a string representation of the OID.
597     #[corresponds(OBJ_txt2obj)]
598     #[allow(clippy::should_implement_trait)]
from_str(txt: &str) -> Result<Asn1Object, ErrorStack>599     pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> {
600         unsafe {
601             ffi::init();
602             let txt = CString::new(txt).unwrap();
603             let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?;
604             Ok(Asn1Object::from_ptr(obj))
605         }
606     }
607 
608     /// Return the OID as an DER encoded array of bytes. This is the ASN.1
609     /// value, not including tag or length.
610     ///
611     /// Requires OpenSSL 1.1.1 or newer.
612     #[corresponds(OBJ_get0_data)]
613     #[cfg(ossl111)]
as_slice(&self) -> &[u8]614     pub fn as_slice(&self) -> &[u8] {
615         unsafe {
616             let len = ffi::OBJ_length(self.as_ptr());
617             slice::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len)
618         }
619     }
620 }
621 
622 impl Asn1ObjectRef {
623     /// Returns the NID associated with this OID.
nid(&self) -> Nid624     pub fn nid(&self) -> Nid {
625         unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
626     }
627 }
628 
629 impl fmt::Display for Asn1ObjectRef {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result630     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
631         unsafe {
632             let mut buf = [0; 80];
633             let len = ffi::OBJ_obj2txt(
634                 buf.as_mut_ptr() as *mut _,
635                 buf.len() as c_int,
636                 self.as_ptr(),
637                 0,
638             );
639             match str::from_utf8(&buf[..len as usize]) {
640                 Err(_) => fmt.write_str("error"),
641                 Ok(s) => fmt.write_str(s),
642             }
643         }
644     }
645 }
646 
647 impl fmt::Debug for Asn1ObjectRef {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result648     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
649         fmt.write_str(self.to_string().as_str())
650     }
651 }
652 
653 cfg_if! {
654     if #[cfg(any(ossl110, libressl273))] {
655         use ffi::ASN1_STRING_get0_data;
656     } else {
657         #[allow(bad_style)]
658         unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar {
659             ffi::ASN1_STRING_data(s)
660         }
661     }
662 }
663 
664 #[cfg(test)]
665 mod tests {
666     use super::*;
667 
668     use crate::bn::BigNum;
669     use crate::nid::Nid;
670 
671     /// Tests conversion between BigNum and Asn1Integer.
672     #[test]
bn_cvt()673     fn bn_cvt() {
674         fn roundtrip(bn: BigNum) {
675             let large = Asn1Integer::from_bn(&bn).unwrap();
676             assert_eq!(large.to_bn().unwrap(), bn);
677         }
678 
679         roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
680         roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
681         roundtrip(BigNum::from_u32(1234).unwrap());
682         roundtrip(-BigNum::from_u32(1234).unwrap());
683     }
684 
685     #[test]
time_from_str()686     fn time_from_str() {
687         Asn1Time::from_str("99991231235959Z").unwrap();
688         #[cfg(ossl111)]
689         Asn1Time::from_str_x509("99991231235959Z").unwrap();
690     }
691 
692     #[test]
time_from_unix()693     fn time_from_unix() {
694         let t = Asn1Time::from_unix(0).unwrap();
695         assert_eq!("Jan  1 00:00:00 1970 GMT", t.to_string());
696     }
697 
698     #[test]
699     #[cfg(ossl102)]
time_eq()700     fn time_eq() {
701         let a = Asn1Time::from_str("99991231235959Z").unwrap();
702         let b = Asn1Time::from_str("99991231235959Z").unwrap();
703         let c = Asn1Time::from_str("99991231235958Z").unwrap();
704         let a_ref = a.as_ref();
705         let b_ref = b.as_ref();
706         let c_ref = c.as_ref();
707         assert!(a == b);
708         assert!(a != c);
709         assert!(a == b_ref);
710         assert!(a != c_ref);
711         assert!(b_ref == a);
712         assert!(c_ref != a);
713         assert!(a_ref == b_ref);
714         assert!(a_ref != c_ref);
715     }
716 
717     #[test]
718     #[cfg(ossl102)]
time_ord()719     fn time_ord() {
720         let a = Asn1Time::from_str("99991231235959Z").unwrap();
721         let b = Asn1Time::from_str("99991231235959Z").unwrap();
722         let c = Asn1Time::from_str("99991231235958Z").unwrap();
723         let a_ref = a.as_ref();
724         let b_ref = b.as_ref();
725         let c_ref = c.as_ref();
726         assert!(a >= b);
727         assert!(a > c);
728         assert!(b <= a);
729         assert!(c < a);
730 
731         assert!(a_ref >= b);
732         assert!(a_ref > c);
733         assert!(b_ref <= a);
734         assert!(c_ref < a);
735 
736         assert!(a >= b_ref);
737         assert!(a > c_ref);
738         assert!(b <= a_ref);
739         assert!(c < a_ref);
740 
741         assert!(a_ref >= b_ref);
742         assert!(a_ref > c_ref);
743         assert!(b_ref <= a_ref);
744         assert!(c_ref < a_ref);
745     }
746 
747     #[test]
object_from_str()748     fn object_from_str() {
749         let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
750         assert_eq!(object.nid(), Nid::SHA256);
751     }
752 
753     #[test]
object_from_str_with_invalid_input()754     fn object_from_str_with_invalid_input() {
755         Asn1Object::from_str("NOT AN OID")
756             .map(|object| object.to_string())
757             .expect_err("parsing invalid OID should fail");
758     }
759 
760     #[test]
761     #[cfg(ossl111)]
object_to_slice()762     fn object_to_slice() {
763         let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
764         assert_eq!(
765             object.as_slice(),
766             &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01],
767         );
768     }
769 }
770