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

randombit / botan / 16581714815

28 Jul 2025 10:25PM UTC coverage: 90.475% (-0.2%) from 90.685%
16581714815

Pull #5021

github

web-flow
Merge 072983077 into 1eacc5b05
Pull Request #5021: Add PK_Signature_Options

99366 of 109827 relevant lines covered (90.48%)

12349417.34 hits per line

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

79.83
/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/hex.h>
15
   #include <botan/rng.h>
16

17
   #include <botan/data_src.h>
18
   #include <botan/hash.h>
19
   #include <botan/pk_algs.h>
20
   #include <botan/pk_keys.h>
21
   #include <botan/pkcs8.h>
22
   #include <botan/pubkey.h>
23
   #include <botan/x509_key.h>
24
   #include <botan/internal/pk_options.h>
25
   #include <botan/internal/workfactor.h>
26

27
   #include <fstream>
28

29
   #if defined(BOTAN_HAS_DL_GROUP)
30
      #include <botan/dl_group.h>
31
   #endif
32

33
   #if defined(BOTAN_HAS_ECC_GROUP)
34
      #include <botan/ec_group.h>
35
   #endif
36

37
namespace Botan_CLI {
38

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

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

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

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

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

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

61
         const std::string pass = get_passphrase_arg("Key passphrase", "passphrase");
40✔
62
         const bool der_out = flag_set("der-out");
20✔
63

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

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

94
BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);
21✔
95

96
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
97

98
namespace {
99

100
Botan::PK_Signature_Options sig_options(
7✔
101
   std::string_view key, std::string_view padding, std::string_view hash, bool use_der, std::string_view provider) {
102
   if(key == "RSA" && padding.empty()) {
7✔
103
      return sig_options(key, "PSS", hash, use_der, provider);
×
104
   }
105

106
   return Botan::PK_Signature_Options()
14✔
107
      .with_hash(hash)
14✔
108
      .with_padding(padding)
14✔
109
      .with_der_encoded_signature(use_der)
21✔
110
      .with_provider(provider);
7✔
111
}
112

113
}  // namespace
114

115
class PK_Fingerprint final : public Command {
116
   public:
117
      PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
16✔
118

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

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

123
      void go() override {
7✔
124
         const std::string hash_algo = get_arg("algo");
7✔
125
         const bool no_fsname = flag_set("no-fsname");
7✔
126

127
         for(const std::string& key_file : get_arg_list("keys")) {
15✔
128
            std::unique_ptr<Botan::Public_Key> key(key_file == "-" ? Botan::X509::load_key(this->slurp_file("-", 4096))
18✔
129
                                                                   : Botan::X509::load_key(key_file));
8✔
130

131
            const std::string fprint = key->fingerprint_public(hash_algo);
8✔
132

133
            if(no_fsname || key_file == "-") {
8✔
134
               output() << fprint << "\n";
7✔
135
            } else {
136
               output() << key_file << ": " << fprint << "\n";
1✔
137
            }
138
         }
23✔
139
      }
7✔
140
};
141

142
BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
8✔
143

144
namespace {
145

146
std::unique_ptr<Botan::Private_Key> load_private_key(const std::string& key_filename, const std::string& passphrase) {
3✔
147
   std::string err_string;
3✔
148

149
   try {
3✔
150
      Botan::DataSource_Stream input(key_filename);
3✔
151
      return Botan::PKCS8::load_key(input, passphrase);
3✔
152
   } catch(Botan::Exception& e) {
3✔
153
      err_string = e.what();
×
154
   }
×
155

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

165
   throw CLI_Error("Loading private key failed (" + err_string + ")");
×
166
}
3✔
167

168
}  // namespace
169

170
class PK_Sign final : public Command {
171
   public:
172
      PK_Sign() : Command("sign --der-format --passphrase= --hash=SHA-256 --padding= --provider= key file") {}
8✔
173

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

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

178
      void go() override {
3✔
179
         const std::string key_file = get_arg("key");
3✔
180
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
6✔
181

182
         auto key = load_private_key(key_file, passphrase);
3✔
183

184
         const std::string hash_fn = get_arg("hash");
3✔
185

186
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn)) {
6✔
187
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
188
         }
189

190
         if(flag_set("der-format") && !key->_signature_element_size_for_DER_encoding()) {
6✔
191
            throw CLI_Usage_Error("Key type " + key->algo_name() + " does not support DER formatting for signatures");
×
192
         }
