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

randombit / botan / 30143835648

25 Jul 2026 02:00AM UTC coverage: 89.429% (+0.01%) from 89.419%
30143835648

push

github

web-flow
Merge pull request #5758 from randombit/jack/hash2curve-query

Add EC_Group::hash_to_curve_supported and enforce RFC 9380 hash requirements

114789 of 128357 relevant lines covered (89.43%)

10704638.98 hits per line

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

91.41
/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/barrett.h>
11
#include <botan/internal/ec_inner_pc.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/pcurves.h>
14
#include <algorithm>
15

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

21
#if defined(BOTAN_HAS_XMD)
22
   #include <botan/hash.h>
23
   #include <botan/internal/xmd.h>
24
#endif
25

26
namespace Botan {
27

28
EC_Group_Data::~EC_Group_Data() = default;
4,091✔
29

30
// Note this constructor *does not* initialize m_curve, m_base_point or m_base_mult
31
EC_Group_Data::EC_Group_Data(const BigInt& p,
683✔
32
                             const BigInt& a,
33
                             const BigInt& b,
34
                             const BigInt& g_x,
35
                             const BigInt& g_y,
36
                             const BigInt& order,
37
                             const BigInt& cofactor,
38
                             const OID& oid,
39
                             EC_Group_Source source) :
683✔
40
      m_p(p),
683✔
41
      m_a(a),
683✔
42
      m_b(b),
683✔
43
      m_g_x(g_x),
683✔
44
      m_g_y(g_y),
683✔
45
      m_order(order),
683✔
46
      m_cofactor(cofactor),
683✔
47
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
48
      m_mod_field(Barrett_Reduction::for_public_modulus(p)),
683✔
49
      m_mod_order(Barrett_Reduction::for_public_modulus(order)),
683✔
50
      m_monty(m_p, m_mod_field),
683✔
51
#endif
52
      m_oid(oid),
683✔
53
      m_p_words(p.sig_words()),
683✔
54
      m_p_bits(p.bits()),
683✔
55
      m_order_bits(order.bits()),
683✔
56
      m_order_bytes((m_order_bits + 7) / 8),
683✔
57
      m_a_is_minus_3(a == p - 3),
683✔
58
      m_a_is_zero(a.is_zero()),
683✔
59
      m_has_cofactor(m_cofactor != 1),
683✔
60
      m_order_is_less_than_p(m_order < p),
683✔
61
      m_source(source) {
2,049✔
62
   // Verify the generator (x, y) satisfies y^2 = x^3 + a*x + b (mod p)
63
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
683✔
64
   const BigInt y2 = mod_p.square(g_y);
683✔
65
   const BigInt x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
683✔
66
   if(y2 != x3_ax_b) {
683✔
67
      throw Invalid_Argument("EC_Group generator is not on the curve");
×
68
   }
69

70
   // TODO(Botan4) we can assume/assert the OID is set
71
   if(!m_oid.empty()) {
683✔
72
      DER_Encoder der(m_der_named_curve);
676✔
73
      der.encode(m_oid);
676✔
74

75
      if(const auto name = m_oid.registered_name()) {
676✔
76
         // returns nullptr if unknown or not supported
77
         m_pcurve = PCurve::PrimeOrderCurve::for_named_curve(*name);
671✔
78
      }
×
79
      if(m_pcurve) {
676✔
80
         m_engine = EC_Group_Engine::Optimized;
586✔
81
      }
82
   }
676✔
83

84
   // Try a generic pcurves instance
85
   if(!m_pcurve && !m_has_cofactor) {
683✔
86
      m_pcurve = PCurve::PrimeOrderCurve::from_params(p, a, b, g_x, g_y, order);
96✔
87
      if(m_pcurve) {
96✔
88
         m_engine = EC_Group_Engine::Generic;
86✔
89
      }
90
      // possibly still null here, if parameters unsuitable or if the
91
      // pcurves_generic module wasn't included in the build
92
   }
93

94
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
95
   secure_vector<word> ws;
683✔
96
   m_a_r = m_monty.mul(a, m_monty.R2(), ws);
1,366✔
97
   m_b_r = m_monty.mul(b, m_monty.R2(), ws);
1,366✔
98
   if(!m_pcurve) {
683✔
99
      m_engine = EC_Group_Engine::Legacy;
11✔
100
   }
101
#else
102
   if(!m_pcurve) {
103
      if(m_oid.empty()) {
104
         throw Not_Implemented("EC_Group this group is not supported in this build configuration");
105
      } else {
106
         throw Not_Implemented(
107
            fmt("EC_Group the group {} is not supported in this build configuration", oid.to_string()));
108
      }
109
   }
110
#endif
111
}
1,366✔
112

113
std::shared_ptr<EC_Group_Data> EC_Group_Data::create(const BigInt& p,
683✔
114
                                                     const BigInt& a,
115
                                                     const BigInt& b,
116
                                                     const BigInt& g_x,
117
                                                     const BigInt& g_y,
118
                                                     const BigInt& order,
119
                                                     const BigInt& cofactor,
120
                                                     const OID& oid,
121
                                                     EC_Group_Source source) {
122
   auto group = std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, source);
683✔
123

124
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
125
   group->m_curve = CurveGFp(group.get());
683✔
126
   group->m_base_point = EC_Point(group->m_curve, g_x, g_y);
1,366✔
127
   if(!group->m_pcurve) {
683✔
128
      group->m_base_mult = std::make_unique<EC_Point_Base_Point_Precompute>(group->m_base_point, group->m_mod_order);
11✔
129
   }
130
#endif
131

132
   return group;
683✔
133
}
×
134

