fortuna: properly handle reseeds
All checks were successful
continuous-integration/drone/push Build is passing

* 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 {} 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( auto Accumulator::_p_pools_equal(
std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>> std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
p_pools) const -> bool { 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 { auto Accumulator::call_reseed(const std::string& seed) -> void {
try { try {
incr_reseed_ctr();
this->Gen->reseed(seed); this->Gen->reseed(seed);
} }
catch (std::exception& e) { catch (std::exception& e) {

View File

@ -12,6 +12,7 @@
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <memory> #include <memory>
#include <mutex>
#include <vector> #include <vector>
namespace fortuna { namespace fortuna {
@ -22,6 +23,9 @@ private:
static constexpr const uint8_t MAX_SOURCES{255}; static constexpr const uint8_t MAX_SOURCES{255};
static constexpr const uint8_t NUM_OF_POOLS{32}; 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{}; std::vector<uint8_t> entropy_sources{};
fortuna::generator::Generator* Gen; fortuna::generator::Generator* Gen;
std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>> std::shared_ptr<std::array<accumulator::Pool, Accumulator::NUM_OF_POOLS>>
@ -33,6 +37,12 @@ protected:
public: public:
constexpr static const unsigned int init_pool_num{0}; 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( auto _p_pools_equal(
std::shared_ptr<std::array<accumulator::Pool, std::shared_ptr<std::array<accumulator::Pool,
Accumulator::NUM_OF_POOLS>> p_pools) const Accumulator::NUM_OF_POOLS>> p_pools) const
@ -82,6 +92,9 @@ public:
Accumulator() noexcept; Accumulator() noexcept;
~Accumulator() noexcept; ~Accumulator() noexcept;
Accumulator(const Accumulator&) = delete; // no copy
Accumulator(Accumulator&) = delete;
Accumulator& operator=(const Accumulator&) = delete;
}; // class Accumulator }; // 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 && // if (R.pools[0].get_s_byte_count() >= min_pool_size &&
// elapsed > R.Gen.reseed_interval) { // elapsed > R.Gen.reseed_interval) {
for (int i = 0; i < static_cast<int>(pools_to_use); ++i) { 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))) == static_cast<uint64_t>(pow(2, static_cast<double>(i))) ==
0) { 0) {
s.append(fortuna::Util::do_sha( s.append(fortuna::Util::do_sha(
@ -101,8 +101,8 @@ auto Fortuna::random_data(unsigned int n_bytes) -> void {
s.clear(); s.clear();
} }
fmt::print("[i] fortuna: reseed ctr {}\n", R.reseed_ctr); fmt::print("[i] fortuna: reseed ctr {}\n", get_reseed_ctr());
if (R.reseed_ctr == 0) { if (get_reseed_ctr() == 0) {
fmt::print("[!] ERROR: reseed ctr is 0, PRNG not seeded!\n"); fmt::print("[!] ERROR: reseed ctr is 0, PRNG not seeded!\n");
throw std::runtime_error("illegal state, PRNG not seeded"); 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> mtx_l(mtx);
std::unique_lock<std::mutex> a_ul(mtx_accu); 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(); a_ul.unlock();
mtx_l.unlock(); mtx_l.unlock();
@ -244,7 +244,7 @@ auto Fortuna::urandom_entropy_src_service() -> void {
if (this->R._p_pools) { if (this->R._p_pools) {
// make sure they're pointing to the same chunk of data // make sure they're pointing to the same chunk of data
// I know, debug-only // 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); ues.event(adder);
} }

View File

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

View File

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

View File

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