Skip to content

Commit 682f17a

Browse files
authored
Variable-time modular inversion support (#731)
Adds (back) support for computing modular inversions in variable-time with respect to the value being inverted, which computes the specific number of safegcd divsteps to perform based on the input, as opposed to using a worst case number based on the bit length. Closes #728
1 parent 9600cab commit 682f17a

File tree

7 files changed

+186
-8
lines changed

7 files changed

+186
-8
lines changed

src/modular/boxed_monty_form/inv.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,34 @@ use core::fmt;
1010
use subtle::CtOption;
1111

1212
impl BoxedMontyForm {
13-
/// Computes `self^-1` representing the multiplicative inverse of `self`.
14-
/// I.e. `self * self^-1 = 1`.
13+
/// Computes `self^-1` representing the multiplicative inverse of `self`,
14+
/// i.e. `self * self^-1 = 1`.
1515
pub fn invert(&self) -> CtOption<Self> {
1616
let inverter = self.params.precompute_inverter();
1717
inverter.invert(self)
1818
}
19+
20+
/// Computes `self^-1` representing the multiplicative inverse of `self`,
21+
/// i.e. `self * self^-1 = 1`.
22+
///
23+
/// This version is variable-time with respect to the value of `self`, but constant-time with
24+
/// respect to `self`'s `params`.
25+
pub fn invert_vartime(&self) -> CtOption<Self> {
26+
let inverter = self.params.precompute_inverter();
27+
inverter.invert_vartime(self)
28+
}
1929
}
2030

2131
impl Invert for BoxedMontyForm {
2232
type Output = CtOption<Self>;
33+
2334
fn invert(&self) -> Self::Output {
2435
self.invert()
2536
}
37+
38+
fn invert_vartime(&self) -> Self::Output {
39+
self.invert_vartime()
40+
}
2641
}
2742

2843
impl PrecomputeInverter for BoxedMontyParams {
@@ -62,6 +77,20 @@ impl Inverter for BoxedMontyFormInverter {
6277

6378
CtOption::new(ret, is_some)
6479
}
80+
81+
fn invert_vartime(&self, value: &BoxedMontyForm) -> CtOption<Self::Output> {
82+
debug_assert_eq!(self.params, value.params);
83+
84+
let montgomery_form = self.inverter.invert_vartime(&value.montgomery_form);
85+
let is_some = montgomery_form.is_some();
86+
let montgomery_form2 = value.montgomery_form.clone();
87+
let ret = BoxedMontyForm {
88+
montgomery_form: Option::from(montgomery_form).unwrap_or(montgomery_form2),
89+
params: value.params.clone(),
90+
};
91+
92+
CtOption::new(ret, is_some)
93+
}
6594
}
6695

6796
impl fmt::Debug for BoxedMontyFormInverter {

src/modular/const_monty_form/inv.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ where
1515
Output = Uint<SAT_LIMBS>,
1616
>,
1717
{
18-
/// Computes `self^-1` representing the multiplicative inverse of `self`.
19-
/// I.e. `self * self^-1 = 1`.
18+
/// Computes `self^-1` representing the multiplicative inverse of `self`,
19+
/// i.e. `self * self^-1 = 1`.
20+
///
2021
/// If the number was invertible, the second element of the tuple is the truthy value,
2122
/// otherwise it is the falsy value (in which case the first element's value is unspecified).
2223
pub const fn inv(&self) -> ConstCtOption<Self> {
@@ -33,6 +34,29 @@ where
3334

3435
ConstCtOption::new(ret, inverse_is_some)
3536
}
37+
38+
/// Computes `self^-1` representing the multiplicative inverse of `self`,
39+
/// i.e. `self * self^-1 = 1`.
40+
///
41+
/// If the number was invertible, the second element of the tuple is the truthy value,
42+
/// otherwise it is the falsy value (in which case the first element's value is unspecified).
43+
///
44+
/// This version is variable-time with respect to the value of `self`, but constant-time with
45+
/// respect to `MOD`.
46+
pub const fn inv_vartime(&self) -> ConstCtOption<Self> {
47+
let inverter =
48+
<Odd<Uint<SAT_LIMBS>> as PrecomputeInverter>::Inverter::new(&MOD::MODULUS, &MOD::R2);
49+
50+
let maybe_inverse = inverter.inv_vartime(&self.montgomery_form);
51+
let (inverse, inverse_is_some) = maybe_inverse.components_ref();
52+
53+
let ret = Self {
54+
montgomery_form: *inverse,
55+
phantom: PhantomData,
56+
};
57+
58+
ConstCtOption::new(ret, inverse_is_some)
59+
}
3660
}
3761

3862
impl<MOD: ConstMontyParams<SAT_LIMBS>, const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Invert
@@ -44,9 +68,14 @@ where
4468
>,
4569
{
4670
type Output = CtOption<Self>;
71+
4772
fn invert(&self) -> Self::Output {
4873
self.inv().into()
4974
}
75+
76+
fn invert_vartime(&self) -> Self::Output {
77+
self.inv_vartime().into()
78+
}
5079
}
5180

5281
/// Bernstein-Yang inverter which inverts [`ConstMontyForm`] types.
@@ -91,6 +120,24 @@ where
91120
};
92121
ConstCtOption::new(ret, is_some)
93122
}
123+
124+
/// Returns either the adjusted modular multiplicative inverse for the argument or None
125+
/// depending on invertibility of the argument, i.e. its coprimality with the modulus.
126+
///
127+
/// This version is variable-time with respect to the value of `self`, but constant-time with
128+
/// respect to `MOD`.
129+
pub const fn inv_vartime(
130+
&self,
131+
value: &ConstMontyForm<MOD, SAT_LIMBS>,
132+
) -> ConstCtOption<ConstMontyForm<MOD, SAT_LIMBS>> {
133+
let montgomery_form = self.inverter.inv_vartime(&value.montgomery_form);
134+
let (montgomery_form_ref, is_some) = montgomery_form.components_ref();
135+
let ret = ConstMontyForm {
136+
montgomery_form: *montgomery_form_ref,
137+
phantom: PhantomData,
138+
};
139+
ConstCtOption::new(ret, is_some)
140+
}
94141
}
95142

