9#include <botan/internal/pbes2.h>
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>
17#include <botan/internal/bit_ops.h>
18#include <botan/internal/fmt.h>
19#include <botan/internal/parsing.h>
25class Pbes2KdfParameters {
27 virtual ~Pbes2KdfParameters() =
default;
28 Pbes2KdfParameters(
const Pbes2KdfParameters& other) =
delete;
29 Pbes2KdfParameters(Pbes2KdfParameters&& other) =
delete;
30 Pbes2KdfParameters& operator=(
const Pbes2KdfParameters& other) =
delete;
31 Pbes2KdfParameters& operator=(Pbes2KdfParameters&& other) =
delete;
33 static std::unique_ptr<Pbes2KdfParameters>
decode(
const AlgorithmIdentifier& alg_id);
35 static std::unique_ptr<Pbes2KdfParameters> tune(std::string_view digest,
36 RandomNumberGenerator& rng,
38 size_t* msec_in_iterations_out,
39 size_t iterations_if_msec_null);
43 virtual AlgorithmIdentifier algorithm_identifier(
bool include_key_length)
const = 0;
46 static constexpr size_t DefaultSaltBytes = 16;
48 const PasswordHash& pwdhash()
const {
return *m_pwdhash; }
50 std::optional<size_t> key_length()
const {
return m_key_length; }
52 std::span<const uint8_t> salt()
const {
return m_salt; }
54 Pbes2KdfParameters(std::vector<uint8_t> salt,
55 std::optional<size_t> key_length,
56 std::unique_ptr<PasswordHash> pwdhash) :
57 m_salt(std::move(salt)), m_key_length(key_length), m_pwdhash(std::move(pwdhash)) {}
59 static std::vector<uint8_t> generate_salt(RandomNumberGenerator& rng) {
60 return rng.random_vec<std::vector<uint8_t>>(DefaultSaltBytes);
63 static void validate_pbes2_params(
size_t salt_len, std::optional<size_t> key_length) {
64 if(key_length && (key_length.value() < 8 || key_length.value() >= 256)) {
65 throw Decoding_Error(
fmt(
"PBES2: Encoded key length ({}) is invalid", key_length.value()));
68 throw Decoding_Error(
"PBES2: Encoded salt is too small");
73 std::vector<uint8_t> m_salt;
74 std::optional<size_t> m_key_length;
75 std::unique_ptr<PasswordHash> m_pwdhash;
78class Pbes2Pbkdf2Parameters final :
public Pbes2KdfParameters {
80 static std::unique_ptr<Pbes2Pbkdf2Parameters>
decode(
const AlgorithmIdentifier& kdf_algo);
82 static std::unique_ptr<Pbes2Pbkdf2Parameters> tune(std::string_view digest,
83 RandomNumberGenerator& rng,
85 size_t* msec_in_iterations_out,
86 size_t iterations_if_msec_null);
88 Pbes2Pbkdf2Parameters(std::vector<uint8_t> salt,
89 std::optional<size_t> key_length,
90 std::unique_ptr<PasswordHash> pwdhash,
92 Pbes2KdfParameters(std::move(salt), key_length, std::move(pwdhash)), m_prf(std::move(prf)) {}
94 AlgorithmIdentifier algorithm_identifier(
bool include_key_length)
const override;
97 static void validate_params(
size_t iterations);
102class Pbes2ScryptParameters final :
public Pbes2KdfParameters {
104 static std::unique_ptr<Pbes2ScryptParameters>
decode(
const AlgorithmIdentifier& kdf_algo);
106 static std::unique_ptr<Pbes2ScryptParameters> tune(RandomNumberGenerator& rng,
108 size_t* msec_in_iterations_out,
109 size_t iterations_if_msec_null);
111 Pbes2ScryptParameters(std::vector<uint8_t> salt,
112 std::optional<size_t> key_length,
113 std::unique_ptr<PasswordHash> pwdhash) :
114 Pbes2KdfParameters(std::move(salt), key_length, std::move(pwdhash)) {}
116 AlgorithmIdentifier algorithm_identifier(
bool include_key_length)
const override;
119 static void validate_params(
size_t N,
size_t r,
size_t p);
124secure_vector<uint8_t> Pbes2KdfParameters::derive_key(std::string_view passphrase,
size_t default_key_size)
const {
125 const size_t kl = m_key_length.value_or(default_key_size);
126 secure_vector<uint8_t> key(kl);
127 m_pwdhash->hash(key, passphrase, m_salt);
131std::unique_ptr<Pbes2KdfParameters> Pbes2KdfParameters::tune(std::string_view digest,
132 RandomNumberGenerator& rng,
134 size_t* msec_in_iterations_out,
135 size_t iterations_if_msec_null) {
136 if(digest ==
"Scrypt") {
137 return Pbes2ScryptParameters::tune(rng, key_length, msec_in_iterations_out, iterations_if_msec_null);
139 return Pbes2Pbkdf2Parameters::tune(digest, rng, key_length, msec_in_iterations_out, iterations_if_msec_null);
143std::unique_ptr<Pbes2KdfParameters> Pbes2KdfParameters::decode(
const AlgorithmIdentifier& kdf_algo) {
144 if(kdf_algo.oid() == OID::from_string(
"PKCS5.PBKDF2")) {
145 return Pbes2Pbkdf2Parameters::decode(kdf_algo);
146 }
else if(kdf_algo.oid() == OID::from_string(
"Scrypt")) {
147 return Pbes2ScryptParameters::decode(kdf_algo);
149 throw Decoding_Error(
fmt(
"PBES2 unknown or unhandled KDF algorithm '{}'", kdf_algo.oid()));
155std::unique_ptr<Pbes2Pbkdf2Parameters> Pbes2Pbkdf2Parameters::tune(std::string_view digest,
156 RandomNumberGenerator& rng,
158 size_t* msec_in_iterations_out,
159 size_t iterations_if_msec_null) {
160 const std::string prf =
fmt(
"HMAC({})", digest);
162 auto pwhash_fam = PasswordHashFamily::create(
fmt(
"PBKDF2({})", prf));
164 throw Invalid_Argument(
fmt(
"Unknown password hash digest {}", digest));
167 std::unique_ptr<PasswordHash> pwhash;
168 if(msec_in_iterations_out !=
nullptr) {
169 pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out);
170 *msec_in_iterations_out = pwhash->iterations();
172 pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
176 validate_params(pwhash->iterations());
178 return std::make_unique<Pbes2Pbkdf2Parameters>(generate_salt(rng), key_length, std::move(pwhash), prf);
182void Pbes2Pbkdf2Parameters::validate_params(
size_t iterations) {
184 constexpr size_t MaximumPbes2Pbkdf2Iterations = 100'000'000;
186 if(iterations == 0 || iterations > MaximumPbes2Pbkdf2Iterations) {
187 throw Decoding_Error(
fmt(
"PBES2: Invalid or unacceptable PBKDF2 iteration count ({})", iterations));
191std::unique_ptr<Pbes2Pbkdf2Parameters> Pbes2Pbkdf2Parameters::decode(
const AlgorithmIdentifier& kdf_algo) {
192 std::vector<uint8_t> salt;
193 size_t iterations = 0;
194 std::optional<size_t> key_length;
196 AlgorithmIdentifier prf_algo;
197 BER_Decoder(kdf_algo.parameters(), BER_Decoder::Limits::DER())
199 .decode(salt, ASN1_Type::OctetString)
201 .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal)
202 .decode_optional(prf_algo,
204 ASN1_Class::Constructed,
205 AlgorithmIdentifier(
"HMAC(SHA-1)", AlgorithmIdentifier::USE_NULL_PARAM))
209 validate_pbes2_params(salt.size(), key_length);
210 validate_params(iterations);
212 const std::string prf = [&]() {
213 if(
const auto name = prf_algo.oid().registered_name()) {
214 if(name->starts_with(
"HMAC")) {
219 throw Decoding_Error(
fmt(
"Unknown PBES2 PRF '{}'", prf_algo.oid()));
224 if(!prf_algo.parameters_are_null_or_empty()) {
225 throw Decoding_Error(
"PBES2 PRF AlgorithmIdentifier has unexpected parameters");
228 auto pbkdf_fam = PasswordHashFamily::create_or_throw(
fmt(
"PBKDF2({})", prf));
229 auto pwdhash = pbkdf_fam->from_params(iterations);
231 return std::make_unique<Pbes2Pbkdf2Parameters>(std::move(salt), key_length, std::move(pwdhash), prf);
234AlgorithmIdentifier Pbes2Pbkdf2Parameters::algorithm_identifier(
bool include_key_length)
const {
235 std::vector<uint8_t> params;
238 .encode(salt(), ASN1_Type::OctetString)
239 .encode(pwdhash().iterations())
240 .encode_if(include_key_length && key_length().has_value(), key_length().value_or(0))
241 .encode_if(m_prf !=
"HMAC(SHA-1)", AlgorithmIdentifier(m_prf, AlgorithmIdentifier::USE_NULL_PARAM))
243 return AlgorithmIdentifier(
"PKCS5.PBKDF2", params);
248std::unique_ptr<Pbes2ScryptParameters> Pbes2ScryptParameters::tune(RandomNumberGenerator& rng,
250 size_t* msec_in_iterations_out,
251 size_t iterations_if_msec_null) {
252 auto pwhash_fam = PasswordHashFamily::create_or_throw(
"Scrypt");
254 std::unique_ptr<PasswordHash> pwhash;
255 if(msec_in_iterations_out !=
nullptr) {
256 pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out);
257 *msec_in_iterations_out = 0;
259 pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
263 validate_params(pwhash->memory_param(), pwhash->iterations(), pwhash->parallelism());
265 return std::make_unique<Pbes2ScryptParameters>(generate_salt(rng), key_length, std::move(pwhash));
269void Pbes2ScryptParameters::validate_params(
size_t N,
size_t r,
size_t p) {
271 throw Decoding_Error(
fmt(
"PBES2: Invalid or unacceptable Scrypt parameter N ({})", N));
273 if(r == 0 || r > 64) {
274 throw Decoding_Error(
fmt(
"PBES2: Invalid or unacceptable Scrypt parameter r ({})", r));
276 if(p == 0 || p >= 1024) {
277 throw Decoding_Error(
fmt(
"PBES2: Invalid or unacceptable Scrypt parameter p ({})", p));
284 const uint64_t scrypt_work = uint64_t(N) * r * p;
285 constexpr uint64_t MaximumPbes2ScryptWork = (1 << 26);
287 if(scrypt_work > MaximumPbes2ScryptWork) {
288 throw Decoding_Error(
fmt(
"PBES2: Invalid or unacceptable Scrypt parameters N={} r={} p={}", N, r, p));
292std::unique_ptr<Pbes2ScryptParameters> Pbes2ScryptParameters::decode(
const AlgorithmIdentifier& kdf_algo) {
293 std::vector<uint8_t> salt;
297 std::optional<size_t> key_length;
299 BER_Decoder(kdf_algo.parameters(), BER_Decoder::Limits::DER())
301 .decode(salt, ASN1_Type::OctetString)
305 .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal)
309 validate_pbes2_params(salt.size(), key_length);
310 validate_params(N, r, p);
312 auto pwdhash_fam = PasswordHashFamily::create_or_throw(
"Scrypt");
313 auto pwdhash = pwdhash_fam->from_params(N, r, p);
315 return std::make_unique<Pbes2ScryptParameters>(std::move(salt), key_length, std::move(pwdhash));
318AlgorithmIdentifier Pbes2ScryptParameters::algorithm_identifier(
bool include_key_length)
const {
319 std::vector<uint8_t> params;
322 .encode(salt(), ASN1_Type::OctetString)
323 .encode(pwdhash().memory_param())
324 .encode(pwdhash().iterations())
325 .encode(pwdhash().parallelism())
326 .encode_if(include_key_length && key_length().has_value(), key_length().value_or(0))
328 return AlgorithmIdentifier(OID::from_string(
"Scrypt"), params);
331bool known_pbes_cipher_mode(std::string_view mode) {
332 return (mode ==
"CBC" || mode ==
"GCM" || mode ==
"SIV");
338std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbes2_encrypt_shared(std::span<const uint8_t> key_bits,
339 std::string_view passphrase,
340 size_t* msec_in_iterations_out,
341 size_t iterations_if_msec_null,
342 std::string_view cipher,
343 std::string_view prf,
344 RandomNumberGenerator& rng) {
347 const auto cipher_spec =
split_on(cipher,
'/');
349 if(cipher_spec.size() != 2 || !known_pbes_cipher_mode(cipher_spec[1]) || !enc) {
350 throw Encoding_Error(
fmt(
"PBES2: Invalid or unavailable cipher '{}'", cipher));
353 const size_t key_length = enc->key_spec().maximum_keylength();
355 const auto iv = rng.random_vec<std::vector<uint8_t>>(enc->default_nonce_length());
357 const bool include_key_length_in_struct = enc->key_spec().minimum_keylength() != enc->key_spec().maximum_keylength();
359 auto kdf_params = Pbes2KdfParameters::tune(prf, rng, key_length, msec_in_iterations_out, iterations_if_msec_null);
360 const auto derived_key = kdf_params->derive_key(passphrase, key_length);
361 const auto kdf_algo = kdf_params->algorithm_identifier(include_key_length_in_struct);
363 enc->set_key(derived_key);
368 std::vector<uint8_t> encoded_iv;
371 std::vector<uint8_t> pbes2_params;
372 DER_Encoder(pbes2_params)
375 .encode(AlgorithmIdentifier(cipher, encoded_iv))
378 const AlgorithmIdentifier id(
OID::from_string(
"PBE-PKCS5v20"), pbes2_params);
380 return std::make_pair(
id,
unlock(ctext));
385std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
pbes2_encrypt(std::span<const uint8_t> key_bits,
386 std::string_view passphrase,
387 std::chrono::milliseconds msec,
388 std::string_view cipher,
389 std::string_view digest,
391 size_t msec_in_iterations_out =
static_cast<size_t>(msec.count());
392 return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
396std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
pbes2_encrypt_msec(std::span<const uint8_t> key_bits,
397 std::string_view passphrase,
398 std::chrono::milliseconds msec,
399 size_t* out_iterations_if_nonnull,
400 std::string_view cipher,
401 std::string_view digest,
403 size_t msec_in_iterations_out =
static_cast<size_t>(msec.count());
405 auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
407 if(out_iterations_if_nonnull !=
nullptr) {
408 *out_iterations_if_nonnull = msec_in_iterations_out;
414std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
pbes2_encrypt_iter(std::span<const uint8_t> key_bits,
415 std::string_view passphrase,
417 std::string_view cipher,
418 std::string_view digest,
420 return pbes2_encrypt_shared(key_bits, passphrase,
nullptr, pbkdf_iter, cipher, digest, rng);
424 std::string_view passphrase,
425 const std::vector<uint8_t>& params) {
436 const std::string cipher = [&]() -> std::string {
438 const auto cipher_spec =
split_on(*name,
'/');
439 if(cipher_spec.size() == 2 && known_pbes_cipher_mode(cipher_spec[1])) {
447 std::vector<uint8_t> iv;
452 throw Decoding_Error(
fmt(
"PBES2 cannot decrypt due to unavailable cipher '{}'", cipher));
457 if(iv.size() != dec->default_nonce_length()) {
458 throw Decoding_Error(
"PBES2 cipher AlgorithmIdentifier has invalid IV length");
461 const size_t default_key_size = dec->key_spec().maximum_keylength();
462 auto pbkdf = Pbes2KdfParameters::decode(kdf_algo);
463 dec->set_key(pbkdf->derive_key(passphrase, default_key_size));
const std::vector< uint8_t > & parameters() const
BER_Decoder & decode(bool &out)
BER_Decoder & verify_end()
BER_Decoder start_sequence()
static std::unique_ptr< Cipher_Mode > create(std::string_view algo, Cipher_Dir direction, std::string_view provider="")
std::optional< std::string > registered_name() const
static OID from_string(std::string_view str)
secure_vector< uint8_t > decode(DataSource &source, std::string &label)
BOTAN_FORCE_INLINE constexpr bool is_power_of_2(T arg)
std::string fmt(std::string_view format, const T &... args)
std::vector< std::string > split_on(std::string_view str, char delim)
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)
secure_vector< uint8_t > pbes2_decrypt(std::span< const uint8_t > key_bits, std::string_view passphrase, const std::vector< uint8_t > ¶ms)
std::vector< T > unlock(const secure_vector< T > &in)
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)
std::vector< T, secure_allocator< T > > secure_vector
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)