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

randombit / botan / 25619874939

09 May 2026 05:34PM UTC coverage: 89.328% (-0.002%) from 89.33%
25619874939

push

github

web-flow
Merge pull request #5591 from randombit/jack/gha-cache-cleanup

In GH Actions clean up branch caches after the PRs are merged

107641 of 120501 relevant lines covered (89.33%)

11279629.67 hits per line

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

95.36
/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/barrett.h>
13
#include <botan/internal/buffer_stuffer.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/loadstor.h>
16
#include <botan/internal/mp_core.h>
17
#include <botan/internal/pcurves_algos.h>
18
#include <botan/internal/pcurves_instance.h>
19
#include <botan/internal/pcurves_mul.h>
20
#include <botan/internal/primality.h>
21
#include <algorithm>
22

23
namespace Botan::PCurve {
24

25
namespace {
26

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

33
   std::array<word, N> r{};
116,822✔
34

35
   const size_t full_words = bytes.size() / WordInfo<word>::bytes;
116,822✔
36
   const size_t extra_bytes = bytes.size() % WordInfo<word>::bytes;
116,822✔
37

38
   for(size_t i = 0; i != full_words; ++i) {
674,077✔
39
      r[i] = load_be<word>(bytes.data(), full_words - 1 - i);
557,255✔
40
   }
41

42
   if(extra_bytes > 0) {
116,822✔
43
      const size_t shift = extra_bytes * 8;
44,138✔
44
      bigint_shl1(r.data(), r.size(), r.size(), shift);
44,138✔
45

46
      for(size_t i = 0; i != extra_bytes; ++i) {
264,460✔
47
         const word b0 = bytes[WordInfo<word>::bytes * full_words + i];
220,322✔
48
         r[0] |= (b0 << (8 * (extra_bytes - 1 - i)));
220,322✔
49
      }
50
   }
51

52
   return r;
116,822✔
53
}
54

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

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

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

65
   tbl.push_back(elem);
40,845✔
66

67
   for(size_t i = 1; i != WindowElements; ++i) {
612,675✔
68
      if(i % 2 == 1) {
571,830✔
69
         tbl.push_back(tbl[i / 2].square());
571,830✔
70
      } else {
71
         tbl.push_back(tbl[i - 1] * tbl[0]);
571,830✔
72
      }
73
   }
74

75
   auto r = one;
40,845✔
76

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

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

83
   for(size_t i = 1; i != Windows; ++i) {
2,485,288✔
84
      for(size_t j = 0; j != WindowBits; ++j) {
12,222,215✔
85
         r = r.square();
9,777,772✔
86
      }
87
      const size_t w = read_window_bits<WindowBits>(exp, (Windows - i - 1) * WindowBits);
2,444,443✔
88

89
      if(w > 0) {
2,444,443✔
90
         r *= tbl[w - 1];
2,281,665✔
91
      }
92
   }
93

94
   return r;
40,845✔
95
}
40,845✔
96

97
}  // namespace
98

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

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

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

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

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

144
      size_t words() const { return m_words; }
7,103,170✔
145

146
      size_t order_bits() const { return m_order_bits; }
42,963✔
147

148
      size_t order_bytes() const { return m_order_bytes; }
79,570✔
149

150
      size_t field_bits() const { return m_field_bits; }
86,340✔
151

152
      size_t field_bytes() const { return m_field_bytes; }
54,950✔
153

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

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

158
      const StorageUnit& field() const { return m_field; }
52,825,032✔
159

160
      const StorageUnit& field_minus_2() const { return m_field_minus_2; }
32,165✔
161

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

164
      const StorageUnit& field_monty_r2() const { return m_field_monty_r2; }
71,697✔
165

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

168
      const StorageUnit& field_inv_2() const { return m_field_inv_2; }
825,425✔
169

170
      word field_p_dash() const { return m_field_p_dash; }
53,114,454✔
171

172
      const StorageUnit& order() const { return m_order; }
1,062,380✔
173

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

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

178
      const StorageUnit& order_monty_r2() const { return m_order_monty_r2; }
36,813✔
179

180
      const StorageUnit& order_monty_r3() const { return m_order_monty_r3; }
9,904✔
181

182
      const StorageUnit& order_inv_2() const { return m_order_inv_2; }
1,001,752✔
183

184
      word order_p_dash() const { return m_order_p_dash; }
1,991,010✔
185

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

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

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

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

194
      bool a_is_minus_3() const { return m_a_is_minus_3; }
938,817✔
195

196
      bool a_is_zero() const { return m_a_is_zero; }
467,463✔
197

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

200
      void mul(std::array<word, 2 * N>& z, const std::array<word, N>& x, const std::array<word, N>& y) const {
26,953,377✔
201
         clear_mem(z);
26,953,377✔
202

203
         if(m_words == 4) {
26,953,377✔
204
            bigint_comba_mul4(z.data(), x.data(), y.data());
11,682,078✔
205
         } else if(m_words == 6) {
15,271,299✔
206
            bigint_comba_mul6(z.data(), x.data(), y.data());
142,455✔
207
         } else if(m_words == 8) {
15,128,844✔
208
            bigint_comba_mul8(z.data(), x.data(), y.data());
3,307,496✔
209
         } else if(m_words == 9) {
11,821,348✔
210
            bigint_comba_mul9(z.data(), x.data(), y.data());
×
211
         } else {
212
            bigint_mul(z.data(), z.size(), x.data(), m_words, m_words, y.data(), m_words, m_words, nullptr, 0);
11,821,348✔
213
         }
214
      }
26,953,377✔
215

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

219
         if(m_words == 4) {
28,012,910✔
220
            bigint_comba_sqr4(z.data(), x.data());
12,059,261✔
221
         } else if(m_words == 6) {
15,953,649✔
222
            bigint_comba_sqr6(z.data(), x.data());
59,638✔
223
         } else if(m_words == 8) {
15,894,011✔
224
            bigint_comba_sqr8(z.data(), x.data());
3,769,202✔
225
         } else if(m_words == 9) {
12,124,809✔
226
            bigint_comba_sqr9(z.data(), x.data());
×
227
         } else {
228
            bigint_sqr(z.data(), z.size(), x.data(), m_words, m_words, nullptr, 0);
12,124,809✔
229
         }
230
      }
28,012,910✔
231

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

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

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

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

255
      Montgomery_Params m_monty_order;
256
      Montgomery_Params m_monty_field;
257

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

369
      friend GenericScalar operator+(const GenericScalar& a, const GenericScalar& b) {
1,004,595✔
370
         const auto* curve = check_curve(a, b);
2,009,190✔
371
         const size_t words = curve->_params().words();
1,004,595✔
372

373
         StorageUnit t{};
1,004,595✔
374
         const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words);
2,009,190✔
375

376
         StorageUnit r{};
1,004,595✔
377
         bigint_monty_maybe_sub(words, r.data(), carry, t.data(), curve->_params().order().data());
1,004,595✔
378
         return GenericScalar(curve, r);
1,004,595✔
379
      }
380

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

