@@ -350,6 +350,22 @@ class IntMod final {
350350 }
351351 }
352352
353+ /* *
354+ * Conditional swap
355+ *
356+ * If `cond` is true, swaps the values of `x` and `y`
357+ */
358+ static constexpr void conditional_swap (CT ::Choice cond, Self& x, Self& y) {
359+ const W mask = CT ::Mask<W>::from_choice (cond).value ();
360+
361+ for (size_t i = 0 ; i != N; ++i) {
362+ auto nx = choose (mask, y.m_val [i], x.m_val [i]);
363+ auto ny = choose (mask, x.m_val [i], y.m_val [i]);
364+ x.m_val [i] = nx;
365+ y.m_val [i] = ny;
366+ }
367+ }
368+
353369 /* *
354370 * Modular squaring
355371 *
@@ -423,6 +439,7 @@ class IntMod final {
423439 tbl[0 ] = (*this );
424440
425441 for (size_t i = 1 ; i != WindowElements; ++i) {
442+ // Conditional ok: table indexes are public here
426443 if (i % 2 == 1 ) {
427444 tbl[i] = tbl[i / 2 ].square ();
428445 } else {
@@ -434,6 +451,7 @@ class IntMod final {
434451
435452 const size_t w0 = read_window_bits<WindowBits>(std::span{exp}, (Windows - 1 ) * WindowBits);
436453
454+ // Conditional ok: this function is variable time
437455 if (w0 > 0 ) {
438456 r = tbl[w0 - 1 ];
439457 }
@@ -443,6 +461,7 @@ class IntMod final {
443461
444462 const size_t w = read_window_bits<WindowBits>(std::span{exp}, (Windows - i - 1 ) * WindowBits);
445463
464+ // Conditional ok: this function is variable time
446465 if (w > 0 ) {
447466 r *= tbl[w - 1 ];
448467 }
@@ -476,11 +495,13 @@ class IntMod final {
476495 static constexpr void _invert_vartime_div2_helper (Self& a, Self& x) {
477496 constexpr auto INV_2 = p_div_2_plus_1 (Rep::P);
478497
498+ // Conditional ok: this function is variable time
479499 while ((a.m_val [0 ] & 1 ) != 1 ) {
480500 shift_right<1 >(a.m_val );
481501
482502 W borrow = shift_right<1 >(x.m_val );
483503
504+ // Conditional ok: this function is variable time
484505 if (borrow) {
485506 bigint_add2_nc (x.m_val .data (), N, INV_2 .data (), N);
486507 }
@@ -523,6 +544,7 @@ class IntMod final {
523544 * a/y resp.
524545 */
525546 constexpr Self invert_vartime () const {
547+ // Conditional ok: this function is variable time
526548 if (this ->is_zero ().as_bool ()) {
527549 return Self::zero ();
528550 }
@@ -541,6 +563,7 @@ class IntMod final {
541563 Self::_invert_vartime_div2_helper (a, y);
542564
543565 for (;;) {
566+ // Conditional ok: this function is variable time
544567 if (a.m_val == b.m_val ) {
545568 // At this point it should be that a == b == 1
546569 auto r = y.negate ();
@@ -564,6 +587,7 @@ class IntMod final {
564587 std::array<W, N> r;
565588 word carry = bigint_sub3 (r.data (), b.data (), N, a.data (), N);
566589
590+ // Conditional ok: this function is variable time
567591 if (carry == 0 ) {
568592 // b > a
569593 b.m_val = r;
@@ -714,12 +738,14 @@ class IntMod final {
714738 * also rejected.
715739 */
716740 static std::optional<Self> deserialize (std::span<const uint8_t > bytes) {
741+ // Conditional ok: input length is public
717742 if (bytes.size () != Self::BYTES ) {
718743 return {};
719744 }
720745
721746 const auto words = bytes_to_words<W, N, BYTES >(bytes.first <Self::BYTES >());
722747
748+ // Conditional acceptable: std::optional is implicitly not constant time
723749 if (!bigint_ct_is_lt (words.data (), N, P.data (), N).as_bool ()) {
724750 return {};
725751 }
@@ -749,6 +775,7 @@ class IntMod final {
749775 * modular reduces it.
750776 */
751777 static constexpr std::optional<Self> from_wide_bytes_varlen (std::span<const uint8_t > bytes) {
778+ // Conditional ok: input length is public
752779 if (bytes.size () > 2 * Self::BYTES ) {
753780 return {};
754781 }
@@ -784,6 +811,7 @@ class IntMod final {
784811 buf[0 ] &= mask;
785812 }
786813
814+ // Conditionals ok: rejection sampling reveals only values we didn't use
787815 if (auto s = Self::deserialize (buf)) {
788816 if (s.value ().is_nonzero ().as_bool ()) {
789817 return s.value ();
@@ -979,11 +1007,22 @@ class ProjectiveCurvePoint {
9791007 * Convert a point from affine to projective form
9801008 */
9811009 static constexpr Self from_affine (const AffinePoint& pt) {
982- if (pt.is_identity ().as_bool ()) {
983- return Self::identity ();
984- } else {
985- return ProjectiveCurvePoint (pt.x (), pt.y ());
986- }
1010+ /*
1011+ * If the point is the identity element (x=0, y=0) then instead of
1012+ * creating (x, y, 1) = (0, 0, 1) we want our projective identity
1013+ * encoding of (0, 1, 0)
1014+ *
1015+ * Which we can achieve by a conditional swap of y and z if the
1016+ * affine point is the identity.
1017+ */
1018+
1019+ auto x = pt.x ();
1020+ auto y = pt.y ();
1021+ auto z = FieldElement::one ();
1022+
1023+ FieldElement::conditional_swap (pt.is_identity (), y, z);
1024+
1025+ return ProjectiveCurvePoint (x, y, z);
9871026 }
9881027
9891028 /* *
@@ -1097,6 +1136,7 @@ class ProjectiveCurvePoint {
10971136 // In certain contexts we may be called with a Null_RNG; in that case the
10981137 // caller is accepting that randomization will not occur
10991138
1139+ // Conditional ok: caller's RNG state (seeded vs not) is presumed public
11001140 if (rng.is_seeded ()) {
11011141 auto r = FieldElement::random (rng);
11021142
@@ -1248,6 +1288,9 @@ class BlindedScalarBits final {
12481288 //
12491289 // This can return any value between 0 and the scalar bit length, as long
12501290 // as it is a multiple of the word size.
1291+ //
1292+ // TODO(Botan4) this function should be consteval but cannot currently to a bug
1293+ // in older versions of Clang. Change to consteval when minimum Clang is bumped.
12511294 static constexpr size_t blinding_bits (size_t sb) {
12521295 constexpr size_t wb = WordInfo<W>::bits;
12531296
@@ -1479,6 +1522,7 @@ class WindowedBoothMulTable final {
14791522 const size_t w_i = bits.get_window (idx);
14801523 const auto [tidx, tneg] = booth_recode<WindowBits>(w_i);
14811524
1525+ // Conditional ok: loop iteration count is public
14821526 if (i == 0 ) {
14831527 accum = ProjectivePoint::from_affine (m_table.ct_select (tidx));
14841528 accum.conditional_assign (tneg, accum.negate ());
@@ -1488,6 +1532,7 @@ class WindowedBoothMulTable final {
14881532
14891533 accum = accum.dbl_n (WindowBits);
14901534
1535+ // Conditional ok: loop iteration count is public
14911536 if (i <= 3 ) {
14921537 accum.randomize_rep (rng);
14931538 }
@@ -1578,6 +1623,7 @@ class VartimeMul2Table final {
15781623 bool s1_is_zero = s1.is_zero ().as_bool ();
15791624 bool s2_is_zero = s2.is_zero ().as_bool ();
15801625
1626+ // Conditional ok: this function is variable time
15811627 if (s1_is_zero && s2_is_zero) {
15821628 return ProjectivePoint::identity ();
15831629 }
@@ -1587,6 +1633,7 @@ class VartimeMul2Table final {
15871633 const size_t w_1 = bits1.get_window ((Windows - i - 1 ) * WindowBits);
15881634 const size_t w_2 = bits2.get_window ((Windows - i - 1 ) * WindowBits);
15891635 const size_t window = w_1 + (w_2 << WindowBits);
1636+ // Conditional ok: this function is variable time
15901637 if (window > 0 ) {
15911638 return std::make_pair (window, i);
15921639 }
@@ -1606,6 +1653,7 @@ class VartimeMul2Table final {
16061653
16071654 const size_t window = w_1 + (w_2 << WindowBits);
16081655
1656+ // Conditional ok: this function is variable time
16091657 if (window > 0 ) {
16101658 accum += m_table[window - 1 ];
16111659 }
0 commit comments