fortuna: properly handle reseeds

* move reseed_ctr and related member functions to Accumulator

* create a std::shared_ptr<Accumulator> to Fortuna's internal
  Accumulator object and feed that into SeedFileManager instead of a
  reference, which used to get copied to a new object in SeedFileManager

* make Accumulator non-copyable, since it's only to be created once.
  instead, a shared_ptr is used to facilitate multiple-access

* handle concurrency in Accumulator as the reseed_ctr-related functions
  can now be accessed from both Fortuna and SeedFileManager, declare mtx
  as mutable (since it's also used in a const function)

* use std::scoped_lock in 'initialize_prng()' to safely lock both mutexes
This commit is contained in:
surtur 2022-01-22 18:36:51 +01:00
parent 760fd1bd9c
commit 52de785399
Signed by: wanderer
GPG Key ID: 19CE1EC1D9E0486D
6 changed files with 68 additions and 49 deletions

View File

@ -28,6 +28,21 @@ Accumulator::Accumulator() noexcept {
Accumulator::~Accumulator() noexcept {}
auto Accumulator::set_reseed_ctr_to_null() -> void {
std::lock_guard<std::mutex> lg(mtx);
this->reseed_ctr = 0x00;
}
auto Accumulator::incr_reseed_ctr() -> void {
std::lock_guard<std::mutex> lg(mtx);
{ ++this->reseed_ctr; }
}
auto Accumulator::get_reseed_ctr() const -> uint64_t {
std::lock_guard<std::mutex> lg(mtx);
return this->reseed_ctr;
}
auto Accumulator::_p_pools_equal(
std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
p_pools) const -> bool {
@ -84,6 +99,7 @@ auto Accumulator::get_random_data(const unsigned int& n_bytes) -> std::string {
auto Accumulator::call_reseed(const std::string& seed) -> void {
try {
incr_reseed_ctr();
this->Gen->reseed(seed);
}
catch (std::exception& e) {

View File

@ -12,6 +12,7 @@
#include <cstdint>
#include <exception>
#include <memory>
#include <mutex>
#include <vector>
namespace fortuna {
@ -22,6 +23,9 @@ private:
static constexpr const uint8_t MAX_SOURCES{255};
static constexpr const uint8_t NUM_OF_POOLS{32};
mutable std::mutex mtx; // used in const fun
uint64_t reseed_ctr{0x00};
std::vector<uint8_t> entropy_sources{};
fortuna::generator::Generator* Gen;
std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
@ -33,6 +37,12 @@ protected:
public:
constexpr static const unsigned int init_pool_num{0};
auto set_reseed_ctr_to_null() -> void;
auto incr_reseed_ctr() -> void;
auto get_reseed_ctr() const -> uint64_t;
auto _p_pools_equal(
std::shared_ptr<std::array<accumulator::Pool,
Accumulator::NUM_OF_POOLS>> p_pools) const
@ -82,6 +92,9 @@ public:
Accumulator() noexcept;
~Accumulator() noexcept;
Accumulator(const Accumulator&) = delete; // no copy
Accumulator(Accumulator&) = delete;
Accumulator& operator=(const Accumulator&) = delete;
}; // class Accumulator

View File

@ -86,7 +86,7 @@ auto Fortuna::random_data(unsigned int n_bytes) -> void {
// if (R.pools[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 (this->R.reseed_ctr %
if (this->get_reseed_ctr() %
static_cast<uint64_t>(pow(2, static_cast<double>(i))) ==
0) {
s.append(fortuna::Util::do_sha(
@ -101,8 +101,8 @@ auto Fortuna::random_data(unsigned int n_bytes) -> void {
s.clear();
}
fmt::print("[i] fortuna: reseed ctr {}\n", R.reseed_ctr);
if (R.reseed_ctr == 0) {
fmt::print("[i] fortuna: reseed ctr {}\n", get_reseed_ctr());
if (get_reseed_ctr() == 0) {
fmt::print("[!] ERROR: reseed ctr is 0, PRNG not seeded!\n");
throw std::runtime_error("illegal state, PRNG not seeded");
}
@ -179,9 +179,9 @@ auto Fortuna::seed_file_manager_service() -> void {
std::unique_lock<std::mutex> mtx_l(mtx);
std::unique_lock<std::mutex> a_ul(mtx_accu);
assert(this->accumulator._p_pools_equal(this->R._p_pools));
assert(this->_p_accumulator->_p_pools_equal(this->R._p_pools));
SeedFileManager sfm(this->accumulator);
SeedFileManager sfm(this->_p_accumulator);
a_ul.unlock();
mtx_l.unlock();
@ -244,7 +244,7 @@ auto Fortuna::urandom_entropy_src_service() -> void {
if (this->R._p_pools) {
// make sure they're pointing to the same chunk of data
// I know, debug-only
assert(this->accumulator._p_pools_equal(this->R._p_pools));
assert(this->_p_accumulator->_p_pools_equal(this->R._p_pools));
ues.event(adder);
}

View File

@ -22,7 +22,7 @@ public:
std::mutex mtx;
std::mutex mtx_random_data;
std::mutex mtx_p_pools;
std::mutex mtx_accu;
mutable std::mutex mtx_accu; // used in const fun, too
std::mutex print_mtx;
std::thread th_gen;
std::thread th_accu;
@ -36,53 +36,47 @@ public:
[[optimize_for_synchronized]] auto random_data(unsigned int) -> void;
auto set_reseed_ctr_to_null() -> void {
std::lock_guard<std::mutex> lg(mtx);
Fortuna::R.null_da_ctr();
std::scoped_lock sl(mtx_accu, print_mtx);
this->_p_accumulator->set_reseed_ctr_to_null();
fmt::print("reseed_ctr set to 0x00\n");
}
auto incr_reseed_ctr() -> void {
std::lock_guard<std::mutex> lg(mtx);
++Fortuna::R.reseed_ctr;
std::scoped_lock sl(mtx, mtx_accu);
this->_p_accumulator->incr_reseed_ctr();
}
auto get_reseed_ctr() const -> uint64_t {
return R.reseed_ctr;
std::lock_guard<std::mutex> lg(mtx_accu);
return this->_p_accumulator->get_reseed_ctr();
}
auto initialize_prng() -> void {
// TODO(me): handle the reseeds here as per Cryptography Engineering,
// p. 153
set_reseed_ctr_to_null();
std::unique_lock<std::mutex> p_ul(print_mtx);
std::scoped_lock sl(mtx_accu, mtx_p_pools);
try {
R.initialize_pools();
fmt::print("[i] fortuna: pools initialized\n");
p_ul.unlock();
accumulator.set_pools_ptr(R._p_pools);
accumulator.set_gen(R.Gen);
set_reseed_ctr_to_null();
// FIXME: bogus first reseed here, P_0 definitely hasn't collected
// enough entropy by now
incr_reseed_ctr();
std::scoped_lock sl(mtx_accu, mtx_p_pools);
p_ul.lock();
fmt::print("first reseed\n");
p_ul.unlock();
{
std::lock_guard<std::mutex> lg(print_mtx);
std::unique_lock<std::mutex> ul(mtx);
R.Gen.reseed("fortuna");
ul.unlock();
R.initialize_pools();
fmt::print("[i] fortuna: pools initialized\n");
}
this->_p_accumulator->set_pools_ptr(R._p_pools);
this->_p_accumulator->set_gen(R.Gen);
this->sync_point.count_down();
}
catch (std::exception& e) {
p_ul.try_lock();
std::lock_guard<std::mutex> lg(print_mtx);
fmt::print(
"{}\n[!] fortuna: fatal error, PRNG initialization FAILED!\n\n",
e.what());
}
p_ul.try_lock();
this->sync_point.count_down();
std::lock_guard<std::mutex> lg(print_mtx);
fmt::print("[*] fortuna: PRNG initialized\n");
}
@ -104,11 +98,6 @@ public:
~R_state() noexcept = default;
protected:
auto null_da_ctr() -> void {
reseed_ctr = 0x00;
fmt::print("reseed_ctr set to 0x00\n");
}
auto initialize_pools() -> void {
for (unsigned int i = accumulator::Accumulator::init_pool_num;
i < Fortuna::NUM_OF_POOLS;
@ -119,7 +108,6 @@ public:
private:
generator::Generator Gen;
uint64_t reseed_ctr{0x00};
// 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
@ -136,7 +124,8 @@ public:
}; // class R_state
fortuna::Fortuna::R_state R;
fortuna::accumulator::Accumulator accumulator;
std::shared_ptr<accumulator::Accumulator> _p_accumulator{
std::make_shared<accumulator::Accumulator>()};
}; // class Fortuna

View File

@ -21,8 +21,9 @@
namespace fortuna {
SeedFileManager::SeedFileManager(
const fortuna::accumulator::Accumulator& da_accumulator) noexcept {
this->accumulator = da_accumulator;
std::shared_ptr<fortuna::accumulator::Accumulator>
da_accumulator) noexcept {
this->_p_accumulator = da_accumulator;
}
SeedFileManager::~SeedFileManager() noexcept {
set_job_running(false); // RIP, well, yeah, not running when the obj dies
@ -108,7 +109,7 @@ auto SeedFileManager::update_seed_file() -> void {
buff.SizeInBytes() *
8); // we need the size in bits
accumulator.call_reseed(str_buff);
this->_p_accumulator->call_reseed(str_buff);
write_seed_file();
}
catch (std::exception& e) {
@ -123,7 +124,7 @@ auto SeedFileManager::write_seed_file() -> void {
fortuna::SeedFileManager::IS_RUNNING = true;
CryptoPP::SecByteBlock buff{config.seed_f_length};
const std::string da_buff{accumulator.get_random_data(
const std::string da_buff{this->_p_accumulator->get_random_data(
static_cast<unsigned int>(config.seed_f_length))};
assert(da_buff.length() % 2 == 0);

View File

@ -26,8 +26,8 @@ public:
auto is_job_running() const -> bool;
auto do_stuff() -> void;
explicit SeedFileManager(
const fortuna::accumulator::Accumulator& accumulator) noexcept;
explicit SeedFileManager(std::shared_ptr<fortuna::accumulator::Accumulator>
accumulator) noexcept;
~SeedFileManager() noexcept;
protected:
@ -38,7 +38,7 @@ private:
mutable std::recursive_mutex mtx;
std::atomic<bool> IS_RUNNING{false};
DoTask do_task;
fortuna::accumulator::Accumulator accumulator;
std::shared_ptr<fortuna::accumulator::Accumulator> _p_accumulator;
auto seed_file_exists() const -> bool;
auto write_seed_file() -> void;