Coverage Report

Created: 2020-11-21 08:34

/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/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 {
23
24
namespace PKCS8 {
25
26
namespace {
27
28
/*
29
* Get info from an EncryptedPrivateKeyInfo
30
*/
31
secure_vector<uint8_t> PKCS8_extract(DataSource& source,
32
                                  AlgorithmIdentifier& pbe_alg_id)
33
2
   {
34
2
   secure_vector<uint8_t> key_data;
35
36
2
   BER_Decoder(source)
37
2
      .start_cons(SEQUENCE)
38
2
         .decode(pbe_alg_id)
39
2
         .decode(key_data, OCTET_STRING)
40
2
      .verify_end();
41
42
2
   return key_data;
43
2
   }
44
45
/*
46
* PEM decode and/or decrypt a private key
47
*/
48
secure_vector<uint8_t> PKCS8_decode(
49
   DataSource& source,
50
   std::function<std::string ()> get_passphrase,
51
   AlgorithmIdentifier& pk_alg_id,
52
   bool is_encrypted)
53
3.39k
   {
54
3.39k
   AlgorithmIdentifier pbe_alg_id;
55
3.39k
   secure_vector<uint8_t> key_data, key;
56
57
3.39k
   try {
58
3.39k
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
59
3.10k
         {
60
3.10k
         if(is_encrypted)
61
0
            {
62
0
            key_data = PKCS8_extract(source, pbe_alg_id);
63
0
            }
64
3.10k
         else
65
3.10k
            {
66
            // todo read more efficiently
67
1.23M
            while(!source.end_of_data())
68
1.23M
               {
69
1.23M
               uint8_t b;
70
1.23M
               size_t read = source.read_byte(b);
71
1.23M
               if(read)
72
1.23M
                  {
73
1.23M
                  key_data.push_back(b);
74
1.23M
                  }
75
1.23M
               }
76
3.10k
            }
77
3.10k
         }
78
289
      else
79
289
         {
80
289
         std::string label;
81
289
         key_data = PEM_Code::decode(source, label);
82
83
         // todo remove autodetect for pem as well?
84
289
         if(label == "PRIVATE KEY")
85
6
            is_encrypted = false;
86
283
         else if(label == "ENCRYPTED PRIVATE KEY")
87
2
            {
88
2
            DataSource_Memory key_source(key_data);
89
2
            key_data = PKCS8_extract(key_source, pbe_alg_id);
90
2
            }
91
281
         else
92
281
            throw PKCS8_Exception("Unknown PEM label " + label);
93
3.11k
         }
94
95
3.11k
      if(key_data.empty())
96
1
         throw PKCS8_Exception("No key data found");
97
248
      }
98
248
   catch(Decoding_Error& e)
99
248
      {
100
248
      throw Decoding_Error("PKCS #8 private key decoding", e);
101
248
      }
102
103
3.11k
   try
104
3.11k
      {
105
3.11k
      if(is_encrypted)
106
0
         {
107
0
         if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
108
0
            throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
109
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
110
0
         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
111
#else
112
         BOTAN_UNUSED(get_passphrase);
113
         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
114
#endif
115
0
         }
116
3.11k
      else
117
3.11k
         key = key_data;
118
119
3.11k
      BER_Decoder(key)
120
3.11k
         .start_cons(SEQUENCE)
121
3.11k
            .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
122
3.11k
            .decode(pk_alg_id)
123
3.11k
            .decode(key, OCTET_STRING)
124
3.11k
            .discard_remaining()
125
3.11k
         .end_cons();
126
3.11k
      }
127
3.11k
   catch(std::exception& e)
128
990
      {
129
990
      throw Decoding_Error("PKCS #8 private key decoding", e);
130
990
      }
131
2.12k
   return key;
132
2.12k
   }
133
134
}
135
136
/*
137
* BER encode a PKCS #8 private key, unencrypted
138
*/
139
secure_vector<uint8_t> BER_encode(const Private_Key& key)
140
0
   {
141
   // keeping around for compat
142
0
   return key.private_key_info();
143
0
   }
144
145
/*
146
* PEM encode a PKCS #8 private key, unencrypted
147
*/
148
std::string PEM_encode(const Private_Key& key)
149
0
   {
150
0
   return PEM_Code::encode(PKCS8::BER_encode(key), "PRIVATE KEY");
151
0
   }
152
153
#if defined(BOTAN_HAS_PKCS5_PBES2)
154
155
namespace {
156
157
std::pair<std::string, std::string>
158
choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
159
0
   {
160
0
   if(pbe_algo.empty())
161
0
      {
162
      /*
163
      * For algorithms where we are using a non-RFC format anyway, default to
164
      * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
165
      * compatible.
166
      */
167
0
      const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
168
169
0
      if(nonstandard_pk)
170
0
         {
171
0
#if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
172
0
         return std::make_pair("AES-256/SIV", "SHA-512");
173
#elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
174
         return std::make_pair("AES-256/GCM", "SHA-512");
175
#endif
176
0
         }
177
178
      // Default is something compatible with everyone else
179
0
      return std::make_pair("AES-256/CBC", "SHA-256");
180
0
      }
181
182
0
   SCAN_Name request(pbe_algo);
183
184
0
   if(request.arg_count() != 2 ||
185
0
      (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
186
0
      {
187
0
      throw Invalid_Argument("Unsupported PBE " + pbe_algo);
188
0
      }
189
190
0
   return std::make_pair(request.arg(0), request.arg(1));
191
0
   }
192
193
}
194
195
#endif
196
197
/*
198
* BER encode a PKCS #8 private key, encrypted
199
*/
200
std::vector<uint8_t> BER_encode(const Private_Key& key,
201
                             RandomNumberGenerator& rng,
202
                             const std::string& pass,
203
                             std::chrono::milliseconds msec,
204
                             const std::string& pbe_algo)
205
0
   {
206
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
207
0
   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
208
209
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
210
0
      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
211
0
                         pbe_params.first, pbe_params.second, rng);
212
213
0
   std::vector<uint8_t> output;
214
0
   DER_Encoder der(output);
215
0
   der.start_cons(SEQUENCE)
216
0
         .encode(pbe_info.first)
217
0
         .encode(pbe_info.second, OCTET_STRING)
218
0
      .end_cons();
219
220
0
   return output;
221
#else
222
   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
223
   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
224
#endif
225
0
   }
226
227
/*
228
* PEM encode a PKCS #8 private key, encrypted
229
*/
230
std::string PEM_encode(const Private_Key& key,
231
                       RandomNumberGenerator& rng,
232
                       const std::string& pass,
233
                       std::chrono::milliseconds msec,
234
                       const std::string& pbe_algo)
235
0
   {
236
0
   if(pass.empty())
237
0
      return PEM_encode(key);
238
239
0
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
240
0
                           "ENCRYPTED PRIVATE KEY");
241
0
   }
242
243
/*
244
* BER encode a PKCS #8 private key, encrypted
245
*/
246
std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
247
                                                     RandomNumberGenerator& rng,
248
                                                     const std::string& pass,
249
                                                     size_t pbkdf_iterations,
250
                                                     const std::string& cipher,
251
                                                     const std::string& pbkdf_hash)
252
0
   {
253
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
254
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
255
0
      pbes2_encrypt_iter(key.private_key_info(),
256
0
                         pass, pbkdf_iterations,
257
0
                         cipher.empty() ? "AES-256/CBC" : cipher,
258
0
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
259
0
                         rng);
260
261
0
   std::vector<uint8_t> output;
262
0
   DER_Encoder der(output);
263
0
   der.start_cons(SEQUENCE)
264
0
         .encode(pbe_info.first)
265
0
         .encode(pbe_info.second, OCTET_STRING)
266
0
      .end_cons();
267
268
0
   return output;
269
270
#else
271
   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
272
   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
273
#endif
274
0
   }
275
276
/*
277
* PEM encode a PKCS #8 private key, encrypted
278
*/
279
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
280
                                            RandomNumberGenerator& rng,
281
                                            const std::string& pass,
282
                                            size_t pbkdf_iterations,
283
                                            const std::string& cipher,
284
                                            const std::string& pbkdf_hash)
