Skip to content

Commit 8e3092c

Browse files
Add pow_vartime for MontyForm, ConstMontyForm (#1118)
This could be further optimized for small exponents where it doesn't make sense to use a precomputed table. That's probably true for the non-vartime version as well. I think this also enables a vartime multi-exponentiation. --------- Signed-off-by: Andrew Whitehead <cywolf@gmail.com>
1 parent 17084dd commit 8e3092c

File tree

5 files changed

+275
-76
lines changed

5 files changed

+275
-76
lines changed

src/modular/const_monty_form/pow.rs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,31 @@ impl<MOD: ConstMontyParams<LIMBS>, const LIMBS: usize> ConstMontyForm<MOD, LIMBS
3131
exponent_bits: u32,
3232
) -> ConstMontyForm<MOD, LIMBS> {
3333
Self {
34-
montgomery_form: pow_montgomery_form(
34+
montgomery_form: pow_montgomery_form::<LIMBS, RHS_LIMBS, false>(
3535
&self.montgomery_form,
3636
exponent,
3737
exponent_bits,
38-
&MOD::PARAMS.modulus,
39-
&MOD::PARAMS.one,
40-
MOD::PARAMS.mod_neg_inv(),
38+
&MOD::PARAMS,
39+
),
40+
phantom: core::marker::PhantomData,
41+
}
42+
}
43+
44+
/// Raises to the `exponent` power.
45+
///
46+
/// This method is variable time in `exponent`.
47+
#[must_use]
48+
pub const fn pow_vartime<const RHS_LIMBS: usize>(
49+
&self,
50+
exponent: &Uint<RHS_LIMBS>,
51+
) -> ConstMontyForm<MOD, LIMBS> {
52+
let exponent_bits = exponent.bits_vartime();
53+
Self {
54+
montgomery_form: pow_montgomery_form::<LIMBS, RHS_LIMBS, true>(
55+
&self.montgomery_form,
56+
exponent,
57+
exponent_bits,
58+
&MOD::PARAMS,
4159
),
4260
phantom: core::marker::PhantomData,
4361
}
@@ -71,12 +89,10 @@ impl<const N: usize, MOD: ConstMontyParams<LIMBS>, const LIMBS: usize, const RHS
7189
}
7290

7391
Self {
74-
montgomery_form: multi_exponentiate_montgomery_form_array(
92+
montgomery_form: multi_exponentiate_montgomery_form_array::<LIMBS, RHS_LIMBS, N, false>(
7593
&bases_and_exponents_montgomery_form,
7694
exponent_bits,
77-
&MOD::PARAMS.modulus,
78-
&MOD::PARAMS.one,
79-
MOD::PARAMS.mod_neg_inv(),
95+
&MOD::PARAMS,
8096
),
8197
phantom: core::marker::PhantomData,
8298
}
@@ -97,12 +113,10 @@ impl<MOD: ConstMontyParams<LIMBS>, const LIMBS: usize, const RHS_LIMBS: usize>
97113
.map(|(base, exp)| (base.montgomery_form, *exp))
98114
.collect();
99115
Self {
100-
montgomery_form: multi_exponentiate_montgomery_form_slice(
116+
montgomery_form: multi_exponentiate_montgomery_form_slice::<LIMBS, RHS_LIMBS, false>(
101117
&bases_and_exponents,
102118
exponent_bits,
103-
&MOD::PARAMS.modulus,
104-
&MOD::PARAMS.one,
105-
MOD::PARAMS.mod_neg_inv(),
119+
&MOD::PARAMS,
106120
),
107121
phantom: core::marker::PhantomData,
108122
}
@@ -124,6 +138,18 @@ mod tests {
124138

125139
const_monty_form!(Fe, Modulus);
126140

141+
#[test]
142+
fn test_powmod_zero() {
143+
let base = U256::from(105u64);
144+
let base_mod = Fe::new(&base);
145+
146+
let res = base_mod.pow(&U256::ZERO);
147+
let res_vartime = base_mod.pow_vartime(&U256::ZERO);
148+
149+
assert_eq!(res.retrieve(), U256::ONE);
150+
assert_eq!(res_vartime.retrieve(), U256::ONE);
151+
}
152+
127153
#[test]
128154
fn test_powmod_small_base() {
129155
let base = U256::from(105u64);
@@ -133,10 +159,12 @@ mod tests {
133159
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
134160

135161
let res = base_mod.pow(&exponent);
162+
let res_vartime = base_mod.pow_vartime(&exponent);
136163

137164
let expected =
138165
U256::from_be_hex("7B2CD7BDDD96C271E6F232F2F415BB03FE2A90BD6CCCEA5E94F1BFD064993766");
139166
assert_eq!(res.retrieve(), expected);
167+
assert_eq!(res_vartime.retrieve(), expected);
140168
}
141169

142170
#[test]
@@ -148,10 +176,12 @@ mod tests {
148176
let exponent = U256::from(105u64);
149177

150178
let res = base_mod.pow(&exponent);
179+
let res_vartime = base_mod.pow_vartime(&exponent);
151180

152181
let expected =
153182
U256::from_be_hex("89E2A4E99F649A5AE2C18068148C355CA927B34A3245C938178ED00D6EF218AA");
154183
assert_eq!(res.retrieve(), expected);
184+
assert_eq!(res_vartime.retrieve(), expected);
155185
}
156186

157187
#[test]
@@ -164,10 +194,12 @@ mod tests {
164194
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
165195

166196
let res = base_mod.pow(&exponent);
197+
let res_vartime = base_mod.pow(&exponent);
167198

168199
let expected =
169200
U256::from_be_hex("3681BC0FEA2E5D394EB178155A127B0FD2EF405486D354251C385BDD51B9D421");
170201
assert_eq!(res.retrieve(), expected);
202+
assert_eq!(res_vartime.retrieve(), expected);
171203
}
172204

173205
#[test]

src/modular/monty_form/pow.rs

Lines changed: 162 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,31 @@ impl<const LIMBS: usize> MontyForm<LIMBS> {
3131
exponent_bits: u32,
3232
) -> Self {
3333
Self {
34-
montgomery_form: pow_montgomery_form(
34+
montgomery_form: pow_montgomery_form::<LIMBS, RHS_LIMBS, false>(
3535
&self.montgomery_form,
3636
exponent,
3737
exponent_bits,
38-
&self.params.modulus,
39-
&self.params.one,
40-
self.params.mod_neg_inv(),
38+
&self.params,
39+
),
40+
params: self.params,
41+
}
42+
}
43+
44+
/// Raises to the `exponent` power.
45+
///
46+
/// This method is variable time in `exponent`.
47+
#[must_use]
48+
pub const fn pow_vartime<const RHS_LIMBS: usize>(
49+
&self,
50+
exponent: &Uint<RHS_LIMBS>,
51+
) -> MontyForm<LIMBS> {
52+
let exponent_bits = exponent.bits_vartime();
53+
Self {
54+
montgomery_form: pow_montgomery_form::<LIMBS, RHS_LIMBS, true>(
55+
&self.montgomery_form,
56+
exponent,
57+
exponent_bits,
58+
&self.params,
4159
),
4260
params: self.params,
4361
}
@@ -74,12 +92,10 @@ impl<const N: usize, const LIMBS: usize, const RHS_LIMBS: usize>
7492
}
7593

7694
Self {
77-
montgomery_form: multi_exponentiate_montgomery_form_array(
95+
montgomery_form: multi_exponentiate_montgomery_form_array::<LIMBS, RHS_LIMBS, N, false>(
7896
&bases_and_exponents_montgomery_form,
7997
exponent_bits,
80-
&params.modulus,
81-
&params.one,
82-
params.mod_neg_inv(),
98+
&params,
8399
),
84100
params,
85101
}
@@ -105,14 +121,148 @@ impl<const LIMBS: usize, const RHS_LIMBS: usize>
105121
.map(|(base, exp)| (base.montgomery_form, *exp))
106122
.collect();
107123
Self {
108-
montgomery_form: multi_exponentiate_montgomery_form_slice(
124+
montgomery_form: multi_exponentiate_montgomery_form_slice::<LIMBS, RHS_LIMBS, false>(
109125
&bases_and_exponents,
110126
exponent_bits,
111-
&params.modulus,
112-
&params.one,
113-
params.mod_neg_inv(),
127+
&params,
114128
),
115129
params,
116130
}
117131
}
118132
}
133+
134+
#[cfg(test)]
135+
mod tests {
136+
use crate::traits::MultiExponentiate;
137+
use crate::{
138+
U256,
139+
modular::{MontyForm, MontyParams},
140+
};
141+
142+
const PARAMS: MontyParams<{ U256::LIMBS }> = MontyParams::new_vartime(
143+
U256::from_be_hex("9CC24C5DF431A864188AB905AC751B727C9447A8E99E6366E1AD78A21E8D882B")
144+
.to_odd()
145+
.expect_copied("ensured odd"),
146+
);
147+
148+
#[test]
149+
fn test_powmod_zero() {
150+
let base = U256::from(105u64);
151+
let base_mod = MontyForm::new(&base, &PARAMS);
152+
153+
let res = base_mod.pow(&U256::ZERO);
154+
let res_vartime = base_mod.pow_vartime(&U256::ZERO);
155+
156+
assert_eq!(res.retrieve(), U256::ONE);
157+
assert_eq!(res_vartime.retrieve(), U256::ONE);
158+
}
159+
160+
#[test]
161+
fn test_powmod_small_base() {
162+
let base = U256::from(105u64);
163+
let base_mod = MontyForm::new(&base, &PARAMS);
164+
165+
let exponent =
166+
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
167+
168+
let res = base_mod.pow(&exponent);
169+
let res_vartime = base_mod.pow_vartime(&exponent);
170+
171+
let expected =
172+
U256::from_be_hex("7B2CD7BDDD96C271E6F232F2F415BB03FE2A90BD6CCCEA5E94F1BFD064993766");
173+
assert_eq!(res.retrieve(), expected);
174+
assert_eq!(res_vartime.retrieve(), expected);
175+
}
176+
177+
#[test]
178+
fn test_powmod_small_exponent() {
179+
let base =
180+
U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4");
181+
let base_mod = MontyForm::new(&base, &PARAMS);
182+
183+
let exponent = U256::from(105u64);
184+
185+
let res = base_mod.pow(&exponent);
186+
let res_vartime = base_mod.pow_vartime(&exponent);
187+
188+
let expected =
189+
U256::from_be_hex("89E2A4E99F649A5AE2C18068148C355CA927B34A3245C938178ED00D6EF218AA");
190+
assert_eq!(res.retrieve(), expected);
191+
assert_eq!(res_vartime.retrieve(), expected);
192+
}
193+
194+
#[test]
195+
fn test_powmod() {
196+
let base =
197+
U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4");
198+
let base_mod = MontyForm::new(&base, &PARAMS);
199+
200+
let exponent =
201+
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
202+
203+
let res = base_mod.pow(&exponent);
204+
let res_vartime = base_mod.pow(&exponent);
205+
206+
let expected =
207+
U256::from_be_hex("3681BC0FEA2E5D394EB178155A127B0FD2EF405486D354251C385BDD51B9D421");
208+
assert_eq!(res.retrieve(), expected);
209+
assert_eq!(res_vartime.retrieve(), expected);
210+
}
211+
212+
#[test]
213+
fn test_multi_exp_array() {
214+
let base = U256::from(2u8);
215+
let base_mod = MontyForm::new(&base, &PARAMS);
216+
217+
let exponent = U256::from(33u8);
218+
let bases_and_exponents = [(base_mod, exponent)];
219+
let res = MontyForm::<{ U256::LIMBS }>::multi_exponentiate(&bases_and_exponents);
220+
221+
let expected =
222+
U256::from_be_hex("0000000000000000000000000000000000000000000000000000000200000000");
223+
224+
assert_eq!(res.retrieve(), expected);
225+
226+
let base2 =
227+
U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4");
228+
let base2_mod = MontyForm::new(&base2, &PARAMS);
229+
230+
let exponent2 =
231+
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
232+
233+
let expected = base_mod.pow(&exponent) * base2_mod.pow(&exponent2);
234+
let bases_and_exponents = [(base_mod, exponent), (base2_mod, exponent2)];
235+
let res = MontyForm::<{ U256::LIMBS }>::multi_exponentiate(&bases_and_exponents);
236+
237+
assert_eq!(res, expected);
238+
}
239+
240+
#[cfg(feature = "alloc")]
241+
#[test]
242+
fn test_multi_exp_slice() {
243+
let base = U256::from(2u8);
244+
let base_mod = MontyForm::new(&base, &PARAMS);
245+
246+
let exponent = U256::from(33u8);
247+
let bases_and_exponents = vec![(base_mod, exponent)];
248+
let res = MontyForm::<{ U256::LIMBS }>::multi_exponentiate(bases_and_exponents.as_slice());
249+
250+
let expected =
251+
U256::from_be_hex("0000000000000000000000000000000000000000000000000000000200000000");
252+
253+
assert_eq!(res.retrieve(), expected);
254+
255+
let base2 =
256+
U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4");
257+
let base2_mod = MontyForm::new(&base2, &PARAMS);
258+
259+
let exponent2 =
260+
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
261+
262+
let expected = base_mod.pow(&exponent) * base2_mod.pow(&exponent2);
263+
let bases_and_exponents = vec![(base_mod, exponent), (base2_mod, exponent2)];
264+
let res = MontyForm::<{ U256::LIMBS }>::multi_exponentiate(bases_and_exponents.as_slice());
265+
266+
assert_eq!(res, expected);
267+
}
268+
}

0 commit comments

Comments
 (0)