/src/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
12 | | namespace Botan { |
13 | | |
14 | | Argon2::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 | 0 | { |
20 | 0 | BOTAN_ARG_CHECK(m_p >= 1 && m_p <= 128, "Invalid Argon2 threads parameter"); |
21 | 0 | BOTAN_ARG_CHECK(m_M >= 8*m_p && m_M <= 8192*1024, "Invalid Argon2 M parameter"); |
22 | 0 | BOTAN_ARG_CHECK(m_t >= 1, "Invalid Argon2 t parameter"); |
23 | 0 | } |
24 | | |
25 | | void 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 | 0 | { |
29 | 0 | argon2(output, output_len, |
30 | 0 | password, password_len, |
31 | 0 | salt, salt_len, |
32 | 0 | nullptr, 0, |
33 | 0 | nullptr, 0); |
34 | 0 | } |
35 | | |
36 | | void Argon2::derive_key(uint8_t output[], size_t output_len, |
37 | | const char* password, size_t password_len, |
38 | | const uint8_t salt[], size_t salt_len, |
39 | | const uint8_t ad[], size_t ad_len, |
40 | | const uint8_t key[], size_t key_len) const |
41 | 0 | { |
42 | 0 | argon2(output, output_len, |
43 | 0 | password, password_len, |
44 | 0 | salt, salt_len, |
45 | 0 | key, key_len, |
46 | 0 | ad, ad_len); |
47 | 0 | } |
48 | | |
49 | | namespace { |
50 | | |
51 | | std::string argon2_family_name(uint8_t f) |
52 | 0 | { |
53 | 0 | switch(f) |
54 | 0 | { |
55 | 0 | case 0: |
56 | 0 | return "Argon2d"; |
57 | 0 | case 1: |
58 | 0 | return "Argon2i"; |
59 | 0 | case 2: |
60 | 0 | return "Argon2id"; |
61 | 0 | default: |
62 | 0 | throw Invalid_Argument("Unknown Argon2 parameter"); |
63 | 0 | } |
64 | 0 | } |
65 | | |
66 | | } |
67 | | |
68 | | std::string Argon2::to_string() const |
69 | 0 | { |
70 | 0 | return argon2_family_name(m_family) + "(" + |
71 | 0 | std::to_string(m_M) + "," + |
72 | 0 | std::to_string(m_t) + "," + |
73 | 0 | std::to_string(m_p) + ")"; |
74 | 0 | } |
75 | | |
76 | | Argon2_Family::Argon2_Family(uint8_t family) : m_family(family) |
77 | 0 | { |
78 | 0 | if(m_family != 0 && m_family != 1 && m_family != 2) |
79 | 0 | throw Invalid_Argument("Unknown Argon2 family identifier"); |
80 | 0 | } |
81 | | |
82 | | std::string Argon2_Family::name() const |
83 | 0 | { |
84 | 0 | return argon2_family_name(m_family); |
85 | 0 | } |
86 | | |
87 | | std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/, |
88 | | std::chrono::milliseconds msec, |
89 | | size_t max_memory) const |
90 | 0 | { |
91 | 0 | const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024; |
92 | | |
93 | | // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate |
94 | | // costs for larger params. Default is 36 MiB, or use 128 for long times. |
95 | 0 | const size_t tune_M = (msec >= std::chrono::milliseconds(500) ? 128 : 36) * 1024; |
96 | 0 | const size_t p = 1; |
97 | 0 | size_t t = 1; |
98 | |
|
99 | 0 | Timer timer("Argon2"); |
100 | 0 | const auto tune_time = BOTAN_PBKDF_TUNING_TIME; |
101 | |
|
102 | 0 | auto pwhash = this->from_params(tune_M, t, p); |
103 | |
|
104 | 0 | timer.run_until_elapsed(tune_time, [&]() { |
105 | 0 | uint8_t output[64] = { 0 }; |
106 | 0 | pwhash->derive_key(output, sizeof(output), |
107 | 0 | "test", 4, |
108 | 0 | nullptr, 0); |
109 | 0 | }); |
110 | |
|
111 | 0 | if(timer.events() == 0 || timer.value() == 0) |
112 | 0 | return default_params(); |
113 | | |
114 | 0 | size_t M = 4*1024; |
115 | |
|
116 | 0 | const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M)); |
117 | |
|
118 | 0 | 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 | 0 | uint64_t est_nsec = measured_time; |
131 | |
|
132 | 0 | if(est_nsec < target_nsec && M < max_kib) |
133 | 0 | { |
134 | 0 | const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec; |
135 | 0 | const uint64_t mem_headroom = max_kib / M; |
136 | |
|
137 | 0 | const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom); |
138 | 0 | M *= static_cast<size_t>(M_mult); |
139 | 0 | est_nsec *= M_mult; |
140 | 0 | } |
141 | |
|
142 | 0 | if(est_nsec < target_nsec) |
143 | 0 | { |
144 | 0 | const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec; |
145 | 0 | t *= static_cast<size_t>(desired_cost_increase); |
146 | 0 | } |
147 | |
|
148 | 0 | return this->from_params(M, t, p); |
149 | 0 | } |
150 | | |
151 | | std::unique_ptr<PasswordHash> Argon2_Family::default_params() const |
152 | 0 | { |
153 | 0 | return this->from_params(128*1024, 1, 1); |
154 | 0 | } |
155 | | |
156 | | std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const |
157 | 0 | { |
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 | 0 | const size_t M = iter; |
164 | 0 | const size_t t = 1; |
165 | 0 | const size_t p = 1; |
166 | 0 | return this->from_params(M, t, p); |
167 | 0 | } |
168 | | |
169 | | std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const |
170 | 0 | { |
171 | 0 | return std::make_unique<Argon2>(m_family, M, t, p); |
172 | 0 | } |
173 | | |
174 | | } |