#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 #include #include #include #include #include #include #include #include #include #include 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 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::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(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(pools_to_use); ++i) { if (this->get_reseed_ctr() % static_cast(pow(2, static_cast(i))) == 0) { s.append(fortuna::Util::do_sha( this->R._p_pools->at(static_cast(i)) .get_s())); this->R._p_pools->at(static_cast(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 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 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 mtx_l(mtx); std::unique_lock 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 p_ul(print_mtx); fmt::print(stderr, "[*] sfm: checkup time @{}\n", right_now); } if (!sfm.is_job_running()) { { std::lock_guard p_ul(print_mtx); fmt::print(stderr, "[*] sfm: job not running, starting\n"); } try { sfm.do_stuff(); } catch (std::exception& e) { { std::lock_guard p_ul(print_mtx); fmt::print( stderr, "[!] sfm: exception caught: {}\n", e.what()); } } } else { { std::lock_guard 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 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 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 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 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 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 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 p_ul(print_mtx); fmt::print(stderr, "[*] ues: stopping, goodbye!\n\n"); this->die_point.count_down(); } } // namespace fortuna #endif // FORTUNA_FORTUNA_CPP