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

randombit / botan / 12805544433

16 Jan 2025 09:08AM UTC coverage: 90.876% (-0.4%) from 91.245%
12805544433

Pull #4540

github

web-flow
Merge cc1ceff51 into 9b798efbb
Pull Request #4540: PKCS #11 Version 3.2 Support

93425 of 102805 relevant lines covered (90.88%)

11409241.89 hits per line

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

83.33
/src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h
1
/*
2
* (C) 2024 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#ifndef BOTAN_PCURVES_WRAP_H_
8
#define BOTAN_PCURVES_WRAP_H_
9

10
#include <botan/internal/pcurves.h>
11
#include <botan/internal/pcurves_impl.h>
12

13
namespace Botan::PCurve {
14

15
template <typename C>
16
concept curve_supports_scalar_invert = requires(const typename C::Scalar& s) {
17
   { C::scalar_invert(s) } -> std::same_as<typename C::Scalar>;
18
};
19

20
/**
21
* This class provides a bridge between the "public" (actually still
22
* internal) PrimeOrderCurve type, and the inner templates which are
23
* subclasses of EllipticCurve from pcurves_impl.h
24
*/
25
template <typename C>
26
class PrimeOrderCurveImpl final : public PrimeOrderCurve {
27
   public:
28
      class PrecomputedMul2TableC final : public PrimeOrderCurve::PrecomputedMul2Table {
×
29
         public:
30
            static constexpr size_t WindowBits = 3;
31

32
            const WindowedMul2Table<C, WindowBits>& table() const { return m_table; }
18,716✔
33

34
            explicit PrecomputedMul2TableC(const typename C::AffinePoint& x, const typename C::AffinePoint& y) :
11,169✔
35
                  m_table(x, y) {}
11,169✔
36

37
         private:
38
            WindowedMul2Table<C, WindowBits> m_table;
39
      };
40

41
      static_assert(C::OrderBits <= PrimeOrderCurve::MaximumBitLength);
42
      static_assert(C::PrimeFieldBits <= PrimeOrderCurve::MaximumBitLength);
43

44
      size_t order_bits() const override { return C::OrderBits; }
×
45

46
      size_t scalar_bytes() const override { return C::Scalar::BYTES; }
×
47

48
      size_t field_element_bytes() const override { return C::FieldElement::BYTES; }
239,265✔
49

50
      ProjectivePoint mul_by_g(const Scalar& scalar, RandomNumberGenerator& rng) const override {
7,959✔
51
         return stash(m_mul_by_g.mul(from_stash(scalar), rng));
7,959✔
52
      }
53

54
      ProjectivePoint mul(const AffinePoint& pt, const Scalar& scalar, RandomNumberGenerator& rng) const override {
10,020✔
55
         auto tbl = WindowedMulTable<C, 4>(from_stash(pt));
10,020✔
56
         return stash(tbl.mul(from_stash(scalar), rng));
10,020✔
57
      }
10,020✔
58

59
      secure_vector<uint8_t> mul_x_only(const AffinePoint& pt,
2,649✔
60
                                        const Scalar& scalar,
61
                                        RandomNumberGenerator& rng) const override {
62
         auto tbl = WindowedMulTable<C, 4>(from_stash(pt));
2,649✔
63
         auto pt_x = to_affine_x<C>(tbl.mul(from_stash(scalar), rng));
2,649✔
64
         secure_vector<uint8_t> x_bytes(C::FieldElement::BYTES);
2,649✔
65
         pt_x.serialize_to(std::span<uint8_t, C::FieldElement::BYTES>{x_bytes});
2,649✔
66
         return x_bytes;
2,649✔
67
      }
2,649✔
68

69
      std::unique_ptr<const PrecomputedMul2Table> mul2_setup(const AffinePoint& x,
11,169✔
70
                                                             const AffinePoint& y) const override {
71
         return std::make_unique<PrecomputedMul2TableC>(from_stash(x), from_stash(y));
11,169✔
72
      }
73

74
      std::optional<ProjectivePoint> mul2_vartime(const PrecomputedMul2Table& tableb,
1,100✔
75
                                                  const Scalar& s1,
76
                                                  const Scalar& s2) const override {
77
         try {
78
            const auto& table = dynamic_cast<const PrecomputedMul2TableC&>(tableb);
1,100✔
79
            auto pt = table.table().mul2_vartime(from_stash(s1), from_stash(s2));
1,100✔
80
            if(pt.is_identity().as_bool()) {
1,100✔
81
               return {};
×
82
            } else {
83
               return stash(pt);
1,100✔
84
            }
85
         } catch(std::bad_cast&) {
×
86
            throw Invalid_Argument("Curve mismatch");
×
87
         }
88
      }
89

90
      std::optional<ProjectivePoint> mul_px_qy(const AffinePoint& p,
2,604✔
91
                                               const Scalar& x,
92
                                               const AffinePoint& q,
93
                                               const Scalar& y,
94
                                               RandomNumberGenerator& rng) const override {
95
         WindowedMul2Table<C, 2> tbl(from_stash(p), from_stash(q));
2,604✔
96
         auto pt = tbl.mul2(from_stash(x), from_stash(y), rng);
2,604✔
97
         if(pt.is_identity().as_bool()) {
2,604✔
98
            return {};
84✔
99
         } else {
100
            return stash(pt);
2,520✔
101
         }
102
      }
2,604✔
103

104
      bool mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& tableb,
17,616✔
105
                                       const Scalar& v,
106
                                       const Scalar& s1,
107
                                       const Scalar& s2) const override {
108
         try {
109
            const auto& table = dynamic_cast<const PrecomputedMul2TableC&>(tableb);
17,616✔
110
            const auto pt = table.table().mul2_vartime(from_stash(s1), from_stash(s2));
17,616✔
111
            // Variable time here, so the early return is fine
112
            if(pt.is_identity().as_bool()) {
17,616✔
113
               return false;
114
            }
115

116
            /*
117
            * Avoid the inversion by instead projecting v.
118
            *
119
            * Given (x*z2) and v we want to know if x % n == v
120
            *
121
            * Inverting z2 to extract x is expensive. Instead compute (v*z2) and
122
            * compare it with (x*z2).
123
            *
124
            * With overwhelming probability, this conversion is correct. The
125
            * only time it is not is in the extremely unlikely case where the
126
            * signer actually reduced the x coordinate modulo the group order.
127
            * That is handled seperately in a second step.
128
            */
129
            const auto z2 = pt.z().square();
17,519✔
130

131
            std::array<uint8_t, C::Scalar::BYTES> v_bytes;
132
            from_stash(v).serialize_to(v_bytes);
17,519✔
133

134
            if(const auto fe_v = C::FieldElement::deserialize(v_bytes)) {
17,519✔
135
               if((*fe_v * z2 == pt.x()).as_bool()) {
17,519✔
136
                  return true;
7,723✔
137
               }
138

139
               /*
140
               * Possibly (if cryptographically unlikely) the signer
141
               * reduced the value modulo the group order.
142
               *
143
               * If so we must check v + n similarly as before. However here
144
               * we must be careful to not overflow since otherwise that
145
               * would lead to us accepting an incorrect signature.
146
               *
147
               * If the order is > p then the reduction modulo p would not have
148
               * had any effect and we don't need to consider the possibility
149
               */
150
               if constexpr(C::OrderIsLessThanField) {
151
                  /*
152
                  * We have to be careful to avoid overflow since this would
153
                  * lead to a forgery
154
                  *
155
                  * v < (p)-n => v + n < p
156
                  *
157
                  * The values n and neg_n could be precomputed but they are
158
                  * fast to compute and this codepath will ~never be taken
159
                  * unless when verifying an invalid signature. In any case
160
                  * it is many times cheaper than performing the modular inversion
161
                  * which this approach avoids.
162
                  */
163

164
                  // Create the group order as a field element, safe because n < p
165
                  const auto n = C::FieldElement::from_words(C::NW);
9,475✔
166
                  const auto neg_n = n.negate().to_words();
11,372✔
167

168
                  const auto vw = fe_v->to_words();
9,475✔
169
                  if(bigint_ct_is_lt(vw.data(), vw.size(), neg_n.data(), neg_n.size()).as_bool()) {
9,475✔
170
                     return (((*fe_v + n) * z2) == pt.x()).as_bool();
58✔
171
                  }
172
               }
173
            }
174

175
            return false;
9,796✔
176
         } catch(std::bad_cast&) {
×
177
            throw Invalid_Argument("Curve mismatch");
×
178
         }
179
      }
