Skip to content

Commit

Permalink
Checked* traits
Browse files Browse the repository at this point in the history
Adds the following traits:

- `CheckedAdd`
- `CheckedSub`
- `CheckedMul`

Also changes the existing inherent methods to use the traits.
  • Loading branch information
tarcieri committed Nov 14, 2021
1 parent ef7c780 commit 6feebc0
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 81 deletions.
23 changes: 12 additions & 11 deletions src/limb/add.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Limb addition

use super::{Limb, LimbUInt, WideLimbUInt};
use crate::{Checked, Wrapping};
use crate::{Checked, CheckedAdd, Limb, LimbUInt, WideLimbUInt, Wrapping};
use core::ops::{Add, AddAssign};
use subtle::CtOption;

Expand All @@ -19,14 +18,6 @@ impl Limb {
)
}

/// Perform checked addition, returning a [`CtOption`] which `is_some` only
/// if the operation did not overflow.
#[inline]
pub fn checked_add(&self, rhs: Self) -> CtOption<Self> {
let (result, carry) = self.adc(rhs, Limb::ZERO);
CtOption::new(result, carry.is_zero())
}

/// Perform saturating addition.
#[inline]
pub fn saturating_add(&self, rhs: Self) -> Self {
Expand All @@ -40,6 +31,16 @@ impl Limb {
}
}