285
0
   {
286
0
   return PEM_Code::encode(
287
0
      PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
288
0
      "ENCRYPTED PRIVATE KEY");
289
0
   }
290
291
/*
292
* BER encode a PKCS #8 private key, encrypted
293
*/
294
std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
295
                                                     RandomNumberGenerator& rng,
296
                                                     const std::string& pass,
297
                                                     std::chrono::milliseconds pbkdf_msec,
298
                                                     size_t* pbkdf_iterations,
299
                                                     const std::string& cipher,
300
                                                     const std::string& pbkdf_hash)
301
0
   {
302
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
303
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
304
0
      pbes2_encrypt_msec(key.private_key_info(), pass,
305
0
                         pbkdf_msec, pbkdf_iterations,
306
0
                         cipher.empty() ? "AES-256/CBC" : cipher,
307
0
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
308
0
                         rng);
309
310
0
   std::vector<uint8_t> output;
311
0
   DER_Encoder(output)
312
0
      .start_cons(SEQUENCE)
313
0
         .encode(pbe_info.first)
314
0
         .encode(pbe_info.second, OCTET_STRING)
315
0
      .end_cons();
316
317
0
   return output;
318
#else
319
   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
320
   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
321
#endif
322
0
   }
323
324
/*
325
* PEM encode a PKCS #8 private key, encrypted
326
*/
327
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
328
                                            RandomNumberGenerator& rng,
329
                                            const std::string& pass,
330
                                            std::chrono::milliseconds pbkdf_msec,
331
                                            size_t* pbkdf_iterations,
332
                                            const std::string& cipher,
333
                                            const std::string& pbkdf_hash)
