diff --git a/src/int/div.rs b/src/int/div.rs index 3918fbc2d..5be9dc00e 100644 --- a/src/int/div.rs +++ b/src/int/div.rs @@ -76,6 +76,125 @@ impl Int { } } +/// Vartime checked division operations. +impl Int { + #[inline] + /// Variable time equivalent of [Self::div_rem_base] + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + const fn div_rem_base_vartime( + &self, + rhs: &NonZero>, + ) -> (Uint, Uint, ConstChoice, ConstChoice) { + // Step 1: split operands into signs and magnitudes. + let (lhs_mag, lhs_sgn) = self.abs_sign(); + let (rhs_mag, rhs_sgn) = rhs.abs_sign(); + + // Step 2. Divide magnitudes + // safe to unwrap since rhs is NonZero. + let (quotient, remainder) = lhs_mag.div_rem_vartime(&rhs_mag); + + (quotient, remainder, lhs_sgn, rhs_sgn) + } + + /// Variable time equivalent of [Self::checked_div_rem] + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub const fn checked_div_rem_vartime( + &self, + rhs: &NonZero>, + ) -> (ConstCtOption, Int) { + let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base_vartime(rhs); + let opposing_signs = lhs_sgn.ne(rhs_sgn); + ( + Self::new_from_abs_sign(quotient, opposing_signs), + remainder.as_int().wrapping_neg_if(lhs_sgn), // as_int mapping is safe; remainder < 2^{k-1} by construction. + ) + } + + /// Variable time equivalent of [Self::checked_div] + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub fn checked_div_vartime( + &self, + rhs: &Int, + ) -> CtOption { + NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_vartime(&rhs).0.into()) + } + + /// Variable time equivalent of [Self::rem] + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub const fn rem_vartime( + &self, + rhs: &NonZero>, + ) -> Int { + self.checked_div_rem_vartime(rhs).1 + } +} + +/// Vartime checked div-floor operations. +impl Int { + /// Variable time equivalent of [Self::checked_div_rem_floor] + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub const fn checked_div_rem_floor_vartime( + &self, + rhs: &NonZero>, + ) -> (ConstCtOption, Int) { + let (lhs_mag, lhs_sgn) = self.abs_sign(); + let (rhs_mag, rhs_sgn) = rhs.abs_sign(); + let (quotient, remainder) = lhs_mag.div_rem_vartime(&rhs_mag); + + // Modify quotient and remainder when lhs and rhs have opposing signs and the remainder is + // non-zero. + let opposing_signs = lhs_sgn.xor(rhs_sgn); + let modify = remainder.is_nonzero().and(opposing_signs); + + // Increase the quotient by one. + let quotient_plus_one = quotient.wrapping_add(&Uint::ONE); // cannot wrap. + let quotient = Uint::select("ient, "ient_plus_one, modify); + + // Invert the remainder. + let inv_remainder = rhs_mag.0.wrapping_sub(&remainder); + let remainder = Uint::select(&remainder, &inv_remainder, modify); + + // Negate output when lhs and rhs have opposing signs. + let quotient = Int::new_from_abs_sign(quotient, opposing_signs); + let remainder = remainder.as_int().wrapping_neg_if(opposing_signs); // rem always small enough for safe as_int conversion + + (quotient, remainder) + } + + /// Variable time equivalent of [Self::checked_div_floor] + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub fn checked_div_floor_vartime( + &self, + rhs: &Int, + ) -> CtOption { + NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_floor_vartime(&rhs).0.into()) + } +} + /// Checked div-floor operations. impl Int { /// Perform checked floored division, returning a [`ConstCtOption`] which `is_some` only if