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

randombit / botan / 11341066650

15 Oct 2024 07:12AM UTC coverage: 91.131% (+0.006%) from 91.125%
11341066650

Pull #3893

github

web-flow
Merge 33000ab14 into 4996790e9
Pull Request #3893: PQC: ML-KEM

90490 of 99297 relevant lines covered (91.13%)

9098152.18 hits per line

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

91.79
/src/lib/pubkey/ec_group/ec_point.cpp
1
/*
2
* Point arithmetic on elliptic curves over GF(p)
3
*
4
* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
5
*     2008-2011,2012,2014,2015,2018 Jack Lloyd
6
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include <botan/ec_point.h>
11

12
#include <botan/numthry.h>
13
#include <botan/rng.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/stl_util.h>
16

17
namespace Botan {
18

19
EC_Point::EC_Point(const CurveGFp& curve) : m_curve(curve), m_coord_x(0), m_coord_y(curve.get_1_rep()), m_coord_z(0) {
20,960✔
20
   // Assumes Montgomery rep of zero is zero
21
}
20,960✔
22

23
EC_Point::EC_Point(const CurveGFp& curve, BigInt x, BigInt y) :
41,648✔
24
      m_curve(curve), m_coord_x(std::move(x)), m_coord_y(std::move(y)), m_coord_z(m_curve.get_1_rep()) {
41,648✔
25
   if(m_coord_x < 0 || m_coord_x >= curve.get_p()) {
83,296✔
26
      throw Invalid_Argument("Invalid EC_Point affine x");
5✔
27
   }
28
   if(m_coord_y < 0 || m_coord_y >= curve.get_p()) {
83,286✔
29
      throw Invalid_Argument("Invalid EC_Point affine y");
×
30
   }
31

32
   secure_vector<word> monty_ws(m_curve.get_ws_size());
41,643✔
33
   m_curve.to_rep(m_coord_x, monty_ws);
41,643✔
34
   m_curve.to_rep(m_coord_y, monty_ws);
41,643✔
35
}
41,663✔
36

37
void EC_Point::randomize_repr(RandomNumberGenerator& rng) {
62,680✔
38
   secure_vector<word> ws(m_curve.get_ws_size());
62,680✔
39
   randomize_repr(rng, ws);
62,680✔
40
}
62,680✔
41

42
void EC_Point::randomize_repr(RandomNumberGenerator& rng, secure_vector<word>& ws) {
71,526✔
43
   const BigInt mask = BigInt::random_integer(rng, 2, m_curve.get_p());
71,526✔
44

45
   /*
46
   * No reason to convert this to Montgomery representation first,
47
   * just pretend the random mask was chosen as Redc(mask) and the
48
   * random mask we generated above is in the Montgomery
49
   * representation.
50
   * //m_curve.to_rep(mask, ws);
51
   */
52
   const BigInt mask2 = m_curve.sqr_to_tmp(mask, ws);
71,526✔
53
   const BigInt mask3 = m_curve.mul_to_tmp(mask2, mask, ws);
71,526✔
54

55
   m_coord_x = m_curve.mul_to_tmp(m_coord_x, mask2, ws);
71,526✔
56
   m_coord_y = m_curve.mul_to_tmp(m_coord_y, mask3, ws);
71,526✔
57
   m_coord_z = m_curve.mul_to_tmp(m_coord_z, mask, ws);
143,052✔
58
}
214,578✔
59

60
namespace {
61

62
inline void resize_ws(std::vector<BigInt>& ws_bn, size_t cap_size) {
5,264,125✔
63
   BOTAN_ASSERT(ws_bn.size() >= EC_Point::WORKSPACE_SIZE, "Expected size for EC_Point workspace");
5,264,125✔
64

65
   for(auto& ws : ws_bn) {
47,377,125✔
66
      if(ws.size() < cap_size) {
42,113,000✔
67
         ws.get_word_vector().resize(cap_size);
1,012,418✔
68
      }
69
   }
70
}
5,264,125✔
71

72
}  // namespace
73