334
0
   {
335
0
   return PEM_Code::encode(
336
0
      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
337
0
      "ENCRYPTED PRIVATE KEY");
338
0
   }
339
340
namespace {
341
342
/*
343
* Extract a private key (encrypted/unencrypted) and return it
344
*/
345
std::unique_ptr<Private_Key>
346
load_key(DataSource& source,
347
         std::function<std::string ()> get_pass,
348
         bool is_encrypted)
349
3.39k
   {
350
3.39k
   AlgorithmIdentifier alg_id;
351
3.39k
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
352
353
3.39k
   const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
354
3.39k
   if(alg_name.empty())
355
155
      throw PKCS8_Exception("Unknown algorithm OID: " +
356
155
                            alg_id.get_oid().to_string());
357
358
3.24k
   return load_private_key(alg_id, pkcs8_key);
359
3.24k
   }
360
361
}
362
363
/*
364
* Extract an encrypted private key and return it
365
*/
366
std::unique_ptr<Private_Key> load_key(DataSource& source,
367
                      std::function<std::string ()> get_pass)
368
0
   {
369
0
   return load_key(source, get_pass, true);
370
0
   }
371
372
/*
373
* Extract an encrypted private key and return it
374
*/
375
std::unique_ptr<Private_Key> load_key(DataSource& source,
376
                      const std::string& pass)
377
0
   {
378
   // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
379
   // See https://github.com/randombit/botan/issues/2255.
380
0
   return load_key(source, std::bind([](const std::string p) { return p; }, pass), true);
381
0
   }
382
383
/*
384
* Extract an unencrypted private key and return it
385
*/
386
std::unique_ptr<Private_Key> load_key(DataSource& source)
387
3.39k
   {
388
0
   auto fail_fn = []() -> std::string {
389
0
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
390
0
   };
391
392
3.39k
   return load_key(source, fail_fn, false);
393
3.39k
   }
394
395
/*
396
* Make a copy of this private key
397
*/
398
std::unique_ptr<Private_Key> copy_key(const Private_Key& key)
399
0
   {
400
0
   DataSource_Memory source(PEM_encode(key));
401
0
   return PKCS8::load_key(source);
402
0
   }
403
404
/*
405
* Extract an encrypted private key and return it
406
*/
407
Private_Key* load_key(DataSource& source,
408
                      RandomNumberGenerator& rng,
409
                      std::function<std::string ()> get_pass)
410
0
   {
411
0
   BOTAN_UNUSED(rng);
412
0
   return PKCS8::load_key(source, get_pass).release();
413
0
   }
414
415
/*
416
* Extract an encrypted private key and return it
417
*/
418
Private_Key* load_key(DataSource& source,
419
                      RandomNumberGenerator& rng,
420
                      const std::string& pass)
421
0
   {
422
0
   BOTAN_UNUSED(rng);
423
0
   return PKCS8::load_key(source, pass).release();
424
0
   }
425
426
/*
427
* Extract an unencrypted private key and return it
428
*/
429
Private_Key* load_key(DataSource& source,
430
                      RandomNumberGenerator& rng)
431
0
   {
432
0
   BOTAN_UNUSED(rng);
433
0
   return PKCS8::load_key(source).release();
434
0
   }
435
436
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
437
438
/*
439
* Extract an encrypted private key and return it
440
*/
441
Private_Key* load_key(const std::string& fsname,
442
                      RandomNumberGenerator& rng,
443
                      std::function<std::string ()> get_pass)
444
0
   {
445
0
   BOTAN_UNUSED(rng);
446
0
   DataSource_Stream in(fsname);
447
0
   return PKCS8::load_key(in, get_pass).release();
448
0
   }
449
450
/*
451
* Extract an encrypted private key and return it
452
*/
453
Private_Key* load_key(const std::string& fsname,
454
                      RandomNumberGenerator& rng,
455
                      const std::string& pass)
456
0
   {
457
0
   BOTAN_UNUSED(rng);
458
0
   DataSource_Stream in(fsname);
459
   // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
460
   // See https://github.com/randombit/botan/issues/2255.
461
0
   return PKCS8::load_key(in, std::bind([](const std::string p) { return p; }, pass)).release();
462
0
   }
463
464
/*
465
* Extract an unencrypted private key and return it
466
*/
467
Private_Key* load_key(const std::string& fsname,
468
                      RandomNumberGenerator& rng)
469
0
   {
470
0
   BOTAN_UNUSED(rng);
471
0
   DataSource_Stream in(fsname);
472
0
   return PKCS8::load_key(in).release();
473
0
   }
474
#endif
475
476
/*
477
* Make a copy of this private key
478
*/
479
Private_Key* copy_key(const Private_Key& key,
480
                      RandomNumberGenerator& rng)
481
0
   {
482
0
   BOTAN_UNUSED(rng);
483
0
   return PKCS8::copy_key(key).release();
484
0
   }
485
486
487
488
}
489
490
}