Skip to content

Commit

Permalink
Impl Wrapping* traits from num-traits
Browse files Browse the repository at this point in the history
Until now we have used traits defined in this crate, primarily because
many of these traits are predicates that return `bool` and in
constant-time code we want those to return `Choice`/`CtOption`.

`Wrapping*` is an example of the sort of trait we'd want to incorporate
unchanged, and there are potentially others, so this adds `num-traits`
as a hard dependency (previously our only hard dependency was `subtle`).
Note that `num-traits` itself has no transitive dependencies (or rather,
they're optional but not enabled).

The `Wrapping*` traits have bounds on the corresponding op traits being
impl'd with `Self` operands e.g. `WrappingAdd: Add<Self, Output = Self>`
so this PR also adds impls of those traits.

We've previously avoided these as in `std` they panic on
overflow/underflow in debug builds and silently wrap in release builds.
This PR always panics.

This required some changes to the `Mul` impls which were conditional on
`ConcatMixed` and implicitly widened. To accomodate impls which are
always available and require no bounds (in order to allow us to impl
`WideningMul`), and renames the following:

- `Uint::mul` -> `Uint::widening_mul`
- `Uint::mul_wide` -> `Uint::widening_mul_split`
  • Loading branch information
tarcieri committed Dec 14, 2023
1 parent 4bf6932 commit 51dd348
Show file tree
Hide file tree
Showing 16 changed files with 268 additions and 102 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ subtle = { version = "2.5", default-features = false }
# optional dependencies
der = { version = "0.7", optional = true, default-features = false }
generic-array = { version = "0.14", optional = true }
num-traits = "0.2"
rand_core = { version = "0.6.4", optional = true }
rlp = { version = "0.5", optional = true, default-features = false }
serdect = { version = "0.2", optional = true, default-features = false }
Expand All @@ -33,7 +34,6 @@ criterion = { version = "0.5", features = ["html_reports"] }
hex-literal = "0.4"
num-bigint = { package = "num-bigint-dig", version = "0.8" }
num-integer = "0.1"
num-traits = "0.2"
proptest = "1"
rand_core = { version = "0.6", features = ["std"] }
rand_chacha = "0.3"
Expand Down
6 changes: 3 additions & 3 deletions src/modular.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ mod tests {
// Reducing xR should return x
let x =
U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56");
let product = x.mul_wide(&Modulus2::R);
let product = x.widening_mul_split(&Modulus2::R);
assert_eq!(
montgomery_reduction::<{ Modulus2::LIMBS }>(
&product,
Expand All @@ -151,10 +151,10 @@ mod tests {
// Reducing xR^2 should return xR
let x =
U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56");
let product = x.mul_wide(&Modulus2::R2);
let product = x.widening_mul_split(&Modulus2::R2);

// Computing xR mod modulus without Montgomery reduction
let (lo, hi) = x.mul_wide(&Modulus2::R);
let (lo, hi) = x.widening_mul_split(&Modulus2::R);
let c = hi.concat(&lo);
let red = c.rem(&NonZero::new(U256::ZERO.concat(&Modulus2::MODULUS)).unwrap());
let (hi, lo) = red.split();
Expand Down
2 changes: 1 addition & 1 deletion src/modular/dyn_residue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub struct DynResidue<const LIMBS: usize> {
impl<const LIMBS: usize> DynResidue<LIMBS> {
/// Instantiates a new `Residue` that represents this `integer` mod `MOD`.
pub const fn new(integer: &Uint<LIMBS>, residue_params: DynResidueParams<LIMBS>) -> Self {
let product = integer.mul_wide(&residue_params.r2);
let product = integer.widening_mul_split(&residue_params.r2);
let montgomery_form = montgomery_reduction(
&product,
&residue_params.modulus,
Expand Down
2 changes: 1 addition & 1 deletion src/modular/inv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub const fn inv_montgomery_form<const LIMBS: usize>(
) -> (Uint<LIMBS>, ConstChoice) {
let (inverse, is_some) = x.inv_odd_mod(modulus);
(
montgomery_reduction(&inverse.mul_wide(r3), modulus, mod_neg_inv),
montgomery_reduction(&inverse.widening_mul_split(r3), modulus, mod_neg_inv),
is_some,
)
}
2 changes: 1 addition & 1 deletion src/modular/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub(crate) const fn mul_montgomery_form<const LIMBS: usize>(
modulus: &Uint<LIMBS>,
mod_neg_inv: Limb,
) -> Uint<LIMBS> {
let product = a.mul_wide(b);
let product = a.widening_mul_split(b);
montgomery_reduction::<LIMBS>(&product, modulus, mod_neg_inv)
}

Expand Down
2 changes: 1 addition & 1 deletion src/modular/residue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Residue<MOD, LIMBS> {

/// Internal helper function to generate a residue; this lets us cleanly wrap the constructors.
const fn generate_residue(integer: &Uint<LIMBS>) -> Self {
let product = integer.mul_wide(&MOD::R2);
let product = integer.widening_mul_split(&MOD::R2);
let montgomery_form =
montgomery_reduction::<LIMBS>(&product, &MOD::MODULUS.0, MOD::MOD_NEG_INV);

Expand Down
7 changes: 7 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Traits provided by this crate
pub use num_traits::{
WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub,
};

use crate::{Limb, NonZero};
use core::fmt::Debug;
use core::ops::{
Expand Down Expand Up @@ -71,6 +75,9 @@ pub trait Integer:
+ ShrAssign<u32>
+ SubMod
+ Sync
+ WrappingAdd
+ WrappingSub
+ WrappingMul
+ Zero
{
/// The value `1`.
Expand Down
33 changes: 28 additions & 5 deletions src/uint/add.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! [`Uint`] addition operations.
use crate::{Checked, CheckedAdd, ConstChoice, Limb, Uint, Wrapping, Zero};
use crate::{Checked, CheckedAdd, ConstChoice, Limb, Uint, Wrapping, WrappingAdd, Zero};
use core::ops::{Add, AddAssign};
use subtle::CtOption;

Expand Down Expand Up @@ -45,12 +45,20 @@ impl<const LIMBS: usize> Uint<LIMBS> {
}
}

impl<const LIMBS: usize> CheckedAdd<&Uint<LIMBS>> for Uint<LIMBS> {
impl<const LIMBS: usize> Add for Uint<LIMBS> {
type Output = Self;

fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
let (result, carry) = self.adc(rhs, Limb::ZERO);
CtOption::new(result, carry.is_zero())
fn add(self, rhs: Self) -> Self {
self.add(&rhs)
}
}

impl<const LIMBS: usize> Add<&Uint<LIMBS>> for Uint<LIMBS> {
type Output = Self;

fn add(self, rhs: &Self) -> Self {
self.checked_add(rhs)
.expect("attempted to add with overflow")
}
}

Expand Down Expand Up @@ -154,6 +162,21 @@ impl<const LIMBS: usize> AddAssign<&Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS
}
}

impl<const LIMBS: usize> CheckedAdd<&Uint<LIMBS>> for Uint<LIMBS> {
type Output = Self;

fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
let (result, carry) = self.adc(rhs, Limb::ZERO);
CtOption::new(result, carry.is_zero())
}
}

impl<const LIMBS: usize> WrappingAdd for Uint<LIMBS> {
fn wrapping_add(&self, v: &Self) -> Self {
self.wrapping_add(v)
}
}

#[cfg(test)]
mod tests {
use crate::{CheckedAdd, Limb, U128};
Expand Down
42 changes: 36 additions & 6 deletions src/uint/boxed/add.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! [`BoxedUint`] addition operations.
use crate::{BoxedUint, CheckedAdd, Limb, Wrapping, WrappingAdd, Zero};
use core::ops::{Add, AddAssign};

use crate::{BoxedUint, CheckedAdd, Limb, Wrapping, Zero};
use subtle::{Choice, ConditionallySelectable, CtOption};

impl BoxedUint {
Expand Down Expand Up @@ -51,12 +50,28 @@ impl BoxedUint {
}
}

impl CheckedAdd<&BoxedUint> for BoxedUint {
impl Add for BoxedUint {
type Output = Self;

fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
let (result, carry) = self.adc(rhs, Limb::ZERO);
CtOption::new(result, carry.is_zero())
fn add(self, rhs: Self) -> Self {
self.add(&rhs)
}
}

impl Add<&BoxedUint> for BoxedUint {
type Output = Self;

fn add(self, rhs: &Self) -> Self {
Add::add(&self, rhs)
}
}

impl Add<&BoxedUint> for &BoxedUint {
type Output = BoxedUint;

fn add(self, rhs: &BoxedUint) -> BoxedUint {
self.checked_add(rhs)
.expect("attempted to add with overflow")
}
}

Expand Down Expand Up @@ -104,6 +119,21 @@ impl AddAssign<&Wrapping<BoxedUint>> for Wrapping<BoxedUint> {
}
}

impl CheckedAdd<&BoxedUint> for BoxedUint {
type Output = Self;

fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
let (result, carry) = self.adc(rhs, Limb::ZERO);
CtOption::new(result, carry.is_zero())
}
}

impl WrappingAdd for BoxedUint {
fn wrapping_add(&self, v: &Self) -> Self {
self.wrapping_add(v)
}
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
Expand Down
63 changes: 52 additions & 11 deletions src/uint/boxed/mul.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! [`BoxedUint`] multiplication operations.
use crate::{uint::mul::mul_limbs, BoxedUint, CheckedMul, Limb, WideningMul, Wrapping, Zero};
use crate::{
uint::mul::mul_limbs, BoxedUint, CheckedMul, Limb, WideningMul, Wrapping, WrappingMul, Zero,
};
use core::ops::{Mul, MulAssign};
use subtle::{Choice, CtOption};

Expand Down Expand Up @@ -51,21 +53,36 @@ impl CheckedMul<&BoxedUint> for BoxedUint {
}
}

impl WideningMul for BoxedUint {
type Output = Self;
impl Mul<BoxedUint> for BoxedUint {
type Output = BoxedUint;

#[inline]
fn widening_mul(&self, rhs: BoxedUint) -> Self {
self.mul(&rhs)
fn mul(self, rhs: BoxedUint) -> Self {
BoxedUint::mul(&self, &rhs)
}
}

impl WideningMul<&BoxedUint> for BoxedUint {
type Output = Self;
impl Mul<&BoxedUint> for BoxedUint {
type Output = BoxedUint;

#[inline]
fn widening_mul(&self, rhs: &BoxedUint) -> Self {
self.mul(rhs)
fn mul(self, rhs: &BoxedUint) -> Self {
BoxedUint::mul(&self, rhs)
}
}

impl Mul<BoxedUint> for &BoxedUint {
type Output = BoxedUint;

fn mul(self, rhs: BoxedUint) -> Self::Output {
BoxedUint::mul(self, &rhs)
}
}

impl Mul<&BoxedUint> for &BoxedUint {
type Output = BoxedUint;

fn mul(self, rhs: &BoxedUint) -> Self::Output {
self.checked_mul(rhs)
.expect("attempted to multiply with overflow")
}
}

Expand Down Expand Up @@ -113,6 +130,30 @@ impl MulAssign<&Wrapping<BoxedUint>> for Wrapping<BoxedUint> {
}
}

impl WideningMul for BoxedUint {
type Output = Self;

#[inline]
fn widening_mul(&self, rhs: BoxedUint) -> Self {
self.mul(&rhs)
}
}

impl WideningMul<&BoxedUint> for BoxedUint {
type Output = Self;

#[inline]
fn widening_mul(&self, rhs: &BoxedUint) -> Self {
self.mul(rhs)
}
}

impl WrappingMul for BoxedUint {
fn wrapping_mul(&self, v: &Self) -> Self {
self.wrapping_mul(v)
}
}

#[cfg(test)]
mod tests {
use crate::BoxedUint;
Expand Down
2 changes: 1 addition & 1 deletion src/uint/boxed/mul_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ mod tests {
assert!(c < **p, "not reduced: {} >= {} ", c, p);

let expected = {
let (lo, hi) = a.mul_wide(&b);
let (lo, hi) = a.widening_mul_split(&b);
let mut prod = Uint::<{ 2 * $size }>::ZERO;
prod.limbs[..$size].clone_from_slice(&lo.limbs);
prod.limbs[$size..].clone_from_slice(&hi.limbs);
Expand Down
34 changes: 33 additions & 1 deletion src/uint/boxed/sub.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! [`BoxedUint`] subtraction operations.
use crate::{BoxedUint, CheckedSub, Limb, Zero};
use crate::{BoxedUint, CheckedSub, Limb, WrappingSub, Zero};
use core::ops::Sub;
use subtle::{Choice, ConditionallySelectable, CtOption};

impl BoxedUint {
Expand Down Expand Up @@ -58,6 +59,37 @@ impl CheckedSub<&BoxedUint> for BoxedUint {
}
}

impl Sub for BoxedUint {
type Output = Self;

fn sub(self, rhs: Self) -> Self {
self.sub(&rhs)
}
}

impl Sub<&BoxedUint> for BoxedUint {
type Output = Self;

fn sub(self, rhs: &Self) -> Self {
Sub::sub(&self, rhs)
}
}

impl Sub<&BoxedUint> for &BoxedUint {
type Output = BoxedUint;

fn sub(self, rhs: &BoxedUint) -> BoxedUint {
self.checked_sub(rhs)
.expect("attempted to subtract with underflow")
}
}

impl WrappingSub for BoxedUint {
fn wrapping_sub(&self, v: &Self) -> Self {
self.wrapping_sub(v)
}
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
Expand Down
2 changes: 1 addition & 1 deletion src/uint/concat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ mod tests {

#[test]
fn convert() {
let res: U128 = U64::ONE.mul_wide(&U64::ONE).into();
let res: U128 = U64::ONE.widening_mul_split(&U64::ONE).into();
assert_eq!(res, U128::ONE);

let res: U128 = U64::ONE.square_wide().into();
Expand Down
Loading

0 comments on commit 51dd348

Please sign in to comment.