From 0df311ee436211413357c571d66a869b510959fc Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 4 Sep 2018 20:25:23 -0400 Subject: Add PasswordHash and PasswordHashFamily This is a contribution by Ribose Inc (@riboseinc) --- src/lib/pbkdf/info.txt | 3 +- src/lib/pbkdf/pbkdf.h | 3 + src/lib/pbkdf/pwdhash.cpp | 88 +++++++++++++++++++++++++++ src/lib/pbkdf/pwdhash.h | 149 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 src/lib/pbkdf/pwdhash.cpp create mode 100644 src/lib/pbkdf/pwdhash.h (limited to 'src/lib') diff --git a/src/lib/pbkdf/info.txt b/src/lib/pbkdf/info.txt index 48c6b56e6..650414f41 100644 --- a/src/lib/pbkdf/info.txt +++ b/src/lib/pbkdf/info.txt @@ -1,5 +1,5 @@ -PBKDF -> 20150626 +PBKDF -> 20180902 @@ -8,5 +8,6 @@ hash +pwdhash.h pbkdf.h diff --git a/src/lib/pbkdf/pbkdf.h b/src/lib/pbkdf/pbkdf.h index 7d3bceffc..9692b2546 100644 --- a/src/lib/pbkdf/pbkdf.h +++ b/src/lib/pbkdf/pbkdf.h @@ -17,6 +17,9 @@ namespace Botan { * Base class for PBKDF (password based key derivation function) * implementations. Converts a password into a key using a salt * and iterated hashing to make brute force attacks harder. +* +* Starting in 2.8 this functionality is also offered by PasswordHash. +* The PBKDF interface may be removed in a future release. */ class BOTAN_PUBLIC_API(2,0) PBKDF { diff --git a/src/lib/pbkdf/pwdhash.cpp b/src/lib/pbkdf/pwdhash.cpp new file mode 100644 index 000000000..783ac7066 --- /dev/null +++ b/src/lib/pbkdf/pwdhash.cpp @@ -0,0 +1,88 @@ +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +#if defined(BOTAN_HAS_PBKDF2) + #include +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + #include +#endif + +#if defined(BOTAN_HAS_SCRYPT) + #include +#endif + +namespace Botan { + +std::unique_ptr PasswordHashFamily::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + // TODO OpenSSL + + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::unique_ptr(new PBKDF2_Family(mac.release())); + + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::unique_ptr(new PBKDF2_Family(mac.release())); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_SCRYPT) + if(req.algo_name() == "Scrypt") + { + return std::unique_ptr(new Scrypt_Family); + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + { + return std::unique_ptr(new RFC4880_S2K_Family(hash.release())); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr +PasswordHashFamily::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PasswordHashFamily::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PasswordHashFamily", algo, provider); + } + +std::vector PasswordHashFamily::providers(const std::string& algo_spec) + { + return probe_providers_of(algo_spec, { "base", "openssl" }); + } + +} diff --git a/src/lib/pbkdf/pwdhash.h b/src/lib/pbkdf/pwdhash.h new file mode 100644 index 000000000..7ecf111c6 --- /dev/null +++ b/src/lib/pbkdf/pwdhash.h @@ -0,0 +1,149 @@ +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PWDHASH_H_ +#define BOTAN_PWDHASH_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +/** +* Base class for password based key derivation functions. +* +* Converts a password into a key using a salt and iterated hashing to +* make brute force attacks harder. +*/ +class BOTAN_PUBLIC_API(2,8) PasswordHash + { + public: + virtual ~PasswordHash() = default; + + virtual std::string to_string() const = 0; + + /** + * Most password hashes have some notion of iterations. + */ + virtual size_t iterations() const = 0; + + /** + * Some password hashing algorithms have a parameter which controls how + * much memory is used. If not supported by some algorithm, returns 0. + */ + virtual size_t memory_param() const { return 0; } + + /** + * Some password hashing algorithms have a parallelism parameter. + * If not supported by some algorithm, just returns 1 + */ + virtual size_t parallelism() const { return 1; } + + /** + * Returns an estimate of the total memory usage required to perform this + * key derivation. + * + * If this algorithm uses a small and constant amount of memory, with no + * effort made towards being memory hard, this function returns 0. + */ + virtual size_t total_memory_usage() const { return 0; } + + /** + * Derive a key from a password + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const = 0; + }; + +class BOTAN_PUBLIC_API(2,8) PasswordHashFamily + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector providers(const std::string& algo_spec); + + virtual ~PasswordHashFamily() = default; + + /** + * @return name of this PasswordHash + */ + virtual std::string name() const = 0; + + /** + * Return a new parameter set tuned for this machine + * @param output_length how long the output length will be + * @param msec the desired execution time in milliseconds + * + * @param max_memory_usage some password hash functions can use a tunable + * amount of memory, in this case max_memory_usage limits the amount of RAM + * the returned parameters will require, in mebibytes (2**20 bytes). It may + * require some small amount above the request. Set to zero to place no + * limit at all. + */ + virtual std::unique_ptr tune(size_t output_len, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb = 128) const = 0; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + virtual std::unique_ptr default_params() const = 0; + + /** + * Create a password hash using some scheme specific format. + * Eg PBKDF2 and PGP-S2K set iterations in i1 + * Scrypt uses N,r,p in i{1-3} + * Bcrypt-PBKDF just has iterations + * Argon2{i,d,id} would use iterations, memory, parallelism for i{1-3}, + * and Argon2 type is part of the family. + * + * Values not needed should be set to 0 + */ + virtual std::unique_ptr from_params( + size_t i1, + size_t i2 = 0, + size_t i3 = 0) const = 0; + }; + +} + +#endif -- cgit v1.2.3