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