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