180

181
      Scalar base_point_mul_x_mod_order(const Scalar& scalar, RandomNumberGenerator& rng) const override {
1,685✔
182
         auto pt = m_mul_by_g.mul(from_stash(scalar), rng);
1,685✔
183
         std::array<uint8_t, C::FieldElement::BYTES> x_bytes;
184
         to_affine_x<C>(pt).serialize_to(std::span{x_bytes});
1,685✔
185
         // Reduction might be required (if unlikely)
186
         return stash(C::Scalar::from_wide_bytes(std::span<const uint8_t, C::FieldElement::BYTES>{x_bytes}));
1,685✔
187
      }
1,685✔
188

189
      AffinePoint generator() const override { return stash(C::G); }
11,169✔
190

191
      AffinePoint point_to_affine(const ProjectivePoint& pt) const override {
26,667✔
192
         return stash(to_affine<C>(from_stash(pt)));
26,667✔
193
      }
194

195
      ProjectivePoint point_to_projective(const AffinePoint& pt) const override {
5,045✔
196
         return stash(C::ProjectivePoint::from_affine(from_stash(pt)));
5,045✔
197
      }
198

199
      ProjectivePoint point_double(const ProjectivePoint& pt) const override { return stash(from_stash(pt).dbl()); }
×
200

201
      ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) const override {
×
202
         return stash(from_stash(a) + from_stash(b));
×
203
      }
