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