@@ -117,14 +117,15 @@ class GenericCurveParams final {
117117 m_field_monty_r1 (bn_to_fixed(m_monty_field.R1 ())),
118118 m_field_monty_r2 (bn_to_fixed(m_monty_field.R2 ())),
119119 m_field_p_plus_1_over_4 (bn_to_fixed_rev((p + 1 ) / 4 )),
120- m_field_p_over_2_plus_1 (bn_to_fixed((p / 2 ) + 1 )),
120+ m_field_inv_2 (bn_to_fixed((p / 2 ) + 1 )),
121121 m_field_p_dash (m_monty_field.p_dash()),
122122
123123 m_order (bn_to_fixed(order)),
124124 m_order_minus_2 (bn_to_fixed_rev(order - 2 )),
125125 m_order_monty_r1 (bn_to_fixed(m_monty_order.R1 ())),
126126 m_order_monty_r2 (bn_to_fixed(m_monty_order.R2 ())),
127127 m_order_monty_r3 (bn_to_fixed(m_monty_order.R3 ())),
128+ m_order_inv_2 (bn_to_fixed((order / 2 ) + 1 )),
128129 m_order_p_dash (m_monty_order.p_dash()),
129130
130131 m_a_is_minus_3 (a + 3 == p),
@@ -162,7 +163,7 @@ class GenericCurveParams final {
162163
163164 const StorageUnit& field_p_plus_1_over_4 () const { return m_field_p_plus_1_over_4; }
164165
165- const StorageUnit& field_p_over_2_plus_1 () const { return m_field_p_over_2_plus_1 ; }
166+ const StorageUnit& field_inv_2 () const { return m_field_inv_2 ; }
166167
167168 word field_p_dash () const { return m_field_p_dash; }
168169
@@ -176,6 +177,8 @@ class GenericCurveParams final {
176177
177178 const StorageUnit& order_monty_r3 () const { return m_order_monty_r3; }
178179
180+ const StorageUnit& order_inv_2 () const { return m_order_inv_2; }
181+
179182 word order_p_dash () const { return m_order_p_dash; }
180183
181184 const StorageUnit& monty_curve_a () const { return m_monty_curve_a; }
@@ -255,14 +258,15 @@ class GenericCurveParams final {
255258 StorageUnit m_field_monty_r1;
256259 StorageUnit m_field_monty_r2;
257260 StorageUnit m_field_p_plus_1_over_4;
258- StorageUnit m_field_p_over_2_plus_1 ;
261+ StorageUnit m_field_inv_2 ;
259262 word m_field_p_dash;
260263
261264 StorageUnit m_order;
262265 StorageUnit m_order_minus_2;
263266 StorageUnit m_order_monty_r1;
264267 StorageUnit m_order_monty_r2;
265268 StorageUnit m_order_monty_r3;
269+ StorageUnit m_order_inv_2;
266270 word m_order_p_dash;
267271
268272 StorageUnit m_monty_curve_a;
@@ -417,6 +421,90 @@ class GenericScalar final {
417421
418422 GenericScalar invert () const { return pow_vartime (m_curve->_params ().order_minus_2 ()); }
419423
424+ /* *
425+ * Helper for variable time BEEA
426+ *
427+ * Note this function assumes that its arguments are in the standard
428+ * domain, not the Montgomery domain. invert_vartime converts its argument
429+ * out of Montgomery, and then back to Montgomery when returning the result.
430+ */
431+ static void _invert_vartime_div2_helper (GenericScalar& a, GenericScalar& x) {
432+ const auto & inv_2 = a.curve ()->_params ().order_inv_2 ();
433+
434+ // Conditional ok: this function is variable time
435+ while ((a.m_val [0 ] & 1 ) != 1 ) {
436+ shift_right<1 >(a.m_val );
437+
438+ W borrow = shift_right<1 >(x.m_val );
439+
440+ // Conditional ok: this function is variable time
441+ if (borrow) {
442+ bigint_add2_nc (x.m_val .data (), N, inv_2.data (), N);
443+ }
444+ }
445+ }
446+
447+ /*
448+ * See the comments on invert_vartime in pcurves_impl.h for background
449+ */
450+ GenericScalar invert_vartime () const {
451+ if (this ->is_zero ().as_bool ()) {
452+ return (*this );
453+ }
454+
455+ auto x = GenericScalar (m_curve, std::array<W, N>{1 });
456+ auto b = GenericScalar (m_curve, from_rep (m_curve, m_val));
457+
458+ // First loop iteration
459+ GenericScalar::_invert_vartime_div2_helper (b, x);
460+
461+ auto a = b.negate ();
462+ // y += x but y is zero at the outset
463+ auto y = x;
464+
465+ // First half of second loop iteration
466+ GenericScalar::_invert_vartime_div2_helper (a, y);
467+
468+ for (;;) {
469+ // Conditional ok: this function is variable time
470+ if (a.m_val == b.m_val ) {
471+ // At this point it should be that a == b == 1
472+ auto r = y.negate ();
473+
474+ // Convert back to Montgomery
475+ return GenericScalar (curve (), to_rep (curve (), r.m_val ));
476+ }
477+
478+ auto nx = x + y;
479+
480+ /*
481+ * Otherwise either b > a or a > b
482+ *
483+ * If b > a we want to set b to b - a
484+ * Otherwise we want to set a to a - b
485+ *
486+ * Compute r = b - a and check if it underflowed
487+ * If it did not then we are in the b > a path
488+ */
489+ std::array<W, N> r;
490+ word carry = bigint_sub3 (r.data (), b.data (), N, a.data (), N);
491+
492+ // Conditional ok: this function is variable time
493+ if (carry == 0 ) {
494+ // b > a
495+ b.m_val = r;
496+ x = nx;
497+ GenericScalar::_invert_vartime_div2_helper (b, x);
498+ } else {
499+ // We know this can't underflow because a > b
500+ bigint_sub3 (r.data (), a.data (), N, b.data (), N);
501+ a.m_val = r;
502+ y = nx;
503+ GenericScalar::_invert_vartime_div2_helper (a, y);
504+ }
505+ }
506+ }
507+
420508 template <concepts::resizable_byte_buffer T>
421509 T serialize () const {
422510 T bytes (m_curve->_params ().order_bytes ());
@@ -587,7 +675,7 @@ class GenericField final {
587675 W borrow = shift_right<1 >(t);
588676
589677 // If value was odd, add (P/2)+1
590- bigint_cnd_add (borrow, t.data (), N, m_curve->_params ().field_p_over_2_plus_1 ().data (), N);
678+ bigint_cnd_add (borrow, t.data (), N, m_curve->_params ().field_inv_2 ().data (), N);
591679
592680 return GenericField (m_curve, t);
593681 }
@@ -1500,8 +1588,7 @@ PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert(const Scalar& s) c
15001588}
15011589
15021590PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert_vartime (const Scalar& s) const {
1503- // TODO support BEEA for this
1504- return stash (from_stash (s).invert ());
1591+ return stash (from_stash (s).invert_vartime ());
15051592}
15061593
15071594PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_negate (const Scalar& s) const {
0 commit comments