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

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 hits per line

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

85.5
/src/lib/pubkey/pkcs8.cpp
1
/*
2
* PKCS #8
3
* (C) 1999-2010,2014,2018 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/pkcs8.h>
9

10
#include <botan/asn1_obj.h>
11
#include <botan/ber_dec.h>
12
#include <botan/der_enc.h>
13
#include <botan/pem.h>
14
#include <botan/pk_algs.h>
15
#include <botan/rng.h>
16
#include <botan/internal/fmt.h>
17
#include <botan/internal/scan_name.h>
18

19
#if defined(BOTAN_HAS_PKCS5_PBES2)
20
   #include <botan/internal/pbes2.h>
21
#endif
22

23
namespace Botan::PKCS8 {
24

25
namespace {
26

27
/*
28
* Get info from an EncryptedPrivateKeyInfo
29
*/
30
secure_vector<uint8_t> PKCS8_extract(DataSource& source, AlgorithmIdentifier& pbe_alg_id) {
223✔
31
   secure_vector<uint8_t> key_data;
223✔
32

33
   BER_Decoder(source).start_sequence().decode(pbe_alg_id).decode(key_data, ASN1_Type::OctetString).verify_end();
446✔
34

35
   return key_data;
223✔
36
}
×
37

38
/*
39
* PEM decode and/or decrypt a private key
40
*/
41
secure_vector<uint8_t> PKCS8_decode(DataSource& source,
4,284✔
42
                                    const std::function<std::string()>& get_passphrase,
43
                                    AlgorithmIdentifier& pk_alg_id,
44
                                    bool is_encrypted) {
45
   AlgorithmIdentifier pbe_alg_id;
4,284✔
46
   secure_vector<uint8_t> key_data, key;
4,284✔
47

48
   try {
4,284✔
49
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
4,284✔
50
         if(is_encrypted) {
1,879✔
51
            key_data = PKCS8_extract(source, pbe_alg_id);
230✔
52
         } else {
53
            // todo read more efficiently
54
            while(!source.end_of_data()) {
733,789✔
55
               uint8_t b;
732,025✔
56
               size_t read = source.read_byte(b);
732,025✔
57
               if(read) {
732,025✔
58
                  key_data.push_back(b);
732,025✔
59
               }
60
            }
61
         }
62
      } else {
63
         std::string label;
2,404✔
64
         key_data = PEM_Code::decode(source, label);
4,746✔
65

66
         // todo remove autodetect for pem as well?
67
         if(label == "PRIVATE KEY")
2,342✔
68
            is_encrypted = false;
69
         else if(label == "ENCRYPTED PRIVATE KEY") {
136✔
70
            DataSource_Memory key_source(key_data);
108✔
71
            key_data = PKCS8_extract(key_source, pbe_alg_id);
216✔
72
         } else {
108✔
73
            throw PKCS8_Exception(fmt("Unknown PEM label '{}'", label));
56✔
74
         }
75
      }
2,404✔
76

77
      if(key_data.empty())
4,193✔
78
         throw PKCS8_Exception("No key data found");
×
79
   } catch(Decoding_Error& e) { throw Decoding_Error("PKCS #8 private key decoding", e); }
91✔
80

81
   try {
4,193✔
82
      if(is_encrypted) {
4,193✔
83
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
223✔
84
            throw PKCS8_Exception(fmt("Unknown PBE type {}", pbe_alg_id.oid()));
×
85
         }
86

87
#if defined(BOTAN_HAS_PKCS5_PBES2)
88
         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.parameters());
815✔
89
#else
90
         BOTAN_UNUSED(get_passphrase);
91
         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
92
#endif
93
      } else
94
         key = key_data;
3,970✔
95

96
      BER_Decoder(key)
4,193✔
97
         .start_sequence()
8,292✔
98
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
8,092✔
99
         .decode(pk_alg_id)
3,993✔
100
         .decode(key, ASN1_Type::OctetString)
3,372✔
101
         .discard_remaining()
3,372✔
102
         .end_cons();
