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

randombit / botan / 22077577527

16 Feb 2026 09:09PM UTC coverage: 90.031% (-0.02%) from 90.047%
22077577527

push

github

web-flow
Merge pull request #5348 from randombit/jack/use-test-helpers

Improve use of test predicates

102329 of 113660 relevant lines covered (90.03%)

11467371.76 hits per line

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

95.89
/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,769✔
28
   if(bytes.size() > WordInfo<word>::bytes * N) {
115,769✔
29
      return std::nullopt;
×
30
   }
31

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

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

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

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

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

51
   return r;
115,769✔
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,115✔
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; }
8,467,643✔
144

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

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

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

151
      size_t field_bytes() const { return m_field_bytes; }
54,854✔
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; }
59,688,853✔
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,298✔
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; }
872,092✔
168

169
      word field_p_dash() const { return m_field_p_dash; }
60,071,242✔
170

171
      const StorageUnit& order() const { return m_order; }
1,031,541✔
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,213✔
178

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

181
      const StorageUnit& order_inv_2() const { return m_order_inv_2; }
971,554✔
182

183
      word order_p_dash() const { return m_order_p_dash; }
1,988,832✔
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; }
1,080,257✔
194

195
      bool a_is_zero() const { return m_a_is_zero; }
554,147✔
196

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

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

202
         if(m_words == 4) {
31,572,484✔
203
            bigint_comba_mul4(z.data(), x.data(), y.data());
12,805,550✔
204
         } else if(m_words == 6) {
18,766,934✔
205
            bigint_comba_mul6(z.data(), x.data(), y.data());
146,762✔
206
         } else if(m_words == 8) {
18,620,172✔
207
            bigint_comba_mul8(z.data(), x.data(), y.data());
3,611,614✔
208
         } else if(m_words == 9) {
15,008,558✔
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);
15,008,558✔
212
         }
213
      }
31,572,484✔
214

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

218
         if(m_words == 4) {
30,349,487✔
219
            bigint_comba_sqr4(z.data(), x.data());
12,679,113✔
220
         } else if(m_words == 6) {
17,670,374✔
221
            bigint_comba_sqr6(z.data(), x.data());
60,426✔
222
         } else if(m_words == 8) {
17,609,948✔
223
            bigint_comba_sqr8(z.data(), x.data());
3,959,238✔
224
         } else if(m_words == 9) {
13,650,710✔
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);
13,650,710✔
228
         }
229
      }
30,349,487✔
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,653✔
291
                                                          std::span<const uint8_t> bytes) {
292
         const size_t mlen = curve->_params().order_bytes();
9,653✔
293

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

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

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

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

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

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

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

325
            // Safe because we checked above that words is an integer < P
326
            return GenericScalar(curve, to_rep(curve, *words));
30,717✔
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,639✔
342
         constexpr size_t MAX_ATTEMPTS = 1000;
18,639✔
343

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

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

348
         for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
19,874✔
349
            rng.randomize(buf);
19,874✔
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,874✔
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,874✔
359
               if(s.value().is_nonzero().as_bool()) {
18,639✔
360
                  return s.value();
18,639✔
361
               }
362
            }
363
         }
364

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

368
      friend GenericScalar operator+(const GenericScalar& a, const GenericScalar& b) {
974,587✔
369
         const auto* curve = check_curve(a, b);
1,949,174✔
370
         const size_t words = curve->_params().words();
974,587✔
371

372
         StorageUnit t{};
974,587✔
373
         const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words);
1,949,174✔
374

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

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

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

385
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
68,331✔
386
         curve->_params().mul(z, a.value(), b.value());
68,331✔
387
         return GenericScalar(curve, redc(curve, z));
68,331✔
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,580✔
415
         auto x_is_zero = CT::all_zeros(this->data(), N);
45,160✔
416

417
         StorageUnit r;
22,580✔
418
         bigint_sub3(r.data(), m_curve->_params().order().data(), N, this->data(), N);
22,580✔
419
         x_is_zero.if_set_zero_out(r.data(), N);
22,580✔
420
         return GenericScalar(m_curve, r);
22,580✔
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) {
971,554✔
433
         const auto& inv_2 = a.curve()->_params().order_inv_2();
971,554✔
434

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

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

441
            // Conditional ok: this function is variable time
442
            if(borrow > 0) {
1,931,852✔
443
               bigint_add2(x.m_val.data(), N, inv_2.data(), N);
968,793✔
444
            }
445
         }
446
      }
971,554✔
447

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

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

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

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

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

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

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

479
            auto nx = x + y;
960,562✔
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{};
960,562✔
491
            const word carry = bigint_sub3(r.data(), b.data(), N, a.data(), N);
1,921,124✔
492

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

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

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

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

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

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

531
      CT::Choice is_nonzero() const { return !is_zero(); }
18,639✔
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,457✔
545

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

548
      const GenericPrimeOrderCurve* curve() const { return m_curve; }
982,859✔
549

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

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

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

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

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

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

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

584
      static StorageUnit wide_to_rep(const GenericPrimeOrderCurve* curve, std::array<W, 2 * N> x) {
9,653✔
585
         auto redc_x = redc(curve, x);
9,653✔
586
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
9,653✔
587
         curve->_params().mul(z, redc_x, curve->_params().order_monty_r3());
9,653✔
588
         return redc(curve, z);
9,653✔
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,958✔
602
                                                     std::span<const uint8_t> bytes) {
603
         const size_t len = curve->_params().field_bytes();
73,958✔
604

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

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

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

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

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

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

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

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

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

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

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

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

651
         for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
56,084✔
652
            rng.randomize(buf);
56,084✔
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,084✔
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,084✔
662
               if(s.value().is_nonzero().as_bool()) {
51,208✔
663
                  return s.value();
51,208✔
664
               }
665
            }
666
         }
667

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

671
      /**
672
      * Return the value of this divided by 2
673
      */
674
      GenericField div2() const {
872,092✔
675
         StorageUnit t = value();
872,092✔
676
         const W borrow = shift_right<1>(t);
872,092✔
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);
872,092✔
680

681
         return GenericField(m_curve, t);
872,092✔
682
      }
683

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

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

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

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

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

703
      friend GenericField operator+(const GenericField& a, const GenericField& b) {
27,159,284✔
704
         const auto* curve = check_curve(a, b);
54,318,568✔
705
         const size_t words = curve->_params().words();
27,159,284✔
706

707
         StorageUnit t{};
27,159,284✔
708
         const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words);
