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

randombit / botan / 21768358452

06 Feb 2026 10:35PM UTC coverage: 90.064% (-0.003%) from 90.067%
21768358452

Pull #5289

github

web-flow
Merge f589db195 into 8ea0ca252
Pull Request #5289: Further misc header reductions, forward declarations, etc

102238 of 113517 relevant lines covered (90.06%)

11357432.36 hits per line

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

91.91
/src/tests/test_ecdsa.cpp
1
/*
2
* (C) 2014,2015 Jack Lloyd
3
* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_ECDSA)
11
   #include "test_pubkey.h"
12
   #include "test_rng.h"
13
   #include <botan/ec_group.h>
14
   #include <botan/ecdsa.h>
15
   #include <botan/hash.h>
16
   #include <botan/pk_algs.h>
17
   #include <botan/pkcs8.h>
18
   #include <botan/pubkey.h>
19
   #include <botan/rng.h>
20
#endif
21

22
namespace Botan_Tests {
23

24
namespace {
25

26
#if defined(BOTAN_HAS_ECDSA)
27

28
class ECDSA_Verification_Tests final : public PK_Signature_Verification_Test {
29
   public:
30
      ECDSA_Verification_Tests() :
1✔
31
            PK_Signature_Verification_Test("ECDSA", "pubkey/ecdsa_verify.vec", "Group,Px,Py,Msg,Signature", "Valid") {}
2✔
32

33
      bool clear_between_callbacks() const override { return false; }
15✔
34

35
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
15✔
36
         return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group"));
15✔
37
      }
38

39
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
15✔
40
         const std::string group_id = vars.get_req_str("Group");
15✔
41
         const BigInt px = vars.get_req_bn("Px");
15✔
42
         const BigInt py = vars.get_req_bn("Py");
15✔
43
         const auto group = Botan::EC_Group::from_name(group_id);
15✔
44

45
         const auto public_key = Botan::EC_AffinePoint::from_bigint_xy(group, px, py).value();
30✔
46

47
         return std::make_unique<Botan::ECDSA_PublicKey>(group, public_key);
30✔
48
      }
15✔
49

50
      std::string default_padding(const VarMap& /*unused*/) const override { return "Raw"; }
15✔
51
};
52

53
class ECDSA_Wycheproof_Verification_Tests final : public PK_Signature_Verification_Test {
54
   public:
55
      ECDSA_Wycheproof_Verification_Tests() :
1✔
56
            PK_Signature_Verification_Test(
57
               "ECDSA", "pubkey/ecdsa_wycheproof.vec", "Group,Px,Py,Hash,Msg,Signature,Valid") {}
2✔
58

59
      bool clear_between_callbacks() const override { return false; }
11,864✔
60

61
      Botan::Signature_Format sig_format() const override { return Botan::Signature_Format::DerSequence; }
47,456✔
62

63
      bool test_random_invalid_sigs() const override { return false; }
3,475✔
64

65
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
11,864✔
66
         return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group"));
11,864✔
67
      }
68

69
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
11,864✔
70
         const std::string group_id = vars.get_req_str("Group");
11,864✔
71
         const BigInt px = vars.get_req_bn("Px");
11,864✔
72
         const BigInt py = vars.get_req_bn("Py");
11,864✔
73
         const auto group = Botan::EC_Group::from_name(group_id);
11,864✔
74

75
         const auto public_key = Botan::EC_AffinePoint::from_bigint_xy(group, px, py).value();
23,728✔
76

77
         return std::make_unique<Botan::ECDSA_PublicKey>(group, public_key);
23,728✔
78
      }
11,864✔
79

80
      std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); }
23,728✔
81
};
82

83
class ECDSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test {
84
   public:
85
      ECDSA_Signature_KAT_Tests() :
1✔
86
            PK_Signature_Generation_Test("ECDSA",
87
   #if defined(BOTAN_HAS_RFC6979_GENERATOR)
88
                                         "pubkey/ecdsa_rfc6979.vec",
89
                                         "Group,X,Hash,Msg,Signature") {
2✔
90
      }
1✔
91
   #else
92
                                         "pubkey/ecdsa_prob.vec",
93
                                         "Group,X,Hash,Msg,Nonce,Signature") {
94
      }
95
   #endif
96

97
      bool clear_between_callbacks() const override { return false; }
