Coverage Report

Created: 2022-06-23 06:44

/src/botan/src/lib/pubkey/pkcs8.cpp
Line
Count
Source (jump to first uncovered line)
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
#include <botan/rng.h>
10
#include <botan/der_enc.h>
11
#include <botan/ber_dec.h>
12
#include <botan/asn1_obj.h>
13
#include <botan/oids.h>
14
#include <botan/pem.h>
15
#include <botan/internal/scan_name.h>
16
#include <botan/pk_algs.h>
17
18
#if defined(BOTAN_HAS_PKCS5_PBES2)
19
   #include <botan/internal/pbes2.h>
20
#endif
21
22
namespace Botan::PKCS8 {
23
24
namespace {
25
26
/*
27
* Get info from an EncryptedPrivateKeyInfo
28
*/
29
secure_vector<uint8_t> PKCS8_extract(DataSource& source,
30
                                  AlgorithmIdentifier& pbe_alg_id)
31
15
   {
32
15
   secure_vector<uint8_t> key_data;
33
34
15
   BER_Decoder(source)
35
15
      .start_sequence()
36
15
         .decode(pbe_alg_id)
37
15
         .decode(key_data, ASN1_Type::OctetString)
38
15
      .verify_end();
39
40
15
   return key_data;
41
15
   }
42
43
/*
44
* PEM decode and/or decrypt a private key
45
*/
46
secure_vector<uint8_t> PKCS8_decode(
47
   DataSource& source,
48
   const std::function<std::string ()>& get_passphrase,
49
   AlgorithmIdentifier& pk_alg_id,
50
   bool is_encrypted)
51
4.73k
   {
52
4.73k
   AlgorithmIdentifier pbe_alg_id;
53
4.73k
   secure_vector<uint8_t> key_data, key;
54
55
4.73k
   try {
56
4.73k
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
57
4.47k
         {
58
4.47k
         if(is_encrypted)
59
0
            {
60
0
            key_data = PKCS8_extract(source, pbe_alg_id);
61
0
            }
62
4.47k
         else
63
4.47k
            {
64
            // todo read more efficiently
65
1.57M
            while(!source.end_of_data())
66
1.56M
               {
67
1.56M
               uint8_t b;
68
1.56M
               size_t read = source.read_byte(b);
69
1.56M
               if(read)
70
1.56M
                  {
71
1.56M
                  key_data.push_back(b);
72
1.56M
                  }
73
1.56M
               }
74
4.47k
            }
75
4.47k
         }
76
260
      else
77
260
         {
78
260
         std::string label;
79
260
         key_data = PEM_Code::decode(source, label);
80
81
         // todo remove autodetect for pem as well?
82
260
         if(label == "PRIVATE KEY")
83
1
            is_encrypted = false;
84
259
         else if(label == "ENCRYPTED PRIVATE KEY")
85
15
            {
86
15
            DataSource_Memory key_source(key_data);
87
15
            key_data = PKCS8_extract(key_source, pbe_alg_id);
88
15
            }
89
244
         else
90
244
            throw PKCS8_Exception("Unknown PEM label " + label);
91
260
         }
92
93
4.49k
      if(key_data.empty())
94
1
         throw PKCS8_Exception("No key data found");
95
4.49k
      }
96
4.73k
   catch(Decoding_Error& e)
97
4.73k
      {
98
225
      throw Decoding_Error("PKCS #8 private key decoding", e);
99
225
      }
100
101
4.47k
   try
102
4.47k
      {
103
4.47k
      if(is_encrypted)
104
0
         {
105
0
         if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
106
0
            throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
107
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
108
0
         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
109
#else
110
         BOTAN_UNUSED(get_passphrase);
111
         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
112
#endif
113
0
         }
114
4.47k
      else
115
4.47k
         key = key_data;
116
117
4.47k
      BER_Decoder(key)
118
4.47k
         .start_sequence()
119
4.47k
            .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
120
4.47k
            .decode(pk_alg_id)
121
4.47k
            .decode(key, ASN1_Type::OctetString)
122
4.47k
            .discard_remaining()
123
4.47k
         .end_cons();
124
4.47k
      }
125
4.47k
   catch(std::exception& e)
126
4.47k
      {
127
1.11k
      throw Decoding_Error("PKCS #8 private key decoding", e);
128
1.11k
      }
129
3.36k
   return key;
130
4.47k
   }
131
132
}
133
134
/*
135
* PEM encode a PKCS #8 private key, unencrypted
136
*/
137
std::string PEM_encode(const Private_Key& key)
138
0
   {
139
0
   return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
140
0
   }
141
142
#if defined(BOTAN_HAS_PKCS5_PBES2)
143
144
namespace {
145
146
std::pair<std::string, std::string>
147
choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
148
0
   {
149
0
   if(pbe_algo.empty())
150
0
      {
151
      /*
152
      * For algorithms where we are using a non-RFC format anyway, default to
153
      * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
154
      * compatible.
155
      */
156
0
      const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
157
158
0
      if(nonstandard_pk)
159
0
         {
160
0
#if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
161
0
         return std::make_pair("AES-256/SIV", "SHA-512");
162
#elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
163
         return std::make_pair("AES-256/GCM", "SHA-512");
164
#endif
165
0
         }
166
167
      // Default is something compatible with everyone else
168
0
      return std::make_pair("AES-256/CBC", "SHA-256");
169
0
      }
170
171
0
   SCAN_Name request(pbe_algo);
172
173
0
   if(request.arg_count() != 2 ||
174
0
      (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
175
0
      {
176
0
      throw Invalid_Argument("Unsupported PBE " + pbe_algo);
177
0
      }
178
179
0
   return std::make_pair(request.arg(0), request.arg(1));
180
0
   }
181
182
}
183
184
#endif
185
186
/*
187
* BER encode a PKCS #8 private key, encrypted
188
*/
189
std::vector<uint8_t> BER_encode(const Private_Key& key,
190
                             RandomNumberGenerator& rng,
191
                             const std::string& pass,
192
                             std::chrono::milliseconds msec,
193
                             const std::string& pbe_algo)
194
0
   {
195
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
196
0
   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
197
198
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
199
0
      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
200
0
                         pbe_params.first, pbe_params.second, rng);
201
202
0
   std::vector<uint8_t> output;
203
0
   DER_Encoder der(output);
204
0
   der.start_sequence()
205
0
         .encode(pbe_info.first)
206
0
         .encode(pbe_info.second, ASN1_Type::OctetString)
207
0
      .end_cons();
208
209
0
   return output;
210
#else
211
   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
212
   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
213
#endif
214
0
   }
215
216
/*
217
* PEM encode a PKCS #8 private key, encrypted
218
*/
219
std::string PEM_encode(const Private_Key& key,
220
                       RandomNumberGenerator& rng,
221
                       const std::string& pass,
222
                       std::chrono::milliseconds msec,
223
                       const std::string& pbe_algo)
224
0
   {
225
0
   if(pass.empty())
226
0
      return PEM_encode(key);
227
228
0
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
229
0
                           "ENCRYPTED PRIVATE KEY");
230
0
   }
231
232
/*
233
* BER encode a PKCS #8 private key, encrypted
234
*/
235
std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
236
                                                     RandomNumberGenerator& rng,
237
                                                     const std::string& pass,
238
                                                     size_t pbkdf_iterations,
239
                                                     const std::string& cipher,
240
                                                     const std::string& pbkdf_hash)
