From 14a6a98ebf536ca423f44c814e8c4fec21b78aa6 Mon Sep 17 00:00:00 2001 From: Brian Olsen Date: Thu, 10 Oct 2019 18:03:45 +0200 Subject: [PATCH 1/2] Add diff method and comparisons to Asn1TimeRef This implements a `diff` method on `Asn1TimeRef` using `ASN1_TIME_diff` and uses this new method to implement combinations of `PartialEq` and `PartialOrd` for `Asn1Time` and `Asn1TimeRef`. This is mostly just a rework of the earlier work done by @illegalprime in his PR #673 and credit should go to him. --- openssl-sys/src/asn1.rs | 2 + openssl/src/asn1.rs | 199 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) diff --git a/openssl-sys/src/asn1.rs b/openssl-sys/src/asn1.rs index 042b3eca..59392633 100644 --- a/openssl-sys/src/asn1.rs +++ b/openssl-sys/src/asn1.rs @@ -39,6 +39,8 @@ 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; diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index c06a800f..4e22511d 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -27,6 +27,8 @@ use ffi; use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_char, c_int, c_long}; +#[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 { @@ -165,6 +273,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 +541,53 @@ mod tests { #[cfg(ossl111)] Asn1Time::from_str_x509("99991231235959Z").unwrap(); } + + #[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); + } } From 80e0dd03ba987293a874450dfbe1d2c53e974110 Mon Sep 17 00:00:00 2001 From: Brian Olsen Date: Thu, 10 Oct 2019 18:33:47 +0200 Subject: [PATCH 2/2] Add method to create Asn1Time from time_t value This is mostly just a rework of the earlier work done by @illegalprime in his PR #673 and credit should go to him. --- openssl-sys/src/asn1.rs | 1 + openssl/src/asn1.rs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/openssl-sys/src/asn1.rs b/openssl-sys/src/asn1.rs index 59392633..2f2bf1b1 100644 --- a/openssl-sys/src/asn1.rs +++ b/openssl-sys/src/asn1.rs @@ -43,6 +43,7 @@ extern "C" { 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 4e22511d..6cd47f06 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -26,7 +26,7 @@ //! ``` 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; @@ -237,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`]. @@ -542,6 +552,12 @@ mod tests { 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() {