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