diff --git a/openssl-sys/src/asn1.rs b/openssl-sys/src/asn1.rs index 042b3eca8324c6362be62a421441d8f1da3f3c60..2f2bf1b134b8d11ee5c78f9f006b90da34700a9a 100644 --- a/openssl-sys/src/asn1.rs +++ b/openssl-sys/src/asn1.rs @@ -39,8 +39,11 @@ extern "C" { pub fn ASN1_GENERALIZEDTIME_free(tm: *mut ASN1_GENERALIZEDTIME); pub fn ASN1_GENERALIZEDTIME_print(b: *mut BIO, tm: *const ASN1_GENERALIZEDTIME) -> c_int; pub fn ASN1_TIME_new() -> *mut ASN1_TIME; + #[cfg(ossl102)] + pub fn ASN1_TIME_diff(pday: *mut c_int, psec: *mut c_int, from: *const ASN1_TIME, to: *const ASN1_TIME) -> c_int; pub fn ASN1_TIME_free(tm: *mut ASN1_TIME); pub fn ASN1_TIME_print(b: *mut BIO, tm: *const ASN1_TIME) -> c_int; + pub fn ASN1_TIME_set(from: *mut ASN1_TIME, to: time_t) -> *mut ASN1_TIME; pub fn ASN1_INTEGER_free(x: *mut ASN1_INTEGER); pub fn ASN1_INTEGER_get(dest: *const ASN1_INTEGER) -> c_long; diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index c06a800fe4dc03e75c39b1919b666559134d6753..6cd47f06dbf08bb4e54a8289915adf5da1b786ad 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -26,7 +26,9 @@ //! ``` use ffi; use foreign_types::{ForeignType, ForeignTypeRef}; -use libc::{c_char, c_int, c_long}; +use libc::{c_char, c_int, c_long, time_t}; +#[cfg(ossl102)] +use std::cmp::Ordering; use std::ffi::CString; use std::fmt; use std::ptr; @@ -75,6 +77,24 @@ impl fmt::Display for Asn1GeneralizedTimeRef { } } +/// Difference between two ASN1 times. +/// +/// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its +/// documentation for more. +/// +/// [`diff`]: struct.Asn1TimeRef.html#method.diff +/// [`Asn1TimeRef`]: struct.Asn1TimeRef.html +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg(ossl102)] +pub struct TimeDiff { + /// Difference in days + pub days: c_int, + /// Difference in seconds. + /// + /// This is always less than the number of seconds in a day. + pub secs: c_int, +} + foreign_type_and_impl_send_sync! { type CType = ffi::ASN1_TIME; fn drop = ffi::ASN1_TIME_free; @@ -95,6 +115,94 @@ foreign_type_and_impl_send_sync! { pub struct Asn1TimeRef; } +impl Asn1TimeRef { + /// Find difference between two times + /// + /// This corresponds to [`ASN1_TIME_diff`]. + /// + /// [`ASN1_TIME_diff`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_diff.html + #[cfg(ossl102)] + pub fn diff(&self, compare: &Self) -> Result { + let mut days = 0; + let mut seconds = 0; + let other = compare.as_ptr(); + + let err = unsafe { + ffi::ASN1_TIME_diff(&mut days, &mut seconds, self.as_ptr(), other) + }; + + match err { + 0 => Err(ErrorStack::get()), + _ => Ok(TimeDiff { + days: days, + secs: seconds, + }), + } + } + + /// Compare two times + /// + /// This corresponds to [`ASN1_TIME_compare`] but is implemented using [`diff`] so that it is + /// also supported on older versions of OpenSSL. + /// + /// [`ASN1_TIME_compare`]: https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_compare.html + /// [`diff`]: struct.Asn1TimeRef.html#method.diff + #[cfg(ossl102)] + pub fn compare(&self, other: &Self) -> Result { + let d = self.diff(other)?; + if d.days > 0 || d.secs > 0 { + return Ok(Ordering::Less); + } + if d.days < 0 || d.secs < 0 { + return Ok(Ordering::Greater); + } + + Ok(Ordering::Equal) + } +} + +#[cfg(ossl102)] +impl PartialEq for Asn1TimeRef { + fn eq(&self, other: &Asn1TimeRef) -> bool { + self.diff(other).map(|t| t.days == 0 && t.secs == 0).unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialEq for Asn1TimeRef { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other).map(|t| t.days == 0 && t.secs == 0).unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl<'a> PartialEq for &'a Asn1TimeRef { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other).map(|t| t.days == 0 && t.secs == 0).unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialOrd for Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1TimeRef) -> Option { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl PartialOrd for Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1Time) -> Option { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl<'a> PartialOrd for &'a Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1Time) -> Option { + self.compare(other).ok() + } +} + impl fmt::Display for Asn1TimeRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { unsafe { @@ -129,6 +237,16 @@ impl Asn1Time { Asn1Time::from_period(days as c_long * 60 * 60 * 24) } + /// Creates a new time from the specified `time_t` value + pub fn from_unix(time: time_t) -> Result { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?; + Ok(Asn1Time::from_ptr(handle)) + } + } + /// Creates a new time corresponding to the specified ASN1 time string. /// /// This corresponds to [`ASN1_TIME_set_string`]. @@ -165,6 +283,48 @@ impl Asn1Time { } } +#[cfg(ossl102)] +impl PartialEq for Asn1Time { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other).map(|t| t.days == 0 && t.secs == 0).unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialEq for Asn1Time { + fn eq(&self, other: &Asn1TimeRef) -> bool { + self.diff(other).map(|t| t.days == 0 && t.secs == 0).unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time { + fn eq(&self, other: & &'a Asn1TimeRef) -> bool { + self.diff(other).map(|t| t.days == 0 && t.secs == 0).unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialOrd for Asn1Time { + fn partial_cmp(&self, other: &Asn1Time) -> Option { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl PartialOrd for Asn1Time { + fn partial_cmp(&self, other: &Asn1TimeRef) -> Option { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time { + fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option { + self.compare(other).ok() + } +} + foreign_type_and_impl_send_sync! { type CType = ffi::ASN1_STRING; fn drop = ffi::ASN1_STRING_free; @@ -391,4 +551,59 @@ mod tests { #[cfg(ossl111)] Asn1Time::from_str_x509("99991231235959Z").unwrap(); } + + #[test] + fn time_from_unix() { + let t = Asn1Time::from_unix(0).unwrap(); + assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string()); + } + + #[test] + #[cfg(ossl102)] + fn time_eq() { + let a = Asn1Time::from_str("99991231235959Z").unwrap(); + let b = Asn1Time::from_str("99991231235959Z").unwrap(); + let c = Asn1Time::from_str("99991231235958Z").unwrap(); + let a_ref = a.as_ref(); + let b_ref = b.as_ref(); + let c_ref = c.as_ref(); + assert!(a == b); + assert!(a != c); + assert!(a == b_ref); + assert!(a != c_ref); + assert!(b_ref == a); + assert!(c_ref != a); + assert!(a_ref == b_ref); + assert!(a_ref != c_ref); + } + + #[test] + #[cfg(ossl102)] + fn time_ord() { + let a = Asn1Time::from_str("99991231235959Z").unwrap(); + let b = Asn1Time::from_str("99991231235959Z").unwrap(); + let c = Asn1Time::from_str("99991231235958Z").unwrap(); + let a_ref = a.as_ref(); + let b_ref = b.as_ref(); + let c_ref = c.as_ref(); + assert!(a >= b); + assert!(a > c); + assert!(b <= a); + assert!(c < a); + + assert!(a_ref >= b); + assert!(a_ref > c); + assert!(b_ref <= a); + assert!(c_ref < a); + + assert!(a >= b_ref); + assert!(a > c_ref); + assert!(b <= a_ref); + assert!(c < a_ref); + + assert!(a_ref >= b_ref); + assert!(a_ref > c_ref); + assert!(b_ref <= a_ref); + assert!(c_ref < a_ref); + } }