135
bool EC_Group_Data::params_match(const BigInt& p,
78✔
136
                                 const BigInt& a,
137
                                 const BigInt& b,
138
                                 const BigInt& g_x,
139
                                 const BigInt& g_y,
140
                                 const BigInt& order,
141
                                 const BigInt& cofactor) const {
142
   if(p != this->p()) {
78✔
143
      return false;
144
   }
145
   if(a != this->a()) {
34✔
146
      return false;
147
   }
148
   if(b != this->b()) {
34✔
149
      return false;
150
   }
151
   if(order != this->order()) {
34✔
152
      return false;
153
   }
154
   if(cofactor != this->cofactor()) {
34✔
155
      return false;
156
   }
157
   if(g_x != this->g_x()) {
34✔
158
      return false;
159
   }
160
   if(g_y != this->g_y()) {
34✔
161
      return false;
162
   }
163

164
   return true;
165
}
166

167
bool EC_Group_Data::params_match(const BigInt& p,
941✔
168
                                 const BigInt& a,
169
                                 const BigInt& b,
170
                                 std::span<const uint8_t> base_pt,
171
                                 const BigInt& order,
172
                                 const BigInt& cofactor) const {
173
   if(p != this->p()) {
941✔
174
      return false;
175
   }
176
   if(a != this->a()) {
76✔
177
      return false;
178
   }
179
   if(b != this->b()) {
71✔
180
      return false;
181
   }
182
   if(order != this->order()) {
64✔
183
      return false;
184
   }
185
   if(cofactor != this->cofactor()) {
63✔
186
      return false;
187
   }
188

189
   const size_t field_len = this->p_bytes();
63✔
190

191
   if(base_pt.size() == 1 + field_len && (base_pt[0] == 0x02 || base_pt[0] == 0x03)) {
63✔
192
      // compressed
193

194
      const auto g_x = m_g_x.serialize(field_len);
×
195
      const auto g_y = m_g_y.is_odd();
×
196

197
      const auto sec1_x = base_pt.subspan(1, field_len);
×
198
      const bool sec1_y = (base_pt[0] == 0x03);
×
199

200
      if(!std::ranges::equal(sec1_x, g_x)) {
×
201
         return false;
202
      }
203

204
      if(sec1_y != g_y) {
×
205
         return false;
206
      }
207

208
      return true;
×
209
   } else if(base_pt.size() == 1 + 2 * field_len && base_pt[0] == 0x04) {
63✔
210
      const auto g_x = m_g_x.serialize(field_len);
62✔
211
      const auto g_y = m_g_y.serialize(field_len);
62✔
212

213
      const auto sec1_x = base_pt.subspan(1, field_len);
62✔
214
      const auto sec1_y = base_pt.subspan(1 + field_len, field_len);
62✔
215

216
      if(!std::ranges::equal(sec1_x, g_x)) {
124✔
217
         return false;
218
      }
219

220
      if(!std::ranges::equal(sec1_y, g_y)) {
174✔
221
         return false;
222
      }
223

224
      return true;
56✔
225
   } else {
124✔
226
      throw Decoding_Error("Invalid base point encoding in explicit group");
1✔
227
   }
228
}
229

