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

randombit / botan / 13021208024

28 Jan 2025 11:07PM UTC coverage: 91.241% (-0.02%) from 91.258%
13021208024

push

github

web-flow
Merge pull request #4604 from randombit/jack/support-minimal-curves

Avoid requiring legacy_ec_group in order to run the tests

94137 of 103174 relevant lines covered (91.24%)

11291073.45 hits per line

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

94.78
/src/tests/test_ecc_pointmul.cpp
1
/*
2
* (C) 2014,2015,2019 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "tests.h"
8

9
#if defined(BOTAN_HAS_ECC_GROUP)
10
   #include <botan/bigint.h>
11
   #include <botan/ec_group.h>
12
   #include <botan/internal/fmt.h>
13
#endif
14

15
namespace Botan_Tests {
16

17
namespace {
18

19
#if defined(BOTAN_HAS_ECC_GROUP)
20

21
class ECC_Basepoint_Mul_Tests final : public Text_Based_Test {
×
22
   public:
23
      ECC_Basepoint_Mul_Tests() : Text_Based_Test("pubkey/ecc_base_point_mul.vec", "k,P") {}
2✔
24

25
      bool skip_this_test(const std::string& group_id, const VarMap&) override {
535✔
26
         return !Botan::EC_Group::supports_named_group(group_id);
535✔
27
      }
28

29
      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override {
535✔
30
         Test::Result result("ECC base point multiply " + group_id);
535✔
31

32
         const auto k_bytes = vars.get_req_bin("k");
535✔
33
         const auto P_bytes = vars.get_req_bin("P");
535✔
34

35
         const auto group = Botan::EC_Group::from_name(group_id);
535✔
36

37
         const Botan::BigInt k(k_bytes);
535✔
38
         std::vector<Botan::BigInt> ws;
535✔
39

40
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
41
         const auto pt = group.OS2ECP(P_bytes);
535✔
42
         const Botan::EC_Point p1 = group.get_base_point() * k;
535✔
43
         result.test_eq("EC_Point Montgomery ladder", p1.encode(Botan::EC_Point_Format::Uncompressed), P_bytes);
1,070✔
44
   #endif
45

46
         const auto scalar = Botan::EC_Scalar::from_bigint(group, k);
535✔
47
         const auto apg = Botan::EC_AffinePoint::g_mul(scalar, this->rng(), ws);
535✔
48
         result.test_eq("AffinePoint::g_mul", apg.serialize_uncompressed(), P_bytes);
1,070✔
49

50
         const auto ag = Botan::EC_AffinePoint::generator(group);
535✔
51
         const auto ap = ag.mul(scalar, this->rng(), ws);
535✔
52
         result.test_eq("AffinePoint::mul", ap.serialize_uncompressed(), P_bytes);
1,070✔
53

54
         return result;
1,070✔
55
      }
2,140✔
56
};
57

58
BOTAN_REGISTER_TEST("pubkey", "ecc_basemul", ECC_Basepoint_Mul_Tests);
59

60
class ECC_Varpoint_Mul_Tests final : public Text_Based_Test {
×
61
   public:
62
      ECC_Varpoint_Mul_Tests() : Text_Based_Test("pubkey/ecc_var_point_mul.vec", "P,k,Z") {}
2✔
63

64
      bool skip_this_test(const std::string& group_id, const VarMap&) override {
829✔
65
         return !Botan::EC_Group::supports_named_group(group_id);
829✔
66
      }
67

68
      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override {
829✔
69
         Test::Result result("ECC var point multiply " + group_id);
829✔
70

71
         const auto p = vars.get_req_bin("P");
829✔
72
         const Botan::BigInt k = vars.get_req_bn("k");
829✔
73
         const auto z = vars.get_req_bin("Z");
829✔
74

75
         const auto group = Botan::EC_Group::from_name(group_id);
829✔
76

77
         std::vector<Botan::BigInt> ws;
829✔
78

79
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
80
         const Botan::EC_Point p1 = group.OS2ECP(p) * k;
1,658✔
81
         result.test_eq("EC_Point Montgomery ladder", p1.encode(Botan::EC_Point::Compressed), z);
1,658✔
82
         result.confirm("Output point is on the curve", p1.on_the_curve());
1,658✔
83
   #endif
84

85
         const auto s_k = Botan::EC_Scalar::from_bigint(group, k);
829✔
86
         const auto apt = Botan::EC_AffinePoint::deserialize(group, p).value();
1,658✔
87
         const auto apt_k = apt.mul(s_k, this->rng(), ws);
829✔
88
         result.test_eq("p * k (AffinePoint)", apt_k.serialize_compressed(), z);
1,658✔
89

90
         const auto apt_k_neg = apt.negate().mul(s_k.negate(), this->rng(), ws);
829✔
91
         result.test_eq("-p * -k (AffinePoint)", apt_k_neg.serialize_compressed(), z);
1,658✔
92

93
         const auto neg_apt_neg_k = apt.mul(s_k.negate(), this->rng(), ws).negate();
829✔
94
         result.test_eq("-(p * -k) (AffinePoint)", neg_apt_neg_k.serialize_compressed(), z);
1,658✔
95

96
         return result;
1,658✔
97
      }
3,316✔
98
};
99

100
BOTAN_REGISTER_TEST("pubkey", "ecc_varmul", ECC_Varpoint_Mul_Tests);
101

102
class ECC_Mul2_Tests final : public Text_Based_Test {
×
103
   public:
104
      ECC_Mul2_Tests() : Text_Based_Test("pubkey/ecc_var_point_mul2.vec", "P,x,Q,y,Z") {}
2✔
105

106
      bool skip_this_test(const std::string& group_id, const VarMap&) override {
819✔
107
         return !Botan::EC_Group::supports_named_group(group_id);
819✔
108
      }
109

110
      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override {
819✔
111
         Test::Result result("ECC mul2 " + group_id);
819✔
112

113
         const auto Z_bytes = vars.get_req_bin("Z");
819✔
114

115
         const auto check_px_qy = [&](const char* what,
4,095✔
116
                                      const Botan::EC_AffinePoint& p,
117
                                      const Botan::EC_Scalar& x,
118
                                      const Botan::EC_AffinePoint& q,
119
                                      const Botan::EC_Scalar& y,
120
                                      bool with_final_negation = false) {
121
            if(const auto z = Botan::EC_AffinePoint::mul_px_qy(p, x, q, y, rng())) {
3,276✔
122
               if(with_final_negation) {
3,276✔
123
                  result.test_eq(what, z->negate().serialize_compressed(), Z_bytes);
4,914✔
124
               } else {
125
                  result.test_eq(what, z->serialize_compressed(), Z_bytes);
4,914✔
126
               }
127
            } else {
128
               result.test_failure("EC_AffinePoint::mul_px_qy failed to produce a result");
×
129
            }
×
130

131
            // Now check the same using naive multiply and add:
132
            std::vector<BigInt> ws;
3,276✔
133
            auto z = p.mul(x, rng(), ws).add(q.mul(y, rng(), ws));
3,276✔
134
            if(with_final_negation) {
3,276✔
135
               z = z.negate();
1,638✔
136
            }
137
            result.test_eq("p*x + q*y naive", z.serialize_compressed(), Z_bytes);
6,552✔
138
         };
3,276✔
139

140
         const auto group = Botan::EC_Group::from_name(group_id);
819✔
141

142
         const auto p = Botan::EC_AffinePoint::deserialize(group, vars.get_req_bin("P")).value();
3,276✔
143
         const auto q = Botan::EC_AffinePoint::deserialize(group, vars.get_req_bin("Q")).value();
3,276✔
144
         const auto x = Botan::EC_Scalar::from_bigint(group, vars.get_req_bn("x"));
1,638✔
145
         const auto y = Botan::EC_Scalar::from_bigint(group, vars.get_req_bn("y"));
1,638✔
146

147
         const auto np = p.negate();
819✔
148
         const auto nq = q.negate();
819✔
149
         const auto nx = x.negate();
819✔
150
         const auto ny = y.negate();
819✔
151

152
         check_px_qy("p*x + q*y", p, x, q, y);
819✔
153
         check_px_qy("-p*-x + -q*-y", np, nx, nq, ny);
819✔
154
         check_px_qy("-(p*-x + q*-y)", p, nx, q, ny, true);
819✔
155
         check_px_qy("-(-p*x + -q*y)", np, x, nq, y, true);
819✔
156

157
         return result;
1,638✔
158
      }
1,638✔
159
};
160

161
BOTAN_REGISTER_TEST("pubkey", "ecc_mul2", ECC_Mul2_Tests);
162

163
class ECC_Mul2_Inf_Tests final : public Test {
×
164
   public:
165
      std::vector<Test::Result> run() override {
1✔
166
         std::vector<Test::Result> results;
1✔
167

168
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
29✔
169
            Test::Result result("ECC mul2 inf " + group_id);
28✔
170

171
            const auto check_px_qy = [&](const char* what,
224✔
172
                                         const Botan::EC_AffinePoint& p,
173
                                         const Botan::EC_Scalar& x,
174
                                         const Botan::EC_AffinePoint& q,
175
                                         const Botan::EC_Scalar& y) {
176
               if(const auto z = Botan::EC_AffinePoint::mul_px_qy(p, x, q, y, rng())) {
196✔
177
                  result.test_failure(Botan::fmt("EC_AffinePoint::mul_px_qy {} unexpectedly produced a result", what));
×
178
               } else {
179
                  result.test_success(Botan::fmt("EC_AffinePoint::mul_px_qy {} returned nullopt as expected", what));
392✔
180
               }
196✔
181
            };
196✔
182

183
            const auto group = Botan::EC_Group::from_name(group_id);
28✔
184

185
            const auto g = Botan::EC_AffinePoint::generator(group);
28✔
186

187
            // Choose some other random point z
188
            std::vector<Botan::BigInt> ws;
28✔
189
            const auto z = g.mul(Botan::EC_Scalar::random(group, rng()), rng(), ws);
28✔
190

191
            const auto r = Botan::EC_Scalar::random(group, rng());
28✔
192
            const auto neg_r = r.negate();
28✔
193
            const auto neg_r2 = neg_r + neg_r;
28✔
194

195
            const auto zero = r - r;
28✔
196
            result.confirm("Computed EC_Scalar is zero", zero.is_zero());
56✔
197

198
            const auto g2 = g.add(g);
28✔
199

200
            const auto id = Botan::EC_AffinePoint::identity(group);
28✔
201

202
            check_px_qy("0*g + r*id", g, zero, id, r);
28✔
203
            check_px_qy("0*id + r*id", id, zero, id, r);
28✔
204
            check_px_qy("0*g + 0*z", g, zero, z, zero);
28✔
205
            check_px_qy("r*g + -r*g", g, r, g, neg_r);
28✔
206
            check_px_qy("-r*g + r*g", g, neg_r, g, r);
28✔
207
            check_px_qy("r*g + r*-g", g, r, g.negate(), r);
28✔
208
            check_px_qy("r*g2 + -r2*g", g2, r, g, neg_r2);
28✔
209

210
            results.push_back(result);
28✔
211
         }
28✔
212

213
         return results;
1✔
214
      }
×
215
};
216

217
BOTAN_REGISTER_TEST("pubkey", "ecc_mul2_inf", ECC_Mul2_Inf_Tests);
218

219
class ECC_Point_Addition_Tests final : public Test {
×
220
   public:
221
      std::vector<Test::Result> run() override {
1✔
222
         std::vector<Test::Result> results;
1✔
223

224
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
29✔
225
            Test::Result result("ECC addition " + group_id);
28✔
226

227
            const auto group = Botan::EC_Group::from_name(group_id);
28✔
228

229
            const auto g = Botan::EC_AffinePoint::generator(group);
28✔
230
            result.test_eq("g is not the identity element", g.is_identity(), false);
28✔
231

232
            // Choose some other random point z
233
            std::vector<Botan::BigInt> ws;
28✔
234
            const auto z = g.mul(Botan::EC_Scalar::random(group, rng()), rng(), ws);
28✔
235
            result.test_eq("z is not the identity element", z.is_identity(), false);
28✔
236

237
            const auto id = Botan::EC_AffinePoint::identity(group);
28✔
238
            result.test_eq("id is the identity element", id.is_identity(), true);
28✔
239

240
            const auto g_bytes = g.serialize_uncompressed();
28✔
241

242
            auto check_expr_is_g = [&](const char* msg, const Botan::EC_AffinePoint& pt) {
308✔
243
               result.test_eq(Botan::fmt("{} is g", msg), pt.serialize_uncompressed(), g_bytes);
560✔
244
            };
280✔
245

246
            const auto nz = z.negate();
28✔
247

248
            check_expr_is_g("g + id", g.add(id));
28✔
249
            check_expr_is_g("id + g", id.add(g));
28✔
250
            check_expr_is_g("g + id", g.add(id));
28✔
251
            check_expr_is_g("g + -id", g.add(id.negate()));
28✔
252
            check_expr_is_g("g + g + -g", g.add(g).add(g.negate()));
28✔
253
            check_expr_is_g("-id + g", id.negate().add(g));
28✔
254
            check_expr_is_g("z + g - z", z.add(g).add(nz));
28✔
255
            check_expr_is_g("z - z + g", z.add(nz).add(g));
28✔
256
            check_expr_is_g("z + z + g - z - z", z.add(z).add(g).add(nz).add(nz));
28✔
257
            check_expr_is_g("z + id + g + z - z - z", z.add(id).add(g).add(z).add(nz).add(nz));
28✔
258

259
            results.push_back(result);
28✔
260
         }
56✔
261

262
         return results;
1✔
263
      }
×
264
};
265

266
BOTAN_REGISTER_TEST("pubkey", "ecc_pt_addition", ECC_Point_Addition_Tests);
267

268
class ECC_Scalar_Arithmetic_Tests final : public Test {
×
269
   public:
270
      std::vector<Test::Result> run() override {
1✔
271
         std::vector<Test::Result> results;
1✔
272

273
         auto& rng = Test::rng();
1✔
274

275
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
29✔
276
            const auto group = Botan::EC_Group::from_name(group_id);
28✔
277

278
            Test::Result result("ECC scalar arithmetic " + group_id);
28✔
279
            test_scalar_arith(result, group, rng);
28✔
280
            results.push_back(result);
28✔
281
         }
28✔
282
         return results;
1✔
283
      }
×
284

285
   private:
286
      void test_scalar_arith(Test::Result& result,
28✔
287
                             const Botan::EC_Group& group,
288
                             Botan::RandomNumberGenerator& rng) const {
289
         const auto one = Botan::EC_Scalar::one(group);
28✔
290
         const auto zero = one - one;
28✔
291
         const auto two = one + one;
28✔
292

293
         const size_t order_bytes = group.get_order_bytes();
28✔
294

295
         const auto ser_zero = std::vector<uint8_t>(order_bytes);
28✔
296

297
         const auto ser_one = [=]() {
56✔
298
            auto b = ser_zero;
28✔
299
            BOTAN_ASSERT_NOMSG(b.size() > 1);
28✔
300
            b[b.size() - 1] = 1;
28✔
301
            return b;
28✔
302
         }();
28✔
303

304
         result.test_eq("Serialization of zero is expected value", zero.serialize(), ser_zero);
56✔
305
         result.test_eq("Serialization of one is expected value", one.serialize(), ser_one);
56✔
306

307
         result.test_eq("Zero is zero", zero.is_zero(), true);
28✔
308
         result.test_eq("Negation of zero is zero", zero.negate().is_zero(), true);
56✔
309
         result.test_eq("One is not zero", one.is_zero(), false);
28✔
310

311
         // Zero inverse is not mathematically correct, but works out for our purposes
312
         result.test_eq("Inverse of zero is zero", zero.invert().serialize(), ser_zero);
84✔
313
         result.test_eq("Inverse of one is one", one.invert().serialize(), ser_one);
84✔
314

315
         constexpr size_t test_iter = 128;
28✔
316

317
         for(size_t i = 0; i != test_iter; ++i) {
3,612✔
318
            const auto r = Botan::EC_Scalar::random(group, rng);
3,584✔
319

320
            // Negation and addition are inverses
321
            result.test_eq("r + -r == 0", (r + r.negate()).serialize(), ser_zero);
14,336✔
322

323
            // Serialization and deserialization are inverses
324
            const auto r_bytes = r.serialize();
3,584✔
325
            result.test_eq("Deserialization of r round trips",
10,752✔
326
                           Botan::EC_Scalar::deserialize(group, r_bytes).value().serialize(),
10,752✔
327
                           r_bytes);
328

329
            // Multiplication and inversion are inverses
330
            const auto r2 = r * r;
3,584✔
331
            const auto r_inv = r.invert();
3,584✔
332
            result.test_eq("r * r^-1 = 1", (r * r_inv).serialize(), ser_one);
10,752✔
333
         }
7,168✔
334

335
         for(size_t i = 0; i != test_iter; ++i) {
3,612✔
336
            const auto a = Botan::EC_Scalar::random(group, rng);
3,584✔
337
            const auto b = Botan::EC_Scalar::random(group, rng);
3,584✔
338

339
            const auto ab = a * b;
3,584✔
340
            const auto a_inv = a.invert();
3,584✔
341
            const auto b_inv = b.invert();
3,584✔
342

343
            result.test_eq("a * b / b = a", (ab * b_inv).serialize(), a.serialize());
10,752✔
344
            result.test_eq("a * b / a = b", (ab * a_inv).serialize(), b.serialize());
10,752✔
345

346
            auto a_plus_b = a + b;
3,584✔
347
            result.test_eq("(a + b) - b == a", (a_plus_b - b).serialize(), a.serialize());
10,752✔
348
            result.test_eq("(a + b) - a == b", (a_plus_b - a).serialize(), b.serialize());
10,752✔
349
            result.test_eq("b - (a + b) == -a", (b - a_plus_b).serialize(), a.negate().serialize());
14,336✔
350
            result.test_eq("a - (a + b) == -b", (a - a_plus_b).serialize(), b.negate().serialize());
14,336✔
351
         }
3,584✔
352

353
         for(size_t i = 0; i != test_iter; ++i) {
3,612✔
354
            const auto a = Botan::EC_Scalar::random(group, rng);
3,584✔
355
            const auto b = Botan::EC_Scalar::random(group, rng);
3,584✔
356
            const auto c = Botan::EC_Scalar::random(group, rng);
3,584✔
357

358
            const auto ab_c = (a + b) * c;
3,584✔
359
            const auto ac_bc = a * c + b * c;
3,584✔
360

361
            result.test_eq("(a + b)*c == a * c + b * c", ab_c.serialize(), ac_bc.serialize());
10,752✔
362
         }
3,584✔
363

364
         for(size_t i = 0; i != test_iter; ++i) {
3,612✔
365
            const auto a = Botan::EC_Scalar::random(group, rng);
3,584✔
366
            const auto b = Botan::EC_Scalar::random(group, rng);
3,584✔
367
            const auto c = a * b;
3,584✔
368

369
            const auto c_bn = (a.to_bigint() * b.to_bigint());
7,168✔
370
            result.test_eq("matches BigInt", c.serialize(), (c_bn % group.get_order()).serialize(order_bytes));
14,336✔
371

372
            const auto c_wide_bytes = c_bn.serialize();
3,584✔
373
            result.test_lte("Expected size", c_wide_bytes.size(), 2 * order_bytes);
3,584✔
374

375
            const auto z = Botan::EC_Scalar::from_bytes_mod_order(group, c_wide_bytes);
3,584✔
376

377
            result.test_eq("from_bytes_mod_order", c.serialize(), z.serialize());
10,752✔
378
         }
10,752✔
379

380
         for(size_t i = 0; i != test_iter; ++i) {
3,612✔
381
            std::vector<uint8_t> r(2 * group.get_order_bytes());
3,584✔
382

383
            rng.randomize(r);
3,584✔
384

385
            const auto ref = Botan::BigInt::from_bytes(r) % group.get_order();
3,584✔
386

387
            const auto scalar = Botan::EC_Scalar::from_bytes_mod_order(group, r);
3,584✔
388

389
            result.test_eq("from_bytes_mod_order (random)", scalar.serialize(), ref.serialize(group.get_order_bytes()));
10,752✔
390
         }
10,752✔
391

392
         result.end_timer();
28✔
393
      }
56✔
394
};
395

396
BOTAN_REGISTER_TEST("pubkey", "ecc_scalar_arith", ECC_Scalar_Arithmetic_Tests);
397

398
#endif
399

400
}  // namespace
401

402
}  // namespace Botan_Tests
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