74
void EC_Point::add_affine(
1,147,927✔
75
   const word x_words[], size_t x_size, const word y_words[], size_t y_size, std::vector<BigInt>& ws_bn) {
76
   if((CT::all_zeros(x_words, x_size) & CT::all_zeros(y_words, y_size)).as_bool()) {
3,443,781✔
77
      return;
78
   }
79

80
   if(is_zero()) {
2,155,503✔
81
      m_coord_x.set_words(x_words, x_size);
10,301✔
82
      m_coord_y.set_words(y_words, y_size);
10,301✔
83
      m_coord_z = m_curve.get_1_rep();
10,301✔
84
      return;
10,301✔
85
   }
86

87
   resize_ws(ws_bn, m_curve.get_ws_size());
1,067,457✔
88

89
   secure_vector<word>& ws = ws_bn[0].get_word_vector();
1,067,457✔
90
   secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
1,067,457✔
91

92
   BigInt& T0 = ws_bn[2];
1,067,457✔
93
   BigInt& T1 = ws_bn[3];
1,067,457✔
94
   BigInt& T2 = ws_bn[4];
1,067,457✔
95
   BigInt& T3 = ws_bn[5];
1,067,457✔
96
   BigInt& T4 = ws_bn[6];
1,067,457✔
97

98
   /*
99
   https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
100
   simplified with Z2 = 1
101
   */
102

103
   const BigInt& p = m_curve.get_p();
1,067,457✔
104

105
   m_curve.sqr(T3, m_coord_z, ws);            // z1^2
1,067,457✔
106
   m_curve.mul(T4, x_words, x_size, T3, ws);  // x2*z1^2
1,067,457✔
107

108
   m_curve.mul(T2, m_coord_z, T3, ws);        // z1^3
1,067,457✔
109
   m_curve.mul(T0, y_words, y_size, T2, ws);  // y2*z1^3
1,067,457✔
110

111
   T4.mod_sub(m_coord_x, p, sub_ws);  // x2*z1^2 - x1*z2^2
1,067,457✔
112

113
   T0.mod_sub(m_coord_y, p, sub_ws);
1,067,457✔
114

115
   if(T4.is_zero()) {
2,134,914✔
116
      if(T0.is_zero()) {
32✔
117
         mult2(ws_bn);
×
118
         return;
×
119
      }
120

121
      // setting to zero:
122
      m_coord_x.clear();
16✔
123
      m_coord_y = m_curve.get_1_rep();
16✔
124
      m_coord_z.clear();
16✔
125
      return;
16✔
126
   }
127

128
   m_curve.sqr(T2, T4, ws);
1,067,441✔
129

130
   m_curve.mul(T3, m_coord_x, T2, ws);
1,067,441✔
131

132
   m_curve.mul(T1, T2, T4, ws);
1,067,441✔
133

134
   m_curve.sqr(m_coord_x, T0, ws);
1,067,441✔
135
   m_coord_x.mod_sub(T1, p, sub_ws);
1,067,441✔
136

137
   m_coord_x.mod_sub(T3, p, sub_ws);
1,067,441✔
138
   m_coord_x.mod_sub(T3, p, sub_ws);
1,067,441✔
139

140
   T3.mod_sub(m_coord_x, p, sub_ws);
1,067,441✔
141

142
   m_curve.mul(T2, T0, T3, ws);
1,067,441✔
143
   m_curve.mul(T0, m_coord_y, T1, ws);
1,067,441✔
144
   T2.mod_sub(T0, p, sub_ws);
1,067,441✔
145
   m_coord_y.swap(T2);
1,067,441✔
146

147
   m_curve.mul(T0, m_coord_z, T4, ws);
1,067,441✔
148
   m_coord_z.swap(T0);
1,067,441✔
149
}
150

