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