193

194
         const auto options =
3✔
195
            sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), get_arg("provider"));
6✔
196

197
         Botan::PK_Signer signer(*key, rng(), options);
3✔
198

199
         auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); };
3✔
200
         Command::read_file(get_arg("file"), onData);
6✔
201

202
         std::vector<uint8_t> sig{signer.signature(rng())};
3✔
203

204
         if(key->stateful_operation()) {
3✔
205
            std::ofstream updated_key(key_file);
2✔
206
            if(passphrase.empty()) {
2✔
207
               updated_key << Botan::PKCS8::PEM_encode(*key);
4✔
208
            } else {
209
               updated_key << Botan::PKCS8::PEM_encode(*key, rng(), passphrase);
×
210
            }
211
         }
2✔
212

213
         output() << Botan::base64_encode(sig) << "\n";
6✔
214
      }
6✔
215
};
216

217
BOTAN_REGISTER_COMMAND("sign", PK_Sign);
4✔
218

219
class PK_Verify final : public Command {
220
   public:
221
      PK_Verify() : Command("verify --der-format --hash=SHA-256 --padding= pubkey file signature") {}
10✔
222

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

225
      std::string description() const override {
1✔
226
         return "Verify the authenticity of the given file with the provided signature";
1✔
227
      }
228

229
      void go() override {
4✔
230
         auto key = Botan::X509::load_key(get_arg("pubkey"));
8✔
231
         if(!key) {
4✔
232
            throw CLI_Error("Unable to load public key");
×
233
         }
234

235
         const std::string hash_fn = get_arg("hash");
4✔
236

237
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn)) {
8✔
238
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
239
         }
240

241
         const auto options = sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), "");
8✔
242

243
         Botan::PK_Verifier verifier(*key, options);
4✔
244
         auto onData = [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); };
4✔
245
         Command::read_file(get_arg("file"), onData);
8✔
246

247
         const Botan::secure_vector<uint8_t> signature =
4✔
248
            Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
8✔
249

250
         const bool valid = verifier.check_signature(signature);
4✔
251

252
         output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
5✔
253
      }
8✔
254
};
255

256
BOTAN_REGISTER_COMMAND("verify", PK_Verify);
5✔
257

