From df0fe8639039a4df7f646d1fdd01075693928f03 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 26 Nov 2023 12:16:21 -0700 Subject: [PATCH] BoxedUint: impl `AddMod` --- src/boxed/uint.rs | 1 + src/boxed/uint/add_mod.rs | 72 +++++++++++++++++++++++++++++++++++++++ src/uint/add_mod.rs | 2 -- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/boxed/uint/add_mod.rs diff --git a/src/boxed/uint.rs b/src/boxed/uint.rs index 1ae097df2..d978f6a59 100644 --- a/src/boxed/uint.rs +++ b/src/boxed/uint.rs @@ -1,6 +1,7 @@ //! Heap-allocated big unsigned integers. mod add; +mod add_mod; mod bit_and; mod cmp; pub(crate) mod encoding; diff --git a/src/boxed/uint/add_mod.rs b/src/boxed/uint/add_mod.rs new file mode 100644 index 000000000..b5dd15d79 --- /dev/null +++ b/src/boxed/uint/add_mod.rs @@ -0,0 +1,72 @@ +//! [`BoxedUint`] modular addition operations. + +use crate::{AddMod, BoxedUint, Limb}; + +impl BoxedUint { + /// Computes `self + rhs mod p`. + /// + /// Assumes `self + rhs` as unbounded integer is `< 2p`. + pub fn add_mod(&self, rhs: &BoxedUint, p: &BoxedUint) -> BoxedUint { + debug_assert_eq!(self.nlimbs(), p.nlimbs()); + debug_assert_eq!(rhs.nlimbs(), p.nlimbs()); + debug_assert!(self < p); + debug_assert!(rhs < p); + + let (w, carry) = self.adc(rhs, Limb::ZERO); + + // Attempt to subtract the modulus, to ensure the result is in the field. + let (w, borrow) = w.sbb(p, Limb::ZERO); + let (_, borrow) = carry.sbb(Limb::ZERO, borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the + // modulus. + let mask = BoxedUint::from_words(vec![borrow.0; p.nlimbs()]); + + w.wrapping_add(&p.bitand(&mask)) + } +} + +impl AddMod for BoxedUint { + type Output = Self; + + fn add_mod(&self, rhs: &Self, p: &Self) -> Self { + self.add_mod(rhs, p) + } +} + +#[cfg(test)] +mod tests { + use super::BoxedUint; + use hex_literal::hex; + + // TODO(tarcieri): proptests + + #[test] + fn add_mod_nist_p256() { + let a = BoxedUint::from_be_slice( + &hex!("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"), + 256, + ) + .unwrap(); + let b = BoxedUint::from_be_slice( + &hex!("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"), + 256, + ) + .unwrap(); + let n = BoxedUint::from_be_slice( + &hex!("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"), + 256, + ) + .unwrap(); + + let actual = a.add_mod(&b, &n); + let expected = BoxedUint::from_be_slice( + &hex!("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"), + 256, + ) + .unwrap(); + + assert_eq!(expected, actual); + } +} diff --git a/src/uint/add_mod.rs b/src/uint/add_mod.rs index 091ba4634..7674bd6b2 100644 --- a/src/uint/add_mod.rs +++ b/src/uint/add_mod.rs @@ -52,8 +52,6 @@ mod tests { use crate::{Limb, NonZero, Random, RandomMod, Uint, U256}; use rand_core::SeedableRng; - // TODO(tarcieri): additional tests + proptests - #[test] fn add_mod_nist_p256() { let a =