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::str; 36 37 use crate::bio::MemBio; 38 use crate::bn::{BigNum, BigNumRef}; 39 use crate::error::ErrorStack; 40 use crate::nid::Nid; 41 use crate::stack::Stackable; 42 use crate::string::OpensslString; 43 use crate::{cvt, cvt_p, util}; 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 { util::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 Ord for Asn1Integer { cmp(&self, other: &Self) -> Ordering515 fn cmp(&self, other: &Self) -> Ordering { 516 Asn1IntegerRef::cmp(self, other) 517 } 518 } 519 impl PartialOrd for Asn1Integer { partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering>520 fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> { 521 Some(self.cmp(other)) 522 } 523 } 524 impl Eq for Asn1Integer {} 525 impl PartialEq for Asn1Integer { eq(&self, other: &Asn1Integer) -> bool526 fn eq(&self, other: &Asn1Integer) -> bool { 527 Asn1IntegerRef::eq(self, other) 528 } 529 } 530 531 impl Asn1IntegerRef { 532 #[allow(missing_docs, clippy::unnecessary_cast)] 533 #[deprecated(since = "0.10.6", note = "use to_bn instead")] get(&self) -> i64534 pub fn get(&self) -> i64 { 535 unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 } 536 } 537 538 /// Converts the integer to a `BigNum`. 539 #[corresponds(ASN1_INTEGER_to_BN)] to_bn(&self) -> Result<BigNum, ErrorStack>540 pub fn to_bn(&self) -> Result<BigNum, ErrorStack> { 541 unsafe { 542 cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut())) 543 .map(|p| BigNum::from_ptr(p)) 544 } 545 } 546 547 /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers 548 /// see [`bn`]. 549 /// 550 /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer 551 #[corresponds(ASN1_INTEGER_set)] set(&mut self, value: i32) -> Result<(), ErrorStack>552 pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> { 553 unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) } 554 } 555 556 /// Creates a new Asn1Integer with the same value. 557 #[corresponds(ASN1_INTEGER_dup)] to_owned(&self) -> Result<Asn1Integer, ErrorStack>558 pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> { 559 unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) } 560 } 561 } 562 563 impl Ord for Asn1IntegerRef { cmp(&self, other: &Self) -> Ordering564 fn cmp(&self, other: &Self) -> Ordering { 565 let res = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), other.as_ptr()) }; 566 res.cmp(&0) 567 } 568 } 569 impl PartialOrd for Asn1IntegerRef { partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering>570 fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> { 571 Some(self.cmp(other)) 572 } 573 } 574 impl Eq for Asn1IntegerRef {} 575 impl PartialEq for Asn1IntegerRef { eq(&self, other: &Asn1IntegerRef) -> bool576 fn eq(&self, other: &Asn1IntegerRef) -> bool { 577 self.cmp(other) == Ordering::Equal 578 } 579 } 580 581 foreign_type_and_impl_send_sync! { 582 type CType = ffi::ASN1_BIT_STRING; 583 fn drop = ffi::ASN1_BIT_STRING_free; 584 /// Sequence of bytes 585 /// 586 /// Asn1BitString is used in [`x509`] certificates for the signature. 587 /// The bit string acts as a collection of bytes. 588 /// 589 /// [`x509`]: ../x509/struct.X509.html#method.signature 590 pub struct Asn1BitString; 591 /// A reference to an [`Asn1BitString`]. 592 pub struct Asn1BitStringRef; 593 } 594 595 impl Asn1BitStringRef { 596 /// Returns the Asn1BitString as a slice. 597 #[corresponds(ASN1_STRING_get0_data)] as_slice(&self) -> &[u8]598 pub fn as_slice(&self) -> &[u8] { 599 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } 600 } 601 602 /// Returns the number of bytes in the string. 603 #[corresponds(ASN1_STRING_length)] len(&self) -> usize604 pub fn len(&self) -> usize { 605 unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize } 606 } 607 608 /// Determines if the string is empty. is_empty(&self) -> bool609 pub fn is_empty(&self) -> bool { 610 self.len() == 0 611 } 612 } 613 614 foreign_type_and_impl_send_sync! { 615 type CType = ffi::ASN1_OCTET_STRING; 616 fn drop = ffi::ASN1_OCTET_STRING_free; 617 /// ASN.1 OCTET STRING type 618 pub struct Asn1OctetString; 619 /// A reference to an [`Asn1OctetString`]. 620 pub struct Asn1OctetStringRef; 621 } 622 623 impl Asn1OctetString { 624 /// Creates an Asn1OctetString from bytes new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack>625 pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> { 626 ffi::init(); 627 unsafe { 628 let s = cvt_p(ffi::ASN1_OCTET_STRING_new())?; 629 ffi::ASN1_OCTET_STRING_set(s, value.as_ptr(), value.len().try_into().unwrap()); 630 Ok(Self::from_ptr(s)) 631 } 632 } 633 } 634 635 impl Asn1OctetStringRef { 636 /// Returns the octet string as an array of bytes. 637 #[corresponds(ASN1_STRING_get0_data)] as_slice(&self) -> &[u8]638 pub fn as_slice(&self) -> &[u8] { 639 unsafe { util::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) } 640 } 641 642 /// Returns the number of bytes in the octet string. 643 #[corresponds(ASN1_STRING_length)] len(&self) -> usize644 pub fn len(&self) -> usize { 645 unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize } 646 } 647 648 /// Determines if the string is empty. is_empty(&self) -> bool649 pub fn is_empty(&self) -> bool { 650 self.len() == 0 651 } 652 } 653 654 foreign_type_and_impl_send_sync! { 655 type CType = ffi::ASN1_OBJECT; 656 fn drop = ffi::ASN1_OBJECT_free; 657 fn clone = ffi::OBJ_dup; 658 659 /// Object Identifier 660 /// 661 /// Represents an ASN.1 Object. Typically, NIDs, or numeric identifiers 662 /// are stored as a table within the [`Nid`] module. These constants are 663 /// used to determine attributes of a certificate, such as mapping the 664 /// attribute "CommonName" to "CN" which is represented as the OID of 13. 665 /// This attribute is a constant in the [`nid::COMMONNAME`]. 666 /// 667 /// OpenSSL documentation at [`OBJ_nid2obj`] 668 /// 669 /// [`Nid`]: ../nid/index.html 670 /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html 671 /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/manmaster/crypto/OBJ_obj2nid.html 672 pub struct Asn1Object; 673 /// A reference to an [`Asn1Object`]. 674 pub struct Asn1ObjectRef; 675 } 676 677 impl Stackable for Asn1Object { 678 type StackType = ffi::stack_st_ASN1_OBJECT; 679 } 680 681 impl Asn1Object { 682 /// Constructs an ASN.1 Object Identifier from a string representation of the OID. 683 #[corresponds(OBJ_txt2obj)] 684 #[allow(clippy::should_implement_trait)] from_str(txt: &str) -> Result<Asn1Object, ErrorStack>685 pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> { 686 unsafe { 687 ffi::init(); 688 let txt = CString::new(txt).unwrap(); 689 let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?; 690 Ok(Asn1Object::from_ptr(obj)) 691 } 692 } 693 694 /// Return the OID as an DER encoded array of bytes. This is the ASN.1 695 /// value, not including tag or length. 696 /// 697 /// Requires OpenSSL 1.1.1 or newer. 698 #[corresponds(OBJ_get0_data)] 699 #[cfg(ossl111)] as_slice(&self) -> &[u8]700 pub fn as_slice(&self) -> &[u8] { 701 unsafe { 702 let len = ffi::OBJ_length(self.as_ptr()); 703 util::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len) 704 } 705 } 706 } 707 708 impl Asn1ObjectRef { 709 /// Returns the NID associated with this OID. nid(&self) -> Nid710 pub fn nid(&self) -> Nid { 711 unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) } 712 } 713 } 714 715 impl fmt::Display for Asn1ObjectRef { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result716 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 717 unsafe { 718 let mut buf = [0; 80]; 719 let len = ffi::OBJ_obj2txt( 720 buf.as_mut_ptr() as *mut _, 721 buf.len() as c_int, 722 self.as_ptr(), 723 0, 724 ); 725 match str::from_utf8(&buf[..len as usize]) { 726 Err(_) => fmt.write_str("error"), 727 Ok(s) => fmt.write_str(s), 728 } 729 } 730 } 731 } 732 733 impl fmt::Debug for Asn1ObjectRef { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result734 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 735 fmt.write_str(self.to_string().as_str()) 736 } 737 } 738 739 cfg_if! { 740 if #[cfg(any(ossl110, libressl273, boringssl))] { 741 use ffi::ASN1_STRING_get0_data; 742 } else { 743 #[allow(bad_style)] 744 unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar { 745 ffi::ASN1_STRING_data(s) 746 } 747 } 748 } 749 750 foreign_type_and_impl_send_sync! { 751 type CType = ffi::ASN1_ENUMERATED; 752 fn drop = ffi::ASN1_ENUMERATED_free; 753 754 /// An ASN.1 enumerated. 755 pub struct Asn1Enumerated; 756 /// A reference to an [`Asn1Enumerated`]. 757 pub struct Asn1EnumeratedRef; 758 } 759 760 impl Asn1EnumeratedRef { 761 /// Get the value, if it fits in the required bounds. 762 #[corresponds(ASN1_ENUMERATED_get_int64)] 763 #[cfg(ossl110)] get_i64(&self) -> Result<i64, ErrorStack>764 pub fn get_i64(&self) -> Result<i64, ErrorStack> { 765 let mut crl_reason = 0; 766 unsafe { 767 cvt(ffi::ASN1_ENUMERATED_get_int64( 768 &mut crl_reason, 769 self.as_ptr(), 770 ))?; 771 } 772 Ok(crl_reason) 773 } 774 } 775 776 #[cfg(test)] 777 mod tests { 778 use super::*; 779 780 use crate::bn::BigNum; 781 use crate::nid::Nid; 782 783 /// Tests conversion between BigNum and Asn1Integer. 784 #[test] bn_cvt()785 fn bn_cvt() { 786 fn roundtrip(bn: BigNum) { 787 let large = Asn1Integer::from_bn(&bn).unwrap(); 788 assert_eq!(large.to_bn().unwrap(), bn); 789 } 790 791 roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); 792 roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); 793 roundtrip(BigNum::from_u32(1234).unwrap()); 794 roundtrip(-BigNum::from_u32(1234).unwrap()); 795 } 796 797 #[test] time_from_str()798 fn time_from_str() { 799 Asn1Time::from_str("99991231235959Z").unwrap(); 800 #[cfg(ossl111)] 801 Asn1Time::from_str_x509("99991231235959Z").unwrap(); 802 } 803 804 #[test] time_from_unix()805 fn time_from_unix() { 806 let t = Asn1Time::from_unix(0).unwrap(); 807 assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string()); 808 } 809 810 #[test] 811 #[cfg(ossl102)] time_eq()812 fn time_eq() { 813 let a = Asn1Time::from_str("99991231235959Z").unwrap(); 814 let b = Asn1Time::from_str("99991231235959Z").unwrap(); 815 let c = Asn1Time::from_str("99991231235958Z").unwrap(); 816 let a_ref = a.as_ref(); 817 let b_ref = b.as_ref(); 818 let c_ref = c.as_ref(); 819 assert!(a == b); 820 assert!(a != c); 821 assert!(a == b_ref); 822 assert!(a != c_ref); 823 assert!(b_ref == a); 824 assert!(c_ref != a); 825 assert!(a_ref == b_ref); 826 assert!(a_ref != c_ref); 827 } 828 829 #[test] 830 #[cfg(ossl102)] time_ord()831 fn time_ord() { 832 let a = Asn1Time::from_str("99991231235959Z").unwrap(); 833 let b = Asn1Time::from_str("99991231235959Z").unwrap(); 834 let c = Asn1Time::from_str("99991231235958Z").unwrap(); 835 let a_ref = a.as_ref(); 836 let b_ref = b.as_ref(); 837 let c_ref = c.as_ref(); 838 assert!(a >= b); 839 assert!(a > c); 840 assert!(b <= a); 841 assert!(c < a); 842 843 assert!(a_ref >= b); 844 assert!(a_ref > c); 845 assert!(b_ref <= a); 846 assert!(c_ref < a); 847 848 assert!(a >= b_ref); 849 assert!(a > c_ref); 850 assert!(b <= a_ref); 851 assert!(c < a_ref); 852 853 assert!(a_ref >= b_ref); 854 assert!(a_ref > c_ref); 855 assert!(b_ref <= a_ref); 856 assert!(c_ref < a_ref); 857 } 858 859 #[test] integer_to_owned()860 fn integer_to_owned() { 861 let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap(); 862 let b = a.to_owned().unwrap(); 863 assert_eq!( 864 a.to_bn().unwrap().to_dec_str().unwrap().to_string(), 865 b.to_bn().unwrap().to_dec_str().unwrap().to_string(), 866 ); 867 assert_ne!(a.as_ptr(), b.as_ptr()); 868 } 869 870 #[test] integer_cmp()871 fn integer_cmp() { 872 let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap(); 873 let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap(); 874 let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43").unwrap()).unwrap(); 875 assert!(a == b); 876 assert!(a != c); 877 assert!(a < c); 878 assert!(c > b); 879 } 880 881 #[test] object_from_str()882 fn object_from_str() { 883 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); 884 assert_eq!(object.nid(), Nid::SHA256); 885 } 886 887 #[test] object_from_str_with_invalid_input()888 fn object_from_str_with_invalid_input() { 889 Asn1Object::from_str("NOT AN OID") 890 .map(|object| object.to_string()) 891 .expect_err("parsing invalid OID should fail"); 892 } 893 894 #[test] 895 #[cfg(ossl111)] object_to_slice()896 fn object_to_slice() { 897 let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); 898 assert_eq!( 899 object.as_slice(), 900 &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01], 901 ); 902 } 903 904 #[test] asn1_octet_string()905 fn asn1_octet_string() { 906 let octet_string = Asn1OctetString::new_from_bytes(b"hello world").unwrap(); 907 assert_eq!(octet_string.as_slice(), b"hello world"); 908 assert_eq!(octet_string.len(), 11); 909 } 910 } 911