54,318,568✔
709

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

715
      friend GenericField operator-(const GenericField& a, const GenericField& b) { return a + b.negate(); }
20,792,887✔
716

717
      friend GenericField operator*(const GenericField& a, const GenericField& b) {
23,837,501✔
718
         const auto* curve = check_curve(a, b);
47,675,002✔
719

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

725
      GenericField& operator*=(const GenericField& other) {
7,201,966✔
726
         const auto* curve = check_curve(*this, other);
14,403,932✔
727

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

734
      GenericField square() const {
28,897,824✔
735
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
28,897,824✔
736
         m_curve->_params().sqr(z, value());
28,897,824✔
737
         return GenericField(m_curve, redc(m_curve, z));
28,897,824✔
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 {
20,800,116✔
748
         auto x_is_zero = CT::all_zeros(this->data(), N);
41,600,232✔
749

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

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

758
      template <concepts::resizable_byte_buffer T>
759
      T serialize() const {
3,646✔
760
         T bytes(m_curve->_params().field_bytes());
3,646✔
761
         serialize_to(bytes);
3,646✔
762
         return bytes;
3,646✔
763
      }
×
764

765
      void serialize_to(std::span<uint8_t> bytes) const {
55,350✔
766
         auto v = from_rep(m_curve, m_val);
55,350✔
767
         std::reverse(v.begin(), v.end());
55,350✔
768

769
         const size_t flen = m_curve->_params().field_bytes();
55,350✔
770
         BOTAN_ARG_CHECK(bytes.size() == flen, "Expected output span provided");
55,350✔
771

772
         // Remove leading zero bytes
773
         const auto padded_bytes = store_be(v);
55,350✔
774
         const size_t extra = N * WordInfo<W>::bytes - flen;
55,350✔
775
         copy_mem(bytes, std::span{padded_bytes}.subspan(extra, flen));
55,350✔
776
      }
55,350✔
777

778
      CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_curve->_params().words()).as_choice(); }
8,405,713✔
779

780
      CT::Choice is_nonzero() const { return !is_zero(); }
51,208✔
781

782
      CT::Choice operator==(const GenericField& other) const {
24,255✔
783
         if(this->m_curve != other.m_curve) {
24,255✔
784
            return CT::Choice::no();
×
785
         }
786

787
         return CT::is_equal(m_val.data(), other.m_val.data(), m_curve->_params().words()).as_choice();
24,255✔
788
      }
789

790
      const StorageUnit& stash_value() const { return m_val; }
791

792
      const GenericPrimeOrderCurve* curve() const { return m_curve; }
35,159✔
793

794
      CT::Choice is_even() const {
2,871✔
795
         auto v = from_rep(m_curve, m_val);
2,871✔
796
         return !CT::Choice::from_int(v[0] & 0x01);
2,871✔
797
      }
798

799
      /**
800
      * Convert the integer to standard representation and return the sequence of words
801
      */
802
      StorageUnit to_words() const { return from_rep(m_curve, m_val); }
2,216✔
803

804
      void _const_time_poison() const { CT::poison(m_val); }
12,869✔
805

806
      void _const_time_unpoison() const { CT::unpoison(m_val); }
12,869✔
807

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

811
         for(size_t i = 0; i != N; ++i) {
252,480✔
812
            auto nx = choose(mask, y.m_val[i], x.m_val[i]);
227,232✔
813
            auto ny = choose(mask, x.m_val[i], y.m_val[i]);
227,232✔
814
            x.m_val[i] = nx;
227,232✔
815
            y.m_val[i] = ny;
227,232✔
816
         }
817
      }
818

819
      void conditional_assign(CT::Choice cond, const GenericField& nx) {
5,742✔
820
         const W mask = cond.into_bitmask<W>();
5,742✔
821

822
         for(size_t i = 0; i != N; ++i) {
57,420✔
823
            m_val[i] = choose(mask, nx.m_val[i], m_val[i]);
51,678✔
824
         }
825
      }
826

827
      /**
828
      * Conditional assignment
829
      *
830
      * If `cond` is true, sets `x` to `nx` and `y` to `ny`
831
      */
832
      static void conditional_assign(
23,386,892✔
833
         GenericField& x, GenericField& y, CT::Choice cond, const GenericField& nx, const GenericField& ny) {
834
         const W mask = cond.into_bitmask<W>();
23,386,892✔
835

836
         for(size_t i = 0; i != N; ++i) {
233,868,920✔
837
            x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
210,482,028✔
838
            y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
210,482,028✔
839
         }
840
      }
841

842
      /**
843
      * Conditional assignment
844
      *
845
      * If `cond` is true, sets `x` to `nx`, `y` to `ny`, and `z` to `nz`
846
      */
847
      static void conditional_assign(GenericField& x,
3,146,976✔
848
                                     GenericField& y,
849
                                     GenericField& z,
850
                                     CT::Choice cond,
851
                                     const GenericField& nx,
852
                                     const GenericField& ny,
853
                                     const GenericField& nz) {
854
         const W mask = cond.into_bitmask<W>();
3,146,976✔
855

856
         for(size_t i = 0; i != N; ++i) {
31,469,760✔
857
            x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
28,322,784✔
858
            y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
28,322,784✔
859
            z.m_val[i] = choose(mask, nz.m_val[i], z.m_val[i]);
28,322,784✔
860
         }
861
      }
3,146,976✔
862

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

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

873
      GenericField(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {}
1,442,928✔
874

875
   private:
876
      const StorageUnit& value() const { return m_val; }
59,937,291✔
877

878
      const W* data() const { return m_val.data(); }
68,759,516✔
879

880
      static const GenericPrimeOrderCurve* check_curve(const GenericField& a, const GenericField& b) {
58,198,751✔
881
         BOTAN_STATE_CHECK(a.m_curve == b.m_curve);
58,198,751✔
882
         return a.m_curve;
58,198,751✔
883
      }
884

885
      static StorageUnit redc(const GenericPrimeOrderCurve* curve, std::array<W, 2 * N> z) {
60,071,242✔
886
         const auto& mod = curve->_params().field();
60,071,242✔
887
         const size_t words = curve->_params().words();
60,071,242✔
888
         StorageUnit r{};
60,071,242✔
889
         StorageUnit ws{};
60,071,242✔
890
         bigint_monty_redc(
60,071,242✔
891
            r.data(), z.data(), mod.data(), words, curve->_params().field_p_dash(), ws.data(), ws.size());
60,071,242✔
892
         return r;
60,071,242✔
893
      }
894

895
      static StorageUnit from_rep(const GenericPrimeOrderCurve* curve, StorageUnit z) {
62,653✔
896
         std::array<W, 2 * N> ze{};
62,653✔
897
         copy_mem(std::span{ze}.template first<N>(), z);
62,653✔
898
         return redc(curve, ze);
62,653✔
899
      }
900

901
      static StorageUnit to_rep(const GenericPrimeOrderCurve* curve, StorageUnit x) {
71,298✔
902
         std::array<W, 2 * N> z{};
71,298✔
903
         curve->_params().mul(z, x, curve->_params().field_monty_r2());
71,298✔
904
         return redc(curve, z);
71,298✔
905
      }
906

907
      const GenericPrimeOrderCurve* m_curve;
908
      StorageUnit m_val;
909
};
910

911
/**
912
* Affine Curve Point
913
*
914
* This contains a pair of integers (x,y) which satisfy the curve equation
915
*/
916
class GenericAffinePoint final {
917
   public:
918
      GenericAffinePoint(const GenericField& x, const GenericField& y) : m_x(x), m_y(y) {}
490,391✔
919

920
      explicit GenericAffinePoint(const GenericPrimeOrderCurve* curve) :
921
            m_x(GenericField::zero(curve)), m_y(GenericField::zero(curve)) {}
922

923
      static GenericAffinePoint identity(const GenericPrimeOrderCurve* curve) {
918,628✔
924
         return GenericAffinePoint(GenericField::zero(curve), GenericField::zero(curve));
918,628✔
925
      }
926

927
      static GenericAffinePoint identity(const GenericAffinePoint& pt) { return identity(pt.curve()); }
482,543✔
928

929
      CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); }
