• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 23096310106

14 Mar 2026 09:03PM UTC coverage: 89.729% (+0.002%) from 89.727%
23096310106

Pull #5438

github

web-flow
Merge 6864c8c43 into 6d28178fb
Pull Request #5438: Handle Not_Implemented in pwdhash tune_params test

104215 of 116144 relevant lines covered (89.73%)

11447901.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.86
/src/tests/test_pbkdf.cpp
1
/*
2
* (C) 2014,2015,2019 Jack Lloyd
3
* (C) 2018 Ribose Inc
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_PBKDF)
11
   #include <botan/exceptn.h>
12
   #include <botan/pbkdf.h>
13
   #include <botan/pwdhash.h>
14
#endif
15

16
#if defined(BOTAN_HAS_RFC4880)
17
   #include <botan/rfc4880.h>
18
#endif
19

20
namespace Botan_Tests {
21

22
namespace {
23

24
#if defined(BOTAN_HAS_PBKDF)
25
class PBKDF_KAT_Tests final : public Text_Based_Test {
×
26
   public:
27
      PBKDF_KAT_Tests() : Text_Based_Test("pbkdf", "Iterations,Salt,Passphrase,Output") {}
2✔
28

29
      Test::Result run_one_test(const std::string& pbkdf_name, const VarMap& vars) override {
28✔
30
         const size_t iterations = vars.get_req_sz("Iterations");
28✔
31
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
28✔
32
         const std::string passphrase = vars.get_req_str("Passphrase");
28✔
33
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
28✔
34
         const size_t outlen = expected.size();
28✔
35

36
         Test::Result result(pbkdf_name);
28✔
37
         auto pbkdf = Botan::PBKDF::create(pbkdf_name);
28✔
38

39
         if(!pbkdf) {
28✔
40
            result.note_missing(pbkdf_name);
×
41
            return result;
42
         }
43

44
         result.test_str_eq("Expected name", pbkdf->name(), pbkdf_name);
28✔
45

46
         const Botan::secure_vector<uint8_t> derived =
28✔
47
            pbkdf->derive_key(outlen, passphrase, salt.data(), salt.size(), iterations).bits_of();
28✔
48
         result.test_bin_eq("derived key", derived, expected);
28✔
49

50
         auto pwdhash_fam = Botan::PasswordHashFamily::create(pbkdf_name);
28✔
51

52
         if(!pwdhash_fam) {
28✔
53
            result.note_missing("No PasswordHashFamily for " + pbkdf_name);
×
54
            return result;
×
55
         }
56

57
         auto pwdhash = pwdhash_fam->from_params(iterations);
28✔
58

59
         std::vector<uint8_t> pwdhash_derived(outlen);
28✔
60
         pwdhash->hash(pwdhash_derived, passphrase, salt);
28✔
61

62
         result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected);
28✔
63

64
         return result;
28✔
65
      }
196✔
66
};
67

68
BOTAN_REGISTER_SMOKE_TEST("pbkdf", "pbkdf_kat", PBKDF_KAT_Tests);
69

70
class Pwdhash_Tests : public Test {
1✔
71
   public:
72
      std::vector<Test::Result> run() override {
1✔
73
         std::vector<Test::Result> results;
1✔
74

75
         const std::vector<std::string> all_pwdhash = {
1✔
76
            "Scrypt", "PBKDF2(SHA-256)", "OpenPGP-S2K(SHA-384)", "Argon2d", "Argon2i", "Argon2id", "Bcrypt-PBKDF"};
1✔
77

78
         const uint64_t run_time = 3;
1✔
79
         const uint64_t tune_time = 1;
1✔
80
         const size_t max_mem = 32;
1✔
81

82
         for(const std::string& pwdhash : all_pwdhash) {
8✔
83
            Test::Result result("Pwdhash " + pwdhash);
7✔
84
            auto pwdhash_fam = Botan::PasswordHashFamily::create(pwdhash);
7✔
85

86
            if(pwdhash_fam) {
7✔
87
               result.start_timer();
7✔
88

89
               std::unique_ptr<Botan::PasswordHash> tuned_pwhash;
7✔
90
               try {
7✔
91
                  tuned_pwhash = pwdhash_fam->tune_params(32, run_time, max_mem, tune_time);
7✔
92
               } catch(const Botan::Not_Implemented&) {
×
93
                  // tune_params requires os_utils for clock access; not available in minimized builds
94
                  result.test_note("tune_params not available in current build config");
×
95
               }
×
96

97
               if(tuned_pwhash != nullptr) {
7✔
98
                  std::vector<uint8_t> output1(32);
7✔
99
                  const std::vector<uint8_t> salt(8);
7✔
100
                  const std::string password = "test";
7✔
101
                  tuned_pwhash->hash(output1, password, salt);
7✔
102

103
                  std::unique_ptr<Botan::PasswordHash> pwhash;
7✔
104

105
                  if(pwdhash_fam->name() == "Scrypt" || pwdhash_fam->name().starts_with("Argon2")) {
7✔
106
                     pwhash = pwdhash_fam->from_params(
4✔
107
                        tuned_pwhash->memory_param(), tuned_pwhash->iterations(), tuned_pwhash->parallelism());
8✔
108
                  } else {
109
                     pwhash = pwdhash_fam->from_params(tuned_pwhash->iterations());
3✔
110
                  }
111

112
                  std::vector<uint8_t> output2(32);
7✔
113
                  pwhash->hash(output2, password, salt);
7✔
114

115
                  result.test_bin_eq("PasswordHash produced same output when run with same params", output1, output2);
7✔
116

117
                  auto default_pwhash = pwdhash_fam->default_params();
7✔
118
                  std::vector<uint8_t> output3(32);
7✔
119
                  default_pwhash->hash(output3, password, salt);
14✔
120
               }
35✔
121
               result.end_timer();
7✔
122
            } else {
×
123
               result.test_note("No such algo", pwdhash);
×
124
            }
125

126
            results.push_back(result);
7✔
127
         }
7✔
128

129
         return results;
1✔
130
      }
1✔
131
};
132

133
BOTAN_REGISTER_TEST("pbkdf", "pwdhash", Pwdhash_Tests);
134

135
#endif
136

137
#if defined(BOTAN_HAS_PBKDF_BCRYPT)
138

139
class Bcrypt_PBKDF_KAT_Tests final : public Text_Based_Test {
×
140
   public:
141
      Bcrypt_PBKDF_KAT_Tests() : Text_Based_Test("bcrypt_pbkdf.vec", "Passphrase,Salt,Iterations,Output") {}
2✔
142

143
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
36✔
144
         const size_t rounds = vars.get_req_sz("Iterations");
36✔
145
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
36✔
146
         const std::string passphrase = vars.get_req_str("Passphrase");
36✔
147
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
36✔
148

149
         Test::Result result("bcrypt PBKDF");
36✔
150

151
         auto pwdhash_fam = Botan::PasswordHashFamily::create("Bcrypt-PBKDF");
36✔
152

153
         if(!pwdhash_fam) {
36✔
154
            result.test_failure("Bcrypt-PBKDF is missing PasswordHashFamily");
×
155
            return result;
156
         }
157

158
         auto pwdhash = pwdhash_fam->from_iterations(rounds);
36✔
159

160
         std::vector<uint8_t> derived(expected.size());
36✔
161
         pwdhash->hash(derived, passphrase, salt);
36✔
162

163
         result.test_bin_eq("derived key", derived, expected);
36✔
164

165
         return result;
36✔
166
      }
180✔
167
};
168

169
BOTAN_REGISTER_TEST("pbkdf", "bcrypt_pbkdf", Bcrypt_PBKDF_KAT_Tests);
170

171
#endif
172

173
#if defined(BOTAN_HAS_SCRYPT)
174

175
class Scrypt_KAT_Tests final : public Text_Based_Test {
×
176
   public:
177
      Scrypt_KAT_Tests() : Text_Based_Test("scrypt.vec", "Passphrase,Salt,N,R,P,Output") {}
2✔
178

179
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
15✔
180
         const size_t N = vars.get_req_sz("N");
15✔
181
         const size_t R = vars.get_req_sz("R");
15✔
182
         const size_t P = vars.get_req_sz("P");
15✔
183
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
15✔
184
         const std::string passphrase = vars.get_req_str("Passphrase");
15✔
185
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
15✔
186

187
         Test::Result result("scrypt");
15✔
188

189
         if(N >= 1048576 && Test::run_long_tests() == false) {
15✔
190
            return result;
191
         }
192

193
         auto pwdhash_fam = Botan::PasswordHashFamily::create("Scrypt");
15✔
194

195
         if(!pwdhash_fam) {
15✔
196
            result.test_failure("Scrypt is missing PasswordHashFamily");
×
197
            return result;
198
         }
199

200
         auto pwdhash = pwdhash_fam->from_params(N, R, P);
15✔
201

202
         std::vector<uint8_t> pwdhash_derived(expected.size());
15✔
203
         pwdhash->hash(pwdhash_derived, passphrase, salt);
15✔
204

205
         result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected);
15✔
206

207
         return result;
15✔
208
      }
75✔
209
};
210

211
BOTAN_REGISTER_TEST("pbkdf", "scrypt", Scrypt_KAT_Tests);
212

213
#endif
214

215
#if defined(BOTAN_HAS_ARGON2)
216

217
class Argon2_KAT_Tests final : public Text_Based_Test {
×
218
   public:
219
      Argon2_KAT_Tests() : Text_Based_Test("argon2.vec", "Passphrase,Salt,P,M,T,Output", "Secret,AD") {}
2✔
220

221
      Test::Result run_one_test(const std::string& mode, const VarMap& vars) override {
1,071✔
222
         const size_t P = vars.get_req_sz("P");
1,071✔
223
         const size_t M = vars.get_req_sz("M");
1,071✔
224
         const size_t T = vars.get_req_sz("T");
1,071✔
225
         const std::vector<uint8_t> key = vars.get_opt_bin("Secret");
1,071✔
226
         const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
1,071✔
227
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
1,071✔
228
         const std::vector<uint8_t> passphrase = vars.get_req_bin("Passphrase");
1,071✔
229
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
1,071✔
230

231
         Test::Result result(mode);
1,071✔
232

233
         auto pwdhash_fam = Botan::PasswordHashFamily::create(mode);
1,071✔
234

235
         if(!pwdhash_fam) {
1,071✔
236
            result.test_failure("Argon2 is missing PasswordHashFamily");
×
237
            return result;
238
         }
239

240
         auto pwdhash = pwdhash_fam->from_params(M, T, P);
1,071✔
241

242
         const std::string passphrase_str(passphrase.begin(), passphrase.end());
2,142✔
243

244
         std::vector<uint8_t> pwdhash_derived(expected.size());
1,071✔
245
         pwdhash->hash(pwdhash_derived, passphrase_str, salt, ad, key);
1,071✔
246

247
         result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected);
1,071✔
248

249
         return result;
1,071✔
250
      }
6,654✔
251
};
252

253
BOTAN_REGISTER_SERIALIZED_TEST("pbkdf", "argon2", Argon2_KAT_Tests);
254

255
#endif
256

257
#if defined(BOTAN_HAS_PGP_S2K)
258

259
class PGP_S2K_Iter_Test final : public Test {
1✔
260
   public:
261
      std::vector<Test::Result> run() override {
1✔
262
         Test::Result result("PGP_S2K iteration encoding");
1✔
263

264
         // The maximum representable iteration count
265
         const size_t max_iter = 65011712;
1✔
266

267
         result.test_sz_eq("Encoding of large value accepted", Botan::RFC4880_encode_count(max_iter * 2), size_t(255));
1✔
268
         result.test_sz_eq("Encoding of small value accepted", Botan::RFC4880_encode_count(0), size_t(0));
1✔
269

270
         for(size_t c = 0; c != 256; ++c) {
257✔
271
            const size_t dec = Botan::RFC4880_decode_count(static_cast<uint8_t>(c));
256✔
272
            const size_t comp_dec = (16 + (c & 0x0F)) << ((c >> 4) + 6);
256✔
273
            result.test_sz_eq("Decoded value matches PGP formula", dec, comp_dec);
256✔
274

275
            const size_t enc = Botan::RFC4880_encode_count(comp_dec);
256✔
276
            result.test_sz_eq("Encoded value matches PGP formula", enc, c);
256✔
277
         }
278

279
         uint8_t last_enc = 0;
280

281
         for(size_t i = 0; i <= max_iter; i += 64) {
1,015,810✔
282
            const uint8_t enc = Botan::RFC4880_encode_count(i);
1,015,809✔
283
            result.test_sz_lte("Encoded value non-decreasing", last_enc, enc);
1,015,809✔
284

285
            /*
286
            The iteration count as encoded may not be exactly the
287
            value requested, but should never be less
288
            */
289
            const size_t dec = Botan::RFC4880_decode_count(enc);
1,015,809✔
290
            result.test_sz_gte("Decoded value is >= requested", dec, i);
1,015,809✔
291

292
            last_enc = enc;
1,015,809✔
293
         }
294

295
         return std::vector<Test::Result>{result};
3✔
296
      }
2✔
297
};
298

299
BOTAN_REGISTER_TEST("pbkdf", "pgp_s2k_iter", PGP_S2K_Iter_Test);
300

301
#endif
302

303
}  // namespace
304

305
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc