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

randombit / botan / 13191848185

07 Feb 2025 02:17AM UTC coverage: 91.304% (+0.03%) from 91.275%
13191848185

Pull #4554

github

web-flow
Merge 1cbe013a3 into d4d48503e
Pull Request #4554: Add generic pcurves for application specific curves

95352 of 104433 relevant lines covered (91.3%)

10947198.13 hits per line

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

96.83
/src/lib/pubkey/ec_group/ec_inner_data.cpp
1
/*
2
* (C) 2024 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

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

9
#include <botan/der_enc.h>
10
#include <botan/internal/ec_inner_pc.h>
11
#include <botan/internal/fmt.h>
12
#include <botan/internal/pcurves.h>
13

14
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
15
   #include <botan/internal/ec_inner_bn.h>
16
   #include <botan/internal/point_mul.h>
17
#endif
18

19
namespace Botan {
20

21
EC_Group_Data::~EC_Group_Data() = default;
13,939✔
22

23
// Note this constructor *does not* initialize m_curve, m_base_point or m_base_mult
24
EC_Group_Data::EC_Group_Data(const BigInt& p,
1,162✔
25
                             const BigInt& a,
26
                             const BigInt& b,
27
                             const BigInt& g_x,
28
                             const BigInt& g_y,
29
                             const BigInt& order,
30
                             const BigInt& cofactor,
31
                             const OID& oid,
32
                             EC_Group_Source source) :
1,162✔
33
      m_p(p),
1,162✔
34
      m_a(a),
1,162✔
35
      m_b(b),
1,162✔
36
      m_g_x(g_x),
1,162✔
37
      m_g_y(g_y),
1,162✔
38
      m_order(order),
1,162✔
39
      m_cofactor(cofactor),
1,162✔
40
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
41
      m_mod_field(Modular_Reducer::for_public_modulus(p)),
1,162✔
42
      m_mod_order(Modular_Reducer::for_public_modulus(order)),
1,162✔
43
      m_monty(m_p, m_mod_field),
1,162✔
44
#endif
45
      m_oid(oid),
1,162✔
46
      m_p_words(p.sig_words()),
1,162✔
47
      m_p_bits(p.bits()),
1,162✔
48
      m_order_bits(order.bits()),
1,162✔
49
      m_order_bytes((m_order_bits + 7) / 8),
1,162✔
50
      m_a_is_minus_3(a == p - 3),
2,324✔
51
      m_a_is_zero(a.is_zero()),
1,162✔
52
      m_has_cofactor(m_cofactor != 1),
1,162✔
53
      m_order_is_less_than_p(m_order < p),
1,162✔
54
      m_source(source) {
3,486✔
55
   // TODO(Botan4) we can assume/assert the OID is set
56
   if(!m_oid.empty()) {
1,162✔
57
      DER_Encoder der(m_der_named_curve);
1,152✔
58
      der.encode(m_oid);
1,152✔
59

60
      if(const auto id = PCurve::PrimeOrderCurveId::from_oid(m_oid)) {
1,152✔
61
         m_pcurve = PCurve::PrimeOrderCurve::from_id(*id);
913✔
62
         if(m_pcurve) {
913✔
63
            m_engine = EC_Group_Engine::Optimized;
913✔
64
         }
65
         // still possibly null, if the curve is supported in general but not
66
         // available in the build
67
      }
68
   }
1,152✔
69

70
   // Try a generic pcurves instance
71
   if(!m_pcurve && !m_has_cofactor) {
1,162✔
72
      m_pcurve = PCurve::PrimeOrderCurve::from_params(p, a, b, g_x, g_y, order);
248✔
73
      if(m_pcurve) {
248✔
74
         m_engine = EC_Group_Engine::Generic;
147✔
75
      }
76
      // possibly still null here, if parameters unsuitable or if the
77
      // pcurves_generic module wasn't included in the build
78
   }
79

80
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
81
   secure_vector<word> ws;
1,162✔
82
   m_a_r = m_monty.mul(a, m_monty.R2(), ws);
2,324✔
83
   m_b_r = m_monty.mul(b, m_monty.R2(), ws);
2,324✔
84
   if(!m_pcurve) {
1,162✔
85
      m_engine = EC_Group_Engine::Legacy;
102✔
86
   }
87
#else
88
   if(!m_pcurve) {
89
      if(m_oid.empty()) {
90
         throw Not_Implemented("EC_Group this group is not supported in this build configuration");
91
      } else {
92
         throw Not_Implemented(
93
            fmt("EC_Group the group {} is not supported in this build configuration", oid.to_string()));
94
      }
95
   }
96
#endif
97
}
1,162✔
98

99
std::shared_ptr<EC_Group_Data> EC_Group_Data::create(const BigInt& p,
1,162✔
100
                                                     const BigInt& a,
101
                                                     const BigInt& b,
102
                                                     const BigInt& g_x,
103
                                                     const BigInt& g_y,
104
                                                     const BigInt& order,
105
                                                     const BigInt& cofactor,
106
                                                     const OID& oid,
107
                                                     EC_Group_Source source) {
108
   auto group = std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, source);
1,162✔
109

110
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
111
   group->m_curve = CurveGFp(group.get());
1,162✔
112
   group->m_base_point = EC_Point(group->m_curve, g_x, g_y);
2,324✔
113
   if(!group->m_pcurve) {
1,162✔
114
      group->m_base_mult = std::make_unique<EC_Point_Base_Point_Precompute>(group->m_base_point, group->m_mod_order);
102✔
115
   }
116
#endif
117

118
   return group;
1,162✔
119
}
×
120

121
bool EC_Group_Data::params_match(const BigInt& p,
853✔
122
                                 const BigInt& a,
123
                                 const BigInt& b,
124
                                 const BigInt& g_x,
125
                                 const BigInt& g_y,
126
                                 const BigInt& order,
127
                                 const BigInt& cofactor) const {
128
   return (this->p() == p && this->a() == a && this->b() == b && this->order() == order &&
945✔
129
           this->cofactor() == cofactor && this->g_x() == g_x && this->g_y() == g_y);
936✔
130
}
131

132
bool EC_Group_Data::params_match(const EC_Group_Data& other) const {
19✔
133
   return params_match(other.p(), other.a(), other.b(), other.g_x(), other.g_y(), other.order(), other.cofactor());
19✔
134
}
135

136
void EC_Group_Data::set_oid(const OID& oid) {
5✔
137
   BOTAN_ARG_CHECK(!oid.empty(), "OID should be set");
5✔
138
   BOTAN_STATE_CHECK(m_oid.empty() && m_der_named_curve.empty());
5✔
139
   m_oid = oid;
5✔
140

141
   DER_Encoder der(m_der_named_curve);
5✔
142
   der.encode(m_oid);
5✔
143
}
5✔
144

145
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bytes_with_trunc(std::span<const uint8_t> bytes) const {
26,062✔
146
   const size_t bit_length = 8 * bytes.size();
26,062✔
147

148
   if(bit_length < order_bits()) {
26,062✔
149
      // No shifting required, but might still need to reduce by modulus
150
      return this->scalar_from_bytes_mod_order(bytes);
5,246✔
151
   } else {
152
      const size_t shift = bit_length - order_bits();
20,816✔
153

154
      const size_t new_length = bytes.size() - (shift / 8);
20,816✔
155
      const size_t bit_shift = shift % 8;
20,816✔
156

157
      if(bit_shift == 0) {
20,816✔
158
         // Easy case just read different bytes
159
         return this->scalar_from_bytes_mod_order(bytes.first(new_length));
18,166✔
160
      } else {
161
         std::vector<uint8_t> sbytes(new_length);
2,650✔
162

163
         uint8_t carry = 0;
2,650✔
164
         for(size_t i = 0; i != new_length; ++i) {
72,445✔
165
            const uint8_t w = bytes[i];
69,795✔
166
            sbytes[i] = (w >> bit_shift) | carry;
69,795✔
167
            carry = w << (8 - bit_shift);
69,795✔
168
         }
169

170
         return this->scalar_from_bytes_mod_order(sbytes);
2,650✔
171
      }
2,650✔
172
   }
173
}
174

175
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bytes_mod_order(std::span<const uint8_t> bytes) const {
34,778✔
176
   if(bytes.size() > 2 * order_bytes()) {
34,778✔
177
      return {};
×
178
   }
179

180
   if(m_pcurve) {
34,778✔
181
      if(auto s = m_pcurve->scalar_from_wide_bytes(bytes)) {
31,957✔
182
         return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), std::move(*s));
31,957✔
183
      } else {
184
         return {};
×
185
      }
31,957✔
186
   } else {
187
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
188
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), m_mod_order.reduce(BigInt(bytes)));
5,642✔
189
#else
190
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
191
#endif
192
   }
193
}
194

195
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_random(RandomNumberGenerator& rng) const {
44,404✔
196
   if(m_pcurve) {
44,404✔
197
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), m_pcurve->random_scalar(rng));
38,212✔
198
   } else {
199
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
200
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(),
6,192✔
201
                                                 BigInt::random_integer(rng, BigInt::one(), m_order));
18,576✔
202
#else
203
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
204
#endif
205
   }
206
}
207

208
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_one() const {
101✔
209
   if(m_pcurve) {
101✔
210
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), m_pcurve->scalar_one());
93✔
211
   } else {
212
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
213
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), BigInt::one());
8✔
214
#else
215
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
216
#endif
217
   }
218
}
219

220
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bigint(const BigInt& bn) const {
4,485✔
221
   if(bn <= 0 || bn >= m_order) {
4,485✔
222
      return {};
×
223
   }
224

225
   if(m_pcurve) {
4,485✔
226
      return this->scalar_deserialize(bn.serialize(m_order_bytes));
8,142✔
227
   } else {
228
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
229
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), bn);
414✔
230
#else
231
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
232
#endif
233
   }
234
}
235

236
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::gk_x_mod_order(const EC_Scalar_Data& scalar,
3,314✔
237
                                                              RandomNumberGenerator& rng,
238
                                                              std::vector<BigInt>& ws) const {
239
   if(m_pcurve) {
3,314✔
240
      const auto& k = EC_Scalar_Data_PC::checked_ref(scalar);
2,914✔
241
      auto gk_x_mod_order = m_pcurve->base_point_mul_x_mod_order(k.value(), rng);
2,914✔
242
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), gk_x_mod_order);
2,914✔
243
   } else {
2,914✔
244
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
245
      const auto& k = EC_Scalar_Data_BN::checked_ref(scalar);
400✔
246
      BOTAN_STATE_CHECK(m_base_mult != nullptr);
400✔
247
      const auto pt = m_base_mult->mul(k.value(), rng, m_order, ws);
400✔
248

249
      if(pt.is_zero()) {
800✔
250
         return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), BigInt::zero());
×
251
      } else {
252
         return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), m_mod_order.reduce(pt.get_affine_x()));
800✔
253
      }
254
#else
255
      BOTAN_UNUSED(ws);
256
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
257
#endif
258
   }
400✔
259
}
260

261
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_deserialize(std::span<const uint8_t> bytes) const {
69,349✔
262
   if(bytes.size() != m_order_bytes) {
69,349✔
263
      return nullptr;
5,808✔
264
   }
265

266
   if(m_pcurve) {
63,541✔
267
      if(auto s = m_pcurve->deserialize_scalar(bytes)) {
59,196✔
268
         return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), *s);
56,764✔
269
      } else {
270
         return nullptr;
2,432✔
271
      }
59,196✔
272
   } else {
273
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
274
      BigInt r(bytes);
4,345✔
275

276
      if(r.is_zero() || r >= m_order) {
8,690✔
277
         return nullptr;
465✔
278
      }
279

280
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), std::move(r));
3,880✔
281
#else
282
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
283
#endif
284
   }
4,345✔
285
}
286

287
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_deserialize(std::span<const uint8_t> bytes) const {
44,561✔
288
   // The deprecated "hybrid" point format
289
   // TODO(Botan4) remove this
290
   if(bytes.size() >= 1 + 2 * 4 && (bytes[0] == 0x06 || bytes[0] == 0x07)) {
44,561✔
291
      bool hdr_y_is_even = bytes[0] == 0x06;
207✔
292
      bool y_is_even = (bytes.back() & 0x01) == 0;
207✔
293

294
      if(hdr_y_is_even == y_is_even) {
207✔
295
         std::vector<uint8_t> sec1(bytes.begin(), bytes.end());
147✔
296
         sec1[0] = 0x04;
147✔
297
         return this->point_deserialize(sec1);
147✔
298
      }
147✔
299
   }
300

301
   try {
44,414✔
302
      if(m_pcurve) {
44,414✔
303
         if(auto pt = m_pcurve->deserialize_point(bytes)) {
41,651✔
304
            return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(*pt));
35,333✔
305
         } else {
306
            return {};
6,318✔
307
         }
41,651✔
308
      } else {
309
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
310
         auto pt = Botan::OS2ECP(bytes, m_curve);
2,763✔
311
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
2,707✔
312
#else
313
         throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
314
#endif
315
      }
2,707✔
316
   } catch(...) {
56✔
317
      return {};
56✔
318
   }
56✔
319
}
320

321
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_hash_to_curve_ro(std::string_view hash_fn,
24✔
322
                                                                           std::span<const uint8_t> input,
323
                                                                           std::span<const uint8_t> domain_sep) const {
324
   if(m_pcurve) {
24✔
325
      auto pt = m_pcurve->hash_to_curve_ro(hash_fn, input, domain_sep);
24✔
326
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(pt));
24✔
327
   } else {
24✔
328
      throw Not_Implemented("Hash to curve is not implemented for this curve");
×
329
   }
330
}
331

332
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_hash_to_curve_nu(std::string_view hash_fn,
33✔
333
                                                                           std::span<const uint8_t> input,
334
                                                                           std::span<const uint8_t> domain_sep) const {
335
   if(m_pcurve) {
33✔
336
      auto pt = m_pcurve->hash_to_curve_nu(hash_fn, input, domain_sep);
33✔
337
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(pt));
30✔
338
   } else {
30✔
339
      throw Not_Implemented("Hash to curve is not implemented for this curve");
×
340
   }
341
}
342

343
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_g_mul(const EC_Scalar_Data& scalar,
15,375✔
344
                                                                RandomNumberGenerator& rng,
345
                                                                std::vector<BigInt>& ws) const {
346
   if(m_pcurve) {
15,375✔
347
      const auto& k = EC_Scalar_Data_PC::checked_ref(scalar);
13,501✔
348
      auto pt = m_pcurve->point_to_affine(m_pcurve->mul_by_g(k.value(), rng));
13,501✔
349
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(pt));
13,501✔
350
   } else {
13,501✔
351
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
352
      const auto& group = scalar.group();
1,874✔
353
      const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar);
1,874✔
354

355
      BOTAN_STATE_CHECK(group->m_base_mult != nullptr);
1,874✔
356
      auto pt = group->m_base_mult->mul(bn.value(), rng, m_order, ws);
1,874✔
357
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
1,874✔
358
#else
359
      BOTAN_UNUSED(ws);
360
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
361
#endif
362
   }
1,874✔
363
}
364

365
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::mul_px_qy(const EC_AffinePoint_Data& p,
3,472✔
366
                                                              const EC_Scalar_Data& x,
367
                                                              const EC_AffinePoint_Data& q,
368
                                                              const EC_Scalar_Data& y,
369
                                                              RandomNumberGenerator& rng) const {
370
   if(m_pcurve) {
3,472✔
371
      auto pt = m_pcurve->mul_px_qy(EC_AffinePoint_Data_PC::checked_ref(p).value(),
6,384✔
372
                                    EC_Scalar_Data_PC::checked_ref(x).value(),
3,192✔
373
                                    EC_AffinePoint_Data_PC::checked_ref(q).value(),
3,192✔
374
                                    EC_Scalar_Data_PC::checked_ref(y).value(),
3,192✔
375
                                    rng);
3,192✔
376

377
      if(pt) {
3,192✔
378
         return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(*pt));
3,024✔
379
      } else {
380
         return nullptr;
168✔
381
      }
382
   } else {
3,192✔
383
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
384
      std::vector<BigInt> ws;
280✔
385
      const auto& group = p.group();
280✔
386

387
      // TODO this could be better!
388
      EC_Point_Var_Point_Precompute p_mul(p.to_legacy_point(), rng, ws);
280✔
389
      EC_Point_Var_Point_Precompute q_mul(q.to_legacy_point(), rng, ws);
280✔
390

391
      const auto order = group->order() * group->cofactor();  // See #3800
280✔
392

393
      auto px = p_mul.mul(EC_Scalar_Data_BN::checked_ref(x).value(), rng, order, ws);
280✔
394
      auto qy = q_mul.mul(EC_Scalar_Data_BN::checked_ref(y).value(), rng, order, ws);
280✔
395

396
      auto px_qy = px + qy;
280✔
397

398
      if(!px_qy.is_zero()) {
532✔
399
         px_qy.force_affine();
252✔
400
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(px_qy));
252✔
401
      } else {
402
         return nullptr;
28✔
403
      }
404
#else
405
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
406
#endif
407
   }
1,120✔
408
}
409

410
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_add(const EC_AffinePoint_Data& p,
7,753✔
411
                                                               const EC_AffinePoint_Data& q) const {
412
   if(m_pcurve) {
7,753✔
413
      auto pt = m_pcurve->point_add(EC_AffinePoint_Data_PC::checked_ref(p).value(),
7,013✔
414
                                    EC_AffinePoint_Data_PC::checked_ref(q).value());
7,013✔
415

416
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(pt));
7,012✔
417
   } else {
7,012✔
418
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
419
      auto pt = p.to_legacy_point() + q.to_legacy_point();
740✔
420
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
740✔
421
#else
422
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
423
#endif
424
   }
740✔
425
}
426

427
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_neg(const EC_AffinePoint_Data& p) const {
9,512✔
428
   if(m_pcurve) {
9,512✔
429
      auto pt = m_pcurve->point_negate(EC_AffinePoint_Data_PC::checked_ref(p).value());
8,454✔
430
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), pt);
8,454✔
431
   } else {
8,454✔
432
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
433
      auto pt = p.to_legacy_point();
1,058✔
434
      pt.negate();  // negates in place
1,058✔
435
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
1,058✔
436
#else
437
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
438
#endif
439
   }
1,058✔
440
}
441

442
std::unique_ptr<EC_Mul2Table_Data> EC_Group_Data::make_mul2_table(const EC_AffinePoint_Data& h) const {
13,256✔
443
   if(m_pcurve) {
13,256✔
444
      return std::make_unique<EC_Mul2Table_Data_PC>(h);
12,955✔
445
   } else {
446
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
447
      EC_AffinePoint_Data_BN g(shared_from_this(), this->base_point());
602✔
448
      return std::make_unique<EC_Mul2Table_Data_BN>(g, h);
301✔
449
#else
450
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
451
#endif
452
   }
301✔
453
}
454

455
}  // namespace Botan
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