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