Coverage Report

Created: 2025-07-17 06:53

/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