use crate::inv::basic_mod_inv;
use crate::parity::Parity;
use crate::wide_mul::WideMul;
use const_num_traits::Odd;
use subtle::Choice;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum NPrimeMethod {
TrialSearch,
ExtendedEuclidean,
HenselsLifting,
#[default]
Newton,
}
fn compute_n_prime_trial_search<T>(modulus: T, r: T) -> Option<T>
where
T: Copy
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Mul<Output = T>
+ core::ops::Rem<Output = T>,
{
let target = r - T::one();
let mut n_prime = T::one();
loop {
if (modulus * n_prime) % r == target {
return Some(n_prime);
}
n_prime = n_prime + T::one();
if n_prime >= r {
return None; }
}
}
fn compute_n_prime_extended_euclidean<T>(modulus: T, r: T) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Mul<Output = T>
+ core::ops::Div<Output = T>,
{
if let Some(modulus_inv) = basic_mod_inv(modulus, r) {
if modulus_inv == T::zero() {
Some(r - T::one()) } else {
Some(r - modulus_inv)
}
} else {
None }
}
fn compute_n_prime_hensels_lifting<T>(modulus: T, r: T, r_bits: usize) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Mul<Output = T>
+ core::ops::Rem<Output = T>
+ core::ops::Shl<usize, Output = T>,
{
let mut n_prime = T::one();
for k in 2..=r_bits {
let target_mod = T::one() << k; let check_val = (modulus * n_prime + T::one()) % target_mod;
if check_val != T::zero() {
let prev_power = T::one() << (k - 1);
if check_val == prev_power {
n_prime = n_prime + prev_power;
}
}
}
let final_check = (modulus * n_prime) % r;
let target = r - T::one();
if final_check != target {
None } else {
Some(n_prime)
}
}
pub fn basic_compute_montgomery_params_with_method<T>(
modulus: T,
method: NPrimeMethod,
) -> Option<(T, T, T, usize)>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Div<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Mul<Output = T>
+ core::ops::Rem<Output = T>
+ core::ops::Add<Output = T>,
{
let mut r = T::one();
let mut r_bits = 0usize;
while r <= modulus {
r = r << 1; r_bits += 1;
}
let r_inv = basic_mod_inv(r, modulus)?;
let n_prime = match method {
NPrimeMethod::TrialSearch => compute_n_prime_trial_search(modulus, r)?,
NPrimeMethod::ExtendedEuclidean => compute_n_prime_extended_euclidean(modulus, r)?,
NPrimeMethod::HenselsLifting => compute_n_prime_hensels_lifting(modulus, r, r_bits)?,
NPrimeMethod::Newton => {
compute_n_prime_extended_euclidean(modulus, r)?
}
};
Some((r, r_inv, n_prime, r_bits))
}
pub fn basic_compute_montgomery_params<T>(modulus: T) -> Option<(T, T, T, usize)>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Div<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Mul<Output = T>
+ core::ops::Rem<Output = T>
+ core::ops::Add<Output = T>,
{
basic_compute_montgomery_params_with_method(modulus, NPrimeMethod::default())
}
pub fn basic_to_montgomery<T>(a: T, modulus: T, r: T) -> T
where
T: core::cmp::PartialOrd
+ Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::ops::wrapping::WrappingAdd<Output = T>
+ const_num_traits::ops::wrapping::WrappingSub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::Rem<Output = T>
+ crate::parity::Parity
+ crate::NonCt,
{
crate::mul::basic_mod_mul(a, r, modulus)
}
pub fn basic_to_montgomery_pr<T>(a: T, modulus: T, r: T) -> T
where
T: core::cmp::PartialOrd
+ Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::ops::wrapping::WrappingAdd<Output = T>
+ const_num_traits::ops::wrapping::WrappingSub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ crate::parity::Parity
+ crate::NonCt,
{
crate::mul::basic_mod_mul_pr(a, r, modulus)
}
pub fn basic_from_montgomery<T>(a_mont: T, modulus: T, n_prime: T, r_bits: usize) -> T
where
T: Copy
+ const_num_traits::One
+ PartialOrd
+ core::ops::Mul<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::Shl<usize, Output = T>
+ core::ops::BitAnd<Output = T>,
{
if r_bits == 0 {
return if a_mont >= modulus {
a_mont - modulus
} else {
a_mont
};
}
let mask = (T::one() << r_bits) - T::one();
let m = ((a_mont & mask) * n_prime) & mask;
let t = (a_mont + m * modulus) >> r_bits;
if t >= modulus { t - modulus } else { t }
}
pub fn basic_montgomery_mul<T>(a_mont: T, b_mont: T, modulus: T, n_prime: T, r_bits: usize) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialOrd
+ core::ops::Mul<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Rem<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::Shl<usize, Output = T>
+ core::ops::BitAnd<Output = T>
+ const_num_traits::ops::wrapping::WrappingAdd<Output = T>
+ const_num_traits::ops::wrapping::WrappingSub<Output = T>
+ crate::parity::Parity
+ crate::NonCt,
{
let product = crate::mul::basic_mod_mul(a_mont, b_mont, modulus);
basic_from_montgomery(product, modulus, n_prime, r_bits)
}
pub fn basic_montgomery_mul_pr<T>(a_mont: T, b_mont: T, modulus: T, n_prime: T, r_bits: usize) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialOrd
+ core::ops::Mul<Output = T>
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::Shl<usize, Output = T>
+ core::ops::BitAnd<Output = T>
+ const_num_traits::ops::wrapping::WrappingAdd<Output = T>
+ const_num_traits::ops::wrapping::WrappingSub<Output = T>
+ crate::parity::Parity
+ crate::NonCt,
{
let product = crate::mul::basic_mod_mul_pr(a_mont, b_mont, modulus);
basic_from_montgomery(product, modulus, n_prime, r_bits)
}
pub const fn type_bit_width<T>() -> usize {
core::mem::size_of::<T>() * 8
}
fn mod_double<T>(val: T, modulus: T) -> T
where
T: Copy
+ PartialOrd
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
let (doubled, overflow) = val.overflowing_add(val);
if overflow || doubled >= modulus {
doubled.wrapping_sub(modulus)
} else {
doubled
}
}
fn mod_double_ct<T>(val: T, modulus: T) -> T
where
T: Copy
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
let doubled = val.wrapping_add(val);
let overflow = doubled.ct_lt(&val);
let sub_result = doubled.wrapping_sub(modulus);
let doubled_ge_modulus = !doubled.ct_lt(&modulus);
let needs_sub = overflow | doubled_ge_modulus;
T::conditional_select(&doubled, &sub_result, needs_sub)
}
pub fn compute_n_prime_newton<T>(modulus: T, w: usize) -> T
where
T: Copy
+ const_num_traits::One
+ const_num_traits::Zero
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::WrappingAdd<Output = T>,
{
let two = T::one().wrapping_add(T::one());
let mut x = T::one(); let mut precision = 1usize;
while precision < w {
x = x.wrapping_mul(two.wrapping_sub(modulus.wrapping_mul(x)));
precision *= 2;
}
T::zero().wrapping_sub(x)
}
fn mod_exp2<T>(val: T, modulus: T, w: usize) -> T
where
T: Copy
+ PartialEq
+ PartialOrd
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
if modulus == T::one() {
return T::zero();
}
let mut result = val;
for _ in 0..w {
result = mod_double(result, modulus);
}
result
}
fn mod_exp2_ct<T>(val: T, modulus: T, w: usize) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeEq
+ subtle::ConstantTimeLess,
{
let mut result = val;
for _ in 0..w {
result = mod_double_ct(result, modulus);
}
let m_is_one = modulus.ct_eq(&T::one());
T::conditional_select(&result, &T::zero(), m_is_one)
}
pub fn compute_r_mod_n<T>(modulus: T, w: usize) -> T
where
T: Copy
+ PartialEq
+ PartialOrd
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
mod_exp2(T::one(), modulus, w)
}
pub fn compute_r_mod_n_ct<T>(modulus: T, w: usize) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
mod_exp2_ct(T::one(), modulus, w)
}
pub fn compute_r2_mod_n<T>(r_mod_n: T, modulus: T, w: usize) -> T
where
T: Copy
+ PartialEq
+ PartialOrd
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
mod_exp2(r_mod_n, modulus, w)
}
pub fn compute_r2_mod_n_ct<T>(r_mod_n: T, modulus: T, w: usize) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
mod_exp2_ct(r_mod_n, modulus, w)
}
fn accumulate_high_half_carry<T>(result: T, carry1: bool, carry2: bool) -> (T, bool)
where
T: const_num_traits::One + const_num_traits::ops::overflowing::OverflowingAdd<Output = T>,
{
if carry1 {
let (r2, carry3) = result.overflowing_add(T::one());
(r2, carry2 || carry3)
} else {
(result, carry2)
}
}
fn accumulate_high_half_carry_ct<T>(result: T, carry1: Choice, carry2: Choice) -> (T, Choice)
where
T: const_num_traits::One
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::CtIsZero
+ subtle::ConditionallySelectable,
{
let r2 = result.wrapping_add(T::one());
let carry3 = r2.ct_is_zero();
let chosen = T::conditional_select(&result, &r2, carry1);
(chosen, carry2 | (carry1 & carry3))
}
pub fn wide_redc<T>(t_lo: T, t_hi: T, modulus: T, n_prime: T) -> T
where
T: Copy
+ const_num_traits::One
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
let m = t_lo.wrapping_mul(n_prime);
let (m_lo, m_hi) = m.wide_mul(&modulus);
let (_discard_lo, carry1) = t_lo.overflowing_add(m_lo);
let (result, carry2) = t_hi.overflowing_add(m_hi);
let (result, extra_bit) = accumulate_high_half_carry(result, carry1, carry2);
if extra_bit || result >= modulus {
result.wrapping_sub(modulus)
} else {
result
}
}
pub fn strict_wide_redc<T>(t_lo: &T, t_hi: &T, modulus: &T, n_prime: &T) -> T
where
T: Copy
+ const_num_traits::One
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
let m = (*t_lo).wrapping_mul(*n_prime);
let (m_lo, m_hi) = m.wide_mul(modulus);
let (_discard_lo, carry1) = (*t_lo).overflowing_add(m_lo);
let (result, carry2) = (*t_hi).overflowing_add(m_hi);
let (result, extra_bit) = accumulate_high_half_carry(result, carry1, carry2);
if extra_bit || &result >= modulus {
result.wrapping_sub(*modulus)
} else {
result
}
}
pub fn wide_redc_ct<T>(t_lo: T, t_hi: T, modulus: T, n_prime: T) -> T
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::CtIsZero
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
let m = t_lo.wrapping_mul(n_prime);
let (m_lo, m_hi) = m.wide_mul(&modulus);
let sum_lo = t_lo.wrapping_add(m_lo);
let carry1 = sum_lo.ct_lt(&t_lo);
let result = t_hi.wrapping_add(m_hi);
let carry2 = result.ct_lt(&t_hi);
let (result, extra_bit) = accumulate_high_half_carry_ct(result, carry1, carry2);
let sub_result = result.wrapping_sub(modulus);
let result_lt_modulus = result.ct_lt(&modulus);
let needs_sub = extra_bit | !result_lt_modulus;
T::conditional_select(&result, &sub_result, needs_sub)
}
pub fn strict_wide_redc_ct<T>(t_lo: &T, t_hi: &T, modulus: &T, n_prime: &T) -> T
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::CtIsZero
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
let m = (*t_lo).wrapping_mul(*n_prime);
let (m_lo, m_hi) = m.wide_mul(modulus);
let sum_lo = (*t_lo).wrapping_add(m_lo);
let carry1 = sum_lo.ct_lt(t_lo);
let result = (*t_hi).wrapping_add(m_hi);
let carry2 = result.ct_lt(t_hi);
let (result, extra_bit) = accumulate_high_half_carry_ct(result, carry1, carry2);
let sub_result = result.wrapping_sub(*modulus);
let result_lt_modulus = result.ct_lt(modulus);
let needs_sub = extra_bit | !result_lt_modulus;
T::conditional_select(&result, &sub_result, needs_sub)
}
pub fn wide_montgomery_mul<T>(a_mont: T, b_mont: T, modulus: T, n_prime: T) -> T
where
T: Copy
+ const_num_traits::One
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
let (lo, hi) = a_mont.wide_mul(&b_mont);
wide_redc(lo, hi, modulus, n_prime)
}
pub fn wide_montgomery_mul_ct<T>(a_mont: T, b_mont: T, modulus: T, n_prime: T) -> T
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::CtIsZero
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
let (lo, hi) = a_mont.wide_mul(&b_mont);
wide_redc_ct(lo, hi, modulus, n_prime)
}
pub fn strict_wide_montgomery_mul<T>(a_mont: &T, b_mont: &T, modulus: &T, n_prime: &T) -> T
where
T: Copy
+ const_num_traits::One
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
let (lo, hi) = a_mont.wide_mul(b_mont);
strict_wide_redc(&lo, &hi, modulus, n_prime)
}
pub fn strict_wide_montgomery_mul_ct<T>(a_mont: &T, b_mont: &T, modulus: &T, n_prime: &T) -> T
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::CtIsZero
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
let (lo, hi) = a_mont.wide_mul(b_mont);
strict_wide_redc_ct(&lo, &hi, modulus, n_prime)
}
pub fn wide_montgomery_mul_acc<T>(acc_lo: T, acc_hi: T, a: T, b: T) -> (T, T)
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>,
{
let (m_lo, m_hi) = a.wide_mul(&b);
let (new_lo, carry1) = acc_lo.overflowing_add(m_lo);
let (sum_hi, _) = acc_hi.overflowing_add(m_hi);
let new_hi = if carry1 {
let (r, _) = sum_hi.overflowing_add(T::one());
r
} else {
sum_hi
};
(new_lo, new_hi)
}
pub fn wide_montgomery_mul_acc_ct<T>(acc_lo: T, acc_hi: T, a: T, b: T) -> (T, T)
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::WrappingAdd<Output = T>
+ subtle::ConstantTimeLess
+ subtle::ConditionallySelectable,
{
let (m_lo, m_hi) = a.wide_mul(&b);
let new_lo = acc_lo.wrapping_add(m_lo);
let carry1 = new_lo.ct_lt(&acc_lo);
let sum_hi = acc_hi.wrapping_add(m_hi);
let r = sum_hi.wrapping_add(T::one());
let new_hi = T::conditional_select(&sum_hi, &r, carry1);
(new_lo, new_hi)
}
pub fn strict_wide_montgomery_mul_acc<T>(acc_lo: &T, acc_hi: &T, a: &T, b: &T) -> (T, T)
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>,
{
let (m_lo, m_hi) = a.wide_mul(b);
let (new_lo, carry1) = (*acc_lo).overflowing_add(m_lo);
let (sum_hi, _) = (*acc_hi).overflowing_add(m_hi);
let new_hi = if carry1 {
let (r, _) = sum_hi.overflowing_add(T::one());
r
} else {
sum_hi
};
(new_lo, new_hi)
}
pub fn strict_wide_montgomery_mul_acc_ct<T>(acc_lo: &T, acc_hi: &T, a: &T, b: &T) -> (T, T)
where
T: Copy
+ const_num_traits::One
+ WideMul
+ const_num_traits::WrappingAdd<Output = T>
+ subtle::ConstantTimeLess
+ subtle::ConditionallySelectable,
{
let (m_lo, m_hi) = a.wide_mul(b);
let new_lo = (*acc_lo).wrapping_add(m_lo);
let carry1 = new_lo.ct_lt(acc_lo);
let sum_hi = (*acc_hi).wrapping_add(m_hi);
let r = sum_hi.wrapping_add(T::one());
let new_hi = T::conditional_select(&sum_hi, &r, carry1);
(new_lo, new_hi)
}
fn reduce_mod<T>(val: T, modulus: T) -> T
where
T: Copy + core::ops::Rem<Output = T>,
{
val % modulus
}
pub fn basic_montgomery_mod_mul_odd<T>(a: T, b: T, modulus: Odd<T>) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ core::ops::Rem<Output = T>,
{
let m = modulus.get();
basic_montgomery_mod_mul_pr_odd(reduce_mod(a, m), reduce_mod(b, m), modulus)
}
pub fn basic_montgomery_mod_mul<T>(a: T, b: T, modulus: T) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ Parity
+ core::ops::Rem<Output = T>,
{
Odd::new(modulus).map(|m| basic_montgomery_mod_mul_odd(a, b, m))
}
pub fn basic_montgomery_mod_mul_pr_odd<T>(a: T, b: T, modulus: Odd<T>) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>,
{
let modulus = modulus.get();
let w = type_bit_width::<T>();
let n_prime = compute_n_prime_newton(modulus, w);
let r_mod_n = compute_r_mod_n(modulus, w);
let r2_mod_n = compute_r2_mod_n(r_mod_n, modulus, w);
let (lo, hi) = a.wide_mul(&r2_mod_n);
let a_m = wide_redc(lo, hi, modulus, n_prime);
let (lo, hi) = b.wide_mul(&r2_mod_n);
let b_m = wide_redc(lo, hi, modulus, n_prime);
let r_m = wide_montgomery_mul(a_m, b_m, modulus, n_prime);
wide_redc(r_m, T::zero(), modulus, n_prime)
}
pub fn basic_montgomery_mod_mul_pr<T>(a: T, b: T, modulus: T) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ Parity,
{
Odd::new(modulus).map(|m| basic_montgomery_mod_mul_pr_odd(a, b, m))
}
pub fn basic_montgomery_mod_exp_odd<T>(base: T, exponent: T, modulus: Odd<T>) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ Parity
+ core::ops::Rem<Output = T>
+ core::ops::ShrAssign<usize>,
{
let m = modulus.get();
basic_montgomery_mod_exp_pr_odd(reduce_mod(base, m), exponent, modulus)
}
pub fn basic_montgomery_mod_exp<T>(base: T, exponent: T, modulus: T) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ Parity
+ core::ops::Rem<Output = T>
+ core::ops::ShrAssign<usize>,
{
Odd::new(modulus).map(|m| basic_montgomery_mod_exp_odd(base, exponent, m))
}
pub fn basic_montgomery_mod_exp_pr_odd<T>(base: T, exponent: T, modulus: Odd<T>) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ Parity
+ core::ops::ShrAssign<usize>,
{
let modulus = modulus.get();
let w = type_bit_width::<T>();
let n_prime = compute_n_prime_newton(modulus, w);
let r_mod_n = compute_r_mod_n(modulus, w);
let r2_mod_n = compute_r2_mod_n(r_mod_n, modulus, w);
let one_mont = r_mod_n;
let (lo, hi) = base.wide_mul(&r2_mod_n);
let mut base_mont = wide_redc(lo, hi, modulus, n_prime);
let mut result = one_mont;
let mut exp = exponent;
while exp > T::zero() {
if exp.is_odd() {
result = wide_montgomery_mul(result, base_mont, modulus, n_prime);
}
exp >>= 1;
if exp > T::zero() {
base_mont = wide_montgomery_mul(base_mont, base_mont, modulus, n_prime);
}
}
wide_redc(result, T::zero(), modulus, n_prime)
}
pub fn basic_montgomery_mod_exp_pr<T>(base: T, exponent: T, modulus: T) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ Parity
+ core::ops::ShrAssign<usize>,
{
Odd::new(modulus).map(|m| basic_montgomery_mod_exp_pr_odd(base, exponent, m))
}
pub fn basic_montgomery_mod_exp_pr_odd_ct<T>(base: T, exponent: T, modulus: Odd<T>) -> T
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::CtIsZero
+ core::ops::Shr<usize, Output = T>
+ core::ops::BitAnd<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
let modulus = modulus.get();
let w = type_bit_width::<T>();
let n_prime = compute_n_prime_newton(modulus, w);
let r_mod_n = compute_r_mod_n(modulus, w);
let r2_mod_n = compute_r2_mod_n(r_mod_n, modulus, w);
let one_mont = r_mod_n;
let base_mont = wide_montgomery_mul_ct(base, r2_mod_n, modulus, n_prime);
let mut result = one_mont;
let one = T::one();
for i in (0..w).rev() {
result = wide_montgomery_mul_ct(result, result, modulus, n_prime);
let multiplied = wide_montgomery_mul_ct(result, base_mont, modulus, n_prime);
let bit_t = (exponent >> i) & one;
let choice = !bit_t.ct_is_zero();
result = T::conditional_select(&result, &multiplied, choice);
}
wide_redc_ct(result, T::zero(), modulus, n_prime)
}
pub fn basic_montgomery_mod_exp_pr_ct<T>(base: T, exponent: T, modulus: T) -> Option<T>
where
T: Copy
+ const_num_traits::Zero
+ const_num_traits::One
+ PartialEq
+ PartialOrd
+ WideMul
+ const_num_traits::WrappingMul<Output = T>
+ const_num_traits::WrappingAdd<Output = T>
+ const_num_traits::ops::overflowing::OverflowingAdd<Output = T>
+ const_num_traits::WrappingSub<Output = T>
+ const_num_traits::CtIsZero
+ Parity
+ core::ops::Shr<usize, Output = T>
+ core::ops::BitAnd<Output = T>
+ subtle::ConditionallySelectable
+ subtle::ConstantTimeLess,
{
Odd::new(modulus).map(|m| basic_montgomery_mod_exp_pr_odd_ct(base, exponent, m))
}
#[cfg(test)]
mod tests {
use super::*;
use const_num_traits::Ct;
use fixed_bigint::FixedUInt;
#[test]
fn test_basic_compute_n_prime_trial_search_failure() {
let modulus = 4u32;
let r = 8u32;
let result = compute_n_prime_trial_search(modulus, r);
assert!(
result.is_none(),
"Should return None for invalid modulus-R pair"
);
}
#[test]
fn test_basic_compute_montgomery_params_failure() {
let even_modulus = 4u32;
let result = basic_compute_montgomery_params(even_modulus);
assert!(result.is_none(), "Should return None for even modulus");
}
#[test]
fn test_basic_compute_montgomery_params_failure_with_method() {
let invalid_modulus = 4u32;
let trial_result =
basic_compute_montgomery_params_with_method(invalid_modulus, NPrimeMethod::TrialSearch);
assert!(
trial_result.is_none(),
"Trial search should fail with even modulus"
);
let euclidean_result = basic_compute_montgomery_params_with_method(
invalid_modulus,
NPrimeMethod::ExtendedEuclidean,
);
assert!(
euclidean_result.is_none(),
"Extended Euclidean should fail with even modulus"
);
let hensels_result = basic_compute_montgomery_params_with_method(
invalid_modulus,
NPrimeMethod::HenselsLifting,
);
assert!(
hensels_result.is_none(),
"Hensel's lifting should fail with even modulus"
);
}
#[test]
fn test_basic_montgomery_mod_mul_parameter_failure() {
let invalid_modulus = 4u32;
let a = 2u32;
let b = 3u32;
let result = basic_montgomery_mod_mul(a, b, invalid_modulus);
assert!(
result.is_none(),
"Montgomery mod_mul should return None for invalid modulus"
);
}
#[test]
fn odd_surface_matches_option_surface_mul() {
let m: u32 = 97;
let modulus_odd = Odd::new(m).expect("97 is odd");
for a in [0u32, 1, 2, 42, 50, 96] {
for b in [0u32, 1, 13, 49, 95] {
let via_odd_pr = basic_montgomery_mod_mul_pr_odd(a % m, b % m, modulus_odd);
let via_opt_pr = basic_montgomery_mod_mul_pr(a % m, b % m, m).unwrap();
assert_eq!(via_odd_pr, via_opt_pr, "_pr divergence at ({a}, {b})");
let via_odd = basic_montgomery_mod_mul_odd(a, b, modulus_odd);
let via_opt = basic_montgomery_mod_mul(a, b, m).unwrap();
assert_eq!(via_odd, via_opt, "mul divergence at ({a}, {b})");
assert_eq!(via_odd, (a * b) % m);
}
}
}
#[test]
fn odd_surface_matches_option_surface_exp() {
let m: u32 = 97;
let modulus_odd = Odd::new(m).expect("97 is odd");
for base in [0u32, 1, 2, 5, 96] {
for exp in [0u32, 1, 5, 96] {
let via_odd_pr = basic_montgomery_mod_exp_pr_odd(base % m, exp, modulus_odd);
let via_opt_pr = basic_montgomery_mod_exp_pr(base % m, exp, m).unwrap();
assert_eq!(via_odd_pr, via_opt_pr, "_pr divergence at ({base}, {exp})");
let via_odd = basic_montgomery_mod_exp_odd(base, exp, modulus_odd);
let via_opt = basic_montgomery_mod_exp(base, exp, m).unwrap();
assert_eq!(via_odd, via_opt, "exp divergence at ({base}, {exp})");
let via_ct = basic_montgomery_mod_exp_pr_odd_ct(base % m, exp, modulus_odd);
assert_eq!(via_ct, via_odd, "ct/nct divergence at ({base}, {exp})");
}
}
}
#[test]
fn test_basic_montgomery_mod_exp_parameter_failure() {
let invalid_modulus = 4u32;
let base = 2u32;
let exponent = 3u32;
let result = basic_montgomery_mod_exp(base, exponent, invalid_modulus);
assert!(
result.is_none(),
"Montgomery mod_exp should return None for invalid modulus"
);
}
#[test]
fn test_basic_montgomery_reduction_final_subtraction() {
let modulus = 15u32;
let (r, _r_inv, n_prime, r_bits) = basic_compute_montgomery_params(modulus).unwrap();
let high_value = 14u32; let mont_high = basic_to_montgomery(high_value, modulus, r);
let result = basic_from_montgomery(mont_high, modulus, n_prime, r_bits);
assert_eq!(result, high_value);
let mont_max = basic_to_montgomery(modulus - 1, modulus, r);
let result_max = basic_from_montgomery(mont_max, modulus, n_prime, r_bits);
assert_eq!(result_max, modulus - 1);
}
#[test]
fn test_basic_multiplication_edge_cases() {
let modulus = 21u32; let (r, _r_inv, n_prime, r_bits) = basic_compute_montgomery_params(modulus).unwrap();
let a = 20u32; let b = 19u32;
let a_mont = basic_to_montgomery(a, modulus, r);
let b_mont = basic_to_montgomery(b, modulus, r);
let result_mont = basic_montgomery_mul(a_mont, b_mont, modulus, n_prime, r_bits);
let result = basic_from_montgomery(result_mont, modulus, n_prime, r_bits);
let expected = (a * b) % modulus;
assert_eq!(result, expected);
}
#[test]
fn test_basic_n_prime_computation_edge_cases() {
let test_moduli = [9u32, 15u32, 21u32, 25u32, 27u32];
for &modulus in &test_moduli {
let trial_result =
basic_compute_montgomery_params_with_method(modulus, NPrimeMethod::TrialSearch);
let euclidean_result = basic_compute_montgomery_params_with_method(
modulus,
NPrimeMethod::ExtendedEuclidean,
);
let hensels_result =
basic_compute_montgomery_params_with_method(modulus, NPrimeMethod::HenselsLifting);
assert!(
trial_result.is_some(),
"Trial search failed for modulus {}",
modulus
);
assert!(
euclidean_result.is_some(),
"Extended Euclidean failed for modulus {}",
modulus
);
assert!(
hensels_result.is_some(),
"Hensel's lifting failed for modulus {}",
modulus
);
assert_eq!(
trial_result, euclidean_result,
"Methods disagree for modulus {}",
modulus
);
assert_eq!(
trial_result, hensels_result,
"Methods disagree for modulus {}",
modulus
);
}
}
#[test]
fn test_basic_exponentiation_loop_branches() {
let modulus = 17u32;
let base = 16u32; let exponent = 15u32;
let result = basic_montgomery_mod_exp(base, exponent, modulus).unwrap();
let expected = crate::exp::basic_mod_exp(base, exponent, modulus);
assert_eq!(result, expected);
let exp_pow2 = 16u32; let result_pow2 = basic_montgomery_mod_exp(3u32, exp_pow2, modulus).unwrap();
let expected_pow2 = crate::exp::basic_mod_exp(3u32, exp_pow2, modulus);
assert_eq!(result_pow2, expected_pow2);
let exp_odd = 255u32; let result_odd = basic_montgomery_mod_exp(2u32, exp_odd, modulus).unwrap();
let expected_odd = crate::exp::basic_mod_exp(2u32, exp_odd, modulus);
assert_eq!(result_odd, expected_odd);
}
#[test]
fn test_basic_extended_euclidean_none_case() {
let even_modulus = 6u32; let r = 8u32;
assert!(
crate::inv::basic_mod_inv(even_modulus, r).is_none(),
"basic_mod_inv should return None for non-coprime inputs"
);
let result = compute_n_prime_extended_euclidean(even_modulus, r);
assert!(
result.is_none(),
"Should return None when basic_mod_inv fails"
);
let test_cases = [
(4u32, 8u32), (6u32, 12u32), (10u32, 16u32), (12u32, 8u32), ];
for (modulus, r) in test_cases.iter() {
assert!(
crate::inv::basic_mod_inv(*modulus, *r).is_none(),
"basic_mod_inv({}, {}) should return None",
modulus,
r
);
let result = compute_n_prime_extended_euclidean(*modulus, *r);
assert!(
result.is_none(),
"compute_n_prime_extended_euclidean({}, {}) should return None",
modulus,
r
);
}
}
#[test]
fn test_type_bit_width() {
assert_eq!(type_bit_width::<u8>(), 8);
assert_eq!(type_bit_width::<u16>(), 16);
assert_eq!(type_bit_width::<u32>(), 32);
assert_eq!(type_bit_width::<u64>(), 64);
}
#[test]
fn test_mod_double() {
assert_eq!(mod_double(5u8, 13), 10);
assert_eq!(mod_double(8u8, 13), 3);
assert_eq!(mod_double(200u8, 201), 199);
assert_eq!(mod_double(0u8, 13), 0);
}
#[test]
fn test_compute_n_prime_newton() {
let test_moduli: &[u8] = &[3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 255];
for &n in test_moduli {
let np = compute_n_prime_newton(n, 8);
let product = n.wrapping_mul(np);
assert_eq!(
product, 0xFF,
"n={n}: n*n_prime={product:#04x}, expected 0xFF"
);
}
let np32 = compute_n_prime_newton(13u32, 32);
assert_eq!(13u32.wrapping_mul(np32), u32::MAX);
}
#[test]
fn test_compute_r_mod_n() {
assert_eq!(compute_r_mod_n(13u8, 8), 9);
assert_eq!(compute_r_mod_n(255u8, 8), 1);
assert_eq!(compute_r_mod_n(3u8, 8), 1);
assert_eq!(compute_r_mod_n(13u32, 32), 9);
}
#[test]
fn test_wide_redc() {
let n: u8 = 13;
let n_prime = compute_n_prime_newton(n, 8);
let result = wide_redc(35u8, 0u8, n, n_prime);
assert_eq!(result, 1);
}
#[test]
fn test_wide_montgomery_roundtrip() {
let modulus = 13u8;
let w = type_bit_width::<u8>();
let n_prime = compute_n_prime_newton(modulus, w);
let r_mod_n = compute_r_mod_n(modulus, w);
let r2_mod_n = compute_r2_mod_n(r_mod_n, modulus, w);
for a in 0u8..13 {
let (lo, hi) = a.wide_mul(&r2_mod_n);
let a_m = wide_redc(lo, hi, modulus, n_prime);
let back = wide_redc(a_m, 0u8, modulus, n_prime);
assert_eq!(back, a, "roundtrip failed for a={a}");
}
}
#[test]
fn test_wide_montgomery_roundtrip_u32() {
let modulus = 0xFFFF_FFF1u32; let w = type_bit_width::<u32>();
let n_prime = compute_n_prime_newton(modulus, w);
let r_mod_n = compute_r_mod_n(modulus, w);
let r2_mod_n = compute_r2_mod_n(r_mod_n, modulus, w);
let test_vals = [0u32, 1, 2, 100, modulus - 1, modulus - 2, 0x7FFF_FFFF];
for &a in &test_vals {
let (lo, hi) = a.wide_mul(&r2_mod_n);
let a_m = wide_redc(lo, hi, modulus, n_prime);
let back = wide_redc(a_m, 0u32, modulus, n_prime);
assert_eq!(back, a, "roundtrip failed for a={a:#x}");
}
}
#[test]
fn test_wide_mod_mul_u8_exhaustive() {
let modulus = 13u8;
for a in 0u8..13 {
for b in 0u8..13 {
let expected = ((a as u16 * b as u16) % 13) as u8;
let got = basic_montgomery_mod_mul(a, b, modulus).unwrap();
assert_eq!(
got, expected,
"{a}*{b} mod 13: got {got}, expected {expected}"
);
}
}
}
#[test]
fn test_wide_mod_mul_u32() {
let modulus = 0xFFFF_FFF1u32;
let vals = [0u32, 1, 2, 7, 1000, 0x7FFF_FFFF, modulus - 1, modulus - 2];
for &a in &vals {
for &b in &vals {
let expected = crate::mul::basic_mod_mul(a, b, modulus);
let got = basic_montgomery_mod_mul(a, b, modulus).unwrap();
assert_eq!(
got, expected,
"{a:#x}*{b:#x} mod {modulus:#x}: got {got:#x}, expected {expected:#x}"
);
}
}
}
#[test]
fn test_wide_mod_exp_small() {
let modulus = 13u8;
for base in 0u8..13 {
for exp in 0u8..20 {
let expected = crate::exp::basic_mod_exp(base, exp, modulus);
let got = basic_montgomery_mod_exp(base, exp, modulus).unwrap();
assert_eq!(
got, expected,
"{base}^{exp} mod 13: got {got}, expected {expected}"
);
}
}
}
#[test]
fn test_wide_mod_exp_u32() {
let modulus = 0xFFFF_FFF1u32;
let expected = crate::exp::basic_mod_exp(2u32, 100, modulus);
let got = basic_montgomery_mod_exp(2, 100, modulus).unwrap();
assert_eq!(got, expected);
let expected = crate::exp::basic_mod_exp(3u32, 1000, modulus);
let got = basic_montgomery_mod_exp(3, 1000, modulus).unwrap();
assert_eq!(got, expected);
let expected = crate::exp::basic_mod_exp(modulus - 1, modulus - 2, modulus);
let got = basic_montgomery_mod_exp(modulus - 1, modulus - 2, modulus).unwrap();
assert_eq!(got, expected);
}
#[test]
fn test_wide_mod_exp_large_u64() {
let modulus = 0xFFFF_FFFF_FFFF_FFC5u64; let base = 0xDEAD_BEEF_CAFE_BABEu64;
let exp = 0x1234_5678u64;
let expected = crate::exp::basic_mod_exp(base, exp, modulus);
let got = basic_montgomery_mod_exp(base, exp, modulus).unwrap();
assert_eq!(got, expected);
}
#[test]
fn test_wide_params_even_modulus() {
assert!(basic_montgomery_mod_mul(2u32, 3u32, 4u32).is_none());
assert!(basic_montgomery_mod_mul(2u32, 3u32, 0u32).is_none());
}
#[test]
fn test_wide_mod_mul_even_modulus() {
assert!(basic_montgomery_mod_mul(2u32, 3u32, 4u32).is_none());
}
#[test]
fn test_wide_mod_exp_even_modulus() {
assert!(basic_montgomery_mod_exp(2u32, 3u32, 4u32).is_none());
}
#[test]
fn test_wide_fixed_bigint() {
type U128 = FixedUInt<u32, 4>;
let modulus = !U128::from(0u64) - U128::from(58u64);
let a = U128::from(0xDEAD_BEEF_u64);
let b = U128::from(0xCAFE_BABE_u64);
let expected = crate::mul::basic_mod_mul(a, b, modulus);
let got = basic_montgomery_mod_mul(a, b, modulus).unwrap();
assert_eq!(got, expected);
let base = U128::from(42u64);
let exp = U128::from(1000u64);
let expected = crate::exp::basic_mod_exp(base, exp, modulus);
let got = basic_montgomery_mod_exp(base, exp, modulus).unwrap();
assert_eq!(got, expected);
}
#[test]
fn test_wide_fixed_bigint_pr() {
type U128 = FixedUInt<u32, 4>;
let modulus = !U128::from(0u64) - U128::from(58u64);
let w = type_bit_width::<U128>();
let n_prime = compute_n_prime_newton(modulus, w);
let r_mod_n = compute_r_mod_n(modulus, w);
let r2_mod_n = compute_r2_mod_n(r_mod_n, modulus, w);
let a = U128::from(0xDEAD_BEEF_u64);
let (lo, hi) = a.wide_mul(&r2_mod_n);
let a_m = wide_redc(lo, hi, modulus, n_prime);
let back = wide_redc(a_m, U128::from(0u64), modulus, n_prime);
assert_eq!(back, a);
let b = U128::from(0xCAFE_BABE_u64);
let expected = crate::mul::basic_mod_mul_pr(a, b, modulus);
let got = basic_montgomery_mod_mul_pr(a, b, modulus).unwrap();
assert_eq!(got, expected);
let base = U128::from(42u64);
let exp = U128::from(1000u64);
let expected = crate::exp::basic_mod_exp_pr(base, exp, modulus);
let got = basic_montgomery_mod_exp_pr(base, exp, modulus).unwrap();
assert_eq!(got, expected);
}
#[test]
fn test_input_reduction_mod_mul() {
let modulus = 13u32;
let a = 27u32; let b = 39u32; let got = basic_montgomery_mod_mul(a, b, modulus).unwrap();
assert_eq!(got, 0, "27 * 39 mod 13 should be 0");
let a = 100u32; let b = 200u32; let expected = (9 * 5) % 13; let got = basic_montgomery_mod_mul(a, b, modulus).unwrap();
assert_eq!(got, expected);
let got = basic_montgomery_mod_mul(13u32, 5u32, modulus).unwrap();
assert_eq!(got, 0, "modulus * 5 mod modulus should be 0");
}
#[test]
fn test_input_reduction_mod_exp() {
let modulus = 13u32;
let base = 27u32; let exp = 100u32;
let expected = 1u32; let got = basic_montgomery_mod_exp(base, exp, modulus).unwrap();
assert_eq!(got, expected);
let base = 100u32; let exp = 3u32;
let expected = crate::exp::basic_mod_exp(9u32, 3, modulus); let got = basic_montgomery_mod_exp(base, exp, modulus).unwrap();
assert_eq!(got, expected);
}
#[test]
fn test_wide_redc_ct_matches_nct_u8() {
let modulus = 13u8;
for t_lo in 0u8..=255 {
for t_hi in 0u8..modulus {
let n_prime = 11u8;
let nct = wide_redc(t_lo, t_hi, modulus, n_prime);
let ct = wide_redc_ct(t_lo, t_hi, modulus, n_prime);
assert_eq!(nct, ct, "wide_redc_ct mismatch at t_lo={t_lo} t_hi={t_hi}");
}
}
}
#[test]
fn test_strict_wide_matches_basic_u8() {
use super::{
wide_montgomery_mul as basic_wide_montgomery_mul,
wide_montgomery_mul_ct as basic_wide_montgomery_mul_ct, wide_redc as basic_wide_redc,
wide_redc_ct as basic_wide_redc_ct,
};
let modulus = 13u8;
let n_prime = 11u8;
for t_lo in 0u8..=255 {
for t_hi in 0u8..modulus {
assert_eq!(
basic_wide_redc(t_lo, t_hi, modulus, n_prime),
strict_wide_redc(&t_lo, &t_hi, &modulus, &n_prime),
"strict_wide_redc mismatch at t_lo={t_lo} t_hi={t_hi}"
);
assert_eq!(
basic_wide_redc_ct(t_lo, t_hi, modulus, n_prime),
strict_wide_redc_ct(&t_lo, &t_hi, &modulus, &n_prime),
"strict_wide_redc_ct mismatch at t_lo={t_lo} t_hi={t_hi}"
);
}
}
for a in 0u8..modulus {
for b in 0u8..modulus {
assert_eq!(
basic_wide_montgomery_mul(a, b, modulus, n_prime),
strict_wide_montgomery_mul(&a, &b, &modulus, &n_prime),
"strict_wide_montgomery_mul mismatch at a={a} b={b}"
);
assert_eq!(
basic_wide_montgomery_mul_ct(a, b, modulus, n_prime),
strict_wide_montgomery_mul_ct(&a, &b, &modulus, &n_prime),
"strict_wide_montgomery_mul_ct mismatch at a={a} b={b}"
);
}
}
}
#[test]
fn test_wide_mul_acc_identity_u8() {
let modulus = 13u8;
let n_prime = compute_n_prime_newton(modulus, 8);
for a in 0u8..modulus {
for b in 0u8..modulus {
let direct = wide_montgomery_mul(a, b, modulus, n_prime);
let (lo, hi) = wide_montgomery_mul_acc(0u8, 0u8, a, b);
let via_acc = wide_redc(lo, hi, modulus, n_prime);
assert_eq!(direct, via_acc, "identity mismatch at a={a} b={b}");
}
}
}
#[test]
fn test_wide_mul_acc_dot_product_u8() {
let modulus = 13u8;
let n_prime = compute_n_prime_newton(modulus, 8);
let r_mod_n = compute_r_mod_n(modulus, 8);
let r2 = compute_r2_mod_n(r_mod_n, modulus, 8);
let to_mont = |x: u8| {
let (lo, hi) = x.wide_mul(&r2);
wide_redc(lo, hi, modulus, n_prime)
};
let pairs: &[(u8, u8)] = &[(2, 3), (5, 7), (11, 4), (1, 12)];
let (mut acc_lo, mut acc_hi) = (0u8, 0u8);
for &(a, b) in pairs {
let (l, h) = wide_montgomery_mul_acc(acc_lo, acc_hi, to_mont(a), to_mont(b));
acc_lo = l;
acc_hi = h;
}
let result_mont = wide_redc(acc_lo, acc_hi, modulus, n_prime);
let result = wide_redc(result_mont, 0u8, modulus, n_prime);
let expected = pairs
.iter()
.fold(0u8, |acc, &(a, b)| (acc + (a * b) % modulus) % modulus);
assert_eq!(result, expected);
}
#[test]
fn test_wide_mul_acc_ct_matches_nct_u8() {
let modulus = 13u8;
for a in 0u8..modulus {
for b in 0u8..modulus {
for acc_lo in [0u8, 1, 50, 100, 200, 255] {
for acc_hi in [0u8, 1, 5, modulus - 1] {
let nct = wide_montgomery_mul_acc(acc_lo, acc_hi, a, b);
let ct = wide_montgomery_mul_acc_ct(acc_lo, acc_hi, a, b);
assert_eq!(
nct, ct,
"ct/nct mismatch at lo={acc_lo} hi={acc_hi} a={a} b={b}"
);
}
}
}
}
}
#[test]
fn test_strict_wide_mul_acc_matches_basic_u8() {
let modulus = 13u8;
for a in 0u8..modulus {
for b in 0u8..modulus {
for acc_lo in [0u8, 1, 50, 100, 255] {
for acc_hi in [0u8, 1, 5, modulus - 1] {
assert_eq!(
wide_montgomery_mul_acc(acc_lo, acc_hi, a, b),
strict_wide_montgomery_mul_acc(&acc_lo, &acc_hi, &a, &b),
"strict mul_acc mismatch at lo={acc_lo} hi={acc_hi} a={a} b={b}"
);
assert_eq!(
wide_montgomery_mul_acc_ct(acc_lo, acc_hi, a, b),
strict_wide_montgomery_mul_acc_ct(&acc_lo, &acc_hi, &a, &b),
"strict mul_acc_ct mismatch at lo={acc_lo} hi={acc_hi} a={a} b={b}"
);
}
}
}
}
}
#[test]
fn test_wide_mul_acc_dot_product_u32_mlkem_q() {
let modulus = 3329u32;
let w = type_bit_width::<u32>();
let n_prime = compute_n_prime_newton(modulus, w);
let r2 = compute_r2_mod_n(compute_r_mod_n(modulus, w), modulus, w);
let to_mont = |x: u32| {
let (lo, hi) = x.wide_mul(&r2);
wide_redc(lo, hi, modulus, n_prime)
};
let pairs: &[(u32, u32)] = &[(123, 456), (789, 1011), (2222, 3000), (1, 3328)];
let (mut acc_lo, mut acc_hi) = (0u32, 0u32);
for &(a, b) in pairs {
let (l, h) = wide_montgomery_mul_acc(acc_lo, acc_hi, to_mont(a), to_mont(b));
acc_lo = l;
acc_hi = h;
}
let result_mont = wide_redc(acc_lo, acc_hi, modulus, n_prime);
let result = wide_redc(result_mont, 0u32, modulus, n_prime);
let expected = pairs.iter().fold(0u64, |acc, &(a, b)| {
acc + (a as u64 * b as u64) % modulus as u64
}) % modulus as u64;
assert_eq!(result as u64, expected);
}
#[test]
fn test_wide_redc_ct_matches_nct_fixed() {
type U128 = FixedUInt<u32, 4>;
type U128Ct = FixedUInt<u32, 4, Ct>;
let modulus = !U128::from(0u64) - U128::from(58u64);
let w = type_bit_width::<U128>();
let n_prime = compute_n_prime_newton(modulus, w);
let modulus_ct: U128Ct = modulus.into();
let n_prime_ct: U128Ct = n_prime.into();
let test_vals = [
U128::from(0u64),
U128::from(1u64),
U128::from(0xDEAD_BEEF_u64),
modulus - U128::from(1u64),
!U128::from(0u64), ];
for &t_lo in &test_vals {
for &t_hi in &test_vals {
let nct = wide_redc(t_lo, t_hi, modulus, n_prime);
let t_lo_ct: U128Ct = t_lo.into();
let t_hi_ct: U128Ct = t_hi.into();
let ct = wide_redc_ct(t_lo_ct, t_hi_ct, modulus_ct, n_prime_ct);
assert_eq!(
nct,
ct.forget_ct(),
"wide_redc_ct mismatch at t_lo={t_lo:?} t_hi={t_hi:?}"
);
}
}
}
#[test]
fn test_basic_montgomery_mod_exp_pr_ct_matches_nct_u32_with_external_reduce() {
let modulus = 13u32;
for base in 0u32..modulus {
for exp in 0u32..32 {
let nct = basic_montgomery_mod_exp(base, exp, modulus).unwrap();
let ct = basic_montgomery_mod_exp_pr_ct(base % modulus, exp, modulus).unwrap();
assert_eq!(nct, ct, "mod_exp_pr_ct mismatch at base={base} exp={exp}");
}
}
}
#[test]
fn test_basic_montgomery_mod_exp_pr_ct_matches_nct_u32() {
let modulus = 13u32;
for base in 0u32..modulus {
for exp in 0u32..32 {
let nct = basic_montgomery_mod_exp_pr(base, exp, modulus).unwrap();
let ct = basic_montgomery_mod_exp_pr_ct(base, exp, modulus).unwrap();
assert_eq!(nct, ct, "mod_exp_pr_ct mismatch at base={base} exp={exp}");
}
}
}
#[test]
fn test_basic_montgomery_mod_exp_pr_ct_matches_nct_fixed() {
type U128 = FixedUInt<u32, 4>;
type U128Ct = FixedUInt<u32, 4, Ct>;
let modulus = !U128::from(0u64) - U128::from(58u64); let modulus_ct: U128Ct = modulus.into();
let bases = [U128::from(2u64), U128::from(0xDEAD_BEEF_u64)];
let exps = [
U128::from(0u64),
U128::from(1u64),
U128::from(7u64),
U128::from(0xCAFE_BABE_u64),
];
for &base in &bases {
for &exp in &exps {
let nct = basic_montgomery_mod_exp_pr(base, exp, modulus).unwrap();
let base_ct: U128Ct = base.into();
let exp_ct: U128Ct = exp.into();
let ct = basic_montgomery_mod_exp_pr_ct(base_ct, exp_ct, modulus_ct).unwrap();
assert_eq!(
nct,
ct.forget_ct(),
"mod_exp_pr_ct mismatch at base={base:?} exp={exp:?}"
);
}
}
}
#[test]
fn test_accumulate_high_half_carry_ct_matches_nct_u8() {
for result in 0u8..=255 {
for &carry1 in &[false, true] {
for &carry2 in &[false, true] {
let nct = accumulate_high_half_carry(result, carry1, carry2);
let ct = accumulate_high_half_carry_ct(
result,
Choice::from(carry1 as u8),
Choice::from(carry2 as u8),
);
let ct = (ct.0, bool::from(ct.1));
assert_eq!(
nct, ct,
"mismatch at result={result} carry1={carry1} carry2={carry2}"
);
}
}
}
}
}