aboutsummaryrefslogtreecommitdiffstats
path: root/src/constructs/passhash/passhash9.cpp
diff options
context:
space:
mode:
authorlloyd <lloyd@randombit.net>2010-02-05 20:41:13 +0000
committerlloyd <lloyd@randombit.net>2010-02-05 20:41:13 +0000
commit3c8bfb624e321e5a56938fc27f9312cfd3d23d97 (patch)
tree8cce22b63e6775a12d6ebd896652d4bcba9cdceb /src/constructs/passhash/passhash9.cpp
parentc9ce8388a2fd3fb93d5afead65626eef3d2d938b (diff)
Further passhash changes before release and things have to be
finalized. Move header to passhash9.h and rename the functions to be passhash9 specific ({generator,check}_passhash9) Add an algorithm identifer field. Currently only id 0 is defined, for HMAC(SHA-1), but this opens up for using HMAC(SHA-512) or HMAC(SHA-3) or CMAC(Blowfish) or whatever in the future if necessary. Increase the salt size to 96 bits and the PRF output size to 192 bits. Document in api.tex
Diffstat (limited to 'src/constructs/passhash/passhash9.cpp')
-rw-r--r--src/constructs/passhash/passhash9.cpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/constructs/passhash/passhash9.cpp b/src/constructs/passhash/passhash9.cpp
new file mode 100644
index 000000000..9e5ff3257
--- /dev/null
+++ b/src/constructs/passhash/passhash9.cpp
@@ -0,0 +1,127 @@
+/*
+* Passhash9 Password Hashing
+* (C) 2010 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/passhash9.h>
+#include <botan/loadstor.h>
+#include <botan/libstate.h>
+#include <botan/pbkdf2.h>
+#include <botan/base64.h>
+#include <botan/pipe.h>
+
+namespace Botan {
+
+namespace {
+
+const std::string MAGIC_PREFIX = "$9$";
+
+const u32bit WORKFACTOR_BYTES = 2;
+const u32bit ALGID_BYTES = 1;
+const u32bit SALT_BYTES = 12; // 96 bits of salt
+const u32bit PBKDF_OUTPUT_LEN = 24; // 192 bits output
+
+const u32bit WORK_FACTOR_SCALE = 10000;
+
+MessageAuthenticationCode* get_pbkdf_prf(byte alg_id)
+ {
+ Algorithm_Factory& af = global_state().algorithm_factory();
+
+ if(alg_id == 0)
+ return af.make_mac("HMAC(SHA-1)");
+
+ return 0;
+ }
+
+std::pair<byte, MessageAuthenticationCode*> choose_pbkdf_prf()
+ {
+ byte alg_id = 0;
+ return std::make_pair(alg_id, get_pbkdf_prf(alg_id));
+ }
+
+}
+
+std::string generate_passhash9(const std::string& pass,
+ RandomNumberGenerator& rng,
+ u16bit work_factor)
+ {
+ std::pair<byte, MessageAuthenticationCode*> prf = choose_pbkdf_prf();
+ byte alg_id = prf.first;
+
+ PKCS5_PBKDF2 kdf(prf.second); // takes ownership of pointer
+
+ SecureVector<byte> salt(SALT_BYTES);
+ rng.randomize(&salt[0], salt.size());
+
+ u32bit kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+ SecureVector<byte> pbkdf2_output =
+ kdf.derive_key(PBKDF_OUTPUT_LEN, pass,
+ &salt[0], salt.size(),
+ kdf_iterations).bits_of();
+
+ Pipe pipe(new Base64_Encoder);
+ pipe.start_msg();
+ pipe.write(alg_id);
+ 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 MAGIC_PREFIX + pipe.read_all_as_string();
+ }
+
+bool check_passhash9(const std::string& pass, const std::string& hash)
+ {
+ const u32bit BINARY_LENGTH =
+ (ALGID_BYTES + WORKFACTOR_BYTES + PBKDF_OUTPUT_LEN + SALT_BYTES);
+
+ const u32bit 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;
+
+ Pipe pipe(new Base64_Decoder);
+ pipe.start_msg();
+ pipe.write(hash.c_str() + MAGIC_PREFIX.size());
+ pipe.end_msg();
+
+ SecureVector<byte> bin = pipe.read_all();
+
+ if(bin.size() != BINARY_LENGTH)
+ return false;
+
+ byte alg_id = bin[0];
+
+ u32bit kdf_iterations =
+ WORK_FACTOR_SCALE * load_be<u16bit>(bin + ALGID_BYTES, 0);
+
+ if(kdf_iterations == 0)
+ return false;
+
+ MessageAuthenticationCode* pbkdf_prf = get_pbkdf_prf(alg_id);
+
+ if(pbkdf_prf == 0)
+ return false; // unknown algorithm, reject
+
+ PKCS5_PBKDF2 kdf(pbkdf_prf); // takes ownership of pointer
+
+ SecureVector<byte> cmp = kdf.derive_key(
+ PBKDF_OUTPUT_LEN, pass,
+ &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
+ kdf_iterations).bits_of();
+
+ return same_mem(cmp.begin(),
+ bin.begin() + ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES,
+ PBKDF_OUTPUT_LEN);
+ }
+
+}