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

randombit / botan / 13257645065

11 Feb 2025 07:10AM UTC coverage: 91.65% (-0.009%) from 91.659%
13257645065

Pull #4647

github

web-flow
Merge b9f3a0603 into f372b5a9e
Pull Request #4647: Avoid using mem_ops.h or assert.h in public headers

94860 of 103502 relevant lines covered (91.65%)

11460675.71 hits per line

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

88.97
/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,774✔
43
                                    const std::function<std::string()>& get_passphrase,
44
                                    AlgorithmIdentifier& pk_alg_id,
45
                                    bool is_encrypted) {
46
   AlgorithmIdentifier pbe_alg_id;
4,774✔
47
   secure_vector<uint8_t> key_data, key;
4,774✔
48

49
   try {
4,774✔
50
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
4,774✔
51
         if(is_encrypted) {
2,156✔
52
            key_data = PKCS8_extract(source, pbe_alg_id);
506✔
53
         } else {
54
            // todo read more efficiently
55
            while(!source.end_of_data()) {
1,199,018✔
56
               uint8_t b;
1,197,115✔
57
               size_t read = source.read_byte(b);
1,197,115✔
58
               if(read) {
1,197,115✔
59
                  key_data.push_back(b);
1,197,115✔
60
               }
61
            }
62
         }
63
      } else {
64
         std::string label;
2,617✔
65
         key_data = PEM_Code::decode(source, label);
5,172✔
66

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

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

85
   try {
4,683✔
86
      if(is_encrypted) {
4,683✔
87
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
499✔
88
            throw PKCS8_Exception(fmt("Unknown PBE type {}", pbe_alg_id.oid()));
×
89
         }
90

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

101
      BER_Decoder(key)
9,366✔
102
         .start_sequence()
4,589✔
103
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
9,077✔
104
         .decode(pk_alg_id)
4,488✔
105
         .decode(key, ASN1_Type::OctetString)
4,348✔
106
         .discard_remaining()
4,348✔
107
         .end_cons();
4,348✔
108
   } catch(std::exception& e) {
335✔
109
      throw Decoding_Error("PKCS #8 private key decoding", e);
335✔
110
   }
335✔
111
   return key;
8,696✔
112
}
5,200✔
113

114
}  // namespace
115

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

123
#if defined(BOTAN_HAS_PKCS5_PBES2)
124

125
namespace {
126

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

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

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

148
   SCAN_Name request(pbe_algo);
444✔
149

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

154
   return std::make_pair(request.arg(0), request.arg(1));
888✔
155
}
444✔
156

157
}  // namespace
158

159
#endif
160

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

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

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

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

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

198
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
726✔
199
}
200

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

219
   std::vector<uint8_t> output;
44✔
220
   DER_Encoder der(output);
44✔
221
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
44✔
222

223
   return output;
44✔
224

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

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

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

264
   std::vector<uint8_t> output;
19✔
265
   DER_Encoder(output)
38✔
266
      .start_sequence()
19✔
267
      .encode(pbe_info.first)
19✔
268
      .encode(pbe_info.second, ASN1_Type::OctetString)
19✔
269
      .end_cons();
19✔
270

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

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

293
namespace {
294

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

304
   const std::string alg_name = alg_id.oid().human_name_or_empty();
4,348✔
305
   if(alg_name.empty()) {
4,348✔
306
      throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
4✔
307
   }
308

309
   return load_private_key(alg_id, pkcs8_key);
7,716✔
310
}
9,122✔
311

312
}  // namespace
313

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

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

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

332
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source) {
1✔
333
   Botan::DataSource_Memory ds(source);
1✔
334
   return load_key(ds);
1✔
335
}
1✔
336

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

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

353
   return load_key(source, fail_fn, false);
7,064✔
354
}
355

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