151
void EC_Point::add(const word x_words[],
1,023,621✔
152
                   size_t x_size,
153
                   const word y_words[],
154
                   size_t y_size,
155
                   const word z_words[],
156
                   size_t z_size,
157
                   std::vector<BigInt>& ws_bn) {
158
   if((CT::all_zeros(x_words, x_size) & CT::all_zeros(z_words, z_size)).as_bool()) {
3,070,863✔
159
      return;
160
   }
161

162
   if(is_zero()) {
2,000,753✔
163
      m_coord_x.set_words(x_words, x_size);
6,524✔
164
      m_coord_y.set_words(y_words, y_size);
6,524✔
165
      m_coord_z.set_words(z_words, z_size);
6,524✔
166
      return;
6,524✔
167
   }
168

169
   resize_ws(ws_bn, m_curve.get_ws_size());
995,801✔
170

171
   secure_vector<word>& ws = ws_bn[0].get_word_vector();
995,801✔
172
   secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
995,801✔
173

174
   BigInt& T0 = ws_bn[2];
995,801✔
175
   BigInt& T1 = ws_bn[3];
995,801✔
176
   BigInt& T2 = ws_bn[4];
995,801✔
177
   BigInt& T3 = ws_bn[5];
995,801✔
178
   BigInt& T4 = ws_bn[6];
995,801✔
179
   BigInt& T5 = ws_bn[7];
995,801✔
180

181
   /*
182
   https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
183
   */
184

185
   const BigInt& p = m_curve.get_p();
995,801✔
186

187
   m_curve.sqr(T0, z_words, z_size, ws);      // z2^2
995,801✔
188
   m_curve.mul(T1, m_coord_x, T0, ws);        // x1*z2^2
995,801✔
189
   m_curve.mul(T3, z_words, z_size, T0, ws);  // z2^3
995,801✔
190
   m_curve.mul(T2, m_coord_y, T3, ws);        // y1*z2^3
995,801✔
191

192
   m_curve.sqr(T3, m_coord_z, ws);            // z1^2
995,801✔
193
   m_curve.mul(T4, x_words, x_size, T3, ws);  // x2*z1^2
995,801✔
194

195
   m_curve.mul(T5, m_coord_z, T3, ws);        // z1^3
995,801✔
196
   m_curve.mul(T0, y_words, y_size, T5, ws);  // y2*z1^3
995,801✔
197

198
   T4.mod_sub(T1, p, sub_ws);  // x2*z1^2 - x1*z2^2
995,801✔
199

200
   T0.mod_sub(T2, p, sub_ws);
995,801✔
201

202
   if(T4.is_zero()) {
1,991,602✔
203
      if(T0.is_zero()) {
548✔
204
         mult2(ws_bn);
30✔
205
         return;
30✔
206
      }
207

208
      // setting to zero:
209
      m_coord_x.clear();
244✔
210
      m_coord_y = m_curve.get_1_rep();
244✔
211
      m_coord_z.clear();
244✔
212
      return;
244✔
213
   }
214

215
   m_curve.sqr(T5, T4, ws);
995,527✔
216

217
   m_curve.mul(T3, T1, T5, ws);
995,527✔
218

219
   m_curve.mul(T1, T5, T4, ws);
995,527✔
220

221
   m_curve.sqr(m_coord_x, T0, ws);
995,527✔
222
   m_coord_x.mod_sub(T1, p, sub_ws);
995,527✔
223
   m_coord_x.mod_sub(T3, p, sub_ws);
995,527✔
224
   m_coord_x.mod_sub(T3, p, sub_ws);
995,527✔
225

226
   T3.mod_sub(m_coord_x, p, sub_ws);
995,527✔
227

228
   m_curve.mul(m_coord_y, T0, T3, ws);
995,527✔
229
   m_curve.mul(T3, T2, T1, ws);
995,527✔
230

231
   m_coord_y.mod_sub(T3, p, sub_ws);
995,527✔
232

233
   m_curve.mul(T3, z_words, z_size, m_coord_z, ws);
995,527✔
234
   m_curve.mul(m_coord_z, T3, T4, ws);
995,527✔
235
}
236

237
void EC_Point::mult2i(size_t iterations, std::vector<BigInt>& ws_bn) {
946,115✔
238
   if(iterations == 0) {
946,115✔
239
      return;
240
   }
241

242
   if(m_coord_y.is_zero()) {
1,892,113✔
243
      *this = EC_Point(m_curve);  // setting myself to zero
×
244
      return;
×
245
   }
246

247
   /*
248
   TODO we can save 2 squarings per iteration by computing
249
   a*Z^4 using values cached from previous iteration
250
   */
251
   for(size_t i = 0; i != iterations; ++i) {
3,497,815✔
252
      mult2(ws_bn);
2,551,700✔
253
   }
254
}
255

