Skip to content

Commit d8158bc

Browse files
authored
Have pow_montgomery_form_amm reduce its output (#1133)
Extracts an `almost_montgomery_reduce` function which accepts a `&mut UintRef` as input and conditionally subtracts the modulus twice, as explained in extensive comments. This required porting `conditional_borrowing_sub_assign` to `UintRef`, as well as adding a `CtLt` impl to `UintRef`. `Uint::lt` and `BoxedUint::conditional_borrowing_sub_assign` now both delegate these functions to `UintRef`. This also adds a `AsRef<GenericMontyParams<Self::Integer>>` bound to `Monty::Params` which can be used to obtain the underlying struct, which is used to query the params for the modulus in `pow_montgomery_form_amm`
1 parent bd8a0a4 commit d8158bc

File tree

12 files changed

+129
-62
lines changed

12 files changed

+129
-62
lines changed

src/modular/boxed_monty_form.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ impl BoxedMontyParams {
127127
}
128128
}
129129

130+
impl AsRef<GenericMontyParams<BoxedUint>> for BoxedMontyParams {
131+
fn as_ref(&self) -> &GenericMontyParams<BoxedUint> {
132+
&self.0
133+
}
134+
}
135+
130136
impl Debug for BoxedMontyParams {
131137
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132138
self.0.debug_struct(f.debug_struct("BoxedMontyParams"))

src/modular/boxed_monty_form/mul.rs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
use super::{BoxedMontyForm, BoxedMontyParams};
99
use crate::{
10-
AmmMultiplier, BoxedUint, CtLt, Limb, MontyMultiplier, Mul, MulAssign, Square, SquareAssign,
10+
AmmMultiplier, BoxedUint, Limb, MontyMultiplier, Mul, MulAssign, Square, SquareAssign,
1111
modular::mul::montgomery_multiply_inner, word,
1212
};
1313

@@ -49,32 +49,6 @@ impl BoxedMontyForm {
4949
params: self.params.clone(),
5050
}
5151
}
52-
53-
/// Instantiate [`BoxedMontyForm`] from the result of an "Almost Montgomery Multiplication".
54-
pub(crate) fn from_amm(mut z: BoxedUint, params: BoxedMontyParams) -> Self {
55-
// Ensure the output is properly reduced.
56-
//
57-
// Using the properties of `almost_montgomery_mul()` (see its documentation):
58-
// - We have an incoming `x` which is fully reduced (`floor(x / modulus) = 0`).
59-
// - We build an array of `powers` which are produced by multiplying the previous power by
60-
// `x`, so for each power `floor(power / modulus) <= 1`.
61-
// - Then we take turns squaring the accumulator `z` (bringing `floor(z / modulus)` to 1
62-
// regardless of the previous reduction level) and multiplying by a power of `x`
63-
// (bringing `floor(z / modulus)` to at most 2).
64-
// - Then we either exit the loop, or square again, which brings `floor(z / modulus)` back
65-
// to 1.
66-
//
67-
// We now need to reduce `z` at most twice to bring it within `[0, modulus)`.
68-
let modulus = params.modulus();
69-
z.conditional_borrowing_sub_assign(modulus, !z.ct_lt(modulus));
70-
z.conditional_borrowing_sub_assign(modulus, !z.ct_lt(modulus));
71-
debug_assert!(&z < modulus);
72-
73-
Self {
74-
montgomery_form: z,
75-
params,
76-
}
77-
}
7852
}
7953

8054
impl Mul<&BoxedMontyForm> for &BoxedMontyForm {

src/modular/boxed_monty_form/pow.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@ impl BoxedMontyForm {
1717
/// NOTE: `exponent_bits` may be leaked in the time pattern.
1818
#[must_use]
1919
pub fn pow_bounded_exp(&self, exponent: &BoxedUint, exponent_bits: u32) -> Self {
20-
let z =
21-
pow_montgomery_form_amm(&self.montgomery_form, exponent, exponent_bits, &self.params);
22-
23-
Self::from_amm(z, self.params.clone())
20+
Self {
21+
montgomery_form: pow_montgomery_form_amm(
22+
&self.montgomery_form,
23+
exponent,
24+
exponent_bits,
25+
&self.params,
26+
),
27+
params: self.params.clone(),
28+
}
2429
}
2530
}
2631

src/modular/monty_params.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ impl<U: Unsigned> GenericMontyParams<U> {
7070
}
7171
}
7272

