summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp')
-rw-r--r--comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp142
1 files changed, 142 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp b/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
new file mode 100644
index 0000000000..98f5a54d51
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
@@ -0,0 +1,142 @@
+/*
+* Passhash9 Password Hashing
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/passhash9.h>
+#include <botan/rng.h>
+#include <botan/loadstor.h>
+#include <botan/pbkdf2.h>
+#include <botan/base64.h>
+
+namespace Botan {
+
+namespace {
+
+const std::string MAGIC_PREFIX = "$9$";
+
+const size_t WORKFACTOR_BYTES = 2;
+const size_t ALGID_BYTES = 1;
+const size_t SALT_BYTES = 12; // 96 bits of salt
+const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
+
+const size_t WORK_FACTOR_SCALE = 10000;
+
+std::unique_ptr<MessageAuthenticationCode> get_pbkdf_prf(uint8_t alg_id)
+ {
+ if(alg_id == 0)
+ return MessageAuthenticationCode::create("HMAC(SHA-1)");
+ else if(alg_id == 1)
+ return MessageAuthenticationCode::create("HMAC(SHA-256)");
+ else if(alg_id == 2)
+ return MessageAuthenticationCode::create("CMAC(Blowfish)");
+ else if(alg_id == 3)
+ return MessageAuthenticationCode::create("HMAC(SHA-384)");
+ else if(alg_id == 4)
+ return MessageAuthenticationCode::create("HMAC(SHA-512)");
+ return nullptr;
+ }
+
+}
+
+std::string generate_passhash9(const std::string& pass,
+ RandomNumberGenerator& rng,
+ uint16_t work_factor,
+ uint8_t alg_id)
+ {
+ BOTAN_ARG_CHECK(work_factor > 0 && work_factor < 512, "Invalid Passhash9 work factor");
+
+ std::unique_ptr<MessageAuthenticationCode> prf = get_pbkdf_prf(alg_id);
+
+ if(!prf)
+ throw Invalid_Argument("Passhash9: Algorithm id " +
+ std::to_string(alg_id) +
+ " is not defined");
+
+ PKCS5_PBKDF2 kdf(prf.release()); // takes ownership of pointer
+
+ secure_vector<uint8_t> salt(SALT_BYTES);
+ rng.randomize(salt.data(), salt.size());
+
+ const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+ secure_vector<uint8_t> blob;
+ blob.push_back(alg_id);
+ blob.push_back(get_byte(0, work_factor));
+ blob.push_back(get_byte(1, work_factor));
+ blob += salt;
+ blob += kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN,
+ pass,
+ salt.data(), salt.size(),
+ kdf_iterations).bits_of();
+
+ return MAGIC_PREFIX + base64_encode(blob);
+ }
+
+bool check_passhash9(const std::string& pass, const std::string& hash)
+ {
+ const size_t BINARY_LENGTH =
+ ALGID_BYTES +
+ WORKFACTOR_BYTES +
+ PASSHASH9_PBKDF_OUTPUT_LEN +
+ SALT_BYTES;
+
+ const size_t BASE64_LENGTH =
+ MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
+
+ if(hash.size() != BASE64_LENGTH)
+ return false;
+
+ for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
+ if(hash[i] != MAGIC_PREFIX[i])
+ return false;
+
+ secure_vector<uint8_t> bin = base64_decode(hash.c_str() + MAGIC_PREFIX.size());
+
+ if(bin.size() != BINARY_LENGTH)
+ return false;
+
+ uint8_t alg_id = bin[0];
+
+ const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0);
+
+ // Bug in the format, bad states shouldn't be representable, but are...
+ if(work_factor == 0)
+ return false;
+
+ if(work_factor > 512)
+ throw Invalid_Argument("Requested passhash9 work factor " +
+ std::to_string(work_factor) + " is too large");
+
+ const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+ std::unique_ptr<MessageAuthenticationCode> pbkdf_prf = get_pbkdf_prf(alg_id);
+
+ if(!pbkdf_prf)
+ return false; // unknown algorithm, reject
+
+ PKCS5_PBKDF2 kdf(pbkdf_prf.release()); // takes ownership of pointer
+
+ secure_vector<uint8_t> cmp = kdf.derive_key(
+ PASSHASH9_PBKDF_OUTPUT_LEN,
+ pass,
+ &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
+ kdf_iterations).bits_of();
+
+ return constant_time_compare(cmp.data(),
+ &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES],
+ PASSHASH9_PBKDF_OUTPUT_LEN);
+ }
+
+bool is_passhash9_alg_supported(uint8_t alg_id)
+ {
+ if (get_pbkdf_prf(alg_id))
+ {
+ return true;
+ }
+ return false;
+ }
+
+}