Coverage Report

Created: 2024-11-29 06:10

/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
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
3
secure_vector<uint8_t> PKCS8_extract(DataSource& source, AlgorithmIdentifier& pbe_alg_id) {
31
3
   secure_vector<uint8_t> key_data;
32
33
3
   BER_Decoder(source).start_sequence().decode(pbe_alg_id).decode(key_data, ASN1_Type::OctetString).verify_end();
34
35
3
   return key_data;
36
3
}
37
38
/*
39
* PEM decode and/or decrypt a private key
40
*/
41
secure_vector<uint8_t> PKCS8_decode(DataSource& source,
42
                                    const std::function<std::string()>& get_passphrase,
43
                                    AlgorithmIdentifier& pk_alg_id,
44
10.0k
                                    bool is_encrypted) {
45
10.0k
   AlgorithmIdentifier pbe_alg_id;
46
10.0k
   secure_vector<uint8_t> key_data, key;
47
48
10.0k
   try {
49
10.0k
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
50
4.48k
         if(is_encrypted) {
51
0
            key_data = PKCS8_extract(source, pbe_alg_id);
52
4.48k
         } else {
53
            // todo read more efficiently
54
2.03M
            while(!source.end_of_data()) {
55
2.03M
               uint8_t b;
56
2.03M
               size_t read = source.read_byte(b);
57
2.03M
               if(read) {
58
2.03M
                  key_data.push_back(b);
59
2.03M
               }
60
2.03M
            }
61
4.48k
         }
62
5.51k
      } else {
63
5.51k
         std::string label;
64
5.51k
         key_data = PEM_Code::decode(source, label);
65
66
         // todo remove autodetect for pem as well?
67
5.51k
         if(label == "PRIVATE KEY") {
68
5.24k
            is_encrypted = false;
69
5.24k
         } else if(label == "ENCRYPTED PRIVATE KEY") {
70
3
            DataSource_Memory key_source(key_data);
71
3
            key_data = PKCS8_extract(key_source, pbe_alg_id);
72
266
         } else {
73
266
            throw PKCS8_Exception(fmt("Unknown PEM label '{}'", label));
74
266
         }
75
5.51k
      }
76
77
9.73k
      if(key_data.empty()) {
78
1
         throw PKCS8_Exception("No key data found");
79
1
      }
80
9.73k
   } catch(Decoding_Error& e) {
81
221
      throw Decoding_Error("PKCS #8 private key decoding", e);
82
221
   }
83
84
9.73k
   try {
85
9.73k
      if(is_encrypted) {
86
0
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
87
0
            throw PKCS8_Exception(fmt("Unknown PBE type {}", pbe_alg_id.oid()));
88
0
         }
89
90
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
91
0
         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.parameters());
92
#else
93
         BOTAN_UNUSED(get_passphrase);
94
         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
95
#endif
96
9.73k
      } else {
97
9.73k
         key = key_data;
98
9.73k
      }
99
100
9.73k
      BER_Decoder(key)
101
9.73k
         .start_sequence()
102
9.73k
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
103
9.73k
         .decode(pk_alg_id)
104
9.73k
         .decode(key, ASN1_Type::OctetString)
105
9.73k
         .discard_remaining()
106
9.73k
         .end_cons();
107
9.73k
   } catch(std::exception& e) {
108
1.18k
      throw Decoding_Error("PKCS #8 private key decoding", e);
109
1.18k
   }
110
8.54k
   return key;
111
9.73k
}
112
113
}  // namespace
114
115
/*
116
* PEM encode a PKCS #8 private key, unencrypted
117
*/
118
0
std::string PEM_encode(const Private_Key& key) {
119
0
   return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
120
0
}
121
122
#if defined(BOTAN_HAS_PKCS5_PBES2)
123
124
namespace {
125
126
0
std::pair<std::string, std::string> choose_pbe_params(std::string_view pbe_algo, std::string_view key_algo) {
127
0
   if(pbe_algo.empty()) {
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
0
      const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
134
135
0
      if(nonstandard_pk) {
136
0
   #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
137
0
         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
0
      }
142
143
      // Default is something compatible with everyone else
144
0
      return std::make_pair("AES-256/CBC", "SHA-256");
145
0
   }
146
147
0
   SCAN_Name request(pbe_algo);
148
149
0
   if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) {
150
0
      throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo));
151
0
   }
152
153
0
   return std::make_pair(request.arg(0), request.arg(1));
154
0
}
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,
164
                                RandomNumberGenerator& rng,
165
                                std::string_view pass,
166
                                std::chrono::milliseconds msec,
167
0
                                std::string_view pbe_algo) {
168
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
169
0
   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
170
171
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
172
0
      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr, pbe_params.first, pbe_params.second, rng);
