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

randombit / botan / 23530940347

25 Mar 2026 08:03AM UTC coverage: 89.481% (-0.002%) from 89.483%
23530940347

Pull #5478

github

web-flow
Merge 2bcf08072 into 60de912fb
Pull Request #5478: Add PKCS#12 KDF

105529 of 117934 relevant lines covered (89.48%)

11749153.42 hits per line

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

90.64
/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
#endif
14

15
#if defined(BOTAN_HAS_PKCS12_KDF)
16
   #include <botan/pkcs12_kdf.h>
17
#endif
18

19
#if defined(BOTAN_HAS_PBKDF) || defined(BOTAN_HAS_PKCS12_KDF)
20
   #include <botan/pwdhash.h>
21
#endif
22

23
#if defined(BOTAN_HAS_RFC4880)
24
   #include <botan/rfc4880.h>
25
#endif
26

27
namespace Botan_Tests {
28

29
namespace {
30

31
#if defined(BOTAN_HAS_PBKDF)
32
class PBKDF_KAT_Tests final : public Text_Based_Test {
×
33
   public:
34
      PBKDF_KAT_Tests() : Text_Based_Test("pbkdf", "Iterations,Salt,Passphrase,Output") {}
2✔
35

36
      Test::Result run_one_test(const std::string& pbkdf_name, const VarMap& vars) override {
38✔
37
         const size_t iterations = vars.get_req_sz("Iterations");
38✔
38
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
38✔
39
         const std::string passphrase = vars.get_req_str("Passphrase");
38✔
40
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
38✔
41
         const size_t outlen = expected.size();
38✔
42

43
         Test::Result result(pbkdf_name);
38✔
44
         auto pbkdf = Botan::PBKDF::create(pbkdf_name);
38✔
45

46
         if(!pbkdf) {
38✔
47
            result.note_missing(pbkdf_name);
10✔
48
            return result;
49
         }
50

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

53
         const Botan::secure_vector<uint8_t> derived =
28✔
54
            pbkdf->derive_key(outlen, passphrase, salt.data(), salt.size(), iterations).bits_of();
28✔
55
         result.test_bin_eq("derived key", derived, expected);
28✔
56

57
         auto pwdhash_fam = Botan::PasswordHashFamily::create(pbkdf_name);
28✔
58

59
         if(!pwdhash_fam) {
28✔
60
            result.note_missing("No PasswordHashFamily for " + pbkdf_name);
×
61
            return result;
×
62
         }
63

64
         auto pwdhash = pwdhash_fam->from_params(iterations);
28✔
65

66
         std::vector<uint8_t> pwdhash_derived(outlen);
28✔
67
         pwdhash->hash(pwdhash_derived, passphrase, salt);
28✔
68

69
         result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected);
28✔
70

71
         return result;
28✔
72
      }
226✔
73
};
74

75
BOTAN_REGISTER_SMOKE_TEST("pbkdf", "pbkdf_kat", PBKDF_KAT_Tests);
76

77
class Pwdhash_Tests : public Test {
1✔
78
   public:
79
      std::vector<Test::Result> run() override {
1✔
80
         std::vector<Test::Result> results;
1✔
81

82
         const std::vector<std::string> all_pwdhash = {"Scrypt",
1✔
83
                                                       "PBKDF2(SHA-256)",
84
                                                       "OpenPGP-S2K(SHA-384)",
85
                                                       "Argon2d",
86
                                                       "Argon2i",
87
                                                       "Argon2id",
88
                                                       "Bcrypt-PBKDF",
89
                                                       "PKCS12-KDF(SHA-1,1)",
90
                                                       "PKCS12-KDF(SHA-256,2)"};
1✔
91

92
         const uint64_t run_time = 3;
1✔
93
         const uint64_t tune_time = 1;
1✔
94
         const size_t max_mem = 32;
1✔
95

96
         for(const std::string& pwdhash : all_pwdhash) {
10✔
97
            Test::Result result("Pwdhash " + pwdhash);
9✔
98
            auto pwdhash_fam = Botan::PasswordHashFamily::create(pwdhash);
9✔
99

100
            if(pwdhash_fam) {
9✔
101
               result.start_timer();
9✔
102

103
               std::unique_ptr<Botan::PasswordHash> tuned_pwhash;
9✔
104
               try {
9✔
105
                  tuned_pwhash = pwdhash_fam->tune_params(32, run_time, max_mem, tune_time);
9✔
106
               } catch(const Botan::Not_Implemented&) {
×
107
                  // tune_params requires os_utils for clock access; not available in minimized builds
108
                  result.test_note("tune_params not available in current build config");
×
109
               }
×
110

111
               if(tuned_pwhash != nullptr) {
9✔
112
                  std::vector<uint8_t> output1(32);
9✔
113
                  const std::vector<uint8_t> salt(8);
9✔
114
                  const std::string password = "test";
9✔
115
                  tuned_pwhash->hash(output1, password, salt);
9✔
116

117
                  std::unique_ptr<Botan::PasswordHash> pwhash;
9✔
118

119
                  if(pwdhash_fam->name() == "Scrypt" || pwdhash_fam->name().starts_with("Argon2")) {
9✔
120
                     pwhash = pwdhash_fam->from_params(
4✔
121
                        tuned_pwhash->memory_param(), tuned_pwhash->iterations(), tuned_pwhash->parallelism());
8✔
122
                  } else {
123
                     pwhash = pwdhash_fam->from_params(tuned_pwhash->iterations());
5✔
124
                  }
125

126
                  std::vector<uint8_t> output2(32);
9✔
127
                  pwhash->hash(output2, password, salt);
9✔
128

129
                  result.test_bin_eq("PasswordHash produced same output when run with same params", output1, output2);
9✔
130

131
                  auto default_pwhash = pwdhash_fam->default_params();
9✔
132
                  std::vector<uint8_t> output3(32);
9✔
133
                  default_pwhash->hash(output3, password, salt);
18✔
134
               }
45✔
135
               result.end_timer();
9✔
136
            } else {
×
137
               result.test_note("No such algo", pwdhash);
×
138
            }
139

140
            results.push_back(result);
9✔
141
         }
9✔
142

143
         return results;
1✔
144
      }
1✔
145
};
146

147
BOTAN_REGISTER_TEST("pbkdf", "pwdhash", Pwdhash_Tests);
148

149
#endif
150

151
#if defined(BOTAN_HAS_PKCS12_KDF)
152

153
class PKCS12_KDF_KAT_Tests final : public Text_Based_Test {
×
154
   public:
155
      PKCS12_KDF_KAT_Tests() : Text_Based_Test("pbkdf/pkcs12_kdf.vec", "Passphrase,Salt,Iterations,Output") {}
2✔
156

157
      Test::Result run_one_test(const std::string& algo_spec, const VarMap& vars) override {
10✔
158
         const std::string passphrase = vars.get_req_str("Passphrase");
10✔
159
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
10✔
160
         const size_t iterations = vars.get_req_sz("Iterations");
10✔
161
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
10✔
162

163
         Test::Result result(algo_spec);
10✔
164

165
         auto pwdhash_fam = Botan::PasswordHashFamily::create(algo_spec);
10✔
166
         if(!pwdhash_fam) {
10✔
167
            result.note_missing(algo_spec);
×
168
            return result;
169
         }
170

171
         result.test_str_eq("family name", pwdhash_fam->name(), algo_spec);
10✔
172

173
         auto pwdhash = pwdhash_fam->from_iterations(iterations);
10✔
174
         result.test_sz_eq("iterations", pwdhash->iterations(), iterations);
10✔
175

176
         std::vector<uint8_t> derived(expected.size());
10✔
177
         pwdhash->hash(derived, passphrase, salt);
10✔
178
         result.test_bin_eq("derived key", derived, expected);
10✔
179

180
         // Verify from_params(iterations) produces the same result
181
         auto pwdhash2 = pwdhash_fam->from_params(iterations);
10✔
182
         std::vector<uint8_t> derived2(expected.size());
10✔
183
         pwdhash2->hash(derived2, passphrase, salt);
10✔
184
         result.test_bin_eq("from_params matches from_iterations", derived2, expected);
10✔
185

186
         return result;
10✔
187
      }
70✔
188
};
189

190
BOTAN_REGISTER_TEST("pbkdf", "pkcs12_kdf_kat", PKCS12_KDF_KAT_Tests);
191

192
#endif
193

194
#if defined(BOTAN_HAS_PBKDF_BCRYPT)
195

196
class Bcrypt_PBKDF_KAT_Tests final : public Text_Based_Test {
×
197
   public:
198
      Bcrypt_PBKDF_KAT_Tests() : Text_Based_Test("bcrypt_pbkdf.vec", "Passphrase,Salt,Iterations,Output") {}
2✔
199

200
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
36✔
201
         const size_t rounds = vars.get_req_sz("Iterations");
36✔
202
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
36✔
203
         const std::string passphrase = vars.get_req_str("Passphrase");
36✔
204
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
36✔
205

206
         Test::Result result("bcrypt PBKDF");
36✔
207

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

210
         if(!pwdhash_fam) {
36✔
211
            result.test_failure("Bcrypt-PBKDF is missing PasswordHashFamily");
×
212
            return result;
213
         }
214

215
         auto pwdhash = pwdhash_fam->from_iterations(rounds);
36✔
216

217
         std::vector<uint8_t> derived(expected.size());
36✔
218
         pwdhash->hash(derived, passphrase, salt);
36✔
219

220
         result.test_bin_eq("derived key", derived, expected);
36✔
221

222
         return result;
36✔
223
      }
180✔
224
};
225

