diff --git a/Cargo.lock b/Cargo.lock index af5ec0a29..8ca1b052a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,6 +143,12 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +[[package]] +name = "cmov" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db222d9b98fd3e72eecfc24c870ac8944dac951418802467dcdbf045c8c37d3" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -222,6 +228,7 @@ version = "0.7.0-rc.10" dependencies = [ "chacha20", "criterion", + "ctutils", "der", "hex-literal", "hybrid-array", @@ -237,6 +244,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ctutils" +version = "0.1.0" +source = "git+https://github.com/RustCrypto/utils#5d2e2fd107c46d0ed20d54e0014eb43176672f3d" +dependencies = [ + "cmov", + "subtle", +] + [[package]] name = "der" version = "0.8.0-rc.10" diff --git a/Cargo.toml b/Cargo.toml index af0e110d9..95a986185 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ edition = "2024" rust-version = "1.85" [dependencies] +ctutils = { version = "0.1", features = ["subtle"] } subtle = { version = "2.6", default-features = false } # optional dependencies @@ -85,3 +86,6 @@ harness = false [profile.dev] opt-level = 2 + +[patch.crates-io.ctutils] +git = "https://github.com/RustCrypto/utils" diff --git a/src/const_choice.rs b/src/const_choice.rs deleted file mode 100644 index e4579b634..000000000 --- a/src/const_choice.rs +++ /dev/null @@ -1,637 +0,0 @@ -use subtle::{Choice, CtOption}; - -use crate::{ - Int, Limb, NonZero, NonZeroInt, Odd, OddInt, Uint, WideWord, Word, - modular::{ConstMontyForm, ConstMontyParams, SafeGcdInverter}, -}; - -/// A boolean value returned by constant-time `const fn`s. -// TODO: should be replaced by `subtle::Choice` or `CtOption` -// when `subtle` starts supporting const fns. -#[derive(Debug, Copy, Clone)] -pub struct ConstChoice(Word); - -impl ConstChoice { - /// The falsy value. - pub const FALSE: Self = Self(0); - - /// The truthy value. - pub const TRUE: Self = Self(Word::MAX); - - #[inline] - #[allow(trivial_numeric_casts)] - pub(crate) const fn as_u32_mask(&self) -> u32 { - self.0 as u32 - } - - #[inline] - #[cfg(target_pointer_width = "32")] - pub(crate) const fn as_u64_mask(&self) -> u64 { - ((self.0 as u64) << 32) | (self.0 as u64) - } - - #[inline] - #[cfg(target_pointer_width = "64")] - pub(crate) const fn as_u64_mask(&self) -> u64 { - self.0 - } - - #[inline] - pub(crate) const fn as_word_mask(&self) -> Word { - self.0 - } - - /// Returns the truthy value if `value == Word::MAX`, and the falsy value if `value == 0`. - /// Panics for other values. - #[inline] - pub(crate) const fn from_word_mask(value: Word) -> Self { - debug_assert!(value == Self::FALSE.0 || value == Self::TRUE.0); - Self(value) - } - - /// Returns the truthy value if `value == 1`, and the falsy value if `value == 0`. - /// Panics for other values. - #[inline] - pub(crate) const fn from_word_lsb(value: Word) -> Self { - debug_assert!(value == 0 || value == 1); - Self(value.wrapping_neg()) - } - - /// Returns the truthy value if the most significant bit of `value` is `1`, - /// and the falsy value if it equals `0`. - #[inline] - pub(crate) const fn from_word_msb(value: Word) -> Self { - Self::from_word_lsb(value >> (Word::BITS - 1)) - } - - /// Returns the truthy value if `value == 1`, and the falsy value if `value == 0`. - /// Panics for other values. - #[inline] - pub(crate) const fn from_wide_word_lsb(value: WideWord) -> Self { - debug_assert!(value == 0 || value == 1); - Self(value.wrapping_neg() as Word) - } - - #[inline] - pub(crate) const fn from_u32_lsb(value: u32) -> Self { - debug_assert!(value == 0 || value == 1); - #[allow(trivial_numeric_casts)] - Self((value as Word).wrapping_neg()) - } - - #[inline] - pub(crate) const fn from_u64_lsb(value: u64) -> Self { - debug_assert!(value == 0 || value == 1); - #[allow(trivial_numeric_casts)] - Self((value as Word).wrapping_neg()) - } - - /// Returns the truthy value if `value != 0`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_u32_nonzero(value: u32) -> Self { - Self::from_u32_lsb((value | value.wrapping_neg()) >> (u32::BITS - 1)) - } - - /// Returns the truthy value if `value != 0`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_u64_nonzero(value: u64) -> Self { - Self::from_u64_lsb((value | value.wrapping_neg()) >> (u64::BITS - 1)) - } - - /// Returns the truthy value if `value != 0`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_word_nonzero(value: Word) -> Self { - Self::from_word_lsb((value | value.wrapping_neg()) >> (Word::BITS - 1)) - } - - /// Returns the truthy value if `x == y`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_u32_eq(x: u32, y: u32) -> Self { - Self::from_u32_nonzero(x ^ y).not() - } - - /// Returns the truthy value if `x == y`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_word_eq(x: Word, y: Word) -> Self { - Self::from_word_nonzero(x ^ y).not() - } - - /// Returns the truthy value if `x < y`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_word_lt(x: Word, y: Word) -> Self { - // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) - let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (Word::BITS - 1); - Self::from_word_lsb(bit) - } - - /// Returns the truthy value if `x > y`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_word_gt(x: Word, y: Word) -> Self { - Self::from_word_lt(y, x) - } - - /// Returns the truthy value if `x < y`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_u32_lt(x: u32, y: u32) -> Self { - // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) - let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (u32::BITS - 1); - Self::from_u32_lsb(bit) - } - - /// Returns the truthy value if `x <= y` and the falsy value otherwise. - #[inline] - pub(crate) const fn from_word_le(x: Word, y: Word) -> Self { - // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) - let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (Word::BITS - 1); - Self::from_word_lsb(bit) - } - - /// Returns the truthy value if `x <= y` and the falsy value otherwise. - #[inline] - pub(crate) const fn from_wide_word_le(x: WideWord, y: WideWord) -> Self { - // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) - let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (WideWord::BITS - 1); - Self::from_wide_word_lsb(bit) - } - - /// Returns the truthy value if `x <= y` and the falsy value otherwise. - #[inline] - pub(crate) const fn from_u32_le(x: u32, y: u32) -> Self { - // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) - let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (u32::BITS - 1); - Self::from_u32_lsb(bit) - } - - /// Returns the truthy value if `x == y`, and the falsy value otherwise. - #[inline] - pub(crate) const fn from_i64_eq(x: i64, y: i64) -> Self { - Self::from_word_nonzero(x as Word ^ y as Word).not() - } - - #[inline] - pub(crate) const fn not(&self) -> Self { - Self(!self.0) - } - - #[inline] - pub(crate) const fn or(&self, other: Self) -> Self { - Self(self.0 | other.0) - } - - #[inline] - pub(crate) const fn and(&self, other: Self) -> Self { - Self(self.0 & other.0) - } - - #[inline] - pub(crate) const fn xor(&self, other: Self) -> Self { - Self(self.0 ^ other.0) - } - - #[inline] - pub(crate) const fn ne(&self, other: Self) -> Self { - Self::xor(self, other) - } - - #[inline] - pub(crate) const fn eq(&self, other: Self) -> Self { - Self::ne(self, other).not() - } - - /// Return `b` if `self` is truthy, otherwise return `a`. - #[inline] - pub(crate) const fn select_word(&self, a: Word, b: Word) -> Word { - a ^ (self.0 & (a ^ b)) - } - - /// Return `b` if `self` is truthy, otherwise return `a`. - #[inline] - pub(crate) const fn select_wide_word(&self, a: WideWord, b: WideWord) -> WideWord { - let mask = ((self.0 as WideWord) << Word::BITS) | (self.0 as WideWord); - a ^ (mask & (a ^ b)) - } - - /// Return `b` if `self` is truthy, otherwise return `a`. - #[inline] - pub(crate) const fn select_u32(&self, a: u32, b: u32) -> u32 { - a ^ (self.as_u32_mask() & (a ^ b)) - } - - /// Return `b` if `self` is truthy, otherwise return `a`. - #[inline] - pub(crate) const fn select_i64(&self, a: i64, b: i64) -> i64 { - self.select_u64(a as u64, b as u64) as i64 - } - - /// Return `b` if `self` is truthy, otherwise return `a`. - #[inline] - pub(crate) const fn select_u64(&self, a: u64, b: u64) -> u64 { - a ^ (self.as_u64_mask() & (a ^ b)) - } - - /// Return `x` if `self` is truthy, otherwise return 0. - #[inline] - pub(crate) const fn if_true_word(&self, x: Word) -> Word { - x & self.0 - } - - /// Return `x` if `self` is truthy, otherwise return 0. - #[inline] - pub(crate) const fn if_true_u32(&self, x: u32) -> u32 { - x & self.as_u32_mask() - } - - /// WARNING: this method should only be used in contexts that aren't constant-time critical! - #[inline] - pub(crate) const fn to_bool_vartime(self) -> bool { - self.0 != 0 - } - - #[inline] - pub(crate) const fn to_u8(self) -> u8 { - (self.0 as u8) & 1 - } -} - -/// `const` equivalent of `u32::max(a, b)`. -pub const fn u32_max(a: u32, b: u32) -> u32 { - ConstChoice::from_u32_lt(a, b).select_u32(a, b) -} - -/// `const` equivalent of `u32::min(a, b)`. -pub const fn u32_min(a: u32, b: u32) -> u32 { - ConstChoice::from_u32_lt(a, b).select_u32(b, a) -} - -impl From for Choice { - #[inline] - fn from(choice: ConstChoice) -> Self { - Choice::from(choice.to_u8()) - } -} - -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.to_bool_vartime() - } -} - -impl PartialEq for ConstChoice { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -/// An equivalent of `subtle::CtOption` usable in a `const fn` context. -#[derive(Debug, Clone)] -pub struct ConstCtOption { - value: T, - is_some: ConstChoice, -} - -impl ConstCtOption { - #[inline] - pub(crate) const fn new(value: T, is_some: ConstChoice) -> Self { - Self { value, is_some } - } - - #[inline] - pub(crate) const fn some(value: T) -> Self { - Self { - value, - is_some: ConstChoice::TRUE, - } - } - - #[inline] - pub(crate) const fn none(dummy_value: T) -> Self { - Self { - value: dummy_value, - is_some: ConstChoice::FALSE, - } - } - - /// Returns a reference to the contents of this structure. - /// - /// **Note:** if the second element is `None`, the first value may take any value. - #[inline] - pub(crate) const fn components_ref(&self) -> (&T, ConstChoice) { - // Since Rust is not smart enough to tell that we would be moving the value, - // and hence no destructors will be called, we have to return a reference instead. - // See https://github.com/rust-lang/rust/issues/66753 - (&self.value, self.is_some) - } - - /// Returns a true [`ConstChoice`] if this value is `Some`. - #[inline] - pub const fn is_some(&self) -> ConstChoice { - self.is_some - } - - /// Returns a true [`ConstChoice`] if this value is `None`. - #[inline] - pub const fn is_none(&self) -> ConstChoice { - self.is_some.not() - } - - /// This returns the underlying value but panics if it is not `Some`. - #[inline] - #[track_caller] - pub fn unwrap(self) -> T { - assert!( - self.is_some.to_bool_vartime(), - "called `ConstCtOption::unwrap()` on a `None` value" - ); - self.value - } - - /// Apply an additional [`ConstChoice`] requirement to `is_some`. - #[inline] - pub(crate) const fn and_choice(mut self, is_some: ConstChoice) -> Self { - self.is_some = self.is_some.and(is_some); - self - } -} - -impl From> for CtOption { - #[inline] - fn from(value: ConstCtOption) -> Self { - CtOption::new(value.value, value.is_some.into()) - } -} - -impl From> for Option { - #[inline] - fn from(value: ConstCtOption) -> Self { - if value.is_some.into() { - Some(value.value) - } else { - None - } - } -} - -// Need specific implementations to work around the -// "destructors cannot be evaluated at compile-time" error -// See https://github.com/rust-lang/rust/issues/66753 - -impl ConstCtOption> { - /// This returns the underlying value if it is `Some` or the provided value otherwise. - #[inline] - pub const fn unwrap_or(self, def: Uint) -> Uint { - Uint::select(&def, &self.value, self.is_some) - } - - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> Uint { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } - - /// Returns the contained value, interpreting the underlying [`Uint`] value as an [`Int`]. - #[inline] - pub const fn as_int(&self) -> ConstCtOption> { - ConstCtOption::new(*self.value.as_int(), self.is_some) - } -} - -impl ConstCtOption<(Uint, Uint)> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> (Uint, Uint) { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption>> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> NonZero> { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption>> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> Odd> { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption> { - /// This returns the underlying value if it is `Some` or the provided value otherwise. - #[inline] - pub const fn unwrap_or(self, def: Int) -> Int { - Int::select(&def, &self.value, self.is_some) - } - - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> Int { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> NonZeroInt { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> OddInt { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> NonZero { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl ConstCtOption> { - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by - /// `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> SafeGcdInverter { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -impl, const LIMBS: usize> ConstCtOption> { - /// This returns the underlying value if it is `Some` or the provided value otherwise. - #[inline] - pub const fn unwrap_or(self, def: ConstMontyForm) -> ConstMontyForm { - ConstMontyForm::::select(&def, &self.value, self.is_some) - } - - /// Returns the contained value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is none with a custom panic message provided by `msg`. - #[inline] - #[track_caller] - pub const fn expect(self, msg: &str) -> ConstMontyForm { - assert!(self.is_some.to_bool_vartime(), "{}", msg); - self.value - } -} - -#[cfg(test)] -mod tests { - use super::{ConstChoice, u32_max, u32_min}; - use crate::{WideWord, Word}; - - #[test] - fn from_u64_lsb() { - assert_eq!(ConstChoice::from_u64_lsb(0), ConstChoice::FALSE); - assert_eq!(ConstChoice::from_u64_lsb(1), ConstChoice::TRUE); - } - - #[test] - fn from_word_lt() { - assert_eq!(ConstChoice::from_word_lt(4, 5), ConstChoice::TRUE); - assert_eq!(ConstChoice::from_word_lt(5, 5), ConstChoice::FALSE); - assert_eq!(ConstChoice::from_word_lt(6, 5), ConstChoice::FALSE); - } - - #[test] - fn from_word_gt() { - assert_eq!(ConstChoice::from_word_gt(4, 5), ConstChoice::FALSE); - assert_eq!(ConstChoice::from_word_gt(5, 5), ConstChoice::FALSE); - assert_eq!(ConstChoice::from_word_gt(6, 5), ConstChoice::TRUE); - } - - #[test] - fn from_wide_word_le() { - assert_eq!(ConstChoice::from_wide_word_le(4, 5), ConstChoice::TRUE); - assert_eq!(ConstChoice::from_wide_word_le(5, 5), ConstChoice::TRUE); - assert_eq!(ConstChoice::from_wide_word_le(6, 5), ConstChoice::FALSE); - } - - #[test] - fn select_u32() { - let a: u32 = 1; - let b: u32 = 2; - assert_eq!(ConstChoice::TRUE.select_u32(a, b), b); - assert_eq!(ConstChoice::FALSE.select_u32(a, b), a); - } - - #[test] - fn select_u64() { - let a: u64 = 1; - let b: u64 = 2; - assert_eq!(ConstChoice::TRUE.select_u64(a, b), b); - assert_eq!(ConstChoice::FALSE.select_u64(a, b), a); - } - - #[test] - fn select_word() { - let a: Word = 1; - let b: Word = 2; - assert_eq!(ConstChoice::TRUE.select_word(a, b), b); - assert_eq!(ConstChoice::FALSE.select_word(a, b), a); - } - - #[test] - fn select_wide_word() { - let a: WideWord = (1 << Word::BITS) + 1; - let b: WideWord = (3 << Word::BITS) + 4; - assert_eq!(ConstChoice::TRUE.select_wide_word(a, b), b); - assert_eq!(ConstChoice::FALSE.select_wide_word(a, b), a); - } - - #[test] - fn test_u32_const_min() { - assert_eq!(u32_min(0, 5), 0); - assert_eq!(u32_min(7, 0), 0); - assert_eq!(u32_min(7, 5), 5); - assert_eq!(u32_min(7, 7), 7); - } - - #[test] - fn test_u32_const_max() { - assert_eq!(u32_max(0, 5), 5); - assert_eq!(u32_max(7, 0), 7); - assert_eq!(u32_max(7, 5), 7); - assert_eq!(u32_max(7, 7), 7); - } -} diff --git a/src/ct.rs b/src/ct.rs new file mode 100644 index 000000000..ed83b6c00 --- /dev/null +++ b/src/ct.rs @@ -0,0 +1,304 @@ +pub use ctutils::Choice as ConstChoice; +use subtle::CtOption; + +use crate::{ + Int, Limb, NonZero, NonZeroInt, Odd, OddInt, Uint, + modular::{ConstMontyForm, ConstMontyParams, SafeGcdInverter}, +}; + +/// `const` equivalent of `u32::max(a, b)`. +pub const fn u32_max(a: u32, b: u32) -> u32 { + ConstChoice::from_u32_lt(a, b).select_u32(a, b) +} + +/// `const` equivalent of `u32::min(a, b)`. +pub const fn u32_min(a: u32, b: u32) -> u32 { + ConstChoice::from_u32_lt(a, b).select_u32(b, a) +} + +/// An equivalent of `subtle::CtOption` usable in a `const fn` context. +#[derive(Debug, Clone)] +pub struct ConstCtOption { + value: T, + is_some: ConstChoice, +} + +impl ConstCtOption { + #[inline] + pub(crate) const fn new(value: T, is_some: ConstChoice) -> Self { + Self { value, is_some } + } + + #[inline] + pub(crate) const fn some(value: T) -> Self { + Self { + value, + is_some: ConstChoice::TRUE, + } + } + + #[inline] + pub(crate) const fn none(dummy_value: T) -> Self { + Self { + value: dummy_value, + is_some: ConstChoice::FALSE, + } + } + + /// Returns a reference to the contents of this structure. + /// + /// **Note:** if the second element is `None`, the first value may take any value. + #[inline] + pub(crate) const fn components_ref(&self) -> (&T, ConstChoice) { + // Since Rust is not smart enough to tell that we would be moving the value, + // and hence no destructors will be called, we have to return a reference instead. + // See https://github.com/rust-lang/rust/issues/66753 + (&self.value, self.is_some) + } + + /// Returns a true [`ConstChoice`] if this value is `Some`. + #[inline] + pub const fn is_some(&self) -> ConstChoice { + self.is_some + } + + /// Returns a true [`ConstChoice`] if this value is `None`. + #[inline] + pub const fn is_none(&self) -> ConstChoice { + self.is_some.not() + } + + /// This returns the underlying value but panics if it is not `Some`. + #[inline] + #[track_caller] + pub fn unwrap(self) -> T { + assert!( + self.is_some.to_bool_vartime(), + "called `ConstCtOption::unwrap()` on a `None` value" + ); + self.value + } + + /// Apply an additional [`ConstChoice`] requirement to `is_some`. + #[inline] + pub(crate) const fn and_choice(mut self, is_some: ConstChoice) -> Self { + self.is_some = self.is_some.and(is_some); + self + } +} + +impl From> for CtOption { + #[inline] + fn from(value: ConstCtOption) -> Self { + CtOption::new(value.value, value.is_some.into()) + } +} + +impl From> for Option { + #[inline] + fn from(value: ConstCtOption) -> Self { + if value.is_some.into() { + Some(value.value) + } else { + None + } + } +} + +// Need specific implementations to work around the +// "destructors cannot be evaluated at compile-time" error +// See https://github.com/rust-lang/rust/issues/66753 + +impl ConstCtOption> { + /// This returns the underlying value if it is `Some` or the provided value otherwise. + #[inline] + pub const fn unwrap_or(self, def: Uint) -> Uint { + Uint::select(&def, &self.value, self.is_some) + } + + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> Uint { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } + + /// Returns the contained value, interpreting the underlying [`Uint`] value as an [`Int`]. + #[inline] + pub const fn as_int(&self) -> ConstCtOption> { + ConstCtOption::new(*self.value.as_int(), self.is_some) + } +} + +impl ConstCtOption<(Uint, Uint)> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> (Uint, Uint) { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption>> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> NonZero> { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption>> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> Odd> { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption> { + /// This returns the underlying value if it is `Some` or the provided value otherwise. + #[inline] + pub const fn unwrap_or(self, def: Int) -> Int { + Int::select(&def, &self.value, self.is_some) + } + + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> Int { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> NonZeroInt { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> OddInt { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> NonZero { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl ConstCtOption> { + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by + /// `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> SafeGcdInverter { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +impl, const LIMBS: usize> ConstCtOption> { + /// This returns the underlying value if it is `Some` or the provided value otherwise. + #[inline] + pub const fn unwrap_or(self, def: ConstMontyForm) -> ConstMontyForm { + ConstMontyForm::::select(&def, &self.value, self.is_some) + } + + /// Returns the contained value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is none with a custom panic message provided by `msg`. + #[inline] + #[track_caller] + pub const fn expect(self, msg: &str) -> ConstMontyForm { + assert!(self.is_some.to_bool_vartime(), "{}", msg); + self.value + } +} + +#[cfg(test)] +mod tests { + use super::{u32_max, u32_min}; + + #[test] + fn test_u32_const_min() { + assert_eq!(u32_min(0, 5), 0); + assert_eq!(u32_min(7, 0), 0); + assert_eq!(u32_min(7, 5), 5); + assert_eq!(u32_min(7, 7), 7); + } + + #[test] + fn test_u32_const_max() { + assert_eq!(u32_max(0, 5), 5); + assert_eq!(u32_max(7, 0), 7); + assert_eq!(u32_max(7, 5), 7); + assert_eq!(u32_max(7, 7), 7); + } +} diff --git a/src/int/gcd.rs b/src/int/gcd.rs index abc6e7d56..4232bfaa8 100644 --- a/src/int/gcd.rs +++ b/src/int/gcd.rs @@ -2,7 +2,7 @@ //! which is described by Pornin in "Optimized Binary GCD for Modular Inversion". //! Ref: -use crate::const_choice::u32_min; +use crate::ct::u32_min; use crate::uint::gcd::{OddUintXgcdOutput, impl_gcd_uint_lhs, impl_gcd_uint_rhs}; use crate::{ ConstChoice, Gcd, Int, NonZero, NonZeroInt, NonZeroUint, Odd, OddInt, OddUint, Uint, Xgcd, diff --git a/src/int/mod_symbol.rs b/src/int/mod_symbol.rs index ba0489914..33a57cd59 100644 --- a/src/int/mod_symbol.rs +++ b/src/int/mod_symbol.rs @@ -1,6 +1,6 @@ //! Support for computing modular symbols. -use crate::{ConstChoice, Int, JacobiSymbol, Odd, Uint}; +use crate::{Int, JacobiSymbol, Odd, Uint, word}; impl Int { /// Compute the Jacobi symbol `(self|rhs)`. @@ -11,7 +11,7 @@ impl Int { let (abs, sign) = self.abs_sign(); let jacobi = abs.jacobi_symbol(rhs) as i64; // (-self|rhs) = -(self|rhs) iff rhs = 3 mod 4 - let swap = sign.and(ConstChoice::from_word_eq(rhs.as_ref().limbs[0].0 & 3, 3)); + let swap = sign.and(word::from_word_eq(rhs.as_ref().limbs[0].0 & 3, 3)); JacobiSymbol::from_i8(swap.select_i64(jacobi, -jacobi) as i8) } diff --git a/src/int/sign.rs b/src/int/sign.rs index 219aa6bdc..e5787e31e 100644 --- a/src/int/sign.rs +++ b/src/int/sign.rs @@ -1,4 +1,4 @@ -use crate::{ConstChoice, ConstCtOption, Int, Uint, Word}; +use crate::{ConstChoice, ConstCtOption, Int, Uint, Word, word}; use num_traits::ConstZero; impl Int { @@ -38,7 +38,7 @@ impl Int { /// Whether this [`Int`] is negative, as a `ConstChoice`. #[inline(always)] pub const fn is_negative(&self) -> ConstChoice { - ConstChoice::from_word_msb(self.most_significant_word()) + word::from_word_msb(self.most_significant_word()) } /// Whether this [`Int`] is positive, as a `ConstChoice`. diff --git a/src/lib.rs b/src/lib.rs index e76a521f4..780619241 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,14 +187,15 @@ pub use { pub use crate::uint::boxed::BoxedUint; pub use crate::{ checked::Checked, - const_choice::{ConstChoice, ConstCtOption}, + ct::{ConstChoice, ConstCtOption}, int::{types::*, *}, jacobi::JacobiSymbol, - limb::{Limb, WideWord, Word}, + limb::Limb, non_zero::*, odd::*, traits::*, uint::{div_limb::Reciprocal, *}, + word::{WideWord, Word}, wrapping::Wrapping, }; @@ -206,7 +207,7 @@ pub mod modular; #[cfg(feature = "hybrid-array")] mod array; mod checked; -mod const_choice; +mod ct; mod int; mod jacobi; mod limb; @@ -215,6 +216,7 @@ mod odd; mod primitives; mod traits; mod uint; +mod word; mod wrapping; /// Import prelude for this crate: includes important traits. diff --git a/src/limb.rs b/src/limb.rs index b884ad858..ae1056d60 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -19,40 +19,15 @@ mod sub; #[cfg(feature = "rand_core")] mod rand; -use crate::{Bounded, ConstCtOption, ConstOne, ConstZero, Constants, NonZero, One, Zero}; +use crate::{ + Bounded, ConstCtOption, ConstOne, ConstZero, Constants, NonZero, One, WideWord, Word, Zero, +}; use core::fmt; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; #[cfg(feature = "serde")] use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] -compile_error!("this crate builds on 32-bit and 64-bit platforms only"); - -// -// 32-bit definitions -// - -/// Inner integer type that the [`Limb`] newtype wraps. -#[cfg(target_pointer_width = "32")] -pub type Word = u32; - -/// Unsigned wide integer type: double the width of [`Word`]. -#[cfg(target_pointer_width = "32")] -pub type WideWord = u64; - -// -// 64-bit definitions -// - -/// Unsigned integer type that the [`Limb`] newtype wraps. -#[cfg(target_pointer_width = "64")] -pub type Word = u64; - -/// Wide integer type: double the width of [`Word`]. -#[cfg(target_pointer_width = "64")] -pub type WideWord = u128; - /// Big integers are represented as an array/vector of smaller CPU word-size integers called /// "limbs". /// diff --git a/src/limb/cmp.rs b/src/limb/cmp.rs index 607f57d1a..5c1c8feea 100644 --- a/src/limb/cmp.rs +++ b/src/limb/cmp.rs @@ -1,6 +1,6 @@ //! Limb comparisons -use crate::{ConstChoice, Limb}; +use crate::{ConstChoice, Limb, word}; use core::cmp::Ordering; use subtle::{ Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess, @@ -29,19 +29,22 @@ impl Limb { /// Return `b` if `c` is truthy, otherwise return `a`. #[inline] pub(crate) const fn select(a: Self, b: Self, c: ConstChoice) -> Self { - Self(c.select_word(a.0, b.0)) + Self(word::select_word(c, a.0, b.0)) } /// Swap the values of `a` and `b` if `c` is truthy, otherwise do nothing. #[inline] pub(crate) const fn ct_conditional_swap(a: &mut Self, b: &mut Self, c: ConstChoice) { - (*a, *b) = (Self(c.select_word(a.0, b.0)), Self(c.select_word(b.0, a.0))) + (*a, *b) = ( + Self(word::select_word(c, a.0, b.0)), + Self(word::select_word(c, b.0, a.0)), + ) } /// Returns the truthy value if `self != 0` and the falsy value otherwise. #[inline] pub(crate) const fn is_nonzero(&self) -> ConstChoice { - ConstChoice::from_word_nonzero(self.0) + word::from_word_nonzero(self.0) } } @@ -60,14 +63,14 @@ impl ConstantTimeEq for Limb { impl ConstantTimeGreater for Limb { #[inline] fn ct_gt(&self, other: &Self) -> Choice { - ConstChoice::from_word_gt(self.0, other.0).into() + word::from_word_gt(self.0, other.0).into() } } impl ConstantTimeLess for Limb { #[inline] fn ct_lt(&self, other: &Self) -> Choice { - ConstChoice::from_word_lt(self.0, other.0).into() + word::from_word_lt(self.0, other.0).into() } } diff --git a/src/modular/bingcd/div_mod_2k.rs b/src/modular/bingcd/div_mod_2k.rs index 79ccd096a..5f23efbe6 100644 --- a/src/modular/bingcd/div_mod_2k.rs +++ b/src/modular/bingcd/div_mod_2k.rs @@ -1,6 +1,6 @@ //! Compute `x / 2^k mod q` for some prime `q`. -use crate::const_choice::u32_min; -use crate::{ConstChoice, Limb, Odd, OddUint, Uint}; + +use crate::{ConstChoice, Limb, Odd, OddUint, Uint, ct::u32_min, word}; impl Uint { /// Compute `self / 2^k mod q`. @@ -80,7 +80,7 @@ impl Limb { let (shifted, carry) = self.shr1(); self = Self::select(self, shifted, execute); - let overflow = ConstChoice::from_word_msb(carry.0); + let overflow = word::from_word_msb(carry.0); let add_back_q = overflow.and(execute); self = self.wrapping_add(Self::select(Self::ZERO, one_half_mod_q, add_back_q)); factor = factor.bitxor(Self::select(Self::ZERO, Self::ONE.shl(i), add_back_q)); diff --git a/src/modular/bingcd/gcd.rs b/src/modular/bingcd/gcd.rs index 8edf83b37..6f88abf74 100644 --- a/src/modular/bingcd/gcd.rs +++ b/src/modular/bingcd/gcd.rs @@ -1,5 +1,8 @@ -use crate::const_choice::{u32_max, u32_min}; -use crate::{ConstChoice, Limb, Odd, U64, U128, Uint, Word}; +use crate::{ + ConstChoice, Limb, Odd, U64, U128, Uint, Word, + ct::{u32_max, u32_min}, + word, +}; /// Binary GCD update step. /// @@ -29,7 +32,7 @@ pub(super) const fn bingcd_step( *a = a_sub_b.wrapping_neg_if(swap).shr1(); // (b|a) = -(a|b) iff a = b = 3 mod 4 (quadratic reciprocity) - let mut jacobi_neg = swap.if_true_word(a_b_mod_4 & (a_b_mod_4 >> 1) & 1); + let mut jacobi_neg = word::select_word(swap, 0, a_b_mod_4 & (a_b_mod_4 >> 1) & 1); // (2a|b) = -(a|b) iff b = ±3 mod 8 // b is always odd, so we ignore the lower bit and check that bits 2 and 3 are '01' or '10' diff --git a/src/modular/bingcd/xgcd.rs b/src/modular/bingcd/xgcd.rs index fd5631ec2..b13700997 100644 --- a/src/modular/bingcd/xgcd.rs +++ b/src/modular/bingcd/xgcd.rs @@ -1,6 +1,6 @@ //! The Binary Extended GCD algorithm. use super::gcd::bingcd_step; -use crate::const_choice::u32_max; +use crate::ct::u32_max; use crate::modular::bingcd::matrix::{DividedIntMatrix, DividedPatternMatrix, PatternMatrix, Unit}; use crate::{ConstChoice, Int, Limb, NonZeroUint, Odd, OddUint, U64, U128, Uint, Word}; diff --git a/src/modular/boxed_monty_form/mul.rs b/src/modular/boxed_monty_form/mul.rs index 33cc936e4..e3b0e1c99 100644 --- a/src/modular/boxed_monty_form/mul.rs +++ b/src/modular/boxed_monty_form/mul.rs @@ -7,8 +7,8 @@ use super::{BoxedMontyForm, BoxedMontyParams}; use crate::{ - AmmMultiplier, BoxedUint, ConstChoice, Limb, MontyMultiplier, Square, SquareAssign, - modular::mul::montgomery_multiply_inner, + AmmMultiplier, BoxedUint, Limb, MontyMultiplier, Square, SquareAssign, + modular::mul::montgomery_multiply_inner, word, }; use core::ops::{Mul, MulAssign}; use subtle::ConstantTimeLess; @@ -314,7 +314,7 @@ pub(crate) fn almost_montgomery_mul( modulus.as_limbs(), mod_neg_inv, ); - let overflow = ConstChoice::from_word_lsb(overflow.0); + let overflow = word::from_word_lsb(overflow.0); out.conditional_borrowing_sub_assign(modulus, overflow.into()); } diff --git a/src/modular/pow.rs b/src/modular/pow.rs index 0098268c6..5d8a5d301 100644 --- a/src/modular/pow.rs +++ b/src/modular/pow.rs @@ -1,5 +1,5 @@ use super::mul::{mul_montgomery_form, square_montgomery_form}; -use crate::{AmmMultiplier, ConstChoice, Limb, Monty, Odd, Uint, Unsigned, Word}; +use crate::{AmmMultiplier, Limb, Monty, Odd, Uint, Unsigned, Word, word}; #[cfg(feature = "alloc")] use alloc::vec::Vec; @@ -258,7 +258,7 @@ const fn multi_exponentiate_montgomery_form_internal::select(&power, &powers[j as usize], choice); j += 1; } diff --git a/src/modular/safegcd.rs b/src/modular/safegcd.rs index 65dfd42c4..6dcb1f979 100644 --- a/src/modular/safegcd.rs +++ b/src/modular/safegcd.rs @@ -14,7 +14,7 @@ pub(crate) mod boxed; use core::fmt; -use crate::{ConstChoice, ConstCtOption, I64, Int, Limb, Odd, U64, Uint, const_choice::u32_min}; +use crate::{ConstChoice, ConstCtOption, I64, Int, Limb, Odd, U64, Uint, ct::u32_min}; const GCD_BATCH_SIZE: u32 = 62; diff --git a/src/modular/safegcd/boxed.rs b/src/modular/safegcd/boxed.rs index 3c06f2dc3..77d250ebe 100644 --- a/src/modular/safegcd/boxed.rs +++ b/src/modular/safegcd/boxed.rs @@ -7,7 +7,7 @@ use super::{GCD_BATCH_SIZE, Matrix, iterations, jump}; use crate::{ BoxedUint, ConstChoice, ConstCtOption, ConstantTimeSelect, I64, Int, Limb, NonZero, Odd, Resize, U64, Uint, - const_choice::{u32_max, u32_min}, + ct::{u32_max, u32_min}, }; use core::fmt; use subtle::{Choice, CtOption}; diff --git a/src/uint/add.rs b/src/uint/add.rs index db1a54c0c..45ed9b140 100644 --- a/src/uint/add.rs +++ b/src/uint/add.rs @@ -1,6 +1,6 @@ //! [`Uint`] addition operations. -use crate::{Checked, CheckedAdd, ConstChoice, Limb, Uint, Wrapping, WrappingAdd, Zero}; +use crate::{Checked, CheckedAdd, Limb, Uint, Wrapping, WrappingAdd, Zero, word}; use core::ops::{Add, AddAssign}; use subtle::CtOption; @@ -30,7 +30,7 @@ impl Uint { /// Perform saturating addition, returning `MAX` on overflow. pub const fn saturating_add(&self, rhs: &Self) -> Self { let (res, overflow) = self.carrying_add(rhs, Limb::ZERO); - Self::select(&res, &Self::MAX, ConstChoice::from_word_lsb(overflow.0)) + Self::select(&res, &Self::MAX, word::from_word_lsb(overflow.0)) } /// Perform wrapping addition, discarding overflow. diff --git a/src/uint/boxed/cmp.rs b/src/uint/boxed/cmp.rs index 364fcc665..ec7eef1b9 100644 --- a/src/uint/boxed/cmp.rs +++ b/src/uint/boxed/cmp.rs @@ -5,7 +5,7 @@ pub(super) use core::cmp::{Ordering, max}; use super::BoxedUint; -use crate::{ConstChoice, Limb, Uint}; +use crate::{Limb, Uint, word}; use subtle::{ Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess, }; @@ -59,7 +59,7 @@ impl ConstantTimeGreater for BoxedUint { #[inline] fn ct_gt(&self, other: &Self) -> Choice { let (_, borrow) = other.borrowing_sub(self, Limb::ZERO); - ConstChoice::from_word_mask(borrow.0).into() + word::from_word_mask(borrow.0).into() } } @@ -67,7 +67,7 @@ impl ConstantTimeLess for BoxedUint { #[inline] fn ct_lt(&self, other: &Self) -> Choice { let (_, borrow) = self.borrowing_sub(other, Limb::ZERO); - ConstChoice::from_word_mask(borrow.0).into() + word::from_word_mask(borrow.0).into() } } diff --git a/src/uint/cmp.rs b/src/uint/cmp.rs index 9281c7e80..613d04086 100644 --- a/src/uint/cmp.rs +++ b/src/uint/cmp.rs @@ -2,14 +2,11 @@ //! //! By default, these are all constant-time. +use super::Uint; +use crate::{ConstChoice, Limb, word}; 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] @@ -70,7 +67,7 @@ impl Uint { /// Returns the truthy value if `self` is odd or the falsy value otherwise. pub(crate) const fn is_odd(&self) -> ConstChoice { - ConstChoice::from_word_lsb(self.limbs[0].0 & 1) + word::from_word_lsb(self.limbs[0].0 & 1) } /// Returns the truthy value if `self == rhs` or the falsy value otherwise. @@ -95,7 +92,7 @@ impl Uint { // but since we have to use Uint::wrapping_sub(), which calls `borrowing_sub()`, // there are no savings compared to just calling `borrowing_sub()` directly. let (_res, borrow) = lhs.borrowing_sub(rhs, Limb::ZERO); - ConstChoice::from_word_mask(borrow.0) + word::from_word_mask(borrow.0) } /// Returns the truthy value if `self <= rhs` and the falsy value otherwise. @@ -108,7 +105,7 @@ impl Uint { #[inline] pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> ConstChoice { let (_res, borrow) = rhs.borrowing_sub(lhs, Limb::ZERO); - ConstChoice::from_word_mask(borrow.0) + word::from_word_mask(borrow.0) } /// Returns the ordering between `self` and `rhs` as an i8. @@ -129,7 +126,7 @@ impl Uint { i += 1; } let sgn = ((borrow.0 & 2) as i8) - 1; - (diff.is_nonzero().to_u8() as i8) * sgn + (diff.is_nonzero().to_u8_vartime() as i8) * sgn } /// Returns the Ordering between `self` and `rhs` in variable time. diff --git a/src/uint/div_limb.rs b/src/uint/div_limb.rs index e1402ada6..a1943d5c6 100644 --- a/src/uint/div_limb.rs +++ b/src/uint/div_limb.rs @@ -1,12 +1,13 @@ //! Implementation of constant-time division via reciprocal precomputation, as described in //! "Improved Division by Invariant Integers" by Niels Möller and Torbjorn Granlund //! (DOI: 10.1109/TC.2010.143, ). -use subtle::{Choice, ConditionallySelectable}; use crate::{ ConstChoice, Limb, NonZero, Uint, WideWord, Word, primitives::{addhilo, widening_mul}, + word, }; +use subtle::{Choice, ConditionallySelectable}; /// Calculates the reciprocal of the given 32-bit divisor with the highmost bit set. #[cfg(target_pointer_width = "32")] @@ -36,7 +37,7 @@ pub const fn reciprocal(d: Word) -> Word { // Hence the `ct_select()`. let x = v2.wrapping_add(1); let (_lo, hi) = widening_mul(x, d); - let hi = ConstChoice::from_u32_nonzero(x).select_word(d, hi); + let hi = word::select_word(ConstChoice::from_u32_nonzero(x), d, hi); v2.wrapping_sub(hi).wrapping_sub(d) } @@ -66,7 +67,7 @@ pub const fn reciprocal(d: Word) -> Word { // Hence the `ct_select()`. let x = v3.wrapping_add(1); let (_lo, hi) = widening_mul(x, d); - let hi = ConstChoice::from_word_nonzero(x).select_word(d, hi); + let hi = word::select_word(word::from_word_nonzero(x), d, hi); v3.wrapping_sub(hi).wrapping_sub(d) } @@ -93,7 +94,7 @@ const fn short_div(mut dividend: u32, dividend_bits: u32, divisor: u32, divisor_ let bit = ConstChoice::from_u32_lt(dividend, divisor); dividend = bit.select_u32(dividend.wrapping_sub(divisor), dividend); divisor >>= 1; - quotient |= bit.not().if_true_u32(1 << i); + quotient |= bit.not().select_u32(0, 1 << i); } quotient @@ -113,17 +114,17 @@ pub(crate) const fn div2by1(u1: Word, u0: Word, reciprocal: &Reciprocal) -> (Wor let q1 = q1.wrapping_add(1); let r = u0.wrapping_sub(q1.wrapping_mul(d)); - let r_gt_q0 = ConstChoice::from_word_lt(q0, r); - let q1 = r_gt_q0.select_word(q1, q1.wrapping_sub(1)); - let r = r_gt_q0.select_word(r, r.wrapping_add(d)); + let r_gt_q0 = word::from_word_lt(q0, r); + let q1 = word::select_word(r_gt_q0, q1, q1.wrapping_sub(1)); + let r = word::select_word(r_gt_q0, r, r.wrapping_add(d)); // If this was a normal `if`, we wouldn't need wrapping ops, because there would be no overflow. // But since we calculate both results either way, we have to wrap. // Added an assert to still check the lack of overflow in debug mode. debug_assert!(r < d || q1 < Word::MAX); - let r_ge_d = ConstChoice::from_word_le(d, r); - let q1 = r_ge_d.select_word(q1, q1.wrapping_add(1)); - let r = r_ge_d.select_word(r, r.wrapping_sub(d)); + let r_ge_d = word::from_word_le(d, r); + let q1 = word::select_word(r_ge_d, q1, q1.wrapping_add(1)); + let r = word::select_word(r_ge_d, r, r.wrapping_sub(d)); (q1, r) } @@ -147,22 +148,30 @@ pub(crate) const fn div3by2( // This method corresponds to Algorithm Q: // https://janmr.com/blog/2014/04/basic-multiple-precision-long-division/ - let q_maxed = ConstChoice::from_word_eq(u2, v1_reciprocal.divisor_normalized); - let (mut quo, rem) = div2by1(q_maxed.select_word(u2, 0), u1, v1_reciprocal); + let q_maxed = word::from_word_eq(u2, v1_reciprocal.divisor_normalized); + let (mut quo, rem) = div2by1(word::select_word(q_maxed, u2, 0), u1, v1_reciprocal); // When the leading dividend word equals the leading divisor word, cap the quotient // at Word::MAX and set the remainder to the sum of the top dividend words. - quo = q_maxed.select_word(quo, Word::MAX); - let mut rem = q_maxed.select_wide_word(rem as WideWord, (u2 as WideWord) + (u1 as WideWord)); + quo = word::select_word(q_maxed, quo, Word::MAX); + let mut rem = word::select_wide_word( + q_maxed, + rem as WideWord, + (u2 as WideWord) + (u1 as WideWord), + ); let mut i = 0; while i < 2 { let qy = (quo as WideWord) * (v0 as WideWord); let rx = (rem << Word::BITS) | (u0 as WideWord); // If r < b and q*y[-2] > r*x[-1], then set q = q - 1 and r = r + v1 - let done = ConstChoice::from_word_nonzero((rem >> Word::BITS) as Word) - .or(ConstChoice::from_wide_word_le(qy, rx)); - quo = done.select_word(quo.wrapping_sub(1), quo); - rem = done.select_wide_word(rem + (v1_reciprocal.divisor_normalized as WideWord), rem); + let done = word::from_word_nonzero((rem >> Word::BITS) as Word) + .or(word::from_wide_word_le(qy, rx)); + quo = word::select_word(done, quo.wrapping_sub(1), quo); + rem = word::select_wide_word( + done, + rem + (v1_reciprocal.divisor_normalized as WideWord), + rem, + ); i += 1; } diff --git a/src/uint/gcd.rs b/src/uint/gcd.rs index 2fe1934e0..24bbb5e74 100644 --- a/src/uint/gcd.rs +++ b/src/uint/gcd.rs @@ -1,6 +1,6 @@ //! This module implements Binary (Extended) GCD for [`Uint`]. -use crate::const_choice::u32_min; +use crate::ct::u32_min; use crate::modular::bingcd::xgcd::PatternXgcdOutput; use crate::modular::safegcd; use crate::{ConstChoice, Gcd, Int, NonZero, NonZeroUint, Odd, OddUint, Uint, Xgcd}; diff --git a/src/uint/neg.rs b/src/uint/neg.rs index 2852ce8fc..2fa131ed1 100644 --- a/src/uint/neg.rs +++ b/src/uint/neg.rs @@ -1,4 +1,4 @@ -use crate::{ConstChoice, Limb, Uint, WideWord, Word, WrappingNeg}; +use crate::{ConstChoice, Limb, Uint, WideWord, Word, WrappingNeg, word}; impl Uint { /// Perform wrapping negation. @@ -20,7 +20,7 @@ impl Uint { carry = r >> Limb::BITS; i += 1; } - (Uint::new(ret), ConstChoice::from_word_lsb(carry as Word)) + (Uint::new(ret), word::from_word_lsb(carry as Word)) } /// Perform wrapping negation, if `negate` is truthy. Otherwise, return `self`. diff --git a/src/uint/neg_mod.rs b/src/uint/neg_mod.rs index 9e1436f69..2aed4a86a 100644 --- a/src/uint/neg_mod.rs +++ b/src/uint/neg_mod.rs @@ -1,6 +1,6 @@ //! [`Uint`] modular negation operations. -use crate::{Limb, NegMod, NonZero, Uint}; +use crate::{Limb, NegMod, NonZero, Uint, word}; impl Uint { /// Computes `-a mod p`. @@ -12,7 +12,7 @@ impl Uint { while i < LIMBS { // Set ret to 0 if the original value was 0, in which // case ret would be p. - ret.limbs[i].0 = z.if_true_word(ret.limbs[i].0); + ret.limbs[i].0 = word::select_word(z, 0, ret.limbs[i].0); i += 1; } ret diff --git a/src/uint/ref_type/bits.rs b/src/uint/ref_type/bits.rs index dc5a4c91d..5ab333fc0 100644 --- a/src/uint/ref_type/bits.rs +++ b/src/uint/ref_type/bits.rs @@ -1,5 +1,5 @@ use super::UintRef; -use crate::{ConstChoice, Limb, traits::BitOps}; +use crate::{ConstChoice, Limb, traits::BitOps, word}; use subtle::Choice; impl UintRef { @@ -21,11 +21,11 @@ impl UintRef { while i < self.0.len() { let bit = self.0[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); + result |= word::select_word(is_right_limb, 0, bit); i += 1; } - ConstChoice::from_word_lsb(result >> index_in_limb) + word::from_word_lsb(result >> index_in_limb) } /// Returns `true` if the bit at position `index` is set, `false` for an unset bit @@ -77,8 +77,9 @@ impl UintRef { while i < self.0.len() { let is_right_limb = ConstChoice::from_u32_eq(i as u32, limb_num); let old_limb = self.0[i].0; - let new_limb = bit_value.select_word(old_limb & !index_mask, old_limb | index_mask); - self.0[i] = Limb(is_right_limb.select_word(old_limb, new_limb)); + let new_limb = + word::select_word(bit_value, old_limb & !index_mask, old_limb | index_mask); + self.0[i] = Limb(word::select_word(is_right_limb, old_limb, new_limb)); i += 1; } } @@ -105,9 +106,9 @@ impl UintRef { i -= 1; let l = self.0[i]; let z = l.leading_zeros(); - count += nonzero_limb_not_encountered.if_true_u32(z); + count += nonzero_limb_not_encountered.select_u32(0, z); nonzero_limb_not_encountered = - nonzero_limb_not_encountered.and(ConstChoice::from_word_nonzero(l.0).not()); + nonzero_limb_not_encountered.and(word::from_word_nonzero(l.0).not()); } count @@ -122,9 +123,9 @@ impl UintRef { while i < self.0.len() { let l = self.0[i]; let z = l.trailing_zeros(); - count += nonzero_limb_not_encountered.if_true_u32(z); + count += nonzero_limb_not_encountered.select_u32(0, z); nonzero_limb_not_encountered = - nonzero_limb_not_encountered.and(ConstChoice::from_word_nonzero(l.0).not()); + nonzero_limb_not_encountered.and(word::from_word_nonzero(l.0).not()); i += 1; } @@ -159,9 +160,9 @@ impl UintRef { while i < self.0.len() { let l = self.0[i]; let z = l.trailing_ones(); - count += nonmax_limb_not_encountered.if_true_u32(z); + count += nonmax_limb_not_encountered.select_u32(0, z); nonmax_limb_not_encountered = - nonmax_limb_not_encountered.and(ConstChoice::from_word_eq(l.0, Limb::MAX.0)); + nonmax_limb_not_encountered.and(word::from_word_eq(l.0, Limb::MAX.0)); i += 1; } @@ -196,7 +197,7 @@ impl UintRef { while i < self.nlimbs() { let apply = ConstChoice::from_u32_eq(i as u32, limb); self.0[i] = self.0[i].bitand(Limb::select( - Limb(clear.not().as_word_mask()), + Limb(word::choice_to_word_mask(clear.not())), limb_mask, apply, )); diff --git a/src/uint/ref_type/div.rs b/src/uint/ref_type/div.rs index d8c42758b..7a83b4a67 100644 --- a/src/uint/ref_type/div.rs +++ b/src/uint/ref_type/div.rs @@ -4,9 +4,12 @@ //! Further explanation at use super::UintRef; -use crate::const_choice::u32_min; -use crate::div_limb::{Reciprocal, div2by1, div3by2}; -use crate::{ConstChoice, Limb, NonZero}; +use crate::{ + ConstChoice, Limb, NonZero, + ct::u32_min, + div_limb::{Reciprocal, div2by1, div3by2}, + word, +}; impl UintRef { /// Computes `self` / `rhs`, returning the quotient in `self` and the remainder in `rhs`. @@ -280,7 +283,7 @@ impl UintRef { if vartime.and(done).to_bool_vartime() { break; } - quo = done.select_word(quo, 0); + quo = word::select_word(done, quo, 0); // Subtract q*divisor from the dividend let borrow = { @@ -301,7 +304,7 @@ impl UintRef { // If the subtraction borrowed, then decrement quo and add back the divisor // The probability of this being needed is very low, about 2/(Limb::MAX+1) quo = { - let ct_borrow = ConstChoice::from_word_mask(borrow.0); + let ct_borrow = word::from_word_mask(borrow.0); carry = Limb::ZERO; i = (xi + 1).saturating_sub(ysize); while i <= xi { @@ -311,7 +314,7 @@ impl UintRef { ); i += 1; } - ct_borrow.select_word(quo, quo.saturating_sub(1)) + word::select_word(ct_borrow, quo, quo.saturating_sub(1)) }; // Store the quotient within dividend and set x_hi to the current highest word @@ -453,7 +456,7 @@ impl UintRef { if vartime.and(done).to_bool_vartime() { break; } - quo = done.select_word(quo, 0); + quo = word::select_word(done, quo, 0); // Subtract q*divisor from the dividend let borrow = { @@ -474,7 +477,7 @@ impl UintRef { // If the subtraction borrowed, then add back the divisor // The probability of this being needed is very low, about 2/(Limb::MAX+1) { - let ct_borrow = ConstChoice::from_word_mask(borrow.0); + let ct_borrow = word::from_word_mask(borrow.0); carry = Limb::ZERO; i = (xi + 1).saturating_sub(ysize); while i <= xi { @@ -556,14 +559,14 @@ impl UintRef { } let lshift = reciprocal.shift(); let nz = ConstChoice::from_u32_nonzero(lshift); - let rshift = nz.if_true_u32(Limb::BITS - lshift); - let mut hi = (carry.0 << lshift) | nz.if_true_word(self.0[nlimbs - 1].0 >> rshift); + let rshift = nz.select_u32(0, Limb::BITS - lshift); + let mut hi = (carry.0 << lshift) | word::select_word(nz, 0, self.0[nlimbs - 1].0 >> rshift); let mut lo; let mut j = nlimbs; while j > 1 { j -= 1; lo = self.0[j].0 << lshift; - lo |= nz.if_true_word(self.0[j - 1].0 >> rshift); + lo |= word::select_word(nz, 0, self.0[j - 1].0 >> rshift); (_, hi) = div2by1(hi, lo, reciprocal); } (_, hi) = div2by1(hi, self.0[0].0 << lshift, reciprocal); diff --git a/src/uint/ref_type/shl.rs b/src/uint/ref_type/shl.rs index b825227cb..36fe34a87 100644 --- a/src/uint/ref_type/shl.rs +++ b/src/uint/ref_type/shl.rs @@ -1,7 +1,7 @@ //! [`Uint`] bitwise left shift operations. use super::UintRef; -use crate::{ConstChoice, Limb, NonZero}; +use crate::{ConstChoice, Limb, NonZero, word}; impl UintRef { /// Left-shifts by `shift` bits in constant-time. @@ -151,7 +151,7 @@ impl UintRef { carry = new_carry; i += 1; } - ConstChoice::from_word_lsb(carry.0) + word::from_word_lsb(carry.0) } /// Conditionally left-shifts by `shift` bits where `0 < shift < Limb::BITS`, returning diff --git a/src/uint/ref_type/shr.rs b/src/uint/ref_type/shr.rs index e555cde18..c41d2ddaa 100644 --- a/src/uint/ref_type/shr.rs +++ b/src/uint/ref_type/shr.rs @@ -3,6 +3,9 @@ use super::UintRef; use crate::{ConstChoice, Limb, NonZero}; +#[cfg(feature = "alloc")] +use crate::word; + impl UintRef { /// Right-shifts by `shift` bits in constant-time. /// @@ -163,7 +166,7 @@ impl UintRef { self.0[i] = limb.bitor(carry); carry = new_carry; } - ConstChoice::from_word_lsb(carry.0 >> Limb::HI_BIT) + word::from_word_lsb(carry.0 >> Limb::HI_BIT) } /// Conditionally right-shifts by `shift` bits where `0 < shift < Limb::BITS`, returning diff --git a/src/uint/shr.rs b/src/uint/shr.rs index 32355b765..61101c84c 100644 --- a/src/uint/shr.rs +++ b/src/uint/shr.rs @@ -1,6 +1,6 @@ //! [`Uint`] bitwise right shift operations. -use crate::{ConstChoice, ConstCtOption, Limb, NonZero, ShrVartime, Uint, WrappingShr}; +use crate::{ConstChoice, ConstCtOption, Limb, NonZero, ShrVartime, Uint, WrappingShr, word}; use core::ops::{Shr, ShrAssign}; use subtle::CtOption; @@ -195,7 +195,7 @@ impl Uint { carry = new_carry; } - (ret, ConstChoice::from_word_lsb(carry.0 >> Limb::HI_BIT)) + (ret, word::from_word_lsb(carry.0 >> Limb::HI_BIT)) } /// Conditionally right-shifts by `shift` bits where `0 < shift < Limb::BITS`, returning diff --git a/src/uint/sub.rs b/src/uint/sub.rs index b5efa69f4..1f2e890c6 100644 --- a/src/uint/sub.rs +++ b/src/uint/sub.rs @@ -1,7 +1,7 @@ //! [`Uint`] subtraction operations. use super::Uint; -use crate::{Checked, CheckedSub, ConstChoice, Limb, Wrapping, WrappingSub, Zero}; +use crate::{Checked, CheckedSub, Limb, Wrapping, WrappingSub, Zero, word}; use core::ops::{Sub, SubAssign}; use subtle::CtOption; @@ -31,7 +31,7 @@ impl Uint { /// Perform saturating subtraction, returning `ZERO` on underflow. pub const fn saturating_sub(&self, rhs: &Self) -> Self { let (res, underflow) = self.borrowing_sub(rhs, Limb::ZERO); - Self::select(&res, &Self::ZERO, ConstChoice::from_word_mask(underflow.0)) + Self::select(&res, &Self::ZERO, word::from_word_mask(underflow.0)) } /// Perform wrapping subtraction, discarding underflow and wrapping around diff --git a/src/word.rs b/src/word.rs new file mode 100644 index 000000000..e879efa95 --- /dev/null +++ b/src/word.rs @@ -0,0 +1,175 @@ +//! `Word` represents the core integer type we use as the core of `Limb`, and is typically the same +//! size as a pointer on a particular CPU. + +use crate::ConstChoice; + +#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] +compile_error!("this crate builds on 32-bit and 64-bit platforms only"); + +// +// 32-bit definitions +// + +/// Inner integer type that the [`Limb`] newtype wraps. +#[cfg(target_pointer_width = "32")] +pub type Word = u32; + +/// Unsigned wide integer type: double the width of [`Word`]. +#[cfg(target_pointer_width = "32")] +pub type WideWord = u64; + +// +// 64-bit definitions +// + +/// Unsigned integer type that the [`Limb`][`crate::Limb`] newtype wraps. +#[cfg(target_pointer_width = "64")] +pub type Word = u64; + +/// Wide integer type: double the width of [`Word`]. +#[cfg(target_pointer_width = "64")] +pub type WideWord = u128; + +/// Returns the truthy value if `x == y`, and the falsy value otherwise. +#[inline] +pub(crate) const fn from_word_eq(x: Word, y: Word) -> ConstChoice { + from_word_nonzero(x ^ y).not() +} + +/// Returns the truthy value if `x > y`, and the falsy value otherwise. +#[inline] +pub(crate) const fn from_word_gt(x: Word, y: Word) -> ConstChoice { + from_word_lt(y, x) +} + +/// Returns the truthy value if `x <= y` and the falsy value otherwise. +#[inline] +pub(crate) const fn from_word_le(x: Word, y: Word) -> ConstChoice { + // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) + let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (Word::BITS - 1); + from_word_lsb(bit) +} + +/// Returns the truthy value if `value == 1`, and the falsy value if `value == 0`. +/// Panics for other values. +#[inline] +pub(crate) const fn from_word_lsb(value: Word) -> ConstChoice { + ConstChoice::new((value & 1) as u8) +} + +/// Returns the truthy value if `x < y`, and the falsy value otherwise. +#[inline] +pub(crate) const fn from_word_lt(x: Word, y: Word) -> ConstChoice { + // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) + let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (Word::BITS - 1); + from_word_lsb(bit) +} + +/// Returns the truthy value if `value == Word::MAX`, and the falsy value if `value == 0`. +/// Panics for other values. +#[inline] +pub(crate) const fn from_word_mask(value: Word) -> ConstChoice { + from_word_eq(value, Word::MAX) +} + +/// Returns the truthy value if the most significant bit of `value` is `1`, +/// and the falsy value if it equals `0`. +#[inline] +pub(crate) const fn from_word_msb(value: Word) -> ConstChoice { + from_word_lsb(value >> (Word::BITS - 1)) +} + +/// Returns the truthy value if `value != 0`, and the falsy value otherwise. +#[inline] +pub(crate) const fn from_word_nonzero(value: Word) -> ConstChoice { + from_word_lsb((value | value.wrapping_neg()) >> (Word::BITS - 1)) +} + +/// Returns the truthy value if `x <= y` and the falsy value otherwise. +#[inline] +pub(crate) const fn from_wide_word_le(x: WideWord, y: WideWord) -> ConstChoice { + // See "Hacker's Delight" 2nd ed, section 2-12 (Comparison predicates) + let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (WideWord::BITS - 1); + from_wide_word_lsb(bit) +} + +/// Returns the truthy value if `value == 1`, and the falsy value if `value == 0`. +/// Panics for other values. +#[inline] +pub(crate) const fn from_wide_word_lsb(value: WideWord) -> ConstChoice { + ConstChoice::new((value & 1) as u8) +} + +/// Return `b` if `self` is truthy, otherwise return `a`. +#[inline] +pub(crate) const fn select_word(choice: ConstChoice, a: Word, b: Word) -> Word { + a ^ (choice_to_word_mask(choice) & (a ^ b)) +} + +/// Return `b` if `self` is truthy, otherwise return `a`. +#[inline] +pub(crate) const fn select_wide_word(choice: ConstChoice, a: WideWord, b: WideWord) -> WideWord { + a ^ (choice_to_wide_word_mask(choice) & (a ^ b)) +} + +/// Create a `Word`-sized bitmask. +/// +/// # Returns +/// - `0` for `ConstChoice::FALSE` +/// - `Word::MAX` for `ConstChoice::TRUE` +#[inline] +pub(crate) const fn choice_to_word_mask(choice: ConstChoice) -> Word { + (choice.to_u8_vartime() as Word).wrapping_neg() +} + +/// Create a `WideWord`-sized bitmask. +/// +/// # Returns +/// - `0` for `ConstChoice::FALSE` +/// - `Word::MAX` for `ConstChoice::TRUE` +#[inline] +pub(crate) const fn choice_to_wide_word_mask(choice: ConstChoice) -> WideWord { + (choice.to_u8_vartime() as WideWord).wrapping_neg() +} + +#[cfg(test)] +mod tests { + use crate::{ConstChoice, WideWord, Word}; + + #[test] + fn from_word_lt() { + assert_eq!(super::from_word_lt(4, 5), ConstChoice::TRUE); + assert_eq!(super::from_word_lt(5, 5), ConstChoice::FALSE); + assert_eq!(super::from_word_lt(6, 5), ConstChoice::FALSE); + } + + #[test] + fn from_word_gt() { + assert_eq!(super::from_word_gt(4, 5), ConstChoice::FALSE); + assert_eq!(super::from_word_gt(5, 5), ConstChoice::FALSE); + assert_eq!(super::from_word_gt(6, 5), ConstChoice::TRUE); + } + + #[test] + fn from_wide_word_le() { + assert_eq!(super::from_wide_word_le(4, 5), ConstChoice::TRUE); + assert_eq!(super::from_wide_word_le(5, 5), ConstChoice::TRUE); + assert_eq!(super::from_wide_word_le(6, 5), ConstChoice::FALSE); + } + + #[test] + fn select_word() { + let a: Word = 1; + let b: Word = 2; + assert_eq!(super::select_word(ConstChoice::TRUE, a, b), b); + assert_eq!(super::select_word(ConstChoice::FALSE, a, b), a); + } + + #[test] + fn select_wide_word() { + let a: WideWord = (1 << Word::BITS) + 1; + let b: WideWord = (3 << Word::BITS) + 4; + assert_eq!(super::select_wide_word(ConstChoice::TRUE, a, b), b); + assert_eq!(super::select_wide_word(ConstChoice::FALSE, a, b), a); + } +}