From d4046818898635bd7a4a753cf85a5b2df56344bd Mon Sep 17 00:00:00 2001 From: surtur Date: Mon, 17 Jan 2022 08:27:24 +0100 Subject: [PATCH] feat: "prepare to add proper entropy source" nits general * make greater use of "this" Fortuna * declare da_pools as a proper std::array of 32 Pool objects * declare da_pools as const * use std::shared_ptr _p_pools to access da_pools and share access to it * reflect change of pools[] -> std::array in how the array elements are accessed, which is a) via _p_pools pointer and b) using ".at(i)" function * pass _p_pools shared_ptr to Accumulator * refactor member function names and variable names * add member function attribute [[optimize_for_synchronized]] * secure conversions with static_cast-s Accumulator * make use of _p_pools * add _p_pools-related member functions * add a static constexpr variable NUM_OF_POOLS UrandomEntropySrc * implement event adding logic using _p_pools * make std::vector non-static in urandom_entropy_src * implement proper urandom entropy source event "sourcing" (from /dev/urandom), event adding, clear bytes array at the end * properly convert using reinterpret_cast * protect access to the main function with std::lock_guard * receive EventAdderImpl as a ref * use return value from "add_entropy()" member function and create sanity guard checking the return code "int ret" EventAdder * pass event (std::vector) by const& EventAdderImpl * make use of _p_pools shared_ptr * implement proper pool-rotating event-adding logic Pool * delete all copy constructors and assignment operator, the objects will not be copied or assigned to * receive parameters by const& where possible/sensible * handle concurrency: * declare std:string s as mutable * declare a rw std::mutex intended for writing and mutable std::recursive_mutex for read-only operations in const member functions ref: https://herbsutter.com/2013/05/24/gotw-6a-const-correctness-part-1-3/ ref: https://arne-mertz.de/2017/10/mutable/ * use std::lock_guard and std::unique_lock * refactor "add_entropy()" member function * get rid of intermediate "event_str" and directly use the "event" std::vector for all operations * add a lock guard to prevent multiple threads (should that route be taken) from modifying pool resources simultaneously * add all_ok bool for basic sanity checking * add print statements (at least for now) * rename "get_s_length()" member function to "get_s_byte_count()" and repurpose it to return byte count of the stored entropy std::string s --- accumulator.cpp | 19 +++++++ accumulator.h | 19 ++++++- event_adder.h | 2 +- event_adder_impl.h | 42 ++++++++++----- fortuna.cpp | 13 +++-- fortuna.h | 30 +++++++---- pool.cpp | 116 ++++++++++++++++++++++++++++++---------- pool.h | 16 ++++-- urandom_entropy_src.cpp | 47 ++++++++++++---- urandom_entropy_src.h | 12 +++-- 10 files changed, 239 insertions(+), 77 deletions(-) diff --git a/accumulator.cpp b/accumulator.cpp index 059a4df..4cc0e21 100644 --- a/accumulator.cpp +++ b/accumulator.cpp @@ -2,12 +2,15 @@ #define FORTUNA_ACCUMULATOR_CPP #include "accumulator.h" +#include "pool.h" #include #include +#include #include #include +#include #include #include @@ -20,10 +23,20 @@ auto wait_for(const unsigned int& milliseconds) -> void { Accumulator::Accumulator() noexcept { this->Gen = nullptr; + this->_p_pools = nullptr; } Accumulator::~Accumulator() noexcept {} +auto Accumulator::_p_pools_equal( + std::shared_ptr> + p_pools) const -> bool { + if (this->_p_pools == p_pools) { + return true; + } + return false; +} + // check if given source id is an id of an already registered entropy source auto Accumulator::src_is_registered(const uint8_t& id) -> bool { bool reg{false}; @@ -46,6 +59,12 @@ auto Accumulator::src_is_registered(const uint8_t& id) -> bool { return false; } +auto Accumulator::set_pools_ptr( + std::shared_ptr> + p_pools) -> void { + this->_p_pools = p_pools; +} + auto Accumulator::set_gen(fortuna::generator::Generator& gen) -> void { // this->Gen = std::move(&gen); // TODO(me): does this make sense? this->Gen = &gen; diff --git a/accumulator.h b/accumulator.h index c772f2d..ceeafcb 100644 --- a/accumulator.h +++ b/accumulator.h @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #include namespace fortuna { @@ -18,8 +20,12 @@ namespace accumulator { class Accumulator { private: static constexpr const uint8_t MAX_SOURCES{255}; + static constexpr const uint8_t NUM_OF_POOLS{32}; + std::vector entropy_sources{}; fortuna::generator::Generator* Gen; + std::shared_ptr> + _p_pools; protected: unsigned int src_count{0}; @@ -27,6 +33,11 @@ protected: public: constexpr static const unsigned int init_pool_num{0}; + auto _p_pools_equal( + std::shared_ptr> p_pools) const + -> bool; + [[maybe_unused]] auto add_source() -> void { static unsigned int src_id{this->src_count}; // make really sure we don't add a duplicate src_id @@ -35,8 +46,7 @@ public: try { entropy_sources.push_back(static_cast(src_id)); ++src_count; - accumulator::Pool pools[32]; - EventAdderImpl event_adder(src_id, pools); + EventAdderImpl event_adder(src_id, this->_p_pools); [[maybe_unused]] bool scheduled; } catch (std::exception& e) { @@ -47,6 +57,11 @@ public: [[maybe_unused]] auto src_is_registered(const uint8_t& id) -> bool; + auto set_pools_ptr( + std::shared_ptr< + std::array> p_pools) + -> void; + auto set_gen(fortuna::generator::Generator& Gen) -> void; auto get_random_data(const unsigned int& n_bytes) -> std::string; diff --git a/event_adder.h b/event_adder.h index b90fbd3..69d2266 100644 --- a/event_adder.h +++ b/event_adder.h @@ -8,7 +8,7 @@ namespace accumulator { class EventAdder { public: - virtual void add(std::vector e) = 0; + virtual void add(const std::vector& e) = 0; }; } // namespace accumulator diff --git a/event_adder_impl.h b/event_adder_impl.h index 0c429ad..e563e40 100644 --- a/event_adder_impl.h +++ b/event_adder_impl.h @@ -4,33 +4,51 @@ #include "event_adder.h" #include "pool.h" +#include + #include +#include +#include namespace fortuna { namespace accumulator { class EventAdderImpl final : public EventAdder { -public: - static constexpr const uint8_t p_size{32}; - private: uint8_t pool_id; - static unsigned int _source_id; - static accumulator::Pool _pools[p_size]; + uint64_t where_to{0}; + unsigned int _source_id; + std::shared_ptr< + std::array> + _pools; public: - EventAdderImpl(const unsigned int source_id, - const accumulator::Pool pools[p_size]) { + EventAdderImpl( + const unsigned int source_id, + std::shared_ptr> pools) { this->_source_id = source_id; - for (uint8_t i{0}; i < this->p_size; i++) { - this->_pools[i] = pools[i]; + if (pools) { + this->_pools = pools; + } + else { + throw std::runtime_error( + "[!] adder: pools pointer no longer valid\n"); } this->pool_id = 0; } - void add(std::vector event) override { - this->pool_id = static_cast((this->pool_id + 1) % p_size); - _pools[this->pool_id].add_entropy(_source_id, event); + void add(const std::vector& event) override { + this->pool_id = (this->where_to) % 32; + fmt::print("[i] add to pool {}\n", this->pool_id); + int ret{this->_pools->at(this->pool_id) + .add_entropy(this->_source_id, event)}; + if (ret == 1) { + throw std::runtime_error( + "\t[!] event_adder: error adding event!\n"); + } + // FIXME: this WILL overflow, too + ++where_to; } }; diff --git a/fortuna.cpp b/fortuna.cpp index fd52e37..46cc20d 100644 --- a/fortuna.cpp +++ b/fortuna.cpp @@ -58,16 +58,19 @@ auto Fortuna::random_data(unsigned int n_bytes) -> void { const int pools_to_use{ffsll(static_cast(get_reseed_ctr()))}; fmt::print("[*] fortuna: current p0 length: {}\n", - R.pools[0].get_s_length()); - if (R.Gen.time_to_reseed(R.pools[0].get_s_length(), + this->R._p_pools->at(0).get_s_byte_count()); + if (R.Gen.time_to_reseed(R._p_pools->at(0).get_s_byte_count(), min_pool_size, elapsed, R.Gen.reseed_interval)) { for (int i = 0; i < static_cast(pools_to_use); ++i) { - if (R.reseed_ctr % static_cast(pow(2, i)) == 0) { + if (this->R.reseed_ctr % + static_cast(pow(2, static_cast(i))) == + 0) { try { - s.append(fortuna::Util::do_sha(R.pools[i].get_s())); - R.pools[i].clear_pool(); + s.append( + fortuna::Util::do_sha(this->R._p_pools->at(i).get_s())); + this->R._p_pools->at(i).clear_pool(); } catch (std::exception& e) { fmt::print("{}\n", e.what()); diff --git a/fortuna.h b/fortuna.h index e273c8f..4f646eb 100644 --- a/fortuna.h +++ b/fortuna.h @@ -3,9 +3,11 @@ #include "accumulator.h" #include "generator.h" +#include "pool.h" #include +#include #include #include #include @@ -15,9 +17,7 @@ namespace fortuna { class Fortuna { public: - // in microseconds - static constexpr const unsigned int reseed_interval{10000}; - static constexpr const char num_of_pools{32}; + static constexpr const char NUM_OF_POOLS{32}; std::mutex mtx; std::mutex accu_mtx; std::mutex print_mtx; @@ -28,7 +28,7 @@ public: Fortuna(); ~Fortuna() noexcept; - auto random_data(unsigned int) -> void; + [[optimize_for_synchronized]] auto random_data(unsigned int) -> void; auto set_reseed_ctr_to_null() -> void { std::lock_guard lg(mtx); @@ -52,18 +52,20 @@ public: std::unique_lock a_ul(accu_mtx); try { R.initialize_pools(); - a_ul.unlock(); fmt::print("[i] fortuna: pools initialized\n"); p_ul.unlock(); - a_ul.lock(); + accumulator.set_pools_ptr(R._p_pools); accumulator.set_gen(R.Gen); a_ul.unlock(); + // FIXME: bogus first reseed here, P_0 definitely hasn't collected // enough entropy by now incr_reseed_ctr(); + p_ul.lock(); fmt::print("first reseed\n"); p_ul.unlock(); + std::unique_lock ul(mtx); R.Gen.reseed("fortuna"); ul.unlock(); @@ -103,16 +105,26 @@ public: auto initialize_pools() -> void { for (unsigned int i = accumulator::Accumulator::init_pool_num; - i < num_of_pools; + i < Fortuna::NUM_OF_POOLS; ++i) { - pools[i].initialize_pool(i); + this->_p_pools->at(i).initialize_pool(i); } } private: generator::Generator Gen; uint64_t reseed_ctr{0x00}; - accumulator::Pool pools[num_of_pools]; + + // da_pools is to be used solely for creating a shared_ptr of the + // object: _p_pools. any further access to the structure should be + // facilitated by the subject _p_pools shared_ptr. to convey this, + // da_pools is declared const + const std::array da_pools; + + // _p_pools points to the da_pools array of 32 Pool objects + std::shared_ptr> + _p_pools{std::make_shared< + std::array>()}; std::chrono::steady_clock::time_point last_reseed; }; // class R_state diff --git a/pool.cpp b/pool.cpp index ed6ff1d..7615a7a 100644 --- a/pool.cpp +++ b/pool.cpp @@ -5,6 +5,12 @@ #include +#include +#include +#include +#include +#include + namespace fortuna { namespace accumulator { @@ -22,62 +28,116 @@ auto Pool::initialize_pool(const unsigned int& id) -> void { fmt::print("\tid set to: {}\n", this->get_id()); } -auto Pool::add_entropy(const uint source, const std::vector& event) - -> int { +auto Pool::add_entropy(const unsigned int& source, + const std::vector& event) -> int { + const size_t event_size{sizeof(char) * event.size()}; + const size_t max_event_size{32}; std::string event_str; - const size_t event_size{event.size()}; + bool all_ok{false}; + fmt::print("\tevent_size (bytes): {}\n", event_size); try { if (source > 255) { throw std::invalid_argument( "source number outside of interval <0,255>\n"); } - if (event_size < 1 || event_size > 32) { - throw std::invalid_argument("the length of the event needs to " - "be from the interval <1,32>\n"); + fmt::print("\tsource_id: {}\n", source); + + if (event_size < 1 || event_size > max_event_size) { + const std::string msg{ + "[!] add_entropy: the length of the event needs to " + "be from the interval <1,32>"}; + fmt::print("\tsource: {}\n{}\nevent size: {}\n\tpool_id: {}", + source, + msg, + event_size, + this->pool_id); + throw std::invalid_argument(msg); + } + else { + all_ok = true; } } catch (const std::exception& e) { - fmt::print("{}", e.what()); + fmt::print("{}\n", e.what()); } + fmt::print("\tall_ok: {}\n", all_ok); - try { - event_str = std::string(event.begin(), event.end()); - } - catch (const std::exception& e) { - fmt::print("{}", e.what()); - } + if (all_ok) { + try { + // FIXME: check for overflow - std::string size bounding? + event_str.assign(event.begin(), event.end()); - try { - std::string digest(fortuna::Util::do_sha(event_str)); - size += event_size; - append_s(digest); - digest.clear(); - } - catch (const std::exception& e) { - fmt::print("{}", e.what()); - } + fmt::print("\tevent_str length: {}\n", event_str.length()); + } + catch (const std::exception& e) { + fmt::print("\t[!] add_entropy: {}\n", e.what()); + throw std::runtime_error(e.what()); + } - return 0; + try { + // FIXME: check size for overflow + // also, atm this counts event size but actually what gets appended + // are digests of 64 characters (hex-encoded 32 bytes) + size += event_size; + + { + const std::string digest(fortuna::Util::do_sha(event)); + + fmt::print("\t[i] add_entropy(digest): {}\n", digest); + + append_s(digest); + } + + std::unique_lock ul(ro_mtx_s); + fmt::print("\t[i] s.length() (simple char count): {}\n" + "\t[i] get_s_byte_count() (byte count): {}\n" + "\t[i] pool_id: {}\n" + "\t[i] s: {}\n", + this->s.length(), + this->get_s_byte_count(), + this->pool_id, + // ""); + this->s); + ul.unlock(); + } + catch (const std::exception& e) { + fmt::print("[!] pool(add_entropy): {}\n", e.what()); + } + + return 0; + } + else { + // everything not ok + return 1; + } } // add_entropy -auto Pool::get_s_length() const -> uint64_t { - // returns total length of entropy contained in this pool - return this->s.length(); +auto Pool::get_s_byte_count() const -> uint64_t { + // returns total byte count of (hex-encoded) entropy string contained in + // the pool. since the string is hex-encoded, we divide its char count by 2 + // to get proper byte count. we need this since we actually don't care about + // the length of the hex string as it is, rather we care about what it came + // from (the raw entropy event) + + std::lock_guard lg(ro_mtx_s); + return this->s.length() / 2; } auto Pool::get_s() const -> std::string { + std::lock_guard lg(ro_mtx_s); return this->s; } auto Pool::clear_pool() -> void { + std::lock_guard lg(mtx); this->s.clear(); } - auto Pool::append_s(const std::string& entropy_s) -> void { + std::lock_guard lg(mtx); try { - (this->s).append(std::string(entropy_s)); + this->s.append(entropy_s); } catch (const std::exception& e) { fmt::print("{}\n", e.what()); diff --git a/pool.h b/pool.h index 96d7f55..d273129 100644 --- a/pool.h +++ b/pool.h @@ -3,14 +3,19 @@ #include "util.h" +#include namespace fortuna { namespace accumulator { class Pool { public: + static constexpr const uint8_t NUM_OF_POOLS{32}; + Pool() noexcept {}; - Pool(const Pool& pool) = delete; // no copy + Pool(const Pool&) = delete; // no copy + Pool(Pool&) = delete; + Pool& operator=(const Pool&) = delete; ~Pool() noexcept = default; auto set_id(const unsigned int& id) noexcept -> void; @@ -20,9 +25,10 @@ public: auto initialize_pool(const unsigned int& id) -> void; // add entropy contained in a random event of 1 to 32 bytes - auto add_entropy(const uint source, const std::vector& event) -> int; + auto add_entropy(const unsigned int& source, const std::vector& event) + -> int; - auto get_s_length() const -> uint64_t; + auto get_s_byte_count() const -> uint64_t; auto get_s() const -> std::string; @@ -33,8 +39,10 @@ protected: private: unsigned int pool_id{0}; - std::string s{""}; + mutable std::string s{""}; uint64_t size{0}; + std::mutex mtx; // mutex for write locks + mutable std::recursive_mutex ro_mtx_s; // mtx for readers of s }; // class Pool diff --git a/urandom_entropy_src.cpp b/urandom_entropy_src.cpp index c311cb5..349c632 100644 --- a/urandom_entropy_src.cpp +++ b/urandom_entropy_src.cpp @@ -2,42 +2,67 @@ #define FORTUNA_URANDOM_ENTROPY_SRC_CPP #include "urandom_entropy_src.h" +#include "event_adder_impl.h" +#include +#include #include +#include +#include +#include +#include namespace fortuna { namespace accumulator { -std::vector UrandomEntropySrc::bytes{}; +auto UrandomEntropySrc::event(accumulator::EventAdderImpl& adder) -> void { + std::lock_guard lg(mtx); -auto UrandomEntropySrc::schedule(accumulator::EventSchedulerImpl scheduler) - -> void { - scheduler.schedule(std::chrono::milliseconds(100)); -} - -auto UrandomEntropySrc::event(accumulator::EventAdderImpl adder) -> void { std::ifstream file; file.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { std::ifstream urandom("/dev/urandom", std::ios::in | std::ios::binary); + // how much to get at once + static constexpr const unsigned int this_many_bytes{32}; + + this->bytes.resize(this_many_bytes); + + assert((bytes.size()) == this_many_bytes); + // check if stream is open if (urandom) { - urandom.read( - reinterpret_cast(&UrandomEntropySrc::bytes), - static_cast(reinterpret_cast(UrandomEntropySrc::bytes.size()))); + fmt::print(stderr, "[i] ues: /dev/urandom stream open\n"); + + std::unique_lock ul; + urandom.read(reinterpret_cast(&this->bytes.front()), + this_many_bytes); urandom.close(); } else { // open failed - fmt::print(stderr, "Failed to open /dev/urandom\n"); + const std::string msg{"\t[!] Failed to open /dev/urandom\n"}; + + fmt::print(stderr, "{}", msg); + throw std::runtime_error(msg); } } catch (std::ifstream::failure& e) { fmt::print("io exception caugth: {}\n", e.what()); } + + try { + adder.add(this->bytes); + std::memset(this->bytes.data(), 0x00, this->bytes.size()); // clear out + } + catch (std::exception& e) { + fmt::print("[!] ues: exception caugth: {}\n", e.what()); + } } +UrandomEntropySrc::UrandomEntropySrc() : EntropySrc::EntropySrc() {} +UrandomEntropySrc::~UrandomEntropySrc() noexcept {} + } // namespace accumulator } // namespace fortuna diff --git a/urandom_entropy_src.h b/urandom_entropy_src.h index aa57d87..44028c1 100644 --- a/urandom_entropy_src.h +++ b/urandom_entropy_src.h @@ -3,13 +3,13 @@ #include "entropy_src.h" #include "event_adder_impl.h" -#include "event_scheduler_impl.h" #include #include #include #include +#include #include namespace fortuna { @@ -17,14 +17,16 @@ namespace accumulator { class UrandomEntropySrc : public EntropySrc { private: - static std::vector bytes; + std::vector bytes{0}; + std::mutex mtx; public: - auto schedule(accumulator::EventSchedulerImpl scheduler) -> void; - - auto event(accumulator::EventAdderImpl adder) -> void; + auto event(accumulator::EventAdderImpl& adder) -> void; auto urandom_entropy_src_service() -> int; + + UrandomEntropySrc(); + ~UrandomEntropySrc() noexcept; }; } // namespace accumulator