• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 22560771261

02 Mar 2026 12:48AM UTC coverage: 91.951% (+1.6%) from 90.305%
22560771261

push

github

web-flow
Merge pull request #5400 from randombit/jack/pcurves-generic-blinding

Generalized ECC scalar blinding in pcurves

105347 of 114569 relevant lines covered (91.95%)

11507103.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.44
/src/lib/math/pcurves/pcurves_generic/pcurves_generic.cpp
1
/*
2
* (C) 2025 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include <botan/internal/pcurves_generic.h>
8

9
#include <botan/bigint.h>
10
#include <botan/exceptn.h>
11
#include <botan/rng.h>
12
#include <botan/internal/buffer_stuffer.h>
13
#include <botan/internal/ct_utils.h>
14
#include <botan/internal/loadstor.h>
15
#include <botan/internal/mp_core.h>
16
#include <botan/internal/pcurves_algos.h>
17
#include <botan/internal/pcurves_instance.h>
18
#include <botan/internal/pcurves_mul.h>
19
#include <botan/internal/primality.h>
20
#include <algorithm>
21

22
namespace Botan::PCurve {
23

24
namespace {
25

26
template <size_t N>
27
constexpr std::optional<std::array<word, N>> bytes_to_words(std::span<const uint8_t> bytes) {
115,680✔
28
   if(bytes.size() > WordInfo<word>::bytes * N) {
115,680✔
29
      return std::nullopt;
×
30
   }
31

32
   std::array<word, N> r{};
115,680✔
33

34
   const size_t full_words = bytes.size() / WordInfo<word>::bytes;
115,680✔
35
   const size_t extra_bytes = bytes.size() % WordInfo<word>::bytes;
115,680✔
36

37
   for(size_t i = 0; i != full_words; ++i) {
664,921✔
38
      r[i] = load_be<word>(bytes.data(), full_words - 1 - i);
549,241✔
39
   }
40

41
   if(extra_bytes > 0) {
115,680✔
42
      const size_t shift = extra_bytes * 8;
43,900✔
43
      bigint_shl1(r.data(), r.size(), r.size(), shift);
43,900✔
44

45
      for(size_t i = 0; i != extra_bytes; ++i) {
262,852✔
46
         const word b0 = bytes[WordInfo<word>::bytes * full_words + i];
218,952✔
47
         r[0] |= (b0 << (8 * (extra_bytes - 1 - i)));
218,952✔
48
      }
49
   }
50

51
   return r;
115,680✔
52
}
53

54
template <typename T>
55
T impl_pow_vartime(const T& elem, const T& one, size_t bits, std::span<const word> exp) {
40,968✔
56
   constexpr size_t WindowBits = 4;
40,968✔
57
   constexpr size_t WindowElements = (1 << WindowBits) - 1;
40,968✔
58

59
   const size_t Windows = (bits + WindowBits - 1) / WindowBits;
40,968✔
60

61
   std::vector<T> tbl;
40,968✔
62
   tbl.reserve(WindowElements);
40,968✔
63

64
   tbl.push_back(elem);
40,968✔
65

66
   for(size_t i = 1; i != WindowElements; ++i) {
614,520✔
67
      if(i % 2 == 1) {
573,552✔
68
         tbl.push_back(tbl[i / 2].square());
573,552✔
69
      } else {
70
         tbl.push_back(tbl[i - 1] * tbl[0]);
573,552✔
71
      }
72
   }
73

74
   auto r = one;
40,968✔
75

76
   const size_t w0 = read_window_bits<WindowBits>(exp, (Windows - 1) * WindowBits);
40,968✔
77

78
   if(w0 > 0) {
40,968✔
79
      r = tbl[w0 - 1];
40,968✔
80
   }
81

82
   for(size_t i = 1; i != Windows; ++i) {
2,490,784✔
83
      for(size_t j = 0; j != WindowBits; ++j) {
12,249,080✔
84
         r = r.square();
9,799,264✔
85
      }
86
      const size_t w = read_window_bits<WindowBits>(exp, (Windows - i - 1) * WindowBits);
2,449,816✔
87

88
      if(w > 0) {
2,449,816✔
89
         r *= tbl[w - 1];
2,287,111✔
90
      }
91
   }
92

93
   return r;
40,968✔
94
}
40,968✔
95

96
}  // namespace
97

98
class GenericCurveParams final {
99
   public:
100
      typedef PrimeOrderCurve::StorageUnit StorageUnit;
101
      static constexpr size_t N = PrimeOrderCurve::StorageWords;
102

103
      GenericCurveParams(const BigInt& p,
147✔
104
                         const BigInt& a,
105
                         const BigInt& b,
106
                         const BigInt& base_x,
107
                         const BigInt& base_y,
108
                         const BigInt& order) :
147✔
109
            m_words(p.sig_words()),
147✔
110
            m_order_bits(order.bits()),
147✔
111
            m_order_bytes(order.bytes()),
147✔
112
            m_field_bits(p.bits()),
147✔
113
            m_field_bytes(p.bytes()),
147✔
114
            m_monty_order(order),
147✔
115
            m_monty_field(p),
147✔
116
            m_field(bn_to_fixed(p)),
147✔
117
            m_field_minus_2(bn_to_fixed_rev(p - 2)),
147✔
118
            m_field_monty_r1(bn_to_fixed(m_monty_field.R1())),
147✔
119
            m_field_monty_r2(bn_to_fixed(m_monty_field.R2())),
147✔
120
            m_field_p_plus_1_over_4(bn_to_fixed_rev((p + 1) / 4)),
147✔
121
            m_field_inv_2(bn_to_fixed((p / 2) + 1)),
294✔
122
            m_field_p_dash(m_monty_field.p_dash()),
147✔
123

124
            m_order(bn_to_fixed(order)),
147✔
125
            m_order_minus_2(bn_to_fixed_rev(order - 2)),
147✔
126
            m_order_monty_r1(bn_to_fixed(m_monty_order.R1())),
147✔
127
            m_order_monty_r2(bn_to_fixed(m_monty_order.R2())),
147✔
128
            m_order_monty_r3(bn_to_fixed(m_monty_order.R3())),
147✔
129
            m_order_inv_2(bn_to_fixed((order / 2) + 1)),
294✔
130
            m_order_p_dash(m_monty_order.p_dash()),
147✔
131

132
            m_a_is_minus_3(a + 3 == p),
147✔
133
            m_a_is_zero(a.is_zero()),
147✔
134
            m_order_is_lt_field(order < p) {
147✔
135
         secure_vector<word> ws;
147✔
136
         m_monty_curve_a = bn_to_fixed(m_monty_field.mul(a, m_monty_field.R2(), ws));
147✔
137
         m_monty_curve_b = bn_to_fixed(m_monty_field.mul(b, m_monty_field.R2(), ws));
147✔
138

139
         m_base_x = bn_to_fixed(m_monty_field.mul(base_x, m_monty_field.R2(), ws));
147✔
140
         m_base_y = bn_to_fixed(m_monty_field.mul(base_y, m_monty_field.R2(), ws));
147✔
141
      }
147✔
142

143
      size_t words() const { return m_words; }
7,330,301✔
144

145
      size_t order_bits() const { return m_order_bits; }
42,829✔
146

147
      size_t order_bytes() const { return m_order_bytes; }
78,928✔
148

149
      size_t field_bits() const { return m_field_bits; }
86,363✔
150

151
      size_t field_bytes() const { return m_field_bytes; }
54,848✔
152

153
      const Montgomery_Params& monty_order() const { return m_monty_order; }
154

155
      const Montgomery_Params& monty_field() const { return m_monty_field; }
156

157
      const StorageUnit& field() const { return m_field; }
53,587,808✔
158

159
      const StorageUnit& field_minus_2() const { return m_field_minus_2; }
32,288✔
160

161
      const StorageUnit& field_monty_r1() const { return m_field_monty_r1; }
162

163
      const StorageUnit& field_monty_r2() const { return m_field_monty_r2; }
71,272✔
164

165
      const StorageUnit& field_p_plus_1_over_4() const { return m_field_p_plus_1_over_4; }
2,871✔
166

167
      const StorageUnit& field_inv_2() const { return m_field_inv_2; }
804,793✔
168

169
      word field_p_dash() const { return m_field_p_dash; }
54,591,697✔
170

171
      const StorageUnit& order() const { return m_order; }
1,030,573✔
172

173
      const StorageUnit& order_minus_2() const { return m_order_minus_2; }
5,809✔
174

175
      const StorageUnit& order_monty_r1() const { return m_order_monty_r1; }
176

177
      const StorageUnit& order_monty_r2() const { return m_order_monty_r2; }
36,201✔
178

179
      const StorageUnit& order_monty_r3() const { return m_order_monty_r3; }
9,648✔
180

181
      const StorageUnit& order_inv_2() const { return m_order_inv_2; }
970,601✔
182

183
      word order_p_dash() const { return m_order_p_dash; }
1,988,725✔
184

185
      const StorageUnit& monty_curve_a() const { return m_monty_curve_a; }
186

187
      const StorageUnit& monty_curve_b() const { return m_monty_curve_b; }
188

189
      const StorageUnit& base_x() const { return m_base_x; }
190

191
      const StorageUnit& base_y() const { return m_base_y; }
192

193
      bool a_is_minus_3() const { return m_a_is_minus_3; }
981,851✔
194

195
      bool a_is_zero() const { return m_a_is_zero; }
497,326✔
196

197
      bool order_is_less_than_field() const { return m_order_is_lt_field; }
2,392✔
198

199
      void mul(std::array<word, 2 * N>& z, const std::array<word, N>& x, const std::array<word, N>& y) const {
28,157,132✔
200
         clear_mem(z);
28,157,132✔
201

202
         if(m_words == 4) {
28,157,132✔
203
            bigint_comba_mul4(z.data(), x.data(), y.data());
11,664,887✔
204
         } else if(m_words == 6) {
16,492,245✔
205
            bigint_comba_mul6(z.data(), x.data(), y.data());
110,252✔
206
         } else if(m_words == 8) {
16,381,993✔
207
            bigint_comba_mul8(z.data(), x.data(), y.data());
3,301,982✔
208
         } else if(m_words == 9) {
13,080,011✔
209
            bigint_comba_mul9(z.data(), x.data(), y.data());
×
210
         } else {
211
            bigint_mul(z.data(), z.size(), x.data(), m_words, m_words, y.data(), m_words, m_words, nullptr, 0);
13,080,011✔
212
         }
213
      }
28,157,132✔
214

215
      void sqr(std::array<word, 2 * N>& z, const std::array<word, N>& x) const {
28,285,225✔
216
         clear_mem(z);
28,285,225✔
217

218
         if(m_words == 4) {
28,285,225✔
219
            bigint_comba_sqr4(z.data(), x.data());
11,953,710✔
220
         } else if(m_words == 6) {
16,331,515✔
221
            bigint_comba_sqr6(z.data(), x.data());
47,571✔
222
         } else if(m_words == 8) {
16,283,944✔
223
            bigint_comba_sqr8(z.data(), x.data());
3,764,065✔
224
         } else if(m_words == 9) {
12,519,879✔
225
            bigint_comba_sqr9(z.data(), x.data());
×
226
         } else {
227
            bigint_sqr(z.data(), z.size(), x.data(), m_words, m_words, nullptr, 0);
12,519,879✔
228
         }
229
      }
28,285,225✔
230

231
   private:
232
      static std::array<word, PrimeOrderCurve::StorageWords> bn_to_fixed(const BigInt& n) {
2,352✔
233
         const size_t n_words = n.sig_words();
2,352✔
234
         BOTAN_ASSERT_NOMSG(n_words <= PrimeOrderCurve::StorageWords);
2,352✔
235

236
         std::array<word, PrimeOrderCurve::StorageWords> r{};
2,352✔
237
         copy_mem(std::span{r}.first(n_words), n._as_span().first(n_words));
2,352✔
238
         return r;
2,352✔
239
      }
2,352✔
240

241
      static std::array<word, PrimeOrderCurve::StorageWords> bn_to_fixed_rev(const BigInt& n) {
441✔
242
         auto v = bn_to_fixed(n);
441✔
243
         std::reverse(v.begin(), v.end());
441✔
244
         return v;
441✔
245
      }
246

247
   private:
248
      size_t m_words;
249
      size_t m_order_bits;
250
      size_t m_order_bytes;
251
      size_t m_field_bits;
252
      size_t m_field_bytes;
253

254
      Montgomery_Params m_monty_order;
255
      Montgomery_Params m_monty_field;
256

257
      StorageUnit m_field;
258
      StorageUnit m_field_minus_2;
259
      StorageUnit m_field_monty_r1;
260
      StorageUnit m_field_monty_r2;
261
      StorageUnit m_field_p_plus_1_over_4;
262
      StorageUnit m_field_inv_2;
263
      word m_field_p_dash;
264

265
      StorageUnit m_order;
266
      StorageUnit m_order_minus_2;
267
      StorageUnit m_order_monty_r1;
268
      StorageUnit m_order_monty_r2;
269
      StorageUnit m_order_monty_r3;
270
      StorageUnit m_order_inv_2;
271
      word m_order_p_dash;
272

273
      StorageUnit m_monty_curve_a{};
274
      StorageUnit m_monty_curve_b{};
275

276
      StorageUnit m_base_x{};
277
      StorageUnit m_base_y{};
278

279
      bool m_a_is_minus_3;
280
      bool m_a_is_zero;
281
      bool m_order_is_lt_field;
282
};
283

284
class GenericScalar final {
285
   public:
286
      typedef word W;
287
      typedef PrimeOrderCurve::StorageUnit StorageUnit;
288
      static constexpr size_t N = PrimeOrderCurve::StorageWords;
289

290
      static std::optional<GenericScalar> from_wide_bytes(const GenericPrimeOrderCurve* curve,
9,648✔
291
                                                          std::span<const uint8_t> bytes) {
292
         const size_t mlen = curve->_params().order_bytes();
9,648✔
293

294
         if(bytes.size() > 2 * mlen) {
9,648✔
295
            return {};
×
296
         }
297

298
         std::array<uint8_t, 2 * sizeof(word) * N> padded_bytes{};
9,648✔
299
         copy_mem(std::span{padded_bytes}.last(bytes.size()), bytes);
9,648✔
300

301
         auto words = bytes_to_words<2 * N>(std::span{padded_bytes});
9,648✔
302
         if(words) {
9,648✔
303
            auto in_rep = wide_to_rep(curve, words.value());
9,648✔
304
            return GenericScalar(curve, in_rep);
9,648✔
305
         } else {
306
            return {};
×
307
         }
308
      }
9,648✔
309

310
      static std::optional<GenericScalar> deserialize(const GenericPrimeOrderCurve* curve,
32,160✔
311
                                                      std::span<const uint8_t> bytes) {
312
         const size_t len = curve->_params().order_bytes();
32,160✔
313

314
         if(bytes.size() != len) {
32,160✔
315
            return {};
×
316
         }
317

318
         const auto words = bytes_to_words<N>(bytes);
32,160✔
319

320
         if(words) {
32,160✔
321
            if(!bigint_ct_is_lt(words->data(), N, curve->_params().order().data(), N).as_bool()) {
32,160✔
322
               return {};
1,454✔
323
            }
324

325
            // Safe because we checked above that words is an integer < P
326
            return GenericScalar(curve, to_rep(curve, *words));
30,706✔
327
         } else {
328
            return {};
×
329
         }
330
      }
331

332
      static GenericScalar zero(const GenericPrimeOrderCurve* curve) {
333
         const StorageUnit zeros{};
334
         return GenericScalar(curve, zeros);
335
      }
336

337
      static GenericScalar one(const GenericPrimeOrderCurve* curve) {
5,853✔
338
         return GenericScalar(curve, curve->_params().order_monty_r1());
5,853✔
339
      }
340

341
      static GenericScalar random(const GenericPrimeOrderCurve* curve, RandomNumberGenerator& rng) {
18,637✔
342
         constexpr size_t MAX_ATTEMPTS = 1000;
18,637✔
343

344
         const size_t bits = curve->_params().order_bits();
18,637✔
345

346
         std::vector<uint8_t> buf(curve->_params().order_bytes());
18,637✔
347

348
         for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
19,892✔
349
            rng.randomize(buf);
19,892✔
350

351
            // Zero off high bits that if set would certainly cause us
352
            // to be out of range
353
            if(bits % 8 != 0) {
19,892✔
354
               const uint8_t mask = 0xFF >> (8 - (bits % 8));
4,641✔
355
               buf[0] &= mask;
4,641✔
356
            }
357

358
            if(auto s = GenericScalar::deserialize(curve, buf)) {
19,892✔
359
               if(s.value().is_nonzero().as_bool()) {
18,637✔
360
                  return s.value();
18,637✔
361
               }
362
            }
363
         }
364

365
         throw Internal_Error("Failed to generate random Scalar within bounded number of attempts");
×
366
      }
18,637✔
367

368
      friend GenericScalar operator+(const GenericScalar& a, const GenericScalar& b) {
973,632✔
369
         const auto* curve = check_curve(a, b);
1,947,264✔
370
         const size_t words = curve->_params().words();
973,632✔
371

372
         StorageUnit t{};
973,632✔
373
         const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words);
1,947,264✔
374

375
         StorageUnit r{};
973,632✔
376
         bigint_monty_maybe_sub(words, r.data(), carry, t.data(), curve->_params().order().data());
973,632✔
377
         return GenericScalar(curve, r);
973,632✔
378
      }
379

380
      friend GenericScalar operator-(const GenericScalar& a, const GenericScalar& b) { return a + b.negate(); }
6,289✔
381

382
      friend GenericScalar operator*(const GenericScalar& a, const GenericScalar& b) {
68,325✔
383
         const auto* curve = check_curve(a, b);
136,650✔
384

385
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
68,325✔
386
         curve->_params().mul(z, a.value(), b.value());
68,325✔
387
         return GenericScalar(curve, redc(curve, z));
68,325✔
388
      }
389

390
      GenericScalar& operator*=(const GenericScalar& other) {
334,065✔
391
         const auto* curve = check_curve(*this, other);
668,130✔
392

393
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
334,065✔
394
         curve->_params().mul(z, value(), other.value());
334,065✔
395
         m_val = redc(curve, z);
334,065✔
396
         return (*this);
334,065✔
397
      }
398

399
      GenericScalar square() const {
1,451,663✔
400
         const auto* curve = this->m_curve;
1,451,663✔
401

402
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
1,451,663✔
403
         curve->_params().sqr(z, value());
1,451,663✔
404
         return GenericScalar(curve, redc(curve, z));
1,451,663✔
405
      }
406

407
      GenericScalar pow_vartime(const StorageUnit& exp) const {
5,809✔
408
         auto one = GenericScalar::one(curve());
5,809✔
409
         auto bits = curve()->_params().order_bits();
5,809✔
410
         auto words = curve()->_params().words();
5,809✔
411
         return impl_pow_vartime(*this, one, bits, std::span{exp}.last(words));
11,618✔
412
      }
413

414
      GenericScalar negate() const {
22,577✔
415
         auto x_is_zero = CT::all_zeros(this->data(), N);
45,154✔
416

417
         StorageUnit r;
22,577✔
418
         bigint_sub3(r.data(), m_curve->_params().order().data(), N, this->data(), N);
22,577✔
419
         x_is_zero.if_set_zero_out(r.data(), N);
22,577✔
420
         return GenericScalar(m_curve, r);
22,577✔
421
      }
422

423
      GenericScalar invert() const { return pow_vartime(m_curve->_params().order_minus_2()); }
5,809✔
424

425
      /**
426
      * Helper for variable time BEEA
427
      *
428
      * Note this function assumes that its arguments are in the standard
429
      * domain, not the Montgomery domain. invert_vartime converts its argument
430
      * out of Montgomery, and then back to Montgomery when returning the result.
431
      */
