summaryrefslogtreecommitdiffstats
path: root/botan/src/rng/randpool/randpool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'botan/src/rng/randpool/randpool.cpp')
-rw-r--r--botan/src/rng/randpool/randpool.cpp214
1 files changed, 214 insertions, 0 deletions
diff --git a/botan/src/rng/randpool/randpool.cpp b/botan/src/rng/randpool/randpool.cpp
new file mode 100644
index 0000000..4d7b92d
--- /dev/null
+++ b/botan/src/rng/randpool/randpool.cpp
@@ -0,0 +1,214 @@
+/*
+* Randpool
+* (C) 1999-2009 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/randpool.h>
+#include <botan/loadstor.h>
+#include <botan/xor_buf.h>
+#include <botan/util.h>
+#include <botan/stl_util.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/**
+* PRF based on a MAC
+*/
+enum RANDPOOL_PRF_TAG {
+ CIPHER_KEY = 0,
+ MAC_KEY = 1,
+ GEN_OUTPUT = 2
+};
+
+}
+
+/**
+* Generate a buffer of random bytes
+*/
+void Randpool::randomize(byte out[], u32bit length)
+ {
+ if(!is_seeded())
+ throw PRNG_Unseeded(name());
+
+ update_buffer();
+ while(length)
+ {
+ const u32bit copied = std::min(length, buffer.size());
+ copy_mem(out, buffer.begin(), copied);
+ out += copied;
+ length -= copied;
+ update_buffer();
+ }
+ }
+
+/**
+* Refill the output buffer
+*/
+void Randpool::update_buffer()
+ {
+ const u64bit timestamp = system_time();
+
+ for(u32bit i = 0; i != counter.size(); ++i)
+ if(++counter[i])
+ break;
+ store_be(timestamp, counter + 4);
+
+ mac->update(static_cast<byte>(GEN_OUTPUT));
+ mac->update(counter, counter.size());
+ SecureVector<byte> mac_val = mac->final();
+
+ for(u32bit i = 0; i != mac_val.size(); ++i)
+ buffer[i % buffer.size()] ^= mac_val[i];
+ cipher->encrypt(buffer);
+
+ if(counter[0] % ITERATIONS_BEFORE_RESEED == 0)
+ mix_pool();
+ }
+
+/**
+* Mix the entropy pool
+*/
+void Randpool::mix_pool()
+ {
+ const u32bit BLOCK_SIZE = cipher->BLOCK_SIZE;
+
+ mac->update(static_cast<byte>(MAC_KEY));
+ mac->update(pool, pool.size());
+ mac->set_key(mac->final());
+
+ mac->update(static_cast<byte>(CIPHER_KEY));
+ mac->update(pool, pool.size());
+ cipher->set_key(mac->final());
+
+ xor_buf(pool, buffer, BLOCK_SIZE);
+ cipher->encrypt(pool);
+ for(u32bit i = 1; i != POOL_BLOCKS; ++i)
+ {
+ const byte* previous_block = pool + BLOCK_SIZE*(i-1);
+ byte* this_block = pool + BLOCK_SIZE*i;
+ xor_buf(this_block, previous_block, BLOCK_SIZE);
+ cipher->encrypt(this_block);
+ }
+
+ update_buffer();
+ }
+
+/**
+* Reseed the internal state
+*/
+void Randpool::reseed(u32bit poll_bits)
+ {
+ Entropy_Accumulator_BufferedComputation accum(*mac, poll_bits);
+
+ if(!entropy_sources.empty())
+ {
+ u32bit poll_attempt = 0;
+
+ while(!accum.polling_goal_achieved() && poll_attempt < poll_bits)
+ {
+ entropy_sources[poll_attempt % entropy_sources.size()]->poll(accum);
+ ++poll_attempt;
+ }
+ }
+
+ SecureVector<byte> mac_val = mac->final();
+
+ xor_buf(pool, mac_val, mac_val.size());
+ mix_pool();
+
+ if(accum.bits_collected() >= poll_bits)
+ seeded = true;
+ }
+
+/**
+* Add user-supplied entropy
+*/
+void Randpool::add_entropy(const byte input[], u32bit length)
+ {
+ SecureVector<byte> mac_val = mac->process(input, length);
+ xor_buf(pool, mac_val, mac_val.size());
+ mix_pool();
+
+ if(length)
+ seeded = true;
+ }
+
+/**
+* Add another entropy source to the list
+*/
+void Randpool::add_entropy_source(EntropySource* src)
+ {
+ entropy_sources.push_back(src);
+ }
+
+/**
+* Clear memory of sensitive data
+*/
+void Randpool::clear() throw()
+ {
+ cipher->clear();
+ mac->clear();
+ pool.clear();
+ buffer.clear();
+ counter.clear();
+ seeded = false;
+ }
+
+/**
+* Return the name of this type
+*/
+std::string Randpool::name() const
+ {
+ return "Randpool(" + cipher->name() + "," + mac->name() + ")";
+ }
+
+/**
+* Randpool Constructor
+*/
+Randpool::Randpool(BlockCipher* cipher_in,
+ MessageAuthenticationCode* mac_in,
+ u32bit pool_blocks,
+ u32bit iter_before_reseed) :
+ ITERATIONS_BEFORE_RESEED(iter_before_reseed),
+ POOL_BLOCKS(pool_blocks),
+ cipher(cipher_in),
+ mac(mac_in)
+ {
+ const u32bit BLOCK_SIZE = cipher->BLOCK_SIZE;
+ const u32bit OUTPUT_LENGTH = mac->OUTPUT_LENGTH;
+
+ if(OUTPUT_LENGTH < BLOCK_SIZE ||
+ !cipher->valid_keylength(OUTPUT_LENGTH) ||
+ !mac->valid_keylength(OUTPUT_LENGTH))
+ {
+ std::string ciphername = cipher->name(), macname = mac->name();
+ delete cipher;
+ delete mac;
+ throw Internal_Error("Randpool: Invalid algorithm combination " +
+ ciphername + "/" + macname);
+ }
+
+ buffer.create(BLOCK_SIZE);
+ pool.create(POOL_BLOCKS * BLOCK_SIZE);
+ counter.create(12);
+ seeded = false;
+ }
+
+/**
+* Randpool Destructor
+*/
+Randpool::~Randpool()
+ {
+ delete cipher;
+ delete mac;
+
+ std::for_each(entropy_sources.begin(), entropy_sources.end(),
+ del_fun<EntropySource>());
+ }
+
+}