256
// *this *= 2
257
void EC_Point::mult2(std::vector<BigInt>& ws_bn) {
3,201,549✔
258
   if(is_zero()) {
6,392,500✔
259
      return;
260
   }
261

262
   if(m_coord_y.is_zero()) {
5,455,736✔
263
      *this = EC_Point(m_curve);  // setting myself to zero
×
264
      return;
×
265
   }
266

267
   resize_ws(ws_bn, m_curve.get_ws_size());
3,200,867✔
268

269
   secure_vector<word>& ws = ws_bn[0].get_word_vector();
3,200,867✔
270
   secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
3,200,867✔
271

272
   BigInt& T0 = ws_bn[2];
3,200,867✔
273
   BigInt& T1 = ws_bn[3];
3,200,867✔
274
   BigInt& T2 = ws_bn[4];
3,200,867✔
275
   BigInt& T3 = ws_bn[5];
3,200,867✔
276
   BigInt& T4 = ws_bn[6];
3,200,867✔
277

278
   /*
279
   https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc
280
   */
281
   const BigInt& p = m_curve.get_p();
3,200,867✔
282

283
   m_curve.sqr(T0, m_coord_y, ws);
3,200,867✔
284

285
   m_curve.mul(T1, m_coord_x, T0, ws);
3,200,867✔
286
   T1.mod_mul(4, p, sub_ws);
3,200,867✔
287

288
   if(m_curve.a_is_zero()) {
3,200,867✔
289
      // if a == 0 then 3*x^2 + a*z^4 is just 3*x^2
290
      m_curve.sqr(T4, m_coord_x, ws);  // x^2
442,500✔
291
      T4.mod_mul(3, p, sub_ws);        // 3*x^2
442,500✔
292
   } else if(m_curve.a_is_minus_3()) {
2,758,367✔
293
      /*
294
      if a == -3 then
295
        3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2)
296
      */
297
      m_curve.sqr(T3, m_coord_z, ws);  // z^2
1,899,773✔
298

299
      // (x-z^2)
300
      T2 = m_coord_x;
1,899,773✔
301
      T2.mod_sub(T3, p, sub_ws);
1,899,773✔
302

303
      // (x+z^2)
304
      T3.mod_add(m_coord_x, p, sub_ws);
1,899,773✔
305

306
      m_curve.mul(T4, T2, T3, ws);  // (x-z^2)*(x+z^2)
1,899,773✔
307

308
      T4.mod_mul(3, p, sub_ws);  // 3*(x-z^2)*(x+z^2)
1,899,773✔
309
   } else {
310
      m_curve.sqr(T3, m_coord_z, ws);                // z^2
858,594✔
311
      m_curve.sqr(T4, T3, ws);                       // z^4
858,594✔
312
      m_curve.mul(T3, m_curve.get_a_rep(), T4, ws);  // a*z^4
858,594✔
313

314
      m_curve.sqr(T4, m_coord_x, ws);  // x^2
858,594✔
315
      T4.mod_mul(3, p, sub_ws);
858,594✔
316
      T4.mod_add(T3, p, sub_ws);  // 3*x^2 + a*z^4
858,594✔
317
   }
318

319
   m_curve.sqr(T2, T4, ws);
3,200,867✔
320
   T2.mod_sub(T1, p, sub_ws);
3,200,867✔
321
   T2.mod_sub(T1, p, sub_ws);
3,200,867✔
322

323
   m_curve.sqr(T3, T0, ws);
3,200,867✔
324
   T3.mod_mul(8, p, sub_ws);
3,200,867✔
325

326
   T1.mod_sub(T2, p, sub_ws);
3,200,867✔
327

328
   m_curve.mul(T0, T4, T1, ws);
3,200,867✔
329
   T0.mod_sub(T3, p, sub_ws);
3,200,867✔
330

331
   m_coord_x.swap(T2);
3,200,867✔
332

333
   m_curve.mul(T2, m_coord_y, m_coord_z, ws);
3,200,867✔
334
   T2.mod_mul(2, p, sub_ws);
3,200,867✔
335

336
   m_coord_y.swap(T0);
3,200,867✔
337
   m_coord_z.swap(T2);
3,200,867✔
338
}
339

340
// arithmetic operators
341
EC_Point& EC_Point::operator+=(const EC_Point& rhs) {
2,337✔
342
   std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
2,337✔
343
   add(rhs, ws);
2,337✔
344
   return *this;
2,336✔
345
}
2,337✔
346

