diff --git a/ctutils/src/lib.rs b/ctutils/src/lib.rs index bec55469..449f8e13 100644 --- a/ctutils/src/lib.rs +++ b/ctutils/src/lib.rs @@ -70,4 +70,4 @@ mod traits; pub use choice::Choice; pub use ct_option::CtOption; -pub use traits::{ct_eq::CtEq, ct_select::CtSelect}; +pub use traits::{ct_eq::CtEq, ct_gt::CtGt, ct_lt::CtLt, ct_select::CtSelect}; diff --git a/ctutils/src/traits.rs b/ctutils/src/traits.rs index a9f87aae..dd68235e 100644 --- a/ctutils/src/traits.rs +++ b/ctutils/src/traits.rs @@ -4,4 +4,6 @@ //! on in the same module. pub(crate) mod ct_eq; +pub(crate) mod ct_gt; +pub(crate) mod ct_lt; pub(crate) mod ct_select; diff --git a/ctutils/src/traits/ct_gt.rs b/ctutils/src/traits/ct_gt.rs new file mode 100644 index 00000000..2036306f --- /dev/null +++ b/ctutils/src/traits/ct_gt.rs @@ -0,0 +1,57 @@ +use crate::Choice; +use core::cmp; + +/// Constant time greater than operation. +pub trait CtGt { + /// Compute whether `self > other` in constant time. + fn ct_gt(&self, other: &Self) -> Choice; +} + +// Impl `CtGt` using overflowing subtraction +macro_rules! impl_unsigned_ct_gt { + ( $($uint:ty),+ ) => { + $( + impl CtGt for $uint { + #[inline] + fn ct_gt(&self, other: &Self) -> Choice { + let (_, overflow) = other.overflowing_sub(*self); + Choice::new(overflow.into()) + } + } + )+ + }; +} + +impl_unsigned_ct_gt!(u8, u16, u32, u64, u128); + +impl CtGt for cmp::Ordering { + #[inline] + fn ct_gt(&self, other: &Self) -> Choice { + // No impl of `CtGt` for `i8`, so use `u8` + let a = (*self as i8) + 1; + let b = (*other as i8) + 1; + (a as u8).ct_gt(&(b as u8)) + } +} + +#[cfg(test)] +mod tests { + use super::CtGt; + use core::cmp::Ordering; + + #[test] + fn ct_gt() { + let a = 42u64; + let b = 43u64; + assert!(!a.ct_gt(&a).to_bool()); + assert!(!a.ct_gt(&b).to_bool()); + assert!(b.ct_gt(&a).to_bool()); + } + + #[test] + fn ordering() { + assert!(!Ordering::Equal.ct_gt(&Ordering::Equal).to_bool()); + assert!(!Ordering::Less.ct_gt(&Ordering::Greater).to_bool()); + assert!(Ordering::Greater.ct_gt(&Ordering::Less).to_bool()); + } +} diff --git a/ctutils/src/traits/ct_lt.rs b/ctutils/src/traits/ct_lt.rs new file mode 100644 index 00000000..db237e34 --- /dev/null +++ b/ctutils/src/traits/ct_lt.rs @@ -0,0 +1,57 @@ +use crate::Choice; +use core::cmp; + +/// Constant time less than operation. +pub trait CtLt { + /// Compute whether `self < other` in constant time. + fn ct_lt(&self, other: &Self) -> Choice; +} + +// Impl `CtLt` using overflowing subtraction +macro_rules! impl_unsigned_ct_lt { + ( $($uint:ty),+ ) => { + $( + impl CtLt for $uint { + #[inline] + fn ct_lt(&self, other: &Self) -> Choice { + let (_, overflow) = self.overflowing_sub(*other); + Choice::new(overflow.into()) + } + } + )+ + }; +} + +impl_unsigned_ct_lt!(u8, u16, u32, u64, u128); + +impl CtLt for cmp::Ordering { + #[inline] + fn ct_lt(&self, other: &Self) -> Choice { + // No impl of `CtLt` for `i8`, so use `u8` + let a = (*self as i8) + 1; + let b = (*other as i8) + 1; + (a as u8).ct_lt(&(b as u8)) + } +} + +#[cfg(test)] +mod tests { + use super::CtLt; + use core::cmp::Ordering; + + #[test] + fn ct_lt() { + let a = 42u64; + let b = 43u64; + assert!(!a.ct_lt(&a).to_bool()); + assert!(a.ct_lt(&b).to_bool()); + assert!(!b.ct_lt(&a).to_bool()); + } + + #[test] + fn ordering() { + assert!(!Ordering::Equal.ct_lt(&Ordering::Equal).to_bool()); + assert!(Ordering::Less.ct_lt(&Ordering::Greater).to_bool()); + assert!(!Ordering::Greater.ct_lt(&Ordering::Less).to_bool()); + } +}