73+
impl<U: Unsigned> AsRef<Self> for GenericMontyParams<U> {
74+
fn as_ref(&self) -> &Self {
75+
self
76+
}
77+
}
78+
7379
impl<U: Unsigned> CtAssign for GenericMontyParams<U> {
7480
fn ct_assign(&mut self, other: &Self, choice: Choice) {
7581
self.modulus.ct_assign(&other.modulus, choice);

src/modular/mul.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
use super::reduction::montgomery_reduction;
2-
use crate::{Limb, Odd, Uint, WideWord, Word};
2+
use crate::{CtLt, Limb, Odd, Uint, UintRef, WideWord, Word};
3+
4+
/// Ensure the output of an "almost" Montgomery multiplication is properly reduced.
5+
///
6+
/// Using the properties of `almost_montgomery_mul()` (see its documentation):
7+
/// - We have an incoming `x` which is fully reduced (`floor(x / modulus) = 0`).
8+
/// - We build an array of `powers` which are produced by multiplying the previous power by
9+
/// `x`, so for each power `floor(power / modulus) <= 1`.
10+
/// - Then we take turns squaring the accumulator `z` (bringing `floor(z / modulus)` to 1
11+
/// regardless of the previous reduction level) and multiplying by a power of `x`
12+
/// (bringing `floor(z / modulus)` to at most 2).
13+
/// - Then we either exit the loop, or square again, which brings `floor(z / modulus)` back
14+
/// to 1.
15+
///
16+
/// We now need to reduce `z` at most twice to bring it within `[0, modulus)`.
17+
pub(crate) fn almost_montgomery_reduce(z: &mut UintRef, modulus: &UintRef) {
18+
z.conditional_borrowing_sub_assign(modulus, !z.ct_lt(modulus));
19+
z.conditional_borrowing_sub_assign(modulus, !z.ct_lt(modulus));
20+
}
321

422
/// Based on Algorithm 14.36 in Handbook of Applied Cryptography
523
/// <https://cacr.uwaterloo.ca/hac/about/chap14.pdf>

src/modular/pow.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::MontyParams;
2-
use super::mul::{mul_montgomery_form, square_montgomery_form};
2+
use super::mul::{almost_montgomery_reduce, mul_montgomery_form, square_montgomery_form};
33
use crate::{AmmMultiplier, CtEq, Limb, Monty, Uint, Unsigned, Word, word};
44
use core::{array, mem};
55

@@ -32,11 +32,7 @@ pub const fn pow_montgomery_form<
3232

3333
/// Performs modular exponentiation using "Almost Montgomery Multiplication".
3434
///
35-
/// NOTE: the resulting output will be reduced to the *bit length* of the modulus, but not fully
36-
/// reduced and may exceed the modulus.
37-
///
38-
/// A final reduction is required to ensure AMM results are fully reduced, and should not be exposed
39-
/// outside the internals of this crate.
35+
/// Returns a result which has been fully reduced by the modulus specified in `params`.
4036
///
4137
/// `exponent_bits` represents the length of the exponent in bits.
4238
///
@@ -121,6 +117,7 @@ where
121117
}
122118
}
123119

120+
almost_montgomery_reduce(z.as_mut(), params.as_ref().modulus().as_uint_ref());
124121
z
125122
}
126123

src/traits.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ pub use num_traits::{
1010
WrappingSub,
1111
};
1212

13-
use crate::{Choice, CtOption, Limb, NonZero, Odd, Reciprocal, UintRef, modular::Retrieve};
13+
use crate::{
14+
Choice, CtOption, Limb, NonZero, Odd, Reciprocal, UintRef,
15+
modular::{GenericMontyParams, Retrieve},
16+
};
1417
use core::fmt::{self, Debug};
1518

1619
#[cfg(feature = "rand_core")]
@@ -1137,7 +1140,14 @@ pub trait Monty:
11371140
type Multiplier<'a>: Debug + Clone + MontyMultiplier<'a, Monty = Self>;
11381141

11391142
/// The precomputed data needed for this representation.
1140-
type Params: 'static + Clone + Debug + Eq + Sized + Send + Sync;
1143+
type Params: 'static
1144+
+ AsRef<GenericMontyParams<Self::Integer>>
1145+
+ Clone
1146+
+ Debug
1147+
+ Eq
1148+
+ Sized
1149+
+ Send
1150+
+ Sync;
11411151

