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

randombit / botan / 4800496062

25 Apr 2023 06:07PM UTC coverage: 92.146% (-0.02%) from 92.164%
4800496062

push

github

77579 of 84191 relevant lines covered (92.15%)

11982024.49 hits per line

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

78.49
/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/pk_keys.h>
18
#include <botan/x509_key.h>
19
#include <botan/pk_algs.h>
20
#include <botan/pkcs8.h>
21
#include <botan/pubkey.h>
22
#include <botan/internal/workfactor.h>
23
#include <botan/data_src.h>
24
#include <botan/hash.h>
25

26
#include <sstream>
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
   {
41
   public:
42
      PK_Keygen() : Command("keygen --algo=RSA --params= --passphrase= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --provider= --der-out") {}
40✔
43

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

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

54
      void go() override
19✔
55
         {
56
         const std::string algo = get_arg("algo");
19✔
57
         const std::string params = get_arg("params");
19✔
58
         const std::string provider = get_arg("provider");
19✔
59

60
         std::unique_ptr<Botan::Private_Key> key =
19✔
61
            Botan::create_private_key(algo, rng(), params, provider);
19✔
62

63
         if(!key)
19✔
64
            {
65
            throw CLI_Error_Unsupported("keygen", algo);
×
66
            }
67

68
         const std::string pass = get_passphrase_arg("Key passphrase", "passphrase");
38✔
69
         const bool der_out = flag_set("der-out");
19✔
70

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

73
         if(der_out)
19✔
74
            {
75
            if(pass.empty())
×
76
               {
77
               write_output(Botan::PKCS8::BER_encode(*key));
×
78
               }
79
            else
80
               {
81
               if(get_arg("pbkdf-iter").empty())
×
82
                  {
83
                  write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(*key, rng(), pass, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf")));
×
84
                  }
85
               else
86
                  {
87
                  write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(*key, rng(), pass, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf")));
×
88
                  }
89
               }
90
            }
91
         else
92
            {
93
            if(pass.empty())
19✔
94
               {
95
               output() << Botan::PKCS8::PEM_encode(*key);
57✔
96
               }
97
            else
98
               {
99
               if(get_arg("pbkdf-iter").empty())
×
100
                  {
101
                  output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(*key, rng(), pass, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf"));
×
102
                  }
103
               else
104
                  {
105
                  output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(*key, rng(), pass, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf"));
×
106
                  }
107
               }
108
            }
109
         }
42✔
110
   };
111

112
BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);
20✔
113

114
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
115

116
namespace {
117

118
std::string choose_sig_padding(const std::string& key, const std::string& padding, const std::string& hash)
7✔
119
   {
120
   if(key == "RSA")
7✔
121
      {
122
      std::ostringstream oss;
×
123
      if(padding.empty())
×
124
         oss << "PSS";
×
125
      else
126
         oss << padding;
×
127

128
      oss << "(" << hash << ")";
×
129
      return oss.str();
×
130
      }
×
131
   else if(padding.empty())
7✔
132
      {
133
      return hash;
7✔
134
      }
135
   else if(hash.empty())
×
136
      {
137
      return padding;
7✔
138
      }
139
   else
140
      {
141
      std::ostringstream oss;
×
142
      oss << padding << "(" << hash << ")";
×
143
      return oss.str();
×
144
      }
×
145
   }
146

147
}
148

149
class PK_Fingerprint final : public Command
150
   {
151
   public:
152
      PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
16✔
153

154
      std::string group() const override
1✔
155
         {
156
         return "pubkey";
2✔
157
         }
158

159
      std::string description() const override
1✔
160
         {
161
         return "Calculate a public key fingerprint";
1✔
162
         }
163

164
      void go() override
7✔
165
         {
166
         const std::string hash_algo = get_arg("algo");
7✔
167
         const bool no_fsname = flag_set("no-fsname");
7✔
168

169
         for(const std::string& key_file : get_arg_list("keys"))
15✔
170
            {
171
            std::unique_ptr<Botan::Public_Key> key(
8✔
172
               key_file == "-"
8✔
173
               ? Botan::X509::load_key(this->slurp_file("-", 4096))
10✔
174
               : Botan::X509::load_key(key_file));
9✔
175

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

178
            if(no_fsname || key_file == "-")
8✔
179
               { output() << fprint << "\n"; }
7✔
180
            else
181
               { output() << key_file << ": " << fprint << "\n"; }
1✔
182
            }
23✔
183
         }
7✔
184
   };
185

186
BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
8✔
187

188
namespace {
189

190
std::unique_ptr<Botan::Private_Key>
191
load_private_key(const std::string& key_filename,
3✔
192
                 const std::string& passphrase)
193
   {
194
   std::string err_string;
3✔
195

196
   try
3✔
197
      {
198
      Botan::DataSource_Stream input(key_filename);
3✔
199
      return Botan::PKCS8::load_key(input, passphrase);
3✔
200
      }
3✔
201
   catch(Botan::Exception& e) { err_string = e.what(); }
×
202

203
   if(passphrase.empty())
×
204
      {
205
      try
×
206
         {
207
         Botan::DataSource_Stream input(key_filename);
×
208
         return Botan::PKCS8::load_key(input);
×
209
         }
×
210
      catch(Botan::Exception& e) { err_string = e.what(); }
×
211
      }
212

213
   throw CLI_Error("Loading private key failed (" + err_string + ")");
×
214
   }
3✔
215

216
}
217

218
class PK_Sign final : public Command
219
   {
220
   public:
221
      PK_Sign() : Command("sign --der-format --passphrase= --hash=SHA-256 --padding= --provider= key file") {}
8✔
222

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

228
      std::string description() const override
1✔
229
         {
230
         return "Sign arbitrary data";
1✔
231
         }
232

233
      void go() override
3✔
234
         {
235
         const std::string key_file = get_arg("key");
3✔
236
         const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
6✔
237

238
         auto key = load_private_key(key_file, passphrase);
3✔
239

240
         const std::string hash_fn = get_arg("hash");
3✔
241

242
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn))
6✔
243
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
244

245
         const std::string sig_padding =
3✔
246
            choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn);
6✔
247

248
         auto format = Botan::Signature_Format::Standard;
3✔
249

250
         if(flag_set("der-format"))
3✔
251
            {
252
            if(key->message_parts() == 1)
×
253
               throw CLI_Usage_Error("Key type " + key->algo_name() +
×
254
                                     " does not support DER formatting for signatures");
×
255
            format = Botan::Signature_Format::DerSequence;
256
            }
257

258
         const std::string provider = get_arg("provider");
3✔
259

260
         Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider);
3✔
261

262
         auto onData = [&signer](const uint8_t b[], size_t l)
6✔
263
            {
264
            signer.update(b, l);
3✔
265
            };
3✔
266
         Command::read_file(get_arg("file"), onData);
6✔
267

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

270
         if(key->stateful_operation())
3✔
271
            {
272
            std::ofstream updated_key(key_file);
2✔
273
            if(passphrase.empty())
2✔
274
               { updated_key << Botan::PKCS8::PEM_encode(*key); }
6✔
275
            else
276
               { updated_key << Botan::PKCS8::PEM_encode(*key, rng(), passphrase); }
×
277
            }
2✔
278

279
         output() << Botan::base64_encode(sig) << "\n";
6✔
280
         }