432
      static void _invert_vartime_div2_helper(GenericScalar& a, GenericScalar& x) {
970,601✔
433
         const auto& inv_2 = a.curve()->_params().order_inv_2();
970,601✔
434

435
         // Conditional ok: this function is variable time
436
         while((a.m_val[0] & 1) != 1) {
3,870,518✔
437
            shift_right<1>(a.m_val);
19,293,160✔
438

439
            const W borrow = shift_right<1>(x.m_val);
19,293,160✔
440

441
            // Conditional ok: this function is variable time
442
            if(borrow > 0) {
1,929,316✔
443
               bigint_add2(x.m_val.data(), N, inv_2.data(), N);
969,950✔
444
            }
445
         }
446
      }
970,601✔
447

448
      /*
449
      * See the comments on invert_vartime in pcurves_impl.h for background
450
      */
451
      GenericScalar invert_vartime() const {
5,507✔
452
         if(this->is_zero().as_bool()) {
5,507✔
453
            return (*this);
12✔
454
         }
455

456
         auto x = GenericScalar(m_curve, std::array<W, N>{1});
5,495✔
457
         auto b = GenericScalar(m_curve, from_rep(m_curve, m_val));
10,990✔
458

459
         // First loop iteration
460
         GenericScalar::_invert_vartime_div2_helper(b, x);
5,495✔
461

462
         auto a = b.negate();
5,495✔
463
         // y += x but y is zero at the outset
464
         auto y = x;
5,495✔
465

466
         // First half of second loop iteration
467
         GenericScalar::_invert_vartime_div2_helper(a, y);
5,495✔
468

469
         for(;;) {
1,924,717✔
470
            // Conditional ok: this function is variable time
471
            if(a.m_val == b.m_val) {
965,106✔
472
               // At this point it should be that a == b == 1
473
               auto r = y.negate();
5,495✔
474

475
               // Convert back to Montgomery
476
               return GenericScalar(curve(), to_rep(curve(), r.m_val));
10,990✔
477
            }
478

479
            auto nx = x + y;
959,611✔
480

481
            /*
482
            * Otherwise either b > a or a > b
483
            *
484
            * If b > a we want to set b to b - a
485
            * Otherwise we want to set a to a - b
486
            *
487
            * Compute r = b - a and check if it underflowed
488
            * If it did not then we are in the b > a path
489
            */
490
            std::array<W, N> r{};
959,611✔
491
            const word carry = bigint_sub3(r.data(), b.data(), N, a.data(), N);
1,919,222✔
492

493
            // Conditional ok: this function is variable time
494
            if(carry == 0) {
959,611✔
495
               // b > a
496
               b.m_val = r;
478,154✔
497
               x = nx;
478,154✔
498
               GenericScalar::_invert_vartime_div2_helper(b, x);
478,154✔
499
            } else {
500
               // We know this can't underflow because a > b
501
               bigint_sub3(r.data(), a.data(), N, b.data(), N);
481,457✔
502
               a.m_val = r;
481,457✔
503
               y = nx;
481,457✔
504
               GenericScalar::_invert_vartime_div2_helper(a, y);
481,457✔
505
            }
506
         }
959,611✔
507
      }
