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