226
BOTAN_REGISTER_TEST("pbkdf", "bcrypt_pbkdf", Bcrypt_PBKDF_KAT_Tests);
227

228
#endif
229

230
#if defined(BOTAN_HAS_SCRYPT)
231

232
class Scrypt_KAT_Tests final : public Text_Based_Test {
×
233
   public:
234
      Scrypt_KAT_Tests() : Text_Based_Test("scrypt.vec", "Passphrase,Salt,N,R,P,Output") {}
2✔
235

236
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
15✔
237
         const size_t N = vars.get_req_sz("N");
15✔
238
         const size_t R = vars.get_req_sz("R");
15✔
239
         const size_t P = vars.get_req_sz("P");
15✔
240
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
15✔
241
         const std::string passphrase = vars.get_req_str("Passphrase");
15✔
242
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
15✔
243

244
         Test::Result result("scrypt");
15✔
245

246
         if(N >= 1048576 && Test::run_long_tests() == false) {
15✔
247
            return result;
248
         }
249

250
         auto pwdhash_fam = Botan::PasswordHashFamily::create("Scrypt");
15✔
251

252
         if(!pwdhash_fam) {
15✔
253
            result.test_failure("Scrypt is missing PasswordHashFamily");
×
254
            return result;
255
         }
256

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

259
         std::vector<uint8_t> pwdhash_derived(expected.size());
15✔
260
         pwdhash->hash(pwdhash_derived, passphrase, salt);
15✔
261

262
         result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected);