383
      friend GenericScalar operator*(const GenericScalar& a, const GenericScalar& b) {
68,665✔
384
         const auto* curve = check_curve(a, b);
137,330✔
385

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

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

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

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

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

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

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

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

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

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

436
         // Conditional ok: this function is variable time
437
         while((a.m_val[0] & 1) != 1) {
3,996,789✔
438
            shift_right<1>(a.m_val);
17,939,565✔
439

440
            const W borrow = shift_right<1>(x.m_val);
1,993,285✔
441

442
            // Conditional ok: this function is variable time
443
            if(borrow > 0) {
1,993,285✔
444
               bigint_add2(x.m_val.data(), N, inv_2.data(), N);
1,000,535✔
445
            }
446
         }
447
      }
1,001,752✔
448

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

457
         auto x = GenericScalar(m_curve, std::array<W, N>{1});
5,663✔
458
         auto b = GenericScalar(m_curve, from_rep(m_curve, m_val));
11,326✔
459

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

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

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

470
         for(;;) {
1,986,515✔
471
            // Conditional ok: this function is variable time
472
            if(a.m_val == b.m_val) {
996,089✔
473
               // At this point it should be that a == b == 1
474
               auto r = y.negate();
5,663✔
475

476
               // Convert back to Montgomery
477
               return GenericScalar(curve(), to_rep(curve(), r.m_val));
11,326✔
478
            }
479

480
            auto nx = x + y;
990,426✔
481

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

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

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

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

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

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

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

532
      CT::Choice is_nonzero() const { return !is_zero(); }
31,150✔
533

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

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

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

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

549
      const GenericPrimeOrderCurve* curve() const { return m_curve; }
1,013,224✔
550

551
      GenericScalar(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {}
58,245✔
552

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

556
      const W* data() const { return m_val.data(); }
2,538,962✔
557

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

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

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

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

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

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

596
namespace {
597

598
class GenericField final {
599
   public:
600
      typedef word W;
601
      typedef PrimeOrderCurve::StorageUnit StorageUnit;
602
      static constexpr size_t N = PrimeOrderCurve::StorageWords;
603

604
      static std::optional<GenericField> deserialize(const GenericPrimeOrderCurve* curve,
74,338✔
605
                                                     std::span<const uint8_t> bytes) {
606
         const size_t len = curve->_params().field_bytes();
74,338✔
607

608
         if(bytes.size() != len) {
74,338✔
609
            return {};
×
610
         }
611

612
         const auto words = bytes_to_words<N>(bytes);
74,338✔
613

614
         if(words) {
74,338✔
615
            if(!bigint_ct_is_lt(words->data(), N, curve->_params().field().data(), N).as_bool()) {
74,338✔
616
               return {};
4,850✔
617
            }
618

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

626
      static GenericField from_words(const GenericPrimeOrderCurve* curve, const std::array<word, N>& words) {
71,697✔
627
         return GenericField(curve, to_rep(curve, words));
71,697✔
628
      }
629

630
      static GenericField zero(const GenericPrimeOrderCurve* curve) {
740,220✔
631
         const StorageUnit zeros{};
740,220✔
632
         return GenericField(curve, zeros);
740,220✔
633
      }
634

635
      static GenericField one(const GenericPrimeOrderCurve* curve) {
1,283,460✔
636
         return GenericField(curve, curve->_params().field_monty_r1());
1,283,460✔
637
      }
638

639
      static GenericField curve_a(const GenericPrimeOrderCurve* curve) {
442,651✔
640
         return GenericField(curve, curve->_params().monty_curve_a());
442,651✔
641
      }
642

643
      static GenericField curve_b(const GenericPrimeOrderCurve* curve) {
20,261✔
644
         return GenericField(curve, curve->_params().monty_curve_b());
20,261✔
645
      }
646

647
      static GenericField random(const GenericPrimeOrderCurve* curve, RandomNumberGenerator& rng) {
51,304✔
648
         constexpr size_t MAX_ATTEMPTS = 1000;
51,304✔
649

650
         const size_t bits = curve->_params().field_bits();
51,304✔
651

652
         std::vector<uint8_t> buf(curve->_params().field_bytes());
51,304✔
653

654
         for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
56,153✔
655
            rng.randomize(buf);
56,153✔
656

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

664
            if(auto s = GenericField::deserialize(curve, buf)) {
56,153✔
665
               if(s.value().is_nonzero().as_bool()) {
51,304✔
666
                  return s.value();
51,304✔
667
               }
668
            }
669
         }
670

671
         throw Internal_Error("Failed to generate random Scalar within bounded number of attempts");
×
672
      }
51,304✔
673

674
      /**
675
      * Return the value of this divided by 2
676
      */
677
      GenericField div2() const {
825,425✔
678
         StorageUnit t = value();
825,425✔
679
         const W borrow = shift_right<1>(t);
825,425✔
680

681
         // If value was odd, add (P/2)+1
682
         bigint_cnd_add(borrow, t.data(), m_curve->_params().field_inv_2().data(), N);
825,425✔
683

684
         return GenericField(m_curve, t);
825,425✔
685
      }
686

687
      /// Return (*this) multiplied by 2
688
      GenericField mul2() const {
10,250,929✔
689
         StorageUnit t = value();
10,250,929✔
690
         const W carry = shift_left<1>(t);
10,250,929✔
691

692
         StorageUnit r;
10,250,929✔
693
         bigint_monty_maybe_sub<N>(r.data(), carry, t.data(), m_curve->_params().field().data());
10,250,929✔
694
         return GenericField(m_curve, r);
10,250,929✔
695
      }
696

697
      /// Return (*this) multiplied by 3
698
      GenericField mul3() const { return mul2() + (*this); }
2,952,848✔
699

700
      /// Return (*this) multiplied by 4
701
      GenericField mul4() const { return mul2().mul2(); }
113,392✔
702

703
      /// Return (*this) multiplied by 8
704
      GenericField mul8() const { return mul2().mul2().mul2(); }
113,392✔
705

706
      friend GenericField operator+(const GenericField& a, const GenericField& b) {
23,931,765✔
707
         const auto* curve = check_curve(a, b);
47,863,530✔
708
         const size_t words = curve->_params().words();
23,931,765✔
709

710
         StorageUnit t{};
23,931,765✔
711
         const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words);
47,863,530✔
712

713
         StorageUnit r{};
23,931,765✔
714
         bigint_monty_maybe_sub(words, r.data(), carry, t.data(), curve->_params().field().data());
23,931,765✔
715
         return GenericField(curve, r);
23,931,765✔
716
      }
717

718
      friend GenericField operator-(const GenericField& a, const GenericField& b) { return a + b.negate(); }
18,241,502✔
719

720
      friend GenericField operator*(const GenericField& a, const GenericField& b) {
19,562,476✔
721
         const auto* curve = check_curve(a, b);
39,124,952✔
722

723
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
19,562,476✔
724
         curve->_params().mul(z, a.value(), b.value());
19,562,476✔
725
         return GenericField(curve, redc(curve, z));
19,562,476✔
726
      }
727

728
      GenericField& operator*=(const GenericField& other) {
6,856,343✔
729
         const auto* curve = check_curve(*this, other);
13,712,686✔
730

731
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
6,856,343✔
732
         curve->_params().mul(z, value(), other.value());
6,856,343✔
733
         m_val = redc(curve, z);
6,856,343✔
734
         return (*this);
6,856,343✔
735
      }
736

737
      GenericField square() const {
26,561,247✔
738
         std::array<W, 2 * N> z;  // NOLINT(*-member-init)
26,561,247✔
739
         m_curve->_params().sqr(z, value());
26,561,247✔
740
         return GenericField(m_curve, redc(m_curve, z));
26,561,247✔
741
      }
742

743
      GenericField pow_vartime(const StorageUnit& exp) const {
35,036✔
744
         auto one = GenericField::one(curve());
35,036✔
745
         auto bits = curve()->_params().field_bits();
35,036✔
746
         auto words = curve()->_params().words();
35,036✔
747
         return impl_pow_vartime(*this, one, bits, std::span{exp}.last(words));
70,072✔
748
      }
749

750
      GenericField negate() const {
18,565,129✔
751
         auto x_is_zero = CT::all_zeros(this->data(), N);
37,130,258✔
752

753
         StorageUnit r;
18,565,129✔
754
         bigint_sub3(r.data(), m_curve->_params().field().data(), N, this->data(), N);
18,565,129✔
755
         x_is_zero.if_set_zero_out(r.data(), N);
18,565,129✔
756
         return GenericField(m_curve, r);
18,565,129✔
757
      }
758

759
      GenericField invert() const { return pow_vartime(m_curve->_params().field_minus_2()); }
30,441✔
760

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

767
      template <concepts::resizable_byte_buffer T>
768
      T serialize() const {
3,646✔
769
         T bytes(m_curve->_params().field_bytes());
3,646✔
770
         serialize_to(bytes);
3,646✔
771
         return bytes;
3,646✔
772
      }
×
773

774
      void serialize_to(std::span<uint8_t> bytes) const {
55,402✔
775
         auto v = from_rep(m_curve, m_val);
55,402✔
776
         std::reverse(v.begin(), v.end());
55,402✔
777

778
         const size_t flen = m_curve->_params().field_bytes();
55,402✔
779
         BOTAN_ARG_CHECK(bytes.size() == flen, "Expected output span provided");
55,402✔
780

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

787
      CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_curve->_params().words()).as_choice(); }
7,027,845✔
788

789
      CT::Choice is_nonzero() const { return !is_zero(); }
51,304✔
790

791
      CT::Choice operator==(const GenericField& other) const {
24,507✔
792
         if(this->m_curve != other.m_curve) {
24,507✔
793
            return CT::Choice::no();
×
794
         }
795

796
         return CT::is_equal(m_val.data(), other.m_val.data(), m_curve->_params().words()).as_choice();
24,507✔
797
      }
798

799
      const StorageUnit& stash_value() const { return m_val; }
800

801
      const GenericPrimeOrderCurve* curve() const { return m_curve; }
35,036✔
802

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

808
      /**
809
      * Convert the integer to standard representation and return the sequence of words
810
      */
811
      StorageUnit to_words() const { return from_rep(m_curve, m_val); }
2,209✔
812

813
      void _const_time_poison() const { CT::poison(m_val); }
12,847✔
814

815
      void _const_time_unpoison() const { CT::unpoison(m_val); }
12,847✔
816

817
      static void conditional_swap(CT::Choice cond, GenericField& x, GenericField& y) {
25,124✔
818
         const W mask = cond.into_bitmask<W>();
25,124✔
819

820
         for(size_t i = 0; i != N; ++i) {
251,240✔
821
            auto nx = choose(mask, y.m_val[i], x.m_val[i]);
226,116✔
822
            auto ny = choose(mask, x.m_val[i], y.m_val[i]);
226,116✔
823
            x.m_val[i] = nx;
226,116✔
824
            y.m_val[i] = ny;
226,116✔
825
         }
826
      }
827

828
      void conditional_assign(CT::Choice cond, const GenericField& nx) {
315,404✔
829
         const W mask = cond.into_bitmask<W>();
315,404✔
830

831
         for(size_t i = 0; i != N; ++i) {
3,154,040✔
832
            m_val[i] = choose(mask, nx.m_val[i], m_val[i]);
2,838,636✔
833
         }
834
      }
835

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

845
         for(size_t i = 0; i != N; ++i) {
186,213,770✔
846
            x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
167,592,393✔
847
            y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
167,592,393✔
848
         }
849
      }
850

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

865
         for(size_t i = 0; i != N; ++i) {
26,389,650✔
866
            x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
23,750,685✔
867
            y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
23,750,685✔
868
            z.m_val[i] = choose(mask, nz.m_val[i], z.m_val[i]);
23,750,685✔
869
         }
870
      }
2,638,965✔
871

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

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

882
      GenericField(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {}
1,276,447✔
883

884
   private:
885
      const StorageUnit& value() const { return m_val; }
52,980,066✔
886

887
      const W* data() const { return m_val.data(); }
61,062,023✔
888

889
      static const GenericPrimeOrderCurve* check_curve(const GenericField& a, const GenericField& b) {
50,350,584✔
890
         BOTAN_STATE_CHECK(a.m_curve == b.m_curve);
50,350,584✔
891
         return a.m_curve;
50,350,584✔
892
      }
893

894
      static StorageUnit redc(const GenericPrimeOrderCurve* curve, std::array<W, 2 * N> z) {
53,114,454✔
895
         const auto& mod = curve->_params().field();
53,114,454✔
896
         const size_t words = curve->_params().words();
53,114,454✔
897
         StorageUnit r{};
53,114,454✔
898
         StorageUnit ws{};
53,114,454✔
899
         bigint_monty_redc(
53,114,454✔
900
            r.data(), z.data(), mod.data(), words, curve->_params().field_p_dash(), ws.data(), ws.size());
53,114,454✔
901
         return r;
53,114,454✔
902
      }
903

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

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

916
      const GenericPrimeOrderCurve* m_curve;
917
      StorageUnit m_val;
918
};
919

920
}  // namespace
921

922
/**
923
* Affine Curve Point
924
*
925
* This contains a pair of integers (x,y) which satisfy the curve equation
926
*/
927
class GenericAffinePoint final {
928
   public:
929
      GenericAffinePoint(const GenericField& x, const GenericField& y) : m_x(x), m_y(y) {}
304,621✔
930

931
      explicit GenericAffinePoint(const GenericPrimeOrderCurve* curve) :
932
            m_x(GenericField::zero(curve)), m_y(GenericField::zero(curve)) {}
933

934
      static GenericAffinePoint identity(const GenericPrimeOrderCurve* curve) {
732,328✔
935
         return GenericAffinePoint(GenericField::zero(curve), GenericField::zero(curve));
732,328✔
936
      }
937

938
      static GenericAffinePoint identity(const GenericAffinePoint& pt) { return identity(pt.curve()); }
415,791✔
939

940
      CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); }
1,307,276✔
941

942
      GenericAffinePoint negate() const { return GenericAffinePoint(x(), y().negate()); }
4,308✔
943

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

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

967
         // Intentionally wrapping; set to maximum size_t if idx == 0
968
         const size_t idx1 = static_cast<size_t>(idx - 1);
316,393✔
969
         for(size_t i = 0; i != pts.size(); ++i) {
10,440,969✔
970
            const auto found = CT::Mask<size_t>::is_equal(idx1, i).as_choice();
10,124,576✔
971
            result.conditional_assign(found, pts[i]);
10,124,576✔
972
         }
973

974
         return result;
316,393✔
975
      }
976

977
      /**
978
      * Return (x^3 + A*x + B) mod p
979
      */
980
      static GenericField x3_ax_b(const GenericField& x) {
20,261✔
981
         return (x.square() + GenericField::curve_a(x.curve())) * x + GenericField::curve_b(x.curve());
20,261✔
982
      }
