use crypto_bigint::{
BoxedUint, NonZero, Odd,
ctutils::CtSelect,
modular::{BoxedMontyForm, BoxedMontyParams},
};
use crypto_bigint::Resize;
use super::sbig::{SBig, resized};
use crate::error::{Error, Result};
#[derive(Clone, Debug)]
pub struct Modulus {
params: BoxedMontyParams,
nz: NonZero<BoxedUint>,
}
pub type Residue = BoxedMontyForm;
impl Modulus {
pub fn new(m: BoxedUint) -> Result<Self> {
let odd = Option::<Odd<BoxedUint>>::from(m.to_odd()).ok_or(Error::InvalidModulus)?;
let nz = Option::<NonZero<BoxedUint>>::from(m.to_nz()).ok_or(Error::InvalidModulus)?;
Ok(Self {
params: BoxedMontyParams::new_vartime(odd),
nz,
})
}
pub fn from_be_bytes(bytes: &[u8]) -> Result<Self> {
let bits = (bytes.len() as u32 * 8).max(64);
let m = BoxedUint::from_be_slice(bytes, bits).map_err(|_| Error::InvalidEncoding)?;
Self::new(m)
}
pub fn modulus(&self) -> &NonZero<BoxedUint> {
&self.nz
}
pub fn bits_precision(&self) -> u32 {
self.params.bits_precision()
}
pub fn residue(&self, x: &BoxedUint) -> Residue {
let prec = self.bits_precision();
let x = if x.bits_precision() > prec {
let wide = Option::<NonZero<BoxedUint>>::from(
resized(self.nz.as_ref(), x.bits_precision()).to_nz(),
)
.unwrap_or_else(|| self.nz.clone());
x.rem(&wide).resize(prec)
} else {
resized(x, prec)
};
BoxedMontyForm::new(x, &self.params)
}
pub fn residue_signed(&self, x: &SBig) -> Residue {
let r = self.residue(x.mag());
let n = r.neg();
Residue::from_montgomery(
r.as_montgomery().ct_select(n.as_montgomery(), x.is_neg()),
&self.params,
)
}
pub fn one(&self) -> Residue {
BoxedMontyForm::one(&self.params)
}
pub fn invert(&self, a: &Residue) -> Result<Residue> {
Option::from(a.invert()).ok_or(Error::NotInvertible)
}
pub fn pow_ct(&self, base: &Residue, exp: &SBig, exp_bits: u32) -> Result<Residue> {
let inv = self.invert(base)?;
let b = Residue::from_montgomery(
base.as_montgomery()
.ct_select(inv.as_montgomery(), exp.is_neg()),
&self.params,
);
Ok(b.pow_bounded_exp(exp.mag(), exp_bits.min(exp.mag().bits_precision())))
}
pub fn pow_vartime(&self, base: &Residue, exp: &SBig) -> Result<Residue> {
let bits = exp.mag().bits_vartime();
let b = if bool::from(exp.is_neg()) {
self.invert(base)?
} else {
base.clone()
};
if bits == 0 {
return Ok(self.one());
}
Ok(b.pow_bounded_exp(exp.mag(), bits))
}
pub fn jacobi_vartime(&self, a: &BoxedUint) -> i8 {
jacobi_vartime(a, self.nz.as_ref())
}
}
pub fn jacobi_vartime(a: &BoxedUint, n: &BoxedUint) -> i8 {
let bits = a.bits_precision().max(n.bits_precision());
let mut n = resized(n, bits);
let mut a = match NonZero::new(n.clone()).into_option() {
Some(nz) => resized(a, bits).rem_vartime(&nz),
None => return 0,
};
let mut t: i8 = 1;
while !bool::from(a.is_zero()) {
let tz = a.trailing_zeros_vartime();
a = a
.shr_vartime(tz)
.unwrap_or_else(|| BoxedUint::zero_with_precision(bits));
let n_mod8 = n.as_words()[0] & 7;
if tz % 2 == 1 && (n_mod8 == 3 || n_mod8 == 5) {
t = -t;
}
core::mem::swap(&mut a, &mut n);
if a.as_words()[0] & 3 == 3 && n.as_words()[0] & 3 == 3 {
t = -t;
}
if let Some(nz) = NonZero::new(n.clone()).into_option() {
a = a.rem_vartime(&nz);
}
}
if n.is_one().into() { t } else { 0 }
}