1 #![deny(missing_docs)] 2 3 //! Defines the format of certificates 4 //! 5 //! This module is used by [`x509`] and other certificate building functions 6 //! to describe time, strings, and objects. 7 //! 8 //! Abstract Syntax Notation One is an interface description language. 9 //! The specification comes from [X.208] by OSI, and rewritten in X.680. 10 //! ASN.1 describes properties of an object with a type set. Those types 11 //! can be atomic, structured, choice, and other (CHOICE and ANY). These 12 //! types are expressed as a number and the assignment operator ::= gives 13 //! the type a name. 14 //! 15 //! The implementation here provides a subset of the ASN.1 types that OpenSSL 16 //! uses, especially in the properties of a certificate used in HTTPS. 17 //! 18 //! [X.208]: https://www.itu.int/rec/T-REC-X.208-198811-W/en 19 //! [`x509`]: ../x509/struct.X509Builder.html 20 //! 21 //! ## Examples 22 //! 23 //! ``` 24 //! use openssl::asn1::Asn1Time; 25 //! let tomorrow = Asn1Time::days_from_now(1); 26 //! ``` 27 use cfg_if::cfg_if; 28 use foreign_types::{ForeignType, ForeignTypeRef}; 29 use libc::{c_char, c_int, c_long, time_t}; 30 #[cfg(ossl102)] 31 use std::cmp::Ordering; 32 use std::ffi::CString; 33 use std::fmt; 34 use std::ptr; 35 use std::slice; 36 use std::str; 37 38 use crate::bio::MemBio; 39 use crate::bn::{BigNum, BigNumRef}; 40 use crate::error::ErrorStack; 41 use crate::nid::Nid; 42 use crate::string::OpensslString; 43 use crate::{cvt, cvt_p}; 44 use openssl_macros::corresponds; 45 46 foreign_type_and_impl_send_sync! { 47 type CType = ffi::ASN1_GENERALIZEDTIME; 48 fn drop = ffi::ASN1_GENERALIZEDTIME_free; 49 50 /// Non-UTC representation of time 51 /// 52 /// If a time can be represented by UTCTime, UTCTime is used 53 /// otherwise, ASN1_GENERALIZEDTIME is used. This would be, for 54 /// example outside the year range of 1950-2049. 55 /// 56 /// [ASN1_GENERALIZEDTIME_set] documentation from OpenSSL provides 57 /// further details of implementation. Note: these docs are from the master 58 /// branch as documentation on the 1.1.0 branch did not include this page. 59 /// 60 /// [ASN1_GENERALIZEDTIME_set]: https://www.openssl.org/docs/manmaster/man3/ASN1_GENERALIZEDTIME_set.html 61 pub struct Asn1GeneralizedTime; 62 /// Reference to a [`Asn1GeneralizedTime`] 63 /// 64 /// [`Asn1GeneralizedTime`]: struct.Asn1GeneralizedTime.html 65 pub struct Asn1GeneralizedTimeRef; 66 } 67 68 impl fmt::Display for Asn1GeneralizedTimeRef { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 unsafe { 71 let mem_bio = match MemBio::new() { 72 Err(_) => return f.write_str("error"), 73 Ok(m) => m, 74 }; 75 let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print( 76 mem_bio.as_ptr(), 77 self.as_ptr(), 78 )); 79 match print_result { 80 Err(_) => f.write_str("error"), 81 Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())), 82 } 83 } 84 } 85 } 86 87 /// The type of an ASN.1 value. 88 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 89 pub struct Asn1Type(c_int); 90 91 #[allow(missing_docs)] // no need to document the constants 92 impl Asn1Type { 93 pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC); 94 95 pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN); 96 97 pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER); 98 99 pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING); 100 101 pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING); 102 103 pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL); 104 105 pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT); 106 107 pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR); 108 109 pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL); 110 111 pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL); 112 113 pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED); 114 115 pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING); 116 117 pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE); 118 119 pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET); 120 121 pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING); 122 123 pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING); 124 125 pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING); 126 127 pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING); 128 129 pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING); 130 131 pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING); 132 133 pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME); 134 135 pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME); 136 137 pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING); 138 139 pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING); 140 141 pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING); 142 143 pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING); 144 145 pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING); 146 147 pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING); 148 149 /// Constructs an `Asn1Type` from a raw OpenSSL value. from_raw(value: c_int) -> Self150 pub fn from_raw(value: c_int) -> Self { 151 Asn1Type(value) 152 } 153 154 /// Returns the raw OpenSSL value represented by this type. as_raw(&self) -> c_int155 pub fn as_raw(&self) -> c_int { 156 self.0 157 } 158 } 159 160 /// Difference between two ASN1 times. 161 /// 162 /// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its 163 /// documentation for more. 164 /// 165 /// [`diff`]: struct.Asn1TimeRef.html#method.diff 166 /// [`Asn1TimeRef`]: struct.Asn1TimeRef.html 167 #[derive(Debug, Clone, PartialEq, Eq, Hash)] 168 #[cfg(ossl102)] 169 pub struct TimeDiff { 170 /// Difference in days 171 pub days: c_int, 172 /// Difference in seconds. 173 /// 174 /// This is always less than the number of seconds in a day. 175 pub secs: c_int, 176 } 177 178 foreign_type_and_impl_send_sync! { 179 type CType = ffi::ASN1_TIME; 180 fn drop = ffi::ASN1_TIME_free; 181 /// Time storage and comparison 182 /// 183 /// Asn1Time should be used to store and share time information 184 /// using certificates. If Asn1Time is set using a string, it must 185 /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format. 186 /// 187 /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation 188 /// used by OpenSSL. 189 /// 190 /// [ASN_TIME_set]: https://www.openssl.org/docs/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 { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) } 460 } 461 462 /// Returns the number of bytes in the string. 463 #[corresponds(ASN1_STRING_length)] len(&self) -> usize464 pub fn len(&self) -> usize { 465 unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize } 466 } 467 468 /// Determines if the string is empty. is_empty(&self) -> bool469 pub fn is_empty(&self) -> bool { 470 self.len() == 0 471 } 472 } 473 474 impl fmt::Debug for Asn1StringRef { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result475 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 476 match self.as_utf8() { 477 Ok(openssl_string) => openssl_string.fmt(fmt), 478 Err(_) => fmt.write_str("error"), 479 } 480 } 481 } 482 483 foreign_type_and_impl_send_sync! { 484 type CType = ffi::ASN1_INTEGER; 485 fn drop = ffi::ASN1_INTEGER_free; 486 487 /// Numeric representation 488 /// 489 /// Integers in ASN.1 may include BigNum, int64 or uint64. BigNum implementation 490 /// can be found within [`bn`] module. 491 /// 492 /// OpenSSL documentation includes [`ASN1_INTEGER_set`]. 493 /// 494 /// [`bn`]: ../bn/index.html 495 /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/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 Asn1IntegerRef { 515 #[allow(missing_docs, clippy::unnecessary_cast)] 516 #[deprecated(since = "0.10.6", note = "use to_bn instead")] get(&self) -> i64517 pub fn get(&self) -> i64 { 518 unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 } 519 } 520 521 /// Converts the integer to a `BigNum`. 522 #[corresponds(ASN1_INTEGER_to_BN)] to_bn(&self) -> Result<BigNum, ErrorStack>523 pub fn to_bn(&self) -> Result<BigNum, ErrorStack> { 524 unsafe { 525 cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut())) 526 .map(|p| BigNum::from_ptr(p)) 527 } 528 } 529 530 /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers 531 /// see [`bn`]. 532 /// 533 /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer 534 #[corresponds(ASN1_INTEGER_set)] set(&mut self, value: i32) -> Result<(), ErrorStack>535 pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> { 536 unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) } 537 } 538 } 539 540 foreign_type_and_impl_send_sync! { 541 type CType = ffi::ASN1_BIT_STRING; 542 fn drop = ffi::ASN1_BIT_STRING_free; 543 /// Sequence of bytes 544 /// 545 /// Asn1BitString is used in [`x509`] certificates for the signature. 546 /// The bit string acts as a collection of bytes. 547 /// 548 /// [`x509`]: ../x509/struct.X509.html#method.signature 549 pub struct Asn1BitString; 550 /// A reference to an [`Asn1BitString`]. 551 pub struct Asn1BitStringRef; 552 } 553 554 impl Asn1BitStringRef { 555 /// Returns the Asn1BitString as a slice. 556 #[corresponds(ASN1_STRING_get0_data)] as_slice(&self) -> &[u8]557 pub fn as_slice(&self) -> &[u8] { 558 unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } 559 } 560 561 /// Returns the number of bytes in the string. 562 #[corresponds(ASN1_STRING_length)] len(&self) -> usize563 pub fn len(&self) -> usize { 564 unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize } 565 } 566 567 /// Determines if the string is empty. is_empty(&self) -> bool568 pub fn is_empty(&self) -> bool { 569 self.len() == 0 570 } 571 } 572 573 foreign_type_and_impl_send_sync! { 574 type CType = ffi::ASN1_OBJECT; 575 fn drop = ffi::ASN1_OBJECT_free; 576 577 /// Object Identifier 578 /// 579 /// Represents an ASN.1 Object. Typically, NIDs, or numeric identifiers 580 /// are stored as a table within the [`Nid`] module. These constants are 581 /// used to determine attributes of a certificate, such as mapping the 582 /// attribute "CommonName" to "CN" which is represented as the OID of 13. 583 /// This attribute is a constant in the [`nid::COMMONNAME`]. 584 /// 585 /// OpenSSL documentation at [`OBJ_nid2obj`] 586 /// 587 /// [`Nid`]: ../nid/index.html 588 /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html 589 /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/manmaster/crypto/OBJ_obj2nid.html 590 pub struct Asn1Object; 591 /// A reference to an [`Asn1Object`]. 592 pub struct Asn1ObjectRef; 593 } 594 595 impl Asn1Object { 596 /// Constructs an ASN.1 Object Identifier from a string representation of the OID. 597 #[corresponds(OBJ_txt2obj)] 598 #[allow(clippy::should_implement_trait)] from_str(txt: &str) -> Result<Asn1Object, ErrorStack>599 pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> { 600 unsafe { 601 ffi::init(); 602 let txt = CString::new(txt).unwrap(); 603 let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?; 604 Ok(Asn1Object::from_ptr(obj)) 605 } 606 } 607 608 /// Return the OID as an DER encoded array of bytes. This is the ASN.1 609 /// value, not including tag or length. 610 /// 611 /// Requires OpenSSL 1.1.1 or newer. 612 #[corresponds(OBJ_get0_data)] 613 #[cfg(ossl111)] as_slice(&self) -> &[u8]614 pub fn as_slice(&self) -> &[u8] { 615 unsafe { 616 let len = ffi::OBJ_length(self.as_ptr()); 617 slice::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len) 618 } 619 } 620 } 621 622 impl Asn1ObjectRef { 623 /// Returns the NID associated with this OID. nid(&self) -> Nid624 pub fn nid(&self) -> Nid { 625 unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) } 626 } 627 } 628 629 impl fmt::Display for Asn1ObjectRef { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result630 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 631 unsafe { 632 let mut buf = [0; 80]; 633 let len = ffi::OBJ_obj2txt( 634 buf.as_mut_ptr() as *mut _, 635 buf.len() as c_int, 636 self.as_ptr(), 637 0, 638 ); 639 match str::from_utf8(&buf[..len as usize]) { 640 Err(_) => fmt.write_str("error"), 641 Ok(s) => fmt.write_str(s), 642 } 643 } 644 } 645 } 646 647 impl fmt::Debug for Asn1ObjectRef { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result648 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 649 fmt.write_str(self.to_string().as_str()) 650 } 651 } 652 653 cfg_if! { 654 if #[cfg(any(ossl110, libressl273))] { 655 use ffi::ASN1_STRING_get0_data; 656 } else { 657 #[allow(bad_style)] 658 unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar { 659 ffi::ASN1_STRING_data(s) 660 } 661 } 662 } 663 664 #[cfg(test)] 665 mod tests { 666 use super::*; 667 668 use crate::bn::BigNum; 669 use crate::nid::Nid; 670 671 /// Tests conversion between BigNum and Asn1Integer. 672 #[test] bn_cvt()673 fn bn_cvt() { 674 fn roundtrip(bn: BigNum) { 675 let large = Asn1Integer::from_bn(&bn).unwrap(); 676 assert_eq!(large.to_bn().unwrap(), bn); 677 } 678 679 roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); 680 roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); 681 roundtrip(BigNum::from_u32(1234).unwrap()); 682 roundtrip(-BigNum::from_u32(1234).unwrap()); 683 } 684 685 #[test] time_from_str()686 fn time_from_str() { 687 Asn1Time::from_str("99991231235959Z").unwrap(); 688 #[cfg(ossl111)] 689 Asn1Time::from_str_x509("99991231235959Z").unwrap(); 690 } 691 692 #[test] time_from_unix()693 fn time_from_unix() { 694 let t = Asn1Time::from_unix(0).unwrap(); 695 assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string()); 696 } 697 698 #[test] 699 #[cfg(ossl102)] time_eq()700 fn time_eq() { 701 let a = Asn1Time::from_str("99991231235959Z").unwrap(); 702 let b = Asn1Time::from_str("99991231235959Z").unwrap(); 703 let c = Asn1Time::from_str("99991231235958Z").unwrap(); 704 let a_ref = a.as_ref(); 705 let b_ref = b.as_ref(); 706 let c_ref = c.as_ref(); 707 assert!(a == b); 708 assert!(a != c); 709 assert!(a == b_ref); 710 assert!(a != c_ref); 711 assert!(b_ref == a); 712 assert!(c_ref != a); 713 assert!(a_ref == b_ref); 714 assert!(a_ref != c_ref); 715 } 716 717 #[test] 718 #[cfg(ossl102)] time_ord()719 fn time_ord() { 720 let a = Asn1Time::from_str("99991231235959Z").unwrap(); 721 let b = Asn1Time::from_str("99991231235959Z").unwrap(); 722 let c = Asn1Time::from_str("99991231235958Z").unwrap(); 723 let a_ref = a.as_ref(); 724 let b_ref = b.as_ref(); 725 let c_ref = c.as_ref(); 726 assert!(a >= b); 727 assert!(a > c); 728 assert!(b <= a); 729 assert!(c < a); 730 731 assert!(a_ref >= b); 732 assert!(a_ref > c); 733 assert!(b_ref <= a); 734 assert!(c_ref < a); 735 736 assert!(a >= b_ref); 737 assert!(a > c_ref); 738 assert!(b <= a_ref); 739 assert!(c < a_ref); 740 741 assert!(a_ref >= b_ref); 742 assert!(a_ref > c_ref); 743 assert!(b_ref <= a_ref); 744 assert!(c_ref < a_ref); 745 } 746 747 #[test] object_from_str()748 fn object_from_str() { 749 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); 750 assert_eq!(object.nid(), Nid::SHA256); 751 } 752 753 #[test] object_from_str_with_invalid_input()754 fn object_from_str_with_invalid_input() { 755 Asn1Object::from_str("NOT AN OID") 756 .map(|object| object.to_string()) 757 .expect_err("parsing invalid OID should fail"); 758 } 759 760 #[test] 761 #[cfg(ossl111)] object_to_slice()762 fn object_to_slice() { 763 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); 764 assert_eq!( 765 object.as_slice(), 766 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01], 767 ); 768 } 769 } 770