508

509
      template <concepts::resizable_byte_buffer T>
510
      T serialize() const {
13,626✔
511
         T bytes(m_curve->_params().order_bytes());
13,626✔
512
         this->serialize_to(bytes);
13,626✔
513
         return bytes;
13,626✔
514
      }
×
515

516
      void serialize_to(std::span<uint8_t> bytes) const {
60,291✔
517
         auto v = from_rep(m_curve, m_val);
60,291✔
518
         std::reverse(v.begin(), v.end());
60,291✔
519

520
         const size_t flen = m_curve->_params().order_bytes();
60,291✔
521
         BOTAN_ARG_CHECK(bytes.size() == flen, "Expected output span provided");
60,291✔
522

523
         // Remove leading zero bytes
524
         const auto padded_bytes = store_be(v);
60,291✔
525
         const size_t extra = N * WordInfo<W>::bytes - flen;
60,291✔
526
         copy_mem(bytes, std::span{padded_bytes}.subspan(extra, flen));
60,291✔
527
      }
60,291✔
528

529
      CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_curve->_params().words()).as_choice(); }
37,661✔
530

531
      CT::Choice is_nonzero() const { return !is_zero(); }
18,637✔
532

533
      CT::Choice operator==(const GenericScalar& other) const {
×
534
         if(this->m_curve != other.m_curve) {
×
535
            return CT::Choice::no();
×
536
         }
537

538
         return CT::is_equal(m_val.data(), other.m_val.data(), m_curve->_params().words()).as_choice();
×
539
      }
540

541
      /**
542
      * Convert the integer to standard representation and return the sequence of words
543
      */
544
      StorageUnit to_words() const { return from_rep(m_curve, m_val); }
13,389✔
545

546
      const StorageUnit& stash_value() const { return m_val; }
547

548
      const GenericPrimeOrderCurve* curve() const { return m_curve; }
981,905✔
549

550
      GenericScalar(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {}
57,197✔
551

552
   private:
553
      const StorageUnit& value() const { return m_val; }
1,854,053✔
554

555
      const W* data() const { return m_val.data(); }
2,459,854✔
556

557
      static const GenericPrimeOrderCurve* check_curve(const GenericScalar& a, const GenericScalar& b) {
1,376,022✔
558
         BOTAN_STATE_CHECK(a.m_curve == b.m_curve);
1,376,022✔
559
         return a.m_curve;
1,376,022✔
560
      }
561

562
      static StorageUnit redc(const GenericPrimeOrderCurve* curve, std::array<W, 2 * N> z) {
1,988,725✔
563
         const auto& mod = curve->_params().order();
1,988,725✔
564
         const size_t words = curve->_params().words();
1,988,725✔
565
         StorageUnit r{};
1,988,725✔
566
         StorageUnit ws{};
1,988,725✔
567
         bigint_monty_redc(
1,988,725✔
568
            r.data(), z.data(), mod.data(), words, curve->_params().order_p_dash(), ws.data(), ws.size());
1,988,725✔
569
         return r;
1,988,725✔
570
      }
571

572
      static StorageUnit from_rep(const GenericPrimeOrderCurve* curve, StorageUnit z) {
79,175✔
573
         std::array<W, 2 * N> ze{};
79,175✔
574
         copy_mem(std::span{ze}.template first<N>(), z);
79,175✔
575
         return redc(curve, ze);
79,175✔
576
      }
79,175✔
577

578
      static StorageUnit to_rep(const GenericPrimeOrderCurve* curve, StorageUnit x) {
36,201✔
579
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
36,201✔
580
         curve->_params().mul(z, x, curve->_params().order_monty_r2());
36,201✔
581
         return redc(curve, z);
36,201✔
582
      }
583

584
      static StorageUnit wide_to_rep(const GenericPrimeOrderCurve* curve, std::array<W, 2 * N> x) {
9,648✔
585
         auto redc_x = redc(curve, x);
9,648✔
586
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
9,648✔
587
         curve->_params().mul(z, redc_x, curve->_params().order_monty_r3());
9,648✔
588
         return redc(curve, z);
9,648✔
589
      }
590

591
      const GenericPrimeOrderCurve* m_curve;
592
      StorageUnit m_val;
593
};
594

595
class GenericField final {
596
   public:
597
      typedef word W;
598
      typedef PrimeOrderCurve::StorageUnit StorageUnit;
599
      static constexpr size_t N = PrimeOrderCurve::StorageWords;
600

601
      static std::optional<GenericField> deserialize(const GenericPrimeOrderCurve* curve,
73,872✔
602
                                                     std::span<const uint8_t> bytes) {
603
         const size_t len = curve->_params().field_bytes();
73,872✔
604

605
         if(bytes.size() != len) {
73,872✔
606
            return {};
×
607
         }
608

609
         const auto words = bytes_to_words<N>(bytes);
73,872✔
610

611
         if(words) {
73,872✔
612
            if(!bigint_ct_is_lt(words->data(), N, curve->_params().field().data(), N).as_bool()) {
73,872✔
613
               return {};
4,804✔
614
            }
615

616
            // Safe because we checked above that words is an integer < P
617
            return GenericField::from_words(curve, *words);
69,068✔
618
         } else {
619
            return {};
×
620
         }
621
      }
622

623
      static GenericField from_words(const GenericPrimeOrderCurve* curve, const std::array<word, N>& words) {
71,272✔
624
         return GenericField(curve, to_rep(curve, words));
71,272✔
625
      }
626

627
      static GenericField zero(const GenericPrimeOrderCurve* curve) {
740,236✔
628
         const StorageUnit zeros{};
740,236✔
629
         return GenericField(curve, zeros);
740,236✔
630
      }
631

632
      static GenericField one(const GenericPrimeOrderCurve* curve) {
1,263,406✔
633
         return GenericField(curve, curve->_params().field_monty_r1());
1,263,406✔
634
      }
635

636
      static GenericField curve_a(const GenericPrimeOrderCurve* curve) {
427,083✔
637
         return GenericField(curve, curve->_params().monty_curve_a());
427,083✔
638
      }
639

640
      static GenericField curve_b(const GenericPrimeOrderCurve* curve) {
20,244✔
641
         return GenericField(curve, curve->_params().monty_curve_b());
20,244✔
642
      }
643

644
      static GenericField random(const GenericPrimeOrderCurve* curve, RandomNumberGenerator& rng) {
51,204✔
645
         constexpr size_t MAX_ATTEMPTS = 1000;
51,204✔
646

647
         const size_t bits = curve->_params().field_bits();
51,204✔
648

649
         std::vector<uint8_t> buf(curve->_params().field_bytes());
51,204✔
650

651
         for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
56,008✔
652
            rng.randomize(buf);
56,008✔
653

654
            // Zero off high bits that if set would certainly cause us
655
            // to be out of range
656
            if(bits % 8 != 0) {
56,008✔
657
               const uint8_t mask = 0xFF >> (8 - (bits % 8));
10,812✔
658
               buf[0] &= mask;
10,812✔
659
            }
660

661
            if(auto s = GenericField::deserialize(curve, buf)) {
56,008✔
662
               if(s.value().is_nonzero().as_bool()) {
51,204✔
663
                  return s.value();
51,204✔
664
               }
665
            }
666
         }
667

668
         throw Internal_Error("Failed to generate random Scalar within bounded number of attempts");
×
669
      }
51,204✔
670

671
      /**
672
      * Return the value of this divided by 2
673
      */
674
      GenericField div2() const {
804,793✔
675
         StorageUnit t = value();
804,793✔
676
         const W borrow = shift_right<1>(t);
804,793✔
677

678
         // If value was odd, add (P/2)+1
679
         bigint_cnd_add(borrow, t.data(), m_curve->_params().field_inv_2().data(), N);
804,793✔
680

681
         return GenericField(m_curve, t);
804,793✔
682
      }