241
0
   {
242
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
243
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
244
0
      pbes2_encrypt_iter(key.private_key_info(),
245
0
                         pass, pbkdf_iterations,
246
0
                         cipher.empty() ? "AES-256/CBC" : cipher,
247
0
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
248
0
                         rng);
249
250
0
   std::vector<uint8_t> output;
251
0
   DER_Encoder der(output);
252
0
   der.start_sequence()
253
0
         .encode(pbe_info.first)
254
0
         .encode(pbe_info.second, ASN1_Type::OctetString)
255
0
      .end_cons();
256
257
0
   return output;
258
259
#else
260
   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
261
   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
262
#endif
263
0
   }
264
265
/*
266
* PEM encode a PKCS #8 private key, encrypted
267
*/
268
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
269
                                            RandomNumberGenerator& rng,
270
                                            const std::string& pass,
271
                                            size_t pbkdf_iterations,
272
                                            const std::string& cipher,
273
                                            const std::string& pbkdf_hash)
274
0
   {
275
0
   return PEM_Code::encode(
276
0
      PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
277
0
      "ENCRYPTED PRIVATE KEY");
278
0
   }
279
280
/*
281
* BER encode a PKCS #8 private key, encrypted
282
*/
283
std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
284
                                                     RandomNumberGenerator& rng,
