Unverified Commit 14a6a98e authored by Brian Olsen's avatar Brian Olsen
Browse files

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.
parent ff14649d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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;

+199 −0
Original line number Diff line number Diff line
@@ -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<TimeDiff, ErrorStack> {
        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<Ordering, ErrorStack> {
        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<Asn1Time> 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<Asn1Time> 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<Ordering> {
        self.compare(other).ok()
    }
}

#[cfg(ossl102)]
impl PartialOrd<Asn1Time> for Asn1TimeRef {
    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
        self.compare(other).ok()
    }
}

#[cfg(ossl102)]
impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
    fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
        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<Asn1TimeRef> 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<Ordering> {
        self.compare(other).ok()
    }
}

#[cfg(ossl102)]
impl PartialOrd<Asn1TimeRef> for Asn1Time {
    fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
        self.compare(other).ok()
    }
}

#[cfg(ossl102)]
impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
    fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
        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);
    }
}