Botan
1.11.15
|
00001 /* 00002 * PBKDF2 00003 * (C) 1999-2007 Jack Lloyd 00004 * 00005 * Botan is released under the Simplified BSD License (see license.txt) 00006 */ 00007 00008 #include <botan/internal/pbkdf_utils.h> 00009 #include <botan/pbkdf2.h> 00010 #include <botan/get_byte.h> 00011 #include <botan/internal/xor_buf.h> 00012 #include <botan/internal/rounding.h> 00013 00014 namespace Botan { 00015 00016 BOTAN_REGISTER_NAMED_T(PBKDF, "PBKDF2", PKCS5_PBKDF2, PKCS5_PBKDF2::make); 00017 00018 PKCS5_PBKDF2* PKCS5_PBKDF2::make(const Spec& spec) 00019 { 00020 if(auto mac = make_a<MessageAuthenticationCode>(spec.arg(0))) 00021 return new PKCS5_PBKDF2(mac); 00022 00023 if(auto mac = make_a<MessageAuthenticationCode>("HMAC(" + spec.arg(0) + ")")) 00024 return new PKCS5_PBKDF2(mac); 00025 00026 return nullptr; 00027 } 00028 00029 size_t 00030 pbkdf2(MessageAuthenticationCode& prf, 00031 byte out[], 00032 size_t out_len, 00033 const std::string& passphrase, 00034 const byte salt[], size_t salt_len, 00035 size_t iterations, 00036 std::chrono::milliseconds msec) 00037 { 00038 clear_mem(out, out_len); 00039 00040 if(out_len == 0) 00041 return 0; 00042 00043 try 00044 { 00045 prf.set_key(reinterpret_cast<const byte*>(passphrase.data()), passphrase.size()); 00046 } 00047 catch(Invalid_Key_Length) 00048 { 00049 throw std::runtime_error("PBKDF2 with " + prf.name() + 00050 " cannot accept passphrases of length " + 00051 std::to_string(passphrase.size())); 00052 } 00053 00054 const size_t prf_sz = prf.output_length(); 00055 secure_vector<byte> U(prf_sz); 00056 00057 const size_t blocks_needed = round_up(out_len, prf_sz) / prf_sz; 00058 00059 std::chrono::microseconds usec_per_block = 00060 std::chrono::duration_cast<std::chrono::microseconds>(msec) / blocks_needed; 00061 00062 u32bit counter = 1; 00063 while(out_len) 00064 { 00065 const size_t prf_output = std::min<size_t>(prf_sz, out_len); 00066 00067 prf.update(salt, salt_len); 00068 prf.update_be(counter++); 00069 prf.final(&U[0]); 00070 00071 xor_buf(out, &U[0], prf_output); 00072 00073 if(iterations == 0) 00074 { 00075 /* 00076 If no iterations set, run the first block to calibrate based 00077 on how long hashing takes on whatever machine we're running on. 00078 */ 00079 00080 const auto start = std::chrono::high_resolution_clock::now(); 00081 00082 iterations = 1; // the first iteration we did above 00083 00084 while(true) 00085 { 00086 prf.update(U); 00087 prf.final(&U[0]); 00088 xor_buf(out, &U[0], prf_output); 00089 iterations++; 00090 00091 /* 00092 Only break on relatively 'even' iterations. For one it 00093 avoids confusion, and likely some broken implementations 00094 break on getting completely randomly distributed values 00095 */ 00096 if(iterations % 10000 == 0) 00097 { 00098 auto time_taken = std::chrono::high_resolution_clock::now() - start; 00099 auto usec_taken = std::chrono::duration_cast<std::chrono::microseconds>(time_taken); 00100 if(usec_taken > usec_per_block) 00101 break; 00102 } 00103 } 00104 } 00105 else 00106 { 00107 for(size_t i = 1; i != iterations; ++i) 00108 { 00109 prf.update(U); 00110 prf.final(&U[0]); 00111 xor_buf(out, &U[0], prf_output); 00112 } 00113 } 00114 00115 out_len -= prf_output; 00116 out += prf_output; 00117 } 00118 00119 return iterations; 00120 } 00121 00122 size_t 00123 PKCS5_PBKDF2::pbkdf(byte key[], size_t key_len, 00124 const std::string& passphrase, 00125 const byte salt[], size_t salt_len, 00126 size_t iterations, 00127 std::chrono::milliseconds msec) const 00128 { 00129 return pbkdf2(*mac.get(), key, key_len, passphrase, salt, salt_len, iterations, msec); 00130 } 00131 00132 00133 }