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