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

randombit / botan / 26799176800

01 Jun 2026 11:54PM UTC coverage: 89.37%. Remained the same
26799176800

push

github

web-flow
Merge pull request #5635 from randombit/jack/rng-thread-fixes

Fix some RNG fork/thread safety bugs

110359 of 123486 relevant lines covered (89.37%)

10923314.11 hits per line

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

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

34
   BER_Decoder(source, BER_Decoder::Limits::DER())
1,002✔
35
      .start_sequence()
501✔
36
      .decode(pbe_alg_id)
501✔
37
      .decode(key_data, ASN1_Type::OctetString)
501✔
38
      .end_cons()
501✔
39
      .verify_end();
501✔
40

41
   return key_data;
501✔
42
}
×
43

44
/*
45
* PEM decode and/or decrypt a private key
46
*/
47
secure_vector<uint8_t> PKCS8_decode(DataSource& source,
4,947✔
48
                                    const std::function<std::string()>& get_passphrase,
49
                                    AlgorithmIdentifier& pk_alg_id,
50
                                    bool is_encrypted) {
51
   AlgorithmIdentifier pbe_alg_id;
4,947✔
52
   secure_vector<uint8_t> key_data;
4,947✔
53
   secure_vector<uint8_t> key;
4,947✔
54

55
   try {
4,947✔
56
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
4,947✔
57
         if(is_encrypted) {
2,173✔
58
            key_data = PKCS8_extract(source, pbe_alg_id);
510✔
59
         } else {
60
            // todo read more efficiently
61
            while(auto b = source.read_byte()) {
1,205,860✔
62
               key_data.push_back(*b);
1,203,942✔
63
            }
1,203,942✔
64
         }
65
      } else {
66
         std::string label;
2,773✔
67
         key_data = PEM_Code::decode(source, label);
5,484✔
68

69
         // todo remove autodetect for pem as well?
70
         if(label == "PRIVATE KEY") {
2,711✔
71
            is_encrypted = false;
72
         } else if(label == "ENCRYPTED PRIVATE KEY") {
274✔
73
            DataSource_Memory key_source(key_data);
336✔
74
            key_data = PKCS8_extract(key_source, pbe_alg_id);
492✔
75
         } else {
246✔
76
            throw PKCS8_Exception(fmt("Unknown PEM label '{}'", label));
28✔
77
         }
78
      }
2,773✔
79

80
      if(key_data.empty()) {
4,856✔
81
         throw PKCS8_Exception("No key data found");
×
82
      }
83
   } catch(Decoding_Error& e) {
91✔
84
      throw Decoding_Error("PKCS #8 private key decoding", e);
83✔
85
   }
83✔
86

87
   try {
4,856✔
88
      if(is_encrypted) {
4,856✔
89
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
501✔
90
            throw PKCS8_Exception(fmt("Unknown PBE type {}", pbe_alg_id.oid()));
×
91
         }
92

93
#if defined(BOTAN_HAS_PKCS5_PBES2)
94
         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.parameters());
1,503✔
95
#else
96
         BOTAN_UNUSED(get_passphrase);
97
         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
98
#endif
99
      } else {
100
         key = key_data;
4,355✔
101
      }
102

103
      BER_Decoder(key, BER_Decoder::Limits::DER())
11,268✔
104
         .start_sequence()
3,709✔
105
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
7,352✔
106
         .decode(pk_alg_id)
3,643✔
107
         .decode(key, ASN1_Type::OctetString)
3,638✔
108
         .discard_remaining()
3,638✔
109
         .end_cons()
3,638✔
110
         .verify_end();
3,638✔
111
   } catch(std::exception& e) {
1,556✔
112
      throw Decoding_Error("PKCS #8 private key decoding", e);
1,556✔
113
   }
1,556✔
114
   return key;
6,600✔
115
}
6,594✔
116

117
}  // namespace
118

119
/*
120
* PEM encode a PKCS #8 private key, unencrypted
121
*/
122
std::string PEM_encode(const Private_Key& key) {
60✔
123
   return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
180✔
124
}
125

126
#if defined(BOTAN_HAS_PKCS5_PBES2)
127

128
namespace {
129

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

139
      if(nonstandard_pk) {
26✔
140
   #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
141
         return std::make_pair("AES-256/SIV", "SHA-512");
×
142
   #elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
143
         return std::make_pair("AES-256/GCM", "SHA-512");
144
   #endif
145
      }
146

147
      // Default is something compatible with everyone else
148
      return std::make_pair("AES-256/CBC", "SHA-256");
26✔
149
   }
150

151
   const SCAN_Name request(pbe_algo);
444✔
152

153
   if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) {
444✔
154
      throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo));
×
155
   }
156

157
   return std::make_pair(request.arg(0), request.arg(1));
888✔
158
}
444✔
159

160
}  // namespace
161

162
#endif
163

164
/*
165
* BER encode a PKCS #8 private key, encrypted
166
*/
167
std::vector<uint8_t> BER_encode(const Private_Key& key,
470✔
168
                                RandomNumberGenerator& rng,
169
                                std::string_view pass,
170
                                std::chrono::milliseconds msec,
171
                                std::string_view pbe_algo) {
172
#if defined(BOTAN_HAS_PKCS5_PBES2)
173
   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
470✔
174

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

178
   std::vector<uint8_t> output;
470✔
179
   DER_Encoder der(output);
470✔
180
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
470✔
181

182
   return output;
470✔
183
#else
184
   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
185
   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
186
#endif
187
}
470✔
188

