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

randombit / botan / 12831658451

17 Jan 2025 03:07PM UTC coverage: 91.206% (-0.006%) from 91.212%
12831658451

Pull #4567

github

web-flow
Merge 34786d9de into 6a97b80e9
Pull Request #4567: Context support for ML-DSA and SLH-DSA

93825 of 102871 relevant lines covered (91.21%)

11369960.98 hits per line

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

79.41
/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/workfactor.h>
25

26
   #include <fstream>
27
   #include <sstream>
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
std::string_view normalize_padding(const std::string& algo, const std::string& requested_padding) {
7✔
101
   if(algo == "RSA" && requested_padding.empty()) {
7✔
102
      return "PSS";
×
103
   } else {
104
      return requested_padding;
7✔
105
   }
106
}
107

108
}  // namespace
109

110
class PK_Fingerprint final : public Command {
111
   public:
112
      PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
16✔
113

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

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

118
      void go() override {
7✔
119
         const std::string hash_algo = get_arg("algo");
7✔
120
         const bool no_fsname = flag_set("no-fsname");
7✔
121

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

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

128
            if(no_fsname || key_file == "-") {
8✔
129
               output() << fprint << "\n";
7✔
130
            } else {
131
               output() << key_file << ": " << fprint << "\n";
1✔
132
            }
133
         }
23✔
134
      }
7✔
135
};
136

137
BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
8✔
138

139
namespace {
140

141
std::unique_ptr<Botan::Private_Key> load_private_key(const std::string& key_filename, const std::string& passphrase) {
3✔
142
   std::string err_string;
3✔
143

144
   try {
3✔
145
      Botan::DataSource_Stream input(key_filename);
3✔
146
      return Botan::PKCS8::load_key(input, passphrase);
3✔
147
   } catch(Botan::Exception& e) {
3✔
148
      err_string = e.what();
×
149
   }
×
150

151
   if(passphrase.empty()) {
×
152
      try {
×
153
         Botan::DataSource_Stream input(key_filename);
×
154
         return Botan::PKCS8::load_key(input);
×
155
      } catch(Botan::Exception& e) {
×
156
         err_string = e.what();
×
157
      }
×
158
   }
159

160
   throw CLI_Error("Loading private key failed (" + err_string + ")");
×
161
}
3✔
162

163
}  // namespace
164

165
class PK_Sign final : public Command {
166
   public:
167
      PK_Sign() : Command("sign --der-format --passphrase= --hash=SHA-256 --padding= --provider= key file") {}
8✔
168

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

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

173
      void go() override {
3✔
174
         const std::string key_file = get_arg("key");
3✔
175
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
6✔
176

177
         auto key = load_private_key(key_file, passphrase);
3✔
178

179
         const std::string hash_fn = get_arg("hash");
3✔
180

181
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn)) {
6✔
182
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
183
         }
184

185
         auto signer_builder = key->signer()
3✔
186
                                  .with_rng(rng())
9✔
187
                                  .with_hash(hash_fn)
6✔
188
                                  .with_der_encoded_signature(flag_set("der-format"))
6✔
189
                                  .with_provider(get_arg("provider"));
6✔
190

191
         if(const auto padding = normalize_padding(key->algo_name(), get_arg("padding")); !padding.empty()) {
3✔
192
            signer_builder.with_padding(padding);
×
193
         }
194

195
         auto signer = signer_builder.create();
3✔
196

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

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

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

211
         output() << Botan::base64_encode(sig) << "\n";
6✔
212
      }
9✔
213
};
214

215
BOTAN_REGISTER_COMMAND("sign", PK_Sign);
4✔
216

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

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

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

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

233
         const std::string hash_fn = get_arg("hash");
4✔
234

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

239
         auto verifier_builder =
4✔
240
            key->signature_verifier().with_hash(hash_fn).with_der_encoded_signature(flag_set("der-format"));
16✔
241

242
         if(const auto padding = normalize_padding(key->algo_name(), get_arg("padding")); !padding.empty()) {
4✔
243
            verifier_builder.with_padding(padding);
×
244
         }
245

246
         auto verifier = verifier_builder.create();
4✔
247

248
         auto onData = [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); };
4✔
249
         Command::read_file(get_arg("file"), onData);
8✔
250

251
         const Botan::secure_vector<uint8_t> signature =
4✔
252
            Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
8✔
253

254
         const bool valid = verifier.check_signature(signature);
4✔
255

256
         output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
5✔
257
      }
12✔
258
};
259

260
BOTAN_REGISTER_COMMAND("verify", PK_Verify);
5✔
261

262
class PKCS8_Tool final : public Command {
263
   public:
264
      PKCS8_Tool() :
10✔
265
            Command(
266
               "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= key") {
10✔
267
      }
10✔
268

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

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

273
      void go() override {
9✔
274
         const std::string key_file = get_arg("key");
9✔
275
         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
18✔
276

277
         Botan::DataSource_Memory key_src(slurp_file(key_file));
18✔
278
         std::unique_ptr<Botan::Private_Key> key;
9✔
279

280
         if(pass_in.empty()) {
9✔
281
            key = Botan::PKCS8::load_key(key_src);
7✔
282
         } else {
283
            key = Botan::PKCS8::load_key(key_src, pass_in);
2✔
284
         }
285

286
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
9✔
287
         const bool der_out = flag_set("der-out");
9✔
288

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

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

327
BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
10✔
328

329
   #endif
330

331
   #if defined(BOTAN_HAS_ECC_GROUP)
332

333
class EC_Group_Info final : public Command {
334
   public:
335
      EC_Group_Info() : Command("ec_group_info --pem name") {}
6✔
336

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

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

343
      void go() override {
2✔
344
         const auto ec_group = Botan::EC_Group::from_name(get_arg("name"));
4✔
345

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

358
BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
3✔
359

360
   #endif
361

362
   #if defined(BOTAN_HAS_DL_GROUP)
363

364
class DL_Group_Info final : public Command {
365
   public:
366
      DL_Group_Info() : Command("dl_group_info --pem name") {}
16✔
367

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

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

374
      void go() override {
7✔
375
         Botan::DL_Group dl_group(get_arg("name"));
14✔
376

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

386
BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
8✔
387

388
class PK_Workfactor final : public Command {
389
   public:
390
      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
12✔
391

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

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

396
      void go() override {
5✔
397
         const size_t bits = get_arg_sz("bits");
5✔
398
         const std::string type = get_arg("type");
5✔
399

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

412
BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
6✔
413

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

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

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

422
      void go() override {
2✔
423
         const size_t pbits = get_arg_sz("pbits");
2✔
424
         const size_t qbits = get_arg_sz("qbits");
2✔
425

426
         const std::string type = get_arg("type");
2✔
427
         const std::string seed_str = get_arg("seed");
2✔
428

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

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

462
         } else {
463
            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
×
464
         }
465
      }
2✔
466
};
467

468
BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
3✔
469

470
   #endif
471

472
}  // namespace Botan_CLI
473

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