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

randombit / botan / 32615609767

23 Aug 2026 01:48AM UTC coverage: 89.812% (+0.02%) from 89.79%
32615609767

push

github

web-flow
Merge pull request #5857 from randombit/jack/whirlpool-generic-simd

Convert the AVX2 Whirlpool implementation to use SIMD_8x32

122363 of 136243 relevant lines covered (89.81%)

10491164.61 hits per line

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

78.16
/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() :
24 ✔
41
            Command(
42
               "keygen --algo=RSA --params= --passphrase= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --provider= --rng-type= --drbg-seed= --der-out") {
24 ✔
43
      }
24 ✔
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 {
23 ✔
50
         const std::string algo = get_arg("algo");
23 ✔
51
         const std::string params = get_arg("params");
23 ✔
52
         const std::string provider = get_arg("provider");
23 ✔
53

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

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

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

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

65
         if(der_out) {
23 ✔
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()) {
23 ✔
79
               output() << Botan::PKCS8::PEM_encode(*key);
46 ✔
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
      }
46 ✔
91
};
92

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

95
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
96

97
namespace {
98

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

108
}  // namespace
109

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

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

120
         auto options = Botan::PK_Signature_Options();
20 ✔
121

122
         const bool prehashed = flag_set("prehashed");
20 ✔
123

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

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

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

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

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

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

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

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

171
         options = options.with_provider(get_arg_or("provider", ""));
20 ✔
172

173
         return options;
20 ✔
174
      }
20 ✔
175
};
176

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

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

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

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

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

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

195
            if(no_fsname || key_file == "-") {
8 ✔
196
               output() << fprint << "\n";
7 ✔
197
            } else {
198
               output() << key_file << ": " << fprint << "\n";
1 ✔
199
            }
200
         }
23 ✔
201
      }
7 ✔
202
};
203

204
BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
8 ✔
205

206
namespace {
207

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

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

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

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

230
}  // namespace
231

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

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

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

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

247
         auto key = load_private_key(key_file, passphrase);
8 ✔
248

249
         Botan::PK_Signer signer(*key, rng(), signature_options(*key));
8 ✔
250

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

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

256
         if(key->stateful_operation()) {
8 ✔
257
            std::ofstream updated_key(key_file);
×
258
            if(passphrase.empty()) {
×
259
               updated_key << Botan::PKCS8::PEM_encode(*key);
×
260
            } else {
261
               updated_key << Botan::PKCS8::PEM_encode(*key, rng(), passphrase);
×
262
            }
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
         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
         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
      }
24 ✔
301
};
302

303
BOTAN_REGISTER_COMMAND("verify", PK_Verify);
13 ✔
304

