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

randombit / botan / 23225340130

18 Mar 2026 01:53AM UTC coverage: 89.677% (-0.001%) from 89.678%
23225340130

push

github

web-flow
Merge pull request #5456 from randombit/jack/clang-tidy-22

Fix various warnings from clang-tidy 22

104438 of 116460 relevant lines covered (89.68%)

11819947.55 hits per line

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

73.54
/src/cli/pubkey.cpp
1
/*
2
* (C) 2010,2014,2015,2019 Jack Lloyd
3
* (C) 2019 Matthias Gierlings
4
* (C) 2015 René Korthaus
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include "cli.h"
10

11
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
12

13
   #include <botan/base64.h>
14
   #include <botan/data_src.h>
15
   #include <botan/hash.h>
16
   #include <botan/hex.h>
17
   #include <botan/pk_algs.h>
18
   #include <botan/pk_keys.h>
19
   #include <botan/pkcs8.h>
20
   #include <botan/pubkey.h>
21
   #include <botan/x509_key.h>
22
   #include <botan/internal/workfactor.h>
23
   #include <fstream>
24
   #include <sstream>
25

26
   #if defined(BOTAN_HAS_DL_GROUP)
27
      #include <botan/dl_group.h>
28
   #endif
29

30
   #if defined(BOTAN_HAS_ECC_GROUP)
31
      #include <botan/ec_group.h>
32
   #endif
33

34
namespace Botan_CLI {
35

36
namespace {
37

38
class PK_Keygen final : public Command {
39
   public:
40
      PK_Keygen() :
20✔
41
            Command(
42
               "keygen --algo=RSA --params= --passphrase= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --provider= --rng-type= --drbg-seed= --der-out") {
20✔
43
      }
20✔
44

45
      std::string group() const override { return "pubkey"; }
1✔
46

47
      std::string description() const override { return "Generate a PKCS #8 private key"; }
1✔
48

49
      void go() override {
19✔
50
         const std::string algo = get_arg("algo");
19✔
51
         const std::string params = get_arg("params");
19✔
52
         const std::string provider = get_arg("provider");
19✔
53

54
         const std::unique_ptr<Botan::Private_Key> key = Botan::create_private_key(algo, rng(), params, provider);
19✔
55

56
         if(!key) {
19✔
57
            throw CLI_Error_Unsupported("keygen", algo);
×
58
         }
59

60
         const std::string pass = get_passphrase_arg("Key passphrase", "passphrase");
38✔
61
         const bool der_out = flag_set("der-out");
19✔
62

63
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
19✔
64

65
         if(der_out) {
19✔
66
            if(pass.empty()) {
×
67
               write_output(Botan::PKCS8::BER_encode(*key));
×
68
            } else {
69
               if(get_arg("pbkdf-iter").empty()) {
×
70
                  write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(
×
71
                     *key, rng(), pass, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf")));
×
72
               } else {
73
                  write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(
×
74
                     *key, rng(), pass, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf")));
×
75
               }
76
            }
77
         } else {
78
            if(pass.empty()) {
19✔
79
               output() << Botan::PKCS8::PEM_encode(*key);
38✔
80
            } else {
81
               if(get_arg("pbkdf-iter").empty()) {
×
82
                  output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(
×
83
                     *key, rng(), pass, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf"));
×
84
               } else {
85
                  output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(
×
86
                     *key, rng(), pass, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf"));
×
87
               }
88
            }
89
         }
90
      }
38✔
91
};
92

93
BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);
20✔
94

95
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
96

97
namespace {
98

99
std::string choose_sig_padding(const std::string& key, const std::string& padding, const std::string& hash) {
3✔
100
   if(key == "RSA") {
3✔
101
      std::ostringstream oss;
×
102
      if(padding.empty()) {
×
103
         oss << "PSS";
×
104
      } else {
105
         oss << padding;
×
106
      }
107

108
      oss << "(" << hash << ")";
×
109
      return oss.str();
×
110
   } else if(padding.empty()) {
3✔
111
      return hash;
3✔
112
   } else if(hash.empty()) {
×
113
      return padding;
×
114
   } else {
115
      std::ostringstream oss;
×
116
      oss << padding << "(" << hash << ")";
×
117
      return oss.str();
×
118
   }
×
119
}
120

121
}  // namespace
122

123
class PK_Fingerprint final : public Command {
124
   public:
125
      PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
12✔
126

127
      std::string group() const override { return "pubkey"; }
1✔
128

129
      std::string description() const override { return "Calculate a public key fingerprint"; }
1✔
130

131
      void go() override {
5✔
132
         const std::string hash_algo = get_arg("algo");
5✔
133
         const bool no_fsname = flag_set("no-fsname");
5✔
134

135
         for(const std::string& key_file : get_arg_list("keys")) {
11✔
136
            std::unique_ptr<Botan::Public_Key> key(key_file == "-" ? Botan::X509::load_key(this->slurp_file("-", 4096))
14✔
137
                                                                   : Botan::X509::load_key(key_file));
6✔
138

139
            const std::string fprint = key->fingerprint_public(hash_algo);
6✔
140

141
            if(no_fsname || key_file == "-") {
6✔
142
               output() << fprint << "\n";
5✔
143
            } else {
144
               output() << key_file << ": " << fprint << "\n";
1✔
145
            }
146
         }
17✔
147
      }
5✔
148
};
149

150
BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
6✔
151

152
namespace {
153

154
std::unique_ptr<Botan::Private_Key> load_private_key(const std::string& key_filename, const std::string& passphrase) {
1✔
155
   std::string err_string;
1✔
156

157
   try {
1✔
158
      Botan::DataSource_Stream input(key_filename);
1✔
159
      return Botan::PKCS8::load_key(input, passphrase);
1✔
160
   } catch(Botan::Exception& e) {
1✔
161
      err_string = e.what();
×
162
   }
×
163

164
   if(passphrase.empty()) {
×
165
      try {
×
166
         Botan::DataSource_Stream input(key_filename);
×
167
         return Botan::PKCS8::load_key(input);
×
168
      } catch(Botan::Exception& e) {
×
169
         err_string = e.what();
×
170
      }
×
171
   }
172

173
   throw CLI_Error("Loading private key failed (" + err_string + ")");
×
174
}
1✔
175

176
}  // namespace
177

178
class PK_Sign final : public Command {
179
   public:
180
      PK_Sign() :
2✔
181
            Command(
182
               "sign --der-format --passphrase= --hash=SHA-256 --padding= --provider= --rng-type= --drbg-seed= key file") {
2✔
183
      }
2✔
184

185
      std::string group() const override { return "pubkey"; }
1✔
186

187
      std::string description() const override { return "Sign arbitrary data"; }
1✔
188

189
      void go() override {
1✔
190
         const std::string key_file = get_arg("key");
1✔
191
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
2✔
192

193
         auto key = load_private_key(key_file, passphrase);
1✔
194

195
         const std::string hash_fn = get_arg("hash");
1✔
196

197
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn)) {
2✔
198
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
199
         }
200

201
         const std::string sig_padding = choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn);
2✔
202

203
         auto format = Botan::Signature_Format::Standard;
1✔
204

205
         if(flag_set("der-format")) {
1✔
206
            if(!key->_signature_element_size_for_DER_encoding()) {
×
207
               throw CLI_Usage_Error("Key type " + key->algo_name() +
×
208
                                     " does not support DER formatting for signatures");
×
209
            }
210
            format = Botan::Signature_Format::DerSequence;
211
         }
212

213
         const std::string provider = get_arg("provider");
1✔
214

215
         Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider);
1✔
216

217
         auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); };
1✔
218
         Command::read_file(get_arg("file"), onData);
2✔
219

220
         std::vector<uint8_t> sig{signer.signature(rng())};
1✔
221

222
         if(key->stateful_operation()) {
1✔
223
            std::ofstream updated_key(key_file);
×
224
            if(passphrase.empty()) {
×
225
               updated_key << Botan::PKCS8::PEM_encode(*key);
×
226
            } else {
227
               updated_key << Botan::PKCS8::PEM_encode(*key, rng(), passphrase);
×
228
            }
229
         }
×
230

231
         output() << Botan::base64_encode(sig) << "\n";
2✔
232
      }
2✔
233
};
234

235
BOTAN_REGISTER_COMMAND("sign", PK_Sign);
2✔
236

237
class PK_Verify final : public Command {
238
   public:
239
      PK_Verify() : Command("verify --der-format --hash=SHA-256 --padding= pubkey file signature") {}
6✔
240

241
      std::string group() const override { return "pubkey"; }
1✔
242

243
      std::string description() const override {
1✔
244
         return "Verify the authenticity of the given file with the provided signature";
1✔
245
      }
246

247
      void go() override {
2✔
248
         auto key = Botan::X509::load_key(get_arg("pubkey"));
4✔
249
         if(!key) {
2✔
250
            throw CLI_Error("Unable to load public key");
×
251
         }
252

253
         const std::string hash_fn = get_arg("hash");
2✔
254

255
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn)) {
4✔
256
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
257
         }
258

259
         const std::string sig_padding = choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn);
4✔
260

261
         auto format = Botan::Signature_Format::Standard;
2✔
262
         if(flag_set("der-format")) {
2✔
263
            if(key->message_parts() == 1) {
×
264
               throw CLI_Usage_Error("Key type " + key->algo_name() +
×
265
                                     " does not support DER formatting for signatures");
×
266
            }
267
            format = Botan::Signature_Format::DerSequence;
268
         }
269

270
         Botan::PK_Verifier verifier(*key, sig_padding, format);
2✔
271
         auto onData = [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); };
2✔
272
         Command::read_file(get_arg("file"), onData);
4✔
273

274
         const Botan::secure_vector<uint8_t> signature =
2✔
275
            Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
4✔
276

277
         const bool valid = verifier.check_signature(signature);
2✔
278

279
         output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
3✔
280
      }
4✔
281
};
282

283
BOTAN_REGISTER_COMMAND("verify", PK_Verify);
3✔
284

285
class PKCS8_Tool final : public Command {
286
   public:
287
      PKCS8_Tool() :
8✔
288
            Command(
289
               "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --rng-type= --drbg-seed= key") {
8✔
290
      }
8✔
291

292
      std::string group() const override { return "pubkey"; }
1✔
293

294
      std::string description() const override { return "Open a PKCS #8 formatted key"; }
1✔
295

296
      void go() override {
7✔
297
         const std::string key_file = get_arg("key");
7✔
298
         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
14✔
299

300
         Botan::DataSource_Memory key_src(slurp_file(key_file));
14✔
301
         std::unique_ptr<Botan::Private_Key> key;
7✔
302

303
         if(pass_in.empty()) {
7✔
304
            key = Botan::PKCS8::load_key(key_src);
5✔
305
         } else {
306
            key = Botan::PKCS8::load_key(key_src, pass_in);
2✔
307
         }
308

309
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
7✔
310
         const bool der_out = flag_set("der-out");
7✔
311

312
         if(flag_set("pub-out")) {
7✔
313
            auto pk = key->public_key();
3✔
314
            if(der_out) {
3✔
315
               write_output(Botan::X509::BER_encode(*pk));
2✔
316
            } else {
317
               output() << Botan::X509::PEM_encode(*pk);
4✔
318
            }
319
         } else {
3✔
320
            const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
8✔
321

322
            if(der_out) {
4✔
323
               if(pass_out.empty()) {
1✔
324
                  write_output(Botan::PKCS8::BER_encode(*key));
×
325
               } else {
326
                  if(get_arg("pbkdf-iter").empty()) {
1✔
327
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(
2✔
328
                        *key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf")));
3✔
329
                  } else {
330
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(
×
331
                        *key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf")));
×
332
                  }
333
               }
334
            } else {
335
               if(pass_out.empty()) {
3✔
336
                  output() << Botan::PKCS8::PEM_encode(*key);
4✔
337
               } else {
338
                  if(get_arg("pbkdf-iter").empty()) {
1✔
339
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(
3✔
340
                        *key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf"));
4✔
341
                  } else {
342
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(
×
343
                        *key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf"));
×
344
                  }
345
               }
346
            }
347
         }
4✔
348
      }
14✔
349
};
350

351
BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
8✔
352

353
   #endif
354

355
   #if defined(BOTAN_HAS_ECC_GROUP)
356

357
class EC_Group_Info final : public Command {
358
   public:
359
      EC_Group_Info() : Command("ec_group_info --pem name") {}
6✔
360

361
      std::string group() const override { return "pubkey"; }
1✔
362

363
      std::string description() const override {
1✔
364
         return "Print raw elliptic curve domain parameters of the standardized curve name";
1✔
365
      }
366

367
      void go() override {
2✔
368
         const auto ec_group = Botan::EC_Group::from_name(get_arg("name"));
4✔
369

370
         if(flag_set("pem")) {
2✔
371
            output() << ec_group.PEM_encode(Botan::EC_Group_Encoding::NamedCurve);
2✔
372
         } else {
373
            output() << "P = " << std::hex << ec_group.get_p() << "\n"
1✔
374
                     << "A = " << std::hex << ec_group.get_a() << "\n"
1✔
375
                     << "B = " << std::hex << ec_group.get_b() << "\n"
1✔
376
                     << "N = " << std::hex << ec_group.get_order() << "\n"
1✔
377
                     << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
1✔
378
         }
379
      }
2✔
380
};
381

382
BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
3✔
383

384
   #endif
385

386
   #if defined(BOTAN_HAS_DL_GROUP)
387

388
class DL_Group_Info final : public Command {
389
   public:
390
      DL_Group_Info() : Command("dl_group_info --pem name") {}
16✔
391

392
      std::string group() const override { return "pubkey"; }
1✔
393

394
      std::string description() const override {
1✔
395
         return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
1✔
396
      }
397

398
      void go() override {
7✔
399
         auto dl_group = Botan::DL_Group::from_name(get_arg("name"));
14✔
400

401
         if(flag_set("pem")) {
7✔
402
            output() << dl_group.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42_DH_PARAMETERS);
×
403
         } else {
404
            output() << "P = " << std::hex << dl_group.get_p() << "\n"
7✔
405
                     << "G = " << dl_group.get_g() << "\n";
7✔
406
         }
407
      }
7✔
408
};
409

410
BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
8✔
411

412
class PK_Workfactor final : public Command {
413
   public:
414
      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
12✔
415

416
      std::string group() const override { return "pubkey"; }
1✔
417

418
      std::string description() const override { return "Provide estimate of strength of public key based on size"; }
1✔
419

420
      void go() override {
5✔
421
         const size_t bits = get_arg_sz("bits");
5✔
422
         const std::string type = get_arg("type");
5✔
423

424
         if(type == "rsa") {
5✔
425
            output() << Botan::if_work_factor(bits) << "\n";
3✔
426
         } else if(type == "dl") {
2✔
427
            output() << Botan::dl_work_factor(bits) << "\n";
1✔
428
         } else if(type == "dl_exp") {
1✔
429
            output() << Botan::dl_exponent_size(bits) << "\n";
1✔
430
         } else {
431
            throw CLI_Usage_Error("Unknown type for pk_workfactor (rsa, dl, dl_exp)");
×
432
         }
433
      }
5✔
434
};
435

436
BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
6✔
437

438
class Gen_DL_Group final : public Command {
439
   public:
440
      Gen_DL_Group() :
3✔
441
            Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup --rng-type= --drbg-seed=") {}
6✔
442

443
      std::string group() const override { return "pubkey"; }
1✔
444

445
      std::string description() const override { return "Generate ANSI X9.42 encoded Diffie-Hellman group parameters"; }
1✔
446

447
      void go() override {
2✔
448
         const size_t pbits = get_arg_sz("pbits");
2✔
449
         const size_t qbits = get_arg_sz("qbits");
2✔
450

451
         const std::string type = get_arg("type");
2✔
452
         const std::string seed_str = get_arg("seed");
2✔
453

454
         if(type == "strong") {
2✔
455
            if(!seed_str.empty()) {
×
456
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
457
            }
458
            const Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
×
459
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
×
460
         } else if(type == "subgroup") {
2✔
461
            if(!seed_str.empty()) {
1✔
462
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
463
            }
464
            const Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
1✔
465
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
2✔
466
         } else if(type == "dsa") {
2✔
467
            size_t dsa_qbits = qbits;
1✔
468
            if(dsa_qbits == 0) {
1✔
469
               if(pbits == 1024) {
1✔
470
                  dsa_qbits = 160;
471
               } else if(pbits == 2048 || pbits == 3072) {
×
472
                  dsa_qbits = 256;
473
               } else {
474
                  throw CLI_Usage_Error("Invalid DSA p/q sizes");
×
475
               }
476
            }
477

478
            if(seed_str.empty()) {
1✔
479
               const Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
1✔
480
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
2✔
481
            } else {
1✔
482
               const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
×
483
               const Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
×
484
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
×
485
            }
×
486

487
         } else {
488
            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
×
489
         }
490
      }
2✔
491
};
492

493
BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
3✔
494

495
   #endif
496

497
}  // namespace
498

499
}  // namespace Botan_CLI
500

501
#endif
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