983

984
      /**
985
      * Point deserialization
986
      *
987
      * This accepts compressed or uncompressed formats.
988
      */
989
      static std::optional<GenericAffinePoint> deserialize(const GenericPrimeOrderCurve* curve,
8,552✔
990
                                                           std::span<const uint8_t> bytes) {
991
         const size_t fe_bytes = curve->_params().field_bytes();
8,552✔
992

993
         if(bytes.size() == 1 + 2 * fe_bytes && bytes[0] == 0x04) {
8,552✔
994
            auto x = GenericField::deserialize(curve, bytes.subspan(1, fe_bytes));
5,537✔
995
            auto y = GenericField::deserialize(curve, bytes.subspan(1 + fe_bytes, fe_bytes));
5,537✔
996

997
            if(x && y) {
5,537✔
998
               const auto lhs = (*y).square();
5,537✔
999
               const auto rhs = GenericAffinePoint::x3_ax_b(*x);
5,537✔
1000
               if((lhs == rhs).as_bool()) {
5,537✔
1001
                  return GenericAffinePoint(*x, *y);
5,463✔
1002
               }
1003
            }
1004
         } else if(bytes.size() == 1 + fe_bytes && (bytes[0] == 0x02 || bytes[0] == 0x03)) {
3,015✔
1005
            const CT::Choice y_is_even = CT::Mask<uint8_t>::is_equal(bytes[0], 0x02).as_choice();
2,871✔
1006

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

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

1021
         return {};
74✔
1022
      }
1023

1024
      /**
1025
      * Return the affine x coordinate
1026
      */
1027
      const GenericField& x() const { return m_x; }
1,244,157✔
1028

1029
      /**
1030
      * Return the affine y coordinate
1031
      */
1032
      const GenericField& y() const { return m_y; }
936,649✔
1033

1034
      /**
1035
      * Conditional assignment of an affine point
1036
      */
1037
      void conditional_assign(CT::Choice cond, const GenericAffinePoint& pt) {
18,621,377✔
1038
         GenericField::conditional_assign(m_x, m_y, cond, pt.x(), pt.y());
18,621,377✔
1039
      }
18,621,377✔
1040

1041
      const GenericPrimeOrderCurve* curve() const { return m_x.curve(); }
758,062✔
1042

1043
      void _const_time_poison() const { CT::poison_all(m_x, m_y); }
1044

1045
      void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y); }
1046

1047
   private:
1048
      GenericField m_x;
1049
      GenericField m_y;
1050
};
1051

1052
class GenericProjectivePoint final {
1053
   public:
1054
      typedef GenericProjectivePoint Self;
1055

1056
      using FieldElement = GenericField;
1057

1058
      /**
1059
      * Convert a point from affine to projective form
1060
      */
1061
      static Self from_affine(const GenericAffinePoint& pt) {
25,124✔
1062
         auto x = pt.x();
25,124✔
1063
         auto y = pt.y();
25,124✔
1064
         auto z = GenericField::one(x.curve());
25,124✔
1065

1066
         // If pt is identity (0,0) swap y/z to convert (0,0,1) into (0,1,0)
1067
         GenericField::conditional_swap(pt.is_identity(), y, z);
25,124✔
1068
         return GenericProjectivePoint(x, y, z);
25,124✔
1069
      }
1070

1071
      /**
1072
      * Return the identity element
1073
      */
1074
      static Self identity(const GenericPrimeOrderCurve* curve) {
1075
         return Self(GenericField::zero(curve), GenericField::one(curve), GenericField::zero(curve));
1076
      }
1077

1078
      /**
1079
      * Default constructor: the identity element
1080
      */
1081
      explicit GenericProjectivePoint(const GenericPrimeOrderCurve* curve) :
5,021✔
1082
            m_x(GenericField::zero(curve)), m_y(GenericField::one(curve)), m_z(GenericField::zero(curve)) {}
5,021✔
1083

1084
      /**
1085
      * Affine constructor: take x/y coordinates
1086
      */
1087
      GenericProjectivePoint(const GenericField& x, const GenericField& y) :
1088
            m_x(x), m_y(y), m_z(GenericField::one(m_x.curve())) {}
1089

1090
      /**
1091
      * Projective constructor: take x/y/z coordinates
1092
      */
1093
      GenericProjectivePoint(const GenericField& x, const GenericField& y, const GenericField& z) :
2,298,642✔
1094
            m_x(x), m_y(y), m_z(z) {}
2,254,934✔
1095

1096
      friend Self operator+(const Self& a, const Self& b) { return Self::add(a, b); }
98,184✔
1097

1098
      friend Self operator+(const Self& a, const GenericAffinePoint& b) { return Self::add_mixed(a, b); }
52,898✔
1099

1100
      friend Self operator+(const GenericAffinePoint& a, const Self& b) { return Self::add_mixed(b, a); }
30,771✔
1101

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

1107
      Self& operator+=(const GenericAffinePoint& other) {
822,980✔
1108
         (*this) = (*this) + other;
822,980✔
1109
         return (*this);
822,980✔
1110
      }
1111

1112
      CT::Choice is_identity() const { return z().is_zero(); }
1,630,879✔
1113

1114
      void conditional_assign(CT::Choice cond, const Self& pt) {
6,731✔
1115
         GenericField::conditional_assign(m_x, m_y, m_z, cond, pt.x(), pt.y(), pt.z());
6,731✔
1116
      }
1117

1118
      /**
1119
      * Mixed (projective + affine) point addition
1120
      */
1121
      static Self add_mixed(const Self& a, const GenericAffinePoint& b) {
908,617✔
1122
         return point_add_mixed<Self, GenericAffinePoint, GenericField>(a, b, GenericField::one(a.curve()));
1,817,234✔
1123
      }
1124

1125
      static Self add_or_sub(const Self& a, const GenericAffinePoint& b, CT::Choice sub) {
309,662✔
1126
         return point_add_or_sub_mixed<Self, GenericAffinePoint, GenericField>(a, b, sub, GenericField::one(a.curve()));
619,324✔
1127
      }
1128

1129
      /**
1130
      * Projective point addition
1131
      */
1132
      static Self add(const Self& a, const Self& b) { return point_add<Self, GenericField>(a, b); }
98,184✔
1133

1134
      /**
1135
      * Iterated point doubling
1136
      */
1137
      Self dbl_n(size_t n) const {
825,425✔
1138
         if(curve()->_params().a_is_minus_3()) {
825,425✔
1139
            return dbl_n_a_minus_3(*this, n);
414,140✔
1140
         } else if(curve()->_params().a_is_zero()) {
411,285✔
1141
            return dbl_n_a_zero(*this, n);
40,212✔
1142
         } else {
1143
            const auto A = GenericField::curve_a(curve());
371,073✔
1144
            return dbl_n_generic(*this, A, n);
371,073✔
1145
         }
1146
      }
1147

1148
      /**
1149
      * Point doubling
1150
      */
1151
      Self dbl() const {
113,392✔
1152
         if(curve()->_params().a_is_minus_3()) {
113,392✔
1153
            return dbl_a_minus_3(*this);
57,214✔
1154
         } else if(curve()->_params().a_is_zero()) {
56,178✔
1155
            return dbl_a_zero(*this);
4,861✔
1156
         } else {
1157
            const auto A = GenericField::curve_a(curve());
51,317✔
1158
            return dbl_generic(*this, A);
51,317✔
1159
         }
1160
      }
1161

1162
      /**
1163
      * Point negation
1164
      */
1165
      Self negate() const { return Self(x(), y().negate(), z()); }
13,462✔
1166

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

1177
         if(rng.is_seeded()) {
51,388✔
1178
            auto r = GenericField::random(curve(), rng);
51,304✔
1179

1180
            auto r2 = r.square();
51,304✔
1181
            auto r3 = r2 * r;
51,304✔
1182

1183
            m_x *= r2;
51,304✔
1184
            m_y *= r3;
51,304✔
1185
            m_z *= r;
51,304✔
1186
         }
1187
      }