9✔
281
   };
282

283
BOTAN_REGISTER_COMMAND("sign", PK_Sign);
4✔
284

285
class PK_Verify final : public Command
286
   {
287
   public:
288
      PK_Verify() : Command("verify --der-format --hash=SHA-256 --padding= pubkey file signature") {}
10✔
289

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

295
      std::string description() const override
1✔
296
         {
297
         return "Verify the authenticity of the given file with the provided signature";
1✔
298
         }
299

300
      void go() override
4✔
301
         {
302
         auto key = Botan::X509::load_key(get_arg("pubkey"));
8✔
303
         if(!key)
4✔
304
            {
305
            throw CLI_Error("Unable to load public key");
×
306
            }
307

308
         const std::string hash_fn = get_arg("hash");
4✔
309

310
         if(!hash_fn.empty() && !Botan::HashFunction::create(hash_fn))
8✔
311
            throw CLI_Error_Unsupported("hashing", hash_fn);
×
312

313
         const std::string sig_padding =
4✔
314
            choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn);
8✔
315

316
         auto format = Botan::Signature_Format::Standard;
4✔
317
         if(flag_set("der-format"))
4✔
318
            {
319
            if(key->message_parts() == 1)
×
320
               throw CLI_Usage_Error("Key type " + key->algo_name() +
×
321
                                     " does not support DER formatting for signatures");
×
322
            format = Botan::Signature_Format::DerSequence;
323
            }
324

325
         Botan::PK_Verifier verifier(*key, sig_padding, format);
4✔
326
         auto onData = [&verifier](const uint8_t b[], size_t l)
8✔
327
            {
328
            verifier.update(b, l);
4✔
329
            };
4✔
330
         Command::read_file(get_arg("file"), onData);
8✔
331

332
         const Botan::secure_vector<uint8_t> signature =
4✔
333
            Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
10✔
334

335
         const bool valid = verifier.check_signature(signature);
4✔
336

337
         output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
5✔
338
         }
