Coverage Report

Created: 2025-06-10 06:14

/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