683

684
      /// Return (*this) multiplied by 2
685
      GenericField mul2() const {
10,554,150✔
686
         StorageUnit t = value();
10,554,150✔
687
         const W carry = shift_left<1>(t);
10,554,150✔
688

689
         StorageUnit r;
10,554,150✔
690
         bigint_monty_maybe_sub<N>(r.data(), carry, t.data(), m_curve->_params().field().data());
10,554,150✔
691
         return GenericField(m_curve, r);
10,554,150✔
692
      }
693

694
      /// Return (*this) multiplied by 3
695
      GenericField mul3() const { return mul2() + (*this); }
2,954,689✔
696

697
      /// Return (*this) multiplied by 4
698
      GenericField mul4() const { return mul2().mul2(); }
177,058✔
699

700
      /// Return (*this) multiplied by 8
701
      GenericField mul8() const { return mul2().mul2().mul2(); }
177,058✔
702

703
      friend GenericField operator+(const GenericField& a, const GenericField& b) {
24,166,554✔
704
         const auto* curve = check_curve(a, b);
48,333,108✔
705
         const size_t words = curve->_params().words();
24,166,554✔
706

707
         StorageUnit t{};
24,166,554✔
708
         const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words);
48,333,108✔
709

710
         StorageUnit r{};
24,166,554✔
711
         bigint_monty_maybe_sub(words, r.data(), carry, t.data(), curve->_params().field().data());
24,166,554✔
712
         return GenericField(curve, r);
24,166,554✔
713
      }
714

715
      friend GenericField operator-(const GenericField& a, const GenericField& b) { return a + b.negate(); }
18,466,566✔
716

717
      friend GenericField operator*(const GenericField& a, const GenericField& b) {
20,866,013✔
718
         const auto* curve = check_curve(a, b);
41,732,026✔
719

720
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
20,866,013✔
721
         curve->_params().mul(z, a.value(), b.value());
20,866,013✔
722
         return GenericField(curve, redc(curve, z));
20,866,013✔
723
      }
724

725
      GenericField& operator*=(const GenericField& other) {
6,758,219✔
726
         const auto* curve = check_curve(*this, other);
13,516,438✔
727

728
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
6,758,219✔
729
         curve->_params().mul(z, value(), other.value());
6,758,219✔
730
         m_val = redc(curve, z);
6,758,219✔
731
         return (*this);
6,758,219✔
732
      }
733

734
      GenericField square() const {
26,833,562✔
735
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
26,833,562✔
736
         m_curve->_params().sqr(z, value());
26,833,562✔
737
         return GenericField(m_curve, redc(m_curve, z));
26,833,562✔
738
      }
739

740
      GenericField pow_vartime(const StorageUnit& exp) const {
35,159✔
741
         auto one = GenericField::one(curve());
35,159✔
742
         auto bits = curve()->_params().field_bits();
35,159✔
743
         auto words = curve()->_params().words();
35,159✔
744
         return impl_pow_vartime(*this, one, bits, std::span{exp}.last(words));
70,318✔
745
      }
746

747
      GenericField negate() const {
18,790,361✔
748
         auto x_is_zero = CT::all_zeros(this->data(), N);
37,580,722✔
749

750
         StorageUnit r;
18,790,361✔
751
         bigint_sub3(r.data(), m_curve->_params().field().data(), N, this->data(), N);
18,790,361✔
752
         x_is_zero.if_set_zero_out(r.data(), N);
18,790,361✔
753
         return GenericField(m_curve, r);
18,790,361✔
754
      }
755

756
      GenericField invert() const { return pow_vartime(m_curve->_params().field_minus_2()); }
30,463✔
757

758
      GenericField invert_vartime() const {
1,825✔
759
         // TODO take advantage of variable time here using eg BEEA
760
         // see IntMod::invert_vartime in pcurves_impl.h
761
         return invert();
1,825✔
762
      }
763

764
      template <concepts::resizable_byte_buffer T>
765
      T serialize() const {
3,644✔
766
         T bytes(m_curve->_params().field_bytes());
3,644✔
767
         serialize_to(bytes);
3,644✔
768
         return bytes;
3,644✔
769
      }
×
770

771
      void serialize_to(std::span<uint8_t> bytes) const {
55,352✔
772
         auto v = from_rep(m_curve, m_val);
55,352✔
773
         std::reverse(v.begin(), v.end());
55,352✔
774

775
         const size_t flen = m_curve->_params().field_bytes();
55,352✔
776
         BOTAN_ARG_CHECK(bytes.size() == flen, "Expected output span provided");
55,352✔
777

778
         // Remove leading zero bytes
779
         const auto padded_bytes = store_be(v);
55,352✔
780
         const size_t extra = N * WordInfo<W>::bytes - flen;
55,352✔
781
         copy_mem(bytes, std::span{padded_bytes}.subspan(extra, flen));
55,352✔
782
      }
55,352✔
783

784
      CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_curve->_params().words()).as_choice(); }
7,268,390✔
785

786
      CT::Choice is_nonzero() const { return !is_zero(); }
51,204✔
787

788
      CT::Choice operator==(const GenericField& other) const {
24,250✔
789
         if(this->m_curve != other.m_curve) {
24,250✔
790
            return CT::Choice::no();
×
791
         }
792

793
         return CT::is_equal(m_val.data(), other.m_val.data(), m_curve->_params().words()).as_choice();
24,250✔
794
      }
795

796
      const StorageUnit& stash_value() const { return m_val; }
797

798
      const GenericPrimeOrderCurve* curve() const { return m_curve; }
35,159✔
799

800
      CT::Choice is_even() const {
2,871✔
801
         auto v = from_rep(m_curve, m_val);
2,871✔
802
         return !CT::Choice::from_int(v[0] & 0x01);
2,871✔
803
      }
804

805
      /**
806
      * Convert the integer to standard representation and return the sequence of words
807
      */
808
      StorageUnit to_words() const { return from_rep(m_curve, m_val); }
2,204✔
809

810
      void _const_time_poison() const { CT::poison(m_val); }
12,868✔
811

812
      void _const_time_unpoison() const { CT::unpoison(m_val); }
12,868✔
813

814
      static void conditional_swap(CT::Choice cond, GenericField& x, GenericField& y) {
25,248✔
815
         const W mask = cond.into_bitmask<W>();
25,248✔
816

817
         for(size_t i = 0; i != N; ++i) {
252,480✔
818
            auto nx = choose(mask, y.m_val[i], x.m_val[i]);
227,232✔
819
            auto ny = choose(mask, x.m_val[i], y.m_val[i]);
227,232✔
820
            x.m_val[i] = nx;
227,232✔
821
            y.m_val[i] = ny;
227,232✔
822
         }
823
      }
824

825
      void conditional_assign(CT::Choice cond, const GenericField& nx) {
315,569✔
826
         const W mask = cond.into_bitmask<W>();
315,569✔
827

828
         for(size_t i = 0; i != N; ++i) {
3,155,690✔
829
            m_val[i] = choose(mask, nx.m_val[i], m_val[i]);
2,840,121✔
830
         }
831
      }
832

833
      /**
834
      * Conditional assignment
835
      *
836
      * If `cond` is true, sets `x` to `nx` and `y` to `ny`
837
      */
838
      static void conditional_assign(
18,628,377✔
839
         GenericField& x, GenericField& y, CT::Choice cond, const GenericField& nx, const GenericField& ny) {
840
         const W mask = cond.into_bitmask<W>();
18,628,377✔
841

842
         for(size_t i = 0; i != N; ++i) {
186,283,770✔
843
            x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
167,655,393✔
844
            y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
167,655,393✔
845
         }
846
      }
847

848
      /**
849
      * Conditional assignment
850
      *
851
      * If `cond` is true, sets `x` to `nx`, `y` to `ny`, and `z` to `nz`
852
      */
853
      static void conditional_assign(GenericField& x,
2,711,247✔
854
                                     GenericField& y,
855
                                     GenericField& z,
856
                                     CT::Choice cond,
857
                                     const GenericField& nx,
858
                                     const GenericField& ny,
859
                                     const GenericField& nz) {
860
         const W mask = cond.into_bitmask<W>();
2,711,247✔
861

862
         for(size_t i = 0; i != N; ++i) {
27,112,470✔
863
            x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
24,401,223✔
864
            y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
24,401,223✔
865
            z.m_val[i] = choose(mask, nz.m_val[i], z.m_val[i]);
24,401,223✔
866
         }
867
      }
2,711,247✔
868

869
      std::pair<GenericField, CT::Choice> sqrt() const {
2,871✔
870
         BOTAN_STATE_CHECK(m_curve->_params().field()[0] % 4 == 3);
2,871✔
871

872
         auto z = pow_vartime(m_curve->_params().field_p_plus_1_over_4());
2,871✔
873
         const CT::Choice correct = (z.square() == *this);
2,871✔
874
         // Zero out the return value if it would otherwise be incorrect
875
         z.conditional_assign(!correct, zero(m_curve));
5,742✔
876
         return {z, correct};
2,871✔
877
      }
878

879
      GenericField(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {}
1,256,493✔
880

881
   private:
882
      const StorageUnit& value() const { return m_val; }
54,457,794✔
883

884
      const W* data() const { return m_val.data(); }
61,747,276✔
885

886
      static const GenericPrimeOrderCurve* check_curve(const GenericField& a, const GenericField& b) {
51,790,786✔
887
         BOTAN_STATE_CHECK(a.m_curve == b.m_curve);
51,790,786✔
888
         return a.m_curve;
51,790,786✔
889
      }
890

891
      static StorageUnit redc(const GenericPrimeOrderCurve* curve, std::array<W, 2 * N> z) {
54,591,697✔
892
         const auto& mod = curve->_params().field();
54,591,697✔
893
         const size_t words = curve->_params().words();
54,591,697✔
894
         StorageUnit r{};
54,591,697✔
895
         StorageUnit ws{};
54,591,697✔
896
         bigint_monty_redc(
54,591,697✔
897
            r.data(), z.data(), mod.data(), words, curve->_params().field_p_dash(), ws.data(), ws.size());
54,591,697✔
898
         return r;
54,591,697✔
899
      }
900

901
      static StorageUnit from_rep(const GenericPrimeOrderCurve* curve, StorageUnit z) {
62,631✔
902
         std::array<W, 2 * N> ze{};
62,631✔
903
         copy_mem(std::span{ze}.template first<N>(), z);
62,631✔
904
         return redc(curve, ze);
62,631✔
905
      }
906

907
      static StorageUnit to_rep(const GenericPrimeOrderCurve* curve, StorageUnit x) {
71,272✔
908
         std::array<W, 2 * N> z{};
71,272✔
909
         curve->_params().mul(z, x, curve->_params().field_monty_r2());
71,272✔
910
         return redc(curve, z);
71,272✔
911
      }
912

913
      const GenericPrimeOrderCurve* m_curve;
914
      StorageUnit m_val;
915
};
916