3,372✔
103
   } catch(std::exception& e) { throw Decoding_Error("PKCS #8 private key decoding", e); }
821✔
104
   return key;
6,744✔
105
}
5,196✔
106

107
}
108

109
/*
110
* PEM encode a PKCS #8 private key, unencrypted
111
*/
112
std::string PEM_encode(const Private_Key& key) { return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY"); }
180✔
113

114
#if defined(BOTAN_HAS_PKCS5_PBES2)
115

116
namespace {
117

118
std::pair<std::string, std::string> choose_pbe_params(std::string_view pbe_algo, std::string_view key_algo) {
194✔
119
   if(pbe_algo.empty()) {
194✔
120
      /*
121
      * For algorithms where we are using a non-RFC format anyway, default to
122
      * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
123
      * compatible.
124
      */
125
      const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
26✔
126

127
      if(nonstandard_pk) {
26✔
128
   #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
129
         return std::make_pair("AES-256/SIV", "SHA-512");
×
130
   #elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
131
         return std::make_pair("AES-256/GCM", "SHA-512");
132
   #endif
133
      }
134

135
      // Default is something compatible with everyone else
136
      return std::make_pair("AES-256/CBC", "SHA-256");
26✔
137
   }
138

139
   SCAN_Name request(pbe_algo);
168✔
140

141
   if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) {
672✔
142
      throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo));
×
143
   }
144

145
   return std::make_pair(request.arg(0), request.arg(1));
336✔
146
}
168✔
147

148
}
149

150
#endif
151

152
/*
153
* BER encode a PKCS #8 private key, encrypted
154
*/
155
std::vector<uint8_t> BER_encode(const Private_Key& key,
194✔
156
                                RandomNumberGenerator& rng,
157
                                std::string_view pass,
158
                                std::chrono::milliseconds msec,
159
                                std::string_view pbe_algo) {
160
#if defined(BOTAN_HAS_PKCS5_PBES2)
161
   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
194✔
162

163
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
194✔
164
      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr, pbe_params.first, pbe_params.second, rng);
194✔
165

166
   std::vector<uint8_t> output;
194✔
167
   DER_Encoder der(output);
194✔
168
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
194✔
169

170
   return output;
194✔
171
#else
172
   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
173
   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
174
#endif
175
}
194✔
176

177
/*
178
* PEM encode a PKCS #8 private key, encrypted
179
*/
180
std::string PEM_encode(const Private_Key& key,
104✔
181
                       RandomNumberGenerator& rng,
182
                       std::string_view pass,
183
                       std::chrono::milliseconds msec,
184
                       std::string_view pbe_algo) {
185
   if(pass.empty())
104✔
186
      return PEM_encode(key);
×
187

188
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
312✔
189
}
190

191
/*
192
* BER encode a PKCS #8 private key, encrypted
193
*/
194
std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
44✔
195
                                                     RandomNumberGenerator& rng,
196
                                                     std::string_view pass,
197
                                                     size_t pbkdf_iterations,
198
                                                     std::string_view cipher,
199
                                                     std::string_view pbkdf_hash) {
200
#if defined(BOTAN_HAS_PKCS5_PBES2)
201
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
44✔
202
      pbes2_encrypt_iter(key.private_key_info(),
88✔
203
                         pass,
204
                         pbkdf_iterations,
205
                         cipher.empty() ? "AES-256/CBC" : cipher,
44✔
206
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
44✔
207
                         rng);
132✔
208

209
   std::vector<uint8_t> output;
44✔
210
   DER_Encoder der(output);
44✔
211
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
44✔
212

213
   return output;
44✔
214

215
#else
216
   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
217
   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
218
#endif
219
}
44✔
220

221
/*
222
* PEM encode a PKCS #8 private key, encrypted
223
*/
224
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
22✔
225
                                            RandomNumberGenerator& rng,
226
                                            std::string_view pass,
227
                                            size_t pbkdf_iterations,
228
                                            std::string_view cipher,
