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