204

205
      ProjectivePoint point_add_mixed(const ProjectivePoint& a, const AffinePoint& b) const override {
5,045✔
206
         return stash(from_stash(a) + from_stash(b));
10,089✔
207
      }
208

209
      AffinePoint point_negate(const AffinePoint& pt) const override { return stash(from_stash(pt).negate()); }
10,075✔
210

211
      bool affine_point_is_identity(const AffinePoint& pt) const override {
81,487✔
212
         return from_stash(pt).is_identity().as_bool();
81,487✔
213
      }
214

215
      void serialize_point(std::span<uint8_t> bytes, const AffinePoint& pt) const override {
81,271✔
216
         BOTAN_ARG_CHECK(bytes.size() == C::AffinePoint::BYTES, "Invalid length for serialize_point");
81,271✔
217
         from_stash(pt).serialize_to(bytes.subspan<0, C::AffinePoint::BYTES>());
81,271✔
218
      }
81,271✔
219

220
      void serialize_point_compressed(std::span<uint8_t> bytes, const AffinePoint& pt) const override {
×
221
         BOTAN_ARG_CHECK(bytes.size() == C::AffinePoint::COMPRESSED_BYTES,
×
222
                         "Invalid length for serialize_point_compressed");
223
         from_stash(pt).serialize_compressed_to(bytes.subspan<0, C::AffinePoint::COMPRESSED_BYTES>());
×
224
      }
×
225

226
      void serialize_point_x(std::span<uint8_t> bytes, const AffinePoint& pt) const override {
×
227
         BOTAN_ARG_CHECK(bytes.size() == C::FieldElement::BYTES, "Invalid length for serialize_point_x");
×
228
         from_stash(pt).serialize_x_to(bytes.subspan<0, C::FieldElement::BYTES>());
×
229
      }
×
230

231
      void serialize_scalar(std::span<uint8_t> bytes, const Scalar& scalar) const override {
46,885✔
232
         BOTAN_ARG_CHECK(bytes.size() == C::Scalar::BYTES, "Invalid length to serialize_scalar");
46,885✔
233
         return from_stash(scalar).serialize_to(bytes.subspan<0, C::Scalar::BYTES>());
46,885✔
234
      }
235

236
      std::optional<Scalar> deserialize_scalar(std::span<const uint8_t> bytes) const override {
45,785✔
237
         if(auto scalar = C::Scalar::deserialize(bytes)) {
45,785✔
238
            if(!scalar->is_zero().as_bool()) {
44,596✔
239
               return stash(*scalar);
43,557✔
240
            }
241
         }
242

243
         return {};
2,228✔
244
      }
245

246
      std::optional<Scalar> scalar_from_wide_bytes(std::span<const uint8_t> bytes) const override {
23,400✔
247
         if(auto s = C::Scalar::from_wide_bytes_varlen(bytes)) {
23,400✔
248
            return stash(*s);
23,400✔
249
         } else {
250
            return {};
×
251
         }
252
      }
253

254
      std::optional<AffinePoint> deserialize_point(std::span<const uint8_t> bytes) const override {
31,886✔
255
         if(auto pt = C::AffinePoint::deserialize(bytes)) {
31,886✔
256
            return stash(*pt);
25,769✔
257
         } else {
258
            return {};
6,117✔
259
         }
260
      }
261

262
      AffinePoint hash_to_curve_nu(std::string_view hash,
33✔
263
                                   std::span<const uint8_t> input,
264
                                   std::span<const uint8_t> domain_sep) const override {
265
         if constexpr(C::ValidForSswuHash) {
266
            return stash(hash_to_curve_sswu<C, false>(hash, input, domain_sep));
30✔
267
         } else {
268
            throw Not_Implemented("Hash to curve is not implemented for this curve");
3✔
269
         }
270
      }
271

272
      ProjectivePoint hash_to_curve_ro(std::string_view hash,
24✔
273
                                       std::span<const uint8_t> input,
274
                                       std::span<const uint8_t> domain_sep) const override {
275
         if constexpr(C::ValidForSswuHash) {
276
            return stash(hash_to_curve_sswu<C, true>(hash, input, domain_sep));
24✔
277
         } else {
278
            throw Not_Implemented("Hash to curve is not implemented for this curve");
×
279
         }
280
      }
281