51,388✔
1188

1189
      /**
1190
      * Return the projective x coordinate
1191
      */
1192
      const GenericField& x() const { return m_x; }
1,750,515✔
1193

1194
      /**
1195
      * Return the projective y coordinate
1196
      */
1197
      const GenericField& y() const { return m_y; }
2,670,478✔
1198

1199
      /**
1200
      * Return the projective z coordinate
1201
      */
1202
      const GenericField& z() const { return m_z; }
2,357,633✔
1203

1204
      const GenericPrimeOrderCurve* curve() const { return m_x.curve(); }
2,208,400✔
1205

1206
      void _const_time_poison() const { CT::poison_all(m_x, m_y, m_z); }
12,847✔
1207

1208
      void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y, m_z); }
12,847✔
1209

1210
   private:
1211
      GenericField m_x;
1212
      GenericField m_y;
1213
      GenericField m_z;
1214
};
1215

1216
namespace {
1217

1218
class GenericCurve final {
1219
   public:
1220
      typedef GenericField FieldElement;
1221
      typedef GenericScalar Scalar;
1222
      typedef GenericAffinePoint AffinePoint;
1223
      typedef GenericProjectivePoint ProjectivePoint;
1224

1225
      typedef word WordType;
1226
};
1227

1228
class GenericBlindedScalarBits final {
6,116✔
1229
   public:
1230
      GenericBlindedScalarBits(const GenericScalar& scalar, RandomNumberGenerator& rng, size_t wb) {
13,435✔
1231
         BOTAN_ASSERT_NOMSG(wb == 1 || wb == 2 || wb == 3 || wb == 4 || wb == 5 || wb == 6 || wb == 7);
13,435✔
1232

1233
         const auto& params = scalar.curve()->_params();
13,435✔
1234

1235
         const size_t order_bits = params.order_bits();
13,435✔
1236
         m_window_bits = wb;
13,435✔
1237

1238
         const size_t blinder_bits = scalar_blinding_bits(order_bits);
13,435✔
1239

1240
         if(blinder_bits > 0 && rng.is_seeded()) {
13,435✔
1241
            const size_t mask_words = (blinder_bits + WordInfo<word>::bits - 1) / WordInfo<word>::bits;
13,414✔
1242
            const size_t mask_bytes = mask_words * WordInfo<word>::bytes;
13,414✔
1243

1244
            const size_t words = params.words();
13,414✔
1245

1246
            secure_vector<uint8_t> maskb(mask_bytes);
13,414✔
1247
            rng.randomize(maskb);
13,414✔
1248

1249
            std::array<word, PrimeOrderCurve::StorageWords> mask{};
13,414✔
1250
            load_le(mask.data(), maskb.data(), mask_words);
13,414✔
1251

1252
            // Mask to exactly blinder_bits and set MSB and LSB
1253
            const size_t excess = mask_words * WordInfo<word>::bits - blinder_bits;
13,414✔
1254
            if(excess > 0) {
13,414✔
1255
               mask[mask_words - 1] &= (static_cast<word>(1) << (WordInfo<word>::bits - excess)) - 1;
12,504✔
1256
            }
1257
            const size_t msb_pos = (blinder_bits - 1) % WordInfo<word>::bits;
13,414✔
1258
            mask[(blinder_bits - 1) / WordInfo<word>::bits] |= static_cast<word>(1) << msb_pos;
13,414✔
1259
            mask[0] |= 1;
13,414✔
1260

1261
            std::array<word, 2 * PrimeOrderCurve::StorageWords> mask_n{};
13,414✔
1262

1263
            const auto sw = scalar.to_words();
13,414✔
1264

1265
            // Compute masked scalar s + k*n
1266
            params.mul(mask_n, mask, params.order());
13,414✔
1267
            bigint_add2(mask_n.data(), 2 * words, sw.data(), words);
13,414✔
1268

1269
            std::reverse(mask_n.begin(), mask_n.end());
13,414✔
1270
            m_bytes = store_be<std::vector<uint8_t>>(mask_n);
13,414✔
1271
            m_bits = order_bits + blinder_bits;
13,414✔
1272
         } else {
13,414✔
1273
            // No RNG available, skip blinding
1274
            m_bytes = scalar.serialize<std::vector<uint8_t>>();
21✔
1275
            m_bits = order_bits;
21✔
1276
         }
1277

1278
         m_windows = (m_bits + wb - 1) / wb;
13,435✔
1279
      }
13,435✔
1280

1281
      size_t windows() const { return m_windows; }
1282

1283
      size_t bits() const { return m_bits; }
12,847✔
1284

1285
      size_t get_window(size_t offset) const {
779,266✔
1286
         if(m_window_bits == 1) {
779,266✔
1287
            return read_window_bits<1>(std::span{m_bytes}, offset);
×
1288
         } else if(m_window_bits == 2) {
1289
            return read_window_bits<2>(std::span{m_bytes}, offset);
×
1290
         } else if(m_window_bits == 3) {
1291
            return read_window_bits<3>(std::span{m_bytes}, offset);
94,164✔
1292
         } else if(m_window_bits == 4) {
1293
            return read_window_bits<4>(std::span{m_bytes}, offset);
368,709✔
1294
         } else if(m_window_bits == 5) {
1295
            return read_window_bits<5>(std::span{m_bytes}, offset);
×
1296
         } else if(m_window_bits == 6) {
1297
            return read_window_bits<6>(std::span{m_bytes}, offset);
×
1298
         } else if(m_window_bits == 7) {
1299
            return read_window_bits<7>(std::span{m_bytes}, offset);
316,393✔
1300
         } else {
1301
            BOTAN_ASSERT_UNREACHABLE();
×
1302
         }
1303
      }
1304

1305
   private:
1306
      std::vector<uint8_t> m_bytes;
1307
      size_t m_bits;
1308
      size_t m_windows;
1309
      size_t m_window_bits;
1310
};
1311

1312
class GenericWindowedMul final {
11,056✔
1313
   public:
1314
      static constexpr size_t WindowBits = VarPointWindowBits;
1315
      static constexpr size_t TableSize = (1 << WindowBits) - 1;
1316

1317
      explicit GenericWindowedMul(const GenericAffinePoint& pt) :
5,528✔
1318
            m_table(varpoint_setup<GenericCurve, TableSize>(pt)) {}
5,528✔
1319

1320
      GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) {
5,528✔
1321
         const GenericBlindedScalarBits bits(s, rng, WindowBits);
5,528✔
1322

1323
         return varpoint_exec<GenericCurve, WindowBits>(m_table, bits, rng);
5,528✔
1324
      }
5,528✔
1325

1326
   private:
1327
      AffinePointTable<GenericCurve> m_table;
1328
};
1329

