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

randombit / botan / 39195077976

18 Oct 2026 05:18PM UTC coverage: 92.258% (+0.06%) from 92.195%
39195077976

push

github

randombit
Update release notes [ci skip]

137358 of 148884 relevant lines covered (92.26%)

8967647.39 hits per line

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

78.95
/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/pk_options.h>
20
   #include <botan/pkcs8.h>
21
   #include <botan/pubkey.h>
22
   #include <botan/x509_key.h>
23
   #include <botan/internal/workfactor.h>
24
   #include <fstream>
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() :
36 ✔
41
            Command(
42
               "keygen --algo=RSA --params= --passphrase= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --provider= --rng-type= --drbg-seed= --der-out") {
36 ✔
43
      }
36 ✔
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
      bool output_contains_secrets() const override { return true; }
34 ✔
50

51
      void go() override {
35 ✔
52
         const std::string algo = get_arg("algo");
35 ✔
53
         const std::string params = get_arg("params");
35 ✔
54
         const std::string provider = get_arg("provider");
35 ✔
55

56
         const std::unique_ptr<Botan::Private_Key> key = Botan::create_private_key(algo, rng(), params, provider);
35 ✔
57

58
         if(!key) {
35 ✔
59
            throw CLI_Error_Unsupported("keygen", algo);
×
60
         }
61

62
         const std::string pass = get_passphrase_arg("Key passphrase", "passphrase");
70 ✔
63
         const bool der_out = flag_set("der-out");
35 ✔
64

65
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
35 ✔
66

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

95
BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);
36 ✔
96

97
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
98

99
namespace {
100

101
/*
102
* Signature schemes for which the caller must select a hash function; the
103
* other schemes either fix the hash by the key type or do not use one.
104
*/
105
bool signature_scheme_requires_hash(std::string_view algo_name) {
17 ✔
106
   return algo_name == "RSA" || algo_name == "DSA" || algo_name == "ECDSA" || algo_name == "ECGDSA" ||
29 ✔
107
          algo_name == "ECKCDSA" || algo_name.starts_with("GOST-34.10");
14 ✔
108
}
109

110
}  // namespace
111

112
/*
113
* Shared handling of the signature related options of the sign and verify commands
114
*/
115
class PK_Signature_Command : public Command {
116
   protected:
117
      using Command::Command;
22 ✔
118

119
      Botan::PK_Signature_Options signature_options(const Botan::Public_Key& key) const {
20 ✔
120
         const std::string algo_name = key.algo_name();
20 ✔
121

122
         auto options = Botan::PK_Signature_Options();
20 ✔
123

124
         const bool prehashed = flag_set("prehashed");
20 ✔
125

126
         std::string hash_fn = get_arg("hash");
20 ✔
127
         // With a prehashed input an unnamed hash means the digest is signed as is
128
         if(hash_fn.empty() && !prehashed && signature_scheme_requires_hash(algo_name)) {
20 ✔
129
            hash_fn = "SHA-256";
8 ✔
130
         }
131
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn)) {
31 ✔
132
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
133
         }
134
         options = options.with_hash(hash_fn);
20 ✔
135

136
         if(prehashed) {
20 ✔
137
            options = options.with_externally_computed_prehash();
×
138
         }
139

140
         std::string padding = get_arg("padding");
20 ✔
141
         if(padding.empty() && algo_name == "RSA") {
20 ✔
142
            padding = "PSS";
2 ✔
143
         }
144
         options = options.with_padding(padding);
20 ✔
145

146
         if(const auto prehash = get_arg_maybe("prehash")) {
40 ✔
147
            if(*prehash == "default") {
2 ✔
148
               options = options.with_prehash();
2 ✔
149
            } else {
150
               options = options.with_prehash(prehash);
×
151
            }
152
         }
×
153

154
         if(const auto context = get_arg_maybe("context")) {
40 ✔
155
            options = options.with_context(*context);
×
156
         }
×
157

158
         if(get_arg_maybe("salt-size")) {
22 ✔
159
            options = options.with_salt_size(get_arg_sz("salt-size"));
2 ✔
160
         }
161

162
         if(flag_set("deterministic")) {
20 ✔
163
            options = options.with_deterministic_signature();
3 ✔
164
         }
165

