//! [`Uint`] comparisons. //! //! By default these are all constant-time and use the `subtle` crate. use super::Uint; use crate::{CtChoice, Limb}; use core::cmp::Ordering; use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess}; impl Uint { /// Return `b` if `c` is truthy, otherwise return `a`. #[inline] pub(crate) const fn ct_select(a: &Self, b: &Self, c: CtChoice) -> Self { let mut limbs = [Limb::ZERO; LIMBS]; let mut i = 0; while i < LIMBS { limbs[i] = Limb::ct_select(a.limbs[i], b.limbs[i], c); i += 1; } Uint { limbs } } #[inline] pub(crate) const fn ct_swap(a: &Self, b: &Self, c: CtChoice) -> (Self, Self) { let new_a = Self::ct_select(a, b, c); let new_b = Self::ct_select(b, a, c); (new_a, new_b) } /// Returns the truthy value if `self`!=0 or the falsy value otherwise. #[inline] pub(crate) const fn ct_is_nonzero(&self) -> CtChoice { let mut b = 0; let mut i = 0; while i < LIMBS { b |= self.limbs[i].0; i += 1; } Limb(b).ct_is_nonzero() } /// Returns the truthy value if `self` is odd or the falsy value otherwise. pub(crate) const fn ct_is_odd(&self) -> CtChoice { CtChoice::from_lsb(self.limbs[0].0 & 1) } /// Returns the truthy value if `self == rhs` or the falsy value otherwise. #[inline] pub(crate) const fn ct_eq(lhs: &Self, rhs: &Self) -> CtChoice { let mut acc = 0; let mut i = 0; while i < LIMBS { acc |= lhs.limbs[i].0 ^ rhs.limbs[i].0; i += 1; } // acc == 0 if and only if self == rhs Limb(acc).ct_is_nonzero().not() } /// Returns the truthy value if `self <= rhs` and the falsy value otherwise. #[inline] pub(crate) const fn ct_lt(lhs: &Self, rhs: &Self) -> CtChoice { // We could use the same approach as in Limb::ct_lt(), // but since we have to use Uint::wrapping_sub(), which calls `sbb()`, // there are no savings compared to just calling `sbb()` directly. let (_res, borrow) = lhs.sbb(rhs, Limb::ZERO); CtChoice::from_mask(borrow.0) } /// Returns the truthy value if `self <= rhs` and the falsy value otherwise. #[inline] pub(crate) const fn ct_gt(lhs: &Self, rhs: &Self) -> CtChoice { let (_res, borrow) = rhs.sbb(lhs, Limb::ZERO); CtChoice::from_mask(borrow.0) } } impl ConstantTimeEq for Uint { #[inline] fn ct_eq(&self, other: &Self) -> Choice { Uint::ct_eq(self, other).into() } } impl ConstantTimeGreater for Uint { #[inline] fn ct_gt(&self, other: &Self) -> Choice { Uint::ct_gt(self, other).into() } } impl ConstantTimeLess for Uint { #[inline] fn ct_lt(&self, other: &Self) -> Choice { Uint::ct_lt(self, other).into() } } impl Eq for Uint {} impl Ord for Uint { fn cmp(&self, other: &Self) -> Ordering { let is_lt = self.ct_lt(other); let is_eq = self.ct_eq(other); if is_lt.into() { Ordering::Less } else if is_eq.into() { Ordering::Equal } else { Ordering::Greater } } } impl PartialOrd for Uint { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl PartialEq for Uint { fn eq(&self, other: &Self) -> bool { self.ct_eq(other).into() } } #[cfg(test)] mod tests { use crate::{Integer, Zero, U128}; use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess}; #[test] fn is_zero() { assert!(bool::from(U128::ZERO.is_zero())); assert!(!bool::from(U128::ONE.is_zero())); assert!(!bool::from(U128::MAX.is_zero())); } #[test] fn is_odd() { assert!(!bool::from(U128::ZERO.is_odd())); assert!(bool::from(U128::ONE.is_odd())); assert!(bool::from(U128::MAX.is_odd())); } #[test] fn ct_eq() { let a = U128::ZERO; let b = U128::MAX; assert!(bool::from(a.ct_eq(&a))); assert!(!bool::from(a.ct_eq(&b))); assert!(!bool::from(b.ct_eq(&a))); assert!(bool::from(b.ct_eq(&b))); } #[test] fn ct_gt() { let a = U128::ZERO; let b = U128::ONE; let c = U128::MAX; assert!(bool::from(b.ct_gt(&a))); assert!(bool::from(c.ct_gt(&a))); assert!(bool::from(c.ct_gt(&b))); assert!(!bool::from(a.ct_gt(&a))); assert!(!bool::from(b.ct_gt(&b))); assert!(!bool::from(c.ct_gt(&c))); assert!(!bool::from(a.ct_gt(&b))); assert!(!bool::from(a.ct_gt(&c))); assert!(!bool::from(b.ct_gt(&c))); } #[test] fn ct_lt() { let a = U128::ZERO; let b = U128::ONE; let c = U128::MAX; assert!(bool::from(a.ct_lt(&b))); assert!(bool::from(a.ct_lt(&c))); assert!(bool::from(b.ct_lt(&c))); assert!(!bool::from(a.ct_lt(&a))); assert!(!bool::from(b.ct_lt(&b))); assert!(!bool::from(c.ct_lt(&c))); assert!(!bool::from(b.ct_lt(&a))); assert!(!bool::from(c.ct_lt(&a))); assert!(!bool::from(c.ct_lt(&b))); } }