use crate::{Limb, Uint, Word};
use super::{
constant_mod::{Residue, ResidueParams},
div_by_2::div_by_2,
reduction::montgomery_reduction,
Retrieve,
};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
mod runtime_add;
mod runtime_inv;
mod runtime_mul;
mod runtime_neg;
mod runtime_pow;
mod runtime_sub;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynResidueParams<const LIMBS: usize> {
modulus: Uint<LIMBS>,
r: Uint<LIMBS>,
r2: Uint<LIMBS>,
r3: Uint<LIMBS>,
mod_neg_inv: Limb,
}
impl<const LIMBS: usize> DynResidueParams<LIMBS> {
const fn generate_params(modulus: &Uint<LIMBS>) -> Self {
let r = Uint::MAX.const_rem(modulus).0.wrapping_add(&Uint::ONE);
let r2 = Uint::const_rem_wide(r.square_wide(), modulus).0;
let modulus_lo = Uint::<1>::from_words([modulus.limbs[0].0]);
let mod_neg_inv = Limb(
Word::MIN.wrapping_sub(modulus_lo.inv_mod2k_vartime(Word::BITS as usize).limbs[0].0),
);
let r3 = montgomery_reduction(&r2.square_wide(), modulus, mod_neg_inv);
Self {
modulus: *modulus,
r,
r2,
r3,
mod_neg_inv,
}
}
pub const fn new(modulus: &Uint<LIMBS>) -> Self {
if modulus.ct_is_odd().to_u8() == 0 {
panic!("modulus must be odd");
}
Self::generate_params(modulus)
}
#[deprecated(
since = "0.5.3",
note = "This functionality will be moved to `new` in a future release."
)]
pub fn new_checked(modulus: &Uint<LIMBS>) -> CtOption<Self> {
CtOption::new(Self::generate_params(modulus), modulus.ct_is_odd().into())
}
pub const fn modulus(&self) -> &Uint<LIMBS> {
&self.modulus
}
pub const fn from_residue_params<P>() -> Self
where
P: ResidueParams<LIMBS>,
{
Self {
modulus: P::MODULUS,
r: P::R,
r2: P::R2,
r3: P::R3,
mod_neg_inv: P::MOD_NEG_INV,
}
}
}
impl<const LIMBS: usize> ConditionallySelectable for DynResidueParams<LIMBS> {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
modulus: Uint::conditional_select(&a.modulus, &b.modulus, choice),
r: Uint::conditional_select(&a.r, &b.r, choice),
r2: Uint::conditional_select(&a.r2, &b.r2, choice),
r3: Uint::conditional_select(&a.r3, &b.r3, choice),
mod_neg_inv: Limb::conditional_select(&a.mod_neg_inv, &b.mod_neg_inv, choice),
}
}
}
impl<const LIMBS: usize> ConstantTimeEq for DynResidueParams<LIMBS> {
fn ct_eq(&self, other: &Self) -> Choice {
self.modulus.ct_eq(&other.modulus)
& self.r.ct_eq(&other.r)
& self.r2.ct_eq(&other.r2)
& self.r3.ct_eq(&other.r3)
& self.mod_neg_inv.ct_eq(&other.mod_neg_inv)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynResidue<const LIMBS: usize> {
montgomery_form: Uint<LIMBS>,
residue_params: DynResidueParams<LIMBS>,
}
impl<const LIMBS: usize> DynResidue<LIMBS> {
pub const fn new(integer: &Uint<LIMBS>, residue_params: DynResidueParams<LIMBS>) -> Self {
let product = integer.mul_wide(&residue_params.r2);
let montgomery_form = montgomery_reduction(
&product,
&residue_params.modulus,
residue_params.mod_neg_inv,
);
Self {
montgomery_form,
residue_params,
}
}
pub const fn retrieve(&self) -> Uint<LIMBS> {
montgomery_reduction(
&(self.montgomery_form, Uint::ZERO),
&self.residue_params.modulus,
self.residue_params.mod_neg_inv,
)
}
pub const fn zero(residue_params: DynResidueParams<LIMBS>) -> Self {
Self {
montgomery_form: Uint::<LIMBS>::ZERO,
residue_params,
}
}
pub const fn one(residue_params: DynResidueParams<LIMBS>) -> Self {
Self {
montgomery_form: residue_params.r,
residue_params,
}
}
pub const fn params(&self) -> &DynResidueParams<LIMBS> {
&self.residue_params
}
pub const fn as_montgomery(&self) -> &Uint<LIMBS> {
&self.montgomery_form
}
pub fn as_montgomery_mut(&mut self) -> &mut Uint<LIMBS> {
&mut self.montgomery_form
}
pub const fn from_montgomery(
integer: Uint<LIMBS>,
residue_params: DynResidueParams<LIMBS>,
) -> Self {
Self {
montgomery_form: integer,
residue_params,
}
}
pub const fn to_montgomery(&self) -> Uint<LIMBS> {
self.montgomery_form
}
pub fn div_by_2(&self) -> Self {
Self {
montgomery_form: div_by_2(&self.montgomery_form, &self.residue_params.modulus),
residue_params: self.residue_params,
}
}
}
impl<const LIMBS: usize> Retrieve for DynResidue<LIMBS> {
type Output = Uint<LIMBS>;
fn retrieve(&self) -> Self::Output {
self.retrieve()
}
}
impl<const LIMBS: usize, P: ResidueParams<LIMBS>> From<&Residue<P, LIMBS>> for DynResidue<LIMBS> {
fn from(residue: &Residue<P, LIMBS>) -> Self {
Self {
montgomery_form: residue.to_montgomery(),
residue_params: DynResidueParams::from_residue_params::<P>(),
}
}
}
impl<const LIMBS: usize> ConditionallySelectable for DynResidue<LIMBS> {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
montgomery_form: Uint::conditional_select(
&a.montgomery_form,
&b.montgomery_form,
choice,
),
residue_params: DynResidueParams::conditional_select(
&a.residue_params,
&b.residue_params,
choice,
),
}
}
}
impl<const LIMBS: usize> ConstantTimeEq for DynResidue<LIMBS> {
fn ct_eq(&self, other: &Self) -> Choice {
self.montgomery_form.ct_eq(&other.montgomery_form)
& self.residue_params.ct_eq(&other.residue_params)
}
}
#[cfg(feature = "zeroize")]
impl<const LIMBS: usize> zeroize::Zeroize for DynResidue<LIMBS> {
fn zeroize(&mut self) {
self.montgomery_form.zeroize()
}
}
#[cfg(test)]
mod test {
use super::*;
const LIMBS: usize = nlimbs!(64);
#[test]
#[allow(deprecated)]
fn test_valid_modulus() {
let valid_modulus = Uint::<LIMBS>::from(3u8);
DynResidueParams::<LIMBS>::new_checked(&valid_modulus).unwrap();
DynResidueParams::<LIMBS>::new(&valid_modulus);
}
#[test]
#[allow(deprecated)]
fn test_invalid_checked_modulus() {
assert!(bool::from(
DynResidueParams::<LIMBS>::new_checked(&Uint::from(2u8)).is_none()
))
}
#[test]
#[should_panic]
fn test_invalid_modulus() {
DynResidueParams::<LIMBS>::new(&Uint::from(2u8));
}
}