1,473,742✔
930

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

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

947
      /**
948
      * If idx is zero then return the identity element. Otherwise return pts[idx - 1]
949
      *
950
      * Returns the identity element also if idx is out of range
951
      */
952
      static auto ct_select(std::span<const GenericAffinePoint> pts, size_t idx) {
435,941✔
953
         BOTAN_ARG_CHECK(!pts.empty(), "Cannot select from an empty set");
435,941✔
954
         auto result = GenericAffinePoint::identity(pts[0].curve());
435,941✔
955

956
         // Intentionally wrapping; set to maximum size_t if idx == 0
957
         const size_t idx1 = static_cast<size_t>(idx - 1);
435,941✔
958
         for(size_t i = 0; i != pts.size(); ++i) {
13,950,112✔
959
            const auto found = CT::Mask<size_t>::is_equal(idx1, i).as_choice();
13,514,171✔
960
            result.conditional_assign(found, pts[i]);
13,514,171✔
961
         }
962

963
         return result;
435,941✔
964
      }
965

966
      /**
967
      * Return (x^3 + A*x + B) mod p
968
      */
969
      static GenericField x3_ax_b(const GenericField& x) {
20,247✔
970
         return (x.square() + GenericField::curve_a(x.curve())) * x + GenericField::curve_b(x.curve());
20,247✔
971
      }
972

973
      /**
974
      * Point deserialization
975
      *
976
      * This accepts compressed or uncompressed formats.
977
      */
978
      static std::optional<GenericAffinePoint> deserialize(const GenericPrimeOrderCurve* curve,
8,652✔
979
                                                           std::span<const uint8_t> bytes) {
980
         const size_t fe_bytes = curve->_params().field_bytes();
8,652✔
981

982
         if(bytes.size() == 1 + 2 * fe_bytes && bytes[0] == 0x04) {
8,652✔
983
            auto x = GenericField::deserialize(curve, bytes.subspan(1, fe_bytes));
5,501✔
984
            auto y = GenericField::deserialize(curve, bytes.subspan(1 + fe_bytes, fe_bytes));
5,501✔
985

986
            if(x && y) {
5,501✔
987
               const auto lhs = (*y).square();
5,501✔
988
               const auto rhs = GenericAffinePoint::x3_ax_b(*x);
5,501✔
989
               if((lhs == rhs).as_bool()) {
5,501✔
990
                  return GenericAffinePoint(*x, *y);
5,440✔
991
               }
992
            }
993
         } else if(bytes.size() == 1 + fe_bytes && (bytes[0] == 0x02 || bytes[0] == 0x03)) {
3,151✔
994
            const CT::Choice y_is_even = CT::Mask<uint8_t>::is_equal(bytes[0], 0x02).as_choice();
2,871✔
995

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

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

1010
         return {};
197✔
1011
      }
1012

1013
      /**
1014
      * Return the affine x coordinate
1015
      */
1016
      const GenericField& x() const { return m_x; }
1,410,503✔
1017

1018
      /**
1019
      * Return the affine y coordinate
1020
      */
1021
      const GenericField& y() const { return m_y; }
1,412,645✔
1022

1023
      /**
1024
      * Conditional assignment of an affine point
1025
      */
1026
      void conditional_assign(CT::Choice cond, const GenericAffinePoint& pt) {
23,386,892✔
1027
         GenericField::conditional_assign(m_x, m_y, cond, pt.x(), pt.y());
23,386,892✔
1028
      }
23,386,892✔
1029

1030
      const GenericPrimeOrderCurve* curve() const { return m_x.curve(); }
944,336✔
1031

1032
      void _const_time_poison() const { CT::poison_all(m_x, m_y); }
1033

1034
      void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y); }
1035

1036
   private:
1037
      GenericField m_x;
1038
      GenericField m_y;
1039
};
1040