230
bool EC_Group_Data::params_match(const EC_Group_Data& other) const {
×
231
   return params_match(other.p(), other.a(), other.b(), other.g_x(), other.g_y(), other.order(), other.cofactor());
×
232
}
233

234
void EC_Group_Data::set_oid(const OID& oid) {
×
235
   BOTAN_ARG_CHECK(!oid.empty(), "OID should be set");
×
236
   BOTAN_STATE_CHECK(m_oid.empty() && m_der_named_curve.empty());
×
237
   m_oid = oid;
×
238

239
   DER_Encoder der(m_der_named_curve);
×
240
   der.encode(m_oid);
×
241
}
×
242

243
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bytes_with_trunc(std::span<const uint8_t> bytes) const {
39,900✔
244
   const size_t bit_length = 8 * bytes.size();
39,900✔
245

246
   if(bit_length < order_bits()) {
39,900✔
247
      // No shifting required, but might still need to reduce by modulus
248
      return this->scalar_from_bytes_mod_order(bytes);
8,274✔
249
   } else {
250
      const size_t shift = bit_length - order_bits();
31,626✔
251

252
      const size_t new_length = bytes.size() - (shift / 8);
31,626✔
253
      const size_t bit_shift = shift % 8;
31,626✔
254

255
      if(bit_shift == 0) {
31,626✔
256
         // Easy case just read different bytes
257
         return this->scalar_from_bytes_mod_order(bytes.first(new_length));
28,667✔
258
      } else {
259
         std::vector<uint8_t> sbytes(new_length);
2,959✔
260

261
         uint8_t carry = 0;
2,959✔
262
         for(size_t i = 0; i != new_length; ++i) {
83,490✔
263
            const uint8_t w = bytes[i];
80,531✔
264
            sbytes[i] = (w >> bit_shift) | carry;
80,531✔
265
            carry = w << (8 - bit_shift);
80,531✔
266
         }
267

268
         return this->scalar_from_bytes_mod_order(sbytes);
2,959✔
269
      }
2,959✔
270
   }
271
}
272

273
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bytes_mod_order(std::span<const uint8_t> bytes) const {
50,810✔
274
   if(bytes.size() > 2 * order_bytes()) {
50,810✔
275
      return {};
×
276
   }
277

278
   if(m_pcurve) {
50,810✔
279
      if(auto s = m_pcurve->scalar_from_wide_bytes(bytes)) {
47,708✔
280
         return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), std::move(*s));
47,708✔
281
      } else {
282
         return {};
×
283
      }
47,708✔
284
   } else {
285
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
286
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), m_mod_order.reduce(BigInt(bytes)));
3,102✔
287
#else
288
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
289
#endif
290
   }
291
}
292

293
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_random(RandomNumberGenerator& rng) const {
46,566✔
294
   if(m_pcurve) {
46,566✔
295
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), m_pcurve->random_scalar(rng));
40,363✔
296
   } else {
297
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
298
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(),
12,406✔
299
                                                 BigInt::random_integer(rng, BigInt::one(), m_order));
18,609✔
300
#else
301
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
302
#endif
303
   }
304
}
305

306
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_one() const {
289✔
307
   if(m_pcurve) {
289✔
308
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), m_pcurve->scalar_one());
273✔
309
   } else {
310
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
311
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), BigInt::one());
16✔
312
#else
313
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
314
#endif
315
   }
316
}
317

318
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bigint(const BigInt& bn) const {
4,549✔
319
   if(bn <= 0 || bn >= m_order) {
4,549✔
320
      return {};
×
321
   }
322

323
   if(m_pcurve) {
4,549✔
324
      return this->scalar_deserialize(bn.serialize(m_order_bytes));
8,260✔
325
   } else {
326
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
327
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), bn);
419✔
328
#else
329
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
330
#endif
331
   }
332
}
333

