Botan
1.11.15
|
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 }