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<char> 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<char>) 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<char> 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
This commit is contained in:
surtur 2022-01-17 08:27:24 +01:00
parent e899c03da2
commit d404681889
Signed by: wanderer
GPG Key ID: 19CE1EC1D9E0486D
10 changed files with 239 additions and 77 deletions

View File

@ -2,12 +2,15 @@
#define FORTUNA_ACCUMULATOR_CPP
#include "accumulator.h"
#include "pool.h"
#include <fmt/core.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <memory>
#include <thread>
#include <vector>
@ -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<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
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<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
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;

View File

@ -8,8 +8,10 @@
#include <fmt/core.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <exception>
#include <memory>
#include <vector>
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<uint8_t> entropy_sources{};
fortuna::generator::Generator* Gen;
std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
_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<std::array<accumulator::Pool,
Accumulator::NUM_OF_POOLS>> 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<uint8_t>(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<accumulator::Pool, Accumulator::NUM_OF_POOLS>> p_pools)
-> void;
auto set_gen(fortuna::generator::Generator& Gen) -> void;
auto get_random_data(const unsigned int& n_bytes) -> std::string;

View File

@ -8,7 +8,7 @@ namespace accumulator {
class EventAdder {
public:
virtual void add(std::vector<char> e) = 0;
virtual void add(const std::vector<char>& e) = 0;
};
} // namespace accumulator

View File

@ -4,33 +4,51 @@
#include "event_adder.h"
#include "pool.h"
#include <fmt/core.h>
#include <cstdint>
#include <memory>
#include <stdexcept>
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<accumulator::Pool, accumulator::Pool::NUM_OF_POOLS>>
_pools;
public:
EventAdderImpl(const unsigned int source_id,
const accumulator::Pool pools[p_size]) {
EventAdderImpl(
const unsigned int source_id,
std::shared_ptr<std::array<accumulator::Pool,
accumulator::Pool::NUM_OF_POOLS>> 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<char> event) override {
this->pool_id = static_cast<uint8_t>((this->pool_id + 1) % p_size);
_pools[this->pool_id].add_entropy(_source_id, event);
void add(const std::vector<char>& 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;
}
};

View File

@ -58,16 +58,19 @@ auto Fortuna::random_data(unsigned int n_bytes) -> void {
const int pools_to_use{ffsll(static_cast<int>(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<int>(pools_to_use); ++i) {
if (R.reseed_ctr % static_cast<int>(pow(2, i)) == 0) {
if (this->R.reseed_ctr %
static_cast<int>(pow(2, static_cast<double>(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());

View File

@ -3,9 +3,11 @@
#include "accumulator.h"
#include "generator.h"
#include "pool.h"
#include <fmt/core.h>
#include <array>
#include <chrono>
#include <memory>
#include <mutex>
@ -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<std::mutex> lg(mtx);
@ -52,18 +52,20 @@ public:
std::unique_lock<std::mutex> 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<std::mutex> 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<accumulator::Pool, Fortuna::NUM_OF_POOLS> da_pools;
// _p_pools points to the da_pools array of 32 Pool objects
std::shared_ptr<std::array<accumulator::Pool, Fortuna::NUM_OF_POOLS>>
_p_pools{std::make_shared<
std::array<accumulator::Pool, Fortuna::NUM_OF_POOLS>>()};
std::chrono::steady_clock::time_point last_reseed;
}; // class R_state

116
pool.cpp
View File

@ -5,6 +5,12 @@
#include <fmt/core.h>
#include <climits>
#include <cstddef>
#include <mutex>
#include <stdexcept>
#include <utility>
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<char>& event)
-> int {
auto Pool::add_entropy(const unsigned int& source,
const std::vector<char>& 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<std::recursive_mutex> 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<std::recursive_mutex> lg(ro_mtx_s);
return this->s.length() / 2;
}
auto Pool::get_s() const -> std::string {
std::lock_guard<std::recursive_mutex> lg(ro_mtx_s);
return this->s;
}
auto Pool::clear_pool() -> void {
std::lock_guard<std::mutex> lg(mtx);
this->s.clear();
}
auto Pool::append_s(const std::string& entropy_s) -> void {
std::lock_guard<std::mutex> 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());

16
pool.h
View File

@ -3,14 +3,19 @@
#include "util.h"
#include <mutex>
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<char>& event) -> int;
auto add_entropy(const unsigned int& source, const std::vector<char>& 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

View File

@ -2,42 +2,67 @@
#define FORTUNA_URANDOM_ENTROPY_SRC_CPP
#include "urandom_entropy_src.h"
#include "event_adder_impl.h"
#include <cassert>
#include <climits>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <stdexcept>
#include <vector>
namespace fortuna {
namespace accumulator {
std::vector<char> UrandomEntropySrc::bytes{};
auto UrandomEntropySrc::event(accumulator::EventAdderImpl& adder) -> void {
std::lock_guard<std::mutex> 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<char*>(&UrandomEntropySrc::bytes),
static_cast<int64_t>(reinterpret_cast<uint64_t>(UrandomEntropySrc::bytes.size())));
fmt::print(stderr, "[i] ues: /dev/urandom stream open\n");
std::unique_lock<std::mutex> ul;
urandom.read(reinterpret_cast<char*>(&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

View File

@ -3,13 +3,13 @@
#include "entropy_src.h"
#include "event_adder_impl.h"
#include "event_scheduler_impl.h"
#include <fmt/core.h>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <mutex>
#include <vector>
namespace fortuna {
@ -17,14 +17,16 @@ namespace accumulator {
class UrandomEntropySrc : public EntropySrc {
private:
static std::vector<char> bytes;
std::vector<char> 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