166
         if(flag_set("der-format")) {
20 ✔
167
            if(!key._signature_element_size_for_DER_encoding()) {
×
168
               throw CLI_Usage_Error("Key type " + algo_name + " does not support DER formatting for signatures");
×
169
            }
170
            options = options.with_der_encoded_signature();
×
171
         }
172

173
         options = options.with_provider(get_arg_or("provider", ""));
20 ✔
174

175
         return options;
20 ✔
176
      }
20 ✔
177
};
178

179
class PK_Fingerprint final : public Command {
180
   public:
181
      PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
22 ✔
182

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

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

187
      void go() override {
10 ✔
188
         const std::string hash_algo = get_arg("algo");
10 ✔
189
         const bool no_fsname = flag_set("no-fsname");
10 ✔
190

191
         for(const std::string& key_file : get_arg_list("keys")) {
21 ✔
192
            std::unique_ptr<Botan::Public_Key> key(key_file == "-" ? Botan::X509::load_key(this->slurp_file("-", 4096))
24 ✔
193
                                                                   : Botan::X509::load_key(key_file));
11 ✔
194

195
            const std::string fprint = key->fingerprint_public(hash_algo);
11 ✔
196

197
            if(no_fsname || key_file == "-") {
11 ✔
198
               output() << fprint << "\n";
10 ✔
199
            } else {
200
               output() << key_file << ": " << fprint << "\n";
1 ✔
201
            }
202
         }
32 ✔
203
      }
10 ✔
204
};
205

206
BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
11 ✔
207

208
namespace {
209

210
std::unique_ptr<Botan::Private_Key> load_private_key(const std::string& key_filename, const std::string& passphrase) {
8 ✔
211
   std::string err_string;
8 ✔
212

213
   try {
8 ✔
214
      Botan::DataSource_Stream input(key_filename);
8 ✔
215
      return Botan::PKCS8::load_key(input, passphrase);
8 ✔
216
   } catch(Botan::Exception& e) {
8 ✔
217
      err_string = e.what();
×
218
   }
×
219

220
   if(passphrase.empty()) {
×
221
      try {
×
222
         Botan::DataSource_Stream input(key_filename);
×
223
         return Botan::PKCS8::load_key(input);
×
224
      } catch(Botan::Exception& e) {
×
225
         err_string = e.what();
×
226
      }
×
227
   }
228

229
   throw CLI_Error("Loading private key failed (" + err_string + ")");
×
230
}
8 ✔
231

232
}  // namespace
233

234
class PK_Sign final : public PK_Signature_Command {
235
   public:
236
      PK_Sign() :
9 ✔
237
            PK_Signature_Command(
238
               "sign --der-format --passphrase= --hash= --padding= --prehash= --prehashed --context= --salt-size= "
239
               "--deterministic --provider= --rng-type= --drbg-seed= key file") {}
18 ✔
240

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

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

245
      void go() override {
8 ✔
246
         const std::string key_file = get_arg("key");
8 ✔
247
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
16 ✔
248

249
         auto key = load_private_key(key_file, passphrase);
8 ✔
250

251
         Botan::PK_Signer signer(*key, rng(), signature_options(*key));
8 ✔
252

253
         const auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); };
8 ✔
254
         Command::read_file(get_arg("file"), onData);
16 ✔
255

256
         std::vector<uint8_t> sig{signer.signature(rng())};
8 ✔
257

258
         if(key->stateful_operation()) {
8 ✔
259
            // The advanced key state must be stored before the signature is released
260
            const std::string updated_key =
×
261
               passphrase.empty() ? Botan::PKCS8::PEM_encode(*key) : Botan::PKCS8::PEM_encode(*key, rng(), passphrase);
×
262
            replace_file_contents(key_file, updated_key, true);
×
263
         }
×
264

265
         output() << Botan::base64_encode(sig) << "\n";
16 ✔
266
      }
16 ✔
267
};
268

269
BOTAN_REGISTER_COMMAND("sign", PK_Sign);
9 ✔
270