334
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::gk_x_mod_order(const EC_Scalar_Data& scalar,
6,273✔
335
                                                              RandomNumberGenerator& rng) const {
336
   if(m_pcurve) {
6,273✔
337
      const auto& k = EC_Scalar_Data_PC::checked_ref(scalar);
5,874✔
338
      auto gk_x_mod_order = m_pcurve->base_point_mul_x_mod_order(k.value(), rng);
5,874✔
339
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), gk_x_mod_order);
5,874✔
340
   } else {
5,874✔
341
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
342
      const auto& k = EC_Scalar_Data_BN::checked_ref(scalar);
399✔
343
      BOTAN_STATE_CHECK(m_base_mult != nullptr);
399✔
344
      std::vector<BigInt> ws;
399✔
345
      const auto pt = m_base_mult->mul(k.value(), rng, m_order, ws);
399✔
346

347
      if(pt.is_zero()) {
798✔
348
         return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), BigInt::zero());
×
349
      } else {
350
         return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), m_mod_order.reduce(pt.get_affine_x()));
399✔
351
      }
352
#else
353
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
354
#endif
355
   }
399✔
356
}
357

358
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_deserialize(std::span<const uint8_t> bytes) const {
96,813✔
359
   if(bytes.size() != m_order_bytes) {
96,813✔
360
      return nullptr;
7,779✔
361
   }
362

363
   if(m_pcurve) {
89,034✔
364
      if(auto s = m_pcurve->deserialize_scalar(bytes)) {
84,135✔
365
         return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), *s);
81,626✔
366
      } else {
367
         return nullptr;
2,509✔
368
      }
84,135✔
369
   } else {
370
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
371
      BigInt r(bytes);
4,899✔
372

373
      if(r.is_zero() || r >= m_order) {
9,798✔
374
         return nullptr;
525✔
375
      }
376

377
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), std::move(r));
4,374✔
378
#else
379
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
380
#endif
381
   }
4,899✔
382
}
383

384
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_deserialize_uncompressed(
46,858✔
385
   std::span<const uint8_t> bytes) const {
386
   if(bytes.size() != 1 + 2 * p_bytes() || bytes[0] != 0x04) {
46,858✔
387
      return {};
8,862✔
388
   }
389

390
   if(m_pcurve) {
37,996✔
391
      if(auto pt = m_pcurve->deserialize_point_uncompressed(bytes)) {
35,717✔
392
         return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(*pt));
35,533✔
393
      } else {
394
         return {};
184✔
395
      }
35,717✔
396
   } else {
397
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
398
      try {
2,279✔
399
         auto pt = Botan::OS2ECP(bytes, m_curve);
2,279✔
400
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
2,271✔
401
      } catch(...) {
2,279✔
402
         return {};
8✔
403
      }
8✔
404
#else
405
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
406
#endif
407
   }
408
}
409

410
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_deserialize_compressed(std::span<const uint8_t> bytes) const {
25,809✔
411
   if(bytes.size() != 1 + p_bytes() || (bytes[0] != 0x02 && bytes[0] != 0x03)) {
25,809✔
412
      return {};
13,758✔
413
   }
414

415
   if(m_pcurve) {
12,051✔
416
      if(auto pt = m_pcurve->deserialize_point_compressed(bytes)) {
10,451✔
417
         return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(*pt));
10,439✔
418
      } else {
419
         return {};
12✔
420
      }
10,451✔
421
   } else {
422
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
423
      try {
1,600✔
424
         auto pt = Botan::OS2ECP(bytes, m_curve);
1,600✔
425
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
1,600✔
426
      } catch(...) {
1,600✔
427
         return {};
×
428
      }
×
429
#else
430
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
431
#endif
432
   }
433
}
434

435
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_identity() const {
366✔
436
   if(m_pcurve) {
366✔
437
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_identity());
314✔
438
   } else {
439
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
440
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), EC_Point(m_curve));
52✔
441
#else
442
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
443
#endif
444
   }
445
}
446