347
EC_Point& EC_Point::operator-=(const EC_Point& rhs) {
85✔
348
   EC_Point minus_rhs = EC_Point(rhs).negate();
85✔
349

350
   if(is_zero()) {
85✔
351
      *this = minus_rhs;
×
352
   } else {
353
      *this += minus_rhs;
85✔
354
   }
355

356
   return *this;
85✔
357
}
85✔
358

359
EC_Point& EC_Point::operator*=(const BigInt& scalar) {
67✔
360
   *this = scalar * *this;
67✔
361
   return *this;
67✔
362
}
363

364
EC_Point EC_Point::mul(const BigInt& scalar) const {
2,607✔
365
   const size_t scalar_bits = scalar.bits();
2,607✔
366

367
   std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
2,607✔
368

369
   EC_Point R[2] = {this->zero(), *this};
7,821✔
370

371
   for(size_t i = scalar_bits; i > 0; i--) {
542,457✔
372
      const size_t b = scalar.get_bit(i - 1);
539,850✔
373
      R[b ^ 1].add(R[b], ws);
539,850✔
374
      R[b].mult2(ws);
539,850✔
375
   }
376

377
   if(scalar.is_negative()) {
2,607✔
378
      R[0].negate();
×
379
   }
380

381
   BOTAN_DEBUG_ASSERT(R[0].on_the_curve());
2,607✔
382

383
   return R[0];
5,214✔
384
}
10,428✔
385

386
//static
387
void EC_Point::force_all_affine(std::span<EC_Point> points, secure_vector<word>& ws) {
1,423✔
388
   if(points.size() <= 1) {
1,423✔
389
      for(auto& point : points) {
×
390
         point.force_affine();
×
391
      }
392
      return;
×
393
   }
394

395
   for(auto& point : points) {
206,161✔
396
      if(point.is_zero()) {
391,696✔
397
         throw Invalid_State("Cannot convert zero ECC point to affine");
×
398
      }
399
   }
400

401
   /*
402
   For >= 2 points use Montgomery's trick
403

404
   See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography"
405
   (Hankerson, Menezes, Vanstone)
406

407
   TODO is it really necessary to save all k points in c?
408
   */
409

410
   const CurveGFp& curve = points[0].m_curve;
1,423✔
411
   const BigInt& rep_1 = curve.get_1_rep();
1,423✔
412

413
   if(ws.size() < curve.get_ws_size()) {
1,423✔
414
      ws.resize(curve.get_ws_size());
×
415
   }
416

417
   std::vector<BigInt> c(points.size());
1,423✔
418
   c[0] = points[0].m_coord_z;
1,423✔
419

420
   for(size_t i = 1; i != points.size(); ++i) {
204,738✔
421
      curve.mul(c[i], c[i - 1], points[i].m_coord_z, ws);
406,630✔
422
   }
423

424
   BigInt s_inv = curve.invert_element(c[c.size() - 1], ws);
1,423✔
425

426
   BigInt z_inv, z2_inv, z3_inv;
1,423✔
427

428
   for(size_t i = points.size() - 1; i != 0; i--) {
204,738✔
429
      EC_Point& point = points[i];
203,315✔
430

431
      curve.mul(z_inv, s_inv, c[i - 1], ws);
203,315✔
432

433
      s_inv = curve.mul_to_tmp(s_inv, point.m_coord_z, ws);
203,315✔
434

435
      curve.sqr(z2_inv, z_inv, ws);
203,315✔
436
      curve.mul(z3_inv, z2_inv, z_inv, ws);
203,315✔
437
      point.m_coord_x = curve.mul_to_tmp(point.m_coord_x, z2_inv, ws);
203,315✔
438
      point.m_coord_y = curve.mul_to_tmp(point.m_coord_y, z3_inv, ws);
203,315✔
439
      point.m_coord_z = rep_1;
203,315✔
440
   }
441

442
   curve.sqr(z2_inv, s_inv, ws);
1,423✔
443
   curve.mul(z3_inv, z2_inv, s_inv, ws);
1,423✔
444
   points[0].m_coord_x = curve.mul_to_tmp(points[0].m_coord_x, z2_inv, ws);
1,423✔
445
   points[0].m_coord_y = curve.mul_to_tmp(points[0].m_coord_y, z3_inv, ws);
1,423✔
446
   points[0].m_coord_z = rep_1;
1,423✔
447
}
5,692✔
448

