Coverage Report

Created: 2021-04-07 06:07

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