1041
class GenericProjectivePoint final {
1042
   public:
1043
      typedef GenericProjectivePoint Self;
1044

1045
      using FieldElement = GenericField;
1046

1047
      /**
1048
      * Convert a point from affine to projective form
1049
      */
1050
      static Self from_affine(const GenericAffinePoint& pt) {
25,248✔
1051
         auto x = pt.x();
25,248✔
1052
         auto y = pt.y();
25,248✔
1053
         auto z = GenericField::one(x.curve());
25,248✔
1054

1055
         // If pt is identity (0,0) swap y/z to convert (0,0,1) into (0,1,0)
1056
         GenericField::conditional_swap(pt.is_identity(), y, z);
25,248✔
1057
         return GenericProjectivePoint(x, y, z);
25,248✔
1058
      }
1059

1060
      /**
1061
      * Return the identity element
1062
      */
1063
      static Self identity(const GenericPrimeOrderCurve* curve) {
1064
         return Self(GenericField::zero(curve), GenericField::one(curve), GenericField::zero(curve));
1065
      }
1066

1067
      /**
1068
      * Default constructor: the identity element
1069
      */
1070
      explicit GenericProjectivePoint(const GenericPrimeOrderCurve* curve) :
4,782✔
1071
            m_x(GenericField::zero(curve)), m_y(GenericField::one(curve)), m_z(GenericField::zero(curve)) {}
4,782✔
1072

1073
      /**
1074
      * Affine constructor: take x/y coordinates
1075
      */
1076
      GenericProjectivePoint(const GenericField& x, const GenericField& y) :
1077
            m_x(x), m_y(y), m_z(GenericField::one(m_x.curve())) {}
1078

1079
      /**
1080
      * Projective constructor: take x/y/z coordinates
1081
      */
1082
      GenericProjectivePoint(const GenericField& x, const GenericField& y, const GenericField& z) :
2,690,868✔
1083
            m_x(x), m_y(y), m_z(z) {}
2,653,745✔
1084

1085
      friend Self operator+(const Self& a, const Self& b) { return Self::add(a, b); }
189,186✔
1086

1087
      friend Self operator+(const Self& a, const GenericAffinePoint& b) { return Self::add_mixed(a, b); }
52,904✔
1088

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

1091
      Self& operator+=(const Self& other) {
1092
         (*this) = (*this) + other;
1093
         return (*this);
1094
      }
1095

1096
      Self& operator+=(const GenericAffinePoint& other) {
1,298,995✔
1097
         (*this) = (*this) + other;
1,298,995✔
1098
         return (*this);
1,298,995✔
1099
      }
1100

1101
      CT::Choice is_identity() const { return z().is_zero(); }
2,070,161✔
1102

1103
      /**
1104
      * Mixed (projective + affine) point addition
1105
      */
1106
      static Self add_mixed(const Self& a, const GenericAffinePoint& b) {
1,384,651✔
1107
         return point_add_mixed<Self, GenericAffinePoint, GenericField>(a, b, GenericField::one(a.curve()));
2,769,302✔
1108
      }
1109

1110
      /**
1111
      * Projective point addition
1112
      */
1113
      static Self add(const Self& a, const Self& b) { return point_add<Self, GenericField>(a, b); }
189,186✔
1114

1115
      /**
1116
      * Iterated point doubling
1117
      */
1118
      Self dbl_n(size_t n) const {
872,092✔
1119
         if(curve()->_params().a_is_minus_3()) {
872,092✔
1120
            return dbl_n_a_minus_3(*this, n);
436,815✔
1121
         } else if(curve()->_params().a_is_zero()) {
435,277✔
1122
            return dbl_n_a_zero(*this, n);
43,876✔
1123
         } else {
1124
            const auto A = GenericField::curve_a(curve());
391,401✔
1125
            return dbl_n_generic(*this, A, n);
391,401✔
1126
         }
1127
      }
1128

1129
      /**
1130
      * Point doubling
1131
      */
1132
      Self dbl() const {
208,165✔
1133
         if(curve()->_params().a_is_minus_3()) {
208,165✔
1134
            return dbl_a_minus_3(*this);
89,295✔
1135
         } else if(curve()->_params().a_is_zero()) {
118,870✔
1136
            return dbl_a_zero(*this);
65,171✔
1137
         } else {
1138
            const auto A = GenericField::curve_a(curve());
53,699✔
1139
            return dbl_generic(*this, A);
53,699✔
1140
         }
1141
      }
1142

1143
      /**
1144
      * Point negation
1145
      */
1146
      Self negate() const { return Self(x(), y().negate(), z()); }
1147

1148
      /**
1149
      * Randomize the point representation
1150
      *
1151
      * Projective coordinates are redundant; if (x,y,z) is a projective
1152
      * point then so is (x*r^2,y*r^3,z*r) for any non-zero r.
1153
      */
1154
      void randomize_rep(RandomNumberGenerator& rng) {
51,476✔
1155
         // In certain contexts we may be called with a Null_RNG; in that case the
1156
         // caller is accepting that randomization will not occur
1157

1158
         if(rng.is_seeded()) {
51,476✔
1159
            auto r = GenericField::random(curve(), rng);
51,208✔
1160

1161
            auto r2 = r.square();
51,208✔
1162
            auto r3 = r2 * r;
51,208✔
1163

1164
            m_x *= r2;
51,208✔
1165
            m_y *= r3;
51,208✔
1166
            m_z *= r;
51,208✔
1167
         }
1168
      }
51,476✔
1169

1170
      /**
1171
      * Return the projective x coordinate
1172
      */
1173
      const GenericField& x() const { return m_x; }
2,288,477✔
1174

1175
      /**
1176
      * Return the projective y coordinate
1177
      */
1178
      const GenericField& y() const { return m_y; }
3,346,109✔
1179

1180
      /**
1181
      * Return the projective z coordinate
1182
      */
1183
      const GenericField& z() const { return m_z; }
3,253,618✔
1184

1185
      const GenericPrimeOrderCurve* curve() const { return m_x.curve(); }
2,516,116✔
1186

1187
      void _const_time_poison() const { CT::poison_all(m_x, m_y, m_z); }
12,869✔
1188

1189
      void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y, m_z); }
12,869✔
1190

1191
   private:
1192
      GenericField m_x;
1193
      GenericField m_y;
1194
      GenericField m_z;
1195
};
1196

1197
class GenericCurve final {
1198
   public:
1199
      typedef GenericField FieldElement;
1200
      typedef GenericScalar Scalar;
1201
      typedef GenericAffinePoint AffinePoint;
1202
      typedef GenericProjectivePoint ProjectivePoint;
1203

1204
      typedef word WordType;
1205
      static constexpr size_t Words = PCurve::PrimeOrderCurve::StorageWords;
1206
};
1207

