Botan  1.11.15
src/lib/pbkdf/pbkdf2/pbkdf2.cpp
Go to the documentation of this file.
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 }