Coverage Report

Created: 2026-03-11 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/pubkey/pbes2/pbes2.cpp
Line
Count
Source
1
/*
2
* PKCS #5 PBES2
3
* (C) 1999-2008,2014,2021 Jack Lloyd
4
* (C) 2018 Ribose Inc
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#include <botan/internal/pbes2.h>
10
11
#include <botan/asn1_obj.h>
12
#include <botan/ber_dec.h>
13
#include <botan/cipher_mode.h>
14
#include <botan/der_enc.h>
15
#include <botan/pwdhash.h>
16
#include <botan/rng.h>
17
#include <botan/internal/fmt.h>
18
#include <botan/internal/parsing.h>
19
20
namespace Botan {
21
22
namespace {
23
24
0
bool known_pbes_cipher_mode(std::string_view mode) {
25
0
   return (mode == "CBC" || mode == "GCM" || mode == "SIV");
26
0
}
27
28
secure_vector<uint8_t> derive_key(std::string_view passphrase,
29
                                  const AlgorithmIdentifier& kdf_algo,
30
0
                                  size_t default_key_size) {
31
0
   if(kdf_algo.oid() == OID::from_string("PKCS5.PBKDF2")) {
32
0
      secure_vector<uint8_t> salt;
33
0
      size_t iterations = 0;
34
0
      size_t key_length = 0;
35
36
0
      AlgorithmIdentifier prf_algo;
37
0
      BER_Decoder(kdf_algo.parameters())
38
0
         .start_sequence()
39
0
         .decode(salt, ASN1_Type::OctetString)
40
0
         .decode(iterations)
41
0
         .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal)
42
0
         .decode_optional(prf_algo,
43
0
                          ASN1_Type::Sequence,
44
0
                          ASN1_Class::Constructed,
45
0
                          AlgorithmIdentifier("HMAC(SHA-1)", AlgorithmIdentifier::USE_NULL_PARAM))
46
0
         .end_cons();
47
48
0
      if(salt.size() < 8) {
49
0
         throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small");
50
0
      }
51
52
0
      if(key_length == 0) {
53
0
         key_length = default_key_size;
54
0
      }
55
56
0
      const std::string prf = prf_algo.oid().human_name_or_empty();
57
0
      if(prf.empty() || !prf.starts_with("HMAC")) {
58
0
         throw Decoding_Error(fmt("Unknown PBES2 PRF {}", prf_algo.oid()));
59
0
      }
60
61
0
      auto pbkdf_fam = PasswordHashFamily::create_or_throw(fmt("PBKDF2({})", prf));
62
0
      auto pbkdf = pbkdf_fam->from_params(iterations);
63
64
0
      secure_vector<uint8_t> derived_key(key_length);
65
0
      pbkdf->hash(derived_key, passphrase, salt);
66
0
      return derived_key;
67
0
   } else if(kdf_algo.oid() == OID::from_string("Scrypt")) {
68
0
      secure_vector<uint8_t> salt;
69
0
      size_t N = 0;
70
0
      size_t r = 0;
71
0
      size_t p = 0;
72
0
      size_t key_length = 0;
73
74
0
      const AlgorithmIdentifier prf_algo;
75
0
      BER_Decoder(kdf_algo.parameters())
76
0
         .start_sequence()
77
0
         .decode(salt, ASN1_Type::OctetString)
78
0
         .decode(N)
79
0
         .decode(r)
80
0
         .decode(p)
81
0
         .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal)
82
0
         .end_cons();
83
84
0
      if(key_length == 0) {
85
0
         key_length = default_key_size;
86
0
      }
87
88
0
      secure_vector<uint8_t> derived_key(key_length);
89
90
0
      auto pwdhash_fam = PasswordHashFamily::create_or_throw("Scrypt");
91
0
      auto pwdhash = pwdhash_fam->from_params(N, r, p);
92
0
      pwdhash->hash(derived_key, passphrase, salt);
93
94
0
      return derived_key;
95
0
   } else {
96
0
      throw Decoding_Error(fmt("PBE-PKCS5 v2.0: Unknown KDF algorithm {}", kdf_algo.oid()));
97
0
   }
98
0
}
99
100
secure_vector<uint8_t> derive_key(std::string_view passphrase,
101
                                  std::string_view digest,
102
                                  RandomNumberGenerator& rng,
103
                                  size_t* msec_in_iterations_out,
104
                                  size_t iterations_if_msec_null,
105
                                  size_t key_length,
106
                                  bool include_key_length_in_struct,
107
0
                                  AlgorithmIdentifier& kdf_algo) {
108
0
   const size_t salt_len = 16;
109
0
   const secure_vector<uint8_t> salt = rng.random_vec(salt_len);
110
111
0
   if(digest == "Scrypt") {
112
0
      auto pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt");
113
114
0
      std::unique_ptr<PasswordHash> pwhash;
115
116
0
      if(msec_in_iterations_out != nullptr) {
117
0
         pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out);
118
0
      } else {
119
0
         pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
120
0
      }
121
122
0
      secure_vector<uint8_t> key(key_length);
123
0
      pwhash->hash(key, passphrase, salt);
124
125
0
      const size_t N = pwhash->memory_param();
126
0
      const size_t r = pwhash->iterations();
127
0
      const size_t p = pwhash->parallelism();
128
129
0
      if(msec_in_iterations_out != nullptr) {
130
0
         *msec_in_iterations_out = 0;
131
0
      }
132
133
0
      std::vector<uint8_t> scrypt_params;
134
0
      DER_Encoder(scrypt_params)
135
0
         .start_sequence()
136
0
         .encode(salt, ASN1_Type::OctetString)
137
0
         .encode(N)
138
0
         .encode(r)
139
0
         .encode(p)
140
0
         .encode_if(include_key_length_in_struct, key_length)
141
0
         .end_cons();
142
143
0
      kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params);
144
0
      return key;
145
0
   } else {
146
0
      const std::string prf = fmt("HMAC({})", digest);
147
0
      const std::string pbkdf_name = fmt("PBKDF2({})", prf);
148
149
0
      auto pwhash_fam = PasswordHashFamily::create(pbkdf_name);
150
0
      if(!pwhash_fam) {
151
0
         throw Invalid_Argument(fmt("Unknown password hash digest {}", digest));
152
0
      }
153
154
0
      std::unique_ptr<PasswordHash> pwhash;
155
156
0
      if(msec_in_iterations_out != nullptr) {
157
0
         pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out);
158
0
      } else {
159
0
         pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
160
0
      }
161
162
0
      secure_vector<uint8_t> key(key_length);
163
0
      pwhash->hash(key, passphrase, salt);
164
165
0
      std::vector<uint8_t> pbkdf2_params;
166
167
0
      const size_t iterations = pwhash->iterations();
168
169
0
      if(msec_in_iterations_out != nullptr) {
170
0
         *msec_in_iterations_out = iterations;
171
0
      }
172
173
0
      DER_Encoder(pbkdf2_params)
174
0
         .start_sequence()
175
0
         .encode(salt, ASN1_Type::OctetString)
176
0
         .encode(iterations)
177
0
         .encode_if(include_key_length_in_struct, key_length)
178
0
         .encode_if(prf != "HMAC(SHA-1)", AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM))
179
0
         .end_cons();
180
181
0
      kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params);
182
0
      return key;
183
0
   }
184
0
}
185
186
/*
187
* PKCS#5 v2.0 PBE Encryption
188
*/
189
std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_shared(std::span<const uint8_t> key_bits,
190
                                                                          std::string_view passphrase,
191
                                                                          size_t* msec_in_iterations_out,
192
                                                                          size_t iterations_if_msec_null,
193
                                                                          std::string_view cipher,
194
                                                                          std::string_view prf,
195
0
                                                                          RandomNumberGenerator& rng) {
196
0
   auto enc = Cipher_Mode::create(cipher, Cipher_Dir::Encryption);
197
198
0
   const auto cipher_spec = split_on(cipher, '/');
199
200
0
   if(cipher_spec.size() != 2 || !known_pbes_cipher_mode(cipher_spec[1]) || !enc) {
201
0
      throw Encoding_Error(fmt("PBE-PKCS5 v2.0: Invalid or unavailable cipher '{}'", cipher));
202
0
   }
203
204
0
   const size_t key_length = enc->key_spec().maximum_keylength();
205
206
0
   const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length());