8✔
339
   };
340

341
BOTAN_REGISTER_COMMAND("verify", PK_Verify);
5✔
342

343
class PKCS8_Tool final : public Command
344
   {
345
   public:
346
      PKCS8_Tool() : Command("pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= key") {}
20✔
347

348
      std::string group() const override
1✔
349
         {
350
         return "pubkey";
1✔
351
         }
352

353
      std::string description() const override
1✔
354
         {
355
         return "Open a PKCS #8 formatted key";
1✔
356
         }
357

358
      void go() override
9✔
359
         {
360
         const std::string key_file = get_arg("key");
9✔
361
         const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
18✔
362

363
         Botan::DataSource_Memory key_src(slurp_file(key_file));
18✔
364
         std::unique_ptr<Botan::Private_Key> key;
9✔
365

366
         if(pass_in.empty())
9✔
367
            {
368
            key = Botan::PKCS8::load_key(key_src);
7✔
369
            }
370
         else
371
            {
372
            key = Botan::PKCS8::load_key(key_src, pass_in);
2✔
373
            }
374

375
         const std::chrono::milliseconds pbkdf_ms(get_arg_sz("pbkdf-ms"));
9✔
376
         const bool der_out = flag_set("der-out");
9✔
377

378
         if(flag_set("pub-out"))
9✔
379
            {
380
            if(der_out)
5✔
381
               {
382
               write_output(Botan::X509::BER_encode(*key));
2✔
383
               }
384
            else
385
               {
386
               output() << Botan::X509::PEM_encode(*key);
12✔
387
               }
388
            }
389
         else
390
            {
391
            const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
8✔
392

393
            if(der_out)
4✔
394
               {
395
               if(pass_out.empty())
1✔
396
                  {
397
                  write_output(Botan::PKCS8::BER_encode(*key));
×
398
                  }
399
               else
400
                  {
401
                  if(get_arg("pbkdf-iter").empty())
1✔
402
                     {
403
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(*key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf")));
2✔
404
                     }
405
                  else
406
                     {
407
                     write_output(Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(*key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf")));
×
408
                     }
409
                  }
410
               }
411
            else
412
               {
413
               if(pass_out.empty())
3✔
414
                  {
415
                  output() << Botan::PKCS8::PEM_encode(*key);
6✔
416
                  }
417
               else
418
                  {
419
                  if(get_arg("pbkdf-iter").empty())
1✔
420
                     {
421
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(*key, rng(), pass_out, pbkdf_ms, nullptr, get_arg("cipher"), get_arg("pbkdf"));
3✔
422
                     }
423
                  else
424
                     {
425
                     output() << Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(*key, rng(), pass_out, get_arg_sz("pbkdf-iter"), get_arg("cipher"), get_arg("pbkdf"));
×
426
                     }
427
                  }
428
               }
429
            }
4✔
430
         }
27✔
431
   };
432

433
BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
10✔
434

435
#endif
436

437
#if defined(BOTAN_HAS_ECC_GROUP)
438

439
class EC_Group_Info final : public Command
440
   {
441
   public:
442
      EC_Group_Info() : Command("ec_group_info --pem name") {}
6✔
443

444
      std::string group() const override
1✔
445
         {
446
         return "pubkey";
1✔
447
         }
448

449
      std::string description() const override
1✔
450
         {
451
         return "Print raw elliptic curve domain parameters of the standardized curve name";
1✔
452
         }
453

454
      void go() override
2✔
455
         {
456
         Botan::EC_Group ec_group(get_arg("name"));
4✔
457

458
         if(flag_set("pem"))
2✔
459
            {
460
            output() << ec_group.PEM_encode();
3✔
461
            }
462
         else
463
            {
464
            output() << "P = " << std::hex << ec_group.get_p() << "\n"
1✔
465
                     << "A = " << std::hex << ec_group.get_a() << "\n"
1✔
466
                     << "B = " << std::hex << ec_group.get_b() << "\n"
1✔
467
                     << "N = " << std::hex << ec_group.get_order() << "\n"
1✔
468
                     << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
1✔
469
            }
470

471
         }
2✔
472
   };
473

474
BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
3✔
475

476
#endif
477

478
#if defined(BOTAN_HAS_DL_GROUP)
479

480
class DL_Group_Info final : public Command
481
   {
482
   public:
483
      DL_Group_Info() : Command("dl_group_info --pem name") {}
16✔
484

485
      std::string group() const override
1✔
486
         {
487
         return "pubkey";
1✔
488
         }
489

490
      std::string description() const override
1✔
491
         {
492
         return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
1✔
493
         }
494

495
      void go() override
7✔
496
         {
497
         Botan::DL_Group dl_group(get_arg("name"));
14✔
498

499
         if(flag_set("pem"))
7✔
500
            {
501
            output() << dl_group.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42_DH_PARAMETERS);
×
502
            }
503
         else
504
            {
505
            output() << "P = " << std::hex << dl_group.get_p() << "\n"
7✔
506
                     << "G = " << dl_group.get_g() << "\n";
7✔
507
            }
508

509
         }
7✔
510
   };
511

512
BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
8✔
513

514
class PK_Workfactor final : public Command
515
   {
516
   public:
517
      PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
12✔
518

519
      std::string group() const override
1✔
520
         {
521
         return "pubkey";
1✔
522
         }
523

524
      std::string description() const override
1✔
525
         {
526
         return "Provide estimate of strength of public key based on size";
1✔
527
         }
528

529
      void go() override
5✔
530
         {
531
         const size_t bits = get_arg_sz("bits");
5✔
532
         const std::string type = get_arg("type");
5✔
533

534
         if(type == "rsa")
5✔
535
            { output() << Botan::if_work_factor(bits) << "\n"; }
3✔
536
         else if(type == "dl")
2✔
537
            { output() << Botan::dl_work_factor(bits) << "\n"; }
1✔
538
         else if(type == "dl_exp")
1✔
539
            { output() << Botan::dl_exponent_size(bits) << "\n"; }
1✔
540
         else
541
            { throw CLI_Usage_Error("Unknown type for pk_workfactor (rsa, dl, dl_exp)"); }
×
542
         }
5✔
543
   };
544

545
BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
6✔
546

547
class Gen_DL_Group final : public Command
548
   {
549
   public:
550
      Gen_DL_Group() : Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup") {}
6✔
551

552
      std::string group() const override
1✔
553
         {
554
         return "pubkey";
1✔
555
         }
556

557
      std::string description() const override
1✔
558
         {
559
         return "Generate ANSI X9.42 encoded Diffie-Hellman group parameters";
1✔
560
         }
561

562
      void go() override
2✔
563
         {
564
         const size_t pbits = get_arg_sz("pbits");
2✔
565
         const size_t qbits = get_arg_sz("qbits");
2✔
566

567
         const std::string type = get_arg("type");
2✔
568
         const std::string seed_str = get_arg("seed");
2✔
569

570
         if(type == "strong")
2✔
571
            {
572
            if(!seed_str.empty())
×
573
               { throw CLI_Usage_Error("Seed only supported for DSA param gen"); }
×
574
            Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
×
575
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
×
576
            }
×
577
         else if(type == "subgroup")
2✔
578
            {
579
            if(!seed_str.empty())
1✔
580
               { throw CLI_Usage_Error("Seed only supported for DSA param gen"); }
×
581
            Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
1✔
582
            output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42);
3✔
583
            }
1✔
584
         else if(type == "dsa")
1✔
585
            {
586
            size_t dsa_qbits = qbits;
1✔
587
            if(dsa_qbits == 0)
1✔
588
               {
589
               if(pbits == 1024)
1✔
590
                  { dsa_qbits = 160; }
591
               else if(pbits == 2048 || pbits == 3072)
×
592
                  { dsa_qbits = 256; }
593
               else
594
                  { throw CLI_Usage_Error("Invalid DSA p/q sizes"); }
×
595
               }
596

597
            if(seed_str.empty())
1✔
598
               {
599
               Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
1✔
600
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
3✔
601
               }
1✔
602
            else
603
               {
604
               const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
×
605
               Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
×
606
               output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57);
×
607
               }
×
608

609
            }
610
         else
611
            {
612
            throw CLI_Usage_Error("Invalid DL type '" + type + "'");
×
613
            }
614
         }
2✔
615
   };
616

617
BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
3✔
618

619
#endif
620

621
}
622

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

© 2025 Coveralls, Inc