1330
}  // namespace
1331

1332
class GenericBaseMulTable final {
94✔
1333
   public:
1334
      static constexpr size_t WindowBits = BasePointWindowBits;
1335

1336
      // +1 for Booth carry from the top window
1337
      explicit GenericBaseMulTable(const GenericAffinePoint& pt) :
47✔
1338
            m_table(basemul_booth_setup<GenericCurve, WindowBits>(pt, blinded_scalar_bits(*pt.curve()) + 1)) {}
141✔
1339

1340
      GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) {
6,731✔
1341
         // W+1 bit windows for Booth recoding overlap
1342
         const GenericBlindedScalarBits scalar(s, rng, WindowBits + 1);
6,731✔
1343
         return basemul_booth_exec<GenericCurve, WindowBits>(m_table, scalar, rng);
6,731✔
1344
      }
6,731✔
1345

1346
   private:
1347
      static size_t blinded_scalar_bits(const GenericPrimeOrderCurve& curve) {
47✔
1348
         const size_t order_bits = curve.order_bits();
47✔
1349
         return order_bits + scalar_blinding_bits(order_bits);
94✔
1350
      }
1351

1352
      std::vector<GenericAffinePoint> m_table;
1353
};
1354

1355
namespace {
1356

1357
class GenericWindowedMul2 final {
1358
   public:
1359
      static constexpr size_t WindowBits = Mul2PrecompWindowBits;
1360

1361
      GenericWindowedMul2(const GenericWindowedMul2& other) = delete;
1362
      GenericWindowedMul2(GenericWindowedMul2&& other) = delete;
1363
      GenericWindowedMul2& operator=(const GenericWindowedMul2& other) = delete;
1364
      GenericWindowedMul2& operator=(GenericWindowedMul2&& other) = delete;
1365

1366
      ~GenericWindowedMul2() = default;
588✔
1367

1368
      GenericWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) :
588✔
1369
            m_table(mul2_setup<GenericCurve, WindowBits>(p, q)) {}
1,176✔
1370

1371
      GenericProjectivePoint mul2(const GenericScalar& x, const GenericScalar& y, RandomNumberGenerator& rng) const {
588✔
1372
         const GenericBlindedScalarBits x_bits(x, rng, WindowBits);
588✔
1373
         const GenericBlindedScalarBits y_bits(y, rng, WindowBits);
588✔
1374
         return mul2_exec<GenericCurve, WindowBits>(m_table, x_bits, y_bits, rng);
588✔
1375
      }
1,176✔
1376

1377
   private:
1378
      AffinePointTable<GenericCurve> m_table;
1379
};
1380

1381
class GenericVartimeWindowedMul2 final : public PrimeOrderCurve::PrecomputedMul2Table {
1382
   public:
1383
      static constexpr size_t WindowBits = Mul2PrecompWindowBits;
1384

1385
      GenericVartimeWindowedMul2(const GenericVartimeWindowedMul2& other) = delete;
1386
      GenericVartimeWindowedMul2(GenericVartimeWindowedMul2&& other) = delete;
1387
      GenericVartimeWindowedMul2& operator=(const GenericVartimeWindowedMul2& other) = delete;
1388
      GenericVartimeWindowedMul2& operator=(GenericVartimeWindowedMul2&& other) = delete;
1389

1390
      ~GenericVartimeWindowedMul2() override = default;
3,558✔
1391

1392
      GenericVartimeWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) :
1,779✔
1393
            m_table(to_affine_batch<GenericCurve, true>(mul2_setup<GenericCurve, WindowBits>(p, q))) {}
3,558✔
1394

1395
      GenericProjectivePoint mul2_vartime(const GenericScalar& x, const GenericScalar& y) const {
5,021✔
1396
         const auto x_bits = x.serialize<std::vector<uint8_t>>();
5,021✔
1397
         const auto y_bits = y.serialize<std::vector<uint8_t>>();
5,021✔
1398

1399
         const auto& curve = m_table[0].curve();
5,021✔
1400
         auto accum = GenericProjectivePoint(curve);
5,021✔
1401

1402
         const size_t order_bits = curve->order_bits();
5,021✔
1403

1404
         const size_t windows = (order_bits + WindowBits - 1) / WindowBits;
5,021✔
1405

1406
         for(size_t i = 0; i != windows; ++i) {
425,792✔
1407
            auto x_i = read_window_bits<WindowBits>(std::span{x_bits}, (windows - i - 1) * WindowBits);
420,771✔
1408
            auto y_i = read_window_bits<WindowBits>(std::span{y_bits}, (windows - i - 1) * WindowBits);
420,771✔
1409

1410
            if(i > 0) {
420,771✔
1411
               accum = accum.dbl_n(WindowBits);
415,750✔
1412
            }
1413

1414
            const size_t idx = (y_i << WindowBits) + x_i;
420,771✔
1415

1416
            if(idx > 0) {
420,771✔
1417
               accum += m_table[idx - 1];
413,305✔
1418
            }
1419
         }
1420

1421
         return accum;
5,021✔
1422
      }
10,042✔
1423

1424
   private:
1425
      std::vector<GenericAffinePoint> m_table;
1426
};
1427

1428
}  // namespace
1429

1430
GenericPrimeOrderCurve::GenericPrimeOrderCurve(
47✔
1431
   const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) :
47✔
1432
      m_params(std::make_unique<GenericCurveParams>(p, a, b, base_x, base_y, order)) {}
47✔
1433

1434
void GenericPrimeOrderCurve::_precompute_base_mul() {
47✔
1435
   BOTAN_STATE_CHECK(m_basemul == nullptr);
47✔
1436
   m_basemul = std::make_unique<GenericBaseMulTable>(from_stash(generator()));
47✔
1437
}
47✔
1438

1439
size_t GenericPrimeOrderCurve::order_bits() const {
5,068✔
1440
   return _params().order_bits();
5,068✔
1441
}
1442

1443
size_t GenericPrimeOrderCurve::scalar_bytes() const {
×
1444
   return _params().order_bytes();
×
1445
}
1446

1447
size_t GenericPrimeOrderCurve::field_element_bytes() const {
60,797✔
1448
   return _params().field_bytes();
60,797✔
1449
}
1450

1451
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul_by_g(const Scalar& scalar,
5,509✔
1452
                                                                  RandomNumberGenerator& rng) const {
1453
   BOTAN_STATE_CHECK(m_basemul != nullptr);
5,509✔
1454
   return stash(m_basemul->mul(from_stash(scalar), rng));
5,509✔
1455
}
1456

1457
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::base_point_mul_x_mod_order(const Scalar& scalar,
1,222✔
1458
                                                                           RandomNumberGenerator& rng) const {
1459
   BOTAN_STATE_CHECK(m_basemul != nullptr);
1,222✔
1460
   auto pt_s = m_basemul->mul(from_stash(scalar), rng);
1,222✔
1461
   BOTAN_STATE_CHECK(!pt_s.is_identity().as_bool());
1,222✔
1462
   const auto x_bytes = to_affine_x<GenericCurve>(pt_s).serialize<secure_vector<uint8_t>>();
1,222✔
1463
   if(auto s = GenericScalar::from_wide_bytes(this, x_bytes)) {
1,222✔
1464
      return stash(*s);
1,222✔
1465
   } else {
1466
      throw Internal_Error("Failed to convert x coordinate to integer modulo scalar");
×
1467
   }
1468
}
1,222✔
1469