917
/**
918
* Affine Curve Point
919
*
920
* This contains a pair of integers (x,y) which satisfy the curve equation
921
*/
922
class GenericAffinePoint final {
923
   public:
924
      GenericAffinePoint(const GenericField& x, const GenericField& y) : m_x(x), m_y(y) {}
424,485✔
925

926
      explicit GenericAffinePoint(const GenericPrimeOrderCurve* curve) :
927
            m_x(GenericField::zero(curve)), m_y(GenericField::zero(curve)) {}
928

929
      static GenericAffinePoint identity(const GenericPrimeOrderCurve* curve) {
732,585✔
930
         return GenericAffinePoint(GenericField::zero(curve), GenericField::zero(curve));
732,585✔
931
      }
932

933
      static GenericAffinePoint identity(const GenericAffinePoint& pt) { return identity(pt.curve()); }
415,863✔
934

935
      CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); }
1,287,315✔
936

937
      GenericAffinePoint negate() const { return GenericAffinePoint(x(), y().negate()); }
4,284✔
938

939
      /**
940
      * Serialize the point in uncompressed format
941
      */
942
      void serialize_to(std::span<uint8_t> bytes) const {
25,854✔
943
         const size_t fe_bytes = curve()->_params().field_bytes();
25,854✔
944
         BOTAN_ARG_CHECK(bytes.size() == 1 + 2 * fe_bytes, "Buffer size incorrect");
25,854✔
945
         BOTAN_STATE_CHECK(this->is_identity().as_bool() == false);
25,854✔
946
         BufferStuffer pack(bytes);
25,854✔
947
         pack.append(0x04);
25,854✔
948
         x().serialize_to(pack.next(fe_bytes));
25,854✔
949
         y().serialize_to(pack.next(fe_bytes));
25,854✔
950
         BOTAN_DEBUG_ASSERT(pack.full());
25,854✔
951
      }
25,854✔
952

953
      /**
954
      * If idx is zero then return the identity element. Otherwise return pts[idx - 1]
955
      *
956
      * Returns the identity element also if idx is out of range
957
      */
958
      static auto ct_select(std::span<const GenericAffinePoint> pts, size_t idx) {
316,578✔
959
         BOTAN_ARG_CHECK(!pts.empty(), "Cannot select from an empty set");
316,578✔
960
         auto result = GenericAffinePoint::identity(pts[0].curve());
316,578✔
961

962
         // Intentionally wrapping; set to maximum size_t if idx == 0
963
         const size_t idx1 = static_cast<size_t>(idx - 1);
316,578✔
964
         for(size_t i = 0; i != pts.size(); ++i) {
10,447,074✔
965
            const auto found = CT::Mask<size_t>::is_equal(idx1, i).as_choice();
10,130,496✔
966
            result.conditional_assign(found, pts[i]);
10,130,496✔
967
         }
968

969
         return result;
316,578✔
970
      }
971

972
      /**
973
      * Return (x^3 + A*x + B) mod p
974
      */
975
      static GenericField x3_ax_b(const GenericField& x) {
20,244✔
976
         return (x.square() + GenericField::curve_a(x.curve())) * x + GenericField::curve_b(x.curve());
20,244✔
977
      }
978

979
      /**
980
      * Point deserialization
981
      *
982
      * This accepts compressed or uncompressed formats.
983
      */
984
      static std::optional<GenericAffinePoint> deserialize(const GenericPrimeOrderCurve* curve,
8,648✔
985
                                                           std::span<const uint8_t> bytes) {
986
         const size_t fe_bytes = curve->_params().field_bytes();
8,648✔
987

988
         if(bytes.size() == 1 + 2 * fe_bytes && bytes[0] == 0x04) {
8,648✔
989
            auto x = GenericField::deserialize(curve, bytes.subspan(1, fe_bytes));
5,497✔
990
            auto y = GenericField::deserialize(curve, bytes.subspan(1 + fe_bytes, fe_bytes));
5,497✔
991

992
            if(x && y) {
5,497✔
993
               const auto lhs = (*y).square();
5,497✔
994
               const auto rhs = GenericAffinePoint::x3_ax_b(*x);
5,497✔
995
               if((lhs == rhs).as_bool()) {
5,497✔
996
                  return GenericAffinePoint(*x, *y);
5,441✔
997
               }
998
            }
999
         } else if(bytes.size() == 1 + fe_bytes && (bytes[0] == 0x02 || bytes[0] == 0x03)) {
3,151✔
1000
            const CT::Choice y_is_even = CT::Mask<uint8_t>::is_equal(bytes[0], 0x02).as_choice();
2,871✔
1001

1002
            if(auto x = GenericField::deserialize(curve, bytes.subspan(1, fe_bytes))) {
2,871✔
1003
               auto [y, is_square] = x3_ax_b(*x).sqrt();
2,871✔
1004

1005
               if(is_square.as_bool()) {
2,871✔
1006
                  const auto flip_y = y_is_even != y.is_even();
2,871✔
1007
                  y.conditional_assign(flip_y, y.negate());
2,871✔
1008
                  return GenericAffinePoint(*x, y);
2,871✔
1009
               }
1010
            }
1011
         } else if(bytes.size() == 1 && bytes[0] == 0x00) {
280✔
1012
            // See SEC1 section 2.3.4
1013
            return GenericAffinePoint::identity(curve);
144✔
1014
         }
1015

1016
         return {};
192✔
1017
      }
1018

1019
      /**
1020
      * Return the affine x coordinate
1021
      */
1022
      const GenericField& x() const { return m_x; }
1,224,073✔
1023

1024
      /**
1025
      * Return the affine y coordinate
1026
      */
1027
      const GenericField& y() const { return m_y; }
916,388✔
1028

1029
      /**
1030
      * Conditional assignment of an affine point
1031
      */
1032
      void conditional_assign(CT::Choice cond, const GenericAffinePoint& pt) {
18,628,377✔
1033
         GenericField::conditional_assign(m_x, m_y, cond, pt.x(), pt.y());
18,628,377✔
1034
      }
18,628,377✔
1035

1036
      const GenericPrimeOrderCurve* curve() const { return m_x.curve(); }
758,295✔
1037

1038
      void _const_time_poison() const { CT::poison_all(m_x, m_y); }
1039

1040
      void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y); }
1041

1042
   private:
1043
      GenericField m_x;
1044
      GenericField m_y;
1045
};
1046