449
void EC_Point::force_affine() {
16,214✔
450
   if(is_zero()) {
16,214✔
451
      throw Invalid_State("Cannot convert zero ECC point to affine");
×
452
   }
453

454
   secure_vector<word> ws;
16,214✔
455

456
   const BigInt z_inv = m_curve.invert_element(m_coord_z, ws);
16,214✔
457
   const BigInt z2_inv = m_curve.sqr_to_tmp(z_inv, ws);
16,214✔
458
   const BigInt z3_inv = m_curve.mul_to_tmp(z_inv, z2_inv, ws);
16,214✔
459
   m_coord_x = m_curve.mul_to_tmp(m_coord_x, z2_inv, ws);
16,214✔
460
   m_coord_y = m_curve.mul_to_tmp(m_coord_y, z3_inv, ws);
16,214✔
461
   m_coord_z = m_curve.get_1_rep();
16,214✔
462
}
64,856✔
463

464
bool EC_Point::is_affine() const {
105,325✔
465
   return m_curve.is_one(m_coord_z);
105,325✔
466
}
467

468
secure_vector<uint8_t> EC_Point::x_bytes() const {
×
469
   const size_t p_bytes = m_curve.get_p_bytes();
×
470
   secure_vector<uint8_t> b(p_bytes);
×
471
   BigInt::encode_1363(b.data(), b.size(), this->get_affine_x());
×
472
   return b;
×
473
}
×
474

475
secure_vector<uint8_t> EC_Point::y_bytes() const {
×
476
   const size_t p_bytes = m_curve.get_p_bytes();
×
477
   secure_vector<uint8_t> b(p_bytes);
×
478
   BigInt::encode_1363(b.data(), b.size(), this->get_affine_y());
×
479
   return b;
×
480
}
×
481

482
secure_vector<uint8_t> EC_Point::xy_bytes() const {
14,829✔
483
   const size_t p_bytes = m_curve.get_p_bytes();
14,829✔
484
   secure_vector<uint8_t> b(2 * p_bytes);
14,829✔
485
   BigInt::encode_1363(&b[0], p_bytes, this->get_affine_x());
14,829✔
486
   BigInt::encode_1363(&b[p_bytes], p_bytes, this->get_affine_y());
14,829✔
487
   return b;
14,829✔
488
}
×
489

490
BigInt EC_Point::get_affine_x() const {
53,502✔
491
   if(is_zero()) {
56,847✔
492
      throw Invalid_State("Cannot convert zero point to affine");
28✔
493
   }
494

495
   secure_vector<word> monty_ws;
53,474✔
496

497
   if(is_affine()) {
53,474✔
498
      return m_curve.from_rep_to_tmp(m_coord_x, monty_ws);
44,340✔
499
   }
500

501
   BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
9,134✔
502
   z2 = m_curve.invert_element(z2, monty_ws);
9,134✔
503

504
   BigInt r;
9,134✔
505
   m_curve.mul(r, m_coord_x, z2, monty_ws);
9,134✔
506
   m_curve.from_rep(r, monty_ws);
9,134✔
507
   return r;
9,134✔
508
}
71,742✔
509

510
BigInt EC_Point::get_affine_y() const {
51,879✔
511
   if(is_zero()) {
51,879✔
512
      throw Invalid_State("Cannot convert zero point to affine");
28✔
513
   }
514

515
   secure_vector<word> monty_ws;
51,851✔
516

517
   if(is_affine()) {
51,851✔
518
      return m_curve.from_rep_to_tmp(m_coord_y, monty_ws);
44,338✔
519
   }
520

521
   const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
7,513✔
522
   const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
7,513✔
523
   const BigInt z3_inv = m_curve.invert_element(z3, monty_ws);
7,513✔
524

525
   BigInt r;
7,513✔
526
   m_curve.mul(r, m_coord_y, z3_inv, monty_ws);
7,513✔
527
   m_curve.from_rep(r, monty_ws);
7,513✔
528
   return r;
7,513✔
529
}
81,903✔
530

531
bool EC_Point::on_the_curve() const {
13,838✔
532
   /*
533
   Is the point still on the curve?? (If everything is correct, the
534
   point is always on its curve; then the function will return true.
535
   If somehow the state is corrupted, which suggests a fault attack
536
   (or internal computational error), then return false.
537
   */
538
   if(is_zero()) {
19,203✔
539
      return true;
540
   }
541

542
   secure_vector<word> monty_ws;
13,782✔
543

544
   const BigInt y2 = m_curve.from_rep_to_tmp(m_curve.sqr_to_tmp(m_coord_y, monty_ws), monty_ws);
13,782✔
545
   const BigInt x3 = m_curve.mul_to_tmp(m_coord_x, m_curve.sqr_to_tmp(m_coord_x, monty_ws), monty_ws);
13,782✔
546
   const BigInt ax = m_curve.mul_to_tmp(m_coord_x, m_curve.get_a_rep(), monty_ws);
13,782✔
547
   const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
13,782✔
548

549
   // Is z equal to 1 (in Montgomery form)?
550
   if(m_coord_z == z2) {
13,782✔
551
      if(y2 != m_curve.from_rep_to_tmp(x3 + ax + m_curve.get_b_rep(), monty_ws)) {
44,810✔
552
         return false;
553
      }
554
   }
555

556
   const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
13,714✔
557
   const BigInt ax_z4 = m_curve.mul_to_tmp(ax, m_curve.sqr_to_tmp(z2, monty_ws), monty_ws);
13,714✔
558
   const BigInt b_z6 = m_curve.mul_to_tmp(m_curve.get_b_rep(), m_curve.sqr_to_tmp(z3, monty_ws), monty_ws);
13,714✔
559

560
   if(y2 != m_curve.from_rep_to_tmp(x3 + ax_z4 + b_z6, monty_ws)) {
68,570✔
561
      return false;
562
   }
563

564
   return true;
565
}
110,052✔
566

567
// swaps the states of *this and other
568
void EC_Point::swap(EC_Point& other) noexcept {
270,219✔
569
   m_curve.swap(other.m_curve);
270,219✔
570
   m_coord_x.swap(other.m_coord_x);
270,219✔
571
   m_coord_y.swap(other.m_coord_y);
270,219✔
572
   m_coord_z.swap(other.m_coord_z);
270,219✔
573
}
270,219✔
574

575
bool EC_Point::operator==(const EC_Point& other) const {
7,505✔
576
   if(m_curve != other.m_curve) {
7,505✔
577
      return false;
578
   }
579

580
   // If this is zero, only equal if other is also zero
581
   if(is_zero()) {
11,644✔
582
      return other.is_zero();
168✔
583
   }
584

585
   return (get_affine_x() == other.get_affine_x() && get_affine_y() == other.get_affine_y());
36,683✔
586
}
587

588
// encoding and decoding
589
std::vector<uint8_t> EC_Point::encode(EC_Point_Format format) const {
22,309✔
590
   if(is_zero()) {
31,597✔
591
      return std::vector<uint8_t>(1);  // single 0 byte
168✔
592
   }
593

594
   const size_t p_bytes = m_curve.get_p().bytes();
22,141✔
595

596
   const BigInt x = get_affine_x();
22,141✔
597
   const BigInt y = get_affine_y();
22,141✔
598

599
   const size_t parts = (format == EC_Point_Format::Compressed) ? 1 : 2;
22,141✔
600

601
   std::vector<uint8_t> result(1 + parts * p_bytes);
22,141✔
602
   BufferStuffer stuffer(result);
22,141✔
603

604
   if(format == EC_Point_Format::Uncompressed) {
22,141✔
605
      stuffer.append(0x04);
21,927✔
606
      x.serialize_to(stuffer.next(p_bytes));
21,927✔
607
      y.serialize_to(stuffer.next(p_bytes));
21,927✔
608
   } else if(format == EC_Point_Format::Compressed) {
214✔
609
      stuffer.append(0x02 | static_cast<uint8_t>(y.get_bit(0)));
242✔
610
      x.serialize_to(stuffer.next(p_bytes));
121✔
611
   } else if(format == EC_Point_Format::Hybrid) {
93✔
612
      stuffer.append(0x06 | static_cast<uint8_t>(y.get_bit(0)));
186✔
613
      x.serialize_to(stuffer.next(p_bytes));
93✔
614
      y.serialize_to(stuffer.next(p_bytes));
93✔
615
   } else {
616
      throw Invalid_Argument("EC2OSP illegal point encoding");
×
617
   }
618

619
   return result;
22,141✔
620
}
66,423✔
621

