diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp')
-rw-r--r-- | comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp new file mode 100644 index 0000000000..96f7f74fee --- /dev/null +++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp @@ -0,0 +1,154 @@ +/** +* (C) 2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/argon2.h> +#include <botan/exceptn.h> +#include <botan/internal/timer.h> +#include <algorithm> + +namespace Botan { + +Argon2::Argon2(uint8_t family, size_t M, size_t t, size_t p) : + m_family(family), + m_M(M), + m_t(t), + m_p(p) + {} + +void Argon2::derive_key(uint8_t output[], size_t output_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + argon2(output, output_len, + password, password_len, + salt, salt_len, + nullptr, 0, + nullptr, 0, + m_family, m_p, m_M, m_t); + } + +namespace { + +std::string argon2_family_name(uint8_t f) + { + switch(f) + { + case 0: + return "Argon2d"; + case 1: + return "Argon2i"; + case 2: + return "Argon2id"; + default: + throw Invalid_Argument("Unknown Argon2 parameter"); + } + } + +} + +std::string Argon2::to_string() const + { + return argon2_family_name(m_family) + "(" + + std::to_string(m_M) + "," + + std::to_string(m_t) + "," + + std::to_string(m_p) + ")"; + } + +Argon2_Family::Argon2_Family(uint8_t family) : m_family(family) + { + if(m_family != 0 && m_family != 1 && m_family != 2) + throw Invalid_Argument("Unknown Argon2 family identifier"); + } + +std::string Argon2_Family::name() const + { + return argon2_family_name(m_family); + } + +std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/, + std::chrono::milliseconds msec, + size_t max_memory) const + { + const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024; + + // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate + // costs for larger params. Default is 36 MiB, or use 128 for long times. + const size_t tune_M = (msec >= std::chrono::milliseconds(500) ? 128 : 36) * 1024; + const size_t p = 1; + size_t t = 1; + + Timer timer("Argon2"); + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t output[64] = { 0 }; + argon2(output, sizeof(output), "test", 4, nullptr, 0, nullptr, 0, nullptr, 0, m_family, p, tune_M, t); + }); + + 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 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. + */ + + uint64_t est_nsec = measured_time; + + if(est_nsec < target_nsec && M < max_kib) + { + const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec; + const uint64_t mem_headroom = max_kib / M; + + const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom); + M *= static_cast<size_t>(M_mult); + est_nsec *= M_mult; + } + + if(est_nsec < target_nsec) + { + const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec; + t *= static_cast<size_t>(desired_cost_increase); + } + + return this->from_params(M, t, p); + } + +std::unique_ptr<PasswordHash> Argon2_Family::default_params() const + { + return this->from_params(128*1024, 1, 1); + } + +std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const + { + /* + These choices are arbitrary, but should not change in future + releases since they will break applications expecting deterministic + mapping from iteration count to params + */ + const size_t M = iter; + const size_t t = 1; + const size_t p = 1; + return this->from_params(M, t, p); + } + +std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const + { + return std::unique_ptr<PasswordHash>(new Argon2(m_family, M, t, p)); + } + +} |