1#![deny(missing_docs)]
2
3//! Defines the format of certificates
4//!
5//! This module is used by [`x509`] and other certificate building functions
6//! to describe time, strings, and objects.
7//!
8//! Abstract Syntax Notation One is an interface description language.
9//! The specification comes from [X.208] by OSI, and rewritten in X.680.
10//! ASN.1 describes properties of an object with a type set. Those types
11//! can be atomic, structured, choice, and other (CHOICE and ANY). These
12//! types are expressed as a number and the assignment operator ::= gives
13//! the type a name.
14//!
15//! The implementation here provides a subset of the ASN.1 types that OpenSSL
16//! uses, especially in the properties of a certificate used in HTTPS.
17//!
18//! [X.208]: https://www.itu.int/rec/T-REC-X.208-198811-W/en
19//! [`x509`]: ../x509/struct.X509Builder.html
20//!
21//! ## Examples
22//!
23//! ```
24//! use openssl::asn1::Asn1Time;
25//! let tomorrow = Asn1Time::days_from_now(1);
26//! ```
27use cfg_if::cfg_if;
28use foreign_types::{ForeignType, ForeignTypeRef};
29use libc::{c_char, c_int, c_long, time_t};
30use std::cmp::Ordering;
31use std::convert::TryInto;
32use std::ffi::CString;
33use std::fmt;
34use std::ptr;
35use std::slice;
36use std::str;
37
38use crate::bio::MemBio;
39use crate::bn::{BigNum, BigNumRef};
40use crate::error::ErrorStack;
41use crate::nid::Nid;
42use crate::stack::Stackable;
43use crate::string::OpensslString;
44use crate::{cvt, cvt_p};
45use openssl_macros::corresponds;
46
47foreign_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
69impl fmt::Display for Asn1GeneralizedTimeRef {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 unsafe {
72 let mem_bio: MemBio = match MemBio::new() {
73 Err(_) => return f.write_str(data:"error"),
74 Ok(m: MemBio) => m,
75 };
76 let print_result: Result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
77 b:mem_bio.as_ptr(),
78 self.as_ptr(),
79 ));
80 match print_result {
81 Err(_) => f.write_str(data:"error"),
82 Ok(_) => f.write_str(data: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)]
90pub struct Asn1Type(c_int);
91
92#[allow(missing_docs)] // no need to document the constants
93impl 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.
151 pub fn from_raw(value: c_int) -> Self {
152 Asn1Type(value)
153 }
154
155 /// Returns the raw OpenSSL value represented by this type.
156 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(any(ossl102, boringssl))]
170pub 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
179foreign_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
199impl Asn1TimeRef {
200 /// Find difference between two times
201 #[corresponds(ASN1_TIME_diff)]
202 #[cfg(any(ossl102, boringssl))]
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(any(ossl102, boringssl))]
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(any(ossl102, boringssl))]
233impl PartialEq for Asn1TimeRef {
234 fn eq(&self, other: &Asn1TimeRef) -> bool {
235 self.diff(other)
236 .map(|t| t.days == 0 && t.secs == 0)
237 .unwrap_or(default:false)
238 }
239}
240
241#[cfg(any(ossl102, boringssl))]
242impl PartialEq<Asn1Time> for Asn1TimeRef {
243 fn eq(&self, other: &Asn1Time) -> bool {
244 self.diff(other)
245 .map(|t| t.days == 0 && t.secs == 0)
246 .unwrap_or(default:false)
247 }
248}
249
250#[cfg(any(ossl102, boringssl))]
251impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
252 fn eq(&self, other: &Asn1Time) -> bool {
253 self.diff(other)
254 .map(|t| t.days == 0 && t.secs == 0)
255 .unwrap_or(default:false)
256 }
257}
258
259#[cfg(any(ossl102, boringssl))]
260impl PartialOrd for Asn1TimeRef {
261 fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
262 self.compare(other).ok()
263 }
264}
265
266#[cfg(any(ossl102, boringssl))]
267impl PartialOrd<Asn1Time> for Asn1TimeRef {
268 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
269 self.compare(other).ok()
270 }
271}
272
273#[cfg(any(ossl102, boringssl))]
274impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
275 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
276 self.compare(other).ok()
277 }
278}
279
280impl fmt::Display for Asn1TimeRef {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 unsafe {
283 let mem_bio: MemBio = match MemBio::new() {
284 Err(_) => return f.write_str(data:"error"),
285 Ok(m: MemBio) => m,
286 };
287 let print_result: Result = cvt(ffi::ASN1_TIME_print(b:mem_bio.as_ptr(), self.as_ptr()));
288 match print_result {
289 Err(_) => f.write_str(data:"error"),
290 Ok(_) => f.write_str(data:str::from_utf8_unchecked(mem_bio.get_buf())),
291 }
292 }
293 }
294}
295
296impl fmt::Debug for Asn1TimeRef {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 f.write_str(&self.to_string())
299 }
300}
301
302impl Asn1Time {
303 #[corresponds(ASN1_TIME_new)]
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)]
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
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)]
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)]
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 BoringSSL or OpenSSL 1.1.1 or newer.
356 #[corresponds(ASN1_TIME_set_string_X509)]
357 #[cfg(any(ossl111, boringssl))]
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(any(ossl102, boringssl))]
371impl PartialEq for Asn1Time {
372 fn eq(&self, other: &Asn1Time) -> bool {
373 self.diff(other)
374 .map(|t| t.days == 0 && t.secs == 0)
375 .unwrap_or(default:false)
376 }
377}
378
379#[cfg(any(ossl102, boringssl))]
380impl PartialEq<Asn1TimeRef> for Asn1Time {
381 fn eq(&self, other: &Asn1TimeRef) -> bool {
382 self.diff(other)
383 .map(|t| t.days == 0 && t.secs == 0)
384 .unwrap_or(default:false)
385 }
386}
387
388#[cfg(any(ossl102, boringssl))]
389impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
390 fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
391 self.diff(other)
392 .map(|t| t.days == 0 && t.secs == 0)
393 .unwrap_or(default:false)
394 }
395}
396
397#[cfg(any(ossl102, boringssl))]
398impl PartialOrd for Asn1Time {
399 fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
400 self.compare(other).ok()
401 }
402}
403
404#[cfg(any(ossl102, boringssl))]
405impl PartialOrd<Asn1TimeRef> for Asn1Time {
406 fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
407 self.compare(other).ok()
408 }
409}
410
411#[cfg(any(ossl102, boringssl))]
412impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
413 fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
414 self.compare(other).ok()
415 }
416}
417
418foreign_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
433impl 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)]
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)]
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)]
465 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.
470 pub fn is_empty(&self) -> bool {
471 self.len() == 0
472 }
473}
474
475impl fmt::Debug for Asn1StringRef {
476 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
477 match self.as_utf8() {
478 Ok(openssl_string: OpensslString) => openssl_string.fmt(fmt),
479 Err(_) => fmt.write_str(data:"error"),
480 }
481 }
482}
483
484foreign_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
502impl 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
510 pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
511 bn.to_asn1_integer()
512 }
513}
514
515impl Ord for Asn1Integer {
516 fn cmp(&self, other: &Self) -> Ordering {
517 Asn1IntegerRef::cmp(self, other)
518 }
519}
520impl PartialOrd for Asn1Integer {
521 fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> {
522 Some(self.cmp(other))
523 }
524}
525impl Eq for Asn1Integer {}
526impl PartialEq for Asn1Integer {
527 fn eq(&self, other: &Asn1Integer) -> bool {
528 Asn1IntegerRef::eq(self, other)
529 }
530}
531
532impl Asn1IntegerRef {
533 #[allow(missing_docs, clippy::unnecessary_cast)]
534 #[deprecated(since = "0.10.6", note = "use to_bn instead")]
535 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)]
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)]
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)]
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
564impl Ord for Asn1IntegerRef {
565 fn cmp(&self, other: &Self) -> Ordering {
566 let res: i32 = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), b:other.as_ptr()) };
567 res.cmp(&0)
568 }
569}
570impl PartialOrd for Asn1IntegerRef {
571 fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> {
572 Some(self.cmp(other))
573 }
574}
575impl Eq for Asn1IntegerRef {}
576impl PartialEq for Asn1IntegerRef {
577 fn eq(&self, other: &Asn1IntegerRef) -> bool {
578 self.cmp(other) == Ordering::Equal
579 }
580}
581
582foreign_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
596impl Asn1BitStringRef {
597 /// Returns the Asn1BitString as a slice.
598 #[corresponds(ASN1_STRING_get0_data)]
599 pub fn as_slice(&self) -> &[u8] {
600 unsafe { slice::from_raw_parts(data: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)]
605 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.
610 pub fn is_empty(&self) -> bool {
611 self.len() == 0
612 }
613}
614
615foreign_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
624impl Asn1OctetString {
625 /// Creates an Asn1OctetString from bytes
626 pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> {
627 ffi::init();
628 unsafe {
629 let s: *mut ASN1_OCTET_STRING = cvt_p(ffi::ASN1_OCTET_STRING_new())?;
630 ffi::ASN1_OCTET_STRING_set(x:s, data:value.as_ptr(), len_in:value.len().try_into().unwrap());
631 Ok(Self::from_ptr(s))
632 }
633 }
634}
635
636impl Asn1OctetStringRef {
637 /// Returns the octet string as an array of bytes.
638 #[corresponds(ASN1_STRING_get0_data)]
639 pub fn as_slice(&self) -> &[u8] {
640 unsafe { slice::from_raw_parts(data: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)]
645 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.
650 pub fn is_empty(&self) -> bool {
651 self.len() == 0
652 }
653}
654
655foreign_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
678impl Stackable for Asn1Object {
679 type StackType = ffi::stack_st_ASN1_OBJECT;
680}
681
682impl 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)]
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)]
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
709impl Asn1ObjectRef {
710 /// Returns the NID associated with this OID.
711 pub fn nid(&self) -> Nid {
712 unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
713 }
714}
715
716impl fmt::Display for Asn1ObjectRef {
717 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
718 unsafe {
719 let mut buf: [u8; 80] = [0; 80];
720 let len: i32 = ffi::OBJ_obj2txt(
721 buf:buf.as_mut_ptr() as *mut _,
722 buf_len:buf.len() as c_int,
723 self.as_ptr(),
724 no_name:0,
725 );
726 match str::from_utf8(&buf[..len as usize]) {
727 Err(_) => fmt.write_str(data:"error"),
728 Ok(s: &str) => fmt.write_str(data:s),
729 }
730 }
731 }
732}
733
734impl fmt::Debug for Asn1ObjectRef {
735 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
736 fmt.write_str(self.to_string().as_str())
737 }
738}
739
740cfg_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
751foreign_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
761impl Asn1EnumeratedRef {
762 /// Get the value, if it fits in the required bounds.
763 #[corresponds(ASN1_ENUMERATED_get_int64)]
764 #[cfg(ossl110)]
765 pub fn get_i64(&self) -> Result<i64, ErrorStack> {
766 let mut crl_reason: i64 = 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)]
778mod 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]
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]
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]
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(any(ossl102, boringssl))]
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(any(ossl102, boringssl))]
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]
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]
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]
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]
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)]
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]
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