1047
class GenericProjectivePoint final {
1048
   public:
1049
      typedef GenericProjectivePoint Self;
1050

1051
      using FieldElement = GenericField;
1052

1053
      /**
1054
      * Convert a point from affine to projective form
1055
      */
1056
      static Self from_affine(const GenericAffinePoint& pt) {
25,248✔
1057
         auto x = pt.x();
25,248✔
1058
         auto y = pt.y();
25,248✔
1059
         auto z = GenericField::one(x.curve());
25,248✔
1060

1061
         // If pt is identity (0,0) swap y/z to convert (0,0,1) into (0,1,0)
1062
         GenericField::conditional_swap(pt.is_identity(), y, z);
25,248✔
1063
         return GenericProjectivePoint(x, y, z);
25,248✔
1064
      }
1065

1066
      /**
1067
      * Return the identity element
1068
      */
1069
      static Self identity(const GenericPrimeOrderCurve* curve) {
1070
         return Self(GenericField::zero(curve), GenericField::one(curve), GenericField::zero(curve));
1071
      }
1072

1073
      /**
1074
      * Default constructor: the identity element
1075
      */
1076
      explicit GenericProjectivePoint(const GenericPrimeOrderCurve* curve) :
4,780✔
1077
            m_x(GenericField::zero(curve)), m_y(GenericField::one(curve)), m_z(GenericField::zero(curve)) {}
4,780✔
1078

1079
      /**
1080
      * Affine constructor: take x/y coordinates
1081
      */
1082
      GenericProjectivePoint(const GenericField& x, const GenericField& y) :
1083
            m_x(x), m_y(y), m_z(GenericField::one(m_x.curve())) {}
1084

1085
      /**
1086
      * Projective constructor: take x/y/z coordinates
1087
      */
1088
      GenericProjectivePoint(const GenericField& x, const GenericField& y, const GenericField& z) :
2,377,974✔
1089
            m_x(x), m_y(y), m_z(z) {}
2,334,099✔
1090

1091
      friend Self operator+(const Self& a, const Self& b) { return Self::add(a, b); }
154,371✔
1092

1093
      friend Self operator+(const Self& a, const GenericAffinePoint& b) { return Self::add_mixed(a, b); }
52,911✔
1094

1095
      friend Self operator+(const GenericAffinePoint& a, const Self& b) { return Self::add_mixed(b, a); }
30,784✔
1096

1097
      Self& operator+=(const Self& other) {
1098
         (*this) = (*this) + other;
1099
         return (*this);
1100
      }
1101

1102
      Self& operator+=(const GenericAffinePoint& other) {
802,729✔
1103
         (*this) = (*this) + other;
802,729✔
1104
         return (*this);
802,729✔
1105
      }
1106

1107
      CT::Choice is_identity() const { return z().is_zero(); }
1,783,005✔
1108

1109
      void conditional_assign(CT::Choice cond, const Self& pt) {
6,751✔
1110
         GenericField::conditional_assign(m_x, m_y, m_z, cond, pt.x(), pt.y(), pt.z());
6,751✔
1111
      }
1112

1113
      /**
1114
      * Mixed (projective + affine) point addition
1115
      */
1116
      static Self add_mixed(const Self& a, const GenericAffinePoint& b) {
888,392✔
1117
         return point_add_mixed<Self, GenericAffinePoint, GenericField>(a, b, GenericField::one(a.curve()));
1,776,784✔
1118
      }
1119

1120
      static Self add_or_sub(const Self& a, const GenericAffinePoint& b, CT::Choice sub) {
309,827✔
1121
         return point_add_or_sub_mixed<Self, GenericAffinePoint, GenericField>(a, b, sub, GenericField::one(a.curve()));
619,654✔
1122
      }
1123

1124
      /**
1125
      * Projective point addition
1126
      */
1127
      static Self add(const Self& a, const Self& b) { return point_add<Self, GenericField>(a, b); }
154,371✔
1128

1129
      /**
1130
      * Iterated point doubling
1131
      */
1132
      Self dbl_n(size_t n) const {
804,793✔
1133
         if(curve()->_params().a_is_minus_3()) {
804,793✔
1134
            return dbl_n_a_minus_3(*this, n);
408,936✔
1135
         } else if(curve()->_params().a_is_zero()) {
395,857✔
1136
            return dbl_n_a_zero(*this, n);
40,338✔
1137
         } else {
1138
            const auto A = GenericField::curve_a(curve());
355,519✔
1139
            return dbl_n_generic(*this, A, n);
355,519✔
1140
         }
1141
      }
1142

1143
      /**
1144
      * Point doubling
1145
      */
1146
      Self dbl() const {
177,058✔
1147
         if(curve()->_params().a_is_minus_3()) {
177,058✔
1148
            return dbl_a_minus_3(*this);
75,589✔
1149
         } else if(curve()->_params().a_is_zero()) {
101,469✔
1150
            return dbl_a_zero(*this);
50,149✔
1151
         } else {
1152
            const auto A = GenericField::curve_a(curve());
51,320✔
1153
            return dbl_generic(*this, A);
51,320✔
1154
         }
1155
      }
1156

1157
      /**
1158
      * Point negation
1159
      */
1160
      Self negate() const { return Self(x(), y().negate(), z()); }
13,502✔
1161

1162
      /**
1163
      * Randomize the point representation
1164
      *
1165
      * Projective coordinates are redundant; if (x,y,z) is a projective
1166
      * point then so is (x*r^2,y*r^3,z*r) for any non-zero r.
1167
      */
1168
      void randomize_rep(RandomNumberGenerator& rng) {
51,472✔
1169
         // In certain contexts we may be called with a Null_RNG; in that case the
1170
         // caller is accepting that randomization will not occur
1171

1172
         if(rng.is_seeded()) {
51,472✔
1173
            auto r = GenericField::random(curve(), rng);
51,204✔
1174

1175
            auto r2 = r.square();
51,204✔
1176
            auto r3 = r2 * r;
51,204✔
1177

1178
            m_x *= r2;
51,204✔
1179
            m_y *= r3;
51,204✔
1180
            m_z *= r;
51,204✔
1181
         }
1182
      }
51,472✔
1183

1184
      /**
1185
      * Return the projective x coordinate
1186
      */
1187
      const GenericField& x() const { return m_x; }
1,970,215✔
1188

1189
      /**
1190
      * Return the projective y coordinate
1191
      */
1192
      const GenericField& y() const { return m_y; }
2,925,735✔
1193

1194
      /**
1195
      * Return the projective z coordinate
1196
      */
1197
      const GenericField& z() const { return m_z; }
2,817,043✔
1198

1199
      const GenericPrimeOrderCurve* curve() const { return m_x.curve(); }
2,231,274✔
1200

1201
      void _const_time_poison() const { CT::poison_all(m_x, m_y, m_z); }
12,868✔
1202

1203
      void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y, m_z); }
12,868✔
1204

1205
   private:
1206
      GenericField m_x;
1207
      GenericField m_y;
1208
      GenericField m_z;
1209
};
1210

1211
class GenericCurve final {
1212
   public:
1213
      typedef GenericField FieldElement;
1214
      typedef GenericScalar Scalar;
1215
      typedef GenericAffinePoint AffinePoint;
1216
      typedef GenericProjectivePoint ProjectivePoint;
1217

1218
      typedef word WordType;
1219
      static constexpr size_t Words = PCurve::PrimeOrderCurve::StorageWords;
1220
};
1221

1222
class GenericBlindedScalarBits final {
6,117✔
1223
   public:
1224
      GenericBlindedScalarBits(const GenericScalar& scalar, RandomNumberGenerator& rng, size_t wb) {
13,456✔
1225
         BOTAN_ASSERT_NOMSG(wb == 1 || wb == 2 || wb == 3 || wb == 4 || wb == 5 || wb == 6 || wb == 7);
13,456✔
1226

1227
         const auto& params = scalar.curve()->_params();
13,456✔
1228

1229
         const size_t order_bits = params.order_bits();
13,456✔
1230
         m_window_bits = wb;
13,456✔
1231

1232
         const size_t blinder_bits = scalar_blinding_bits(order_bits);
13,456✔
1233

1234
         if(blinder_bits > 0 && rng.is_seeded()) {
13,456✔
1235
            const size_t mask_words = (blinder_bits + WordInfo<word>::bits - 1) / WordInfo<word>::bits;
13,389✔
1236
            const size_t mask_bytes = mask_words * WordInfo<word>::bytes;
13,389✔
1237

1238
            const size_t words = params.words();
13,389✔
1239

1240
            secure_vector<uint8_t> maskb(mask_bytes);
13,389✔
1241
            rng.randomize(maskb);
13,389✔
1242

1243
            std::array<word, PrimeOrderCurve::StorageWords> mask{};
13,389✔
1244
            load_le(mask.data(), maskb.data(), mask_words);
13,389✔
1245

1246
            // Mask to exactly blinder_bits and set MSB and LSB
1247
            const size_t excess = mask_words * WordInfo<word>::bits - blinder_bits;
13,389✔
1248
            if(excess > 0) {
13,389✔
1249
               mask[mask_words - 1] &= (static_cast<word>(1) << (WordInfo<word>::bits - excess)) - 1;
12,481✔
1250
            }
1251
            const size_t msb_pos = (blinder_bits - 1) % WordInfo<word>::bits;
13,389✔
1252
            mask[(blinder_bits - 1) / WordInfo<word>::bits] |= static_cast<word>(1) << msb_pos;
13,389✔
1253
            mask[0] |= 1;
13,389✔
1254

1255
            std::array<word, 2 * PrimeOrderCurve::StorageWords> mask_n{};
13,389✔
1256

1257
            const auto sw = scalar.to_words();
13,389✔
1258

1259
            // Compute masked scalar s + k*n
1260
            params.mul(mask_n, mask, params.order());
13,389✔
1261
            bigint_add2(mask_n.data(), 2 * words, sw.data(), words);
13,389✔
1262

1263
            std::reverse(mask_n.begin(), mask_n.end());
13,389✔
1264
            m_bytes = store_be<std::vector<uint8_t>>(mask_n);
13,389✔
1265
            m_bits = order_bits + blinder_bits;
13,389✔
1266
         } else {
13,389✔
1267
            // No RNG available, skip blinding
1268
            m_bytes = scalar.serialize<std::vector<uint8_t>>();
67✔
1269
            m_bits = order_bits;
67✔
1270
         }
1271

1272
         m_windows = (m_bits + wb - 1) / wb;
13,456✔
1273
      }
13,456✔
1274

1275
      size_t windows() const { return m_windows; }
1276

1277
      size_t bits() const { return m_bits; }
12,868✔
1278

1279
      size_t get_window(size_t offset) const {
779,523✔
1280
         if(m_window_bits == 1) {
779,523✔
1281
            return read_window_bits<1>(std::span{m_bytes}, offset);
×
1282
         } else if(m_window_bits == 2) {
1283
            return read_window_bits<2>(std::span{m_bytes}, offset);
×
1284
         } else if(m_window_bits == 3) {
1285
            return read_window_bits<3>(std::span{m_bytes}, offset);
94,164✔
1286
         } else if(m_window_bits == 4) {
1287
            return read_window_bits<4>(std::span{m_bytes}, offset);
368,781✔
1288
         } else if(m_window_bits == 5) {
1289
            return read_window_bits<5>(std::span{m_bytes}, offset);
×
1290
         } else if(m_window_bits == 6) {
1291
            return read_window_bits<6>(std::span{m_bytes}, offset);
×
1292
         } else if(m_window_bits == 7) {
1293
            return read_window_bits<7>(std::span{m_bytes}, offset);
316,578✔
1294
         } else {
1295
            BOTAN_ASSERT_UNREACHABLE();
×
1296
         }
1297
      }
1298

1299
   private:
1300
      std::vector<uint8_t> m_bytes;
1301
      size_t m_bits;
1302
      size_t m_windows;
1303
      size_t m_window_bits;
1304
};
1305

1306
class GenericWindowedMul final {
11,058✔
1307
   public:
1308
      static constexpr size_t WindowBits = VarPointWindowBits;
1309
      static constexpr size_t TableSize = (1 << WindowBits) - 1;
1310

1311
      explicit GenericWindowedMul(const GenericAffinePoint& pt) :
5,529✔
1312
            m_table(varpoint_setup<GenericCurve, TableSize>(pt)) {}
5,529✔
1313

1314
      GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) {
5,529✔
1315
         const GenericBlindedScalarBits bits(s, rng, WindowBits);
5,529✔
1316

1317
         return varpoint_exec<GenericCurve, WindowBits>(m_table, bits, rng);
5,529✔
1318
      }
5,529✔
1319

1320
   private:
1321
      AffinePointTable<GenericCurve> m_table;
1322
};
1323

1324
class GenericBaseMulTable final {
294✔
1325
   public:
1326
      static constexpr size_t WindowBits = BasePointWindowBits;
1327

1328
      // +1 for Booth carry from the top window
1329
      explicit GenericBaseMulTable(const GenericAffinePoint& pt) :
147✔
1330
            m_table(basemul_booth_setup<GenericCurve, WindowBits>(pt, blinded_scalar_bits(*pt.curve()) + 1)) {}
441✔
1331

1332
      GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) {
6,751✔
1333
         // W+1 bit windows for Booth recoding overlap
1334
         const GenericBlindedScalarBits scalar(s, rng, WindowBits + 1);
6,751✔
1335
         return basemul_booth_exec<GenericCurve, WindowBits>(m_table, scalar, rng);
6,751✔
1336
      }
