Botan  1.11.15
src/lib/rng/hmac_rng/hmac_rng.cpp
Go to the documentation of this file.
00001 /*
00002 * HMAC_RNG
00003 * (C) 2008-2009,2013,2015 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/hmac_rng.h>
00009 #include <botan/get_byte.h>
00010 #include <botan/entropy_src.h>
00011 #include <botan/internal/xor_buf.h>
00012 #include <algorithm>
00013 #include <chrono>
00014 
00015 namespace Botan {
00016 
00017 namespace {
00018 
00019 void hmac_prf(MessageAuthenticationCode& prf,
00020               secure_vector<byte>& K,
00021               u32bit& counter,
00022               const std::string& label)
00023    {
00024    typedef std::chrono::high_resolution_clock clock;
00025 
00026    auto timestamp = clock::now().time_since_epoch().count();
00027 
00028    prf.update(K);
00029    prf.update(label);
00030    prf.update_be(timestamp);
00031    prf.update_be(counter);
00032    prf.final(&K[0]);
00033 
00034    ++counter;
00035    }
00036 
00037 }
00038 
00039 /*
00040 * HMAC_RNG Constructor
00041 */
00042 HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor,
00043                    MessageAuthenticationCode* prf) :
00044    m_extractor(extractor), m_prf(prf)
00045    {
00046    if(!m_prf->valid_keylength(m_extractor->output_length()) ||
00047       !m_extractor->valid_keylength(m_prf->output_length()))
00048       throw Invalid_Argument("HMAC_RNG: Bad algo combination " +
00049                              m_extractor->name() + " and " +
00050                              m_prf->name());
00051 
00052    // First PRF inputs are all zero, as specified in section 2
00053    m_K.resize(m_prf->output_length());
00054 
00055    /*
00056    Normally we want to feedback PRF outputs to the extractor function
00057    to ensure a single bad poll does not reduce entropy. Thus in reseed
00058    we'll want to invoke the PRF before we reset the PRF key, but until
00059    the first reseed the PRF is unkeyed. Rather than trying to keep
00060    track of this, just set the initial PRF key to constant zero.
00061    Since all PRF inputs in the first reseed are constants, this
00062    amounts to suffixing the seed in the first poll with a fixed
00063    constant string.
00064 
00065    The PRF key will not be used to generate outputs until after reseed
00066    sets m_seeded to true.
00067    */
00068    secure_vector<byte> prf_key(m_extractor->output_length());
00069    m_prf->set_key(prf_key);
00070 
00071    /*
00072    Use PRF("Botan HMAC_RNG XTS") as the intitial XTS key.
00073 
00074    This will be used during the first extraction sequence; XTS values
00075    after this one are generated using the PRF.
00076 
00077    If I understand the E-t-E paper correctly (specifically Section 4),
00078    using this fixed extractor key is safe to do.
00079    */
00080    m_extractor->set_key(prf->process("Botan HMAC_RNG XTS"));
00081    }
00082 
00083 /*
00084 * Generate a buffer of random bytes
00085 */
00086 void HMAC_RNG::randomize(byte out[], size_t length)
00087    {
00088    if(!is_seeded())
00089       {
00090       reseed(256);
00091       if(!is_seeded())
00092          throw PRNG_Unseeded(name());
00093       }
00094 
00095    const size_t max_per_prf_iter = m_prf->output_length() / 2;
00096 
00097    m_output_since_reseed += length;
00098 
00099    if(m_output_since_reseed >= BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED)
00100       reseed(BOTAN_RNG_RESEED_POLL_BITS);
00101 
00102    /*
00103     HMAC KDF as described in E-t-E, using a CTXinfo of "rng"
00104    */
00105    while(length)
00106       {
00107       hmac_prf(*m_prf, m_K, m_counter, "rng");
00108 
00109       const size_t copied = std::min<size_t>(length, max_per_prf_iter);
00110 
00111       copy_mem(out, &m_K[0], copied);
00112       out += copied;
00113       length -= copied;
00114       }
00115    }
00116 
00117 /*
00118 * Poll for entropy and reset the internal keys
00119 */
00120 void HMAC_RNG::reseed(size_t poll_bits)
00121    {
00122    /*
00123    Using the terminology of E-t-E, XTR is the MAC function (normally
00124    HMAC) seeded with XTS (below) and we form SKM, the key material, by
00125    polling as many sources as we think needed to reach our polling
00126    goal. We then also include feedback of the current PRK so that
00127    a bad poll doesn't wipe us out.
00128    */
00129 
00130    double bits_collected = 0;
00131 
00132    Entropy_Accumulator accum(
00133       [&](const byte in[], size_t in_len, double entropy_estimate)
00134       {
00135       m_extractor->update(in, in_len);
00136       bits_collected += entropy_estimate;
00137       return (bits_collected >= poll_bits);
00138       });
00139 
00140    EntropySource::poll_available_sources(accum);
00141 
00142    /*
00143    * It is necessary to feed forward poll data. Otherwise, a good poll
00144    * (collecting a large amount of conditional entropy) followed by a
00145    * bad one (collecting little) would be unsafe. Do this by
00146    * generating new PRF outputs using the previous key and feeding
00147    * them into the extractor function.
00148    *
00149    * Cycle the RNG once (CTXinfo="rng"), then generate a new PRF
00150    * output using the CTXinfo "reseed". Provide these values as input
00151    * to the extractor function.
00152    */
00153    hmac_prf(*m_prf, m_K, m_counter, "rng");
00154    m_extractor->update(m_K); // K is the CTXinfo=rng PRF output
00155 
00156    hmac_prf(*m_prf, m_K, m_counter, "reseed");
00157    m_extractor->update(m_K); // K is the CTXinfo=reseed PRF output
00158 
00159    /* Now derive the new PRK using everything that has been fed into
00160       the extractor, and set the PRF key to that */
00161    m_prf->set_key(m_extractor->final());
00162 
00163    // Now generate a new PRF output to use as the XTS extractor salt
00164    hmac_prf(*m_prf, m_K, m_counter, "xts");
00165    m_extractor->set_key(m_K);
00166 
00167    // Reset state
00168    zeroise(m_K);
00169    m_counter = 0;
00170 
00171    m_collected_entropy_estimate =
00172       std::min<size_t>(m_collected_entropy_estimate + bits_collected,
00173                        m_extractor->output_length() * 8);
00174 
00175    m_output_since_reseed = 0;
00176    }
00177 
00178 bool HMAC_RNG::is_seeded() const
00179    {
00180    return (m_collected_entropy_estimate >= 256);
00181    }
00182 
00183 /*
00184 * Add user-supplied entropy to the extractor input
00185 */
00186 void HMAC_RNG::add_entropy(const byte input[], size_t length)
00187    {
00188    m_extractor->update(input, length);
00189    reseed(BOTAN_RNG_RESEED_POLL_BITS);
00190    }
00191 
00192 /*
00193 * Clear memory of sensitive data
00194 */
00195 void HMAC_RNG::clear()
00196    {
00197    m_collected_entropy_estimate = 0;
00198    m_extractor->clear();
00199    m_prf->clear();
00200    zeroise(m_K);
00201    m_counter = 0;
00202    }
00203 
00204 /*
00205 * Return the name of this type
00206 */
00207 std::string HMAC_RNG::name() const
00208    {
00209    return "HMAC_RNG(" + m_extractor->name() + "," + m_prf->name() + ")";
00210    }
00211 
00212 }