189
/*
190
* PEM encode a PKCS #8 private key, encrypted
191
*/
192
std::string PEM_encode(const Private_Key& key,
242✔
193
                       RandomNumberGenerator& rng,
194
                       std::string_view pass,
195
                       std::chrono::milliseconds msec,
196
                       std::string_view pbe_algo) {
197
   if(pass.empty()) {
242✔
198
      return PEM_encode(key);
×
199
   }
200

201
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
726✔
202
}
203

204
/*
205
* BER encode a PKCS #8 private key, encrypted
206
*/
207
std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
48✔
208
                                                     RandomNumberGenerator& rng,
209
                                                     std::string_view pass,
210
                                                     size_t pbkdf_iterations,
211
                                                     std::string_view cipher,
212
                                                     std::string_view pbkdf_hash) {
213
#if defined(BOTAN_HAS_PKCS5_PBES2)
214
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
48✔
215
      pbes2_encrypt_iter(key.private_key_info(),
48✔
216
                         pass,
217
                         pbkdf_iterations,
218
                         cipher.empty() ? "AES-256/CBC" : cipher,
48✔
219
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
48✔
220
                         rng);
96✔
221

222
   std::vector<uint8_t> output;
48✔
223
   DER_Encoder der(output);
48✔
224
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
48✔
225

226
   return output;
48✔
227

228
#else
229
   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
230
   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
231
#endif
232
}
48✔
233

234
/*
235
* PEM encode a PKCS #8 private key, encrypted
236
*/
237
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
24✔
238
                                            RandomNumberGenerator& rng,
239
                                            std::string_view pass,
240
                                            size_t pbkdf_iterations,
241
                                            std::string_view cipher,
242
                                            std::string_view pbkdf_hash) {
243
   return PEM_Code::encode(PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
24✔
244
                           "ENCRYPTED PRIVATE KEY");
48✔
245
}
246

247
/*
248
* BER encode a PKCS #8 private key, encrypted
249
*/
250
std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
20✔
251
                                                     RandomNumberGenerator& rng,
252
                                                     std::string_view pass,
253
                                                     std::chrono::milliseconds pbkdf_msec,
254
                                                     size_t* pbkdf_iterations,
255
                                                     std::string_view cipher,
256
                                                     std::string_view pbkdf_hash) {
257
#if defined(BOTAN_HAS_PKCS5_PBES2)
258
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
20✔
259
      pbes2_encrypt_msec(key.private_key_info(),
20✔
260
                         pass,
261
                         pbkdf_msec,
262
                         pbkdf_iterations,
263
                         cipher.empty() ? "AES-256/CBC" : cipher,
20✔
264
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
20✔
265
                         rng);
40✔
266

267
   std::vector<uint8_t> output;
20✔
268
   DER_Encoder(output)
20✔
269
      .start_sequence()
20✔
270
      .encode(pbe_info.first)
20✔
271
      .encode(pbe_info.second, ASN1_Type::OctetString)
40✔
272
      .end_cons();
20✔
273

274
   return output;
20✔
275
#else
276
   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
277
   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
278
#endif
279
}
20✔
280

281
/*
282
* PEM encode a PKCS #8 private key, encrypted
283
*/
284
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
4✔
285
                                            RandomNumberGenerator& rng,
286
                                            std::string_view pass,
287
                                            std::chrono::milliseconds pbkdf_msec,
288
                                            size_t* pbkdf_iterations,
289
                                            std::string_view cipher,
290
                                            std::string_view pbkdf_hash) {
291
   return PEM_Code::encode(
4✔
292
      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
4✔
293
      "ENCRYPTED PRIVATE KEY");
8✔
294
}
295

296
namespace {
297

298
/*
299
* Extract a private key (encrypted/unencrypted) and return it
300
*/
301
std::unique_ptr<Private_Key> load_key(DataSource& source,
4,947✔
302
                                      const std::function<std::string()>& get_pass,
303
                                      bool is_encrypted) {
304
   AlgorithmIdentifier alg_id;
4,947✔
305
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
4,947✔
306

307
   const std::string alg_name = alg_id.oid().human_name_or_empty();
3,300✔
308
   if(alg_name.empty()) {
3,300✔
309
      throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
×
310
   }
311

312
   return load_private_key(alg_id, pkcs8_key);
6,511✔
313
}
8,247✔
314

315
}  // namespace
316

317
/*
318
* Extract an encrypted private key and return it
319
*/
320
std::unique_ptr<Private_Key> load_key(DataSource& source, const std::function<std::string()>& get_pass) {
11✔
321
   return load_key(source, get_pass, true);
11✔
322
}
323

324
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source,
×
325
                                      const std::function<std::string()>& get_passphrase) {
326
   Botan::DataSource_Memory ds(source);
×
327
   return load_key(ds, get_passphrase);
×
328
}
×
329

330
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source, std::string_view pass) {
×
331
   Botan::DataSource_Memory ds(source);
×
332
   return load_key(ds, pass);
×
333
}
×
334

335
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source) {
14✔
336
   Botan::DataSource_Memory ds(source);
14✔
337
   return load_key(ds);
14✔
338
}
14✔
339

340
/*
341
* Extract an encrypted private key and return it
342
*/
343
std::unique_ptr<Private_Key> load_key(DataSource& source, std::string_view pass) {
524✔
344
   return load_key(
524✔
345
      source, [pass]() { return std::string(pass); }, true);
1,539✔
346
}
347

348
/*
349
* Extract an unencrypted private key and return it
350
*/
351
std::unique_ptr<Private_Key> load_key(DataSource& source) {
4,412✔
352
   auto fail_fn = []() -> std::string {
4,412✔
353
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
×
354
   };
355

356
   return load_key(source, fail_fn, false);
7,088✔
357
}
358

359
}  // 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