diff --git a/src/const_choice.rs b/src/const_choice.rs index 5152ff09d..72687151b 100644 --- a/src/const_choice.rs +++ b/src/const_choice.rs @@ -163,6 +163,13 @@ impl From for Choice { } } +impl From for ConstChoice { + #[inline] + fn from(choice: Choice) -> Self { + ConstChoice::from_word_lsb(choice.unwrap_u8() as Word) + } +} + impl From for bool { fn from(choice: ConstChoice) -> Self { choice.is_true_vartime() diff --git a/src/traits.rs b/src/traits.rs index 122309053..a2439bd32 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -95,6 +95,7 @@ pub trait Integer: + for<'a> BitXor<&'a Self, Output = Self> + BitXorAssign + for<'a> BitXorAssign<&'a Self> + + BitOps + CheckedAdd + CheckedSub + CheckedMul @@ -154,24 +155,6 @@ pub trait Integer: /// The value `1`. fn one() -> Self; - /// Calculate the number of bits required to represent a given number. - fn bits(&self) -> u32; - - /// Calculate the number of bits required to represent a given number in variable-time with - /// respect to `self`. - fn bits_vartime(&self) -> u32; - - /// Precision of this integer in bits. - fn bits_precision(&self) -> u32; - - /// Precision of this integer in bytes. - fn bytes_precision(&self) -> usize; - - /// Calculate the number of leading zeros in the binary representation of this number. - fn leading_zeros(&self) -> u32 { - self.bits_precision() - self.bits() - } - /// Number of limbs in this integer. fn nlimbs(&self) -> usize; @@ -496,6 +479,69 @@ pub trait RemLimb: Sized { fn rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> Limb; } +/// Bit counting and bit operations. +pub trait BitOps { + /// Precision of this integer in bits. + fn bits_precision(&self) -> u32; + + /// `floor(log2(self.bits_precision()))`. + fn log2_bits(&self) -> u32 { + u32::BITS - self.bits_precision().leading_zeros() - 1 + } + + /// Precision of this integer in bytes. + fn bytes_precision(&self) -> usize; + + /// Calculate the number of bits needed to represent this number. + fn bit(&self, index: u32) -> Choice; + + /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`. + fn set_bit(&mut self, index: u32, bit_value: Choice); + + /// Calculate the number of bits required to represent a given number. + fn bits(&self) -> u32 { + self.bits_precision() - self.leading_zeros() + } + + /// Calculate the number of trailing zeros in the binary representation of this number. + fn trailing_zeros(&self) -> u32; + + /// Calculate the number of trailing ones in the binary representation of this number. + fn trailing_ones(&self) -> u32; + + /// Calculate the number of leading zeros in the binary representation of this number. + fn leading_zeros(&self) -> u32; + + /// Returns `true` if the bit at position `index` is set, `false` otherwise. + /// + /// # Remarks + /// This operation is variable time with respect to `index` only. + fn bit_vartime(&self, index: u32) -> bool; + + /// Calculate the number of bits required to represent a given number in variable-time with + /// respect to `self`. + fn bits_vartime(&self) -> u32 { + self.bits_precision() - self.leading_zeros_vartime() + } + + /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`, + /// variable time in `self`. + fn set_bit_vartime(&mut self, index: u32, bit_value: bool); + + /// Calculate the number of leading zeros in the binary representation of this number. + fn leading_zeros_vartime(&self) -> u32 { + self.bits_precision() - self.bits_vartime() + } + + /// Calculate the number of trailing zeros in the binary representation of this number in + /// variable-time with respect to `self`. + fn trailing_zeros_vartime(&self) -> u32; + + /// Calculate the number of trailing ones in the binary representation of this number, + /// variable time in `self`. + fn trailing_ones_vartime(&self) -> u32; +} + /// Constant-time exponentiation. pub trait Pow { /// Raises to the `exponent` power. diff --git a/src/uint.rs b/src/uint.rs index ef30f85c1..7ab7ead59 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -249,22 +249,6 @@ impl Integer for Uint { Self::ONE } - fn bits(&self) -> u32 { - self.bits() - } - - fn bits_vartime(&self) -> u32 { - self.bits_vartime() - } - - fn bits_precision(&self) -> u32 { - Self::BITS - } - - fn bytes_precision(&self) -> usize { - Self::BYTES - } - fn nlimbs(&self) -> usize { Self::LIMBS } diff --git a/src/uint/bits.rs b/src/uint/bits.rs index 30682cb31..526191680 100644 --- a/src/uint/bits.rs +++ b/src/uint/bits.rs @@ -1,25 +1,137 @@ -use crate::{ConstChoice, Limb, Uint}; +use subtle::Choice; + +use crate::{BitOps, ConstChoice, Limb, Uint, Word}; + +#[inline(always)] +pub(crate) const fn bit(limbs: &[Limb], index: u32) -> ConstChoice { + let limb_num = index / Limb::BITS; + let index_in_limb = index % Limb::BITS; + let index_mask = 1 << index_in_limb; + + let mut result = 0; + let mut i = 0; + while i < limbs.len() { + let bit = limbs[i].0 & index_mask; + let is_right_limb = ConstChoice::from_u32_eq(i as u32, limb_num); + result |= is_right_limb.if_true_word(bit); + i += 1; + } -impl Uint { - /// Get the value of the bit at position `index`, as a truthy or falsy `ConstChoice`. - /// Returns the falsy value for indices out of range. - pub const fn bit(&self, index: u32) -> ConstChoice { - let limb_num = index / Limb::BITS; - let index_in_limb = index % Limb::BITS; - let index_mask = 1 << index_in_limb; + ConstChoice::from_word_lsb(result >> index_in_limb) +} - let limbs = self.as_words(); +/// Calculate the number of leading zeros in the binary representation of this number. +pub(crate) const fn leading_zeros(limbs: &[Limb]) -> u32 { + let mut count = 0; + let mut i = limbs.len(); + let mut nonzero_limb_not_encountered = ConstChoice::TRUE; + while i > 0 { + i -= 1; + let l = limbs[i]; + let z = l.leading_zeros(); + count += nonzero_limb_not_encountered.if_true_u32(z); + nonzero_limb_not_encountered = + nonzero_limb_not_encountered.and(ConstChoice::from_word_nonzero(l.0).not()); + } - let mut result = 0; - let mut i = 0; - while i < LIMBS { - let bit = limbs[i] & index_mask; - let is_right_limb = ConstChoice::from_u32_eq(i as u32, limb_num); - result |= is_right_limb.if_true_word(bit); - i += 1; + count +} + +#[inline(always)] +pub(crate) const fn bit_vartime(limbs: &[Limb], index: u32) -> bool { + let limb_num = (index / Limb::BITS) as usize; + let index_in_limb = (index % Limb::BITS) as usize; + if limb_num >= limbs.len() { + false + } else { + (limbs[limb_num].0 >> index_in_limb) & 1 == 1 + } +} + +#[inline(always)] +pub(crate) const fn bits_vartime(limbs: &[Limb]) -> u32 { + let mut i = limbs.len() - 1; + while i > 0 && limbs[i].0 == 0 { + i -= 1; + } + + let limb = limbs[i]; + Limb::BITS * (i as u32 + 1) - limb.leading_zeros() +} + +#[inline(always)] +pub(crate) const fn trailing_zeros(limbs: &[Limb]) -> u32 { + let mut count = 0; + let mut i = 0; + let mut nonzero_limb_not_encountered = ConstChoice::TRUE; + while i < limbs.len() { + let l = limbs[i]; + let z = l.trailing_zeros(); + count += nonzero_limb_not_encountered.if_true_u32(z); + nonzero_limb_not_encountered = + nonzero_limb_not_encountered.and(ConstChoice::from_word_nonzero(l.0).not()); + i += 1; + } + + count +} + +#[inline(always)] +pub(crate) const fn trailing_zeros_vartime(limbs: &[Limb]) -> u32 { + let mut count = 0; + let mut i = 0; + while i < limbs.len() { + let l = limbs[i]; + let z = l.trailing_zeros(); + count += z; + if z != Limb::BITS { + break; + } + i += 1; + } + + count +} + +#[inline(always)] +pub(crate) const fn trailing_ones(limbs: &[Limb]) -> u32 { + let mut count = 0; + let mut i = 0; + let mut nonmax_limb_not_encountered = ConstChoice::TRUE; + while i < limbs.len() { + let l = limbs[i]; + let z = l.trailing_ones(); + count += nonmax_limb_not_encountered.if_true_u32(z); + nonmax_limb_not_encountered = + nonmax_limb_not_encountered.and(ConstChoice::from_word_eq(l.0, Limb::MAX.0)); + i += 1; + } + + count +} + +#[inline(always)] +pub(crate) const fn trailing_ones_vartime(limbs: &[Limb]) -> u32 { + let mut count = 0; + let mut i = 0; + while i < limbs.len() { + let l = limbs[i]; + let z = l.trailing_ones(); + count += z; + if z != Limb::BITS { + break; } + i += 1; + } + + count +} - ConstChoice::from_word_lsb(result >> index_in_limb) +impl Uint { + /// Get the value of the bit at position `index`, as a truthy or falsy `ConstChoice`. + /// Returns the falsy value for indices out of range. + pub const fn bit(&self, index: u32) -> ConstChoice { + bit(&self.limbs, index) } /// Returns `true` if the bit at position `index` is set, `false` otherwise. @@ -28,11 +140,7 @@ impl Uint { /// This operation is variable time with respect to `index` only. #[inline(always)] pub const fn bit_vartime(&self, index: u32) -> bool { - if index >= Self::BITS { - false - } else { - (self.limbs[(index / Limb::BITS) as usize].0 >> (index % Limb::BITS)) & 1 == 1 - } + bit_vartime(&self.limbs, index) } /// Calculate the number of bits needed to represent this number. @@ -44,130 +152,40 @@ impl Uint { /// Calculate the number of bits needed to represent this number in variable-time with respect /// to `self`. pub const fn bits_vartime(&self) -> u32 { - let mut i = LIMBS - 1; - while i > 0 && self.limbs[i].0 == 0 { - i -= 1; - } - - let limb = self.limbs[i]; - Limb::BITS * (i as u32 + 1) - limb.leading_zeros() + bits_vartime(&self.limbs) } /// Calculate the number of leading zeros in the binary representation of this number. pub const fn leading_zeros(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = LIMBS; - let mut nonzero_limb_not_encountered = ConstChoice::TRUE; - while i > 0 { - i -= 1; - let l = limbs[i]; - let z = l.leading_zeros(); - count += nonzero_limb_not_encountered.if_true_u32(z); - nonzero_limb_not_encountered = - nonzero_limb_not_encountered.and(ConstChoice::from_word_nonzero(l.0).not()); - } - - count + leading_zeros(&self.limbs) } /// Calculate the number of leading zeros in the binary representation of this number in /// variable-time with respect to `self`. pub const fn leading_zeros_vartime(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = LIMBS; - while i > 0 { - i -= 1; - let l = limbs[i]; - let z = l.leading_zeros(); - count += z; - if z != Limb::BITS { - break; - } - } - - count + Self::BITS - self.bits_vartime() } /// Calculate the number of trailing zeros in the binary representation of this number. pub const fn trailing_zeros(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = 0; - let mut nonzero_limb_not_encountered = ConstChoice::TRUE; - while i < LIMBS { - let l = limbs[i]; - let z = l.trailing_zeros(); - count += nonzero_limb_not_encountered.if_true_u32(z); - nonzero_limb_not_encountered = - nonzero_limb_not_encountered.and(ConstChoice::from_word_nonzero(l.0).not()); - i += 1; - } - - count + trailing_zeros(&self.limbs) } /// Calculate the number of trailing zeros in the binary representation of this number in /// variable-time with respect to `self`. pub const fn trailing_zeros_vartime(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = 0; - while i < LIMBS { - let l = limbs[i]; - let z = l.trailing_zeros(); - count += z; - if z != Limb::BITS { - break; - } - i += 1; - } - - count + trailing_zeros_vartime(&self.limbs) } /// Calculate the number of trailing ones in the binary representation of this number. pub const fn trailing_ones(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = 0; - let mut nonmax_limb_not_encountered = ConstChoice::TRUE; - while i < LIMBS { - let l = limbs[i]; - let z = l.trailing_ones(); - count += nonmax_limb_not_encountered.if_true_u32(z); - nonmax_limb_not_encountered = - nonmax_limb_not_encountered.and(ConstChoice::from_word_eq(l.0, Limb::MAX.0)); - i += 1; - } - - count + trailing_ones(&self.limbs) } /// Calculate the number of trailing ones in the binary representation of this number, /// variable time in `self`. pub const fn trailing_ones_vartime(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = 0; - while i < LIMBS { - let l = limbs[i]; - let z = l.trailing_ones(); - count += z; - if z != Limb::BITS { - break; - } - i += 1; - } - - count + trailing_ones_vartime(&self.limbs) } /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`. @@ -187,6 +205,73 @@ impl Uint { } result } + + /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`, + /// variable time in `self`. + pub(crate) const fn set_bit_vartime(self, index: u32, bit_value: bool) -> Self { + let mut result = self; + let limb_num = (index / Limb::BITS) as usize; + let index_in_limb = index % Limb::BITS; + if bit_value { + result.limbs[limb_num].0 |= 1 << index_in_limb; + } else { + #[allow(trivial_numeric_casts)] + { + result.limbs[limb_num].0 &= !((1 as Word) << index_in_limb); + } + } + result + } +} + +impl BitOps for Uint { + fn bits_precision(&self) -> u32 { + Self::BITS + } + + fn bytes_precision(&self) -> usize { + Self::BYTES + } + + fn leading_zeros(&self) -> u32 { + self.leading_zeros() + } + + fn bit(&self, index: u32) -> Choice { + self.bit(index).into() + } + + fn set_bit(&mut self, index: u32, bit_value: Choice) { + *self = Self::set_bit(*self, index, bit_value.into()); + } + + fn trailing_zeros(&self) -> u32 { + self.trailing_zeros() + } + + fn trailing_ones(&self) -> u32 { + self.trailing_ones() + } + + fn bit_vartime(&self, index: u32) -> bool { + self.bit_vartime(index) + } + + fn bits_vartime(&self) -> u32 { + self.bits_vartime() + } + + fn set_bit_vartime(&mut self, index: u32, bit_value: bool) { + *self = Self::set_bit_vartime(*self, index, bit_value); + } + + fn trailing_zeros_vartime(&self) -> u32 { + self.trailing_zeros_vartime() + } + + fn trailing_ones_vartime(&self) -> u32 { + self.trailing_ones_vartime() + } } #[cfg(test)] diff --git a/src/uint/boxed.rs b/src/uint/boxed.rs index da05ff005..d8b506470 100644 --- a/src/uint/boxed.rs +++ b/src/uint/boxed.rs @@ -307,22 +307,6 @@ impl Integer for BoxedUint { Self::one() } - fn bits(&self) -> u32 { - self.bits() - } - - fn bits_vartime(&self) -> u32 { - self.bits_vartime() - } - - fn bits_precision(&self) -> u32 { - self.bits_precision() - } - - fn bytes_precision(&self) -> usize { - self.nlimbs() * Limb::BYTES - } - fn nlimbs(&self) -> usize { self.nlimbs() } diff --git a/src/uint/boxed/bits.rs b/src/uint/boxed/bits.rs index f59d52a8d..e7a7506b5 100644 --- a/src/uint/boxed/bits.rs +++ b/src/uint/boxed/bits.rs @@ -1,56 +1,47 @@ //! Bit manipulation functions. -use crate::{BoxedUint, ConstChoice, Limb, Zero}; +use crate::{ + uint::bits::{ + bit, bit_vartime, bits_vartime, leading_zeros, trailing_ones, trailing_ones_vartime, + trailing_zeros, trailing_zeros_vartime, + }, + BitOps, BoxedUint, Limb, Word, +}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; impl BoxedUint { + /// Get the value of the bit at position `index`, as a truthy or falsy `Choice`. + /// Returns the falsy value for indices out of range. + pub fn bit(&self, index: u32) -> Choice { + bit(&self.limbs, index).into() + } + + /// Returns `true` if the bit at position `index` is set, `false` otherwise. + /// + /// # Remarks + /// This operation is variable time with respect to `index` only. + #[inline(always)] + pub const fn bit_vartime(&self, index: u32) -> bool { + bit_vartime(&self.limbs, index) + } + /// Calculate the number of bits needed to represent this number, i.e. the index of the highest /// set bit. /// /// Use [`BoxedUint::bits_precision`] to get the total capacity of this integer. pub fn bits(&self) -> u32 { - // Use `u32` because `subtle` can't select on `usize` and it matches what `core` uses for - // the return value of `leading_zeros` - let mut leading_zeros = 0u32; - let mut n = 0u32; - - for limb in self.limbs.iter().rev() { - n.conditional_assign(&(n + 1), !limb.is_zero() | !n.ct_eq(&0)); - - // Set `leading_zeros` for the first nonzero limb we encounter - leading_zeros.conditional_assign(&limb.leading_zeros(), n.ct_eq(&1)); - } - - Limb::BITS * n - leading_zeros - } - - /// `floor(log2(self.bits_precision()))`. - pub(crate) fn log2_bits(&self) -> u32 { - u32::BITS - self.bits_precision().leading_zeros() - 1 + self.bits_precision() - self.leading_zeros() } /// Calculate the number of bits needed to represent this number in variable-time with respect /// to `self`. pub fn bits_vartime(&self) -> u32 { - let mut i = self.nlimbs() - 1; - while i > 0 && self.limbs[i].0 == 0 { - i -= 1; - } - - let limb = self.limbs[i]; - Limb::BITS * (i as u32 + 1) - limb.leading_zeros() + bits_vartime(&self.limbs) } - /// Returns `true` if the bit at position `index` is set, `false` otherwise. - /// - /// # Remarks - /// This operation is variable time with respect to `index` only. - pub fn bit_vartime(&self, index: u32) -> bool { - if index >= self.bits_precision() { - false - } else { - (self.limbs[(index / Limb::BITS) as usize].0 >> (index % Limb::BITS)) & 1 == 1 - } + /// Calculate the number of leading zeros in the binary representation of this number. + pub const fn leading_zeros(&self) -> u32 { + leading_zeros(&self.limbs) } /// Get the precision of this [`BoxedUint`] in bits. @@ -60,55 +51,24 @@ impl BoxedUint { /// Calculate the number of trailing zeros in the binary representation of this number. pub fn trailing_zeros(&self) -> u32 { - let mut count = 0; - let mut nonzero_limb_not_encountered = Choice::from(1u8); - - for l in &*self.limbs { - let z = l.trailing_zeros(); - count += u32::conditional_select(&0, &z, nonzero_limb_not_encountered); - nonzero_limb_not_encountered &= l.is_zero(); - } - - count + trailing_zeros(&self.limbs) } /// Calculate the number of trailing ones in the binary representation of this number. pub fn trailing_ones(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = 0; - let mut nonmax_limb_not_encountered = ConstChoice::TRUE; - while i < limbs.len() { - let l = limbs[i]; - let z = l.trailing_ones(); - count += nonmax_limb_not_encountered.if_true_u32(z); - nonmax_limb_not_encountered = - nonmax_limb_not_encountered.and(ConstChoice::from_word_eq(l.0, Limb::MAX.0)); - i += 1; - } + trailing_ones(&self.limbs) + } - count + /// Calculate the number of trailing zeros in the binary representation of this number in + /// variable-time with respect to `self`. + pub fn trailing_zeros_vartime(&self) -> u32 { + trailing_zeros_vartime(&self.limbs) } /// Calculate the number of trailing ones in the binary representation of this number, /// variable time in `self`. pub fn trailing_ones_vartime(&self) -> u32 { - let limbs = self.as_limbs(); - - let mut count = 0; - let mut i = 0; - while i < limbs.len() { - let l = limbs[i]; - let z = l.trailing_ones(); - count += z; - if z != Limb::BITS { - break; - } - i += 1; - } - - count + trailing_ones_vartime(&self.limbs) } /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`. @@ -129,6 +89,73 @@ impl BoxedUint { *limb = Limb::conditional_select(&old_limb, &new_limb, is_right_limb); } } + + pub(crate) fn set_bit_vartime(&mut self, index: u32, bit_value: bool) { + let limb_num = (index / Limb::BITS) as usize; + let index_in_limb = index % Limb::BITS; + if bit_value { + self.limbs[limb_num].0 |= 1 << index_in_limb; + } else { + #[allow(trivial_numeric_casts)] + { + self.limbs[limb_num].0 &= !((1 as Word) << index_in_limb); + } + } + } +} + +impl BitOps for BoxedUint { + fn bits_precision(&self) -> u32 { + self.bits_precision() + } + + fn bytes_precision(&self) -> usize { + self.nlimbs() * Limb::BYTES + } + + fn leading_zeros(&self) -> u32 { + self.leading_zeros() + } + + fn bits(&self) -> u32 { + self.bits() + } + + fn bit(&self, index: u32) -> Choice { + self.bit(index) + } + + fn set_bit(&mut self, index: u32, bit_value: Choice) { + self.set_bit(index, bit_value) + } + + fn trailing_zeros(&self) -> u32 { + self.trailing_zeros() + } + + fn trailing_ones(&self) -> u32 { + self.trailing_ones() + } + + fn bit_vartime(&self, index: u32) -> bool { + self.bit_vartime(index) + } + + fn bits_vartime(&self) -> u32 { + self.bits_vartime() + } + + fn set_bit_vartime(&mut self, index: u32, bit_value: bool) { + self.set_bit_vartime(index, bit_value) + } + + fn trailing_zeros_vartime(&self) -> u32 { + self.trailing_zeros_vartime() + } + + fn trailing_ones_vartime(&self) -> u32 { + self.trailing_ones_vartime() + } } #[cfg(test)] diff --git a/src/uint/boxed/sqrt.rs b/src/uint/boxed/sqrt.rs index ad7ffddcb..a9484ce75 100644 --- a/src/uint/boxed/sqrt.rs +++ b/src/uint/boxed/sqrt.rs @@ -2,7 +2,7 @@ use subtle::{ConstantTimeEq, ConstantTimeGreater, CtOption}; -use crate::{BoxedUint, ConstantTimeSelect, NonZero, SquareRoot}; +use crate::{BitOps, BoxedUint, ConstantTimeSelect, NonZero, SquareRoot}; impl BoxedUint { /// Computes √(`self`) in constant time.