Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/pubkey/ec_group/ec_inner_data.cpp
Line
Count
Source (jump to first uncovered line)
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
#if defined(BOTAN_HAS_XMD)
20
   #include <botan/internal/xmd.h>
21
#endif
22
23
namespace Botan {
24
25
2.01k
EC_Group_Data::~EC_Group_Data() = default;
26
27
// Note this constructor *does not* initialize m_curve, m_base_point or m_base_mult
28
EC_Group_Data::EC_Group_Data(const BigInt& p,
29
                             const BigInt& a,
30
                             const BigInt& b,
31
                             const BigInt& g_x,
32
                             const BigInt& g_y,
33
                             const BigInt& order,
34
                             const BigInt& cofactor,
35
                             const OID& oid,
36
                             EC_Group_Source source) :
37
2.01k
      m_p(p),
38
2.01k
      m_a(a),
39
2.01k
      m_b(b),
40
2.01k
      m_g_x(g_x),
41
2.01k
      m_g_y(g_y),
42
2.01k
      m_order(order),
43
2.01k
      m_cofactor(cofactor),
44
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
45
2.01k
      m_mod_field(Modular_Reducer::for_public_modulus(p)),
46
2.01k
      m_mod_order(Modular_Reducer::for_public_modulus(order)),
47
2.01k
      m_monty(m_p, m_mod_field),
48
#endif
49
2.01k
      m_oid(oid),
50
2.01k
      m_p_words(p.sig_words()),
51
2.01k
      m_p_bits(p.bits()),
52
2.01k
      m_order_bits(order.bits()),
53
2.01k
      m_order_bytes((m_order_bits + 7) / 8),
54
2.01k
      m_a_is_minus_3(a == p - 3),
55
2.01k
      m_a_is_zero(a.is_zero()),
56
2.01k
      m_has_cofactor(m_cofactor != 1),
57
2.01k
      m_order_is_less_than_p(m_order < p),
58
2.01k
      m_source(source) {
59
   // TODO(Botan4) we can assume/assert the OID is set
60
2.01k
   if(!m_oid.empty()) {
61
1.41k
      DER_Encoder der(m_der_named_curve);
62
1.41k
      der.encode(m_oid);
63
64
1.41k
      const std::string name = m_oid.human_name_or_empty();
65
1.41k
      if(!name.empty()) {
66
         // returns nullptr if unknown or not supported
67
1.41k
         m_pcurve = PCurve::PrimeOrderCurve::for_named_curve(name);
68
1.41k
      }
69
1.41k
      if(m_pcurve) {
70
582
         m_engine = EC_Group_Engine::Optimized;
71
582
      }
72
1.41k
   }
73
74
   // Try a generic pcurves instance
75
2.01k
   if(!m_pcurve && !m_has_cofactor) {
76
1.27k
      m_pcurve = PCurve::PrimeOrderCurve::from_params(p, a, b, g_x, g_y, order);
77
1.27k
      if(m_pcurve) {
78
404
         m_engine = EC_Group_Engine::Generic;
79
404
      }
80
      // possibly still null here, if parameters unsuitable or if the
81
      // pcurves_generic module wasn't included in the build
82
1.27k
   }
83
84
2.01k
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
85
2.01k
   secure_vector<word> ws;
86
2.01k
   m_a_r = m_monty.mul(a, m_monty.R2(), ws);
87
2.01k
   m_b_r = m_monty.mul(b, m_monty.R2(), ws);
88
2.01k
   if(!m_pcurve) {
89
1.02k
      m_engine = EC_Group_Engine::Legacy;
90
1.02k
   }
91
#else
92
   if(!m_pcurve) {
93
      if(m_oid.empty()) {
94
         throw Not_Implemented("EC_Group this group is not supported in this build configuration");
95
      } else {
96
         throw Not_Implemented(
97
            fmt("EC_Group the group {} is not supported in this build configuration", oid.to_string()));
98
      }
99
   }
100
#endif
101
2.01k
}
102
103
std::shared_ptr<EC_Group_Data> EC_Group_Data::create(const BigInt& p,
104
                                                     const BigInt& a,
105
                                                     const BigInt& b,
106
                                                     const BigInt& g_x,
107
                                                     const BigInt& g_y,
108
                                                     const BigInt& order,
109
                                                     const BigInt& cofactor,
110
                                                     const OID& oid,
111
2.01k
                                                     EC_Group_Source source) {
112
2.01k
   auto group = std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, source);
113
114
2.01k
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
115
2.01k
   group->m_curve = CurveGFp(group.get());
116
2.01k
   group->m_base_point = EC_Point(group->m_curve, g_x, g_y);
117
2.01k
   if(!group->m_pcurve) {
118
1.02k
      group->m_base_mult = std::make_unique<EC_Point_Base_Point_Precompute>(group->m_base_point, group->m_mod_order);
119
1.02k
   }
120
2.01k
#endif
121
122
2.01k
   return group;
123
2.01k
}
124
125
bool EC_Group_Data::params_match(const BigInt& p,
126
                                 const BigInt& a,
127
                                 const BigInt& b,
128
                                 const BigInt& g_x,
129
                                 const BigInt& g_y,
130
                                 const BigInt& order,
131
55.2k
                                 const BigInt& cofactor) const {
132
55.2k
   return (this->p() == p && this->a() == a && this->b() == b && this->order() == order &&
133
55.2k
           this->cofactor() == cofactor && this->g_x() == g_x && this->g_y() == g_y);
134
55.2k
}
135
136
0
bool EC_Group_Data::params_match(const EC_Group_Data& other) const {
137
0
   return params_match(other.p(), other.a(), other.b(), other.g_x(), other.g_y(), other.order(), other.cofactor());
138
0
}
139
140
0
void EC_Group_Data::set_oid(const OID& oid) {
141
0
   BOTAN_ARG_CHECK(!oid.empty(), "OID should be set");
142
0
   BOTAN_STATE_CHECK(m_oid.empty() && m_der_named_curve.empty());
143
0
   m_oid = oid;
144
145
0
   DER_Encoder der(m_der_named_curve);
146
0
   der.encode(m_oid);
147
0
}
148
149
497
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bytes_with_trunc(std::span<const uint8_t> bytes) const {
150
497
   const size_t bit_length = 8 * bytes.size();
151
152
497
   if(bit_length < order_bits()) {
153
      // No shifting required, but might still need to reduce by modulus
154
187
      return this->scalar_from_bytes_mod_order(bytes);
155
310
   } else {
156
310
      const size_t shift = bit_length - order_bits();
157
158
310
      const size_t new_length = bytes.size() - (shift / 8);
159
310
      const size_t bit_shift = shift % 8;
160
161
310
      if(bit_shift == 0) {
162
         // Easy case just read different bytes
163
293
         return this->scalar_from_bytes_mod_order(bytes.first(new_length));
164
293
      } else {
165
17
         std::vector<uint8_t> sbytes(new_length);
166
167
17
         uint8_t carry = 0;
168
833
         for(size_t i = 0; i != new_length; ++i) {
169
816
            const uint8_t w = bytes[i];
170
816
            sbytes[i] = (w >> bit_shift) | carry;
171
816
            carry = w << (8 - bit_shift);
172
816
         }
173
174
17
         return this->scalar_from_bytes_mod_order(sbytes);
175
17
      }
176
310
   }
177
497
}
178
179
978
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bytes_mod_order(std::span<const uint8_t> bytes) const {
180
978
   if(bytes.size() > 2 * order_bytes()) {
181
0
      return {};
182
0
   }
183
184
978
   if(m_pcurve) {
185
902
      if(auto s = m_pcurve->scalar_from_wide_bytes(bytes)) {
186
902
         return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), std::move(*s));
187
902
      } else {
188
0
         return {};
189
0
      }
