diff options
author | Jack Lloyd <[email protected]> | 2019-05-31 23:04:32 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2019-05-31 23:04:32 -0400 |
commit | 32e54436797d8cba658f5663cf1a0dae589f8670 (patch) | |
tree | 329d69bfd71d53b126c02864d779887b42ce13dd | |
parent | dbf4b8d7418587d4d30c048e1ff13f418944ffc4 (diff) | |
parent | 184a7825a8cc449476951de0d2019d8014667c8f (diff) |
Merge GH #1990 Add Bcrypt-PBKDF
-rw-r--r-- | src/lib/block/blowfish/blowfish.cpp | 14 | ||||
-rw-r--r-- | src/lib/block/blowfish/blowfish.h | 2 | ||||
-rw-r--r-- | src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp | 182 | ||||
-rw-r--r-- | src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h | 75 | ||||
-rw-r--r-- | src/lib/pbkdf/bcrypt_pbkdf/info.txt | 8 | ||||
-rw-r--r-- | src/lib/pbkdf/pwdhash.cpp | 11 | ||||
-rw-r--r-- | src/tests/data/bcrypt_pbkdf.vec | 184 | ||||
-rw-r--r-- | src/tests/test_pbkdf.cpp | 50 |
8 files changed, 520 insertions, 6 deletions
diff --git a/src/lib/block/blowfish/blowfish.cpp b/src/lib/block/blowfish/blowfish.cpp index d219bc22a..ecb9f82e3 100644 --- a/src/lib/block/blowfish/blowfish.cpp +++ b/src/lib/block/blowfish/blowfish.cpp @@ -374,7 +374,7 @@ void Blowfish::key_expansion(const uint8_t key[], */ void Blowfish::salted_set_key(const uint8_t key[], size_t length, const uint8_t salt[], size_t salt_length, - size_t workfactor) + size_t workfactor, bool salt_first) { BOTAN_ARG_CHECK(salt_length > 0 && salt_length % 4 == 0, "Invalid salt length for Blowfish salted key schedule"); @@ -398,8 +398,16 @@ void Blowfish::salted_set_key(const uint8_t key[], size_t length, for(size_t r = 0; r != rounds; ++r) { - key_expansion(key, length, nullptr, 0); - key_expansion(salt, salt_length, nullptr, 0); + if(salt_first) + { + key_expansion(salt, salt_length, nullptr, 0); + key_expansion(key, length, nullptr, 0); + } + else + { + key_expansion(key, length, nullptr, 0); + key_expansion(salt, salt_length, nullptr, 0); + } } } } diff --git a/src/lib/block/blowfish/blowfish.h b/src/lib/block/blowfish/blowfish.h index d5c318752..97a1b841c 100644 --- a/src/lib/block/blowfish/blowfish.h +++ b/src/lib/block/blowfish/blowfish.h @@ -26,7 +26,7 @@ class BOTAN_PUBLIC_API(2,0) Blowfish final : public Block_Cipher_Fixed_Params<8, */ void salted_set_key(const uint8_t key[], size_t key_length, const uint8_t salt[], size_t salt_length, - size_t workfactor); + const size_t workfactor, bool salt_first = false); BOTAN_DEPRECATED("Use Blowfish::salted_set_key taking salt length") void eks_key_schedule(const uint8_t key[], size_t key_length, diff --git a/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp new file mode 100644 index 000000000..ecdac689a --- /dev/null +++ b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp @@ -0,0 +1,182 @@ +/* +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/bcrypt_pbkdf.h> +#include <botan/blowfish.h> +#include <botan/hash.h> +#include <botan/internal/timer.h> + +namespace Botan { + +void Bcrypt_PBKDF::derive_key(uint8_t output[], size_t output_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + bcrypt_pbkdf(output, output_len, + password, password_len, + salt, salt_len, + m_iterations); + } + +std::string Bcrypt_PBKDF::to_string() const + { + return "Bcrypt-PBKDF(" + std::to_string(m_iterations) + ")"; + } + +std::string Bcrypt_PBKDF_Family::name() const + { + return "Bcrypt-PBKDF"; + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length, + std::chrono::milliseconds msec, + size_t /*max_memory*/) const + { + Timer timer("Bcrypt_PBKDF"); + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + const size_t blocks = (output_length + 32 - 1) / 32; + + if(blocks == 0) + return default_params(); + + const size_t starting_iter = 2; + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t output[32] = { 0 }; + bcrypt_pbkdf(output, sizeof(output), "test", 4, nullptr, 0, starting_iter); + }); + + if(timer.events() < blocks || timer.value() == 0) + return default_params(); + + const uint64_t measured_time = timer.value() / (timer.events() / blocks); + + const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000); + + const size_t desired_increase = target_nsec / measured_time; + + if(desired_increase == 0) + return this->from_iterations(starting_iter); + + return this->from_iterations(desired_increase * starting_iter); + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::default_params() const + { + return this->from_iterations(32); // About 100 ms on fast machine + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_iterations(size_t iter) const + { + return std::unique_ptr<PasswordHash>(new Bcrypt_PBKDF(iter)); + } + +std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_params(size_t iter, size_t /*t*/, size_t /*p*/) const + { + return this->from_iterations(iter); + } + +namespace { + +void bcrypt_round(Blowfish& blowfish, + const secure_vector<uint8_t>& pass_hash, + const secure_vector<uint8_t>& salt_hash, + secure_vector<uint8_t>& out, + secure_vector<uint8_t>& tmp) + { + const size_t BCRYPT_PBKDF_OUTPUT = 32; + + // "OxychromaticBlowfishSwatDynamite" + static const uint8_t BCRYPT_PBKDF_MAGIC[BCRYPT_PBKDF_OUTPUT] = { + 0x4F, 0x78, 0x79, 0x63, 0x68, 0x72, 0x6F, 0x6D, + 0x61, 0x74, 0x69, 0x63, 0x42, 0x6C, 0x6F, 0x77, + 0x66, 0x69, 0x73, 0x68, 0x53, 0x77, 0x61, 0x74, + 0x44, 0x79, 0x6E, 0x61, 0x6D, 0x69, 0x74, 0x65 + }; + + const size_t BCRYPT_PBKDF_WORKFACTOR = 6; + const size_t BCRYPT_PBKDF_ROUNDS = 64; + + blowfish.salted_set_key(pass_hash.data(), pass_hash.size(), + salt_hash.data(), salt_hash.size(), + BCRYPT_PBKDF_WORKFACTOR, true); + + copy_mem(tmp.data(), BCRYPT_PBKDF_MAGIC, BCRYPT_PBKDF_OUTPUT); + for(size_t i = 0; i != BCRYPT_PBKDF_ROUNDS; ++i) + blowfish.encrypt(tmp); + + /* + Bcrypt PBKDF loads the Blowfish output as big endian for no reason + in particular. We can't just swap everything once at the end + because the (big-endian) values are fed into SHA-512 to generate + the salt for the next round + */ + for(size_t i = 0; i != 32/4; ++i) + { + const uint32_t w = load_le<uint32_t>(tmp.data(), i); + store_be(w, &tmp[sizeof(uint32_t)*i]); + } + + xor_buf(out.data(), tmp.data(), BCRYPT_PBKDF_OUTPUT); + } + +} + +void bcrypt_pbkdf(uint8_t output[], size_t output_len, + const char* pass, size_t pass_len, + const uint8_t salt[], size_t salt_len, + size_t rounds) + { + BOTAN_ARG_CHECK(rounds >= 1, "Invalid rounds for Bcrypt PBKDF"); + + // No output desired, so we are all done already... + if(output_len == 0) + return; + + BOTAN_ARG_CHECK(output_len <= 10*1024*1024, "Too much output for Bcrypt PBKDF"); + + const size_t BCRYPT_BLOCK_SIZE = 32; + const size_t blocks = (output_len + BCRYPT_BLOCK_SIZE - 1) / BCRYPT_BLOCK_SIZE; + + std::unique_ptr<HashFunction> sha512 = HashFunction::create_or_throw("SHA-512"); + const secure_vector<uint8_t> pass_hash = sha512->process(reinterpret_cast<const uint8_t*>(pass), pass_len); + + secure_vector<uint8_t> salt_hash(sha512->output_length()); + + Blowfish blowfish; + secure_vector<uint8_t> out(BCRYPT_BLOCK_SIZE); + secure_vector<uint8_t> tmp(BCRYPT_BLOCK_SIZE); + + for(size_t block = 0; block != blocks; ++block) + { + clear_mem(out.data(), out.size()); + + sha512->update(salt, salt_len); + sha512->update_be(static_cast<uint32_t>(block + 1)); + sha512->final(salt_hash.data()); + + bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp); + + for(size_t r = 1; r != rounds; ++r) + { + // Next salt is H(prev_output) + sha512->update(tmp); + sha512->final(salt_hash.data()); + + bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp); + } + + for(size_t i = 0; i != BCRYPT_BLOCK_SIZE; ++i) + { + const size_t dest = i * blocks + block; + if(dest < output_len) + output[dest] = out[i]; + } + } + } + +} diff --git a/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h new file mode 100644 index 000000000..8984e6497 --- /dev/null +++ b/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h @@ -0,0 +1,75 @@ +/* +* (C) 2018,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBKDF_BCRYPT_H_ +#define BOTAN_PBKDF_BCRYPT_H_ + +#include <botan/pwdhash.h> + +namespace Botan { + +/** +* Bcrypt-PBKDF key derivation function +*/ +class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF final : public PasswordHash + { + public: + Bcrypt_PBKDF(size_t iterations) : m_iterations(iterations) {} + + Bcrypt_PBKDF(const Bcrypt_PBKDF& other) = default; + Bcrypt_PBKDF& operator=(const Bcrypt_PBKDF&) = default; + + /** + * Derive a new key under the current Bcrypt-PBKDF parameter set + */ + void derive_key(uint8_t out[], size_t out_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + + std::string to_string() const override; + + size_t iterations() const override { return m_iterations; } + + size_t parallelism() const override { return 0; } + + size_t memory_param() const override { return 0; } + + size_t total_memory_usage() const override { return 4096; } + + private: + size_t m_iterations; + }; + +class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF_Family final : public PasswordHashFamily + { + public: + Bcrypt_PBKDF_Family() {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t i, size_t, size_t) const override; + }; + +/** +* Bcrypt PBKDF compatible with OpenBSD bcrypt_pbkdf +*/ +void BOTAN_UNSTABLE_API bcrypt_pbkdf(uint8_t output[], size_t output_len, + const char* pass, size_t pass_len, + const uint8_t salt[], size_t salt_len, + size_t rounds); + +} + +#endif diff --git a/src/lib/pbkdf/bcrypt_pbkdf/info.txt b/src/lib/pbkdf/bcrypt_pbkdf/info.txt new file mode 100644 index 000000000..0e2b10178 --- /dev/null +++ b/src/lib/pbkdf/bcrypt_pbkdf/info.txt @@ -0,0 +1,8 @@ +<defines> +PBKDF_BCRYPT -> 20190531 +</defines> + +<requires> +blowfish +sha2_64 +</requires> diff --git a/src/lib/pbkdf/pwdhash.cpp b/src/lib/pbkdf/pwdhash.cpp index f5ee26c6a..718024a22 100644 --- a/src/lib/pbkdf/pwdhash.cpp +++ b/src/lib/pbkdf/pwdhash.cpp @@ -24,6 +24,10 @@ #include <botan/argon2.h> #endif +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + #include <botan/bcrypt_pbkdf.h> +#endif + namespace Botan { std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec, @@ -71,6 +75,13 @@ std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string } #endif +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + if(req.algo_name() == "Bcrypt-PBKDF") + { + return std::unique_ptr<PasswordHashFamily>(new Bcrypt_PBKDF_Family); + } +#endif + #if defined(BOTAN_HAS_PGP_S2K) if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) { diff --git a/src/tests/data/bcrypt_pbkdf.vec b/src/tests/data/bcrypt_pbkdf.vec new file mode 100644 index 000000000..e56745265 --- /dev/null +++ b/src/tests/data/bcrypt_pbkdf.vec @@ -0,0 +1,184 @@ + +# From https://github.com/dchest/bcrypt_pbkdf/blob/master/bcrypt_pbkdf_test.go +Passphrase = password +Salt = 73616C74 +Iterations = 12 +Output = 1AE42C05D487BC02F64921A4EBE4EA93BCACFE135FDA99974C06B7B01FAE149A + +# Generated by OpenBSD bcrypt_pbkdf +Passphrase = A +Salt = 0001020304050607 +Iterations = 10 +Output = 3C705DA7E10C61C2523B + +Passphrase = A +Salt = 0001020304050607 +Iterations = 10 +Output = 3CF670095D5FA753E1430C6D6177C299521B3B614D9E53FA89C7F893F33289F677DDD72EE1AA29CBF5574A948DE4623C71996FFD16C8806FDD7ACBE02992DEAC + +# Generated by https://github.com/dchest/bcrypt_pbkdf/ +Passphrase = password +Salt = 73616C74 +Iterations = 3 +Output = CCB508027535A962F591D4A7EEA08FE15EE0B68044B1A7B209605FDFBFB41228 + +Passphrase = password +Salt = 73616C74 +Iterations = 4 +Output = 5BBF0CC293587F1C3635555C27796598 + +Passphrase = password +Salt = 73616C74 +Iterations = 4 +Output = 5BBF0CC293587F1C3635555C27796598D47E579071BF427E9D8FBE842ABA34D9 + +Passphrase = password +Salt = 73616C74 +Iterations = 4 +Output = 5BA4BFC60C7AC272931458407F4C1C4936EA356C55125C5A279B791D65BF9842D49D7E1B572A9052715EBFA9421E7E94 + +Passphrase = password +Salt = 73616C74 +Iterations = 4 +Output = 5BA4BFC60C7AC272931458407F4C1C4936EA356C55125C5A279B791D65BF9842D49D7E1B572A9052715EBFA9421E7E949D8F8F19BE3284732AF1BA28341DD9BF + +Passphrase = password +Salt = 73616C74 +Iterations = 5 +Output = 4137FF05DC28382925690E6FD53B8EA1 + +Passphrase = password +Salt = 73616C74 +Iterations = 5 +Output = 4137FF05DC28382925690E6FD53B8EA112DA02D8F79B787E48ADBE77B1CE5B30 + +Passphrase = password +Salt = 73616C74 +Iterations = 5 +Output = 41CF37FCFF41055CDC5C280D389829D625DD69490E3B6F6BD5EB3B4C8E51A19E12A2DAFB02E8D82EF7F69B4A78717E51 + +Passphrase = password +Salt = 73616C74 +Iterations = 5 +Output = 41CF37FCFF41055CDC5C280D389829D625DD69490E3B6F6BD5EB3B4C8E51A19E12A2DAFB02E8D82EF7F69B4A78717E514812AD4CBE5877D3B1D0CEB35BA3308E + +Passphrase = password +Salt = 73616C74 +Iterations = 6 +Output = 1E6CF2445415961A1FC9FCEDA40DF381 + +Passphrase = password +Salt = 73616C74 +Iterations = 6 +Output = 1E6CF2445415961A1FC9FCEDA40DF381A86F5B327864A3CEA627EA8CFB081969 + +Passphrase = password +Salt = 73616C74 +Iterations = 6 +Output = 1E906CE9F245442B5418153196301AFE1F5DC9FAFC06ED55A4160DBBF3BC81FFA8A16FD95B15326C78B3644CA356CE28 + +Passphrase = password +Salt = 73616C74 +Iterations = 6 +Output = 1E906CE9F245442B5418153196301AFE1F5DC9FAFC06ED55A4160DBBF3BC81FFA8A16FD95B15326C78B3644CA356CE28A6162741EAE68C2AFB5E08A81921697C + +Passphrase = password +Salt = 73616C74 +Iterations = 7 +Output = 2BF6125E9A0532A025B1E777FD5F9540 + +Passphrase = password +Salt = 73616C74 +Iterations = 7 +Output = 2BF6125E9A0532A025B1E777FD5F9540C95408393ADEA4241A5AACFEC17A74FC + +Passphrase = password +Salt = 73616C74 +Iterations = 7 +Output = 2BC7F6A012985EF39A6B05343202A0972507B1DFE7F97703FD9D5FF7951F40E3C92854FE087739603ADDDE00A4722471 + +Passphrase = password +Salt = 73616C74 +Iterations = 7 +Output = 2BC7F6A012985EF39A6B05343202A0972507B1DFE7F97703FD9D5FF7951F40E3C92854FE087739603ADDDE00A47224711A8F5A40ACBFFE3DC1A87A8174FAFC9E + +Passphrase = password +Salt = 73616C74 +Iterations = 8 +Output = E17E1533ACC14423155493C99B9C3BBE + +Passphrase = password +Salt = 73616C74 +Iterations = 8 +Output = E17E1533ACC14423155493C99B9C3BBE62EA0884207A7802E7BA72EFF94D085E + +Passphrase = password +Salt = 73616C74 +Iterations = 8 +Output = E1367EC5151A33FAAC4CC1C144CD23FA15D5548493ECC99B9B5D9C0D3B27BEC76227EA66088B849B20AB7AA478010246 + +Passphrase = password +Salt = 73616C74 +Iterations = 8 +Output = E1367EC5151A33FAAC4CC1C144CD23FA15D5548493ECC99B9B5D9C0D3B27BEC76227EA66088B849B20AB7AA478010246E74BBA51723FEFA9F9474D6508845E8D + +Passphrase = password +Salt = 73616C74 +Iterations = 9 +Output = 5871A1FE827AC417E5B17BC90E0CF8EB + +Passphrase = password +Salt = 73616C74 +Iterations = 9 +Output = 5871A1FE827AC417E5B17BC90E0CF8EBC59384735EF44F0A4A79FFC70629D39B + +Passphrase = password +Salt = 73616C74 +Iterations = 9 +Output = 586571F0A1F9FE2982337A42C478174BE58EB17C7B90C9B40E800CBAF82DEB7BC54D936784A973FC5E01F4484F120AD3 + +Passphrase = password +Salt = 73616C74 +Iterations = 9 +Output = 586571F0A1F9FE2982337A42C478174BE58EB17C7B90C9B40E800CBAF82DEB7BC54D936784A973FC5E01F4484F120AD34A7179E4FF1EC7D406922935D3809B86 + +Passphrase = password +Salt = 73616C74 +Iterations = 10 +Output = ABC7DCFB41032D844AF67A1D1EF8A5E6 + +Passphrase = password +Salt = 73616C74 +Iterations = 10 +Output = ABC7DCFB41032D844AF67A1D1EF8A5E6A04AA3073D4F2777BFA5FE07A54CFF9D + +Passphrase = password +Salt = 73616C74 +Iterations = 10 +Output = ABFBC7DBDCB6FBC9414703BF2D0584994A9DF60A7AA71DC51EC7F8E1A55BE673A01D4AB9A3BA070E3DD74F92270177F9 + +Passphrase = password +Salt = 73616C74 +Iterations = 10 +Output = ABFBC7DBDCB6FBC9414703BF2D0584994A9DF60A7AA71DC51EC7F8E1A55BE673A01D4AB9A3BA070E3DD74F92270177F9BFCFA5A9FEA107DCA59F4C5EFFC09D96 + +Passphrase = password +Salt = 73616C74 +Iterations = 11 +Output = 07B2C9848D4535A9E96EDB0AB4783841 + +Passphrase = password +Salt = 73616C74 +Iterations = 11 +Output = 07B2C9848D4535A9E96EDB0AB4783841A2A4571BE59CB3D14742218AA72A89D5 + +Passphrase = password +Salt = 73616C74 +Iterations = 11 +Output = 0726B275C9CD84B18DD245DB35F4A944E9A46E7FDBB50A14B4C4789E38AF41C8A228A4FC57601B32E5409C5DB3C6D194 + +Passphrase = password +Salt = 73616C74 +Iterations = 11 +Output = 0726B275C9CD84B18DD245DB35F4A944E9A46E7FDBB50A14B4C4789E38AF41C8A228A4FC57601B32E5409C5DB3C6D194475D424421568AEBA7732ABF89C6D56D + diff --git a/src/tests/test_pbkdf.cpp b/src/tests/test_pbkdf.cpp index 0e780437b..f10b721e2 100644 --- a/src/tests/test_pbkdf.cpp +++ b/src/tests/test_pbkdf.cpp @@ -1,5 +1,5 @@ /* -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2019 Jack Lloyd * (C) 2018 Ribose Inc * * Botan is released under the Simplified BSD License (see license.txt) @@ -24,6 +24,10 @@ #include <botan/argon2.h> #endif +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + #include <botan/bcrypt_pbkdf.h> +#endif + namespace Botan_Tests { namespace { @@ -88,7 +92,17 @@ class Pwdhash_Tests : public Test { std::vector<Test::Result> results; - for(std::string pwdhash : { "Scrypt", "PBKDF2(SHA-256)", "OpenPGP-S2K(SHA-384)", "Argon2d", "Argon2i", "Argon2id" }) + const std::vector<std::string> all_pwdhash = { + "Scrypt", + "PBKDF2(SHA-256)", + "OpenPGP-S2K(SHA-384)", + "Argon2d", + "Argon2i", + "Argon2id", + "Bcrypt-PBKDF" + }; + + for(std::string pwdhash : all_pwdhash) { Test::Result result("Pwdhash " + pwdhash); auto pwdhash_fam = Botan::PasswordHashFamily::create(pwdhash); @@ -150,6 +164,38 @@ BOTAN_REGISTER_TEST("pwdhash", Pwdhash_Tests); #endif +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + +class Bcrypt_PBKDF_KAT_Tests final : public Text_Based_Test + { + public: + Bcrypt_PBKDF_KAT_Tests() : Text_Based_Test("bcrypt_pbkdf.vec", "Passphrase,Salt,Iterations,Output") {} + + Test::Result run_one_test(const std::string&, const VarMap& vars) override + { + const size_t rounds = vars.get_req_sz("Iterations"); + const std::vector<uint8_t> salt = vars.get_req_bin("Salt"); + const std::string passphrase = vars.get_req_str("Passphrase"); + const std::vector<uint8_t> expected = vars.get_req_bin("Output"); + + Test::Result result("bcrypt PBKDF"); + + std::vector<uint8_t> output(expected.size()); + Botan::bcrypt_pbkdf(output.data(), output.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size(), + rounds); + + result.test_eq("derived key", output, expected); + + return result; + } + }; + +BOTAN_REGISTER_TEST("bcrypt_pbkdf", Bcrypt_PBKDF_KAT_Tests); + +#endif + #if defined(BOTAN_HAS_SCRYPT) class Scrypt_KAT_Tests final : public Text_Based_Test |