• 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::str;
36 
37 use crate::bio::MemBio;
38 use crate::bn::{BigNum, BigNumRef};
39 use crate::error::ErrorStack;
40 use crate::nid::Nid;
41 use crate::stack::Stackable;
42 use crate::string::OpensslString;
43 use crate::{cvt, cvt_p, util};
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/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 
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/manmaster/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 { 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)]
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/manmaster/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/manmaster/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 Ord for Asn1Integer {
cmp(&self, other: &Self) -> Ordering515     fn cmp(&self, other: &Self) -> Ordering {
516         Asn1IntegerRef::cmp(self, other)
517     }
518 }
519 impl PartialOrd for Asn1Integer {
partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering>520     fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> {
521         Some(self.cmp(other))
522     }
523 }
524 impl Eq for Asn1Integer {}
525 impl PartialEq for Asn1Integer {
eq(&self, other: &Asn1Integer) -> bool526     fn eq(&self, other: &Asn1Integer) -> bool {
527         Asn1IntegerRef::eq(self, other)
528     }
529 }
530 
531 impl Asn1IntegerRef {
532     #[allow(missing_docs, clippy::unnecessary_cast)]
533     #[deprecated(since = "0.10.6", note = "use to_bn instead")]
get(&self) -> i64534     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)]
to_bn(&self) -> Result<BigNum, ErrorStack>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)]
set(&mut self, value: i32) -> Result<(), ErrorStack>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)]
to_owned(&self) -> Result<Asn1Integer, ErrorStack>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 
563 impl Ord for Asn1IntegerRef {
cmp(&self, other: &Self) -> Ordering564     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 }
569 impl PartialOrd for Asn1IntegerRef {
partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering>570     fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> {
571         Some(self.cmp(other))
572     }
573 }
574 impl Eq for Asn1IntegerRef {}
575 impl PartialEq for Asn1IntegerRef {
eq(&self, other: &Asn1IntegerRef) -> bool576     fn eq(&self, other: &Asn1IntegerRef) -> bool {
577         self.cmp(other) == Ordering::Equal
578     }
579 }
580 
581 foreign_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 
595 impl Asn1BitStringRef {
596     /// Returns the Asn1BitString as a slice.
597     #[corresponds(ASN1_STRING_get0_data)]
as_slice(&self) -> &[u8]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)]
len(&self) -> usize604     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.
is_empty(&self) -> bool609     pub fn is_empty(&self) -> bool {
610         self.len() == 0
611     }
612 }
613 
614 foreign_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 
623 impl Asn1OctetString {
624     /// Creates an Asn1OctetString from bytes
new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack>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 
635 impl Asn1OctetStringRef {
636     /// Returns the octet string as an array of bytes.
637     #[corresponds(ASN1_STRING_get0_data)]
as_slice(&self) -> &[u8]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)]
len(&self) -> usize644     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.
is_empty(&self) -> bool649     pub fn is_empty(&self) -> bool {
650         self.len() == 0
651     }
652 }
653 
654 foreign_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 
677 impl Stackable for Asn1Object {
678     type StackType = ffi::stack_st_ASN1_OBJECT;
679 }
680 
681 impl 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)]
from_str(txt: &str) -> Result<Asn1Object, ErrorStack>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)]
as_slice(&self) -> &[u8]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 
708 impl Asn1ObjectRef {
709     /// Returns the NID associated with this OID.
nid(&self) -> Nid710     pub fn nid(&self) -> Nid {
711         unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
712     }
713 }
714 
715 impl fmt::Display for Asn1ObjectRef {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result716     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 
733 impl fmt::Debug for Asn1ObjectRef {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result734     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
735         fmt.write_str(self.to_string().as_str())
736     }
737 }
738 
739 cfg_if! {
740     if #[cfg(any(ossl110, libressl273, boringssl))] {
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 
750 foreign_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 
760 impl Asn1EnumeratedRef {
761     /// Get the value, if it fits in the required bounds.
762     #[corresponds(ASN1_ENUMERATED_get_int64)]
763     #[cfg(ossl110)]
get_i64(&self) -> Result<i64, ErrorStack>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)]
777 mod 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]
bn_cvt()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]
time_from_str()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]
time_from_unix()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(ossl102)]
time_eq()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(ossl102)]
time_ord()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]
integer_to_owned()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]
integer_cmp()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]
object_from_str()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]
object_from_str_with_invalid_input()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)]
object_to_slice()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]
asn1_octet_string()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 }
911