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

randombit / botan / 21966968252

12 Feb 2026 10:36PM UTC coverage: 90.07% (+0.003%) from 90.067%
21966968252

Pull #5321

github

web-flow
Merge fd30428c7 into e7443105f
Pull Request #5321: Avoid various unneeded include files

102234 of 113505 relevant lines covered (90.07%)

11432801.46 hits per line

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

88.89
/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) {
501✔
31
   secure_vector<uint8_t> key_data;
501✔
32

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

35
   return key_data;
501✔
36
}
×
37

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

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

63
         // todo remove autodetect for pem as well?
64
         if(label == "PRIVATE KEY") {
2,547✔
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,609✔
73

74
      if(key_data.empty()) {
4,691✔
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,691✔
82
      if(is_encrypted) {
4,691✔
83
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
501✔
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,503✔
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,190✔
95
      }
96

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

110
}  // namespace
111

112
/*
113
* PEM encode a PKCS #8 private key, unencrypted
114
*/
115
std::string PEM_encode(const Private_Key& key) {
57✔
116
   return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
171✔
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
   const 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,
48✔
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 =
48✔
208
      pbes2_encrypt_iter(key.private_key_info(),
48✔
209
                         pass,
210
                         pbkdf_iterations,
211
                         cipher.empty() ? "AES-256/CBC" : cipher,
48✔
212
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
48✔
213
                         rng);
96✔
214

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

219
   return output;
48✔
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
}
48✔
226

227
/*
228
* PEM encode a PKCS #8 private key, encrypted
229
*/
230
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
24✔
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),
24✔
237
                           "ENCRYPTED PRIVATE KEY");
48✔
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,
20✔
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 =
20✔
252
      pbes2_encrypt_msec(key.private_key_info(),
20✔
253
                         pass,
254
                         pbkdf_msec,
255
                         pbkdf_iterations,
256
                         cipher.empty() ? "AES-256/CBC" : cipher,
20✔
257
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
20✔
258
                         rng);
40✔
259

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

267
   return output;
20✔
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
}
20✔
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,782✔
295
                                      const std::function<std::string()>& get_pass,
296
                                      bool is_encrypted) {
297
   AlgorithmIdentifier alg_id;
4,782✔
298
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
4,782✔
299

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

305
   return load_private_key(alg_id, pkcs8_key);
7,733✔
306
}
9,138✔
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) {
525✔
337
   return load_key(
525✔
338
      source, [pass]() { return std::string(pass); }, true);
1,541✔
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