1470
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul(const AffinePoint& pt,
3,104✔
1471
                                                             const Scalar& scalar,
1472
                                                             RandomNumberGenerator& rng) const {
1473
   GenericWindowedMul pt_table(from_stash(pt));
3,104✔
1474
   return stash(pt_table.mul(from_stash(scalar), rng));
3,104✔
1475
}
3,104✔
1476

1477
secure_vector<uint8_t> GenericPrimeOrderCurve::mul_x_only(const AffinePoint& pt,
2,424✔
1478
                                                          const Scalar& scalar,
1479
                                                          RandomNumberGenerator& rng) const {
1480
   GenericWindowedMul pt_table(from_stash(pt));
2,424✔
1481
   auto pt_s = pt_table.mul(from_stash(scalar), rng);
2,424✔
1482
   BOTAN_STATE_CHECK(!pt_s.is_identity().as_bool());
2,424✔
1483
   return to_affine_x<GenericCurve>(pt_s).serialize<secure_vector<uint8_t>>();
2,424✔
1484
}
2,424✔
1485

1486
std::unique_ptr<const PrimeOrderCurve::PrecomputedMul2Table> GenericPrimeOrderCurve::mul2_setup_g(
1,779✔
1487
   const AffinePoint& q) const {
1488
   return std::make_unique<GenericVartimeWindowedMul2>(from_stash(generator()), from_stash(q));
3,558✔
1489
}
1490

1491
std::optional<PrimeOrderCurve::ProjectivePoint> GenericPrimeOrderCurve::mul2_vartime(const PrecomputedMul2Table& tableb,
768✔
1492
                                                                                     const Scalar& s1,
1493
                                                                                     const Scalar& s2) const {
1494
   const auto& tbl = dynamic_cast<const GenericVartimeWindowedMul2&>(tableb);
768✔
1495
   auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2));
768✔
1496
   if(pt.is_identity().as_bool()) {
768✔
1497
      return {};
×
1498
   } else {
1499
      return stash(pt);
768✔
1500
   }
1501
}
1502

1503
std::optional<PrimeOrderCurve::ProjectivePoint> GenericPrimeOrderCurve::mul_px_qy(
588✔
1504
   const AffinePoint& p, const Scalar& x, const AffinePoint& q, const Scalar& y, RandomNumberGenerator& rng) const {
1505
   const GenericWindowedMul2 table(from_stash(p), from_stash(q));
588✔
1506
   auto pt = table.mul2(from_stash(x), from_stash(y), rng);
588✔
1507
   if(pt.is_identity().as_bool()) {
588✔
1508
      return {};
84✔
1509
   } else {
1510
      return stash(pt);
504✔
1511
   }
1512
}
588✔
1513

1514
bool GenericPrimeOrderCurve::mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& tableb,
4,253✔
1515
                                                         const Scalar& v,
1516
                                                         const Scalar& s1,
1517
                                                         const Scalar& s2) const {
1518
   const auto& tbl = dynamic_cast<const GenericVartimeWindowedMul2&>(tableb);
4,253✔
1519
   auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2));
4,253✔
1520

1521
   if(!pt.is_identity().as_bool()) {
4,253✔
1522
      const auto z2 = pt.z().square();
4,240✔
1523

1524
      const auto v_bytes = from_stash(v).serialize<std::vector<uint8_t>>();
4,240✔
1525

1526
      if(auto fe_v = GenericField::deserialize(this, v_bytes)) {
4,240✔
1527
         if((*fe_v * z2 == pt.x()).as_bool()) {
4,239✔
1528
            return true;
1,852✔
1529
         }
1530

1531
         if(_params().order_is_less_than_field()) {
2,394✔
1532
            const auto n = GenericField::from_words(this, _params().order());
2,209✔
1533
            const auto neg_n = n.negate().to_words();
2,209✔
1534

1535
            const auto vw = fe_v->to_words();
2,209✔
1536
            if(bigint_ct_is_lt(vw.data(), vw.size(), neg_n.data(), neg_n.size()).as_bool()) {
2,209✔
1537
               return (((*fe_v + n) * z2) == pt.x()).as_bool();
7✔
1538
            }
1539
         }
1540
      }
1541
   }
4,240✔
1542

1543
   return false;
1544
}
1545

1546
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::generator() const {
1,826✔
1547
   return PrimeOrderCurve::AffinePoint::_create(shared_from_this(), _params().base_x(), _params().base_y());
1,826✔
1548
}
1549

1550
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_to_affine(const ProjectivePoint& pt) const {
11,853✔
1551
   auto affine = to_affine<GenericCurve>(from_stash(pt));
11,853✔
1552

1553
   const auto y2 = affine.y().square();
11,853✔
1554
   const auto x3_ax_b = GenericCurve::AffinePoint::x3_ax_b(affine.x());
11,853✔
1555
   const auto valid_point = affine.is_identity() || (y2 == x3_ax_b);
11,853✔
1556

1557
   BOTAN_ASSERT(valid_point.as_bool(), "Computed point is on the curve");
11,853✔
1558

1559
   return stash(affine);
11,853✔
1560
}
1561

1562
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::point_add(const AffinePoint& a, const AffinePoint& b) const {
1,968✔
1563
   return stash(GenericProjectivePoint::from_affine(from_stash(a)) + from_stash(b));
1,968✔
1564
}
1565

1566
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_negate(const AffinePoint& pt) const {
2,154✔
1567
   return stash(from_stash(pt).negate());
2,154✔
1568
}
1569

1570
bool GenericPrimeOrderCurve::affine_point_is_identity(const AffinePoint& pt) const {
26,142✔
1571
   return from_stash(pt).is_identity().as_bool();
26,142✔
1572
}
1573

1574
void GenericPrimeOrderCurve::serialize_point(std::span<uint8_t> bytes, const AffinePoint& pt) const {
25,878✔
1575
   from_stash(pt).serialize_to(bytes);
25,878✔
1576
}
25,878✔
1577

1578
void GenericPrimeOrderCurve::serialize_scalar(std::span<uint8_t> bytes, const Scalar& scalar) const {
46,616✔
1579
   BOTAN_ARG_CHECK(bytes.size() == _params().order_bytes(), "Invalid length to serialize_scalar");
46,616✔
1580
   from_stash(scalar).serialize_to(bytes);
46,616✔
1581
}
46,616✔
1582

1583
std::optional<PrimeOrderCurve::Scalar> GenericPrimeOrderCurve::deserialize_scalar(
12,716✔
1584
   std::span<const uint8_t> bytes) const {
1585
   if(auto s = GenericScalar::deserialize(this, bytes)) {
12,716✔
1586
      if(s->is_nonzero().as_bool()) {
12,499✔
1587
         return stash(s.value());
12,432✔
1588
      }
1589
   }
1590

1591
   return {};
284✔
1592
}
1593

1594
std::optional<PrimeOrderCurve::Scalar> GenericPrimeOrderCurve::scalar_from_wide_bytes(
8,682✔
1595
   std::span<const uint8_t> bytes) const {
1596
   if(auto s = GenericScalar::from_wide_bytes(this, bytes)) {
8,682✔
1597
      return stash(s.value());
8,682✔
1598
   } else {
1599
      return {};
×
1600
   }
1601
}
1602