447
std::function<void(std::span<uint8_t>)> h2c_expand_message(std::string_view hash_fn,
251✔
448
                                                           size_t order_bits,
449
                                                           std::span<const uint8_t> input,
450
                                                           std::span<const uint8_t> domain_sep) {
451
   /*
452
   * This could be extended to support expand_message_xof or a MHF like Argon2
453
   */
454

455
   if(hash_fn.starts_with("SHAKE")) {
251✔
456
      throw Not_Implemented("Hash to curve currently does not support expand_message_xof");
14✔
457
   }
458

459
#if defined(BOTAN_HAS_XMD)
460
   // Here we capture the HashFunction by shared_ptr because it will be owned by
461
   // the returned std::function
462
   const std::shared_ptr<HashFunction> hash = HashFunction::create_or_throw(hash_fn);
460✔
463

464
   /*
465
   * RFC 9380 Section 5.3.1: "The number of bits output by H MUST be b >= 2 * k,
466
   * where k is the target security level in bits", as this "ensures k-bit
467
   * collision resistance". Checking the hash's collision resistance estimate
468
   * covers this, and also rejects hashes with known collision attacks. The
469
   * target level is capped at 256 since the RFC 9380 suites for P-521 use k = 256.
470
   */
471
   const size_t k = std::min<size_t>((order_bits + 1) / 2, 256);
223✔
472

473
   if(hash->security_level() < k) {
223✔
474
      throw Invalid_Argument(fmt("Hash {} is too weak for use with a {} bit group", hash->name(), order_bits));
80✔
475
   }
476

477
   return [hash, input, domain_sep](std::span<uint8_t> uniform_bytes) {
1,138✔
478
      expand_message_xmd(*hash, uniform_bytes, input, domain_sep);
183✔
479
   };
183✔
480
#else
481
   BOTAN_UNUSED(order_bits, input, domain_sep);
482
   throw Not_Implemented("Hash to curve is not implemented due to XMD being disabled");
483
#endif
484
}
183✔
485

486
bool EC_Group_Data::hash_to_curve_supported(std::string_view hash_fn) const {
262✔
487
#if defined(BOTAN_HAS_XMD)
488
   if(!m_pcurve || !m_pcurve->supports_hash_to_curve()) {
262✔
489
      return false;
126✔
490
   }
491

492
   // Consistent with h2c_expand_message; XOF based expansion is not implemented
493
   if(hash_fn.starts_with("SHAKE")) {
136✔
494
      return false;
495
   }
496

497
   auto hash = HashFunction::create(hash_fn);
129✔
498
   if(hash == nullptr) {
129✔
499
      return false;
500
   }
501

502
   // The same hash strength requirement enforced by h2c_expand_message
503
   const size_t k = std::min<size_t>((order_bits() + 1) / 2, 256);
122✔
504
   if(hash->security_level() < k) {
122✔
505
      return false;
506
   }
507

508
   // The same requirements enforced by expand_message_xmd
509
   return hash->hash_block_size() > 0 && hash->output_length() <= hash->hash_block_size();
102✔
510
#else
511
   BOTAN_UNUSED(hash_fn);
512
   return false;
513
#endif
514
}
129✔
515

516
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_hash_to_curve_ro(std::string_view hash_fn,
239✔
517
                                                                           std::span<const uint8_t> input,
518
                                                                           std::span<const uint8_t> domain_sep) const {
519
   if(m_pcurve && m_pcurve->supports_hash_to_curve()) {
239✔
520
      auto pt = m_pcurve->hash_to_curve_ro(h2c_expand_message(hash_fn, order_bits(), input, domain_sep));
113✔
521
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(pt));
79✔
522
   } else {
79✔
523
      throw Not_Implemented("Hash to curve is not implemented for this curve");
126✔
524
   }
525
}
526

527
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_hash_to_curve_nu(std::string_view hash_fn,
237✔
528
                                                                           std::span<const uint8_t> input,
529
                                                                           std::span<const uint8_t> domain_sep) const {
530
   if(m_pcurve && m_pcurve->supports_hash_to_curve()) {
237✔
531
      auto pt = m_pcurve->hash_to_curve_nu(h2c_expand_message(hash_fn, order_bits(), input, domain_sep));
111✔
532
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(pt));
82✔
533
   } else {
82✔
534
      throw Not_Implemented("Hash to curve is not implemented for this curve");
126✔
535
   }
536
}
537

538
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_g_mul(const EC_Scalar_Data& scalar,
17,549✔
539
                                                                RandomNumberGenerator& rng) const {
540
   if(m_pcurve) {
17,549✔
541
      const auto& k = EC_Scalar_Data_PC::checked_ref(scalar);
15,696✔
542
      auto pt = m_pcurve->point_to_affine(m_pcurve->mul_by_g(k.value(), rng));
15,696✔
543
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(pt));
15,696✔
544
   } else {
15,696✔
545
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
546
      const auto& group = scalar.group();
1,853✔
547
      const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar);