190
902
   } else {
191
76
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
192
76
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), m_mod_order.reduce(BigInt(bytes)));
193
#else
194
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
195
#endif
196
76
   }
197
978
}
198
199
12.8k
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_random(RandomNumberGenerator& rng) const {
200
12.8k
   if(m_pcurve) {
201
12.8k
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), m_pcurve->random_scalar(rng));
202
12.8k
   } else {
203
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
204
0
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(),
205
0
                                                 BigInt::random_integer(rng, BigInt::one(), m_order));
206
#else
207
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
208
#endif
209
0
   }
210
12.8k
}
211
212
0
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_one() const {
213
0
   if(m_pcurve) {
214
0
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), m_pcurve->scalar_one());
215
0
   } else {
216
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
217
0
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), BigInt::one());
218
#else
219
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
220
#endif
221
0
   }
222
0
}
223
224
0
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_from_bigint(const BigInt& bn) const {
225
0
   if(bn <= 0 || bn >= m_order) {
226
0
      return {};
227
0
   }
228
229
0
   if(m_pcurve) {
230
0
      return this->scalar_deserialize(bn.serialize(m_order_bytes));
231
0
   } else {
232
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
233
0
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), bn);
234
#else
235
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
236
#endif
237
0
   }
238
0
}
239
240
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::gk_x_mod_order(const EC_Scalar_Data& scalar,
241
0
                                                              RandomNumberGenerator& rng) const {
242
0
   if(m_pcurve) {
243
0
      const auto& k = EC_Scalar_Data_PC::checked_ref(scalar);
244
0
      auto gk_x_mod_order = m_pcurve->base_point_mul_x_mod_order(k.value(), rng);
245
0
      return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), gk_x_mod_order);