282
      Scalar scalar_add(const Scalar& a, const Scalar& b) const override {
8,467✔
283
         return stash(from_stash(a) + from_stash(b));
8,467✔
284
      }
285

286
      Scalar scalar_sub(const Scalar& a, const Scalar& b) const override {
6,484✔
287
         return stash(from_stash(a) - from_stash(b));
6,484✔
288
      }
289

290
      Scalar scalar_mul(const Scalar& a, const Scalar& b) const override {
60,235✔
291
         return stash(from_stash(a) * from_stash(b));
60,235✔
292
      }
293

294
      Scalar scalar_square(const Scalar& s) const override { return stash(from_stash(s).square()); }
3,184✔
295

296
      Scalar scalar_invert(const Scalar& ss) const override {
24,338✔
297
         auto s = from_stash(ss);
24,338✔
298
         if constexpr(curve_supports_scalar_invert<C>) {
299
            return stash(C::scalar_invert(s));
16,040✔
300
         } else {
301
            return stash(s.invert());
8,298✔
302
         }
303
      }
304

305
      Scalar scalar_negate(const Scalar& s) const override { return stash(from_stash(s).negate()); }
7,426✔
306

307
      bool scalar_is_zero(const Scalar& s) const override { return from_stash(s).is_zero().as_bool(); }
39,668✔
308

309
      bool scalar_equal(const Scalar& a, const Scalar& b) const override {
×
310
         return (from_stash(a) == from_stash(b)).as_bool();
×
311
      }
312

313
      Scalar scalar_zero() const override { return stash(C::Scalar::zero()); }
×
314

315
      Scalar scalar_one() const override { return stash(C::Scalar::one()); }
61✔
316

317
      Scalar random_scalar(RandomNumberGenerator& rng) const override { return stash(C::Scalar::random(rng)); }
19,426✔
318

319
      PrimeOrderCurveImpl() : m_mul_by_g(C::G) {}
491✔
320

321
      static std::shared_ptr<const PrimeOrderCurve> instance() {
906,263✔
322
         static auto g_curve = std::make_shared<const PrimeOrderCurveImpl<C>>();
906,754✔
323
         return g_curve;
906,263✔
324
      }
325

326
   private:
327
      static Scalar stash(const typename C::Scalar& s) {
198,263✔
328
         return Scalar::_create(instance(), s.template stash_value<StorageWords>());
198,263✔
329
      }
330

331
      static typename C::Scalar from_stash(const Scalar& s) {
354,345✔
332
         if(s._curve() != instance()) {
708,690✔
333
            throw Invalid_Argument("Curve mismatch");
×
334
         }
335
         return C::Scalar::from_stash(s._value());
642,098✔
336
      }
337

338
      static AffinePoint stash(const typename C::AffinePoint& pt) {
69,947✔
339
         auto x_w = pt.x().template stash_value<StorageWords>();
139,894✔
340
         auto y_w = pt.y().template stash_value<StorageWords>();
69,947✔
341
         return AffinePoint::_create(instance(), x_w, y_w);
69,947✔
342
      }
343

344
      static typename C::AffinePoint from_stash(const AffinePoint& pt) {
219,375✔
345
         if(pt._curve() != instance()) {
438,750✔
346
            throw Invalid_Argument("Curve mismatch");
1✔
347
         }
348
         auto x = C::FieldElement::from_stash(pt._x());
399,269✔
349
         auto y = C::FieldElement::from_stash(pt._y());
399,269✔
350
         return typename C::AffinePoint(x, y);
219,374✔
351
      }
352

353
      static ProjectivePoint stash(const typename C::ProjectivePoint& pt) {
31,712✔
354
         auto x_w = pt.x().template stash_value<StorageWords>();
63,424✔
355
         auto y_w = pt.y().template stash_value<StorageWords>();
63,424✔
356
         auto z_w = pt.z().template stash_value<StorageWords>();
31,712✔
357
         return ProjectivePoint::_create(instance(), x_w, y_w, z_w);
31,712✔
358
      }
359

360
      static typename C::ProjectivePoint from_stash(const ProjectivePoint& pt) {
31,711✔
361
         if(pt._curve() != instance()) {
63,422✔
362
            throw Invalid_Argument("Curve mismatch");
×
363
         }
364
         auto x = C::FieldElement::from_stash(pt._x());
56,527✔
365
         auto y = C::FieldElement::from_stash(pt._y());
56,527✔
366
         auto z = C::FieldElement::from_stash(pt._z());
56,527✔
367
         return typename C::ProjectivePoint(x, y, z);
31,711✔
368
      }
369

370
   private:
371
      const PrecomputedBaseMulTable<C, 5> m_mul_by_g;
372
};
373

374
}  // namespace Botan::PCurve
375

376
#endif
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

© 2025 Coveralls, Inc