229
                                            std::string_view pbkdf_hash) {
230
   return PEM_Code::encode(PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
22✔
231
                           "ENCRYPTED PRIVATE KEY");
44✔
232
}
233

234
/*
235
* BER encode a PKCS #8 private key, encrypted
236
*/
237
std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
19✔
238
                                                     RandomNumberGenerator& rng,
239
                                                     std::string_view pass,
240
                                                     std::chrono::milliseconds pbkdf_msec,
241
                                                     size_t* pbkdf_iterations,
242
                                                     std::string_view cipher,
243
                                                     std::string_view pbkdf_hash) {
244
#if defined(BOTAN_HAS_PKCS5_PBES2)
245
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
19✔
246
      pbes2_encrypt_msec(key.private_key_info(),
38✔
247
                         pass,
248
                         pbkdf_msec,
249
                         pbkdf_iterations,
250
                         cipher.empty() ? "AES-256/CBC" : cipher,
19✔
251
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
19✔
252
                         rng);
27✔
253

254
   std::vector<uint8_t> output;
19✔
255
   DER_Encoder(output)
38✔
256
      .start_sequence()
19✔
257
      .encode(pbe_info.first)
19✔
258
      .encode(pbe_info.second, ASN1_Type::OctetString)
19✔
259
      .end_cons();
19✔
260

261
   return output;
19✔
262
#else
263
   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
264
   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
265
#endif
266
}
19✔
267

268
/*
269
* PEM encode a PKCS #8 private key, encrypted
270
*/
271
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
4✔
272
                                            RandomNumberGenerator& rng,
273
                                            std::string_view pass,
274
                                            std::chrono::milliseconds pbkdf_msec,
275
                                            size_t* pbkdf_iterations,
276
                                            std::string_view cipher,
277
                                            std::string_view pbkdf_hash) {
278
   return PEM_Code::encode(
4✔
279
      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
4✔
280
      "ENCRYPTED PRIVATE KEY");
8✔
281
}
282

283
namespace {
284

285
/*
286
* Extract a private key (encrypted/unencrypted) and return it
287
*/
288
std::unique_ptr<Private_Key> load_key(DataSource& source,
4,284✔
289
                                      const std::function<std::string()>& get_pass,
290
                                      bool is_encrypted) {
291
   AlgorithmIdentifier alg_id;
4,284✔
292
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
4,284✔
293

294
   const std::string alg_name = alg_id.oid().human_name_or_empty();
3,372✔
295
   if(alg_name.empty()) {
3,372✔
296
      throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
56✔
297
   }
298

299
   return load_private_key(alg_id, pkcs8_key);
6,071✔
300
}
7,656✔
301

302
}
303

304
/*
305
* Extract an encrypted private key and return it
306
*/
307
std::unique_ptr<Private_Key> load_key(DataSource& source, const std::function<std::string()>& get_pass) {
11✔
308
   return load_key(source, get_pass, true);
11✔
309
}
310

311
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source,
×
312
                                      const std::function<std::string()>& get_passphrase) {
313
   Botan::DataSource_Memory ds(source);
×
314
   return load_key(ds, get_passphrase);
×
315
}
×
316

317
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source, std::string_view pass) {
×
318
   Botan::DataSource_Memory ds(source);
×
319
   return load_key(ds, pass);
×
320
}
×
321

322
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source) {
×
323
   Botan::DataSource_Memory ds(source);
×
324
   return load_key(ds);
×
325
}
×
326

327
/*
328
* Extract an encrypted private key and return it
329
*/
330
std::unique_ptr<Private_Key> load_key(DataSource& source, std::string_view pass) {
237✔
331
   return load_key(
237✔
332
      source, [pass]() { return std::string(pass); }, true);
687✔
333
}
334

335
/*
336
* Extract an unencrypted private key and return it
337
*/
338
std::unique_ptr<Private_Key> load_key(DataSource& source) {
4,036✔
339
   auto fail_fn = []() -> std::string {
4,036✔
340
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
×
341
   };
342

343
   return load_key(source, fail_fn, false);
6,487✔
344
}
345

346
}
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