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/pbes2.h>
20 #endif
21 
22 namespace Botan {
23 
24 namespace PKCS8 {
25 
26 namespace {
27 
28 /*
29 * Get info from an EncryptedPrivateKeyInfo
30 */
PKCS8_extract(DataSource & source,AlgorithmIdentifier & pbe_alg_id)31 secure_vector<uint8_t> PKCS8_extract(DataSource& source,
32                                   AlgorithmIdentifier& pbe_alg_id)
33    {
34    secure_vector<uint8_t> key_data;
35 
36    BER_Decoder(source)
37       .start_cons(SEQUENCE)
38          .decode(pbe_alg_id)
39          .decode(key_data, OCTET_STRING)
40       .verify_end();
41 
42    return key_data;
43    }
44 
45 /*
46 * PEM decode and/or decrypt a private key
47 */
PKCS8_decode(DataSource & source,std::function<std::string ()> get_passphrase,AlgorithmIdentifier & pk_alg_id,bool is_encrypted)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    {
54    AlgorithmIdentifier pbe_alg_id;
55    secure_vector<uint8_t> key_data, key;
56 
57    try {
58       if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
59          {
60          if(is_encrypted)
61             {
62             key_data = PKCS8_extract(source, pbe_alg_id);
63             }
64          else
65             {
66             // todo read more efficiently
67             while(!source.end_of_data())
68                {
69                uint8_t b;
70                size_t read = source.read_byte(b);
71                if(read)
72                   {
73                   key_data.push_back(b);
74                   }
75                }
76             }
77          }
78       else
79          {
80          std::string label;
81          key_data = PEM_Code::decode(source, label);
82 
83          // todo remove autodetect for pem as well?
84          if(label == "PRIVATE KEY")
85             is_encrypted = false;
86          else if(label == "ENCRYPTED PRIVATE KEY")
87             {
88             DataSource_Memory key_source(key_data);
89             key_data = PKCS8_extract(key_source, pbe_alg_id);
90             }
91          else
92             throw PKCS8_Exception("Unknown PEM label " + label);
93          }
94 
95       if(key_data.empty())
96          throw PKCS8_Exception("No key data found");
97       }
98    catch(Decoding_Error& e)
99       {
100       throw Decoding_Error("PKCS #8 private key decoding", e);
101       }
102 
103    try
104       {
105       if(is_encrypted)
106          {
107          if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
108             throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
109 #if defined(BOTAN_HAS_PKCS5_PBES2)
110          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          }
116       else
117          key = key_data;
118 
119       BER_Decoder(key)
120          .start_cons(SEQUENCE)
121             .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
122             .decode(pk_alg_id)
123             .decode(key, OCTET_STRING)
124             .discard_remaining()
125          .end_cons();
126       }
127    catch(std::exception& e)
128       {
129       throw Decoding_Error("PKCS #8 private key decoding", e);
130       }
131    return key;
132    }
133 
134 }
135 
136 /*
137 * BER encode a PKCS #8 private key, unencrypted
138 */
BER_encode(const Private_Key & key)139 secure_vector<uint8_t> BER_encode(const Private_Key& key)
140    {
141    // keeping around for compat
142    return key.private_key_info();
143    }
144 
145 /*
146 * PEM encode a PKCS #8 private key, unencrypted
147 */
PEM_encode(const Private_Key & key)148 std::string PEM_encode(const Private_Key& key)
149    {
150    return PEM_Code::encode(PKCS8::BER_encode(key), "PRIVATE KEY");
151    }
152 
153 #if defined(BOTAN_HAS_PKCS5_PBES2)
154 
155 namespace {
156 
157 std::pair<std::string, std::string>
choose_pbe_params(const std::string & pbe_algo,const std::string & key_algo)158 choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
159    {
160    if(pbe_algo.empty())
161       {
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       const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
168 
169       if(nonstandard_pk)
170          {
171 #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
172          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          }
177 
178       // Default is something compatible with everyone else
179       return std::make_pair("AES-256/CBC", "SHA-256");
180       }
181 
182    SCAN_Name request(pbe_algo);
183 
184    if(request.arg_count() != 2 ||
185       (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
186       {
187       throw Invalid_Argument("Unsupported PBE " + pbe_algo);
188       }
189 
190    return std::make_pair(request.arg(0), request.arg(1));
191    }
192 
193 }
194 
195 #endif
196 
197 /*
198 * BER encode a PKCS #8 private key, encrypted
199 */
BER_encode(const Private_Key & key,RandomNumberGenerator & rng,const std::string & pass,std::chrono::milliseconds msec,const std::string & pbe_algo)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    {
206 #if defined(BOTAN_HAS_PKCS5_PBES2)
207    const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
208 
209    const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
210       pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
211                          pbe_params.first, pbe_params.second, rng);
212 
213    std::vector<uint8_t> output;
214    DER_Encoder der(output);
215    der.start_cons(SEQUENCE)
216          .encode(pbe_info.first)
217          .encode(pbe_info.second, OCTET_STRING)
218       .end_cons();
219 
220    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    }
226 
227 /*
228 * PEM encode a PKCS #8 private key, encrypted
229 */
PEM_encode(const Private_Key & key,RandomNumberGenerator & rng,const std::string & pass,std::chrono::milliseconds msec,const std::string & pbe_algo)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    {
236    if(pass.empty())
237       return PEM_encode(key);
238 
239    return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
240                            "ENCRYPTED PRIVATE KEY");
241    }
242 
243 /*
244 * BER encode a PKCS #8 private key, encrypted
245 */
BER_encode_encrypted_pbkdf_iter(const Private_Key & key,RandomNumberGenerator & rng,const std::string & pass,size_t pbkdf_iterations,const std::string & cipher,const std::string & pbkdf_hash)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    {
253 #if defined(BOTAN_HAS_PKCS5_PBES2)
254    const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
255       pbes2_encrypt_iter(key.private_key_info(),
256                          pass, pbkdf_iterations,
257                          cipher.empty() ? "AES-256/CBC" : cipher,
258                          pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
259                          rng);
260 
261    std::vector<uint8_t> output;
262    DER_Encoder der(output);
263    der.start_cons(SEQUENCE)
264          .encode(pbe_info.first)
265          .encode(pbe_info.second, OCTET_STRING)
266       .end_cons();
267 
268    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    }
275 
276 /*
277 * PEM encode a PKCS #8 private key, encrypted
278 */
PEM_encode_encrypted_pbkdf_iter(const Private_Key & key,RandomNumberGenerator & rng,const std::string & pass,size_t pbkdf_iterations,const std::string & cipher,const std::string & pbkdf_hash)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    {
286    return PEM_Code::encode(
287       PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
288       "ENCRYPTED PRIVATE KEY");
289    }
290 
291 /*
292 * BER encode a PKCS #8 private key, encrypted
293 */
BER_encode_encrypted_pbkdf_msec(const Private_Key & key,RandomNumberGenerator & rng,const std::string & pass,std::chrono::milliseconds pbkdf_msec,size_t * pbkdf_iterations,const std::string & cipher,const std::string & pbkdf_hash)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    {
302 #if defined(BOTAN_HAS_PKCS5_PBES2)
303    const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
304       pbes2_encrypt_msec(key.private_key_info(), pass,
305                          pbkdf_msec, pbkdf_iterations,
306                          cipher.empty() ? "AES-256/CBC" : cipher,
307                          pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
308                          rng);
309 
310    std::vector<uint8_t> output;
311    DER_Encoder(output)
312       .start_cons(SEQUENCE)
313          .encode(pbe_info.first)
314          .encode(pbe_info.second, OCTET_STRING)
315       .end_cons();
316 
317    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    }
323 
324 /*
325 * PEM encode a PKCS #8 private key, encrypted
326 */
PEM_encode_encrypted_pbkdf_msec(const Private_Key & key,RandomNumberGenerator & rng,const std::string & pass,std::chrono::milliseconds pbkdf_msec,size_t * pbkdf_iterations,const std::string & cipher,const std::string & pbkdf_hash)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    {
335    return PEM_Code::encode(
336       PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
337       "ENCRYPTED PRIVATE KEY");
338    }
339 
340 namespace {
341 
342 /*
343 * Extract a private key (encrypted/unencrypted) and return it
344 */
345 std::unique_ptr<Private_Key>
load_key(DataSource & source,std::function<std::string ()> get_pass,bool is_encrypted)346 load_key(DataSource& source,
347          std::function<std::string ()> get_pass,
348          bool is_encrypted)
349    {
350    AlgorithmIdentifier alg_id;
351    secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
352 
353    const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
354    if(alg_name.empty())
355       throw PKCS8_Exception("Unknown algorithm OID: " +
356                             alg_id.get_oid().to_string());
357 
358    return load_private_key(alg_id, pkcs8_key);
359    }
360 
361 }
362 
363 /*
364 * Extract an encrypted private key and return it
365 */
load_key(DataSource & source,std::function<std::string ()> get_pass)366 std::unique_ptr<Private_Key> load_key(DataSource& source,
367                       std::function<std::string ()> get_pass)
368    {
369    return load_key(source, get_pass, true);
370    }
371 
372 /*
373 * Extract an encrypted private key and return it
374 */
load_key(DataSource & source,const std::string & pass)375 std::unique_ptr<Private_Key> load_key(DataSource& source,
376                       const std::string& pass)
377    {
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    return load_key(source, std::bind([](const std::string p) { return p; }, pass), true);
381    }
382 
383 /*
384 * Extract an unencrypted private key and return it
385 */
load_key(DataSource & source)386 std::unique_ptr<Private_Key> load_key(DataSource& source)
387    {
388    auto fail_fn = []() -> std::string {
389       throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
390    };
391 
392    return load_key(source, fail_fn, false);
393    }
394 
395 /*
396 * Make a copy of this private key
397 */
copy_key(const Private_Key & key)398 std::unique_ptr<Private_Key> copy_key(const Private_Key& key)
399    {
400    DataSource_Memory source(PEM_encode(key));
401    return PKCS8::load_key(source);
402    }
403 
404 /*
405 * Extract an encrypted private key and return it
406 */
load_key(DataSource & source,RandomNumberGenerator & rng,std::function<std::string ()> get_pass)407 Private_Key* load_key(DataSource& source,
408                       RandomNumberGenerator& rng,
409                       std::function<std::string ()> get_pass)
410    {
411    BOTAN_UNUSED(rng);
412    return PKCS8::load_key(source, get_pass).release();
413    }
414 
415 /*
416 * Extract an encrypted private key and return it
417 */
load_key(DataSource & source,RandomNumberGenerator & rng,const std::string & pass)418 Private_Key* load_key(DataSource& source,
419                       RandomNumberGenerator& rng,
420                       const std::string& pass)
421    {
422    BOTAN_UNUSED(rng);
423    return PKCS8::load_key(source, pass).release();
424    }
425 
426 /*
427 * Extract an unencrypted private key and return it
428 */
load_key(DataSource & source,RandomNumberGenerator & rng)429 Private_Key* load_key(DataSource& source,
430                       RandomNumberGenerator& rng)
431    {
432    BOTAN_UNUSED(rng);
433    return PKCS8::load_key(source).release();
434    }
435 
436 #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
437 
438 /*
439 * Extract an encrypted private key and return it
440 */
load_key(const std::string & fsname,RandomNumberGenerator & rng,std::function<std::string ()> get_pass)441 Private_Key* load_key(const std::string& fsname,
442                       RandomNumberGenerator& rng,
443                       std::function<std::string ()> get_pass)
444    {
445    BOTAN_UNUSED(rng);
446    DataSource_Stream in(fsname);
447    return PKCS8::load_key(in, get_pass).release();
448    }
449 
450 /*
451 * Extract an encrypted private key and return it
452 */
load_key(const std::string & fsname,RandomNumberGenerator & rng,const std::string & pass)453 Private_Key* load_key(const std::string& fsname,
454                       RandomNumberGenerator& rng,
455                       const std::string& pass)
456    {
457    BOTAN_UNUSED(rng);
458    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    return PKCS8::load_key(in, std::bind([](const std::string p) { return p; }, pass)).release();
462    }
463 
464 /*
465 * Extract an unencrypted private key and return it
466 */
load_key(const std::string & fsname,RandomNumberGenerator & rng)467 Private_Key* load_key(const std::string& fsname,
468                       RandomNumberGenerator& rng)
469    {
470    BOTAN_UNUSED(rng);
471    DataSource_Stream in(fsname);
472    return PKCS8::load_key(in).release();
473    }
474 #endif
475 
476 /*
477 * Make a copy of this private key
478 */
copy_key(const Private_Key & key,RandomNumberGenerator & rng)479 Private_Key* copy_key(const Private_Key& key,
480                       RandomNumberGenerator& rng)
481    {
482    BOTAN_UNUSED(rng);
483    return PKCS8::copy_key(key).release();
484    }
485 
486 
487 
488 }
489 
490 }
491