diff options
-rw-r--r-- | checks/validate.cpp | 32 | ||||
-rw-r--r-- | doc/examples/passhash.cpp | 4 | ||||
-rw-r--r-- | src/constructs/passhash/info.txt | 3 | ||||
-rw-r--r-- | src/constructs/passhash/passhash.cpp | 42 | ||||
-rw-r--r-- | src/constructs/passhash/passhash.h | 2 |
5 files changed, 64 insertions, 19 deletions
diff --git a/checks/validate.cpp b/checks/validate.cpp index 9500589ca..bbb710b91 100644 --- a/checks/validate.cpp +++ b/checks/validate.cpp @@ -18,6 +18,11 @@ #include <botan/exceptn.h> #include <botan/selftest.h> #include <botan/libstate.h> + +#if defined(BOTAN_HAS_PASSHASH) + #include <botan/passhash.h> +#endif + using namespace Botan; #include "validate.h" @@ -61,6 +66,26 @@ std::vector<std::string> parse(const std::string&); void strip(std::string&); Botan::SecureVector<byte> decode_hex(const std::string&); +bool test_passhash(RandomNumberGenerator& rng) + { +#if defined(BOTAN_HAS_PASSHASH) + + const std::string input = "secret"; + const std::string fixed_hash = "$9$AArBRAG0kcKp3XPDUgd32ONhutn9HMQKix7H"; + + if(!password_hash_ok(input, fixed_hash)) + return false; + + std::string gen_hash = password_hash(input, rng, 5); + + if(!password_hash_ok(input, gen_hash)) + return false; + +#endif + + return true; + } + u32bit do_validation_tests(const std::string& filename, RandomNumberGenerator& rng, bool should_pass) @@ -179,6 +204,13 @@ u32bit do_validation_tests(const std::string& filename, } } + + if(should_pass && !test_passhash(rng)) + { + std::cout << "Passhash tests failed" << std::endl; + errors++; + } + if(should_pass) std::cout << std::endl; return errors; diff --git a/doc/examples/passhash.cpp b/doc/examples/passhash.cpp index 0949e944c..1e4c8c505 100644 --- a/doc/examples/passhash.cpp +++ b/doc/examples/passhash.cpp @@ -29,10 +29,8 @@ int main(int argc, char* argv[]) { Botan::AutoSeeded_RNG rng; - Botan::u32bit work_factor = 10; - std::cout << "H('" << argv[1] << "') = " - << Botan::password_hash(argv[1], rng, work_factor) << '\n'; + << Botan::password_hash(argv[1], rng) << '\n'; } else { diff --git a/src/constructs/passhash/info.txt b/src/constructs/passhash/info.txt index b2f54ef0a..fdc68deac 100644 --- a/src/constructs/passhash/info.txt +++ b/src/constructs/passhash/info.txt @@ -1,9 +1,8 @@ define PASSHASH <requires> +libstate pbkdf2 -sha2 -hmac rng base64 </requires> diff --git a/src/constructs/passhash/passhash.cpp b/src/constructs/passhash/passhash.cpp index dade15ddb..d3571808d 100644 --- a/src/constructs/passhash/passhash.cpp +++ b/src/constructs/passhash/passhash.cpp @@ -6,9 +6,9 @@ */ #include <botan/passhash.h> +#include <botan/loadstor.h> +#include <botan/libstate.h> #include <botan/pbkdf2.h> -#include <botan/hmac.h> -#include <botan/sha2_64.h> #include <botan/base64.h> #include <botan/pipe.h> @@ -16,17 +16,21 @@ namespace Botan { namespace { -const u32bit SALT_BYTES = 8; // 64 bits of salt +const std::string MAGIC_PREFIX = "$9$"; +const u32bit SALT_BYTES = 10; // 80 bits of salt const u32bit PBKDF_OUTPUT_LEN = 15; // 112 bits output const u32bit WORK_FACTOR_SCALE = 10000; +const std::string PBKDF_MAC = "HMAC(SHA-1)"; } std::string password_hash(const std::string& pass, RandomNumberGenerator& rng, - byte work_factor) + u16bit work_factor) { - PKCS5_PBKDF2 kdf(new HMAC(new SHA_512)); + PKCS5_PBKDF2 kdf( + global_state().algorithm_factory().make_mac(PBKDF_MAC) + ); SecureVector<byte> salt(SALT_BYTES); rng.randomize(&salt[0], salt.size()); @@ -40,40 +44,52 @@ std::string password_hash(const std::string& pass, Pipe pipe(new Base64_Encoder); pipe.start_msg(); - pipe.write(work_factor); + pipe.write(get_byte(0, work_factor)); + pipe.write(get_byte(1, work_factor)); pipe.write(salt); pipe.write(pbkdf2_output); pipe.end_msg(); - return pipe.read_all_as_string(); + return MAGIC_PREFIX + pipe.read_all_as_string(); } bool password_hash_ok(const std::string& pass, const std::string& hash) { + if(hash.size() != (36 + MAGIC_PREFIX.size())) + return false; + + for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i) + if(hash[i] != MAGIC_PREFIX[i]) + return false; + Pipe pipe(new Base64_Decoder); pipe.start_msg(); - pipe.write(hash); + pipe.write(hash.c_str() + MAGIC_PREFIX.size()); pipe.end_msg(); SecureVector<byte> bin = pipe.read_all(); - if(bin.size() != (1 + PBKDF_OUTPUT_LEN + SALT_BYTES)) + const u32bit WORKFACTOR_BYTES = 2; + + if(bin.size() != (WORKFACTOR_BYTES + PBKDF_OUTPUT_LEN + SALT_BYTES)) return false; - u32bit kdf_iterations = WORK_FACTOR_SCALE * bin[0]; + u32bit kdf_iterations = WORK_FACTOR_SCALE * load_be<u16bit>(bin, 0); if(kdf_iterations == 0) return false; - PKCS5_PBKDF2 kdf(new HMAC(new SHA_512)); + PKCS5_PBKDF2 kdf( + global_state().algorithm_factory().make_mac(PBKDF_MAC) + ); SecureVector<byte> cmp = kdf.derive_key( PBKDF_OUTPUT_LEN, pass, - &bin[1], SALT_BYTES, + &bin[WORKFACTOR_BYTES], SALT_BYTES, kdf_iterations).bits_of(); return same_mem(cmp.begin(), - bin.begin() + 1 + SALT_BYTES, + bin.begin() + WORKFACTOR_BYTES + SALT_BYTES, PBKDF_OUTPUT_LEN); } diff --git a/src/constructs/passhash/passhash.h b/src/constructs/passhash/passhash.h index e3b0419fe..9676ff85e 100644 --- a/src/constructs/passhash/passhash.h +++ b/src/constructs/passhash/passhash.h @@ -20,7 +20,7 @@ namespace Botan { */ std::string BOTAN_DLL password_hash(const std::string& password, RandomNumberGenerator& rng, - byte work_factor = 10); + u16bit work_factor = 10); /** * Check a previously created password hash |