173
174
0
   std::vector<uint8_t> output;
175
0
   DER_Encoder der(output);
176
0
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
177
178
0
   return output;
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
0
}
184
185
/*
186
* PEM encode a PKCS #8 private key, encrypted
187
*/
188
std::string PEM_encode(const Private_Key& key,
189
                       RandomNumberGenerator& rng,
190
                       std::string_view pass,
191
                       std::chrono::milliseconds msec,
192
0
                       std::string_view pbe_algo) {
193
0
   if(pass.empty()) {
194
0
      return PEM_encode(key);
195
0
   }
196
197
0
   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
198
0
}
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,
204
                                                     RandomNumberGenerator& rng,
205
                                                     std::string_view pass,
206
                                                     size_t pbkdf_iterations,
207
                                                     std::string_view cipher,
208
0
                                                     std::string_view pbkdf_hash) {
209
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
210
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
211
0
      pbes2_encrypt_iter(key.private_key_info(),
212
0
                         pass,
213
0
                         pbkdf_iterations,
214
0
                         cipher.empty() ? "AES-256/CBC" : cipher,
215
0
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
216
0
                         rng);
217
218
0
   std::vector<uint8_t> output;
219
0
   DER_Encoder der(output);
220
0
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
221
222
0
   return output;
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
0
}
229
230
/*
231
* PEM encode a PKCS #8 private key, encrypted
232
*/
233
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
234
                                            RandomNumberGenerator& rng,
235
                                            std::string_view pass,
236
                                            size_t pbkdf_iterations,
237
                                            std::string_view cipher,
238
0
                                            std::string_view pbkdf_hash) {
239
0
   return PEM_Code::encode(PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
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_msec(const Private_Key& key,
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
0
                                                     std::string_view pbkdf_hash) {
253
0
#if defined(BOTAN_HAS_PKCS5_PBES2)
254
0
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
255
0
      pbes2_encrypt_msec(key.private_key_info(),
256
0
                         pass,
257
0
                         pbkdf_msec,
258
0
                         pbkdf_iterations,
259
0
                         cipher.empty() ? "AES-256/CBC" : cipher,
260
0
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
261
0
                         rng);
262
263
0
   std::vector<uint8_t> output;
264
0
   DER_Encoder(output)
265
0
      .start_sequence()
266
0
      .encode(pbe_info.first)
267
0
      .encode(pbe_info.second, ASN1_Type::OctetString)
268
0
      .end_cons();
269
270
0
   return output;
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
0
}
276
277
/*
278
* PEM encode a PKCS #8 private key, encrypted
279
*/
280
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
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
0
                                            std::string_view pbkdf_hash) {
287
0
   return PEM_Code::encode(
288
0
      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
289
0
      "ENCRYPTED PRIVATE KEY");
290
0
}
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,
298
                                      const std::function<std::string()>& get_pass,
299
10.0k
                                      bool is_encrypted) {
300
10.0k
   AlgorithmIdentifier alg_id;
301
10.0k
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
302
303
10.0k
   const std::string alg_name = alg_id.oid().human_name_or_empty();
304
10.0k
   if(alg_name.empty()) {
305
255
      throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
306
255
   }
307
308
9.74k
   return load_private_key(alg_id, pkcs8_key);
309
10.0k
}
310
311
}  // namespace
312
313
/*
314
* Extract an encrypted private key and return it
315
*/
316
0
std::unique_ptr<Private_Key> load_key(DataSource& source, const std::function<std::string()>& get_pass) {
317
0
   return load_key(source, get_pass, true);
318
0
}
319
320
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source,
321
0
                                      const std::function<std::string()>& get_passphrase) {
322
0
   Botan::DataSource_Memory ds(source);
323
0
   return load_key(ds, get_passphrase);
324
0
}
325
326
0
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source, std::string_view pass) {
327
0
   Botan::DataSource_Memory ds(source);
328
0
   return load_key(ds, pass);
329
0
}
330
331
0
std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source) {
332
0
   Botan::DataSource_Memory ds(source);
333
0
   return load_key(ds);
334
0
}
335
336
/*
337
* Extract an encrypted private key and return it
338
*/
339
0
std::unique_ptr<Private_Key> load_key(DataSource& source, std::string_view pass) {
340
0
   return load_key(
341
0
      source, [pass]() { return std::string(pass); }, true);
342
0
}
343
344
/*
345
* Extract an unencrypted private key and return it
346
*/
347
10.0k
std::unique_ptr<Private_Key> load_key(DataSource& source) {
348
10.0k
   auto fail_fn = []() -> std::string {
349
0
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
350
0
   };
351
352
10.0k
   return load_key(source, fail_fn, false);
353
10.0k
}
354
355
}  // namespace Botan::PKCS8