246
0
   } else {
247
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
248
0
      const auto& k = EC_Scalar_Data_BN::checked_ref(scalar);
249
0
      BOTAN_STATE_CHECK(m_base_mult != nullptr);
250
0
      std::vector<BigInt> ws;
251
0
      const auto pt = m_base_mult->mul(k.value(), rng, m_order, ws);
252
253
0
      if(pt.is_zero()) {
254
0
         return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), BigInt::zero());
255
0
      } else {
256
0
         return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), m_mod_order.reduce(pt.get_affine_x()));
257
0
      }
258
#else
259
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
260
#endif
261
0
   }
262
0
}
263
264
9.57k
std::unique_ptr<EC_Scalar_Data> EC_Group_Data::scalar_deserialize(std::span<const uint8_t> bytes) const {
265
9.57k
   if(bytes.size() != m_order_bytes) {
266
288
      return nullptr;
267
288
   }
268
269
9.28k
   if(m_pcurve) {
270
8.67k
      if(auto s = m_pcurve->deserialize_scalar(bytes)) {
271
8.57k
         return std::make_unique<EC_Scalar_Data_PC>(shared_from_this(), *s);
272
8.57k
      } else {
273
102
         return nullptr;
274
102
      }
275
8.67k
   } else {
276
604
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
277
604
      BigInt r(bytes);
278
279
604
      if(r.is_zero() || r >= m_order) {
280
24
         return nullptr;
281
24
      }
282
283
580
      return std::make_unique<EC_Scalar_Data_BN>(shared_from_this(), std::move(r));
284
#else
285
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
286
#endif
287
604
   }
288
9.28k
}
289
290
15.1k
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_deserialize(std::span<const uint8_t> bytes) const {
291
   // The deprecated "hybrid" point format
292
   // TODO(Botan4) remove this
293
15.1k
   if(bytes.size() >= 1 + 2 * 4 && (bytes[0] == 0x06 || bytes[0] == 0x07)) {
294
688
      bool hdr_y_is_even = bytes[0] == 0x06;
295
688
      bool y_is_even = (bytes.back() & 0x01) == 0;
296
297
688
      if(hdr_y_is_even == y_is_even) {
298
431
         std::vector<uint8_t> sec1(bytes.begin(), bytes.end());
299
431
         sec1[0] = 0x04;
300
431
         return this->point_deserialize(sec1);
301
431
      }
302
688
   }
303
304
14.6k
   try {
305
14.6k
      if(m_pcurve) {
306
13.9k
         if(auto pt = m_pcurve->deserialize_point(bytes)) {
307
11.3k
            return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(*pt));
308
11.3k
         } else {
309
2.59k
            return {};
310
2.59k
         }
311
13.9k
      } else {
312
708
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
313
708
         auto pt = Botan::OS2ECP(bytes, m_curve);
314
708
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
315
#else
316
         throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
317
#endif
318
708
      }
319
14.6k
   } catch(...) {
320
447
      return {};
321
447
   }