258
class PKCS8_Tool final : public Command {
259
   public:
260
      PKCS8_Tool() :
10✔
261
            Command(
262
               "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= key") {
10✔
263
      }
10✔
264

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

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

269
      void go() override {
9✔
270
         const std::string key_file = get_arg("key");
9✔
271
         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
18✔
272

273
         Botan::DataSource_Memory key_src(slurp_file(key_file));
18✔
274
         std::unique_ptr<Botan::Private_Key> key;
9✔
275

276
         if(pass_in.empty()) {
9✔
277
            key = Botan::PKCS8::load_key(key_src);
7✔
278
         } else {
279
            key = Botan::PKCS8::load_key(key_src, pass_in);
2✔
280
         }
281

282
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
9✔
283
         const bool der_out = flag_set("der-out");
9✔
284

285
         if(flag_set("pub-out")) {
9✔
286
            auto pk = key->public_key();
5✔
287
            if(der_out) {
5✔
288
               write_output(Botan::X509::BER_encode(*pk));
2✔
289
            } else {
290
               output() << Botan::X509::PEM_encode(*pk);
8✔
291
            }
292
         } else {
5✔
293
            const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
8✔
294

295
            if(der_out) {
4✔
296
               if(pass_out.empty()) {
1✔
297
                  write_output(Botan::PKCS8::BER_encode(*key));
×
298
               } else {
299
                  if(get_arg("pbkdf-iter").empty()) {
1✔
300
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(
2✔
301
                        *key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf")));
3✔
302
                  } else {
303
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(
×
304
                        *key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf")));
×
305
                  }
306
               }
307
            } else {
308
               if(pass_out.empty()) {
3✔
309
                  output() << Botan::PKCS8::PEM_encode(*key);
4✔
310
               } else {
311
                  if(get_arg("pbkdf-iter").empty()) {
1✔
312
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(
3✔
313
                        *key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf"));
4✔
314
                  } else {
315
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(
×
316
                        *key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf"));
×
317
                  }
318
               }
319
            }
320
         }
4✔
321
      }
18✔
322
};
323

324
BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
10✔
325

326
   #endif
327

328
   #if defined(BOTAN_HAS_ECC_GROUP)
329

330
class EC_Group_Info final : public Command {
331
   public:
332
      EC_Group_Info() : Command("ec_group_info --pem name") {}
6✔
333

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

336
      std::string description() const override {
1✔
337
         return "Print raw elliptic curve domain parameters of the standardized curve name";
1✔
338
      }
339

340
      void go() override {
2✔
341
         const auto ec_group = Botan::EC_Group::from_name(get_arg("name"));
4✔
342

343
         if(flag_set("pem")) {
2✔
344
            output() << ec_group.PEM_encode(Botan::EC_Group_Encoding::NamedCurve);
2✔
345
         } else {
346
            output() << "P = " << std::hex << ec_group.get_p() << "\n"
1✔
347
                     << "A = " << std::hex << ec_group.get_a() << "\n"
1✔
348
                     << "B = " << std::hex << ec_group.get_b() << "\n"
1✔
349
                     << "N = " << std::hex << ec_group.get_order() << "\n"
1✔
350
                     << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
1✔
351
         }
352
      }
2✔
353
};
354

355
BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
3✔
356

357
   #endif
358

359
   #if defined(BOTAN_HAS_DL_GROUP)
360

361
class DL_Group_Info final : public Command {
362
   public:
363
      DL_Group_Info() : Command("dl_group_info --pem name") {}
16✔
364

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

367
      std::string description() const override {
1✔
368
         return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
1✔
369
      }
370

371
      void go() override {
7✔
372
         auto dl_group = Botan::DL_Group::from_name(get_arg("name"));
14✔
373

374
         if(flag_set("pem")) {
7✔
375
            output() << dl_group.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42_DH_PARAMETERS);
×
376
         } else {
377
            output() << "P = " << std::hex << dl_group.get_p() << "\n"
7✔
378
                     << "G = " << dl_group.get_g() << "\n";
7✔
379
         }
380
      }
7✔
381
};
382

383
BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
8✔
384

385
class PK_Workfactor final : public Command {
386
   public:
387
      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
12✔
388

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

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

393
      void go() override {
5✔
394
         const size_t bits = get_arg_sz("bits");
5✔
395
         const std::string type = get_arg("type");
5✔
396

397
         if(type == "rsa") {
5✔
398
            output() << Botan::if_work_factor(bits) << "\n";
3✔
399
         } else if(type == "dl") {
2✔
400
            output() << Botan::dl_work_factor(bits) << "\n";
1✔
401
         } else if(type == "dl_exp") {
1✔
402
            output() << Botan::dl_exponent_size(bits) << "\n";
1✔
403
         } else {
404
            throw CLI_Usage_Error("Unknown type for pk_workfactor (rsa, dl, dl_exp)");
×
405
         }
406
      }
5✔
407
};
408

409
BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
6✔
410

411
class Gen_DL_Group final : public Command {
412
   public:
413
      Gen_DL_Group() : Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup") {}
6✔
414

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

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

419
      void go() override {
2✔
420
         const size_t pbits = get_arg_sz("pbits");
2✔
421
         const size_t qbits = get_arg_sz("qbits");
2✔
422

423
         const std::string type = get_arg("type");
2✔
424
         const std::string seed_str = get_arg("seed");
2✔
425

426
         if(type == "strong") {
2✔
427
            if(!seed_str.empty()) {
×
428
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
429
            }
430
            Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
×
431
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
×
432
         } else if(type == "subgroup") {
2✔
433
            if(!seed_str.empty()) {
1✔
434
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
435
            }
436
            Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
1✔
437
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
2✔
438
         } else if(type == "dsa") {
2✔
439
            size_t dsa_qbits = qbits;
1✔
440
            if(dsa_qbits == 0) {
1✔
441
               if(pbits == 1024) {
1✔
442
                  dsa_qbits = 160;
443
               } else if(pbits == 2048 || pbits == 3072) {
×
444
                  dsa_qbits = 256;
445
               } else {
446
                  throw CLI_Usage_Error("Invalid DSA p/q sizes");
×
447
               }
448
            }
449

450
            if(seed_str.empty()) {
1✔
451
               Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
1✔
452
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
2✔
453
            } else {
1✔
454
               const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
×
455
               Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
×
456
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
×
457
            }
×
458

459
         } else {
460
            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
×
461
         }
462
      }
2✔
463
};
464

465
BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
3✔
466

467
   #endif
468

469
}  // namespace Botan_CLI
470

471
#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