271
class PK_Verify final : public PK_Signature_Command {
272
   public:
273
      PK_Verify() :
13 ✔
274
            PK_Signature_Command(
275
               "verify --der-format --hash= --padding= --prehash= --prehashed --context= --salt-size= pubkey file "
276
               "signature") {}
26 ✔
277

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

280
      std::string description() const override {
1 ✔
281
         return "Verify the authenticity of the given file with the provided signature";
1 ✔
282
      }
283

284
      void go() override {
12 ✔
285
         const auto key = Botan::X509::load_key(get_arg("pubkey"));
24 ✔
286
         if(!key) {
12 ✔
287
            throw CLI_Error("Unable to load public key");
×
288
         }
289

290
         Botan::PK_Verifier verifier(*key, signature_options(*key));
12 ✔
291
         const auto onData = [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); };
12 ✔
292
         Command::read_file(get_arg("file"), onData);
24 ✔
293

294
         const Botan::secure_vector<uint8_t> signature =
12 ✔
295
            Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
24 ✔
296

297
         const bool valid = verifier.check_signature(signature);
12 ✔
298

299
         output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
15 ✔
300

301
         if(!valid) {
12 ✔
302
            set_return_code(1);
3 ✔
303
         }
304
      }
24 ✔
305
};
306

307
BOTAN_REGISTER_COMMAND("verify", PK_Verify);
13 ✔
308

