Skip to content

Commit

Permalink
Add simplified measurement loop for password hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
randombit committed Dec 2, 2024
1 parent c8cf4ce commit 8e21b6d
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 53 deletions.
22 changes: 7 additions & 15 deletions src/lib/pbkdf/argon2/argon2pwhash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include <botan/exceptn.h>
#include <botan/internal/fmt.h>
#include <botan/internal/timer.h>
#include <botan/internal/time_utils.h>
#include <algorithm>
#include <limits>

Expand Down Expand Up @@ -85,33 +85,25 @@ std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
const size_t p = 1;
size_t t = 1;

Timer timer("Argon2");
size_t M = 4 * 1024;

auto pwhash = this->from_params(tune_M, t, p);

timer.run_until_elapsed(tune_time, [&]() {
auto tune_fn = [&]() {
uint8_t output[64] = {0};
pwhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0);
});

if(timer.events() == 0 || timer.value() == 0) {
return default_params();
}
};

size_t M = 4 * 1024;

const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M));
const uint64_t measured_time = measure_cost(tune_time, tune_fn) / (tune_M / m);

Check failure on line 97 in src/lib/pbkdf/argon2/argon2pwhash.cpp

View workflow job for this annotation

GitHub Actions / Clang Tidy

use of undeclared identifier 'm'

const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);

/*
* Argon2 scaling rules:
* k*M, k*t, k*p all increase cost by about k
*
* Since we don't even take advantage of p > 1, we prefer increasing
* t or M instead.
*
* If possible to increase M, prefer that.
* First preference is to increase M up to max allowed value.
* Any remaining time budget is spent on increasing t.
*/

uint64_t est_nsec = measured_time;
Expand Down
14 changes: 4 additions & 10 deletions src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include <botan/internal/blowfish.h>
#include <botan/internal/fmt.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/timer.h>
#include <botan/internal/time_utils.h>

namespace Botan {

Expand All @@ -30,8 +30,6 @@ std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length,
std::chrono::milliseconds msec,
size_t /*max_memory*/,
std::chrono::milliseconds tune_time) const {
Timer timer("Bcrypt_PBKDF");

const size_t blocks = (output_length + 32 - 1) / 32;

if(blocks == 0) {
Expand All @@ -42,16 +40,12 @@ std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length,

auto pwhash = this->from_iterations(starting_iter);

timer.run_until_elapsed(tune_time, [&]() {
auto tune_fn = [&]() {
uint8_t output[32] = {0};
pwhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0);
});

if(timer.events() < blocks || timer.value() == 0) {
return default_params();
}
};

const uint64_t measured_time = timer.value() / (timer.events() / blocks);
const uint64_t measured_time = measure_cost(tune_time, tune_fn) / blocks;

const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);

Expand Down
12 changes: 2 additions & 10 deletions src/lib/pbkdf/pbkdf2/pbkdf2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#include <botan/exceptn.h>
#include <botan/internal/fmt.h>
#include <botan/internal/timer.h>
#include <botan/internal/time_utils.h>

namespace Botan {

Expand Down Expand Up @@ -40,22 +40,14 @@ size_t tune_pbkdf2(MessageAuthenticationCode& prf,

// Short output ensures we only need a single PBKDF2 block

Timer timer("PBKDF2");

prf.set_key(nullptr, 0);

timer.run_until_elapsed(tune_time, [&]() {
const uint64_t duration_nsec = measure_cost(tune_time, [&]() {
uint8_t out[12] = {0};
uint8_t salt[12] = {0};
pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations);
});

if(timer.events() == 0) {
return trial_iterations;
}

const uint64_t duration_nsec = timer.value() / timer.events();

const uint64_t desired_nsec = static_cast<uint64_t>(msec.count()) * 1000000;

if(duration_nsec > desired_nsec) {
Expand Down
9 changes: 4 additions & 5 deletions src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#include <botan/exceptn.h>
#include <botan/internal/fmt.h>
#include <botan/internal/timer.h>
#include <botan/internal/time_utils.h>
#include <algorithm>

namespace Botan {
Expand Down Expand Up @@ -95,13 +95,12 @@ std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(size_t output_len,
std::chrono::milliseconds msec,
size_t /*max_memory_usage_mb*/,
std::chrono::milliseconds tune_time) const {
const size_t buf_size = 1024;
constexpr size_t buf_size = 1024;
std::vector<uint8_t> buffer(buf_size);

Timer timer("RFC4880_S2K", buf_size);
timer.run_until_elapsed(tune_time, [&]() { m_hash->update(buffer); });
const uint64_t measured_nsec = measure_cost(tune_time, [&]() { m_hash->update(buffer); });

const double hash_bytes_per_second = timer.bytes_per_second();
const double hash_bytes_per_second = (buf_size * 1000000000.0) / measured_nsec;
const uint64_t desired_nsec = msec.count() * 1000000;

const size_t hash_size = m_hash->output_length();
Expand Down
15 changes: 2 additions & 13 deletions src/lib/pbkdf/scrypt/scrypt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <botan/internal/fmt.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/salsa20.h>
#include <botan/internal/timer.h>
#include <botan/internal/time_utils.h>

namespace Botan {

Expand Down Expand Up @@ -59,24 +59,13 @@ std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
size_t r = 1;
size_t p = 1;

Timer timer("Scrypt");

auto pwdhash = this->from_params(N, r, p);

timer.run_until_elapsed(tune_time, [&]() {
const uint64_t measured_time = measure_cost(tune_time, [&]() {
uint8_t output[32] = {0};
pwdhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0);
});

// No timer events seems strange, perhaps something is wrong - give
// up on this and just return default params
if(timer.events() == 0) {
return default_params();
}

// nsec per eval of scrypt with initial params
const uint64_t measured_time = timer.value() / timer.events();

const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);

uint64_t est_nsec = measured_time;
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils/info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ rotate.h
rounding.h
scan_name.h
stl_util.h
time_utils.h
</header:internal>

<requires>
Expand Down
42 changes: 42 additions & 0 deletions src/lib/utils/time_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* (C) 2024 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_TIME_UTILS_H_
#define BOTAN_TIME_UTILS_H_

#include <botan/internal/os_utils.h>
#include <chrono>

namespace Botan {

template <typename F>
uint64_t measure_cost(std::chrono::milliseconds trial_msec, F func) {
const uint64_t trial_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(trial_msec).count();

uint64_t total_nsec = 0;
uint64_t trials = 0;

auto trial_start = OS::get_system_timestamp_ns();

for(;;) {
const auto start = OS::get_system_timestamp_ns();
func();
const auto end = OS::get_system_timestamp_ns();

if(end >= start) {
total_nsec += (end - start);
trials += 1;

if((end - trial_start) >= trial_nsec) {
return (total_nsec / trials);
}
}
}
}

} // namespace Botan

#endif

0 comments on commit 8e21b6d

Please sign in to comment.