207
208
0
   AlgorithmIdentifier kdf_algo;
209
210
0
   const bool include_key_length_in_struct = enc->key_spec().minimum_keylength() != enc->key_spec().maximum_keylength();
211
212
0
   const auto derived_key = derive_key(passphrase,
213
0
                                       prf,
214
0
                                       rng,
215
0
                                       msec_in_iterations_out,
216
0
                                       iterations_if_msec_null,
217
0
                                       key_length,
218
0
                                       include_key_length_in_struct,
219
0
                                       kdf_algo);
220
221
0
   enc->set_key(derived_key);
222
0
   enc->start(iv);
223
0
   secure_vector<uint8_t> ctext(key_bits.begin(), key_bits.end());
224
0
   enc->finish(ctext);
225
226
0
   std::vector<uint8_t> encoded_iv;
227
0
   DER_Encoder(encoded_iv).encode(iv, ASN1_Type::OctetString);
228
229
0
   std::vector<uint8_t> pbes2_params;
230
0
   DER_Encoder(pbes2_params)
231
0
      .start_sequence()
232
0
      .encode(kdf_algo)
233
0
      .encode(AlgorithmIdentifier(cipher, encoded_iv))
234
0
      .end_cons();
235
236
0
   const AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params);
237
238
0
   return std::make_pair(id, unlock(ctext));