1208
class GenericBlindedScalarBits final {
6,116✔
1209
   public:
1210
      GenericBlindedScalarBits(const GenericScalar& scalar, RandomNumberGenerator& rng, size_t wb) {
13,457✔
1211
         // Just a simplifying assumption for get_window, can extend to 1..7 as required
1212
         BOTAN_ASSERT_NOMSG(wb == 3 || wb == 4 || wb == 5);
13,457✔
1213

1214
         const auto& params = scalar.curve()->_params();
13,457✔
1215

1216
         const size_t order_bits = params.order_bits();
13,457✔
1217
         const size_t blinder_bits = blinding_bits(order_bits);
26,914✔
1218

1219
         const size_t mask_words = blinder_bits / WordInfo<word>::bits;
13,457✔
1220
         const size_t mask_bytes = mask_words * WordInfo<word>::bytes;
13,457✔
1221

1222
         const size_t words = params.words();
13,457✔
1223

1224
         secure_vector<uint8_t> maskb(mask_bytes);
26,914✔
1225
         if(rng.is_seeded()) {
13,457✔
1226
            rng.randomize(maskb);
13,390✔
1227
         } else {
1228
            auto sbytes = scalar.serialize<std::vector<uint8_t>>();
67✔
1229
            for(size_t i = 0; i != sbytes.size(); ++i) {
1,745✔
1230
               maskb[i % mask_bytes] ^= sbytes[i];
1,678✔
1231
            }
1232
         }
67✔
1233

1234
         std::array<word, PrimeOrderCurve::StorageWords> mask{};
13,457✔
1235
         load_le(mask.data(), maskb.data(), mask_words);
13,457✔
1236
         mask[mask_words - 1] |= WordInfo<word>::top_bit;
13,457✔
1237
         mask[0] |= 1;
13,457✔
1238

1239
         std::array<word, 2 * PrimeOrderCurve::StorageWords> mask_n{};
13,457✔
1240

1241
         const auto sw = scalar.to_words();
13,457✔
1242

1243
         // Compute masked scalar s + k*n
1244
         params.mul(mask_n, mask, params.order());
13,457✔
1245
         bigint_add2(mask_n.data(), 2 * words, sw.data(), words);
13,457✔
1246

1247
         std::reverse(mask_n.begin(), mask_n.end());
13,457✔
1248
         m_bytes = store_be<std::vector<uint8_t>>(mask_n);
13,457✔
1249
         m_bits = order_bits + blinder_bits;
13,457✔
1250
         m_window_bits = wb;
13,457✔
1251
         m_windows = (order_bits + blinder_bits + wb - 1) / wb;
13,457✔
1252
      }
13,457✔
1253

1254
      size_t windows() const { return m_windows; }
1255

1256
      size_t bits() const { return m_bits; }
12,869✔
1257

1258
      size_t get_window(size_t offset) const {
973,371✔
1259
         if(m_window_bits == 3) {
973,371✔
1260
            return read_window_bits<3>(std::span{m_bytes}, offset);
109,774✔
1261
         } else if(m_window_bits == 4) {
863,597✔
1262
            return read_window_bits<4>(std::span{m_bytes}, offset);
427,656✔
1263
         } else if(m_window_bits == 5) {
435,941✔
1264
            return read_window_bits<5>(std::span{m_bytes}, offset);
435,941✔
1265
         } else {
1266
            BOTAN_ASSERT_UNREACHABLE();
×
1267
         }
1268
      }
1269

1270
      static size_t blinding_bits(size_t order_bits) {
13,457✔
1271
         if(order_bits > 512) {
13,604✔
1272
            return blinding_bits(512);
1273
         }
1274

1275
         const size_t wb = sizeof(word) * 8;
13,604✔
1276
         return ((order_bits / 4 + wb - 1) / wb) * wb;
13,457✔
1277
      }
1278

1279
   private:
1280
      std::vector<uint8_t> m_bytes;
1281
      size_t m_bits;
1282
      size_t m_windows;
1283
      size_t m_window_bits;
1284
};
1285

1286
class GenericWindowedMul final {
11,056✔
1287
   public:
1288
      static constexpr size_t WindowBits = VarPointWindowBits;
1289
      static constexpr size_t TableSize = (1 << WindowBits) - 1;
1290

1291
      explicit GenericWindowedMul(const GenericAffinePoint& pt) :
5,528✔
1292
            m_table(varpoint_setup<GenericCurve, TableSize>(pt)) {}
5,528✔
1293

1294
      GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) {
5,528✔
1295
         const GenericBlindedScalarBits bits(s, rng, WindowBits);
5,528✔
1296

1297
         return varpoint_exec<GenericCurve, WindowBits>(m_table, bits, rng);
5,528✔
1298
      }
5,528✔
1299

1300
   private:
1301
      AffinePointTable<GenericCurve> m_table;
1302
};
1303

1304
class GenericBaseMulTable final {
294✔
1305
   public:
1306
      static constexpr size_t WindowBits = BasePointWindowBits;
1307

1308
      static constexpr size_t WindowElements = (1 << WindowBits) - 1;
1309

1310
      explicit GenericBaseMulTable(const GenericAffinePoint& pt) :
147✔
1311
            m_table(basemul_setup<GenericCurve, WindowBits>(pt, blinded_scalar_bits(*pt.curve()))) {}
441✔
1312

1313
      GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) {
6,753✔
1314
         const GenericBlindedScalarBits scalar(s, rng, WindowBits);
6,753✔
1315
         return basemul_exec<GenericCurve, WindowBits>(m_table, scalar, rng);
6,753✔
1316
      }
6,753✔
1317

1318
   private:
1319
      static size_t blinded_scalar_bits(const GenericPrimeOrderCurve& curve) {
147✔
1320
         const size_t order_bits = curve.order_bits();
147✔
1321
         return order_bits + GenericBlindedScalarBits::blinding_bits(order_bits);
294✔
1322
      }
1323

1324
      std::vector<GenericAffinePoint> m_table;
1325
};
1326

1327
class GenericWindowedMul2 final {
1328
   public:
1329
      static constexpr size_t WindowBits = Mul2PrecompWindowBits;
1330

1331
      GenericWindowedMul2(const GenericWindowedMul2& other) = delete;
1332
      GenericWindowedMul2(GenericWindowedMul2&& other) = delete;
1333
      GenericWindowedMul2& operator=(const GenericWindowedMul2& other) = delete;
1334
      GenericWindowedMul2& operator=(GenericWindowedMul2&& other) = delete;
1335

1336
      ~GenericWindowedMul2() = default;
588✔
1337

1338
      GenericWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) :
588✔
1339
            m_table(mul2_setup<GenericCurve, WindowBits>(p, q)) {}
1,176✔
1340

1341
      GenericProjectivePoint mul2(const GenericScalar& x, const GenericScalar& y, RandomNumberGenerator& rng) const {
588✔
1342
         const GenericBlindedScalarBits x_bits(x, rng, WindowBits);
588✔
1343
         const GenericBlindedScalarBits y_bits(y, rng, WindowBits);
588✔
1344
         return mul2_exec<GenericCurve, WindowBits>(m_table, x_bits, y_bits, rng);
588✔
1345
      }
1,176✔
1346

1347
   private:
1348
      AffinePointTable<GenericCurve> m_table;
1349
};
1350

1351
class GenericVartimeWindowedMul2 final : public PrimeOrderCurve::PrecomputedMul2Table {
1352
   public:
1353
      static constexpr size_t WindowBits = Mul2PrecompWindowBits;
1354

1355
      GenericVartimeWindowedMul2(const GenericVartimeWindowedMul2& other) = delete;
1356
      GenericVartimeWindowedMul2(GenericVartimeWindowedMul2&& other) = delete;
1357
      GenericVartimeWindowedMul2& operator=(const GenericVartimeWindowedMul2& other) = delete;
1358
      GenericVartimeWindowedMul2& operator=(GenericVartimeWindowedMul2&& other) = delete;
1359

1360
      ~GenericVartimeWindowedMul2() override = default;
3,560✔
1361

1362
      GenericVartimeWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) :
1,780✔
1363
            m_table(to_affine_batch<GenericCurve>(mul2_setup<GenericCurve, WindowBits>(p, q))) {}
3,560✔
1364

1365
      GenericProjectivePoint mul2_vartime(const GenericScalar& x, const GenericScalar& y) const {
4,782✔
1366
         const auto x_bits = x.serialize<std::vector<uint8_t>>();
4,782✔
1367
         const auto y_bits = y.serialize<std::vector<uint8_t>>();
4,782✔
1368

1369
         const auto& curve = m_table[0].curve();
4,782✔
1370
         auto accum = GenericProjectivePoint(curve);
4,782✔
1371

1372
         const size_t order_bits = curve->order_bits();
4,782✔
1373

1374
         const size_t windows = (order_bits + WindowBits - 1) / WindowBits;
4,782✔
1375

1376
         for(size_t i = 0; i != windows; ++i) {
405,229✔
1377
            auto x_i = read_window_bits<WindowBits>(std::span{x_bits}, (windows - i - 1) * WindowBits);
400,447✔
1378
            auto y_i = read_window_bits<WindowBits>(std::span{y_bits}, (windows - i - 1) * WindowBits);
400,447✔
1379

1380
            if(i > 0) {
400,447✔
1381
               accum = accum.dbl_n(WindowBits);
395,665✔
1382
            }
1383

1384
            const size_t idx = (y_i << WindowBits) + x_i;
400,447✔
1385

1386
            if(idx > 0) {
400,447✔
1387
               accum += m_table[idx - 1];
393,380✔
1388
            }
1389
         }
1390

1391
         return accum;
4,782✔
1392
      }
9,564✔
1393

1394
   private:
1395
      std::vector<GenericAffinePoint> m_table;
1396
};
1397

1398
GenericPrimeOrderCurve::GenericPrimeOrderCurve(
147✔
1399
   const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) :
147✔
1400
      m_params(std::make_unique<GenericCurveParams>(p, a, b, base_x, base_y, order)) {}
147✔
1401

1402
void GenericPrimeOrderCurve::_precompute_base_mul() {
147✔
1403
   BOTAN_STATE_CHECK(m_basemul == nullptr);
147✔
1404
   m_basemul = std::make_unique<GenericBaseMulTable>(from_stash(generator()));
147✔
1405
}
147✔
1406

1407
size_t GenericPrimeOrderCurve::order_bits() const {
4,929✔
1408
   return _params().order_bits();
4,929✔
1409
}
1410

1411
size_t GenericPrimeOrderCurve::scalar_bytes() const {
×
1412
   return _params().order_bytes();
×
1413
}
1414

1415
size_t GenericPrimeOrderCurve::field_element_bytes() const {
60,658✔
1416
   return _params().field_bytes();
60,658✔
1417
}
1418

1419
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul_by_g(const Scalar& scalar,
5,531✔
1420
                                                                  RandomNumberGenerator& rng) const {
1421
   BOTAN_STATE_CHECK(m_basemul != nullptr);
5,531✔
1422
   return stash(m_basemul->mul(from_stash(scalar), rng));
5,531✔
1423
}
1424

1425
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::base_point_mul_x_mod_order(const Scalar& scalar,
1,222✔
1426
                                                                           RandomNumberGenerator& rng) const {
1427
   BOTAN_STATE_CHECK(m_basemul != nullptr);
1,222✔
1428
   auto pt_s = m_basemul->mul(from_stash(scalar), rng);
1,222✔
1429
   const auto x_bytes = to_affine_x<GenericCurve>(pt_s).serialize<secure_vector<uint8_t>>();
1,222✔
1430
   if(auto s = GenericScalar::from_wide_bytes(this, x_bytes)) {
1,222✔
1431
      return stash(*s);
1,222✔
1432
   } else {
1433
      throw Internal_Error("Failed to convert x coordinate to integer modulo scalar");
×
1434
   }
1435
}
1,222✔
1436

1437
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul(const AffinePoint& pt,
3,104✔
1438
                                                             const Scalar& scalar,
1439
                                                             RandomNumberGenerator& rng) const {
1440
   GenericWindowedMul pt_table(from_stash(pt));
3,104✔
1441
   return stash(pt_table.mul(from_stash(scalar), rng));
3,104✔
1442
}
3,104✔
1443

1444
secure_vector<uint8_t> GenericPrimeOrderCurve::mul_x_only(const AffinePoint& pt,
2,424✔
1445
                                                          const Scalar& scalar,
1446
                                                          RandomNumberGenerator& rng) const {
1447
   GenericWindowedMul pt_table(from_stash(pt));
2,424✔
1448
   auto pt_s = pt_table.mul(from_stash(scalar), rng);
2,424✔
1449
   return to_affine_x<GenericCurve>(pt_s).serialize<secure_vector<uint8_t>>();
2,424✔
1450
}
2,424✔
1451

1452
std::unique_ptr<const PrimeOrderCurve::PrecomputedMul2Table> GenericPrimeOrderCurve::mul2_setup_g(
1,780✔
1453
   const AffinePoint& q) const {
1454
   return std::make_unique<GenericVartimeWindowedMul2>(from_stash(generator()), from_stash(q));
3,560✔
1455
}
1456

1457
std::optional<PrimeOrderCurve::ProjectivePoint> GenericPrimeOrderCurve::mul2_vartime(const PrecomputedMul2Table& tableb,
768✔
1458
                                                                                     const Scalar& s1,
1459
                                                                                     const Scalar& s2) const {
1460
   const auto& tbl = dynamic_cast<const GenericVartimeWindowedMul2&>(tableb);
768✔
1461
   auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2));
768✔
1462
   if(pt.is_identity().as_bool()) {
768✔
1463
      return {};
×
1464
   } else {
1465
      return stash(pt);
768✔
1466
   }
1467
}
1468

1469
std::optional<PrimeOrderCurve::ProjectivePoint> GenericPrimeOrderCurve::mul_px_qy(
588✔
1470
   const AffinePoint& p, const Scalar& x, const AffinePoint& q, const Scalar& y, RandomNumberGenerator& rng) const {
1471
   const GenericWindowedMul2 table(from_stash(p), from_stash(q));
588✔
1472
   auto pt = table.mul2(from_stash(x), from_stash(y), rng);
588✔
1473
   if(pt.is_identity().as_bool()) {
588✔
1474
      return {};
84✔
1475
   } else {
1476
      return stash(pt);
504✔
1477
   }
1478
}
588✔
1479

1480
bool GenericPrimeOrderCurve::mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& tableb,
4,014✔
1481
                                                         const Scalar& v,
1482
                                                         const Scalar& s1,
1483
                                                         const Scalar& s2) const {
1484
   const auto& tbl = dynamic_cast<const GenericVartimeWindowedMul2&>(tableb);
4,014✔
1485
   auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2));
4,014✔
1486

1487
   if(!pt.is_identity().as_bool()) {
4,014✔
1488
      const auto z2 = pt.z().square();
4,001✔
1489

1490
      const auto v_bytes = from_stash(v).serialize<std::vector<uint8_t>>();
4,001✔
1491

1492
      if(auto fe_v = GenericField::deserialize(this, v_bytes)) {
4,001✔
1493
         if((*fe_v * z2 == pt.x()).as_bool()) {
4,001✔
1494
            return true;
1,614✔
1495
         }
1496

1497
         if(_params().order_is_less_than_field()) {
2,394✔
1498
            const auto n = GenericField::from_words(this, _params().order());
2,216✔
1499
            const auto neg_n = n.negate().to_words();
2,216✔
1500

1501
            const auto vw = fe_v->to_words();
2,216✔
1502
            if(bigint_ct_is_lt(vw.data(), vw.size(), neg_n.data(), neg_n.size()).as_bool()) {
2,216✔
1503
               return (((*fe_v + n) * z2) == pt.x()).as_bool();
7✔
1504
            }
1505
         }
1506
      }
1507
   }
4,001✔
1508

1509
   return false;
1510
}
1511

1512
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::generator() const {
1,927✔
1513
   return PrimeOrderCurve::AffinePoint::_create(shared_from_this(), _params().base_x(), _params().base_y());
1,927✔
1514
}
1515

1516
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_to_affine(const ProjectivePoint& pt) const {
11,875✔
1517
   auto affine = to_affine<GenericCurve>(from_stash(pt));
11,875✔
1518

1519
   const auto y2 = affine.y().square();
11,875✔
1520
   const auto x3_ax_b = GenericCurve::AffinePoint::x3_ax_b(affine.x());
11,875✔
1521
   const auto valid_point = affine.is_identity() || (y2 == x3_ax_b);
11,875✔
1522

1523
   BOTAN_ASSERT(valid_point.as_bool(), "Computed point is on the curve");
11,875✔
1524

1525
   return stash(affine);
11,875✔
1526
}
1527

1528
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::point_add(const AffinePoint& a, const AffinePoint& b) const {
1,968✔
1529
   return stash(GenericProjectivePoint::from_affine(from_stash(a)) + from_stash(b));
1,968✔
1530
}
1531

1532
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_negate(const AffinePoint& pt) const {
2,142✔
1533
   return stash(from_stash(pt).negate());
2,142✔
1534
}
1535

1536
bool GenericPrimeOrderCurve::affine_point_is_identity(const AffinePoint& pt) const {
26,116✔
1537
   return from_stash(pt).is_identity().as_bool();
26,116✔
1538
}
1539

1540
void GenericPrimeOrderCurve::serialize_point(std::span<uint8_t> bytes, const AffinePoint& pt) const {
25,852✔
1541
   from_stash(pt).serialize_to(bytes);
25,852✔
1542
}
25,852✔
1543

1544
void GenericPrimeOrderCurve::serialize_scalar(std::span<uint8_t> bytes, const Scalar& scalar) const {
46,669✔
1545
   BOTAN_ARG_CHECK(bytes.size() == _params().order_bytes(), "Invalid length to serialize_scalar");
46,669✔
1546
   from_stash(scalar).serialize_to(bytes);
46,669✔
1547
}
46,669✔
1548

1549
std::optional<PrimeOrderCurve::Scalar> GenericPrimeOrderCurve::deserialize_scalar(
12,284✔
1550
   std::span<const uint8_t> bytes) const {
1551
   if(auto s = GenericScalar::deserialize(this, bytes)) {
12,284✔
1552
      return stash(s.value());
12,078✔
1553
   } else {
1554
      return {};
206✔
1555
   }
1556
}
1557

1558
std::optional<PrimeOrderCurve::Scalar> GenericPrimeOrderCurve::scalar_from_wide_bytes(
8,431✔
1559
   std::span<const uint8_t> bytes) const {
1560
   if(auto s = GenericScalar::from_wide_bytes(this, bytes)) {
8,431✔
1561
      return stash(s.value());
8,431✔
1562
   } else {
1563
      return {};
×
1564
   }
1565
}
1566