1,853✔
548

549
      BOTAN_STATE_CHECK(group->m_base_mult != nullptr);
1,853✔
550
      std::vector<BigInt> ws;
1,853✔
551
      auto pt = group->m_base_mult->mul(bn.value(), rng, m_order, ws);
1,853✔
552
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
1,853✔
553
#else
554
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
555
#endif
556
   }
1,853✔
557
}
558

559
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::mul_px_qy(const EC_AffinePoint_Data& p,
3,488✔
560
                                                              const EC_Scalar_Data& x,
561
                                                              const EC_AffinePoint_Data& q,
562
                                                              const EC_Scalar_Data& y,
563
                                                              RandomNumberGenerator& rng) const {
564
   if(m_pcurve) {
3,488✔
565
      auto pt = m_pcurve->mul_px_qy(EC_AffinePoint_Data_PC::checked_ref(p).value(),
6,416✔
566
                                    EC_Scalar_Data_PC::checked_ref(x).value(),
3,208✔
567
                                    EC_AffinePoint_Data_PC::checked_ref(q).value(),
3,208✔
568
                                    EC_Scalar_Data_PC::checked_ref(y).value(),
3,208✔
569
                                    rng);
3,208✔
570

571
      if(pt) {
3,208✔
572
         return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(*pt));
3,040✔
573
      } else {
574
         return nullptr;
168✔
575
      }
576
   } else {
3,208✔
577
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
578
      std::vector<BigInt> ws;
280✔
579
      const auto& group = p.group();
280✔
580

581
      // TODO this could be better!
582
      const EC_Point_Var_Point_Precompute p_mul(p.to_legacy_point(), rng, ws);
280✔
583
      const EC_Point_Var_Point_Precompute q_mul(q.to_legacy_point(), rng, ws);
280✔
584

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

587
      auto px = p_mul.mul(EC_Scalar_Data_BN::checked_ref(x).value(), rng, order, ws);
280✔
588
      auto qy = q_mul.mul(EC_Scalar_Data_BN::checked_ref(y).value(), rng, order, ws);
280✔
589

590
      auto px_qy = px + qy;
280✔
591

592
      if(!px_qy.is_zero()) {
532✔
593
         px_qy.force_affine();
252✔
594
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(px_qy));
252✔
595
      } else {
596
         return nullptr;
28✔
597
      }
598
#else
599
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
600
#endif
601
   }
840✔
602
}
603

604
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_add(const EC_AffinePoint_Data& p,
10,201✔
605
                                                               const EC_AffinePoint_Data& q) const {
606
   if(m_pcurve) {
10,201✔
607
      auto pt = m_pcurve->point_add(EC_AffinePoint_Data_PC::checked_ref(p).value(),
9,461✔
608
                                    EC_AffinePoint_Data_PC::checked_ref(q).value());
9,461✔
609

610
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(pt));
9,460✔
611
   } else {
9,460✔
612
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
613
      auto pt = p.to_legacy_point() + q.to_legacy_point();
740✔
614
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
740✔
615
#else
616
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
617
#endif
618
   }
740✔
619
}
620

621
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_neg(const EC_AffinePoint_Data& p) const {
9,621✔
622
   if(m_pcurve) {
9,621✔
623
      auto pt = m_pcurve->point_negate(EC_AffinePoint_Data_PC::checked_ref(p).value());
8,559✔
624
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), pt);
8,559✔
625
   } else {
8,559✔
626
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
627
      auto pt = p.to_legacy_point();
1,062✔
628
      pt.negate();  // negates in place
1,062✔
629
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
1,062✔
630
#else
631
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
632
#endif
633
   }
1,062✔
634
}
635

636
std::unique_ptr<EC_Mul2Table_Data> EC_Group_Data::make_mul2_table(const EC_AffinePoint_Data& h) const {
14,510✔
637
   if(m_pcurve) {
14,510✔
638
      return std::make_unique<EC_Mul2Table_Data_PC>(h);
14,206✔
639
   } else {
640
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
641
      const EC_AffinePoint_Data_BN g(shared_from_this(), this->base_point());
608✔
642
      return std::make_unique<EC_Mul2Table_Data_BN>(g, h);
304✔
643
#else
644
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
645
#endif
646
   }
304✔
647
}
648

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

© 2026 Coveralls, Inc