crypto_bigint/modular/boxed_monty_form/
mul.rsuse super::{BoxedMontyForm, BoxedMontyParams};
use crate::{
modular::reduction::montgomery_reduction_boxed_mut, uint::mul::mul_limbs, BoxedUint, Limb,
Square, SquareAssign, Word, Zero,
};
use core::{
borrow::Borrow,
ops::{Mul, MulAssign},
};
use subtle::{ConditionallySelectable, ConstantTimeLess};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
impl BoxedMontyForm {
pub fn mul(&self, rhs: &Self) -> Self {
debug_assert_eq!(&self.params, &rhs.params);
let montgomery_form = MontyMultiplier::from(self.params.borrow())
.mul(&self.montgomery_form, &rhs.montgomery_form);
Self {
montgomery_form,
params: self.params.clone(),
}
}
pub fn square(&self) -> Self {
let montgomery_form =
MontyMultiplier::from(self.params.borrow()).square(&self.montgomery_form);
Self {
montgomery_form,
params: self.params.clone(),
}
}
}
impl Mul<&BoxedMontyForm> for &BoxedMontyForm {
type Output = BoxedMontyForm;
fn mul(self, rhs: &BoxedMontyForm) -> BoxedMontyForm {
self.mul(rhs)
}
}
impl Mul<BoxedMontyForm> for &BoxedMontyForm {
type Output = BoxedMontyForm;
#[allow(clippy::op_ref)]
fn mul(self, rhs: BoxedMontyForm) -> BoxedMontyForm {
self * &rhs
}
}
impl Mul<&BoxedMontyForm> for BoxedMontyForm {
type Output = BoxedMontyForm;
#[allow(clippy::op_ref)]
fn mul(self, rhs: &BoxedMontyForm) -> BoxedMontyForm {
&self * rhs
}
}
impl Mul<BoxedMontyForm> for BoxedMontyForm {
type Output = BoxedMontyForm;
fn mul(self, rhs: BoxedMontyForm) -> BoxedMontyForm {
&self * &rhs
}
}
impl MulAssign<BoxedMontyForm> for BoxedMontyForm {
fn mul_assign(&mut self, rhs: BoxedMontyForm) {
Self::mul_assign(self, &rhs)
}
}
impl MulAssign<&BoxedMontyForm> for BoxedMontyForm {
fn mul_assign(&mut self, rhs: &BoxedMontyForm) {
debug_assert_eq!(&self.params, &rhs.params);
MontyMultiplier::from(self.params.borrow())
.mul_assign(&mut self.montgomery_form, &rhs.montgomery_form);
}
}
impl Square for BoxedMontyForm {
fn square(&self) -> Self {
BoxedMontyForm::square(self)
}
}
impl SquareAssign for BoxedMontyForm {
fn square_assign(&mut self) {
MontyMultiplier::from(self.params.borrow()).square_assign(&mut self.montgomery_form);
}
}
impl<'a> From<&'a BoxedMontyParams> for MontyMultiplier<'a> {
fn from(params: &'a BoxedMontyParams) -> MontyMultiplier<'a> {
MontyMultiplier::new(¶ms.modulus, params.mod_neg_inv)
}
}
pub(super) struct MontyMultiplier<'a> {
product: BoxedUint,
modulus: &'a BoxedUint,
mod_neg_inv: Limb,
}
impl<'a> MontyMultiplier<'a> {
pub(super) fn new(modulus: &'a BoxedUint, mod_neg_inv: Limb) -> Self {
Self {
product: BoxedUint::zero_with_precision(modulus.bits_precision() * 2),
modulus,
mod_neg_inv,
}
}
pub(super) fn mul(&mut self, a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
let mut ret = a.clone();
self.mul_assign(&mut ret, b);
ret
}
pub(super) fn mul_assign(&mut self, a: &mut BoxedUint, b: &BoxedUint) {
debug_assert_eq!(a.bits_precision(), self.modulus.bits_precision());
debug_assert_eq!(b.bits_precision(), self.modulus.bits_precision());
mul_limbs(&a.limbs, &b.limbs, &mut self.product.limbs);
montgomery_reduction_boxed_mut(&mut self.product, self.modulus, self.mod_neg_inv, a);
debug_assert!(&*a < self.modulus);
}
pub(super) fn square(&mut self, a: &BoxedUint) -> BoxedUint {
let mut ret = a.clone();
self.square_assign(&mut ret);
ret
}
pub(super) fn square_assign(&mut self, a: &mut BoxedUint) {
debug_assert_eq!(a.bits_precision(), self.modulus.bits_precision());
mul_limbs(&a.limbs, &a.limbs, &mut self.product.limbs);
montgomery_reduction_boxed_mut(&mut self.product, self.modulus, self.mod_neg_inv, a);
debug_assert!(&*a < self.modulus);
}
pub(super) fn mul_amm(&mut self, a: &BoxedUint, b: &BoxedUint) -> BoxedUint {
let mut ret = a.clone();
self.mul_amm_assign(&mut ret, b);
ret
}
pub(super) fn mul_amm_assign(&mut self, a: &mut BoxedUint, b: &BoxedUint) {
debug_assert_eq!(a.bits_precision(), self.modulus.bits_precision());
debug_assert_eq!(b.bits_precision(), self.modulus.bits_precision());
self.clear_product();
almost_montgomery_mul(
self.product.as_limbs_mut(),
a.as_limbs(),
b.as_limbs(),
self.modulus.as_limbs(),
self.mod_neg_inv,
);
a.limbs
.copy_from_slice(&self.product.limbs[..a.limbs.len()]);
}
#[allow(dead_code)] pub(super) fn square_amm(&mut self, a: &BoxedUint) -> BoxedUint {
let mut ret = a.clone();
self.square_amm_assign(&mut ret);
ret
}
pub(super) fn square_amm_assign(&mut self, a: &mut BoxedUint) {
debug_assert_eq!(a.bits_precision(), self.modulus.bits_precision());
self.clear_product();
almost_montgomery_mul(
self.product.as_limbs_mut(),
a.as_limbs(),
a.as_limbs(),
self.modulus.as_limbs(),
self.mod_neg_inv,
);
a.limbs
.copy_from_slice(&self.product.limbs[..a.limbs.len()]);
}
fn clear_product(&mut self) {
self.product
.limbs
.iter_mut()
.for_each(|limb| *limb = Limb::ZERO);
}
}
#[cfg(feature = "zeroize")]
impl Drop for MontyMultiplier<'_> {
fn drop(&mut self) {
self.product.zeroize();
}
}
fn almost_montgomery_mul(z: &mut [Limb], x: &[Limb], y: &[Limb], m: &[Limb], k: Limb) {
let n = m.len();
let pre_cond = z.len() > n && z[n..].len() == n && x.len() == n && y.len() == n && m.len() == n;
if !pre_cond {
panic!("Failed preconditions in montgomery_mul");
}
let mut c = Limb::ZERO;
for i in 0..n {
let c2 = add_mul_vvw(&mut z[i..n + i], x, y[i]);
let t = z[i].wrapping_mul(k);
let c3 = add_mul_vvw(&mut z[i..n + i], m, t);
let cx = c.wrapping_add(c2);
let cy = cx.wrapping_add(c3);
z[n + i] = cy;
c = Limb((cx.ct_lt(&c2) | cy.ct_lt(&c3)).unwrap_u8() as Word);
}
let (lower, upper) = z.split_at_mut(n);
sub_vv(lower, upper, m);
let is_zero = c.is_zero();
for (a, b) in lower.iter_mut().zip(upper.iter()) {
a.conditional_assign(b, is_zero);
}
}
#[inline]
fn add_mul_vvw(z: &mut [Limb], x: &[Limb], y: Limb) -> Limb {
let mut c = Limb::ZERO;
for (zi, xi) in z.iter_mut().zip(x.iter()) {
let (z0, z1) = zi.mac(*xi, y, Limb::ZERO);
let (zi_, c_) = z0.overflowing_add(c);
*zi = zi_;
c = c_.wrapping_add(z1);
}
c
}
#[inline(always)]
fn sub_vv(z: &mut [Limb], x: &[Limb], y: &[Limb]) {
let mut borrow = Limb::ZERO;
for (i, (&xi, &yi)) in x.iter().zip(y.iter()).enumerate().take(z.len()) {
let (zi, new_borrow) = xi.sbb(yi, borrow);
z[i] = zi;
borrow = new_borrow;
}
}
#[cfg(test)]
mod tests {
use super::{BoxedMontyForm, BoxedMontyParams, BoxedUint};
#[test]
fn square() {
let x = 0x20u128;
let modulus = 0xB44677037A7DBDE04814256570DCBD8Du128;
let boxed_modulus = BoxedUint::from(modulus);
let boxed_params = BoxedMontyParams::new(boxed_modulus.to_odd().unwrap());
let boxed_monty = BoxedMontyForm::new(BoxedUint::from(x), boxed_params);
let boxed_square = boxed_monty.square();
assert!(boxed_square.as_montgomery() < boxed_square.params().modulus());
}
}