Botan 3.11.0
Crypto and TLS for C&
argon2pwhash.cpp
Go to the documentation of this file.
1/**
2* (C) 2019 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/argon2.h>
8
9#include <botan/assert.h>
10#include <botan/exceptn.h>
11#include <botan/internal/fmt.h>
12#include <botan/internal/time_utils.h>
13#include <algorithm>
14#include <limits>
15
16namespace Botan {
17
18Argon2::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) {
19 BOTAN_ARG_CHECK(m_p >= 1 && m_p <= 128, "Invalid Argon2 threads parameter");
20 BOTAN_ARG_CHECK(m_M >= 8 * m_p && m_M <= 8192 * 1024, "Invalid Argon2 M parameter");
21 BOTAN_ARG_CHECK(m_t >= 1 && m_t <= std::numeric_limits<uint32_t>::max(), "Invalid Argon2 t parameter");
22}
23
24void Argon2::derive_key(uint8_t output[],
25 size_t output_len,
26 const char* password,
27 size_t password_len,
28 const uint8_t salt[],
29 size_t salt_len) const {
30 argon2(output, output_len, password, password_len, salt, salt_len, nullptr, 0, nullptr, 0);
31}
32
33void Argon2::derive_key(uint8_t output[],
34 size_t output_len,
35 const char* password,
36 size_t password_len,
37 const uint8_t salt[],
38 size_t salt_len,
39 const uint8_t ad[],
40 size_t ad_len,
41 const uint8_t key[],
42 size_t key_len) const {
43 argon2(output, output_len, password, password_len, salt, salt_len, key, key_len, ad, ad_len);
44}
45
46namespace {
47
48std::string argon2_family_name(uint8_t f) {
49 switch(f) {
50 case 0:
51 return "Argon2d";
52 case 1:
53 return "Argon2i";
54 case 2:
55 return "Argon2id";
56 default:
57 throw Invalid_Argument("Unknown Argon2 parameter");
58 }
59}
60
61} // namespace
62
63std::string Argon2::to_string() const {
64 return fmt("{}({},{},{})", argon2_family_name(m_family), m_M, m_t, m_p);
65}
66
67Argon2_Family::Argon2_Family(uint8_t family) : m_family(family) {
68 if(m_family != 0 && m_family != 1 && m_family != 2) {
69 throw Invalid_Argument("Unknown Argon2 family identifier");
70 }
71}
72
73std::string Argon2_Family::name() const {
74 return argon2_family_name(m_family);
75}
76
77std::unique_ptr<PasswordHash> Argon2_Family::tune_params(size_t /*output_length*/,
78 uint64_t desired_msec,
79 std::optional<size_t> max_memory,
80 uint64_t tune_msec) const {
81 // If not set use 256 MB as default max
82 const size_t max_kib = max_memory.value_or(256) * 1024;
83
84 // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate
85 // costs for larger params. Default is 36 MiB, or use 128 for long times.
86 const size_t tune_M = (desired_msec >= 200 ? 128 : 36) * 1024;
87 const size_t p = 1;
88 size_t t = 1;
89
90 size_t M = 4 * 1024;
91
92 auto pwhash = this->from_params(tune_M, t, p);
93
94 auto tune_fn = [&]() {
95 uint8_t output[64] = {0};
96 pwhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0);
97 };
98
99 const uint64_t measured_time = measure_cost(tune_msec, tune_fn) / (tune_M / M);
100
101 const uint64_t target_nsec = desired_msec * static_cast<uint64_t>(1000000);
102
103 /*
104 * Argon2 scaling rules:
105 * k*M, k*t, k*p all increase cost by about k
106 *
107 * First preference is to increase M up to max allowed value.
108 * Any remaining time budget is spent on increasing t.
109 */
110
111 uint64_t est_nsec = measured_time;
112
113 if(est_nsec < target_nsec && M < max_kib) {
114 const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
115 const uint64_t mem_headroom = max_kib / M;
116
117 const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom);
118 M *= static_cast<size_t>(M_mult);
119 est_nsec *= M_mult;
120 }
121
122 if(est_nsec < target_nsec / 2) {
123 const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
124 t *= static_cast<size_t>(desired_cost_increase);
125 }
126
127 return this->from_params(M, t, p);
128}
129
130std::unique_ptr<PasswordHash> Argon2_Family::default_params() const {
131 return this->from_params(128 * 1024, 1, 1);
132}
133
134std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const {
135 /*
136 These choices are arbitrary, but should not change in future
137 releases since they will break applications expecting deterministic
138 mapping from iteration count to params
139 */
140 const size_t M = iter;
141 const size_t t = 1;
142 const size_t p = 1;
143 return this->from_params(M, t, p);
144}
145
146std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const {
147 return std::make_unique<Argon2>(m_family, M, t, p);
148}
149
150} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
std::unique_ptr< PasswordHash > from_params(size_t M, size_t t, size_t p) const override
std::string name() const override
BOTAN_FUTURE_EXPLICIT Argon2_Family(uint8_t family)
std::unique_ptr< PasswordHash > tune_params(size_t output_len, uint64_t desired_runtime_msec, std::optional< size_t > max_memory, uint64_t tune_msec) const override
std::unique_ptr< PasswordHash > default_params() const override
std::unique_ptr< PasswordHash > from_iterations(size_t iter) const override
size_t p() const
Definition argon2.h:55
size_t t() const
Definition argon2.h:53
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
size_t M() const
Definition argon2.h:51
Argon2(uint8_t family, size_t M, size_t t, size_t p)
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
uint64_t measure_cost(uint64_t trial_msec, F func)
Definition time_utils.h:19