15✔
263

264
         return result;
15✔
265
      }
75✔
266
};
267

268
BOTAN_REGISTER_TEST("pbkdf", "scrypt", Scrypt_KAT_Tests);
269

270
#endif
271

272
#if defined(BOTAN_HAS_ARGON2)
273

274
class Argon2_KAT_Tests final : public Text_Based_Test {
×
275
   public:
276
      Argon2_KAT_Tests() : Text_Based_Test("argon2.vec", "Passphrase,Salt,P,M,T,Output", "Secret,AD") {}
2✔
277

278
      Test::Result run_one_test(const std::string& mode, const VarMap& vars) override {
1,071✔
279
         const size_t P = vars.get_req_sz("P");
1,071✔
280
         const size_t M = vars.get_req_sz("M");
1,071✔
281
         const size_t T = vars.get_req_sz("T");
1,071✔
282
         const std::vector<uint8_t> key = vars.get_opt_bin("Secret");
1,071✔
283
         const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
1,071✔
284
         const std::vector<uint8_t> salt = vars.get_req_bin("Salt");
1,071✔
285
         const std::vector<uint8_t> passphrase = vars.get_req_bin("Passphrase");
1,071✔
286
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
1,071✔
287

288
         Test::Result result(mode);
1,071✔
289

290
         auto pwdhash_fam = Botan::PasswordHashFamily::create(mode);
1,071✔
291

292
         if(!pwdhash_fam) {
1,071✔
293
            result.test_failure("Argon2 is missing PasswordHashFamily");
×
294
            return result;
295
         }
296

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

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

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

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

306
         return result;
1,071✔
307
      }
6,654✔
308
};
309

310
BOTAN_REGISTER_SERIALIZED_TEST("pbkdf", "argon2", Argon2_KAT_Tests);
311

312
#endif
313

314
#if defined(BOTAN_HAS_PGP_S2K)
315

316
class PGP_S2K_Iter_Test final : public Test {
1✔
317
   public:
318
      std::vector<Test::Result> run() override {
1✔
319
         Test::Result result("PGP_S2K iteration encoding");
1✔
320

321
         // The maximum representable iteration count
322
         const size_t max_iter = 65011712;
1✔
323

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

327
         for(size_t c = 0; c != 256; ++c) {
257✔
328
            const size_t dec = Botan::RFC4880_decode_count(static_cast<uint8_t>(c));
256✔
329
            const size_t comp_dec = (16 + (c & 0x0F)) << ((c >> 4) + 6);
256✔
330
            result.test_sz_eq("Decoded value matches PGP formula", dec, comp_dec);
256✔
331

332
            const size_t enc = Botan::RFC4880_encode_count(comp_dec);
256✔
333
            result.test_sz_eq("Encoded value matches PGP formula", enc, c);
256✔
334
         }
335

336
         uint8_t last_enc = 0;
337

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

342
            /*
343
            The iteration count as encoded may not be exactly the
344
            value requested, but should never be less
345
            */
346
            const size_t dec = Botan::RFC4880_decode_count(enc);
1,015,809✔
347
            result.test_sz_gte("Decoded value is >= requested", dec, i);
1,015,809✔
348

349
            last_enc = enc;
1,015,809✔
350
         }
351

352
         return std::vector<Test::Result>{result};
3✔
353
      }
2✔
354
};
355

356
BOTAN_REGISTER_TEST("pbkdf", "pgp_s2k_iter", PGP_S2K_Iter_Test);
357

358
#endif
359

360
}  // namespace
361

362
}  // 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