Botan 3.12.0
Crypto and TLS for C&
pbes2.cpp
Go to the documentation of this file.
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
20namespace Botan {
21
22namespace {
23
24bool known_pbes_cipher_mode(std::string_view mode) {
25 return (mode == "CBC" || mode == "GCM" || mode == "SIV");
26}
27
28secure_vector<uint8_t> derive_key(std::string_view passphrase,
29 const AlgorithmIdentifier& kdf_algo,
30 size_t default_key_size) {
31 if(kdf_algo.oid() == OID::from_string("PKCS5.PBKDF2")) {
33 size_t iterations = 0;
34 size_t key_length = 0;
35
36 AlgorithmIdentifier prf_algo;
37 BER_Decoder(kdf_algo.parameters(), BER_Decoder::Limits::DER())
40 .decode(iterations)
42 .decode_optional(prf_algo,
46 .end_cons()
47 .verify_end();
48
49 if(iterations == 0) {
50 throw Decoding_Error("PBE-PKCS5 v2.0: Iteration count must be positive");
51 }
52
53 if(salt.size() < 8) {
54 throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small");
55 }
56
57 if(key_length == 0) {
58 key_length = default_key_size;
59 }
60
61 const std::string prf = prf_algo.oid().human_name_or_empty();
62 if(prf.empty() || !prf.starts_with("HMAC")) {
63 throw Decoding_Error(fmt("Unknown PBES2 PRF {}", prf_algo.oid()));
64 }
65
66 auto pbkdf_fam = PasswordHashFamily::create_or_throw(fmt("PBKDF2({})", prf));
67 auto pbkdf = pbkdf_fam->from_params(iterations);
68
69 secure_vector<uint8_t> derived_key(key_length);
70 pbkdf->hash(derived_key, passphrase, salt);
71 return derived_key;
72 } else if(kdf_algo.oid() == OID::from_string("Scrypt")) {
74 size_t N = 0;
75 size_t r = 0;
76 size_t p = 0;
77 size_t key_length = 0;
78
79 const AlgorithmIdentifier prf_algo;
80 BER_Decoder(kdf_algo.parameters(), BER_Decoder::Limits::DER())
83 .decode(N)
84 .decode(r)
85 .decode(p)
87 .end_cons()
88 .verify_end();
89
90 if(N == 0 || r == 0 || p == 0) {
91 throw Decoding_Error("PBE-PKCS5 v2.0: Invalid Scrypt parameters");
92 }
93
94 if(key_length == 0) {
95 key_length = default_key_size;
96 }
97
98 secure_vector<uint8_t> derived_key(key_length);
99
100 auto pwdhash_fam = PasswordHashFamily::create_or_throw("Scrypt");
101 auto pwdhash = pwdhash_fam->from_params(N, r, p);
102 pwdhash->hash(derived_key, passphrase, salt);
103
104 return derived_key;
105 } else {
106 throw Decoding_Error(fmt("PBE-PKCS5 v2.0: Unknown KDF algorithm {}", kdf_algo.oid()));
107 }
108}
109
110secure_vector<uint8_t> derive_key(std::string_view passphrase,
111 std::string_view digest,
113 size_t* msec_in_iterations_out,
114 size_t iterations_if_msec_null,
115 size_t key_length,
116 bool include_key_length_in_struct,
117 AlgorithmIdentifier& kdf_algo) {
118 const size_t salt_len = 16;
119 const secure_vector<uint8_t> salt = rng.random_vec(salt_len);
120
121 if(digest == "Scrypt") {
122 auto pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt");
123
124 std::unique_ptr<PasswordHash> pwhash;
125
126 if(msec_in_iterations_out != nullptr) {
127 pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out);
128 } else {
129 pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
130 }
131
132 secure_vector<uint8_t> key(key_length);
133 pwhash->hash(key, passphrase, salt);
134
135 const size_t N = pwhash->memory_param();
136 const size_t r = pwhash->iterations();
137 const size_t p = pwhash->parallelism();
138
139 if(msec_in_iterations_out != nullptr) {
140 *msec_in_iterations_out = 0;
141 }
142
143 std::vector<uint8_t> scrypt_params;
144 DER_Encoder(scrypt_params)
147 .encode(N)
148 .encode(r)
149 .encode(p)
150 .encode_if(include_key_length_in_struct, key_length)
151 .end_cons();
152
153 kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params);
154 return key;
155 } else {
156 const std::string prf = fmt("HMAC({})", digest);
157 const std::string pbkdf_name = fmt("PBKDF2({})", prf);
158
159 auto pwhash_fam = PasswordHashFamily::create(pbkdf_name);
160 if(!pwhash_fam) {
161 throw Invalid_Argument(fmt("Unknown password hash digest {}", digest));
162 }
163
164 std::unique_ptr<PasswordHash> pwhash;
165
166 if(msec_in_iterations_out != nullptr) {
167 pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out);
168 } else {
169 pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
170 }
171
172 secure_vector<uint8_t> key(key_length);
173 pwhash->hash(key, passphrase, salt);
174
175 std::vector<uint8_t> pbkdf2_params;
176
177 const size_t iterations = pwhash->iterations();
178
179 if(msec_in_iterations_out != nullptr) {
180 *msec_in_iterations_out = iterations;
181 }
182
183 DER_Encoder(pbkdf2_params)
186 .encode(iterations)
187 .encode_if(include_key_length_in_struct, key_length)
189 .end_cons();
190
191 kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params);
192 return key;
193 }
194}
195
196/*
197* PKCS#5 v2.0 PBE Encryption
198*/
199std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_shared(std::span<const uint8_t> key_bits,
200 std::string_view passphrase,
201 size_t* msec_in_iterations_out,
202 size_t iterations_if_msec_null,
203 std::string_view cipher,
204 std::string_view prf,
207
208 const auto cipher_spec = split_on(cipher, '/');
209
210 if(cipher_spec.size() != 2 || !known_pbes_cipher_mode(cipher_spec[1]) || !enc) {
211 throw Encoding_Error(fmt("PBE-PKCS5 v2.0: Invalid or unavailable cipher '{}'", cipher));
212 }
213
214 const size_t key_length = enc->key_spec().maximum_keylength();
215
216 const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length());
217
218 AlgorithmIdentifier kdf_algo;
219
220 const bool include_key_length_in_struct = enc->key_spec().minimum_keylength() != enc->key_spec().maximum_keylength();
221
222 const auto derived_key = derive_key(passphrase,
223 prf,
224 rng,
225 msec_in_iterations_out,
226 iterations_if_msec_null,
227 key_length,
228 include_key_length_in_struct,
229 kdf_algo);
230
231 enc->set_key(derived_key);
232 enc->start(iv);
233 secure_vector<uint8_t> ctext(key_bits.begin(), key_bits.end());
234 enc->finish(ctext);
235
236 std::vector<uint8_t> encoded_iv;
238
239 std::vector<uint8_t> pbes2_params;
240 DER_Encoder(pbes2_params)
242 .encode(kdf_algo)
243 .encode(AlgorithmIdentifier(cipher, encoded_iv))
244 .end_cons();
245
246 const AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params);
247
248 return std::make_pair(id, unlock(ctext));
249}
250
251} // namespace
252
253std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt(std::span<const uint8_t> key_bits,
254 std::string_view passphrase,
255 std::chrono::milliseconds msec,
256 std::string_view cipher,
257 std::string_view digest,
259 size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
260 return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
261 // return value msec_in_iterations_out discarded
262}
263
264std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_msec(std::span<const uint8_t> key_bits,
265 std::string_view passphrase,
266 std::chrono::milliseconds msec,
267 size_t* out_iterations_if_nonnull,
268 std::string_view cipher,
269 std::string_view digest,
271 size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
272
273 auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
274
275 if(out_iterations_if_nonnull != nullptr) {
276 *out_iterations_if_nonnull = msec_in_iterations_out;
277 }
278
279 return ret;
280}
281
282std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_iter(std::span<const uint8_t> key_bits,
283 std::string_view passphrase,
284 size_t pbkdf_iter,
285 std::string_view cipher,
286 std::string_view digest,
288 return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng);
289}
290
291secure_vector<uint8_t> pbes2_decrypt(std::span<const uint8_t> key_bits,
292 std::string_view passphrase,
293 const std::vector<uint8_t>& params) {
294 AlgorithmIdentifier kdf_algo;
295 AlgorithmIdentifier enc_algo;
296
299 .decode(kdf_algo)
300 .decode(enc_algo)
301 .end_cons()
302 .verify_end();
303
304 const std::string cipher = enc_algo.oid().human_name_or_empty();
305 const auto cipher_spec = split_on(cipher, '/');
306 if(cipher_spec.size() != 2 || !known_pbes_cipher_mode(cipher_spec[1])) {
307 throw Decoding_Error(fmt("PBE-PKCS5 v2.0: Unknown/invalid cipher OID {}", enc_algo.oid()));
308 }
309
312
314 if(!dec) {
315 throw Decoding_Error(fmt("PBE-PKCS5 cannot decrypt no cipher '{}'", cipher));
316 }
317
318 dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength()));
319
320 dec->start(iv);
321
322 secure_vector<uint8_t> buf(key_bits.begin(), key_bits.end());
323 dec->finish(buf);
324
325 return buf;
326}
327
328} // namespace Botan
const std::vector< uint8_t > & parameters() const
Definition asn1_obj.h:409
const OID & oid() const
Definition asn1_obj.h:407
static Limits DER()
Definition ber_dec.h:35
BER_Decoder & decode(bool &out)
Definition ber_dec.h:220
BER_Decoder & verify_end()
Definition ber_dec.cpp:381
BER_Decoder & end_cons()
Definition ber_dec.cpp:524
BER_Decoder start_sequence()
Definition ber_dec.h:160
BER_Decoder & decode_optional(T &out, ASN1_Type type_tag, ASN1_Class class_tag, const T &default_value=T())
Definition ber_dec.h:285
static std::unique_ptr< Cipher_Mode > create(std::string_view algo, Cipher_Dir direction, std::string_view provider="")
DER_Encoder & start_sequence()
Definition der_enc.h:67
DER_Encoder & encode_if(bool pred, DER_Encoder &enc)
Definition der_enc.h:155
DER_Encoder & end_cons()
Definition der_enc.cpp:173
DER_Encoder & encode(bool b)
Definition der_enc.cpp:245
std::string human_name_or_empty() const
Definition asn1_oid.cpp:147
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:86
static std::unique_ptr< PasswordHashFamily > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition pwdhash.cpp:110
static std::unique_ptr< PasswordHashFamily > create(std::string_view algo_spec, std::string_view provider="")
Definition pwdhash.cpp:54
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
std::vector< std::string > split_on(std::string_view str, char delim)
Definition parsing.cpp:110
std::pair< AlgorithmIdentifier, std::vector< uint8_t > > pbes2_encrypt_iter(std::span< const uint8_t > key_bits, std::string_view passphrase, size_t pbkdf_iter, std::string_view cipher, std::string_view digest, RandomNumberGenerator &rng)
Definition pbes2.cpp:282
secure_vector< uint8_t > pbes2_decrypt(std::span< const uint8_t > key_bits, std::string_view passphrase, const std::vector< uint8_t > &params)
Definition pbes2.cpp:291
std::vector< T > unlock(const secure_vector< T > &in)
Definition secmem.h:85
std::pair< AlgorithmIdentifier, std::vector< uint8_t > > pbes2_encrypt_msec(std::span< const uint8_t > key_bits, std::string_view passphrase, std::chrono::milliseconds msec, size_t *out_iterations_if_nonnull, std::string_view cipher, std::string_view digest, RandomNumberGenerator &rng)
Definition pbes2.cpp:264
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
std::pair< AlgorithmIdentifier, std::vector< uint8_t > > pbes2_encrypt(std::span< const uint8_t > key_bits, std::string_view passphrase, std::chrono::milliseconds msec, std::string_view cipher, std::string_view digest, RandomNumberGenerator &rng)
Definition pbes2.cpp:253