96143
impl<MOD: ConstMontyParams<SAT_LIMBS>, const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Inverter
@@ -106,6 +153,10 @@ where
106153
fn invert(&self, value: &ConstMontyForm<MOD, SAT_LIMBS>) -> CtOption<Self::Output> {
107154
self.inv(value).into()
108155
}
156+
157+
fn invert_vartime(&self, value: &ConstMontyForm<MOD, SAT_LIMBS>) -> CtOption<Self::Output> {
158+
self.inv_vartime(value).into()
159+
}
109160
}
110161

111162
impl<MOD: ConstMontyParams<SAT_LIMBS>, const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> fmt::Debug

src/modular/monty_form/inv.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ where
1616
>,
1717
{
1818
/// Computes `self^-1` representing the multiplicative inverse of `self`.
19-
/// I.e. `self * self^-1 = 1`.
19+
/// i.e. `self * self^-1 = 1`.
20+
///
2021
/// If the number was invertible, the second element of the tuple is the truthy value,
2122
/// otherwise it is the falsy value (in which case the first element's value is unspecified).
2223
pub const fn inv(&self) -> ConstCtOption<Self> {
@@ -35,6 +36,31 @@ where
3536

3637
ConstCtOption::new(ret, inverse_is_some)
3738
}
39+
40+
/// Computes `self^-1` representing the multiplicative inverse of `self`.
41+
/// i.e. `self * self^-1 = 1`.
42+
///
43+
/// If the number was invertible, the second element of the tuple is the truthy value,
44+
/// otherwise it is the falsy value (in which case the first element's value is unspecified).
45+
///
46+
/// This version is variable-time with respect to the value of `self`, but constant-time with
47+
/// respect to `self`'s `params`.
48+
pub const fn inv_vartime(&self) -> ConstCtOption<Self> {
49+
let inverter = <Odd<Uint<SAT_LIMBS>> as PrecomputeInverter>::Inverter::new(
50+
&self.params.modulus,
51+
&self.params.r2,
52+
);
53+
54+
let maybe_inverse = inverter.inv_vartime(&self.montgomery_form);
55+
let (inverse, inverse_is_some) = maybe_inverse.components_ref();
56+
57+
let ret = Self {
58+
montgomery_form: *inverse,
59+
params: self.params,
60+
};
61+
62+
ConstCtOption::new(ret, inverse_is_some)
63+
}
3864
}
3965

4066
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Invert for MontyForm<SAT_LIMBS>
@@ -49,6 +75,10 @@ where
4975
fn invert(&self) -> Self::Output {
5076
self.inv().into()
5177
}
78+
79+
fn invert_vartime(&self) -> Self::Output {
80+
self.inv_vartime().into()
81+
}
5282
}
5383

5484
impl<const LIMBS: usize> PrecomputeInverter for MontyParams<LIMBS>
@@ -92,6 +122,17 @@ where
92122
params: value.params,
93123
})
94124
}
125+
126+
fn invert_vartime(&self, value: &MontyForm<LIMBS>) -> CtOption<Self::Output> {
127+
debug_assert_eq!(self.params, value.params);
128+
129+
self.inverter
130+
.invert_vartime(&value.montgomery_form)
131+
.map(|montgomery_form| MontyForm {
132+
montgomery_form,
133+
params: value.params,
134+
})
135+
}
95136
}
96137

97138
impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> fmt::Debug for MontyFormInverter<SAT_LIMBS>

src/modular/safegcd.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> SafeGcdInverter<SAT_LIMBS
7373
}
7474

