/src/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * PBKDF2 |
3 | | * (C) 1999-2007 Jack Lloyd |
4 | | * (C) 2018 Ribose Inc |
5 | | * |
6 | | * Botan is released under the Simplified BSD License (see license.txt) |
7 | | */ |
8 | | |
9 | | #include <botan/pbkdf2.h> |
10 | | #include <botan/exceptn.h> |
11 | | #include <botan/internal/rounding.h> |
12 | | #include <botan/internal/timer.h> |
13 | | |
14 | | namespace Botan { |
15 | | |
16 | | namespace { |
17 | | |
18 | | void pbkdf2_set_key(MessageAuthenticationCode& prf, |
19 | | const char* password, |
20 | | size_t password_len) |
21 | 0 | { |
22 | 0 | try |
23 | 0 | { |
24 | 0 | prf.set_key(cast_char_ptr_to_uint8(password), password_len); |
25 | 0 | } |
26 | 0 | catch(Invalid_Key_Length&) |
27 | 0 | { |
28 | 0 | throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size"); |
29 | 0 | } |
30 | 0 | } |
31 | | |
32 | | } |
33 | | |
34 | | size_t |
35 | | pbkdf2(MessageAuthenticationCode& prf, |
36 | | uint8_t out[], |
37 | | size_t out_len, |
38 | | const std::string& password, |
39 | | const uint8_t salt[], size_t salt_len, |
40 | | size_t iterations, |
41 | | std::chrono::milliseconds msec) |
42 | 0 | { |
43 | 0 | if(iterations == 0) |
44 | 0 | { |
45 | 0 | iterations = PBKDF2(prf, out_len, msec).iterations(); |
46 | 0 | } |
47 | 0 |
|
48 | 0 | PBKDF2 pbkdf2(prf, iterations); |
49 | 0 |
|
50 | 0 | pbkdf2.derive_key(out, out_len, |
51 | 0 | password.c_str(), password.size(), |
52 | 0 | salt, salt_len); |
53 | 0 |
|
54 | 0 | return iterations; |
55 | 0 | } |
56 | | |
57 | | namespace { |
58 | | |
59 | | size_t tune_pbkdf2(MessageAuthenticationCode& prf, |
60 | | size_t output_length, |
61 | | uint32_t msec) |
62 | 0 | { |
63 | 0 | if(output_length == 0) |
64 | 0 | output_length = 1; |
65 | 0 |
|
66 | 0 | const size_t prf_sz = prf.output_length(); |
67 | 0 | BOTAN_ASSERT_NOMSG(prf_sz > 0); |
68 | 0 | secure_vector<uint8_t> U(prf_sz); |
69 | 0 |
|
70 | 0 | const size_t trial_iterations = 2000; |
71 | 0 |
|
72 | 0 | // Short output ensures we only need a single PBKDF2 block |
73 | 0 |
|
74 | 0 | Timer timer("PBKDF2"); |
75 | 0 |
|
76 | 0 | const auto tune_time = BOTAN_PBKDF_TUNING_TIME; |
77 | 0 |
|
78 | 0 | prf.set_key(nullptr, 0); |
79 | 0 |
|
80 | 0 | timer.run_until_elapsed(tune_time, [&]() { |
81 | 0 | uint8_t out[12] = { 0 }; |
82 | 0 | uint8_t salt[12] = { 0 }; |
83 | 0 | pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations); |
84 | 0 | }); |
85 | 0 |
|
86 | 0 | if(timer.events() == 0) |
87 | 0 | return trial_iterations; |
88 | 0 | |
89 | 0 | const uint64_t duration_nsec = timer.value() / timer.events(); |
90 | 0 |
|
91 | 0 | const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000; |
92 | 0 |
|
93 | 0 | if(duration_nsec > desired_nsec) |
94 | 0 | return trial_iterations; |
95 | 0 | |
96 | 0 | const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz; |
97 | 0 |
|
98 | 0 | const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed); |
99 | 0 |
|
100 | 0 | if(multiplier == 0) |
101 | 0 | return trial_iterations; |
102 | 0 | else |
103 | 0 | return trial_iterations * multiplier; |
104 | 0 | } |
105 | | |
106 | | } |
107 | | |
108 | | void pbkdf2(MessageAuthenticationCode& prf, |
109 | | uint8_t out[], |
110 | | size_t out_len, |
111 | | const uint8_t salt[], |
112 | | size_t salt_len, |
113 | | size_t iterations) |
114 | 0 | { |
115 | 0 | if(iterations == 0) |
116 | 0 | throw Invalid_Argument("PBKDF2: Invalid iteration count"); |
117 | 0 | |
118 | 0 | clear_mem(out, out_len); |
119 | 0 |
|
120 | 0 | if(out_len == 0) |
121 | 0 | return; |
122 | 0 | |
123 | 0 | const size_t prf_sz = prf.output_length(); |
124 | 0 | BOTAN_ASSERT_NOMSG(prf_sz > 0); |
125 | 0 |
|
126 | 0 | secure_vector<uint8_t> U(prf_sz); |
127 | 0 |
|
128 | 0 | uint32_t counter = 1; |
129 | 0 | while(out_len) |
130 | 0 | { |
131 | 0 | const size_t prf_output = std::min<size_t>(prf_sz, out_len); |
132 | 0 |
|
133 | 0 | prf.update(salt, salt_len); |
134 | 0 | prf.update_be(counter++); |
135 | 0 | prf.final(U.data()); |
136 | 0 |
|
137 | 0 | xor_buf(out, U.data(), prf_output); |
138 | 0 |
|
139 | 0 | for(size_t i = 1; i != iterations; ++i) |
140 | 0 | { |
141 | 0 | prf.update(U); |
142 | 0 | prf.final(U.data()); |
143 | 0 | xor_buf(out, U.data(), prf_output); |
144 | 0 | } |
145 | 0 |
|
146 | 0 | out_len -= prf_output; |
147 | 0 | out += prf_output; |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | | // PBKDF interface |
152 | | size_t |
153 | | PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len, |
154 | | const std::string& password, |
155 | | const uint8_t salt[], size_t salt_len, |
156 | | size_t iterations, |
157 | | std::chrono::milliseconds msec) const |
158 | 0 | { |
159 | 0 | if(iterations == 0) |
160 | 0 | { |
161 | 0 | iterations = PBKDF2(*m_mac, key_len, msec).iterations(); |
162 | 0 | } |
163 | 0 |
|
164 | 0 | PBKDF2 pbkdf2(*m_mac, iterations); |
165 | 0 |
|
166 | 0 | pbkdf2.derive_key(key, key_len, |
167 | 0 | password.c_str(), password.size(), |
168 | 0 | salt, salt_len); |
169 | 0 |
|
170 | 0 | return iterations; |
171 | 0 | } |
172 | | |
173 | | std::string PKCS5_PBKDF2::name() const |
174 | 0 | { |
175 | 0 | return "PBKDF2(" + m_mac->name() + ")"; |
176 | 0 | } |
177 | | |
178 | | PBKDF* PKCS5_PBKDF2::clone() const |
179 | 0 | { |
180 | 0 | return new PKCS5_PBKDF2(m_mac->clone()); |
181 | 0 | } |
182 | | |
183 | | // PasswordHash interface |
184 | | |
185 | | PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) : |
186 | | m_prf(prf.clone()), |
187 | | m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count()))) |
188 | 0 | {} |
189 | | |
190 | | std::string PBKDF2::to_string() const |
191 | 0 | { |
192 | 0 | return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")"; |
193 | 0 | } |
194 | | |
195 | | void PBKDF2::derive_key(uint8_t out[], size_t out_len, |
196 | | const char* password, const size_t password_len, |
197 | | const uint8_t salt[], size_t salt_len) const |
198 | 0 | { |
199 | 0 | pbkdf2_set_key(*m_prf, password, password_len); |
200 | 0 | pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations); |
201 | 0 | } |
202 | | |
203 | | std::string PBKDF2_Family::name() const |
204 | 0 | { |
205 | 0 | return "PBKDF2(" + m_prf->name() + ")"; |
206 | 0 | } |
207 | | |
208 | | std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const |
209 | 0 | { |
210 | 0 | return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, output_len, msec)); |
211 | 0 | } |
212 | | |
213 | | std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const |
214 | 0 | { |
215 | 0 | return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, 150000)); |
216 | 0 | } |
217 | | |
218 | | std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t, size_t) const |
219 | 0 | { |
220 | 0 | return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter)); |
221 | 0 | } |
222 | | |
223 | | std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const |
224 | 0 | { |
225 | 0 | return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter)); |
226 | 0 | } |
227 | | |
228 | | } |