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