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

randombit / botan / 16094367594

06 Jul 2025 02:24AM UTC coverage: 90.574%. Remained the same
16094367594

push

github

web-flow
Merge pull request #4959 from randombit/jack/fix-clang-tidy-cppcoreguidelines-init-variables

Enable and fix clang-tidy warning cppcoreguidelines-init-variables

99051 of 109359 relevant lines covered (90.57%)

12385300.74 hits per line

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

88.81
/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/assert.h>
12
#include <botan/ber_dec.h>
13
#include <botan/der_enc.h>
14
#include <botan/pem.h>
15
#include <botan/pk_algs.h>
16
#include <botan/rng.h>
17
#include <botan/internal/fmt.h>
18
#include <botan/internal/scan_name.h>
19

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

24
namespace Botan::PKCS8 {
25

26
namespace {
27

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

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

36
   return key_data;
499✔
37
}
×
38

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

49
   try {
4,785✔
50
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
4,785✔
51
         if(is_encrypted) {
2,169✔
52
            key_data = PKCS8_extract(source, pbe_alg_id);
506✔
53
         } else {
54
            // todo read more efficiently
55
            while(auto b = source.read_byte()) {
1,204,504✔
56
               key_data.push_back(*b);
1,202,588✔
57
            }
1,202,588✔
58
         }
59
      } else {
60
         std::string label;
2,615✔
61
         key_data = PEM_Code::decode(source, label);
5,168✔
62

63
         // todo remove autodetect for pem as well?
64
         if(label == "PRIVATE KEY") {
2,553✔
65
            is_encrypted = false;
66
         } else if(label == "ENCRYPTED PRIVATE KEY") {
274✔
67
            DataSource_Memory key_source(key_data);
336✔
68
            key_data = PKCS8_extract(key_source, pbe_alg_id);
492✔
69
         } else {
246✔
70
            throw PKCS8_Exception(fmt("Unknown PEM label '{}'", label));
28✔
71
         }
72
      }
2,615✔
73

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

81
   try {
4,694✔
82
      if(is_encrypted) {
4,694✔
83
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
499✔
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());
1,497✔
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;
4,195✔
95
      }
96

97
      BER_Decoder(key)
9,388✔
98
         .start_sequence()
4,600✔
99
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
9,099✔
100
         .decode(pk_alg_id)
4,499✔
101
         .decode(key, ASN1_Type::OctetString)
4,359✔
102
         .discard_remaining()
4,359✔
103
         .end_cons();
4,359✔
104
   } catch(std::exception& e) {
335✔
105
      throw Decoding_Error("PKCS #8 private key decoding", e);
335✔
106
   }
335✔
107
   return key;
8,718✔
108
}
5,211✔
109

110
}  // namespace
111

112
/*
113
* PEM encode a PKCS #8 private key, unencrypted
114
*/
115
std::string PEM_encode(const Private_Key& key) {
61✔
116
   return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
183✔
117
}
118

119
#if defined(BOTAN_HAS_PKCS5_PBES2)
120

121
namespace {
122

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

132
      if(nonstandard_pk) {
26✔
133
   #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
134
         return std::make_pair("AES-256/SIV", "SHA-512");
×
135
   #elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
136
         return std::make_pair("AES-256/GCM", "SHA-512");
137
   #endif
138
      }
139

140
      // Default is something compatible with everyone else
141
      return std::make_pair("AES-256/CBC", "SHA-256");
26✔
142
   }
143

144
   SCAN_Name request(pbe_algo);
444✔
145

146
   if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) {
444✔
147
      throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo));
×
148
   }
149

150
   return std::make_pair(request.arg(0), request.arg(1));
888✔
151
}
444✔
152

153
}  // namespace
154

155
#endif
156

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

168
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
470✔
169
      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr, pbe_params.first, pbe_params.second, rng);
470✔
170

171
   std::vector<uint8_t> output;
470✔
172
   DER_Encoder der(output);
470✔
173
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
470✔
174

175
   return output;
940✔
176
#else
177
   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
178
   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
179
#endif
180
}
940✔
181

182
/*
183
* PEM encode a PKCS #8 private key, encrypted
184
*/
185
std::string PEM_encode(const Private_Key& key,
242✔
186
                       RandomNumberGenerator& rng,
187
                       std::string_view pass,
188
                       std::chrono::milliseconds msec,
189
                       std::string_view pbe_algo) {
190
   if(pass.empty()) {
242✔
191
      return PEM_encode(key);
×
192
   }
193

194
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
726✔
195
}
196

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

215
   std::vector<uint8_t> output;
44✔
216
   DER_Encoder der(output);
44✔
217
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
44✔
218

219
   return output;
44✔
220

221
#else
222
   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
223
   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
224
#endif
225
}
44✔
226

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

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

260
   std::vector<uint8_t> output;
19✔
261
   DER_Encoder(output)
38✔
262
      .start_sequence()
19✔
263
      .encode(pbe_info.first)
19✔
264
      .encode(pbe_info.second, ASN1_Type::OctetString)
19✔
265
      .end_cons();
19✔
266

267
   return output;
19✔
268
#else
269
   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
270
   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
271
#endif
272
}
19✔
273

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

289
namespace {
290

291
/*
292
* Extract a private key (encrypted/unencrypted) and return it
293
*/
294
std::unique_ptr<Private_Key> load_key(DataSource& source,
4,785✔
295
                                      const std::function<std::string()>& get_pass,
296
                                      bool is_encrypted) {
297
   AlgorithmIdentifier alg_id;
4,785✔
298
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
4,785✔
299

300
   const std::string alg_name = alg_id.oid().human_name_or_empty();
4,359✔
301
   if(alg_name.empty()) {
4,359✔
302
      throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
4✔
303
   }
304

305
   return load_private_key(alg_id, pkcs8_key);
7,739✔
306
}
9,144✔
307

308
}  // namespace
309

310
/*
311
* Extract an encrypted private key and return it
312
*/
313
std::unique_ptr<Private_Key> load_key(DataSource& source, const std::function<std::string()>& get_pass) {
11✔
314
   return load_key(source, get_pass, true);
11✔
315
}
316

317
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source,
×
318
                                      const std::function<std::string()>& get_passphrase) {
319
   Botan::DataSource_Memory ds(source);
×
320
   return load_key(ds, get_passphrase);
×
321
}
×
322

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

328
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source) {
14✔
329
   Botan::DataSource_Memory ds(source);
14✔
330
   return load_key(ds);
14✔
331
}
14✔
332

333
/*
334
* Extract an encrypted private key and return it
335
*/
336
std::unique_ptr<Private_Key> load_key(DataSource& source, std::string_view pass) {
528✔
337
   return load_key(
528✔
338
      source, [pass]() { return std::string(pass); }, true);
1,545✔
339
}
340

341
/*
342
* Extract an unencrypted private key and return it
343
*/
344
std::unique_ptr<Private_Key> load_key(DataSource& source) {
4,246✔
345
   auto fail_fn = []() -> std::string {
4,246✔
346
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
×
347
   };
348

349
   return load_key(source, fail_fn, false);
7,087✔
350
}
351

352
}  // namespace Botan::PKCS8
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