@@ -10,46 +10,70 @@ const fn muladdcarry(x: Word, y: Word, z: Word, w: Word) -> (Word, Word) {
1010 ( ( res >> Word :: BITS ) as Word , res as Word )
1111}
1212
13- /// Algorithm 14.32 in Handbook of Applied Cryptography <https://cacr.uwaterloo.ca/hac/about/chap14.pdf>
14- pub const fn montgomery_reduction < const LIMBS : usize > (
15- lower_upper : & ( Uint < LIMBS > , Uint < LIMBS > ) ,
16- modulus : & Uint < LIMBS > ,
17- mod_neg_inv : Limb ,
18- ) -> Uint < LIMBS > {
19- let ( mut lower, mut upper) = * lower_upper;
13+ /// Impl the core Montgomery reduction algorithm.
14+ ///
15+ /// This is implemented as a macro to abstract over `const fn` and boxed use cases, since the latter
16+ /// needs mutable references and thus the unstable `const_mut_refs` feature (rust-lang/rust#57349).
17+ // TODO(tarcieri): change this into a `const fn` when `const_mut_refs` is stable
18+ macro_rules! impl_montgomery_reduction {
19+ ( $upper: expr, $lower: expr, $modulus: expr, $mod_neg_inv: expr, $limbs: expr) => { {
20+ let mut meta_carry = Limb ( 0 ) ;
21+ let mut new_sum;
2022
21- let mut meta_carry = Limb ( 0 ) ;
22- let mut new_sum;
23+ let mut i = 0 ;
24+ while i < $limbs {
25+ let u = $lower[ i] . 0 . wrapping_mul( $mod_neg_inv. 0 ) ;
2326
24- let mut i = 0 ;
25- while i < LIMBS {
26- let u = lower. limbs [ i] . 0 . wrapping_mul ( mod_neg_inv. 0 ) ;
27+ let ( mut carry, _) = muladdcarry( u, $modulus[ 0 ] . 0 , $lower[ i] . 0 , 0 ) ;
28+ let mut new_limb;
2729
28- let ( mut carry, _) = muladdcarry ( u, modulus. limbs [ 0 ] . 0 , lower. limbs [ i] . 0 , 0 ) ;
29- let mut new_limb;
30+ let mut j = 1 ;
31+ while j < ( $limbs - i) {
32+ ( carry, new_limb) = muladdcarry( u, $modulus[ j] . 0 , $lower[ i + j] . 0 , carry) ;
33+ $lower[ i + j] = Limb ( new_limb) ;
34+ j += 1 ;
35+ }
36+ while j < $limbs {
37+ ( carry, new_limb) = muladdcarry( u, $modulus[ j] . 0 , $upper[ i + j - $limbs] . 0 , carry) ;
38+ $upper[ i + j - $limbs] = Limb ( new_limb) ;
39+ j += 1 ;
40+ }
3041
31- let mut j = 1 ;
32- while j < ( LIMBS - i) {
33- ( carry, new_limb) = muladdcarry ( u, modulus. limbs [ j] . 0 , lower. limbs [ i + j] . 0 , carry) ;
34- lower. limbs [ i + j] = Limb ( new_limb) ;
35- j += 1 ;
36- }
37- while j < LIMBS {
38- ( carry, new_limb) =
39- muladdcarry ( u, modulus. limbs [ j] . 0 , upper. limbs [ i + j - LIMBS ] . 0 , carry) ;
40- upper. limbs [ i + j - LIMBS ] = Limb ( new_limb) ;
41- j += 1 ;
42+ ( new_sum, meta_carry) = $upper[ i] . adc( Limb ( carry) , meta_carry) ;
43+ $upper[ i] = new_sum;
44+
45+ i += 1 ;
4246 }
4347
44- ( new_sum, meta_carry) = upper. limbs [ i] . adc ( Limb ( carry) , meta_carry) ;
45- upper. limbs [ i] = new_sum;
48+ meta_carry
49+ } } ;
50+ }
4651
47- i += 1 ;
48- }
52+ /// Algorithm 14.32 in Handbook of Applied Cryptography <https://cacr.uwaterloo.ca/hac/about/chap14.pdf>
53+ pub const fn montgomery_reduction < const LIMBS : usize > (
54+ lower_upper : & ( Uint < LIMBS > , Uint < LIMBS > ) ,
55+ modulus : & Uint < LIMBS > ,
56+ mod_neg_inv : Limb ,
57+ ) -> Uint < LIMBS > {
58+ let ( mut lower, mut upper) = * lower_upper;
59+ let meta_carry =
60+ impl_montgomery_reduction ! ( upper. limbs, lower. limbs, & modulus. limbs, mod_neg_inv, LIMBS ) ;
4961
5062 // Division is simply taking the upper half of the limbs
5163 // Final reduction (at this point, the value is at most 2 * modulus,
5264 // so `meta_carry` is either 0 or 1)
53-
5465 upper. sub_mod_with_carry ( meta_carry, modulus, modulus)
5566}
67+
68+ /// Shim used by [`BoxedUint`] to perform a Montgomery reduction.
69+ #[ cfg( feature = "alloc" ) ]
70+ pub ( crate ) fn montgomery_reduction_core (
71+ lower : & mut [ Limb ] ,
72+ upper : & mut [ Limb ] ,
73+ modulus : & [ Limb ] ,
74+ mod_neg_inv : Limb ,
75+ ) -> Limb {
76+ debug_assert_eq ! ( lower. len( ) , modulus. len( ) ) ;
77+ debug_assert_eq ! ( upper. len( ) , modulus. len( ) ) ;
78+ impl_montgomery_reduction ! ( upper, lower, modulus, mod_neg_inv, modulus. len( ) )
79+ }
0 commit comments