8#include <botan/internal/pkcs12_kdf.h>
10#include <botan/assert.h>
11#include <botan/exceptn.h>
12#include <botan/hash.h>
13#include <botan/mem_ops.h>
14#include <botan/internal/charset.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/int_utils.h>
17#include <botan/internal/time_utils.h>
29void copy_repeat(std::span<uint8_t> buf, std::span<const uint8_t> data) {
36 while(pos < buf.size()) {
37 const size_t to_copy = std::min(data.size(), buf.size() - pos);
38 copy_mem(buf.subspan(pos, to_copy), data.first(to_copy));
46void bigendian_add_one(std::span<uint8_t> block, std::span<const uint8_t> addend) {
50 for(
size_t k = block.size(); k > 0; --k) {
51 carry +=
static_cast<uint16_t
>(block[k - 1]) +
static_cast<uint16_t
>(addend[k - 1]);
52 block[k - 1] =
static_cast<uint8_t
>(
carry & 0xFF);
58 if(password.empty()) {
70void pkcs12_kdf_with_hash(std::span<uint8_t> out,
71 std::span<const uint8_t> pwd_bytes,
72 std::span<const uint8_t> salt,
79 if(id < 1 || id > 3) {
80 throw Invalid_Argument(
"PKCS12-KDF: Invalid id (must be 1=key, 2=IV, or 3=MAC)");
84 const size_t v = hash.hash_block_size();
86 throw Invalid_Argument(
fmt(
"PKCS12-KDF does not support hash '{}': undefined block size", hash.name()));
93 const size_t hash_len = hash.output_length();
95 const size_t pwd_len = pwd_bytes.size();
96 const size_t salt_len = salt.size();
100 auto round_up_to_v = [v](
size_t len) ->
size_t {
104 const size_t blocks = (len / v) + (len % v != 0 ? 1 : 0);
105 return mul_or_throw(blocks, v,
"PKCS12-KDF: input too large");
109 const size_t S_len = round_up_to_v(salt_len);
110 const size_t P_len = round_up_to_v(pwd_len);
111 const size_t I_len =
add_or_throw(S_len, P_len,
"PKCS12-KDF: input too large");
119 copy_repeat(std::span{I}.first(S_len), salt);
122 copy_repeat(std::span{I}.last(P_len), pwd_bytes);
128 size_t out_offset = 0;
129 while(out_offset < out.size()) {
135 for(
size_t iter = 1; iter < iterations; ++iter) {
141 const size_t to_copy = std::min(hash_len, out.size() - out_offset);
142 copy_mem(out.subspan(out_offset, to_copy), std::span{A}.first(to_copy));
143 out_offset += to_copy;
149 for(
size_t j = 0; j < I_len; j += v) {
150 bigendian_add_one(std::span{I}.subspan(j, v), B);
158 std::span<const uint8_t> pwd_bytes,
159 std::span<const uint8_t> salt,
163 pkcs12_kdf_with_hash(out, pwd_bytes, salt, iterations,
id, hash);
168 BOTAN_ARG_CHECK(m_hash !=
nullptr,
"PKCS12-KDF: hash must not be null");
169 BOTAN_ARG_CHECK(m_iterations > 0,
"PKCS12-KDF: iterations must be greater than zero");
170 BOTAN_ARG_CHECK(m_id >= 1 && m_id <= 3,
"PKCS12-KDF: id must be 1 (key), 2 (IV), or 3 (MAC)");
174 return fmt(
"PKCS12-KDF({},{},{})", m_hash->name(),
static_cast<unsigned>(m_id), m_iterations);
179 const char* password,
181 const uint8_t salt[],
182 size_t salt_len)
const {
183 const std::string_view pwd =
184 (password !=
nullptr && password_len > 0) ? std::string_view(password, password_len) : std::string_view{};
185 pkcs12_kdf_with_hash({out, out_len}, pkcs12_encode_password(pwd), {salt, salt_len}, m_iterations, m_id, *m_hash);
189 m_hash(std::move(hash)), m_id(static_cast<uint8_t>(id)) {
190 BOTAN_ARG_CHECK(m_hash !=
nullptr,
"PKCS12-KDF: hash must not be null");
191 BOTAN_ARG_CHECK(
id >= 1 &&
id <= 3,
"PKCS12-KDF: id must be 1 (key), 2 (IV), or 3 (MAC)");
195 return fmt(
"PKCS12-KDF({},{})", m_hash->name(),
static_cast<unsigned>(m_id));
199 uint64_t desired_msec,
200 std::optional<size_t> ,
201 uint64_t tuning_msec)
const {
205 const size_t tuning_iterations = 10000;
206 const size_t tuning_out_len = std::max<size_t>(output_length, 1);
207 const std::array<uint8_t, 16> tuning_salt{};
208 const std::array<uint8_t, 16> tuning_pwd{};
209 const auto pwd_bytes =
210 pkcs12_encode_password(std::string_view(
reinterpret_cast<const char*
>(tuning_pwd.data()), tuning_pwd.size()));
211 std::vector<uint8_t> tuning_out(tuning_out_len);
213 auto tuning_hash = m_hash->new_object();
214 const uint64_t measured_nsec =
measure_cost(tuning_msec, [&]() {
215 pkcs12_kdf_with_hash(tuning_out, pwd_bytes, tuning_salt, tuning_iterations, m_id, *tuning_hash);
219 const double measured = std::max<double>(1.0,
static_cast<double>(measured_nsec));
220 const double desired =
static_cast<double>(desired_msec) * 1'000'000.0;
221 const double est = (desired *
static_cast<double>(tuning_iterations)) / measured;
222 const double est_clamped = std::clamp(est, 1.0,
static_cast<double>(std::numeric_limits<size_t>::max()));
223 const size_t iterations =
static_cast<size_t>(est_clamped);
225 return std::make_unique<PKCS12_KDF>(m_hash->new_object(), m_id, iterations);
229 return std::make_unique<PKCS12_KDF>(m_hash->new_object(), m_id, 2048);
233 return std::make_unique<PKCS12_KDF>(m_hash->new_object(), m_id, iterations);
237 return std::make_unique<PKCS12_KDF>(m_hash->new_object(), m_id, i1);
#define BOTAN_DEBUG_ASSERT(expr)
#define BOTAN_ARG_CHECK(expr, msg)
std::string name() const override
std::unique_ptr< PasswordHash > from_params(size_t i1, size_t i2=0, size_t i3=0) const override
std::unique_ptr< PasswordHash > tune_params(size_t output_length, uint64_t desired_runtime_msec, std::optional< size_t > max_memory_usage_mb={}, uint64_t tuning_msec=10) const override
std::unique_ptr< PasswordHash > default_params() const override
std::unique_ptr< PasswordHash > from_iterations(size_t iterations) const override
PKCS12_KDF_Family(std::unique_ptr< HashFunction > hash, size_t id)
void derive_key(uint8_t out[], size_t out_len, const char *password, size_t password_len, const uint8_t salt[], size_t salt_len) const override
std::string to_string() const override
PKCS12_KDF(std::unique_ptr< HashFunction > hash, uint8_t id, size_t iterations)
size_t iterations() const override
void hash(std::span< uint8_t > out, std::string_view password, std::span< const uint8_t > salt) const
constexpr T add_or_throw(T a, T b, std::string_view msg)
constexpr T mul_or_throw(T a, T b, std::string_view msg)
std::string fmt(std::string_view format, const T &... args)
constexpr void copy_mem(T *out, const T *in, size_t n)
void secure_scrub_memory(void *ptr, size_t n)
void carry(int64_t &h0, int64_t &h1)
std::vector< T, secure_allocator< T > > secure_vector
uint64_t measure_cost(uint64_t trial_msec, F func)
void pkcs12_kdf(std::span< uint8_t > out, std::span< const uint8_t > pwd_bytes, std::span< const uint8_t > salt, size_t iterations, uint8_t id, HashFunction &hash)
constexpr void clear_mem(T *ptr, size_t n)
std::vector< uint8_t > utf8_to_ucs2(std::string_view utf8)