impl CheckedAdd for Limb {
type Output = Self;

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

impl Add for Wrapping<Limb> {
type Output = Self;

Expand Down Expand Up @@ -142,7 +143,7 @@ impl AddAssign<&Checked<Limb>> for Checked<Limb> {

#[cfg(test)]
mod tests {
use crate::Limb;
use crate::{CheckedAdd, Limb};

#[test]
fn adc_no_carry() {
Expand Down
23 changes: 12 additions & 11 deletions src/limb/mul.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Limb multiplication

use super::{Limb, LimbUInt, WideLimbUInt};
use crate::{Checked, Wrapping};
use crate::{Checked, CheckedMul, Limb, LimbUInt, WideLimbUInt, Wrapping};
use core::ops::{Mul, MulAssign};
use subtle::CtOption;

Expand All @@ -26,19 +25,21 @@ impl Limb {
Limb(self.0.wrapping_mul(rhs.0))
}

/// Perform checked multiplication, returning a [`CtOption`] which `is_some`
/// only if the operation did not overflow.
/// Compute "wide" multiplication, with a product twice the size of the input.
pub(crate) const fn mul_wide(&self, rhs: Self) -> WideLimbUInt {
(self.0 as WideLimbUInt) * (rhs.0 as WideLimbUInt)
}
}

impl CheckedMul for Limb {
type Output = Self;

#[inline]
pub fn checked_mul(&self, rhs: Self) -> CtOption<Self> {
fn checked_mul(&self, rhs: Self) -> CtOption<Self> {
let result = self.mul_wide(rhs);
let overflow = Limb((result >> Self::BIT_SIZE) as LimbUInt);
CtOption::new(Limb(result as LimbUInt), overflow.is_zero())
}

/// Compute "wide" multiplication, with a product twice the size of the input.
pub(crate) const fn mul_wide(&self, rhs: Self) -> WideLimbUInt {
(self.0 as WideLimbUInt) * (rhs.0 as WideLimbUInt)
}
}

impl Mul for Wrapping<Limb> {
Expand Down Expand Up @@ -143,7 +144,7 @@ impl MulAssign<&Checked<Limb>> for Checked<Limb> {

#[cfg(test)]
mod tests {
use super::{Limb, WideLimbUInt};
use super::{CheckedMul, Limb, WideLimbUInt};

#[test]
fn mul_wide_zero_and_one() {
Expand Down
23 changes: 12 additions & 11 deletions src/limb/sub.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Limb subtraction

use super::{Limb, LimbUInt, WideLimbUInt};
use crate::{Checked, Wrapping};
use crate::{Checked, CheckedSub, Limb, LimbUInt, WideLimbUInt, Wrapping};
use core::ops::{Sub, SubAssign};
use subtle::CtOption;

Expand All @@ -19,14 +18,6 @@ impl Limb {
)
}

/// Perform checked subtraction, returning a [`CtOption`] which `is_some`
/// only if the operation did not overflow.
#[inline]
pub fn checked_sub(&self, rhs: Self) -> CtOption<Self> {
let (result, underflow) = self.sbb(rhs, Limb::ZERO);
CtOption::new(result, underflow.is_zero())
}

/// Perform saturating subtraction.
#[inline]
pub fn saturating_sub(&self, rhs: Self) -> Self {
Expand All @@ -41,6 +32,16 @@ impl Limb {
}
}

impl CheckedSub for Limb {
type Output = Self;

#[inline]
fn checked_sub(&self, rhs: Self) -> CtOption<Self> {
let (result, underflow) = self.sbb(rhs, Limb::ZERO);
CtOption::new(result, underflow.is_zero())
}
}

impl Sub for Wrapping<Limb> {
type Output = Self;

Expand Down Expand Up @@ -143,7 +144,7 @@ impl SubAssign<&Checked<Limb>> for Checked<Limb> {

#[cfg(test)]
mod tests {
use crate::Limb;
use crate::{CheckedSub, Limb};

#[test]
fn sbb_no_borrow() {
Expand Down
34 changes: 34 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::fmt::Debug;
use core::ops::{Div, Rem};
use subtle::{
Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess,
CtOption,
};

#[cfg(feature = "rand")]
Expand All @@ -14,6 +15,9 @@ use rand_core::{CryptoRng, RngCore};
pub trait Integer:
'static
+ AsRef<[Limb]>
+ for<'a> CheckedAdd<&'a Self, Output = Self>
+ for<'a> CheckedSub<&'a Self, Output = Self>
+ for<'a> CheckedMul<&'a Self, Output = Self>
+ Copy
+ ConditionallySelectable
+ ConstantTimeEq
Expand Down Expand Up @@ -128,6 +132,36 @@ pub trait MulMod<Rhs = Self> {
fn mul_mod(&self, rhs: &Rhs, p: &Self, p_inv: Limb) -> Self::Output;
}

/// Checked addition.
pub trait CheckedAdd<Rhs = Self>: Sized {
/// Output type.
type Output;

/// Perform checked subtraction, returning a [`CtOption`] which `is_some`
/// only if the operation did not overflow.
fn checked_add(&self, rhs: Rhs) -> CtOption<Self>;
}

/// Checked multiplication.
pub trait CheckedMul<Rhs = Self>: Sized {
/// Output type.
type Output;

/// Perform checked multiplication, returning a [`CtOption`] which `is_some`
/// only if the operation did not overflow.
fn checked_mul(&self, rhs: Rhs) -> CtOption<Self>;
}

/// Checked substraction.
pub trait CheckedSub<Rhs = Self>: Sized {
/// Output type.
type Output;

/// Perform checked subtraction, returning a [`CtOption`] which `is_some`
/// only if the operation did not underflow.
fn checked_sub(&self, rhs: Rhs) -> CtOption<Self>;
}

/// Concatenate two numbers into a "wide" twice-width value, using the `rhs`
/// value as the least significant value.
pub trait Concat<Rhs = Self> {
Expand Down
13 changes: 7 additions & 6 deletions src/uint/add.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! [`UInt`] addition operations.

use super::UInt;
use crate::{Checked, Limb, Wrapping};
use crate::{Checked, CheckedAdd, Limb, UInt, Wrapping};
use core::ops::{Add, AddAssign};
use subtle::CtOption;

Expand All @@ -26,10 +25,12 @@ impl<const LIMBS: usize> UInt<LIMBS> {
pub const fn wrapping_add(&self, rhs: &Self) -> Self {
self.adc(rhs, Limb::ZERO).0
}
}

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

/// Perform checked addition, returning a [`CtOption`] which `is_some` only
/// if the operation did not overflow.
pub fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
let (result, carry) = self.adc(rhs, Limb::ZERO);
CtOption::new(result, carry.is_zero())
}
Expand Down Expand Up @@ -137,7 +138,7 @@ impl<const LIMBS: usize> AddAssign<&Checked<UInt<LIMBS>>> for Checked<UInt<LIMBS

#[cfg(test)]
mod tests {
use crate::{Limb, U128};
use crate::{CheckedAdd, Limb, U128};

#[test]
fn adc_no_carry() {
Expand Down
10 changes: 6 additions & 4 deletions src/uint/div.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,14 @@ impl<const LIMBS: usize> RemAssign<&NonZero<UInt<LIMBS>>> for Wrapping<UInt<LIMB
#[cfg(test)]
mod tests {
use super::*;
use crate::limb::HI_BIT;
use crate::Limb;
use crate::U256;
use crate::{limb::HI_BIT, Limb, U256};

#[cfg(feature = "rand")]
use {rand_chacha::ChaChaRng, rand_core::SeedableRng};
use {
crate::{CheckedMul, Random},
rand_chacha::ChaChaRng,
rand_core::SeedableRng,
};

#[test]
fn div_word() {
Expand Down
21 changes: 11 additions & 10 deletions src/uint/mul.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! [`UInt`] addition operations.

use super::UInt;
use crate::{Checked, Concat, Limb, Wrapping};
use crate::{Checked, CheckedMul, Concat, Limb, Wrapping};
use core::ops::{Mul, MulAssign};
use subtle::CtOption;

Expand Down Expand Up @@ -57,13 +57,6 @@ impl<const LIMBS: usize> UInt<LIMBS> {
self.mul_wide(rhs).0
}

/// Perform checked multiplication, returning a [`CtOption`] which `is_some`
/// only if the operation did not overflow.
pub fn checked_mul(&self, rhs: &Self) -> CtOption<Self> {
let (lo, hi) = self.mul_wide(rhs);
CtOption::new(lo, hi.is_zero())
}

/// Square self, returning a "wide" result.
pub fn square(&self) -> <Self as Concat>::Output
where
Expand All @@ -74,6 +67,15 @@ impl<const LIMBS: usize> UInt<LIMBS> {
}
}

impl<const LIMBS: usize> CheckedMul<&UInt<LIMBS>> for UInt<LIMBS> {
type Output = Self;

fn checked_mul(&self, rhs: &Self) -> CtOption<Self> {
let (lo, hi) = self.mul_wide(rhs);
CtOption::new(lo, hi.is_zero())
}
}

impl<const LIMBS: usize> Mul for Wrapping<UInt<LIMBS>> {
type Output = Self;

Expand Down Expand Up @@ -164,8 +166,7 @@ impl<const LIMBS: usize> MulAssign<&Checked<UInt<LIMBS>>> for Checked<UInt<LIMBS

#[cfg(test)]
mod tests {
use crate::Split;
use crate::U64;
use crate::{CheckedMul, Split, U64};

#[test]
fn mul_wide_zero_and_one() {
Expand Down
26 changes: 7 additions & 19 deletions src/uint/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ use crate::{Limb, NonZero, Random, RandomMod};
use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeLess;

// TODO(tarcieri): replace this `impl` block with `impl Random`/`impl RandomMod`
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
impl<const LIMBS: usize> UInt<LIMBS> {
impl<const LIMBS: usize> Random for UInt<LIMBS> {
/// Generate a cryptographically secure random [`UInt`].
pub fn random(mut rng: impl CryptoRng + RngCore) -> Self {
fn random(mut rng: impl CryptoRng + RngCore) -> Self {
let mut limbs = [Limb::ZERO; LIMBS];

for limb in &mut limbs {
Expand All @@ -19,7 +18,10 @@ impl<const LIMBS: usize> UInt<LIMBS> {

limbs.into()
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
impl<const LIMBS: usize> RandomMod for UInt<LIMBS> {
/// Generate a cryptographically secure random [`UInt`] which is less than
/// a given `modulus`.
///
Expand All @@ -31,7 +33,7 @@ impl<const LIMBS: usize> UInt<LIMBS> {
/// issue so long as the underlying random number generator is truly a
/// [`CryptoRng`], where previous outputs are unrelated to subsequent
/// outputs and do not reveal information about the RNG's internal state.
pub fn random_mod(mut rng: impl CryptoRng + RngCore, modulus: &NonZero<Self>) -> Self {
fn random_mod(mut rng: impl CryptoRng + RngCore, modulus: &NonZero<Self>) -> Self {
let mut n = Self::ZERO;

// TODO(tarcieri): use `div_ceil` when available
Expand Down Expand Up @@ -64,23 +66,9 @@ impl<const LIMBS: usize> UInt<LIMBS> {
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
impl<const LIMBS: usize> Random for UInt<LIMBS> {
fn random(rng: impl CryptoRng + RngCore) -> Self {
Self::random(rng)
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
impl<const LIMBS: usize> RandomMod for UInt<LIMBS> {
fn random_mod(rng: impl CryptoRng + RngCore, modulus: &NonZero<Self>) -> Self {
Self::random_mod(rng, modulus)
}
}

#[cfg(test)]
mod tests {
use crate::{NonZero, U256};
use crate::{NonZero, RandomMod, U256};
use rand_core::SeedableRng;

#[test]
Expand Down
5 changes: 2 additions & 3 deletions src/uint/sqrt.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! [`UInt`] square root operations.

use super::UInt;
use crate::limb::LimbUInt;
use crate::Limb;
use crate::{Limb, LimbUInt};
use subtle::{ConstantTimeEq, CtOption};

impl<const LIMBS: usize> UInt<LIMBS> {
Expand Down Expand Up @@ -71,7 +70,7 @@ mod tests {

#[cfg(feature = "rand")]
use {
crate::U512,
crate::{CheckedMul, Random, U512},
rand_chacha::ChaChaRng,
rand_core::{RngCore, SeedableRng},
};
Expand Down
Loading

0 comments on commit 6feebc0

Please sign in to comment.