7575
/// Returns either the adjusted modular multiplicative inverse for the argument or `None`
76-
/// depending on invertibility of the argument, i.e. its coprimality with the modulus
76+
/// depending on invertibility of the argument, i.e. its coprimality with the modulus.
7777
pub const fn inv(&self, value: &Uint<SAT_LIMBS>) -> ConstCtOption<Uint<SAT_LIMBS>> {
7878
let (d, f) = divsteps(
7979
self.adjuster,
@@ -91,6 +91,27 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> SafeGcdInverter<SAT_LIMBS
9191
ConstCtOption::new(ret.to_uint(), is_some)
9292
}
9393

94+
/// Returns either the adjusted modular multiplicative inverse for the argument or `None`
95+
/// depending on invertibility of the argument, i.e. its coprimality with the modulus.
96+
///
97+
/// This version is variable-time with respect to `value`.
98+
pub const fn inv_vartime(&self, value: &Uint<SAT_LIMBS>) -> ConstCtOption<Uint<SAT_LIMBS>> {
99+
let (d, f) = divsteps_vartime(
100+
self.adjuster,
101+
self.modulus,
102+
UnsatInt::from_uint(value),
103+
self.inverse,
104+
);
105+
106+
// At this point the absolute value of "f" equals the greatest common divisor of the
107+
// integer to be inverted and the modulus the inverter was created for.
108+
// Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
109+
let antiunit = f.eq(&UnsatInt::MINUS_ONE);
110+
let ret = self.norm(d, antiunit);
111+
let is_some = f.eq(&UnsatInt::ONE).or(antiunit);
112+
ConstCtOption::new(ret.to_uint(), is_some)
113+
}
114+
94115
/// Returns the greatest common divisor (GCD) of the two given numbers.
95116
///
96117
/// This is defined on this type to piggyback on the definitions for `SAT_LIMBS` and
@@ -142,6 +163,10 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Inverter
142163
fn invert(&self, value: &Uint<SAT_LIMBS>) -> CtOption<Self::Output> {
143164
self.inv(value).into()
144165
}
166+
167+
fn invert_vartime(&self, value: &Uint<SAT_LIMBS>) -> CtOption<Self::Output> {
168+
self.inv_vartime(value).into()
169+
}
145170
}
146171

147172
/// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based

src/modular/safegcd/boxed.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ impl Inverter for BoxedSafeGcdInverter {
6767

6868
CtOption::new(ret.to_uint(value.bits_precision()), is_some)
6969
}
70+
71+
fn invert_vartime(&self, value: &BoxedUint) -> CtOption<Self::Output> {
72+
let mut d = BoxedUnsatInt::zero(self.modulus.nlimbs());
73+
let mut g = BoxedUnsatInt::from(value).widen(d.nlimbs());
74+
let f = divsteps_vartime(&mut d, &self.adjuster, &self.modulus, &mut g, self.inverse);
75+
76+
// At this point the absolute value of "f" equals the greatest common divisor of the
77+
// integer to be inverted and the modulus the inverter was created for.
78+
// Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
79+
let antiunit = f.is_minus_one();
80+
let ret = self.norm(d, antiunit);
81+
let is_some = f.is_one() | antiunit;
82+
83+
CtOption::new(ret.to_uint(value.bits_precision()), is_some)
84+
}
7085
}
7186

7287
/// Compute the number of unsaturated limbs needed to represent a saturated integer with the given

src/traits.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,15 @@ pub trait Inverter {
220220
/// Output of an inversion.
221221
type Output;
222222

223-
/// Compute a modular inversion, returning `None` if the result is undefined (i.e. if `value` is zero or isn't
224-
/// prime relative to the modulus).
223+
/// Compute a modular inversion, returning `None` if the result is undefined (i.e. if `value` is
224+
/// zero or isn't prime relative to the modulus).
225225
fn invert(&self, value: &Self::Output) -> CtOption<Self::Output>;
226+
227+
/// Compute a modular inversion, returning `None` if the result is undefined (i.e. if `value` is
228+
/// zero or isn't prime relative to the modulus).
229+
///
230+
/// This version is variable-time with respect to `value`.
231+
fn invert_vartime(&self, value: &Self::Output) -> CtOption<Self::Output>;
226232
}
227233

228234
/// Obtain a precomputed inverter for efficiently computing modular inversions for a given modulus.
@@ -757,6 +763,9 @@ pub trait Invert: Sized {
757763

758764
/// Computes the inverse.
759765
fn invert(&self) -> Self::Output;
766+
767+
/// Computes the inverse in variable-time.
768+
fn invert_vartime(&self) -> Self::Output;
760769
}
761770

762771
/// Widening multiply: returns a value with a number of limbs equal to the sum of the inputs.

tests/safegcd.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ proptest! {
5151
let inv_bi = to_biguint(&actual);
5252
let res = (inv_bi * x_bi) % p_bi;
5353
prop_assert_eq!(res, BigUint::one());
54+
55+
// check vartime implementation equivalence
56+
let actual_vartime = inverter.invert_vartime(&x).unwrap();
57+
prop_assert_eq!(actual, actual_vartime);
5458
}
5559
}
5660

@@ -73,6 +77,10 @@ proptest! {
7377
let inv_bi = to_biguint(&actual);
7478
let res = (inv_bi * x_bi) % p_bi;
7579
prop_assert_eq!(res, BigUint::one());
80+
81+
// check vartime implementation equivalence
82+
let actual_vartime = inverter.invert_vartime(&x).unwrap();
83+
prop_assert_eq!(actual, actual_vartime);
7684
}
7785
}
7886
}

0 commit comments

Comments
 (0)