surtur
c4dcab3046
All checks were successful
continuous-integration/drone/push Build is passing
in fortuna, print the random bytes as they come, without a newline, to stdout. the idea is it can be directly piped to, say, dieharder, for, e.g. testing purposes. also print info msg to stderr as is now customary throughout the program.
261 lines
7.4 KiB
C++
261 lines
7.4 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 <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());
|
|
// 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 {}\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 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
|