6,751✔
1337

1338
   private:
1339
      static size_t blinded_scalar_bits(const GenericPrimeOrderCurve& curve) {
147✔
1340
         const size_t order_bits = curve.order_bits();
147✔
1341
         return order_bits + scalar_blinding_bits(order_bits);
294✔
1342
      }
1343

1344
      std::vector<GenericAffinePoint> m_table;
1345
};
1346

1347
class GenericWindowedMul2 final {
1348
   public:
1349
      static constexpr size_t WindowBits = Mul2PrecompWindowBits;
1350

1351
      GenericWindowedMul2(const GenericWindowedMul2& other) = delete;
1352
      GenericWindowedMul2(GenericWindowedMul2&& other) = delete;
1353
      GenericWindowedMul2& operator=(const GenericWindowedMul2& other) = delete;
1354
      GenericWindowedMul2& operator=(GenericWindowedMul2&& other) = delete;
1355

1356
      ~GenericWindowedMul2() = default;
588✔
1357

1358
      GenericWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) :
588✔
1359
            m_table(mul2_setup<GenericCurve, WindowBits>(p, q)) {}
1,176✔
1360

1361
      GenericProjectivePoint mul2(const GenericScalar& x, const GenericScalar& y, RandomNumberGenerator& rng) const {
588✔
1362
         const GenericBlindedScalarBits x_bits(x, rng, WindowBits);
588✔
1363
         const GenericBlindedScalarBits y_bits(y, rng, WindowBits);
588✔
1364
         return mul2_exec<GenericCurve, WindowBits>(m_table, x_bits, y_bits, rng);
588✔
1365
      }
1,176✔
1366

1367
   private:
1368
      AffinePointTable<GenericCurve> m_table;
1369
};
1370

1371
class GenericVartimeWindowedMul2 final : public PrimeOrderCurve::PrecomputedMul2Table {
1372
   public:
1373
      static constexpr size_t WindowBits = Mul2PrecompWindowBits;
1374

1375
      GenericVartimeWindowedMul2(const GenericVartimeWindowedMul2& other) = delete;
1376
      GenericVartimeWindowedMul2(GenericVartimeWindowedMul2&& other) = delete;
1377
      GenericVartimeWindowedMul2& operator=(const GenericVartimeWindowedMul2& other) = delete;
1378
      GenericVartimeWindowedMul2& operator=(GenericVartimeWindowedMul2&& other) = delete;
1379

1380
      ~GenericVartimeWindowedMul2() override = default;
3,560✔
1381

1382
      GenericVartimeWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) :
1,780✔
1383
            m_table(to_affine_batch<GenericCurve, true>(mul2_setup<GenericCurve, WindowBits>(p, q))) {}
3,560✔
1384

1385
      GenericProjectivePoint mul2_vartime(const GenericScalar& x, const GenericScalar& y) const {
4,780✔
1386
         const auto x_bits = x.serialize<std::vector<uint8_t>>();
4,780✔
1387
         const auto y_bits = y.serialize<std::vector<uint8_t>>();
4,780✔
1388

1389
         const auto& curve = m_table[0].curve();
4,780✔
1390
         auto accum = GenericProjectivePoint(curve);
4,780✔
1391

1392
         const size_t order_bits = curve->order_bits();
4,780✔
1393

1394
         const size_t windows = (order_bits + WindowBits - 1) / WindowBits;
4,780✔
1395

1396
         for(size_t i = 0; i != windows; ++i) {
404,607✔
1397
            auto x_i = read_window_bits<WindowBits>(std::span{x_bits}, (windows - i - 1) * WindowBits);
399,827✔
1398
            auto y_i = read_window_bits<WindowBits>(std::span{y_bits}, (windows - i - 1) * WindowBits);
399,827✔
1399

1400
            if(i > 0) {
399,827✔
1401
               accum = accum.dbl_n(WindowBits);
395,047✔
1402
            }
1403

1404
            const size_t idx = (y_i << WindowBits) + x_i;
399,827✔
1405

1406
            if(idx > 0) {
399,827✔
1407
               accum += m_table[idx - 1];
392,983✔
1408
            }
1409
         }
1410

1411
         return accum;
4,780✔
1412
      }
9,560✔
1413

1414
   private:
1415
      std::vector<GenericAffinePoint> m_table;
1416
};
1417

1418
GenericPrimeOrderCurve::GenericPrimeOrderCurve(
147✔
1419
   const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) :
147✔
1420
      m_params(std::make_unique<GenericCurveParams>(p, a, b, base_x, base_y, order)) {}
147✔
1421

1422
void GenericPrimeOrderCurve::_precompute_base_mul() {
147✔
1423
   BOTAN_STATE_CHECK(m_basemul == nullptr);
147✔
1424
   m_basemul = std::make_unique<GenericBaseMulTable>(from_stash(generator()));
147✔
1425
}
147✔
1426

1427
size_t GenericPrimeOrderCurve::order_bits() const {
4,927✔
1428
   return _params().order_bits();
4,927✔
1429
}
1430

1431
size_t GenericPrimeOrderCurve::scalar_bytes() const {
×
1432
   return _params().order_bytes();
×
1433
}
1434

1435
size_t GenericPrimeOrderCurve::field_element_bytes() const {
60,666✔
1436
   return _params().field_bytes();
60,666✔
1437
}
1438

1439
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul_by_g(const Scalar& scalar,
5,531✔
1440
                                                                  RandomNumberGenerator& rng) const {
1441
   BOTAN_STATE_CHECK(m_basemul != nullptr);
5,531✔
1442
   return stash(m_basemul->mul(from_stash(scalar), rng));
5,531✔
1443
}
1444

1445
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::base_point_mul_x_mod_order(const Scalar& scalar,
1,220✔
1446
                                                                           RandomNumberGenerator& rng) const {
1447
   BOTAN_STATE_CHECK(m_basemul != nullptr);
1,220✔
1448
   auto pt_s = m_basemul->mul(from_stash(scalar), rng);
1,220✔
1449
   const auto x_bytes = to_affine_x<GenericCurve>(pt_s).serialize<secure_vector<uint8_t>>();
1,220✔
1450
   if(auto s = GenericScalar::from_wide_bytes(this, x_bytes)) {
1,220✔
1451
      return stash(*s);
1,220✔
1452
   } else {
1453
      throw Internal_Error("Failed to convert x coordinate to integer modulo scalar");
×
1454
   }
1455
}
1,220✔
1456

1457
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul(const AffinePoint& pt,
3,105✔
1458
                                                             const Scalar& scalar,
1459
                                                             RandomNumberGenerator& rng) const {
1460
   GenericWindowedMul pt_table(from_stash(pt));
3,105✔
1461
   return stash(pt_table.mul(from_stash(scalar), rng));
3,105✔
1462
}
3,105✔
1463

1464
secure_vector<uint8_t> GenericPrimeOrderCurve::mul_x_only(const AffinePoint& pt,
2,424✔
1465
                                                          const Scalar& scalar,
1466
                                                          RandomNumberGenerator& rng) const {
1467
   GenericWindowedMul pt_table(from_stash(pt));
2,424✔
1468
   auto pt_s = pt_table.mul(from_stash(scalar), rng);
2,424✔
1469
   return to_affine_x<GenericCurve>(pt_s).serialize<secure_vector<uint8_t>>();
2,424✔
1470
}
2,424✔
1471

1472
std::unique_ptr<const PrimeOrderCurve::PrecomputedMul2Table> GenericPrimeOrderCurve::mul2_setup_g(
1,780✔
1473
   const AffinePoint& q) const {
1474
   return std::make_unique<GenericVartimeWindowedMul2>(from_stash(generator()), from_stash(q));
3,560✔
1475
}
1476

1477
std::optional<PrimeOrderCurve::ProjectivePoint> GenericPrimeOrderCurve::mul2_vartime(const PrecomputedMul2Table& tableb,
768✔
1478
                                                                                     const Scalar& s1,
1479
                                                                                     const Scalar& s2) const {
1480
   const auto& tbl = dynamic_cast<const GenericVartimeWindowedMul2&>(tableb);
768✔
1481
   auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2));
768✔
1482
   if(pt.is_identity().as_bool()) {
768✔
1483
      return {};
×
1484
   } else {
1485
      return stash(pt);
768✔
1486
   }
1487
}
1488

1489
std::optional<PrimeOrderCurve::ProjectivePoint> GenericPrimeOrderCurve::mul_px_qy(
588✔
1490
   const AffinePoint& p, const Scalar& x, const AffinePoint& q, const Scalar& y, RandomNumberGenerator& rng) const {
1491
   const GenericWindowedMul2 table(from_stash(p), from_stash(q));
588✔
1492
   auto pt = table.mul2(from_stash(x), from_stash(y), rng);
588✔
1493
   if(pt.is_identity().as_bool()) {
588✔
1494
      return {};
84✔
1495
   } else {
1496
      return stash(pt);
504✔
1497
   }
1498
}
588✔
1499

1500
bool GenericPrimeOrderCurve::mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& tableb,
4,012✔
1501
                                                         const Scalar& v,
1502
                                                         const Scalar& s1,
1503
                                                         const Scalar& s2) const {
1504
   const auto& tbl = dynamic_cast<const GenericVartimeWindowedMul2&>(tableb);
4,012✔
1505
   auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2));
4,012✔
1506

1507
   if(!pt.is_identity().as_bool()) {
4,012✔
1508
      const auto z2 = pt.z().square();
3,999✔
1509

1510
      const auto v_bytes = from_stash(v).serialize<std::vector<uint8_t>>();
3,999✔
1511

1512
      if(auto fe_v = GenericField::deserialize(this, v_bytes)) {
3,999✔
1513
         if((*fe_v * z2 == pt.x()).as_bool()) {
3,999✔
1514
            return true;
1,614✔
1515
         }
1516

1517
         if(_params().order_is_less_than_field()) {
2,392✔
1518
            const auto n = GenericField::from_words(this, _params().order());
2,204✔
1519
            const auto neg_n = n.negate().to_words();
2,204✔
1520

1521
            const auto vw = fe_v->to_words();
2,204✔
1522
            if(bigint_ct_is_lt(vw.data(), vw.size(), neg_n.data(), neg_n.size()).as_bool()) {
2,204✔
1523
               return (((*fe_v + n) * z2) == pt.x()).as_bool();
7✔
1524
            }
1525
         }
1526
      }
1527
   }
