diff --git a/src/uint/modular/constant_mod.rs b/src/uint/modular/constant_mod.rs index 92b6ce761..b775af45b 100644 --- a/src/uint/modular/constant_mod.rs +++ b/src/uint/modular/constant_mod.rs @@ -1,6 +1,6 @@ use core::{fmt::Debug, marker::PhantomData}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::{Limb, Uint, Zero}; @@ -87,8 +87,8 @@ impl, const LIMBS: usize> Residue { phantom: PhantomData, }; - /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. - pub const fn new(integer: &Uint) -> Self { + // Internal helper function to generate a residue; this lets us wrap the constructors more cleanly + const fn generate_residue(integer: &Uint) -> Self { let product = integer.mul_wide(&MOD::R2); let montgomery_form = montgomery_reduction::(&product, &MOD::MODULUS, MOD::MOD_NEG_INV); @@ -99,6 +99,29 @@ impl, const LIMBS: usize> Residue { } } + /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. + /// If the modulus represented by `MOD` is not odd, this function will panic; use [`new_checked`][`Residue::new_checked`] if you want to be able to detect an invalid modulus. + pub const fn new(integer: &Uint) -> Self { + // A valid modulus must be odd + if MOD::MODULUS.ct_is_odd().to_u8() == 0 { + panic!("modulus must be odd"); + } + + Self::generate_residue(integer) + } + + /// Instantiates a new `Residue` that represents this `integer` mod `MOD` if the modulus is odd. + /// Returns a `CtOption` that is `None` if the provided modulus is not odd; this is a safer version of [`new`][`Residue::new`], which can panic. + // TODO: remove this method when we can use `generic_const_exprs.` to ensure the modulus is + // always valid. + pub fn new_checked(integer: &Uint) -> CtOption { + // A valid modulus must be odd, which we can check in constant time + CtOption::new( + Self::generate_residue(integer), + MOD::MODULUS.ct_is_odd().into(), + ) + } + /// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced. pub const fn retrieve(&self) -> Uint { montgomery_reduction::( diff --git a/src/uint/modular/constant_mod/macros.rs b/src/uint/modular/constant_mod/macros.rs index 1583ca529..70dc4379d 100644 --- a/src/uint/modular/constant_mod/macros.rs +++ b/src/uint/modular/constant_mod/macros.rs @@ -2,6 +2,7 @@ #[macro_export] /// Implements a modulus with the given name, type, and value, in that specific order. Please `use crypto_bigint::traits::Encoding` to make this work. /// For example, `impl_modulus!(MyModulus, U256, "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");` implements a 256-bit modulus named `MyModulus`. +/// The modulus _must_ be odd, or this will panic. macro_rules! impl_modulus { ($name:ident, $uint_type:ty, $value:expr) => { #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -14,8 +15,16 @@ macro_rules! impl_modulus { $crate::Concat>, { const LIMBS: usize = { $crate::nlimbs!(<$uint_type>::BITS) }; - const MODULUS: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = - <$uint_type>::from_be_hex($value); + const MODULUS: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = { + let res = <$uint_type>::from_be_hex($value); + + // Check that the modulus is odd + if res.as_limbs()[0].0 & 1 == 0 { + panic!("modulus must be odd"); + } + + res + }; const R: $crate::Uint<{ $crate::nlimbs!(<$uint_type>::BITS) }> = $crate::Uint::MAX .const_rem(&Self::MODULUS) .0 @@ -43,6 +52,7 @@ macro_rules! impl_modulus { #[macro_export] /// Creates a `Residue` with the given value for a specific modulus. /// For example, `residue!(U256::from(105u64), MyModulus);` creates a `Residue` for 105 mod `MyModulus`. +/// The modulus _must_ be odd, or this will panic. macro_rules! const_residue { ($variable:ident, $modulus:ident) => { $crate::modular::constant_mod::Residue::<$modulus, { $modulus::LIMBS }>::new(&$variable)