269 lines
7.5 KiB
C++
269 lines
7.5 KiB
C++
#ifndef FORTUNA_FORTUNA_CPP
|
|
#define FORTUNA_FORTUNA_CPP
|
|
|
|
#include "fortuna.h"
|
|
#include "event_adder_impl.h"
|
|
#include "pool.h"
|
|
#include "seed_file_management.h"
|
|
#include "urandom_entropy_src.h"
|
|
#include "util.h"
|
|
|
|
#include <cryptopp/cryptlib.h>
|
|
#include <fmt/chrono.h>
|
|
#include <fmt/color.h>
|
|
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <exception>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
namespace fortuna {
|
|
static constexpr const unsigned int min_pool_size{64};
|
|
auto now{std::chrono::steady_clock::now()};
|
|
|
|
|
|
Fortuna::Fortuna() {
|
|
try {
|
|
initialize_prng();
|
|
|
|
this->sync_point.wait(); // wait for init
|
|
|
|
th_sfm = std::jthread(&Fortuna::seed_file_manager_service, this);
|
|
th_urandom = std::jthread(&Fortuna::urandom_entropy_src_service, this);
|
|
}
|
|
catch (CryptoPP::Exception& e) {
|
|
fmt::print(stderr, "{}\n", e.what());
|
|
throw;
|
|
// perhaps die on error
|
|
}
|
|
}
|
|
Fortuna::~Fortuna() noexcept {
|
|
this->die_point.wait();
|
|
fmt::print(stderr, "[*] Fortuna says goodbye!\n");
|
|
}
|
|
|
|
|
|
auto Fortuna::random_data(const uint64_t n_bytes) -> void {
|
|
if (!this->continue_running.load()) {
|
|
return;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lg(mtx_random_data);
|
|
|
|
const auto start{std::chrono::system_clock::now()};
|
|
fmt::print(stderr, "random_data starting - {}\n", start);
|
|
auto elapsed{std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::steady_clock::now().time_since_epoch() -
|
|
now.time_since_epoch())};
|
|
fmt::print(stderr, "[i] fortuna: last_reseed: {} ago\n", elapsed);
|
|
now = std::chrono::steady_clock::now();
|
|
|
|
try {
|
|
std::string s;
|
|
// synchronise reads and writes to the between
|
|
// {generator,accumulator,fortuna} service threads -> in member
|
|
// functions
|
|
|
|
const int pools_to_use{ffsll(static_cast<int>(get_reseed_ctr()))};
|
|
|
|
fmt::print(stderr,
|
|
"[*] fortuna: current p0 length: {}\n",
|
|
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)) {
|
|
// 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->get_reseed_ctr() %
|
|
static_cast<uint64_t>(pow(2, static_cast<double>(i))) ==
|
|
0) {
|
|
s.append(fortuna::Util::do_sha(
|
|
this->R._p_pools->at(static_cast<uint64_t>(i))
|
|
.get_s()));
|
|
this->R._p_pools->at(static_cast<uint64_t>(i)).clear_pool();
|
|
}
|
|
}
|
|
incr_reseed_ctr();
|
|
R.Gen.reseed(s);
|
|
R.last_reseed = std::chrono::steady_clock::now();
|
|
s.clear();
|
|
}
|
|
|
|
fmt::print(stderr, "[i] fortuna: reseed ctr ");
|
|
fmt::print(stderr,
|
|
fmt::emphasis::bold | bg(fmt::color::hot_pink),
|
|
"{}\n",
|
|
get_reseed_ctr());
|
|
if (get_reseed_ctr() == 0) {
|
|
fmt::print(stderr,
|
|
"[!] ERROR: reseed ctr is 0, PRNG not seeded!\n");
|
|
throw std::runtime_error("illegal state, PRNG not seeded");
|
|
}
|
|
else {
|
|
const std::string n{R.Gen.generate_random_data(n_bytes)};
|
|
fmt::print("{}", n); // intentionally without a newline
|
|
fmt::print(stderr, "[i] fortuna: delivered {} bytes\n", n_bytes);
|
|
}
|
|
}
|
|
catch (std::exception& e) {
|
|
fmt::print(stderr, "{}\n", e.what());
|
|
}
|
|
|
|
const auto end{std::chrono::system_clock::now()};
|
|
const std::chrono::duration<float> diff = end - start;
|
|
fmt::print(stderr, "random_data done - {}\n", end);
|
|
fmt::print(stderr, "getting random data ");
|
|
fmt::print(
|
|
stderr, bg(fmt::color::dark_cyan), "took {:.{}f}s\n", diff.count(), 12);
|
|
} // random_data
|
|
|
|
auto Fortuna::moar_random_data(const uint64_t& n_bytes) -> void {
|
|
// used for requests of more than 2^20 bytes
|
|
// modus operandi: calculate how many consecutive requests to
|
|
// "random_data(2^20)" are needed, call random_data() until done
|
|
|
|
if (n_bytes > Fortuna::two_pow_twenty) {
|
|
uint64_t how_many_ops{n_bytes / Fortuna::two_pow_twenty};
|
|
|
|
uint64_t remaining{n_bytes};
|
|
fmt::print(stderr, "[i] fortuna: remaining {} bytes\n", remaining);
|
|
while (how_many_ops-- > 0) {
|
|
this->random_data(Fortuna::two_pow_twenty);
|
|
remaining -= Fortuna::two_pow_twenty;
|
|
fmt::print(stderr, "[i] fortuna: remaining {} bytes\n", remaining);
|
|
}
|
|
this->random_data(remaining);
|
|
}
|
|
else {
|
|
this->random_data(n_bytes);
|
|
}
|
|
} // moar_random_data
|
|
|
|
|
|
auto Fortuna::seed_file_manager_service() -> void {
|
|
static constexpr const std::chrono::seconds checkup_interval{7};
|
|
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[i] fortuna: starting seed file manager service\n");
|
|
fmt::print(stderr, "[*] sfm: checkup interval {}\n", checkup_interval);
|
|
}
|
|
|
|
auto right_now{fortuna::Util::current_time()};
|
|
std::unique_lock<std::mutex> mtx_l(mtx);
|
|
std::unique_lock<std::mutex> a_ul(mtx_accu);
|
|
|
|
assert(this->_p_accumulator->_p_pools_equal(this->R._p_pools));
|
|
|
|
SeedFileManager sfm(this->_p_accumulator);
|
|
assert(checkup_interval < sfm.get_write_interval());
|
|
|
|
a_ul.unlock();
|
|
mtx_l.unlock();
|
|
|
|
while (this->continue_running.load()) {
|
|
right_now = fortuna::Util::current_time();
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[*] sfm: checkup time @{}\n", right_now);
|
|
}
|
|
if (!sfm.is_job_running()) {
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[*] sfm: job not running, starting\n");
|
|
}
|
|
try {
|
|
sfm.do_stuff();
|
|
}
|
|
catch (std::exception& e) {
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(
|
|
stderr, "[!] sfm: exception caught: {}\n", e.what());
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[*] sfm: job running\n");
|
|
}
|
|
// sleeping here assumes nothing goes wrong. should sth break, there
|
|
// will be no sleeps when trying to restart sfm in the above block
|
|
std::this_thread::sleep_until(
|
|
right_now + std::chrono::seconds(checkup_interval));
|
|
}
|
|
}
|
|
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[*] sfm: stopping, goodbye!\n\n");
|
|
this->die_point.count_down();
|
|
}
|
|
|
|
auto Fortuna::urandom_entropy_src_service() -> void {
|
|
static constexpr const std::chrono::milliseconds wakeup_interval{50};
|
|
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr,
|
|
"[i] fortuna: starting urandom entropy src service\n");
|
|
fmt::print(stderr, "[*] ues: wakeup interval {}\n", wakeup_interval);
|
|
}
|
|
|
|
auto right_now{fortuna::Util::current_time()};
|
|
|
|
std::unique_lock<std::mutex> mtx_l(mtx);
|
|
|
|
accumulator::UrandomEntropySrc ues;
|
|
static constexpr const uint8_t src_id{0};
|
|
accumulator::EventAdderImpl adder(src_id, this->R._p_pools);
|
|
mtx_l.unlock();
|
|
|
|
while (this->continue_running.load()) {
|
|
try {
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[i] now: {}\n", fortuna::Util::current_time());
|
|
|
|
// check if ptr still valid
|
|
if (this->R._p_pools) {
|
|
// make sure they're pointing to the same chunk of data
|
|
// I know, debug-only
|
|
assert(this->_p_accumulator->_p_pools_equal(this->R._p_pools));
|
|
|
|
ues.event(adder);
|
|
}
|
|
}
|
|
catch (std::exception& e) {
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[!] ues exception: {}\n", e.what());
|
|
}
|
|
}
|
|
right_now = fortuna::Util::current_time();
|
|
std::this_thread::sleep_until(
|
|
right_now + std::chrono::milliseconds(wakeup_interval));
|
|
mtx_l.lock();
|
|
{
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr,
|
|
"[*] fortuna: current p0 length: {}\n\n",
|
|
this->R._p_pools->at(0).get_s_byte_count());
|
|
}
|
|
mtx_l.unlock();
|
|
}
|
|
|
|
std::lock_guard<std::mutex> p_ul(print_mtx);
|
|
fmt::print(stderr, "[*] ues: stopping, goodbye!\n\n");
|
|
this->die_point.count_down();
|
|
}
|
|
|
|
} // namespace fortuna
|
|
|
|
#endif // FORTUNA_FORTUNA_CPP
|