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

randombit / botan / 10329059925

10 Aug 2024 04:49AM UTC coverage: 91.276% (-0.02%) from 91.298%
10329059925

push

github

web-flow
Merge pull request #4143 from randombit/jack/bridge-pcurves-and-ec

Bridge pcurves into the main elliptic curve arithmetic

87809 of 96202 relevant lines covered (91.28%)

9299919.33 hits per line

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

88.4
/src/tests/test_pcurves.cpp
1
/*
2
* (C) 2024 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_PCURVES)
10
   #include "test_rng.h"
11
   #include <botan/hash.h>
12
   #include <botan/mem_ops.h>
13
   #include <botan/internal/pcurves.h>
14
   #include <botan/internal/stl_util.h>
15
#endif
16

17
namespace Botan_Tests {
18

19
#if defined(BOTAN_HAS_PCURVES)
20

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

25
      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override {
529✔
26
         Test::Result result("Pcurves base point multiply " + group_id);
529✔
27

28
         const auto k_bytes = vars.get_req_bin("k");
529✔
29
         const auto P_bytes = vars.get_req_bin("P");
529✔
30

31
         auto& rng = Test::rng();
529✔
32
         Botan::Null_RNG null_rng;
529✔
33

34
         if(auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_id)) {
529✔
35
            if(auto scalar = curve->deserialize_scalar(k_bytes)) {
527✔
36
               const auto k = scalar.value();
527✔
37
               auto pt2 = curve->mul_by_g(k, rng).to_affine().serialize();
1,581✔
38
               result.test_eq("mul_by_g correct", pt2, P_bytes);
527✔
39

40
               auto pt3 = curve->mul_by_g(k, null_rng).to_affine().serialize();
1,581✔
41
               result.test_eq("mul_by_g (Null_RNG) correct", pt3, P_bytes);
527✔
42

43
               auto g = curve->generator();
527✔
44
               auto pt4 = curve->mul(g, k, rng).to_affine().serialize();
1,581✔
45
               result.test_eq("mul correct", pt4, P_bytes);
527✔
46

47
               auto pt5 = curve->mul(g, k, null_rng).to_affine().serialize();
1,581✔
48
               result.test_eq("mul correct (Null_RNG)", pt5, P_bytes);
527✔
49

50
               // Now test the var point mul with a blinded point ((g*b)*k)/b = pt
51
               auto b = curve->random_scalar(rng);
527✔
52
               auto binv = b.invert();
527✔
53
               auto gx = curve->mul_by_g(b, rng).to_affine();
1,054✔
54
               auto gx_k = curve->mul(gx, k, rng).to_affine();
1,054✔
55
               auto g_k = curve->mul(gx_k, binv, rng).to_affine();
1,054✔
56
               result.test_eq("blinded mul correct", g_k.serialize(), P_bytes);
1,581✔
57
            } else {
5,797✔
58
               result.test_failure("Curve rejected scalar input");
×
59
            }
527✔
60
         }
×
61

62
         return result;
529✔
63
      }
1,587✔
64
};
65

66
BOTAN_REGISTER_TEST("pcurves", "pcurves_basemul", Pcurve_Basemul_Tests);
67

68
class Pcurve_Ecdh_Tests final : public Text_Based_Test {
×
69
   public:
70
      Pcurve_Ecdh_Tests() : Text_Based_Test("pubkey/ecdh.vec", "Secret,CounterKey,K") {}
2✔
71

72
      Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override {
156✔
73
         Test::Result result("Pcurves ECDH " + group_id);
156✔
74

75
         const auto sk = vars.get_req_bin("Secret");
156✔
76
         const auto peer_key = vars.get_req_bin("CounterKey");
156✔
77
         const auto shared_secret = vars.get_req_bin("K");
156✔
78

79
         auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_id);
156✔
80

81
         if(!curve) {
156✔
82
            result.test_note("Skipping test due to missing pcurve " + group_id);
×
83
            return result;
×
84
         }
85

86
         auto x = curve->deserialize_scalar(sk);
156✔
87
         auto pt = curve->deserialize_point(peer_key);
156✔
88

89
         if(x && pt) {
156✔
90
            auto ss = curve->mul(pt.value(), x.value(), rng()).to_affine().x_bytes();
468✔
91
            result.test_eq("shared secret", ss, shared_secret);
312✔
92
         } else {
156✔
93
            result.test_failure("Curve rejected test inputs");
×
94
         }
95

96
         return result;
156✔
97
      }
936✔
98
};
99

100
BOTAN_REGISTER_TEST("pcurves", "pcurves_ecdh", Pcurve_Ecdh_Tests);
101

102
class Pcurve_Arithmetic_Tests final : public Test {
×
103
   public:
104
      std::vector<Test::Result> run() override {
1✔
105
         std::vector<Test::Result> results;
1✔
106

107
         auto& rng = Test::rng();
1✔
108

109
         for(auto id : Botan::PCurve::PrimeOrderCurveId::all()) {
13✔
110
            Test::Result result("Pcurves point operations " + id.to_string());
24✔
111

112
            result.start_timer();
12✔
113

114
            auto curve = Botan::PCurve::PrimeOrderCurve::from_id(id);
12✔
115

116
            if(!curve) {
12✔
117
               result.test_note("Skipping test due to missing pcurve " + id.to_string());
×
118
               continue;
×
119
            }
120

121
            const auto zero = curve->scalar_zero();
12✔
122
            const auto one = curve->scalar_one();
12✔
123
            const auto g = curve->generator();
12✔
124
            const auto g_bytes = g.serialize();
12✔
125

126
            const auto inf = curve->mul_by_g(zero, rng);
12✔
127
            result.confirm("g*zero is point at infinity", inf.to_affine().is_identity());
24✔
128

129
            std::vector<uint8_t> inf_bytes(g_bytes.size());
12✔
130
            inf_bytes[0] = 0x04;
12✔
131

132
            result.test_eq("infinity has expected encoding", inf.to_affine().serialize(), inf_bytes);
36✔
133

134
            const auto inf2 = inf.dbl();
12✔
135
            result.test_eq("infinity * 2 is infinity", inf2.to_affine().serialize(), inf_bytes);
36✔
136

137
            const auto inf3 = inf2 + inf;
12✔
138
            result.test_eq("infinity plus itself is infinity", inf3.to_affine().serialize(), inf_bytes);
36✔
139

140
            const auto g_one = curve->mul_by_g(one, rng);
12✔
141
            result.test_eq("g*one == generator", g_one.to_affine().serialize(), g_bytes);
36✔
142

143
            const auto g_plus_inf = g_one + inf;
12✔
144
            result.test_eq("g + inf == g", g_plus_inf.to_affine().serialize(), g_bytes);
36✔
145

146
            const auto g_plus_infa = g_one + inf.to_affine();
12✔
147
            result.test_eq("g + inf (affine) == g", g_plus_infa.to_affine().serialize(), g_bytes);
36✔
148

149
            const auto inf_plus_g = inf + g_one;
12✔
150
            result.test_eq("inf + g == g", inf_plus_g.to_affine().serialize(), g_bytes);
48✔
151

152
            const auto inf_plus_ga = inf + g_one.to_affine();
12✔
153
            result.test_eq("inf + g (affine) == g", inf_plus_ga.to_affine().serialize(), g_bytes);
36✔
154

155
            const auto g_neg_one = curve->mul_by_g(one.negate(), rng).to_affine();
36✔
156

157
            const auto inf_from_g = g_one + g_neg_one;
12✔
158
            result.test_eq("g - g is infinity", inf_from_g.to_affine().serialize(), inf_bytes);
36✔
159

160
            const auto g_two = curve->mul_by_g(one + one, rng);
12✔
161
            const auto g_plus_g = g_one + g_one;
12✔
162
            result.test_eq("2*g == g+g", g_two.to_affine().serialize(), g_plus_g.to_affine().serialize());
72✔
163

164
            result.confirm("Scalar::zero is zero", curve->scalar_zero().is_zero());
36✔
165
            result.confirm("Scalar::one is not zero", !curve->scalar_one().is_zero());
36✔
166

167
            for(size_t i = 0; i != 16; ++i) {
204✔
168
               const auto pt = curve->mul_by_g(curve->random_scalar(rng), rng).to_affine();
576✔
169

170
               const auto a = curve->random_scalar(rng);
192✔
171
               const auto b = curve->random_scalar(rng);
192✔
172
               const auto c = a + b;
192✔
173

174
               const auto Pa = curve->mul(pt, a, rng);
192✔
175
               const auto Pb = curve->mul(pt, b, rng);
192✔
176
               const auto Pc = curve->mul(pt, c, rng);
192✔
177

178
               const auto Pc_bytes = Pc.to_affine().serialize();
192✔
179

180
               const auto Pab = Pa + Pb;
192✔
181
               result.test_eq("Pa + Pb == Pc", Pab.to_affine().serialize(), Pc_bytes);
576✔
182

183
               const auto Pba = Pb + Pa;
192✔
184
               result.test_eq("Pb + Pa == Pc", Pba.to_affine().serialize(), Pc_bytes);
384✔
185

186
               const auto Pabm = Pa + Pb.to_affine();
192✔
187
               result.test_eq("Pa + Pb == Pc (mixed)", Pabm.to_affine().serialize(), Pc_bytes);
576✔
188
               const auto Pbam = Pb + Pa.to_affine();
192✔
189
               result.test_eq("Pb + Pa == Pc (mixed)", Pbam.to_affine().serialize(), Pc_bytes);
768✔
190
            }
2,304✔
191

192
            for(size_t i = 0; i != 16; ++i) {
204✔
193
               const auto pt1 = curve->mul_by_g(curve->random_scalar(rng), rng).to_affine();
576✔
194
               const auto pt2 = curve->mul_by_g(curve->random_scalar(rng), rng).to_affine();
576✔
195

196
               const auto s1 = curve->random_scalar(rng);
192✔
197
               const auto s2 = curve->random_scalar(rng);
192✔
198

199
               const auto mul2_table = curve->mul2_setup(pt1, pt2);
192✔
200

201
               const auto ref = (curve->mul(pt1, s1, rng) + curve->mul(pt2, s2, rng)).to_affine();
768✔
202

203
               if(auto mul2pt = curve->mul2_vartime(*mul2_table, s1, s2)) {
192✔
204
                  result.test_eq("ref == mul2t", ref.serialize(), mul2pt->to_affine().serialize());
960✔
205
               } else {
206
                  result.confirm("ref is identity", ref.is_identity());
×
207
               }
192✔
208
            }
1,152✔
209

210
            // Test cases where the two points have a linear relation
211
            for(size_t i = 0; i != 16; ++i) {
204✔
212
               const auto pt1 = curve->generator();
192✔
213

214
               auto pt2 = [&]() {
×
215
                  const auto lo = curve->scalar_from_u32(static_cast<uint32_t>(i / 2));
192✔
216
                  auto x = curve->mul_by_g(lo, rng);
192✔
217
                  if(i % 2 == 0) {
192✔
218
                     x = x.negate();
96✔
219
                  }
220
                  return x.to_affine();
192✔
221
               }();
576✔
222

223
               const auto s1 = curve->random_scalar(rng);
192✔
224
               const auto s2 = curve->random_scalar(rng);
192✔
225

226
               const auto mul2_table = curve->mul2_setup(pt1, pt2);
192✔
227

228
               const auto ref = (curve->mul(pt1, s1, rng) + curve->mul(pt2, s2, rng)).to_affine();
768✔
229

230
               if(auto mul2pt = curve->mul2_vartime(*mul2_table, s1, s2)) {
192✔
231
                  result.test_eq("ref == mul2t", ref.serialize(), mul2pt->to_affine().serialize());
960✔
232
               } else {
233
                  result.confirm("ref is identity", ref.is_identity());
×
234
               }
192✔
235
            }
1,152✔
236

237
            result.end_timer();
12✔
238

239
            results.push_back(result);
12✔
240
         }
204✔
241

242
         return results;
1✔
243
      }
×
244
};
245

246
BOTAN_REGISTER_TEST("pcurves", "pcurves_arith", Pcurve_Arithmetic_Tests);
247

248
class Pcurve_PointEnc_Tests final : public Test {
×
249
   public:
250
      std::vector<Test::Result> run() override {
1✔
251
         std::vector<Test::Result> results;
1✔
252

253
         auto& rng = Test::rng();
1✔
254

255
         for(auto id : Botan::PCurve::PrimeOrderCurveId::all()) {
13✔
256
            Test::Result result("Pcurves point operations " + id.to_string());
24✔
257

258
            result.start_timer();
12✔
259

260
            auto curve = Botan::PCurve::PrimeOrderCurve::from_id(id);
12✔
261

262
            if(!curve) {
12✔
263
               result.test_note("Skipping test due to missing pcurve " + id.to_string());
×
264
               continue;
×
265
            }
266

267
            for(size_t trial = 0; trial != 100; ++trial) {
1,212✔
268
               const auto scalar = curve->random_scalar(rng);
1,200✔
269
               const auto pt = curve->mul_by_g(scalar, rng).to_affine();
2,400✔
270

271
               const auto pt_u = pt.serialize();
1,200✔
272
               result.test_eq("Expected uncompressed header", static_cast<size_t>(pt_u[0]), 0x04);
1,200✔
273
               const size_t fe_bytes = (pt_u.size() - 1) / 2;
1,200✔
274
               const auto pt_c = pt.serialize_compressed();
1,200✔
275

276
               result.test_eq("Expected compressed size", pt_c.size(), 1 + fe_bytes);
1,200✔
277
               result.confirm("Expected compressed header", pt_c[0] == 0x02 || pt_c[0] == 0x03);
3,600✔
278

279
               if(auto d_pt_u = curve->deserialize_point(pt_u)) {
1,200✔
280
                  result.test_eq("Deserializing uncompressed returned correct point", d_pt_u->serialize(), pt_u);
3,600✔
281
               } else {
282
                  result.test_failure("Failed to deserialize uncompressed point");
×
283
               }
×
284

285
               if(auto d_pt_c = curve->deserialize_point(pt_c)) {
1,200✔
286
                  result.test_eq("Deserializing compressed returned correct point", d_pt_c->serialize(), pt_u);
3,600✔
287
               } else {
288
                  result.test_failure("Failed to deserialize compressed point");
×
289
               }
1,200✔
290
            }
3,600✔
291

292
            result.end_timer();
12✔
293

294
            results.push_back(result);
12✔
295
         }
12✔
296

297
         return results;
1✔
298
      }
×
299
};
300

301
BOTAN_REGISTER_TEST("pcurves", "pcurves_point_enc", Pcurve_PointEnc_Tests);
302

303
#endif
304

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