104✔
98

99
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
104✔
100
         return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group"));
104✔
101
      }
102

103
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
104✔
104
         const std::string group_id = vars.get_req_str("Group");
104✔
105
         const BigInt x = vars.get_req_bn("X");
104✔
106
         const auto group = Botan::EC_Group::from_name(group_id);
104✔
107

108
         return std::make_unique<Botan::ECDSA_PrivateKey>(this->rng(), group, x);
208✔
109
      }
104✔
110

111
      std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); }
208✔
112

113
   #if !defined(BOTAN_HAS_RFC6979_GENERATOR)
114
      std::unique_ptr<Botan::RandomNumberGenerator> test_rng(const std::vector<uint8_t>& nonce) const override {
115
         // probabilistic ecdsa signature generation extracts more random than just the nonce,
116
         // but the nonce is extracted first
117
         return std::make_unique<Fixed_Output_Position_RNG>(nonce, 1, this->rng());
118
      }
119
   #endif
120
};
121

122
class ECDSA_KAT_Verification_Tests final : public PK_Signature_Verification_Test {
123
   public:
124
      ECDSA_KAT_Verification_Tests() :
1✔
125
            PK_Signature_Verification_Test("ECDSA",
126
   #if !defined(BOTAN_HAS_RFC6979_GENERATOR)
127
                                           "pubkey/ecdsa_rfc6979.vec",
128
                                           "Group,X,Hash,Msg,Signature") {
129
      }
130
   #else
131
                                           "pubkey/ecdsa_prob.vec",
132
                                           "Group,X,Hash,Msg,Nonce,Signature") {
2✔
133
      }
1✔
134
   #endif
135

136
      bool clear_between_callbacks() const override { return false; }
251✔
137

138
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
251✔
139
         return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group"));
251✔
140
      }
141

142
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
251✔
143
         const std::string group_id = vars.get_req_str("Group");
251✔
144
         const BigInt x = vars.get_req_bn("X");
251✔
145
         const auto group = Botan::EC_Group::from_name(group_id);
251✔
146

147
         const Botan::ECDSA_PrivateKey priv_key(this->rng(), group, x);
251✔
148

149
         return priv_key.public_key();
251✔
150
      }
251✔
151

152
      std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); }
502✔
153
};
154

155
class ECDSA_Sign_Verify_DER_Test final : public PK_Sign_Verify_DER_Test {
×
156
   public:
157
      ECDSA_Sign_Verify_DER_Test() : PK_Sign_Verify_DER_Test("ECDSA", "SHA-512") {}
2✔
158

159
      std::unique_ptr<Botan::Private_Key> key() override {
1✔
160
         return Botan::create_private_key("ECDSA", this->rng(), "secp256r1");
1✔
161
      }
162
};
163

164
class ECDSA_Keygen_Tests final : public PK_Key_Generation_Test {
1✔
165
   public:
166
      std::vector<std::string> keygen_params() const override {
1✔
167
         const auto& grp = Botan::EC_Group::known_named_groups();
1✔
168
         return std::vector<std::string>(grp.begin(), grp.end());
1✔
169
      }
170

171
      std::string algo_name() const override { return "ECDSA"; }
28✔
172

173
      std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view keygen_params,
28✔
174
                                                             std::string_view /* provider */,
175
                                                             std::span<const uint8_t> raw_pk) const override {
176
         const auto group = Botan::EC_Group(keygen_params);
28✔
177
         const auto public_key = Botan::EC_AffinePoint(group, raw_pk);
28✔
178
         return std::make_unique<Botan::ECDSA_PublicKey>(group, public_key);
84✔
179
      }
28✔
180
};
181

182
class ECDSA_Keygen_Stability_Tests final : public PK_Key_Generation_Stability_Test {
183
   public:
184
      ECDSA_Keygen_Stability_Tests() : PK_Key_Generation_Stability_Test("ECDSA", "pubkey/ecdsa_keygen.vec") {}
1✔
185
};
186

187
   #if defined(BOTAN_HAS_EMSA_RAW)
188

189
class ECDSA_Key_Recovery_Tests final : public Text_Based_Test {
×
190
   public:
191
      ECDSA_Key_Recovery_Tests() : Text_Based_Test("pubkey/ecdsa_key_recovery.vec", "Group,Msg,R,S,V,Pubkey") {}
2✔
192

193
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
2✔
194
         return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group"));
2✔
195
      }
196

197
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
2✔
198
         Test::Result result("ECDSA key recovery");
2✔
199

200
         const std::string group_id = vars.get_req_str("Group");
2✔
201
         const auto group = Botan::EC_Group::from_name(group_id);
2✔
202

203
         const BigInt R = vars.get_req_bn("R");
2✔
204
         const BigInt S = vars.get_req_bn("S");
2✔
205
         const uint8_t V = vars.get_req_u8("V");
2✔
206
         const std::vector<uint8_t> msg = vars.get_req_bin("Msg");
2✔
207
         const auto expected_pubkey = vars.get_req_bin("Pubkey");
2✔
208

209
         try {
2✔
210
            const Botan::ECDSA_PublicKey pubkey(group, msg, R, S, V);
2✔
211
            result.test_eq("Pubkey X coordinate", pubkey.public_key_bits(), expected_pubkey);
4✔
212

213
            const uint8_t computed_V = pubkey.recovery_param(msg, R, S);
2✔
214
            result.test_eq("Recovery param is correct", static_cast<size_t>(computed_V), static_cast<size_t>(V));
2✔
215

216
            Botan::PK_Verifier verifier(pubkey, "Raw");
2✔
217

218
            auto sig = Botan::BigInt::encode_fixed_length_int_pair(R, S, group.get_order_bytes());
2✔
219

220
            result.confirm("Signature verifies", verifier.verify_message(msg, sig));
4✔
221
         } catch(Botan::Exception& e) {
2✔
222
            result.test_failure("Failed to recover ECDSA public key", e.what());
×
223
         }
×
224

225
         return result;
2✔
226
      }
4✔
227
};
228

229
BOTAN_REGISTER_TEST("pubkey", "ecdsa_key_recovery", ECDSA_Key_Recovery_Tests);
230

231
   #endif
232

233
class ECDSA_Invalid_Key_Tests final : public Text_Based_Test {
×
234
   public:
235
      ECDSA_Invalid_Key_Tests() : Text_Based_Test("pubkey/ecdsa_invalid.vec", "Group,InvalidKeyX,InvalidKeyY") {}
2✔
236

237
      bool clear_between_callbacks() const override { return false; }
78✔
238

239
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
78✔
240
         return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group"));
78✔
241
      }
242

243
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
78✔
244
         Test::Result result("ECDSA invalid keys");
78✔
245

246
         const std::string group_id = vars.get_req_str("Group");
78✔
247
         const auto group = Botan::EC_Group::from_name(group_id);
78✔
248
         const Botan::BigInt x = vars.get_req_bn("InvalidKeyX");
78✔
249
         const Botan::BigInt y = vars.get_req_bn("InvalidKeyY");
78✔
250

251
         if(auto pt = Botan::EC_AffinePoint::from_bigint_xy(group, x, y)) {
78✔
252
            result.test_failure("Invalid public key was deserialized");
×
253
         } else {
254
            result.test_success("Invalid public key was rejected");
156✔
255
         }
×
256

257
         return result;
156✔
258
      }
78✔
259
};
260

261
class ECDSA_AllGroups_Test : public Test {
1✔
262
   public:
263
      std::vector<Test::Result> run() override {
1✔
264
         std::vector<Test::Result> results;
1✔
265

266
         const std::vector<std::string> hash_fn = {
1✔
267
            "SHA-256", "SHA-384", "SHA-512", "SHAKE-128(208)", "SHAKE-128(520)", "SHAKE-128(1032)"};
1✔
268

269
         for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
29✔
270
            Test::Result result("ECDSA " + group_name);
28✔
271

272
            result.start_timer();
28✔
273

274
            const auto group = Botan::EC_Group::from_name(group_name);
28✔
275

276
            const Botan::ECDSA_PrivateKey priv(rng(), group);
28✔
277
            const auto pub = priv.public_key();
28✔
278

279
            for(const auto& hash : hash_fn) {
196✔
280
               if(!Botan::HashFunction::create(hash)) {
336✔
281
                  continue;
×
282
               }
283

284
               try {
168✔
285
                  Botan::PK_Signer signer(priv, rng(), hash);
168✔
286
                  Botan::PK_Verifier verifier(*pub, hash);
168✔
287

288
                  for(size_t i = 0; i != 16; ++i) {
2,856✔
289
                     auto message = Botan::unlock(rng().random_vec(rng().next_byte()));
5,376✔
290
                     auto sig = signer.sign_message(message, rng());
2,688✔
291
                     result.test_eq("Expected signature size", sig.size(), 2 * group.get_order_bytes());
2,688✔
292

293
                     result.confirm("Signature accepted", verifier.verify_message(message, sig));
5,376✔
294

295
                     const auto corrupted_message = mutate_vec(message, rng(), true);
2,688✔
296
                     result.confirm("Modified message rejected", !verifier.verify_message(corrupted_message, sig));
5,376✔
297

298
                     const auto corrupted_sig = mutate_vec(sig, rng(), true);
2,688✔
299
                     result.confirm("Modified signature rejected", !verifier.verify_message(message, corrupted_sig));
5,376✔
300
                  }
10,744✔
301
               } catch(std::exception& e) {
168✔
302
                  result.test_failure("Exception", e.what());
×
303
               }
×
304
            }
305

306
            result.end_timer();
28✔
307
            results.push_back(result);
28✔
308
         }
28✔
309

310
         return results;
1✔
311
      }
1✔
312
};
313

314
class ECDSA_ExplicitCurveKey_Test : public Text_Based_Test {
×
315
   public:
316
      ECDSA_ExplicitCurveKey_Test() : Text_Based_Test("pubkey/ecdsa_explicit.vec", "Key") {}
2✔
317

318
      bool clear_between_callbacks() const override { return false; }
13✔
319

320
      bool skip_this_test(const std::string& group, const VarMap& /*vars*/) override {
13✔
321
         return !Botan::EC_Group::supports_named_group(group);
13✔
322
      }
323

324
      Test::Result run_one_test(const std::string& group_name, const VarMap& vars) override {
13✔
325
         Test::Result result("ECDSA explicit key " + group_name);
13✔
326

327
         const auto key_bytes = vars.get_req_bin("Key");
13✔
328

329
         try {
13✔
330
            const auto expected_oid = Botan::OID::from_name(group_name).value();
26✔
331

332
            auto key = Botan::PKCS8::load_key(key_bytes);
13✔
333
            const auto* ecdsa = dynamic_cast<const Botan::ECDSA_PrivateKey*>(key.get());
13✔
334
            if(ecdsa != nullptr) {
13✔
335
               result.test_success("Returned key was ECDSA");
13✔
336

337
               const auto& group = ecdsa->domain();
13✔
338
               result.test_eq("Key is marked as explicit encoding", group.used_explicit_encoding(), true);
13✔
339
               result.confirm("Group has expected OID", group.get_curve_oid() == expected_oid);
26✔
340
            } else {
341
               result.test_failure("Returned key was some other type");
×
342
            }
343
         } catch(Botan::Exception& e) {
13✔
344
            result.test_failure("Failed to parse key", e.what());
×
345
         }
×
346

347
         return result;
13✔
348
      }
13✔
349
};
350

351
BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify", ECDSA_Verification_Tests);
352
BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify_wycheproof", ECDSA_Wycheproof_Verification_Tests);
353
BOTAN_REGISTER_TEST("pubkey", "ecdsa_sign", ECDSA_Signature_KAT_Tests);
354
BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify_kat", ECDSA_KAT_Verification_Tests);
355
BOTAN_REGISTER_TEST("pubkey", "ecdsa_sign_verify_der", ECDSA_Sign_Verify_DER_Test);
356
BOTAN_REGISTER_TEST("pubkey", "ecdsa_keygen", ECDSA_Keygen_Tests);
357
BOTAN_REGISTER_TEST("pubkey", "ecdsa_keygen_stability", ECDSA_Keygen_Stability_Tests);
358
BOTAN_REGISTER_TEST("pubkey", "ecdsa_invalid", ECDSA_Invalid_Key_Tests);
359
BOTAN_REGISTER_TEST("pubkey", "ecdsa_all_groups", ECDSA_AllGroups_Test);
360
BOTAN_REGISTER_TEST("pubkey", "ecdsa_explicit_curve_key", ECDSA_ExplicitCurveKey_Test);
361

362
#endif
363

364
}  // namespace
365

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