#ifndef FORTUNA_GENERATOR_CPP #define FORTUNA_GENERATOR_CPP #include "generator.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fortuna { namespace generator { Generator::Generator() /*noexcept*/ { try { initialize_generator(); } catch (CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); throw; } } Generator::~Generator() noexcept {} void Generator::initialize_generator() { std::lock_guard lg(mtx); try { std::memset(G.k, 0x00, G.k.size()); std::memset(G.ctr.begin(), 0x00, this->ctr_len); fmt::print(stderr, "Generator initialized\n"); } catch (CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); throw; } } auto Generator::get_state() const -> G_state { std::lock_guard lg(mtx); return G; } auto Generator::time_to_reseed( const uint64_t& pool0_len, const unsigned int& min_p_size, const std::chrono::duration>& time_elapsed, const std::chrono::milliseconds& gen_reseed_interval) const -> bool { std::lock_guard lg(mtx); return (pool0_len >= min_p_size && time_elapsed > gen_reseed_interval); } auto Generator::reseed(const std::string& s) -> void { std::lock_guard lg(mtx); std::unique_lock ul(reseed_mtx); // ref: https://www.cryptopp.com/wiki/SecBlock std::string da_key(reinterpret_cast(&G.k[0]), G.k.SizeInBytes() * 8); // we need the size in bits try { std::string a{fortuna::Util::do_sha(da_key + s)}; std::memmove(G.k, fortuna::Util::de_hex(a).c_str(), G.k_length); Generator::ctr_inc(); fmt::print(stderr, "[i] generator: "); fmt::print(stderr, bg(fmt::color::rebecca_purple), "reseeded\n"); } catch (std::exception& e) { fmt::print(stderr, bg(fmt::color::red), "{}\n", e.what()); throw; } } auto Generator::do_crypto() -> std::string { /* this function calls the block cipher * returns a string of k*(16 bytes); * do whatever atm */ std::lock_guard lg(mtx); // for the moment loosely based on // https://www.cryptopp.com/wiki/CTR_Mode // William Shakespeare, Romeo and Juliet const std::string plain{"Oh, I am fortune's fool!"}; std::string cipher; std::unique_lock ul(crypt_mtx); CryptoPP::FixedSizeSecBlock ctr; std::memmove(ctr, G.ctr.data(), Generator::ctr_len); try { this->enc.SetKeyWithIV(G.k, G.k.size(), ctr); // The StreamTransformationFilter adds padding as required. ECB and // CBC Mode must be padded to the block size of the cipher. CTR // mode not. // the "true" param - pump all of the data immediately to its // attached transformation CryptoPP::StringSource str_src1( plain, true, new CryptoPP::StreamTransformationFilter( this->enc, new CryptoPP::StringSink(cipher)) // StreamTransformationFilter ); // StringSource } catch (CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); throw; } // just return the bytes, hex-encode can be done at a later point or by the // final user/in console/perhaps not at all return cipher; } auto Generator::generate_blocks(unsigned int k_blocks) -> std::string { std::lock_guard lg(mtx); assert((G.ctr != this->null_blk) && "Counter is not 0, generator has been seeded"); if (!this->is_seeded()) { throw std::logic_error("G.ctr == 0, generator has not been seeded!"); } std::string r{""}; while (k_blocks--) { r += Generator::do_crypto(); Generator::ctr_inc(); } return r; } auto Generator::generate_random_data(const uint64_t& n) -> std::string { std::lock_guard lg(mtx); if (n == 0) { // const std::string msg{"zero bytes requested, bailing\n"}; fmt::print(stderr, "[!] error: zero bytes requested, bailing\n"); // throw std::invalid_argument(msg); // TODO(me): throw or not? // perhaps just return prematurely return ""; } // pre-computed 2^20 if (n > 1048576) { throw std::invalid_argument("[!] error: n cannot be > 2^20\n"); } std::string r; try { /* do magic to compute r * r ← first-n-bytes(GenerateBlocks(G, ceil(n/16) )) */ const unsigned int how_many( static_cast(std::ceil(static_cast(n) / 16))); std::string rr{generate_blocks(how_many)}; r = rr.substr(0, n); // not hex-encoded anymore, just ask for n chars rr.clear(); /* re-key */ const std::string nu_G_k{generate_blocks(2)}; /* clear out the old key and set a new one */ std::memset(G.k, 0x00, G.k_length); std::memmove(G.k, nu_G_k.c_str(), G.k_length); } catch (std::exception& e) { fmt::print(stderr, "{}\n", e.what()); } return r; } auto Generator::ctr_inc() -> void { // increment the least-significant-byte-first ctr std::atomic i{0}; while (true) { this->G.ctr.at(i) = static_cast( static_cast(this->G.ctr.at(i)) + 0x01); if (this->G.ctr.at(i) == static_cast(0x00) && ++i < this->G.ctr.size()) { continue; } break; } } } // namespace generator } // namespace fortuna #endif