622
namespace {
623

624
BigInt decompress_point(
346✔
625
   bool yMod2, const BigInt& x, const BigInt& curve_p, const BigInt& curve_a, const BigInt& curve_b) {
626
   BigInt xpow3 = x * x * x;
346✔
627

628
   BigInt g = curve_a * x;
346✔
629
   g += xpow3;
346✔
630
   g += curve_b;
346✔
631
   g = g % curve_p;
352✔
632

633
   BigInt z = sqrt_modulo_prime(g, curve_p);
346✔
634

635
   if(z < 0) {
346✔
636
      throw Decoding_Error("Error during EC point decompression");
6✔
637
   }
638

639
   if(z.get_bit(0) != yMod2) {
680✔
640
      z = curve_p - z;
378✔
641
   }
642

643
   return z;
340✔
644
}
692✔
645

646
}  // namespace
647

648
EC_Point OS2ECP(const uint8_t data[], size_t data_len, const CurveGFp& curve) {
6,590✔
649
   // Should we really be doing this?
650
   if(data_len <= 1) {
6,590✔
651
      return EC_Point(curve);  // return zero
128✔
652
   }
653

654
   std::pair<BigInt, BigInt> xy = OS2ECP(data, data_len, curve.get_p(), curve.get_a(), curve.get_b());
6,462✔
655

656
   EC_Point point(curve, xy.first, xy.second);
12,884✔
657

658
   if(!point.on_the_curve()) {
6,438✔
659
      throw Decoding_Error("OS2ECP: Decoded point was not on the curve");
68✔
660
   }
661

662
   return point;
6,370✔
663
}
6,510✔
664

665
std::pair<BigInt, BigInt> OS2ECP(
6,518✔
666
   const uint8_t data[], size_t data_len, const BigInt& curve_p, const BigInt& curve_a, const BigInt& curve_b) {
667
   if(data_len <= 1) {
6,518✔
668
      throw Decoding_Error("OS2ECP invalid point");
×
669
   }
670

671
   const uint8_t pc = data[0];
6,518✔
672

673
   BigInt x, y;
6,518✔
674

675
   if(pc == 2 || pc == 3) {
6,518✔
676
      //compressed form
677
      x = BigInt::decode(&data[1], data_len - 1);
235✔
678

679
      const bool y_mod_2 = ((pc & 0x01) == 1);
235✔
680
      y = decompress_point(y_mod_2, x, curve_p, curve_a, curve_b);
235✔
681
   } else if(pc == 4) {
6,283✔
682
      const size_t l = (data_len - 1) / 2;
6,169✔
683

684
      // uncompressed form
685
      x = BigInt::decode(&data[1], l);
6,169✔
686
      y = BigInt::decode(&data[l + 1], l);
6,169✔
687
   } else if(pc == 6 || pc == 7) {
114✔
688
      const size_t l = (data_len - 1) / 2;
111✔
689

690
      // hybrid form
691
      x = BigInt::decode(&data[1], l);
111✔
692
      y = BigInt::decode(&data[l + 1], l);
111✔
693

694
      const bool y_mod_2 = ((pc & 0x01) == 1);
111✔
695

696
      if(decompress_point(y_mod_2, x, curve_p, curve_a, curve_b) != y) {
341✔
697
         throw Decoding_Error("OS2ECP: Decoding error in hybrid format");
11✔
698
      }
699
   } else {
700
      throw Invalid_Argument("OS2ECP: Unknown format type " + std::to_string(pc));
9✔
701
   }
702

703
   return std::make_pair(x, y);
6,498✔
704
}
13,016✔
705

706
EC_Point OS2ECP(std::span<const uint8_t> data, const CurveGFp& curve) {
×
707
   return OS2ECP(data.data(), data.size(), curve);
×
708
}
709

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

© 2025 Coveralls, Inc