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