285
                                                     const std::string& pass,
286
                                                     std::chrono::milliseconds pbkdf_msec,
287
                                                     size_t* pbkdf_iterations,
288
                                                     const std::string& cipher,
289
                                                     const std::string& pbkdf_hash)
290
0
   {
291
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
292
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
293
0
      pbes2_encrypt_msec(key.private_key_info(), pass,
294
0
                         pbkdf_msec, pbkdf_iterations,
295
0
                         cipher.empty() ? "AES-256/CBC" : cipher,
296
0
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
297
0
                         rng);
298
299
0
   std::vector<uint8_t> output;
300
0
   DER_Encoder(output)
301
0
      .start_sequence()
302
0
         .encode(pbe_info.first)
303
0
         .encode(pbe_info.second, ASN1_Type::OctetString)
304
0
      .end_cons();
305
306
0
   return output;
307
#else
308
   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
309
   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
310
#endif
311
0
   }
312
313
/*
314
* PEM encode a PKCS #8 private key, encrypted
315
*/
316
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
317
                                            RandomNumberGenerator& rng,
318
                                            const std::string& pass,
319
                                            std::chrono::milliseconds pbkdf_msec,
320
                                            size_t* pbkdf_iterations,
321
                                            const std::string& cipher,
322
                                            const std::string& pbkdf_hash)
323
0
   {
324
0
   return PEM_Code::encode(
325
0
      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
326
0
      "ENCRYPTED PRIVATE KEY");
327
0
   }
328
329
namespace {
330
331
/*
332
* Extract a private key (encrypted/unencrypted) and return it
333
*/
334
std::unique_ptr<Private_Key>
335
load_key(DataSource& source,
336
         const std::function<std::string ()>& get_pass,
337
         bool is_encrypted)
338
4.73k
   {
339
4.73k
   AlgorithmIdentifier alg_id;
340
4.73k
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
341
342
4.73k
   const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
343
4.73k
   if(alg_name.empty())
344
139
      throw PKCS8_Exception("Unknown algorithm OID: " +
345
139
                            alg_id.get_oid().to_string());
346
347
4.59k
   return load_private_key(alg_id, pkcs8_key);
348
4.73k
   }
349
350
}
351
352
/*
353
* Extract an encrypted private key and return it
354
*/
355
std::unique_ptr<Private_Key> load_key(DataSource& source,
356
                                      const std::function<std::string ()>& get_pass)
357
0
   {
358
0
   return load_key(source, get_pass, true);
359
0
   }
360
361
/*
362
* Extract an encrypted private key and return it
363
*/
364
std::unique_ptr<Private_Key> load_key(DataSource& source,
365
                                      const std::string& pass)
366
0
   {
367
   // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
368
   // See https://github.com/randombit/botan/issues/2255.
369
0
   return load_key(source, std::bind([](const std::string& p) { return p; }, pass), true);
370
0
   }
371
372
/*
373
* Extract an unencrypted private key and return it
374
*/
375
std::unique_ptr<Private_Key> load_key(DataSource& source)
376
4.73k
   {
377
4.73k
   auto fail_fn = []() -> std::string {
378
0
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
379
0
   };
380
381
4.73k
   return load_key(source, fail_fn, false);
382
4.73k
   }
383
384
}