239
0
}
240
241
}  // namespace
242
243
std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt(std::span<const uint8_t> key_bits,
244
                                                                   std::string_view passphrase,
245
                                                                   std::chrono::milliseconds msec,
246
                                                                   std::string_view cipher,
247
                                                                   std::string_view digest,
248
0
                                                                   RandomNumberGenerator& rng) {
249
0
   size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
250
0
   return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
251
   // return value msec_in_iterations_out discarded
252
0
}
253
254
std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_msec(std::span<const uint8_t> key_bits,
255
                                                                        std::string_view passphrase,
256
                                                                        std::chrono::milliseconds msec,
257
                                                                        size_t* out_iterations_if_nonnull,
258
                                                                        std::string_view cipher,
259
                                                                        std::string_view digest,
260
0
                                                                        RandomNumberGenerator& rng) {
261
0
   size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
262
263
0
   auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
264
265
0
   if(out_iterations_if_nonnull != nullptr) {
266
0
      *out_iterations_if_nonnull = msec_in_iterations_out;
267
0
   }
268
269
0
   return ret;
270
0
}
271
272
std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_iter(std::span<const uint8_t> key_bits,
273
                                                                        std::string_view passphrase,
274
                                                                        size_t pbkdf_iter,
275
                                                                        std::string_view cipher,
276
                                                                        std::string_view digest,
277
0
                                                                        RandomNumberGenerator& rng) {
278
0
   return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng);
279
0
}
280
281
secure_vector<uint8_t> pbes2_decrypt(std::span<const uint8_t> key_bits,
282
                                     std::string_view passphrase,
283
0
                                     const std::vector<uint8_t>& params) {
284
0
   AlgorithmIdentifier kdf_algo;
285
0
   AlgorithmIdentifier enc_algo;
286
287
0
   BER_Decoder(params).start_sequence().decode(kdf_algo).decode(enc_algo).end_cons();
288
289
0
   const std::string cipher = enc_algo.oid().human_name_or_empty();
290
0
   const auto cipher_spec = split_on(cipher, '/');
291
0
   if(cipher_spec.size() != 2 || !known_pbes_cipher_mode(cipher_spec[1])) {
292
0
      throw Decoding_Error(fmt("PBE-PKCS5 v2.0: Unknown/invalid cipher OID {}", enc_algo.oid()));
293
0
   }
294
295
0
   secure_vector<uint8_t> iv;
296
0
   BER_Decoder(enc_algo.parameters()).decode(iv, ASN1_Type::OctetString).verify_end();
297
298
0
   auto dec = Cipher_Mode::create(cipher, Cipher_Dir::Decryption);
299
0
   if(!dec) {
300
0
      throw Decoding_Error(fmt("PBE-PKCS5 cannot decrypt no cipher '{}'", cipher));
301
0
   }
302
303
0
   dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength()));
304
305
0
   dec->start(iv);
306
307
0
   secure_vector<uint8_t> buf(key_bits.begin(), key_bits.end());
308
0
   dec->finish(buf);
309
310
0
   return buf;
311
0
}
312
313
}  // namespace Botan