305
class PKCS8_Tool final : public Command {
306
   public:
307
      PKCS8_Tool() :
12 ✔
308
            Command(
309
               "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --rng-type= --drbg-seed= key") {
12 ✔
310
      }
12 ✔
311

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

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

316
      void go() override {
11 ✔
317
         const std::string key_file = get_arg("key");
11 ✔
318
         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
22 ✔
319

320
         Botan::DataSource_Memory key_src(slurp_file(key_file));
22 ✔
321
         std::unique_ptr<Botan::Private_Key> key;
11 ✔
322

323
         if(pass_in.empty()) {
11 ✔
324
            key = Botan::PKCS8::load_key(key_src);
9 ✔
325
         } else {
326
            key = Botan::PKCS8::load_key(key_src, pass_in);
2 ✔
327
         }
328

329
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
11 ✔
330
         const bool der_out = flag_set("der-out");
11 ✔
331

332
         if(flag_set("pub-out")) {
11 ✔
333
            auto pk = key->public_key();
7 ✔
334
            if(der_out) {
7 ✔
335
               write_output(Botan::X509::BER_encode(*pk));
2 ✔
336
            } else {
337
               output() << Botan::X509::PEM_encode(*pk);
12 ✔
338
            }
339
         } else {
7 ✔
340
            const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
8 ✔
341

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

371
BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
12 ✔
372

373
   #endif
374

375
   #if defined(BOTAN_HAS_ECC_GROUP)
376

377
class EC_Group_Info final : public Command {
378
   public:
379
      EC_Group_Info() : Command("ec_group_info --pem name") {}
6 ✔
380

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

383
      std::string description() const override {
1 ✔
384
         return "Print raw elliptic curve domain parameters of the standardized curve name";
1 ✔
385
      }
386

387
      void go() override {
2 ✔
388
         const auto ec_group = Botan::EC_Group::from_name(get_arg("name"));
4 ✔
389

390
         if(flag_set("pem")) {
2 ✔
391
            output() << ec_group.PEM_encode(Botan::EC_Group_Encoding::NamedCurve);
2 ✔
392
         } else {
393
            output() << "P = " << std::hex << ec_group.get_p() << "\n"
1 ✔
394
                     << "A = " << std::hex << ec_group.get_a() << "\n"
1 ✔
395
                     << "B = " << std::hex << ec_group.get_b() << "\n"
1 ✔
396
                     << "N = " << std::hex << ec_group.get_order() << "\n"
1 ✔
397
                     << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
1 ✔
398
         }
399
      }
2 ✔
400
};
401

402
BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
3 ✔
403

404
   #endif
405

406
   #if defined(BOTAN_HAS_DL_GROUP)
407

408
class DL_Group_Info final : public Command {
409
   public:
410
      DL_Group_Info() : Command("dl_group_info --pem name") {}
16 ✔
411

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

414
      std::string description() const override {
1 ✔
415
         return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
1 ✔
416
      }
417

418
      void go() override {
7 ✔
419
         auto dl_group = Botan::DL_Group::from_name(get_arg("name"));
14 ✔
420

421
         if(flag_set("pem")) {
7 ✔
422
            output() << dl_group.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42_DH_PARAMETERS);
×
423
         } else {
424
            output() << "P = " << std::hex << dl_group.get_p() << "\n"
7 ✔
425
                     << "G = " << dl_group.get_g() << "\n";
7 ✔
426
         }
427
      }
7 ✔
428
};
429

430
BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
8 ✔
431

432
class PK_Workfactor final : public Command {
433
   public:
434
      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
12 ✔
435

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

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

440
      void go() override {
5 ✔
441
         const size_t bits = get_arg_sz("bits");
5 ✔
442
         const std::string type = get_arg("type");
5 ✔
443

444
         if(type == "rsa") {
5 ✔
445
            output() << Botan::if_work_factor(bits) << "\n";
3 ✔
446
         } else if(type == "dl") {
2 ✔
447
            output() << Botan::dl_work_factor(bits) << "\n";
1 ✔
448
         } else if(type == "dl_exp") {
1 ✔
449
            output() << Botan::dl_exponent_size(bits) << "\n";
1 ✔
450
         } else {
451
            throw CLI_Usage_Error("Unknown type for pk_workfactor (rsa, dl, dl_exp)");
×
452
         }
453
      }
5 ✔
454
};
455

456
BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
6 ✔
457

458
class Gen_DL_Group final : public Command {
459
   public:
460
      Gen_DL_Group() :
3 ✔
461
            Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup --rng-type= --drbg-seed=") {}
6 ✔
462

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

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

467
      void go() override {
2 ✔
468
         const size_t pbits = get_arg_sz("pbits");
2 ✔
469
         const size_t qbits = get_arg_sz("qbits");
2 ✔
470

471
         const std::string type = get_arg("type");
2 ✔
472
         const std::string seed_str = get_arg("seed");
2 ✔
473

474
         if(type == "strong") {
2 ✔
475
            if(!seed_str.empty()) {
×
476
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
477
            }
478
            const Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
×
479
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
×
480
         } else if(type == "subgroup") {
2 ✔
481
            if(!seed_str.empty()) {
1 ✔
482
               throw CLI_Usage_Error("Seed only supported for DSA param gen");
×
483
            }
484
            const Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
1 ✔
485
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
2 ✔
486
         } else if(type == "dsa") {
2 ✔
487
            size_t dsa_qbits = qbits;
1 ✔
488
            if(dsa_qbits == 0) {
1 ✔
489
               if(pbits == 1024) {
1 ✔
490
                  dsa_qbits = 160;
491
               } else if(pbits == 2048 || pbits == 3072) {
×
492
                  dsa_qbits = 256;
493
               } else {
494
                  throw CLI_Usage_Error("Invalid DSA p/q sizes");
×
495
               }
496
            }
497

498
            if(seed_str.empty()) {
1 ✔
499
               const Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
1 ✔
500
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
2 ✔
501
            } else {
1 ✔
502
               const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
×
503
               const Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
×
504
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
×
505
            }
×
506

507
         } else {
508
            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
×
509
         }
510
      }
2 ✔
511
};
512

513
BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
3 ✔
514

515
   #endif
516

517
}  // namespace
518

519
}  // namespace Botan_CLI
520

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