309
class PKCS8_Tool final : public Command {
310
   public:
311
      PKCS8_Tool() :
16 ✔
312
            Command(
313
               "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --rng-type= --drbg-seed= key") {
16 ✔
314
      }
16 ✔
315

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

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

320
      bool output_contains_secrets() const override { return !flag_set("pub-out"); }
13 ✔
321

322
      void go() override {
15 ✔
323
         const std::string key_file = get_arg("key");
15 ✔
324
         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
30 ✔
325

326
         Botan::DataSource_Memory key_src(slurp_file(key_file));
30 ✔
327
         std::unique_ptr<Botan::Private_Key> key;
15 ✔
328

329
         if(pass_in.empty()) {
15 ✔
330
            key = Botan::PKCS8::load_key(key_src);
13 ✔
331
         } else {
332
            key = Botan::PKCS8::load_key(key_src, pass_in);
2 ✔
333
         }
334

335
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
15 ✔
336
         const bool der_out = flag_set("der-out");
15 ✔
337

338
         if(flag_set("pub-out")) {
15 ✔
339
            const auto pk = key->public_key();
11 ✔
340
            if(der_out) {
11 ✔
341
               write_output(Botan::X509::BER_encode(*pk));
2 ✔
342
            } else {
343
               output() << Botan::X509::PEM_encode(*pk);
20 ✔
344
            }
345
         } else {
11 ✔
346
            const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
8 ✔
347

348
            if(der_out) {
4 ✔
349
               if(pass_out.empty()) {
1 ✔
350
                  write_output(Botan::PKCS8::BER_encode(*key));
×
351
               } else {
352
                  if(get_arg("pbkdf-iter").empty()) {
1 ✔
353
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(
2 ✔
354
                        *key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf")));
3 ✔
355
                  } else {
356
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(
×
357
                        *key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf")));
×
358
                  }
359
               }
360
            } else {
361
               if(pass_out.empty()) {
3 ✔
362
                  output() << Botan::PKCS8::PEM_encode(*key);
4 ✔
363
               } else {
364
                  if(get_arg("pbkdf-iter").empty()) {
1 ✔
365
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(
3 ✔
366
                        *key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf"));
4 ✔
367
                  } else {
368
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(
×
369
                        *key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf"));
×
370
                  }
371
               }
372
            }
373
         }
4 ✔
374
      }
30 ✔
375
};
376

377
BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
16 ✔
378

379
   #endif
380

381
   #if defined(BOTAN_HAS_ECC_GROUP)
382

383
class EC_Group_Info final : public Command {
384
   public:
385
      EC_Group_Info() : Command("ec_group_info --pem name") {}
6 ✔
386

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

389
      std::string description() const override {
1 ✔
390
         return "Print raw elliptic curve domain parameters of the standardized curve name";
1 ✔
391
      }
392

393
      void go() override {
2 ✔
394
         const auto ec_group = Botan::EC_Group::from_name(get_arg("name"));
4 ✔
395

396
         if(flag_set("pem")) {
2 ✔
397
            output() << ec_group.PEM_encode(Botan::EC_Group_Encoding::NamedCurve);
2 ✔
398
         } else {
399
            output() << "P = " << std::hex << ec_group.get_p() << "\n"
1 ✔
400
                     << "A = " << std::hex << ec_group.get_a() << "\n"
1 ✔
401
                     << "B = " << std::hex << ec_group.get_b() << "\n"
1 ✔
402
                     << "N = " << std::hex << ec_group.get_order() << "\n"
1 ✔
403
                     << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
1 ✔
404
         }
405
      }
2 ✔
406
};
407

408
BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
3 ✔
409

410
   #endif
411

412
   #if defined(BOTAN_HAS_DL_GROUP)
413

414
class DL_Group_Info final : public Command {
415
   public:
416
      DL_Group_Info() : Command("dl_group_info --pem name") {}
16 ✔
417

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

420
      std::string description() const override {
1 ✔
421
         return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
1 ✔
422
      }
423

424
      void go() override {
7 ✔
425
         const auto dl_group = Botan::DL_Group::from_name(get_arg("name"));
14 ✔
426

427
         if(flag_set("pem")) {
7 ✔
428
            output() << dl_group.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42_DH_PARAMETERS);
×
429
         } else {
430
            output() << "P = " << std::hex << dl_group.get_p() << "\n"
7 ✔
431
                     << "G = " << dl_group.get_g() << "\n";
7 ✔
432
         }
433
      }
7 ✔
434
};
435

436
BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
8 ✔
437

438
class PK_Workfactor final : public Command {
439
   public:
440
      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
12 ✔
441

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

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

446
      void go() override {
5 ✔
447
         const size_t bits = get_arg_sz("bits");
5 ✔
448
         const std::string type = get_arg("type");
5 ✔
449

450
         if(type == "rsa") {
5 ✔
451
            output() << Botan::if_work_factor(bits) << "\n";
3 ✔
452
         } else if(type == "dl") {
2 ✔
453
            output() << Botan::dl_work_factor(bits) << "\n";
1 ✔
454
         } else if(type == "dl_exp") {
1 ✔
455
            output() << Botan::dl_exponent_size(bits) << "\n";
1 ✔
456
         } else {
457
            throw CLI_Usage_Error("Unknown type for pk_workfactor (rsa, dl, dl_exp)");
×
458
         }
459
      }
5 ✔
460
};
461

462
BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
6 ✔
463

464
class Gen_DL_Group final : public Command {
465
   public:
466
      Gen_DL_Group() :
4 ✔
467
            Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup --rng-type= --drbg-seed=") {}
8 ✔
468

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

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

473
      void go() override {
3 ✔
474
         const size_t pbits = get_arg_sz("pbits");
3 ✔
475
         const size_t qbits = get_arg_sz("qbits");
3 ✔
476

477
         const std::string type = get_arg("type");
3 ✔
478
         const std::string seed_str = get_arg("seed");
4 ✔
479

480
         if(type == "strong") {
3 ✔
481
            if(!seed_str.empty()) {
×
482
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
483
            }
484
            const Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
×
485
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
×
486
         } else if(type == "subgroup") {
3 ✔
487
            if(!seed_str.empty()) {
2 ✔
488
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
489
            }
490
            // Match the floor the FFI DSA/ElGamal wrappers enforce; a tiny
491
            // subgroup order makes discrete logs trivial
492
            if(qbits != 0 && qbits < 160) {
2 ✔
493
               throw CLI_Usage_Error("Subgroup order of " + std::to_string(qbits) + " bits is too small");
3 ✔
494
            }
495
            const Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
1 ✔
496
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
2 ✔
497
         } else if(type == "dsa") {
2 ✔
498
            size_t dsa_qbits = qbits;
1 ✔
499
            if(dsa_qbits == 0) {
1 ✔
500
               if(pbits == 1024) {
1 ✔
501
                  dsa_qbits = 160;
502
               } else if(pbits == 2048 || pbits == 3072) {
×
503
                  dsa_qbits = 256;
504
               } else {
505
                  throw CLI_Usage_Error("Invalid DSA p/q sizes");
×
506
               }
507
            }
508

509
            if(seed_str.empty()) {
1 ✔
510
               const Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
1 ✔
511
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
2 ✔
512
            } else {
1 ✔
513
               const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
×
514
               const Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
×
515
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
×
516
            }
×
517

518
         } else {
519
            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
×
520
         }
521
      }
3 ✔
522
};
523

524
BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
4 ✔
525

526
   #endif
527

528
}  // namespace
529

530
}  // namespace Botan_CLI
531

532
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc