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

randombit / botan / 5230455705

10 Jun 2023 02:30PM UTC coverage: 91.715% (-0.03%) from 91.746%
5230455705

push

github

randombit
Merge GH #3584 Change clang-format AllowShortFunctionsOnASingleLine config from All to Inline

77182 of 84154 relevant lines covered (91.72%)

11975295.43 hits per line

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

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

48
   try {
4,287✔
49
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
4,287✔
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,791✔
55
               uint8_t b;
732,027✔
56
               size_t read = source.read_byte(b);
732,027✔
57
               if(read) {
732,027✔
58
                  key_data.push_back(b);
732,027✔
59
               }
60
            }
61
         }
62
      } else {
63
         std::string label;
2,407✔
64
         key_data = PEM_Code::decode(source, label);
4,752✔
65

66
         // todo remove autodetect for pem as well?
67
         if(label == "PRIVATE KEY") {
2,345✔
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,407✔
76

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

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

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

100
      BER_Decoder(key)
4,196✔
101
         .start_sequence()
8,298✔
102
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
8,103✔
103
         .decode(pk_alg_id)
4,001✔
104
         .decode(key, ASN1_Type::OctetString)
3,930✔
105
         .discard_remaining()
3,930✔
106
         .end_cons();
3,930✔
107
   } catch(std::exception& e) {
266✔
108
      throw Decoding_Error("PKCS #8 private key decoding", e);
266✔
109
   }
266✔
110
   return key;
7,860✔
111
}
4,644✔
112

113
}  // namespace
114

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

122
#if defined(BOTAN_HAS_PKCS5_PBES2)
123

124
namespace {
125

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

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

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

147
   SCAN_Name request(pbe_algo);
168✔
148

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

153
   return std::make_pair(request.arg(0), request.arg(1));
336✔
154
}
168✔
155

156
}  // namespace
157

158
#endif
159

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

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

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

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

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

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

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

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

222
   return output;
44✔
223

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

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

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

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

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

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

292
namespace {
293

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

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

308
   return load_private_key(alg_id, pkcs8_key);
7,021✔
309
}
8,217✔
310

311
}  // namespace
312

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

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

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

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

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

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

352
   return load_key(source, fail_fn, false);
6,852✔
353
}
354

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