322
14.6k
}
323
324
namespace {
325
326
std::function<void(std::span<uint8_t>)> h2c_expand_message(std::string_view hash_fn,
327
                                                           std::span<const uint8_t> input,
328
0
                                                           std::span<const uint8_t> domain_sep) {
329
   /*
330
   * This could be extended to support expand_message_xof or a MHF like Argon2
331
   */
332
333
0
   if(hash_fn.starts_with("SHAKE")) {
334
0
      throw Not_Implemented("Hash to curve currently does not support expand_message_xof");
335
0
   }
336
337
0
   return [=](std::span<uint8_t> uniform_bytes) {
338
0
#if defined(BOTAN_HAS_XMD)
339
0
      expand_message_xmd(hash_fn, uniform_bytes, input, domain_sep);
340
#else
341
      BOTAN_UNUSED(hash_fn, uniform_bytes, input, domain_sep);
342
      throw Not_Implemented("Hash to curve is not implemented due to XMD being disabled");
343
#endif
344
0
   };
345
0
}
346
347
}  // namespace
348
349
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_hash_to_curve_ro(std::string_view hash_fn,
350
                                                                           std::span<const uint8_t> input,
351
0
                                                                           std::span<const uint8_t> domain_sep) const {
352
0
   if(m_pcurve) {
353
0
      auto pt = m_pcurve->hash_to_curve_ro(h2c_expand_message(hash_fn, input, domain_sep));
354
0
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(pt));
355
0
   } else {
356
0
      throw Not_Implemented("Hash to curve is not implemented for this curve");
357
0
   }
358
0
}
359
360
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_hash_to_curve_nu(std::string_view hash_fn,
361
                                                                           std::span<const uint8_t> input,
362
0
                                                                           std::span<const uint8_t> domain_sep) const {
363
0
   if(m_pcurve) {
364
0
      auto pt = m_pcurve->hash_to_curve_nu(h2c_expand_message(hash_fn, input, domain_sep));
365
0
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(pt));
366
0
   } else {
367
0
      throw Not_Implemented("Hash to curve is not implemented for this curve");
368
0
   }
369
0
}
370
371
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::point_g_mul(const EC_Scalar_Data& scalar,
372
13.4k
                                                                RandomNumberGenerator& rng) const {
373
13.4k
   if(m_pcurve) {
374
13.3k
      const auto& k = EC_Scalar_Data_PC::checked_ref(scalar);
375
13.3k
      auto pt = m_pcurve->point_to_affine(m_pcurve->mul_by_g(k.value(), rng));
376
13.3k
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), std::move(pt));
377
13.3k
   } else {
378
80
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
379
80
      const auto& group = scalar.group();
380
80
      const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar);
381
382
80
      BOTAN_STATE_CHECK(group->m_base_mult != nullptr);
383
80
      std::vector<BigInt> ws;
384
80
      auto pt = group->m_base_mult->mul(bn.value(), rng, m_order, ws);