11421152
/// Create the precomputed data for Montgomery representation of integers modulo `modulus`,
11431153
/// variable time in `modulus`.

src/uint/boxed/sub.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! [`BoxedUint`] subtraction operations.
22
33
use crate::{
4-
BoxedUint, CheckedSub, Choice, CtOption, CtSelect, Limb, Sub, SubAssign, U64, U128, Uint,
5-
Wrapping, WrappingSub,
4+
BoxedUint, CheckedSub, Choice, CtOption, Limb, Sub, SubAssign, U64, U128, Uint, Wrapping,
5+
WrappingSub,
66
};
77

88
impl BoxedUint {
@@ -61,18 +61,8 @@ impl BoxedUint {
6161
rhs: &Self,
6262
choice: Choice,
6363
) -> Choice {
64-
debug_assert!(self.bits_precision() <= rhs.bits_precision());
65-
let mask = Limb::ct_select(&Limb::ZERO, &Limb::MAX, choice);
66-
let mut borrow = Limb::ZERO;
67-
68-
for i in 0..self.nlimbs() {
69-
let masked_rhs = *rhs.limbs.get(i).unwrap_or(&Limb::ZERO) & mask;
70-
let (limb, b) = self.limbs[i].borrowing_sub(masked_rhs, borrow);
71-
self.limbs[i] = limb;
72-
borrow = b;
73-
}
74-
75-
borrow.lsb_to_choice()
64+
self.as_mut_uint_ref()
65+
.conditional_borrowing_sub_assign(rhs.as_uint_ref(), choice)
7666
}
7767
}
7868

src/uint/cmp.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Constant-time unless explicitly noted otherwise.
44
55
use super::Uint;
6-
use crate::{Choice, CtEq, Limb, word};
6+
use crate::{Choice, CtEq, Limb, UintRef, word};
77
use core::cmp::Ordering;
88

99
impl<const LIMBS: usize> Uint<LIMBS> {
@@ -46,11 +46,7 @@ impl<const LIMBS: usize> Uint<LIMBS> {
4646
/// Returns the truthy value if `self < rhs` and the falsy value otherwise.
4747
#[inline]
4848
pub(crate) const fn lt(lhs: &Self, rhs: &Self) -> Choice {
49-
// We could use the same approach as in Limb::ct_lt(),
50-
// but since we have to use Uint::wrapping_sub(), which calls `borrowing_sub()`,
51-
// there are no savings compared to just calling `borrowing_sub()` directly.
52-
let (_res, borrow) = lhs.borrowing_sub(rhs, Limb::ZERO);
53-
word::choice_from_mask(borrow.0)
49+
UintRef::lt(lhs.as_uint_ref(), rhs.as_uint_ref())
5450
}
5551

5652
/// Returns the truthy value if `self <= rhs` and the falsy value otherwise.

src/uint/ref_type/cmp.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,41 @@ impl UintRef {
4747
}
4848
true
4949
}
50+
51+
/// Returns the truthy value if `self < rhs` and the falsy value otherwise.
52+
///
53+
/// # Panics
54+
/// If `lhs` and `rhs` are not the same size
55+
#[inline]
56+
pub(crate) const fn lt(lhs: &Self, rhs: &Self) -> Choice {
57+
// TODO(tarcieri): support for differently sized inputs?
58+
debug_assert!(
59+
lhs.bits_precision() == rhs.bits_precision(),
60+
"lhs and rhs sizes differ"
61+
);
62+
63+
// NOTE: this is effectively a `borrowing_sub` that only computes the borrow
64+
let mut i = 0;
65+
let mut borrow = Limb::ZERO;
66+
67+
while i < lhs.nlimbs() {
68+
borrow = lhs.limbs[i].borrowing_sub(rhs.limbs[i], borrow).1;
69+
i += 1;
70+
}
71+
72+
borrow.lsb_to_choice()
73+
}
74+
}
75+
76+
#[cfg(test)]
77+
mod tests {
78+
use super::UintRef;
79+
use crate::Limb;
80+
81+
#[test]
82+
fn lt() {
83+
let lesser = UintRef::new(&[Limb::ZERO, Limb::ZERO, Limb::ZERO]);
84+
let greater = UintRef::new(&[Limb::ZERO, Limb::ONE, Limb::ZERO]);
85+
assert!(UintRef::lt(lesser, greater).to_bool());
86+
}
5087
}

0 commit comments

Comments
 (0)