3,999✔
1528

1529
   return false;
1530
}
1531

1532
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::generator() const {
1,927✔
1533
   return PrimeOrderCurve::AffinePoint::_create(shared_from_this(), _params().base_x(), _params().base_y());
1,927✔
1534
}
1535

1536
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_to_affine(const ProjectivePoint& pt) const {
11,876✔
1537
   auto affine = to_affine<GenericCurve>(from_stash(pt));
11,876✔
1538

1539
   const auto y2 = affine.y().square();
11,876✔
1540
   const auto x3_ax_b = GenericCurve::AffinePoint::x3_ax_b(affine.x());
11,876✔
1541
   const auto valid_point = affine.is_identity() || (y2 == x3_ax_b);
11,876✔
1542

1543
   BOTAN_ASSERT(valid_point.as_bool(), "Computed point is on the curve");
11,876✔
1544

1545
   return stash(affine);
11,876✔
1546
}
1547

1548
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::point_add(const AffinePoint& a, const AffinePoint& b) const {
1,968✔
1549
   return stash(GenericProjectivePoint::from_affine(from_stash(a)) + from_stash(b));
1,968✔
1550
}
1551

1552
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_negate(const AffinePoint& pt) const {
2,142✔
1553
   return stash(from_stash(pt).negate());
2,142✔
1554
}
1555

1556
bool GenericPrimeOrderCurve::affine_point_is_identity(const AffinePoint& pt) const {
26,118✔
1557
   return from_stash(pt).is_identity().as_bool();
26,118✔
1558
}
1559

1560
void GenericPrimeOrderCurve::serialize_point(std::span<uint8_t> bytes, const AffinePoint& pt) const {
25,854✔
1561
   from_stash(pt).serialize_to(bytes);
25,854✔
1562
}
25,854✔
1563

1564
void GenericPrimeOrderCurve::serialize_scalar(std::span<uint8_t> bytes, const Scalar& scalar) const {
46,665✔
1565
   BOTAN_ARG_CHECK(bytes.size() == _params().order_bytes(), "Invalid length to serialize_scalar");
46,665✔
1566
   from_stash(scalar).serialize_to(bytes);
46,665✔
1567
}
46,665✔
1568

1569
std::optional<PrimeOrderCurve::Scalar> GenericPrimeOrderCurve::deserialize_scalar(
12,268✔
1570
   std::span<const uint8_t> bytes) const {
1571
   if(auto s = GenericScalar::deserialize(this, bytes)) {
12,268✔
1572
      return stash(s.value());
12,069✔
1573
   } else {
1574
      return {};
199✔
1575
   }
1576
}
1577

1578
std::optional<PrimeOrderCurve::Scalar> GenericPrimeOrderCurve::scalar_from_wide_bytes(
8,428✔
1579
   std::span<const uint8_t> bytes) const {
1580
   if(auto s = GenericScalar::from_wide_bytes(this, bytes)) {
8,428✔
1581
      return stash(s.value());
8,428✔
1582
   } else {
1583
      return {};
×
1584
   }
1585
}
1586

1587
std::optional<PrimeOrderCurve::AffinePoint> GenericPrimeOrderCurve::deserialize_point(
8,648✔
1588
   std::span<const uint8_t> bytes) const {
1589
   if(auto pt = GenericAffinePoint::deserialize(this, bytes)) {
8,648✔
1590
      return stash(pt.value());
8,456✔
1591
   } else {
1592
      return {};
192✔
1593
   }
1594
}
1595

1596
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_add(const Scalar& a, const Scalar& b) const {
7,732✔
1597
   return stash(from_stash(a) + from_stash(b));
7,732✔
1598
}
1599

1600
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_sub(const Scalar& a, const Scalar& b) const {
6,289✔
1601
   return stash(from_stash(a) - from_stash(b));
6,289✔
1602
}
1603

1604
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_mul(const Scalar& a, const Scalar& b) const {
27,662✔
1605
   return stash(from_stash(a) * from_stash(b));
27,662✔
1606
}
1607

1608
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_square(const Scalar& s) const {
1,164✔
1609
   return stash(from_stash(s).square());
1,164✔
1610
}
1611

1612
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert(const Scalar& s) const {
5,809✔
1613
   return stash(from_stash(s).invert());
11,618✔
1614
}
1615

1616
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert_vartime(const Scalar& s) const {
5,507✔
1617
   return stash(from_stash(s).invert_vartime());
5,507✔
1618
}
1619

1620
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_negate(const Scalar& s) const {
5,298✔
1621
   return stash(from_stash(s).negate());
5,298✔
1622
}
1623

1624
bool GenericPrimeOrderCurve::scalar_is_zero(const Scalar& s) const {
13,517✔
1625
   return from_stash(s).is_zero().as_bool();
13,517✔
1626
}
1627

1628
bool GenericPrimeOrderCurve::scalar_equal(const Scalar& a, const Scalar& b) const {
×
1629
   return (from_stash(a) == from_stash(b)).as_bool();
×
1630
}
1631

1632
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_one() const {
44✔
1633
   return stash(GenericScalar::one(this));
44✔
1634
}
1635

1636
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::random_scalar(RandomNumberGenerator& rng) const {
18,637✔
1637
   return stash(GenericScalar::random(this, rng));
18,637✔
1638
}
1639

1640
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::stash(const GenericScalar& s) const {
99,859✔
1641
   return Scalar::_create(shared_from_this(), s.stash_value());
99,859✔
1642
}
1643

1644
GenericScalar GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::Scalar& s) const {
188,341✔
1645
   BOTAN_ARG_CHECK(s._curve().get() == this, "Curve mismatch");
188,341✔
1646
   return GenericScalar(this, s._value());
188,341✔
1647
}
1648

1649
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::stash(const GenericAffinePoint& pt) const {
22,474✔
1650
   auto x_w = pt.x().stash_value();
22,474✔
1651
   auto y_w = pt.y().stash_value();
22,474✔
1652
   return AffinePoint::_create(shared_from_this(), x_w, y_w);
22,474✔
1653
}
1654

1655
GenericAffinePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::AffinePoint& pt) const {
68,462✔
1656
   BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch");
68,462✔
1657
   auto x = GenericField(this, pt._x());
68,462✔
1658
   auto y = GenericField(this, pt._y());
68,462✔
1659
   return GenericAffinePoint(x, y);
68,462✔
1660
}
1661

1662
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::stash(const GenericProjectivePoint& pt) const {
11,876✔
1663
   auto x_w = pt.x().stash_value();
11,876✔
1664
   auto y_w = pt.y().stash_value();
11,876✔
1665
   auto z_w = pt.z().stash_value();
11,876✔
1666
   return ProjectivePoint::_create(shared_from_this(), x_w, y_w, z_w);
11,876✔
1667
}
1668

1669
GenericProjectivePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::ProjectivePoint& pt) const {
11,876✔
1670
   BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch");
11,876✔
1671
   auto x = GenericField(this, pt._x());
11,876✔
1672
   auto y = GenericField(this, pt._y());
11,876✔
1673
   auto z = GenericField(this, pt._z());
11,876✔
1674
   return GenericProjectivePoint(x, y, z);
11,876✔
1675
}
1676

1677
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::hash_to_curve_nu(
×
1678
   std::function<void(std::span<uint8_t>)> expand_message) const {
1679
   BOTAN_UNUSED(expand_message);
×
1680
   throw Not_Implemented("Hash to curve is not implemented for this curve");
×
1681
}
1682

1683
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::hash_to_curve_ro(
×
1684
   std::function<void(std::span<uint8_t>)> expand_message) const {
1685
   BOTAN_UNUSED(expand_message);
×
1686
   throw Not_Implemented("Hash to curve is not implemented for this curve");
×
1687
}
1688

1689
std::shared_ptr<const PrimeOrderCurve> PCurveInstance::from_params(
247✔
1690
   const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) {
1691
   // We don't check that p and order are prime here on the assumption this has
1692
   // been checked already by EC_Group
1693

1694
   BOTAN_ARG_CHECK(a >= 0 && a < p, "a is invalid");
494✔
1695
   BOTAN_ARG_CHECK(b > 0 && b < p, "b is invalid");
494✔
1696
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "base_x is invalid");
494✔
1697
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "base_y is invalid");
494✔
1698

1699
   const size_t p_bits = p.bits();
247✔
1700

1701
   // Same size restrictions as EC_Group however here we do not require
1702
   // exactly the primes for the 521 or 239 bit exceptions; this code
1703
   // should work fine with any such prime and we are relying on the higher
1704
   // levels to prevent creating such a group in the first place
1705
   //
1706
   // TODO(Botan4) increase the 128 here to 192 when the corresponding EC_Group constructor is changed
1707
   //
1708
   if(p_bits != 521 && p_bits != 239 && (p_bits < 128 || p_bits > 512 || p_bits % 32 != 0)) {
247✔
1709
      return {};
×
1710
   }
1711

1712
   // We don't want to deal with Shanks-Tonelli in the generic case
1713
   if(p % 4 != 3) {
247✔
1714
      return {};
71✔
1715
   }
1716

1717
   // The bit length of the field and order being the same simplifies things
1718
   if(p_bits != order.bits()) {
176✔
1719
      return {};
29✔
1720
   }
1721

1722
   auto gpoc = std::make_shared<GenericPrimeOrderCurve>(p, a, b, base_x, base_y, order);
147✔
1723
   /*
1724
   The implementation of this needs to call shared_from_this which is not usable
1725
   until after the constructor has completed, so we have to do a two-stage
1726
   construction process. This is certainly not so clean but it is contained to
1727
   this single file so seems tolerable.
1728

1729
   Alternately we could lazily compute the base mul table but this brings in
1730
   locking issues which seem a worse alternative overall.
1731
   */
1732
   gpoc->_precompute_base_mul();
147✔
1733
   return gpoc;
147✔
1734
}
147✔
1735

1736
}  // namespace Botan::PCurve
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc