From d2e56923541796826926c658bf04c011a35d56ea Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 21 Oct 2024 11:44:24 +0200 Subject: [PATCH 1/3] Introduce `lte` and `gte` for `Uint` --- src/uint/cmp.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/uint/cmp.rs b/src/uint/cmp.rs index eeb9a554..22572229 100644 --- a/src/uint/cmp.rs +++ b/src/uint/cmp.rs @@ -2,11 +2,14 @@ //! //! By default these are all constant-time and use the `subtle` crate. -use super::Uint; -use crate::{ConstChoice, Limb}; use core::cmp::Ordering; + use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess}; +use crate::{ConstChoice, Limb}; + +use super::Uint; + impl Uint { /// Return `b` if `c` is truthy, otherwise return `a`. #[inline] @@ -64,6 +67,12 @@ impl Uint { ConstChoice::from_word_mask(borrow.0) } + /// Returns the truthy value if `self <= rhs` and the falsy value otherwise. + #[inline] + pub(crate) const fn lte(lhs: &Self, rhs: &Self) -> ConstChoice { + Self::gt(lhs, rhs).not() + } + /// Returns the truthy value if `self > rhs` and the falsy value otherwise. #[inline] pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> ConstChoice { @@ -71,6 +80,12 @@ impl Uint { ConstChoice::from_word_mask(borrow.0) } + /// Returns the truthy value if `self >= rhs` and the falsy value otherwise. + #[inline] + pub(crate) const fn gte(lhs: &Self, rhs: &Self) -> ConstChoice { + Self::lt(lhs, rhs).not() + } + /// Returns the ordering between `self` and `rhs` as an i8. /// Values correspond to the Ordering enum: /// -1 is Less @@ -160,10 +175,12 @@ impl PartialEq for Uint { #[cfg(test)] mod tests { - use crate::{Integer, Zero, U128}; use core::cmp::Ordering; + use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess}; + use crate::{Integer, Uint, Zero, U128}; + #[test] fn is_zero() { assert!(bool::from(U128::ZERO.is_zero())); @@ -214,6 +231,25 @@ mod tests { assert!(!bool::from(b.ct_gt(&c))); } + #[test] + fn gte() { + let a = U128::ZERO; + let b = U128::ONE; + let c = U128::MAX; + + assert!(bool::from(Uint::gte(&b, &a))); + assert!(bool::from(Uint::gte(&c, &a))); + assert!(bool::from(Uint::gte(&c, &b))); + + assert!(bool::from(Uint::gte(&a, &a))); + assert!(bool::from(Uint::gte(&b, &b))); + assert!(bool::from(Uint::gte(&c, &c))); + + assert!(!bool::from(Uint::gte(&a, &b))); + assert!(!bool::from(Uint::gte(&a, &c))); + assert!(!bool::from(Uint::gte(&b, &c))); + } + #[test] fn ct_lt() { let a = U128::ZERO; @@ -233,6 +269,25 @@ mod tests { assert!(!bool::from(c.ct_lt(&b))); } + #[test] + fn lte() { + let a = U128::ZERO; + let b = U128::ONE; + let c = U128::MAX; + + assert!(bool::from(Uint::lte(&a, &b))); + assert!(bool::from(Uint::lte(&a, &c))); + assert!(bool::from(Uint::lte(&b, &c))); + + assert!(bool::from(Uint::lte(&a, &a))); + assert!(bool::from(Uint::lte(&b, &b))); + assert!(bool::from(Uint::lte(&c, &c))); + + assert!(!bool::from(Uint::lte(&b, &a))); + assert!(!bool::from(Uint::lte(&c, &a))); + assert!(!bool::from(Uint::lte(&c, &b))); + } + #[test] fn cmp() { let a = U128::ZERO; From 6e0410997f67d0903e5e5b49971dac968e1d6527 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 21 Oct 2024 11:52:01 +0200 Subject: [PATCH 2/3] Introduce `lte` and `gte` for `Int` --- src/int/cmp.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/int/cmp.rs b/src/int/cmp.rs index 7019140c..851a14ea 100644 --- a/src/int/cmp.rs +++ b/src/int/cmp.rs @@ -39,12 +39,24 @@ impl Int { Uint::lt(&lhs.invert_msb().0, &rhs.invert_msb().0) } + /// Returns the truthy value if `self <= rhs` and the falsy value otherwise. + #[inline] + pub(crate) const fn lte(lhs: &Self, rhs: &Self) -> ConstChoice { + Self::gt(lhs, rhs).not() + } + /// Returns the truthy value if `self > rhs` and the falsy value otherwise. #[inline] pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> ConstChoice { Uint::gt(&lhs.invert_msb().0, &rhs.invert_msb().0) } + /// Returns the truthy value if `self >= rhs` and the falsy value otherwise. + #[inline] + pub(crate) const fn gte(lhs: &Self, rhs: &Self) -> ConstChoice { + Self::lt(lhs, rhs).not() + } + /// Returns the ordering between `self` and `rhs` as an i8. /// Values correspond to the Ordering enum: /// -1 is Less @@ -109,7 +121,9 @@ impl PartialEq for Int { #[cfg(test)] mod tests { - use crate::{I128, U128}; + use subtle::{ConstantTimeGreater, ConstantTimeLess}; + + use crate::{Int, I128, U128}; #[test] fn test_is_nonzero() { @@ -172,4 +186,80 @@ mod tests { assert_ne!(true, I128::MINUS_ONE < I128::MINUS_ONE); assert_ne!(true, I128::MIN < I128::MIN); } + + #[test] + fn ct_gt() { + let a = I128::MIN; + let b = I128::ZERO; + let c = I128::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 gte() { + let a = I128::MIN; + let b = I128::ZERO; + let c = I128::MAX; + + assert!(bool::from(Int::gte(&b, &a))); + assert!(bool::from(Int::gte(&c, &a))); + assert!(bool::from(Int::gte(&c, &b))); + + assert!(bool::from(Int::gte(&a, &a))); + assert!(bool::from(Int::gte(&b, &b))); + assert!(bool::from(Int::gte(&c, &c))); + + assert!(!bool::from(Int::gte(&a, &b))); + assert!(!bool::from(Int::gte(&a, &c))); + assert!(!bool::from(Int::gte(&b, &c))); + } + + #[test] + fn ct_lt() { + let a = I128::ZERO; + let b = I128::ONE; + let c = I128::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))); + } + + #[test] + fn lte() { + let a = I128::ZERO; + let b = I128::ONE; + let c = I128::MAX; + + assert!(bool::from(Int::lte(&a, &b))); + assert!(bool::from(Int::lte(&a, &c))); + assert!(bool::from(Int::lte(&b, &c))); + + assert!(bool::from(Int::lte(&a, &a))); + assert!(bool::from(Int::lte(&b, &b))); + assert!(bool::from(Int::lte(&c, &c))); + + assert!(!bool::from(Int::lte(&b, &a))); + assert!(!bool::from(Int::lte(&c, &a))); + assert!(!bool::from(Int::lte(&c, &b))); + } } From 34290de5de124426fab423b1d09f2f87f9cdd14e Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 21 Oct 2024 12:55:57 +0200 Subject: [PATCH 3/3] Update `Int::div_rem` --- src/const_choice.rs | 5 +++ src/int/div.rs | 75 ++++++++++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/const_choice.rs b/src/const_choice.rs index 90ebd169..f3f2ad77 100644 --- a/src/const_choice.rs +++ b/src/const_choice.rs @@ -206,6 +206,11 @@ impl ConstChoice { Self(self.0 ^ other.0) } + #[inline] + pub(crate) const fn ne(&self, other: Self) -> Self { + Self::xor(self, other) + } + /// Return `b` if `self` is truthy, otherwise return `a`. #[inline] pub(crate) const fn select_word(&self, a: Word, b: Word) -> Word { diff --git a/src/int/div.rs b/src/int/div.rs index 06305454..a892131b 100644 --- a/src/int/div.rs +++ b/src/int/div.rs @@ -4,41 +4,64 @@ use core::ops::Div; use subtle::{Choice, CtOption}; -use crate::{CheckedDiv, ConstChoice, Int, NonZero, Uint}; +use crate::{CheckedDiv, ConstChoice, ConstCtOption, Int, NonZero, Uint}; impl Int { #[inline] - /// Base checked_div_mod operation. - /// Given `(a, b)` computes the quotient and remainder of the absolute - /// Note: this operation rounds towards zero, truncating any fractional part of the exact result. - fn checked_div_mod( + /// Base div_rem operation. + /// Given `(a, b)`, computes the quotient and remainder of their absolute values. Furthermore, + /// returns the signs of `a` and `b`. + fn div_rem_base( &self, rhs: &NonZero, - ) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice) { - // Step 1: split operands into signs, magnitudes and whether they are zero. + ) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice, ConstChoice) { + // Step 1: split operands into signs and magnitudes. let (lhs_sgn, lhs_mag) = self.sign_and_magnitude(); - let (rhs_sgn, rhs_mag) = rhs.sign_and_magnitude(); + let (rhs_sgn, rhs_mag) = rhs.0.sign_and_magnitude(); - // Step 2. Determine if the result should be negated. - // This should be done if and only if lhs and rhs have opposing signs. - // Note: if the lhs is zero, the resulting magnitude will also be zero. Negating zero, - // however, still yields zero, so having a truthy `negate_result` in that scenario is OK. - let opposing_signs = lhs_sgn.xor(rhs_sgn); - - // Step 3. Construct result + // Step 2. Divide magnitudes // safe to unwrap since rhs is NonZero. let (quotient, remainder) = lhs_mag.div_rem(&NonZero::new(rhs_mag).unwrap()); - (quotient, remainder, opposing_signs) + (quotient, remainder, lhs_sgn, rhs_sgn) + } + + /// + /// Example: + /// ``` + /// use crypto_bigint::{I128, NonZero}; + /// let (quotient, remainder) = I128::from(8).checked_div_rem(&I128::from(3).to_nz().unwrap()); + /// assert_eq!(quotient.unwrap(), I128::from(2)); + /// assert_eq!(remainder.unwrap(), I128::from(2)); + /// + /// let (quotient, remainder) = I128::from(-8).checked_div_rem(&I128::from(3).to_nz().unwrap()); + /// assert_eq!(quotient.unwrap(), I128::from(-2)); + /// assert_eq!(remainder.unwrap(), I128::from(-2)); + /// + /// let (quotient, remainder) = I128::from(8).checked_div_rem(&I128::from(-3).to_nz().unwrap()); + /// assert_eq!(quotient.unwrap(), I128::from(-2)); + /// assert_eq!(remainder.unwrap(), I128::from(2)); + /// + /// let (quotient, remainder) = I128::from(-8).checked_div_rem(&I128::from(-3).to_nz().unwrap()); + /// assert_eq!(quotient.unwrap(), I128::from(2)); + /// assert_eq!(remainder.unwrap(), I128::from(-2)); + /// ``` + pub fn checked_div_rem( + &self, + rhs: &NonZero, + ) -> (ConstCtOption, ConstCtOption) { + let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(rhs); + let opposing_signs = lhs_sgn.ne(rhs_sgn); + ( + Self::new_from_sign_and_magnitude(opposing_signs, quotient), + Self::new_from_sign_and_magnitude(lhs_sgn, remainder), + ) } /// Perform checked division, returning a [`CtOption`] which `is_some` only if the rhs != 0. /// Note: this operation rounds towards zero, truncating any fractional part of the exact result. pub fn checked_div(&self, rhs: &Self) -> CtOption { - NonZero::new(*rhs).and_then(|rhs| { - let (quotient, _, opposing_signs) = self.checked_div_mod(&rhs); - Self::new_from_sign_and_magnitude(opposing_signs, quotient).into() - }) + NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem(&rhs).0.into()) } /// Perform checked division, returning a [`CtOption`] which `is_some` only if the rhs != 0. @@ -66,10 +89,12 @@ impl Int { /// ``` pub fn checked_div_floor(&self, rhs: &Self) -> CtOption { NonZero::new(*rhs).and_then(|rhs| { - let (quotient, remainder, opposing_signs) = self.checked_div_mod(&rhs); + let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(&rhs); - // Increase the quotient by one when lhs and rhs have opposing signs and there - // is a non-zero remainder. + // Increment the quotient when + // - lhs and rhs have opposing signs, and + // - there is a non-zero remainder. + let opposing_signs = lhs_sgn.ne(rhs_sgn); let increment_quotient = remainder.is_nonzero().and(opposing_signs); let quotient_sub_one = quotient.wrapping_add(&Uint::ONE); let quotient = Uint::select("ient, "ient_sub_one, increment_quotient); @@ -134,7 +159,7 @@ impl Int { impl CheckedDiv for Int { fn checked_div(&self, rhs: &Int) -> CtOption { - self.checked_div(rhs) + self.checked_div(rhs).into() } } @@ -166,7 +191,7 @@ impl Div>> for Int { type Output = CtOption>; fn div(self, rhs: NonZero>) -> Self::Output { - self.checked_div(&rhs) + self.checked_div(&rhs).into() } }