/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 |