385
80
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
386
#else
387
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
388
#endif
389
80
   }
390
13.4k
}
391
392
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::mul_px_qy(const EC_AffinePoint_Data& p,
393
                                                              const EC_Scalar_Data& x,
394
                                                              const EC_AffinePoint_Data& q,
395
                                                              const EC_Scalar_Data& y,
396
0
                                                              RandomNumberGenerator& rng) const {
397
0
   if(m_pcurve) {
398
0
      auto pt = m_pcurve->mul_px_qy(EC_AffinePoint_Data_PC::checked_ref(p).value(),
399
0
                                    EC_Scalar_Data_PC::checked_ref(x).value(),
400
0
                                    EC_AffinePoint_Data_PC::checked_ref(q).value(),
401
0
                                    EC_Scalar_Data_PC::checked_ref(y).value(),
402
0
                                    rng);
403
404
0
      if(pt) {
405
0
         return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(*pt));
406
0
      } else {
407
0
         return nullptr;
408
0
      }
409
0
   } else {
410
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
411
0
      std::vector<BigInt> ws;
412
0
      const auto& group = p.group();
413
414
      // TODO this could be better!
415
0
      EC_Point_Var_Point_Precompute p_mul(p.to_legacy_point(), rng, ws);
416
0
      EC_Point_Var_Point_Precompute q_mul(q.to_legacy_point(), rng, ws);
417
418
0
      const auto order = group->order() * group->cofactor();  // See #3800
419
420
0
      auto px = p_mul.mul(EC_Scalar_Data_BN::checked_ref(x).value(), rng, order, ws);
421
0
      auto qy = q_mul.mul(EC_Scalar_Data_BN::checked_ref(y).value(), rng, order, ws);
422
423
0
      auto px_qy = px + qy;
424
425
0
      if(!px_qy.is_zero()) {
426
0
         px_qy.force_affine();
427
0
         return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(px_qy));
428
0
      } else {
429
0
         return nullptr;
430
0
      }
431
#else
432
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
433
#endif
434
0
   }
435
0
}
436
437
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_add(const EC_AffinePoint_Data& p,
438
680
                                                               const EC_AffinePoint_Data& q) const {
439
680
   if(m_pcurve) {
440
680
      auto pt = m_pcurve->point_add(EC_AffinePoint_Data_PC::checked_ref(p).value(),
441
680
                                    EC_AffinePoint_Data_PC::checked_ref(q).value());
442
443
680
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), m_pcurve->point_to_affine(pt));
444
680
   } else {
445
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
446
0
      auto pt = p.to_legacy_point() + q.to_legacy_point();
447
0
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
448
#else
449
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
450
#endif
451
0
   }
452
680
}
453
454
0
std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_neg(const EC_AffinePoint_Data& p) const {
455
0
   if(m_pcurve) {
456
0
      auto pt = m_pcurve->point_negate(EC_AffinePoint_Data_PC::checked_ref(p).value());
457
0
      return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), pt);
458
0
   } else {
459
0
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
460
0
      auto pt = p.to_legacy_point();
461
0
      pt.negate();  // negates in place
462
0
      return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
463
#else
464
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
465
#endif
466
0
   }
467
0
}
468
469
821
std::unique_ptr<EC_Mul2Table_Data> EC_Group_Data::make_mul2_table(const EC_AffinePoint_Data& h) const {
470
821
   if(m_pcurve) {
471
730
      return std::make_unique<EC_Mul2Table_Data_PC>(h);
472
730
   } else {
473
91
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
474
91
      EC_AffinePoint_Data_BN g(shared_from_this(), this->base_point());
475
91
      return std::make_unique<EC_Mul2Table_Data_BN>(g, h);
476
#else
477
      throw Not_Implemented("Legacy EC interfaces disabled in this build configuration");
478
#endif
479
91
   }
480
821
}
481
482
}  // namespace Botan