diff options
Diffstat (limited to 'src/lib/rng')
-rw-r--r-- | src/lib/rng/auto_rng/auto_rng.h | 41 | ||||
-rw-r--r-- | src/lib/rng/auto_rng/info.txt | 8 | ||||
-rw-r--r-- | src/lib/rng/hmac_rng/hmac_rng.cpp | 205 | ||||
-rw-r--r-- | src/lib/rng/hmac_rng/hmac_rng.h | 57 | ||||
-rw-r--r-- | src/lib/rng/hmac_rng/info.txt | 5 | ||||
-rw-r--r-- | src/lib/rng/info.txt | 3 | ||||
-rw-r--r-- | src/lib/rng/rng.cpp | 34 | ||||
-rw-r--r-- | src/lib/rng/rng.h | 174 | ||||
-rw-r--r-- | src/lib/rng/x931_rng/info.txt | 5 | ||||
-rw-r--r-- | src/lib/rng/x931_rng/x931_rng.cpp | 146 | ||||
-rw-r--r-- | src/lib/rng/x931_rng/x931_rng.h | 50 |
11 files changed, 728 insertions, 0 deletions
diff --git a/src/lib/rng/auto_rng/auto_rng.h b/src/lib/rng/auto_rng/auto_rng.h new file mode 100644 index 000000000..13201f251 --- /dev/null +++ b/src/lib/rng/auto_rng/auto_rng.h @@ -0,0 +1,41 @@ +/* +* Auto Seeded RNG +* (C) 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_AUTO_SEEDING_RNG_H__ +#define BOTAN_AUTO_SEEDING_RNG_H__ + +#include <botan/rng.h> +#include <string> +#include <memory> + +namespace Botan { + +class BOTAN_DLL AutoSeeded_RNG : public RandomNumberGenerator + { + public: + void randomize(byte out[], size_t len) + { m_rng->randomize(out, len); } + + bool is_seeded() const { return m_rng->is_seeded(); } + + void clear() { m_rng->clear(); } + + std::string name() const { return m_rng->name(); } + + void reseed(size_t poll_bits = 256) { m_rng->reseed(poll_bits); } + + void add_entropy(const byte in[], size_t len) + { m_rng->add_entropy(in, len); } + + AutoSeeded_RNG() : m_rng(RandomNumberGenerator::make_rng()) {} + private: + std::unique_ptr<RandomNumberGenerator> m_rng; + }; + +} + +#endif diff --git a/src/lib/rng/auto_rng/info.txt b/src/lib/rng/auto_rng/info.txt new file mode 100644 index 000000000..848c06ac3 --- /dev/null +++ b/src/lib/rng/auto_rng/info.txt @@ -0,0 +1,8 @@ +define AUTO_SEEDING_RNG 20131128 + +<requires> +hmac_rng +hmac +sha2_32 +sha2_64 +</requires> diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp new file mode 100644 index 000000000..98ae6439e --- /dev/null +++ b/src/lib/rng/hmac_rng/hmac_rng.cpp @@ -0,0 +1,205 @@ +/* +* HMAC_RNG +* (C) 2008-2009,2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/hmac_rng.h> +#include <botan/libstate.h> +#include <botan/get_byte.h> +#include <botan/entropy_src.h> +#include <botan/internal/xor_buf.h> +#include <algorithm> +#include <chrono> + +namespace Botan { + +namespace { + +void hmac_prf(MessageAuthenticationCode& prf, + secure_vector<byte>& K, + u32bit& counter, + const std::string& label) + { + typedef std::chrono::high_resolution_clock clock; + + auto timestamp = clock::now().time_since_epoch().count(); + + prf.update(K); + prf.update(label); + prf.update_be(timestamp); + prf.update_be(counter); + prf.final(&K[0]); + + ++counter; + } + +} + +/* +* HMAC_RNG Constructor +*/ +HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor, + MessageAuthenticationCode* prf) : + m_extractor(extractor), m_prf(prf) + { + if(!m_prf->valid_keylength(m_extractor->output_length()) || + !m_extractor->valid_keylength(m_prf->output_length())) + throw Invalid_Argument("HMAC_RNG: Bad algo combination " + + m_extractor->name() + " and " + + m_prf->name()); + + // First PRF inputs are all zero, as specified in section 2 + m_K.resize(m_prf->output_length()); + + /* + Normally we want to feedback PRF outputs to the extractor function + to ensure a single bad poll does not reduce entropy. Thus in reseed + we'll want to invoke the PRF before we reset the PRF key, but until + the first reseed the PRF is unkeyed. Rather than trying to keep + track of this, just set the initial PRF key to constant zero. + Since all PRF inputs in the first reseed are constants, this + amounts to suffixing the seed in the first poll with a fixed + constant string. + + The PRF key will not be used to generate outputs until after reseed + sets m_seeded to true. + */ + secure_vector<byte> prf_key(m_extractor->output_length()); + m_prf->set_key(prf_key); + + /* + Use PRF("Botan HMAC_RNG XTS") as the intitial XTS key. + + This will be used during the first extraction sequence; XTS values + after this one are generated using the PRF. + + If I understand the E-t-E paper correctly (specifically Section 4), + using this fixed extractor key is safe to do. + */ + m_extractor->set_key(prf->process("Botan HMAC_RNG XTS")); + } + +/* +* Generate a buffer of random bytes +*/ +void HMAC_RNG::randomize(byte out[], size_t length) + { + if(!is_seeded()) + { + reseed(256); + if(!is_seeded()) + throw PRNG_Unseeded(name()); + } + + const size_t max_per_prf_iter = m_prf->output_length() / 2; + + /* + HMAC KDF as described in E-t-E, using a CTXinfo of "rng" + */ + while(length) + { + hmac_prf(*m_prf, m_K, m_counter, "rng"); + + const size_t copied = std::min<size_t>(length, max_per_prf_iter); + + copy_mem(out, &m_K[0], copied); + out += copied; + length -= copied; + + m_output_since_reseed += copied; + + if(m_output_since_reseed >= BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) + reseed(BOTAN_RNG_RESEED_POLL_BITS); + } + } + +/* +* Poll for entropy and reset the internal keys +*/ +void HMAC_RNG::reseed(size_t poll_bits) + { + /* + Using the terminology of E-t-E, XTR is the MAC function (normally + HMAC) seeded with XTS (below) and we form SKM, the key material, by + polling as many sources as we think needed to reach our polling + goal. We then also include feedback of the current PRK so that + a bad poll doesn't wipe us out. + */ + + Entropy_Accumulator_BufferedComputation accum(*m_extractor, poll_bits); + + global_state().poll_available_sources(accum); + + /* + * It is necessary to feed forward poll data. Otherwise, a good poll + * (collecting a large amount of conditional entropy) followed by a + * bad one (collecting little) would be unsafe. Do this by + * generating new PRF outputs using the previous key and feeding + * them into the extractor function. + * + * Cycle the RNG once (CTXinfo="rng"), then generate a new PRF + * output using the CTXinfo "reseed". Provide these values as input + * to the extractor function. + */ + hmac_prf(*m_prf, m_K, m_counter, "rng"); + m_extractor->update(m_K); // K is the CTXinfo=rng PRF output + + hmac_prf(*m_prf, m_K, m_counter, "reseed"); + m_extractor->update(m_K); // K is the CTXinfo=reseed PRF output + + /* Now derive the new PRK using everything that has been fed into + the extractor, and set the PRF key to that */ + m_prf->set_key(m_extractor->final()); + + // Now generate a new PRF output to use as the XTS extractor salt + hmac_prf(*m_prf, m_K, m_counter, "xts"); + m_extractor->set_key(m_K); + + // Reset state + zeroise(m_K); + m_counter = 0; + + m_collected_entropy_estimate = + std::min(m_collected_entropy_estimate + accum.bits_collected(), + m_extractor->output_length() * 8); + + m_output_since_reseed = 0; + } + +bool HMAC_RNG::is_seeded() const + { + return (m_collected_entropy_estimate >= 256); + } + +/* +* Add user-supplied entropy to the extractor input +*/ +void HMAC_RNG::add_entropy(const byte input[], size_t length) + { + m_extractor->update(input, length); + reseed(BOTAN_RNG_RESEED_POLL_BITS); + } + +/* +* Clear memory of sensitive data +*/ +void HMAC_RNG::clear() + { + m_collected_entropy_estimate = 0; + m_extractor->clear(); + m_prf->clear(); + zeroise(m_K); + m_counter = 0; + } + +/* +* Return the name of this type +*/ +std::string HMAC_RNG::name() const + { + return "HMAC_RNG(" + m_extractor->name() + "," + m_prf->name() + ")"; + } + +} diff --git a/src/lib/rng/hmac_rng/hmac_rng.h b/src/lib/rng/hmac_rng/hmac_rng.h new file mode 100644 index 000000000..8fee5be5a --- /dev/null +++ b/src/lib/rng/hmac_rng/hmac_rng.h @@ -0,0 +1,57 @@ +/* +* HMAC RNG +* (C) 2008,2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_HMAC_RNG_H__ +#define BOTAN_HMAC_RNG_H__ + +#include <botan/mac.h> +#include <botan/rng.h> +#include <vector> + +namespace Botan { + +/** +* HMAC_RNG - based on the design described in "On Extract-then-Expand +* Key Derivation Functions and an HMAC-based KDF" by Hugo Krawczyk +* (henceforce, 'E-t-E') +* +* However it actually can be parameterized with any two MAC functions, +* not restricted to HMAC (this variation is also described in +* Krawczyk's paper), for instance one could use HMAC(SHA-512) as the +* extractor and CMAC(AES-256) as the PRF. +*/ +class BOTAN_DLL HMAC_RNG : public RandomNumberGenerator + { + public: + void randomize(byte buf[], size_t len); + bool is_seeded() const; + void clear(); + std::string name() const; + + void reseed(size_t poll_bits); + void add_entropy(const byte[], size_t); + + /** + * @param extractor a MAC used for extracting the entropy + * @param prf a MAC used as a PRF using HKDF construction + */ + HMAC_RNG(MessageAuthenticationCode* extractor, + MessageAuthenticationCode* prf); + private: + std::unique_ptr<MessageAuthenticationCode> m_extractor; + std::unique_ptr<MessageAuthenticationCode> m_prf; + + size_t m_collected_entropy_estimate = 0; + size_t m_output_since_reseed = 0; + + secure_vector<byte> m_K; + u32bit m_counter = 0; + }; + +} + +#endif diff --git a/src/lib/rng/hmac_rng/info.txt b/src/lib/rng/hmac_rng/info.txt new file mode 100644 index 000000000..36a8a7a34 --- /dev/null +++ b/src/lib/rng/hmac_rng/info.txt @@ -0,0 +1,5 @@ +define HMAC_RNG 20131128 + +<requires> +mac +</requires> diff --git a/src/lib/rng/info.txt b/src/lib/rng/info.txt new file mode 100644 index 000000000..4c88ba382 --- /dev/null +++ b/src/lib/rng/info.txt @@ -0,0 +1,3 @@ +<requires> +entropy +</requires> diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp new file mode 100644 index 000000000..12a6c163e --- /dev/null +++ b/src/lib/rng/rng.cpp @@ -0,0 +1,34 @@ +/* +* Random Number Generator Base +* (C) 1999-2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/rng.h> +#include <botan/hmac_rng.h> +#include <botan/libstate.h> + +namespace Botan { + +RandomNumberGenerator* RandomNumberGenerator::make_rng() + { + return make_rng(global_state().algorithm_factory()).release(); + } + +/* +* Create and seed a new RNG object +*/ +std::unique_ptr<RandomNumberGenerator> RandomNumberGenerator::make_rng(Algorithm_Factory& af) + { + std::unique_ptr<RandomNumberGenerator> rng( + new HMAC_RNG(af.make_mac("HMAC(SHA-512)"), + af.make_mac("HMAC(SHA-256)")) + ); + + rng->reseed(256); + + return rng; + } + +} diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h new file mode 100644 index 000000000..8664ccbf7 --- /dev/null +++ b/src/lib/rng/rng.h @@ -0,0 +1,174 @@ +/* +* RandomNumberGenerator +* (C) 1999-2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H__ +#define BOTAN_RANDOM_NUMBER_GENERATOR_H__ + +#include <botan/entropy_src.h> +#include <botan/exceptn.h> +#include <string> +#include <memory> +#include <mutex> + +namespace Botan { + +/** +* This class represents a random number (RNG) generator object. +*/ +class BOTAN_DLL RandomNumberGenerator + { + public: + /** + * Create a seeded and active RNG object for general application use + * Added in 1.8.0 + */ + static RandomNumberGenerator* make_rng(); + + /** + * Create a seeded and active RNG object for general application use + * Added in 1.11.5 + */ + static std::unique_ptr<RandomNumberGenerator> make_rng(class Algorithm_Factory& af); + + /** + * Randomize a byte array. + * @param output the byte array to hold the random output. + * @param length the length of the byte array output. + */ + virtual void randomize(byte output[], size_t length) = 0; + + /** + * Return a random vector + * @param bytes number of bytes in the result + * @return randomized vector of length bytes + */ + secure_vector<byte> random_vec(size_t bytes) + { + secure_vector<byte> output(bytes); + randomize(&output[0], output.size()); + return output; + } + + /** + * Return a random byte + * @return random byte + */ + byte next_byte() + { + byte out; + this->randomize(&out, 1); + return out; + } + + /** + * Check whether this RNG is seeded. + * @return true if this RNG was already seeded, false otherwise. + */ + virtual bool is_seeded() const = 0; + + /** + * Clear all internally held values of this RNG. + */ + virtual void clear() = 0; + + /** + * Return the name of this object + */ + virtual std::string name() const = 0; + + /** + * Seed this RNG using the entropy sources it contains. + * @param bits_to_collect is the number of bits of entropy to + attempt to gather from the entropy sources + */ + virtual void reseed(size_t bits_to_collect) = 0; + + /** + * Add entropy to this RNG. + * @param in a byte array containg the entropy to be added + * @param length the length of the byte array in + */ + virtual void add_entropy(const byte in[], size_t length) = 0; + + /* + * Never copy a RNG, create a new one + */ + RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; + RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; + + RandomNumberGenerator() {} + virtual ~RandomNumberGenerator() {} + }; + +/** +* Null/stub RNG - fails if you try to use it for anything +*/ +class BOTAN_DLL Null_RNG : public RandomNumberGenerator + { + public: + void randomize(byte[], size_t) override { throw PRNG_Unseeded("Null_RNG"); } + + void clear() override {} + + std::string name() const override { return "Null_RNG"; } + + void reseed(size_t) override {} + bool is_seeded() const override { return false; } + void add_entropy(const byte[], size_t) override {} + }; + +/** +* Wraps access to a RNG in a mutex +*/ +class BOTAN_DLL Serialized_RNG : public RandomNumberGenerator + { + public: + void randomize(byte out[], size_t len) + { + std::lock_guard<std::mutex> lock(m_mutex); + m_rng->randomize(out, len); + } + + bool is_seeded() const + { + std::lock_guard<std::mutex> lock(m_mutex); + return m_rng->is_seeded(); + } + + void clear() + { + std::lock_guard<std::mutex> lock(m_mutex); + m_rng->clear(); + } + + std::string name() const + { + std::lock_guard<std::mutex> lock(m_mutex); + return m_rng->name(); + } + + void reseed(size_t poll_bits) + { + std::lock_guard<std::mutex> lock(m_mutex); + m_rng->reseed(poll_bits); + } + + void add_entropy(const byte in[], size_t len) + { + std::lock_guard<std::mutex> lock(m_mutex); + m_rng->add_entropy(in, len); + } + + Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {} + private: + mutable std::mutex m_mutex; + std::unique_ptr<RandomNumberGenerator> m_rng; + }; + +} + +#endif diff --git a/src/lib/rng/x931_rng/info.txt b/src/lib/rng/x931_rng/info.txt new file mode 100644 index 000000000..beab0dbfd --- /dev/null +++ b/src/lib/rng/x931_rng/info.txt @@ -0,0 +1,5 @@ +define X931_RNG 20131128 + +<requires> +block +</requires> diff --git a/src/lib/rng/x931_rng/x931_rng.cpp b/src/lib/rng/x931_rng/x931_rng.cpp new file mode 100644 index 000000000..b36f87106 --- /dev/null +++ b/src/lib/rng/x931_rng/x931_rng.cpp @@ -0,0 +1,146 @@ +/* +* ANSI X9.31 RNG +* (C) 1999-2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/x931_rng.h> +#include <botan/internal/xor_buf.h> +#include <algorithm> + +namespace Botan { + +/* +* Generate a buffer of random bytes +*/ +void ANSI_X931_RNG::randomize(byte out[], size_t length) + { + if(!is_seeded()) + throw PRNG_Unseeded(name()); + + while(length) + { + if(position == R.size()) + update_buffer(); + + const size_t copied = std::min<size_t>(length, R.size() - position); + + copy_mem(out, &R[position], copied); + out += copied; + length -= copied; + position += copied; + } + } + +/* +* Refill the internal state +*/ +void ANSI_X931_RNG::update_buffer() + { + const size_t BLOCK_SIZE = cipher->block_size(); + + secure_vector<byte> DT = prng->random_vec(BLOCK_SIZE); + cipher->encrypt(DT); + + xor_buf(&R[0], &V[0], &DT[0], BLOCK_SIZE); + cipher->encrypt(R); + + xor_buf(&V[0], &R[0], &DT[0], BLOCK_SIZE); + cipher->encrypt(V); + + position = 0; + } + +/* +* Reset V and the cipher key with new values +*/ +void ANSI_X931_RNG::rekey() + { + const size_t BLOCK_SIZE = cipher->block_size(); + + if(prng->is_seeded()) + { + cipher->set_key(prng->random_vec(cipher->maximum_keylength())); + + if(V.size() != BLOCK_SIZE) + V.resize(BLOCK_SIZE); + prng->randomize(&V[0], V.size()); + + update_buffer(); + } + } + +/* +* Reseed the internal state +*/ +void ANSI_X931_RNG::reseed(size_t poll_bits) + { + prng->reseed(poll_bits); + rekey(); + } + +/* +* Add some entropy to the underlying PRNG +*/ +void ANSI_X931_RNG::add_entropy(const byte input[], size_t length) + { + prng->add_entropy(input, length); + rekey(); + } + +/* +* Check if the the PRNG is seeded +*/ +bool ANSI_X931_RNG::is_seeded() const + { + return (V.size() > 0); + } + +/* +* Clear memory of sensitive data +*/ +void ANSI_X931_RNG::clear() + { + cipher->clear(); + prng->clear(); + zeroise(R); + V.clear(); + + position = 0; + } + +/* +* Return the name of this type +*/ +std::string ANSI_X931_RNG::name() const + { + return "X9.31(" + cipher->name() + ")"; + } + +/* +* ANSI X931 RNG Constructor +*/ +ANSI_X931_RNG::ANSI_X931_RNG(BlockCipher* cipher_in, + RandomNumberGenerator* prng_in) + { + if(!prng_in || !cipher_in) + throw Invalid_Argument("ANSI_X931_RNG constructor: NULL arguments"); + + cipher = cipher_in; + prng = prng_in; + + R.resize(cipher->block_size()); + position = 0; + } + +/* +* ANSI X931 RNG Destructor +*/ +ANSI_X931_RNG::~ANSI_X931_RNG() + { + delete cipher; + delete prng; + } + +} diff --git a/src/lib/rng/x931_rng/x931_rng.h b/src/lib/rng/x931_rng/x931_rng.h new file mode 100644 index 000000000..8052cedc3 --- /dev/null +++ b/src/lib/rng/x931_rng/x931_rng.h @@ -0,0 +1,50 @@ +/* +* ANSI X9.31 RNG +* (C) 1999-2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_ANSI_X931_RNG_H__ +#define BOTAN_ANSI_X931_RNG_H__ + +#include <botan/rng.h> +#include <botan/block_cipher.h> + +namespace Botan { + +/** +* ANSI X9.31 RNG +*/ +class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator + { + public: + void randomize(byte[], size_t); + bool is_seeded() const; + void clear(); + std::string name() const; + + void reseed(size_t poll_bits); + void add_entropy(const byte[], size_t); + + /** + * @param cipher the block cipher to use in this PRNG + * @param rng the underlying PRNG for generating inputs + * (eg, an HMAC_RNG) + */ + ANSI_X931_RNG(BlockCipher* cipher, + RandomNumberGenerator* rng); + ~ANSI_X931_RNG(); + private: + void rekey(); + void update_buffer(); + + BlockCipher* cipher; + RandomNumberGenerator* prng; + secure_vector<byte> V, R; + size_t position; + }; + +} + +#endif |