Botan  1.11.15
src/lib/passhash/passhash9/passhash9.cpp
Go to the documentation of this file.
00001 /*
00002 * Passhash9 Password Hashing
00003 * (C) 2010 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/passhash9.h>
00009 #include <botan/loadstor.h>
00010 #include <botan/lookup.h>
00011 #include <botan/pbkdf2.h>
00012 #include <botan/base64.h>
00013 
00014 namespace Botan {
00015 
00016 namespace {
00017 
00018 const std::string MAGIC_PREFIX = "$9$";
00019 
00020 const size_t WORKFACTOR_BYTES = 2;
00021 const size_t ALGID_BYTES = 1;
00022 const size_t SALT_BYTES = 12; // 96 bits of salt
00023 const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
00024 
00025 const size_t WORK_FACTOR_SCALE = 10000;
00026 
00027 MessageAuthenticationCode* get_pbkdf_prf(byte alg_id)
00028    {
00029    try
00030       {
00031       if(alg_id == 0)
00032          return get_mac("HMAC(SHA-1)");
00033       else if(alg_id == 1)
00034          return get_mac("HMAC(SHA-256)");
00035       else if(alg_id == 2)
00036          return get_mac("CMAC(Blowfish)");
00037       else if(alg_id == 3)
00038          return get_mac("HMAC(SHA-384)");
00039       else if(alg_id == 4)
00040          return get_mac("HMAC(SHA-512)");
00041       }
00042    catch(Algorithm_Not_Found) {}
00043 
00044    return nullptr;
00045    }
00046 
00047 }
00048 
00049 std::string generate_passhash9(const std::string& pass,
00050                                RandomNumberGenerator& rng,
00051                                u16bit work_factor,
00052                                byte alg_id)
00053    {
00054    MessageAuthenticationCode* prf = get_pbkdf_prf(alg_id);
00055 
00056    if(!prf)
00057       throw Invalid_Argument("Passhash9: Algorithm id " +
00058                              std::to_string(alg_id) +
00059                              " is not defined");
00060 
00061    PKCS5_PBKDF2 kdf(prf); // takes ownership of pointer
00062 
00063    secure_vector<byte> salt(SALT_BYTES);
00064    rng.randomize(&salt[0], salt.size());
00065 
00066    const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
00067 
00068    secure_vector<byte> blob;
00069    blob.push_back(alg_id);
00070    blob.push_back(get_byte(0, work_factor));
00071    blob.push_back(get_byte(1, work_factor));
00072    blob += salt;
00073    blob += kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN,
00074                           pass,
00075                           &salt[0], salt.size(),
00076                           kdf_iterations).bits_of();
00077 
00078    return MAGIC_PREFIX + base64_encode(blob);
00079    }
00080 
00081 bool check_passhash9(const std::string& pass, const std::string& hash)
00082    {
00083    const size_t BINARY_LENGTH =
00084      ALGID_BYTES +
00085      WORKFACTOR_BYTES +
00086      PASSHASH9_PBKDF_OUTPUT_LEN +
00087      SALT_BYTES;
00088 
00089    const size_t BASE64_LENGTH =
00090       MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
00091 
00092    if(hash.size() != BASE64_LENGTH)
00093       return false;
00094 
00095    for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
00096       if(hash[i] != MAGIC_PREFIX[i])
00097          return false;
00098 
00099    secure_vector<byte> bin = base64_decode(hash.c_str() + MAGIC_PREFIX.size());
00100 
00101    if(bin.size() != BINARY_LENGTH)
00102       return false;
00103 
00104    byte alg_id = bin[0];
00105 
00106    const size_t work_factor = load_be<u16bit>(&bin[ALGID_BYTES], 0);
00107 
00108    // Bug in the format, bad states shouldn't be representable, but are...
00109    if(work_factor == 0)
00110       return false;
00111 
00112    if(work_factor > 512)
00113       throw std::invalid_argument("Requested Bcrypt work factor " +
00114                                   std::to_string(work_factor) + " too large");
00115 
00116    const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
00117 
00118    MessageAuthenticationCode* pbkdf_prf = get_pbkdf_prf(alg_id);
00119 
00120    if(!pbkdf_prf)
00121       return false; // unknown algorithm, reject
00122 
00123    PKCS5_PBKDF2 kdf(pbkdf_prf); // takes ownership of pointer
00124 
00125    secure_vector<byte> cmp = kdf.derive_key(
00126       PASSHASH9_PBKDF_OUTPUT_LEN,
00127       pass,
00128       &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
00129       kdf_iterations).bits_of();
00130 
00131    return same_mem(&cmp[0],
00132                    &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES],
00133                    PASSHASH9_PBKDF_OUTPUT_LEN);
00134    }
00135 
00136 }