1603
std::optional<PrimeOrderCurve::AffinePoint> GenericPrimeOrderCurve::deserialize_point(
8,552✔
1604
   std::span<const uint8_t> bytes) const {
1605
   if(auto pt = GenericAffinePoint::deserialize(this, bytes)) {
8,552✔
1606
      return stash(pt.value());
8,478✔
1607
   } else {
1608
      return {};
74✔
1609
   }
1610
}
1611

1612
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_add(const Scalar& a, const Scalar& b) const {
7,807✔
1613
   return stash(from_stash(a) + from_stash(b));
7,807✔
1614
}
1615

1616
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_sub(const Scalar& a, const Scalar& b) const {
6,362✔
1617
   return stash(from_stash(a) - from_stash(b));
6,362✔
1618
}
1619

1620
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_mul(const Scalar& a, const Scalar& b) const {
28,002✔
1621
   return stash(from_stash(a) * from_stash(b));
28,002✔
1622
}
1623

1624
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_square(const Scalar& s) const {
1,164✔
1625
   return stash(from_stash(s).square());
1,164✔
1626
}
1627

1628
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert(const Scalar& s) const {
5,809✔
1629
   return stash(from_stash(s).invert());
11,618✔
1630
}
1631

1632
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert_vartime(const Scalar& s) const {
5,675✔
1633
   return stash(from_stash(s).invert_vartime());
5,675✔
1634
}
1635

1636
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_negate(const Scalar& s) const {
5,308✔
1637
   return stash(from_stash(s).negate());
5,308✔
1638
}
1639

1640
bool GenericPrimeOrderCurve::scalar_is_zero(const Scalar& s) const {
13,993✔
1641
   return from_stash(s).is_zero().as_bool();
13,993✔
1642
}
1643

1644
bool GenericPrimeOrderCurve::scalar_equal(const Scalar& a, const Scalar& b) const {
×
1645
   return (from_stash(a) == from_stash(b)).as_bool();
×
1646
}
1647

1648
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_one() const {
56✔
1649
   return stash(GenericScalar::one(this));
56✔
1650
}
1651

1652
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::random_scalar(RandomNumberGenerator& rng) const {
18,651✔
1653
   return stash(GenericScalar::random(this, rng));
18,651✔
1654
}
1655

1656
PrimeOrderCurve::Scalar GenericPrimeOrderCurve::stash(const GenericScalar& s) const {
101,170✔
1657
   return Scalar::_create(shared_from_this(), s.stash_value());
101,170✔
1658
}
1659

1660
GenericScalar GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::Scalar& s) const {
190,624✔
1661
   BOTAN_ARG_CHECK(s._curve().get() == this, "Curve mismatch");
190,624✔
1662
   return GenericScalar(this, s._value());
190,624✔
1663
}
1664

1665
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::stash(const GenericAffinePoint& pt) const {
22,485✔
1666
   auto x_w = pt.x().stash_value();
22,485✔
1667
   auto y_w = pt.y().stash_value();
22,485✔
1668
   return AffinePoint::_create(shared_from_this(), x_w, y_w);
22,485✔
1669
}
1670

1671
GenericAffinePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::AffinePoint& pt) const {
68,419✔
1672
   BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch");
68,419✔
1673
   auto x = GenericField(this, pt._x());
68,419✔
1674
   auto y = GenericField(this, pt._y());
68,419✔
1675
   return GenericAffinePoint(x, y);
68,419✔
1676
}
1677

1678
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::stash(const GenericProjectivePoint& pt) const {
11,853✔
1679
   auto x_w = pt.x().stash_value();
11,853✔
1680
   auto y_w = pt.y().stash_value();
11,853✔
1681
   auto z_w = pt.z().stash_value();
11,853✔
1682
   return ProjectivePoint::_create(shared_from_this(), x_w, y_w, z_w);
11,853✔
1683
}
1684

1685
GenericProjectivePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::ProjectivePoint& pt) const {
11,853✔
1686
   BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch");
11,853✔
1687
   auto x = GenericField(this, pt._x());
11,853✔
1688
   auto y = GenericField(this, pt._y());
11,853✔
1689
   auto z = GenericField(this, pt._z());
11,853✔
1690
   return GenericProjectivePoint(x, y, z);
11,853✔
1691
}
1692

1693
PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::hash_to_curve_nu(
×
1694
   std::function<void(std::span<uint8_t>)> expand_message) const {
1695
   BOTAN_UNUSED(expand_message);
×
1696
   throw Not_Implemented("Hash to curve is not implemented for this curve");
×
1697
}
1698

1699
PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::hash_to_curve_ro(
×
1700
   std::function<void(std::span<uint8_t>)> expand_message) const {
1701
   BOTAN_UNUSED(expand_message);
×
1702
   throw Not_Implemented("Hash to curve is not implemented for this curve");
×
1703
}
1704

1705
std::shared_ptr<const PrimeOrderCurve> PCurveInstance::from_params(
57✔
1706
   const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) {
1707
   // We don't check that p and order are prime here on the assumption this has
1708
   // been checked already by EC_Group
1709

1710
   BOTAN_ARG_CHECK(a >= 0 && a < p, "a is invalid");
114✔
1711
   BOTAN_ARG_CHECK(b > 0 && b < p, "b is invalid");
114✔
1712
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "base_x is invalid");
114✔
1713
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "base_y is invalid");
114✔
1714

1715
   const size_t p_bits = p.bits();
57✔
1716

1717
   // Same size restrictions as EC_Group however here we do not require
1718
   // exactly the primes for the 521 or 239 bit exceptions; this code
1719
   // should work fine with any such prime and we are relying on the higher
1720
   // levels to prevent creating such a group in the first place
1721
   //
1722
   // TODO(Botan4) increase the 128 here to 192 when the corresponding EC_Group constructor is changed
1723
   //
1724
   if(p_bits != 521 && p_bits != 239 && (p_bits < 128 || p_bits > 512 || p_bits % 32 != 0)) {
57✔
1725
      return {};
×
1726
   }
1727

1728
   // We don't want to deal with Shanks-Tonelli in the generic case
1729
   if(p % 4 != 3) {
57✔
1730
      return {};
4✔
1731
   }
1732

1733
   // The bit length of the field and order being the same simplifies things
1734
   if(p_bits != order.bits()) {
53✔
1735
      return {};
6✔
1736
   }
1737

1738
   // Check that the (x,y) generator point is on the curve
1739
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
47✔
1740
   const BigInt y2 = mod_p.square(base_y);
47✔
1741
   const BigInt x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
47✔
1742
   if(y2 != x3_ax_b) {
47✔
1743
      return {};
×
1744
   }
1745

1746
   auto gpoc = std::make_shared<GenericPrimeOrderCurve>(p, a, b, base_x, base_y, order);
47✔
1747
   /*
1748
   The implementation of this needs to call shared_from_this which is not usable
1749
   until after the constructor has completed, so we have to do a two-stage
1750
   construction process. This is certainly not so clean but it is contained to
1751
   this single file so seems tolerable.
1752

1753
   Alternately we could lazily compute the base mul table but this brings in
1754
   locking issues which seem a worse alternative overall.
1755
   */
1756
   gpoc->_precompute_base_mul();
47✔
1757
   return gpoc;
47✔
1758
}
94✔
1759

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