Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/uint/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod add_mod;
mod bit_and;
mod bits;
mod cmp;
mod div;
pub(crate) mod encoding;
mod modular;
mod mul;
Expand Down Expand Up @@ -170,6 +171,19 @@ impl BoxedUint {
Self { limbs }
}

/// Widen this type's precision to the given number of bits.
///
/// Panics if `bits_precision` is not a multiple of `Limb::BITS` or smaller than the current
/// precision.
pub fn widen(&self, bits_precision: usize) -> BoxedUint {
assert!(bits_precision % Limb::BITS == 0);
assert!(bits_precision >= self.bits_precision());

let mut ret = BoxedUint::zero_with_precision(bits_precision);
ret.limbs[..self.nlimbs()].copy_from_slice(&self.limbs);
ret
}

/// Perform a carry chain-like operation over the limbs of the inputs,
/// constructing a result from the returned limbs and carry which is
/// widened to the same width as the widest input.
Expand Down
44 changes: 44 additions & 0 deletions src/uint/boxed/div.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! [`BoxedUint`] division operations.

use crate::{BoxedUint, Limb};
use subtle::{ConstantTimeEq, CtOption};

impl BoxedUint {
/// Computes self % rhs, returns the remainder.
///
/// Variable-time with respect to `rhs`.
///
/// Panics if `self` and `rhs` have different precisions.
// TODO(tarcieri): make infallible by making `rhs` into `NonZero`; don't panic
pub fn rem_vartime(&self, rhs: &Self) -> CtOption<Self> {
debug_assert_eq!(self.nlimbs(), rhs.nlimbs());
let mb = rhs.bits();
let mut bd = self.bits_precision() - mb;
let mut rem = self.clone();
let mut c = rhs.shl_vartime(bd);

loop {
let (r, borrow) = rem.sbb(&c, Limb::ZERO);
rem = Self::conditional_select(&r, &rem, !borrow.ct_eq(&Limb::ZERO));
if bd == 0 {
break;
}
bd -= 1;
c = c.shr_vartime(1);
}

CtOption::new(rem, !(mb as u32).ct_eq(&0))
}
}

#[cfg(test)]
mod tests {
use super::BoxedUint;

#[test]
fn rem_vartime() {
let n = BoxedUint::from(0xFFEECCBBAA99887766u128);
let p = BoxedUint::from(997u128);
assert_eq!(BoxedUint::from(648u128), n.rem_vartime(&p).unwrap());
}
}
26 changes: 26 additions & 0 deletions tests/boxed_uint_proptests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,26 @@ fn to_uint(big_uint: BigUint) -> BoxedUint {
}

prop_compose! {
/// Generate a random `uint()`.
fn uint()(mut bytes in any::<Vec<u8>>()) -> BoxedUint {
let extra = bytes.len() % Limb::BYTES;
let bytes_precision = bytes.len() - extra;
bytes.truncate(bytes_precision);
BoxedUint::from_be_slice(&bytes, bytes_precision * 8).unwrap()
}
}
prop_compose! {
/// Generate a pair of random uints with the same precision.
fn uint_pair()(mut a in uint(), mut b in uint()) -> (BoxedUint, BoxedUint) {
if a.bits_precision() > b.bits_precision() {
b = b.widen(a.bits_precision());
} else if a.bits_precision() < b.bits_precision() {
a = a.widen(b.bits_precision());
}

(a, b)
}
}

proptest! {
#[test]
Expand Down Expand Up @@ -58,4 +71,17 @@ proptest! {

prop_assert_eq!(expected, to_biguint(&actual));
}

#[test]
fn rem_vartime((a, b) in uint_pair()) {
if bool::from(!b.is_zero()) {
let a_bi = to_biguint(&a);
let b_bi = to_biguint(&b);

let expected = a_bi % b_bi;
let actual = a.rem_vartime(&b).unwrap();

prop_assert_eq!(expected, to_biguint(&actual));
}
}
}