1567
std::optional<PrimeOrderCurve::AffinePoint> GenericPrimeOrderCurve::deserialize_point(
8,652✔
1568
   std::span<const uint8_t> bytes) const {
1569
   if(auto pt = GenericAffinePoint::deserialize(this, bytes)) {
8,652✔
1570
      return stash(pt.value());
8,455✔
1571
   } else {
1572
      return {};
197✔
1573
   }
1574
}
1575

1576
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_add(const Scalar& a, const Scalar& b) const {
7,735✔
1577
   return stash(from_stash(a) + from_stash(b));
7,735✔
1578
}
1579

1580
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_sub(const Scalar& a, const Scalar& b) const {
6,290✔
1581
   return stash(from_stash(a) - from_stash(b));
6,290✔
1582
}
1583

1584
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_mul(const Scalar& a, const Scalar& b) const {
27,668✔
1585
   return stash(from_stash(a) * from_stash(b));
27,668✔
1586
}
1587

1588
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_square(const Scalar& s) const {
1,164✔
1589
   return stash(from_stash(s).square());
1,164✔
1590
}
1591

1592
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert(const Scalar& s) const {
5,809✔
1593
   return stash(from_stash(s).invert());
11,618✔
1594
}
1595

1596
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert_vartime(const Scalar& s) const {
5,508✔
1597
   return stash(from_stash(s).invert_vartime());
5,508✔
1598
}
1599

1600
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_negate(const Scalar& s) const {
5,298✔
1601
   return stash(from_stash(s).negate());
5,298✔
1602
}
1603

1604
bool GenericPrimeOrderCurve::scalar_is_zero(const Scalar& s) const {
13,528✔
1605
   return from_stash(s).is_zero().as_bool();
13,528✔
1606
}
1607

1608
bool GenericPrimeOrderCurve::scalar_equal(const Scalar& a, const Scalar& b) const {
×
1609
   return (from_stash(a) == from_stash(b)).as_bool();
×
1610
}
1611

1612
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_one() const {
44✔
1613
   return stash(GenericScalar::one(this));
44✔
1614
}
1615

1616
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::random_scalar(RandomNumberGenerator& rng) const {
18,639✔
1617
   return stash(GenericScalar::random(this, rng));
18,639✔
1618
}
1619

1620
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::stash(const GenericScalar& s) const {
99,886✔
1621
   return Scalar::_create(shared_from_this(), s.stash_value());
99,886✔
1622
}
1623

1624
GenericScalar GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::Scalar& s) const {
188,384✔
1625
   BOTAN_ARG_CHECK(s._curve().get() == this, "Curve mismatch");
188,384✔
1626
   return GenericScalar(this, s._value());
188,384✔
1627
}
1628

1629
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::stash(const GenericAffinePoint& pt) const {
22,472✔
1630
   auto x_w = pt.x().stash_value();
22,472✔
1631
   auto y_w = pt.y().stash_value();
22,472✔
1632
   return AffinePoint::_create(shared_from_this(), x_w, y_w);
22,472✔
1633
}
1634

1635
GenericAffinePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::AffinePoint& pt) const {
68,457✔
1636
   BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch");
68,457✔
1637
   auto x = GenericField(this, pt._x());
68,457✔
1638
   auto y = GenericField(this, pt._y());
68,457✔
1639
   return GenericAffinePoint(x, y);
68,457✔
1640
}
1641

1642
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::stash(const GenericProjectivePoint& pt) const {
11,875✔
1643
   auto x_w = pt.x().stash_value();
11,875✔
1644
   auto y_w = pt.y().stash_value();
11,875✔
1645
   auto z_w = pt.z().stash_value();
11,875✔
1646
   return ProjectivePoint::_create(shared_from_this(), x_w, y_w, z_w);
11,875✔
1647
}
1648

1649
GenericProjectivePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::ProjectivePoint& pt) const {
11,875✔
1650
   BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch");
11,875✔
1651
   auto x = GenericField(this, pt._x());
11,875✔
1652
   auto y = GenericField(this, pt._y());
11,875✔
1653
   auto z = GenericField(this, pt._z());
11,875✔
1654
   return GenericProjectivePoint(x, y, z);
11,875✔
1655
}
1656

1657
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::hash_to_curve_nu(
×
1658
   std::function<void(std::span<uint8_t>)> expand_message) const {
1659
   BOTAN_UNUSED(expand_message);
×
1660
   throw Not_Implemented("Hash to curve is not implemented for this curve");
×
1661
}
1662

1663
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::hash_to_curve_ro(
×
1664
   std::function<void(std::span<uint8_t>)> expand_message) const {
1665
   BOTAN_UNUSED(expand_message);
×
1666
   throw Not_Implemented("Hash to curve is not implemented for this curve");
×
1667
}
1668

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

1674
   BOTAN_ARG_CHECK(a >= 0 && a < p, "a is invalid");
494✔
1675
   BOTAN_ARG_CHECK(b > 0 && b < p, "b is invalid");
494✔
1676
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "base_x is invalid");
494✔
1677
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "base_y is invalid");
494✔
1678

1679
   const size_t p_bits = p.bits();
247✔
1680

1681
   // Same size restrictions as EC_Group however here we do not require
1682
   // exactly the primes for the 521 or 239 bit exceptions; this code
1683
   // should work fine with any such prime and we are relying on the higher
1684
   // levels to prevent creating such a group in the first place
1685
   //
1686
   // TODO(Botan4) increase the 128 here to 192 when the corresponding EC_Group constructor is changed
1687
   //
1688
   if(p_bits != 521 && p_bits != 239 && (p_bits < 128 || p_bits > 512 || p_bits % 32 != 0)) {
247✔
1689
      return {};
×
1690
   }
1691

1692
   // We don't want to deal with Shanks-Tonelli in the generic case
1693
   if(p % 4 != 3) {
247✔
1694
      return {};
71✔
1695
   }
1696

1697
   // The bit length of the field and order being the same simplifies things
1698
   if(p_bits != order.bits()) {
176✔
1699
      return {};
29✔
1700
   }
1701

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

1709
   Alternately we could lazily compute the base mul table but this brings in
1710
   locking issues which seem a worse alternative overall.
1711
   */
1712
   gpoc->_precompute_base_mul();
147✔
1713
   return gpoc;
147✔
1714
}
147✔
1715

1716
}  // 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