#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 namespace fortuna { namespace generator { Generator::Generator() /*noexcept*/ { try { initialize_generator(); } catch (CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); exit(1); } } Generator::~Generator() noexcept {} void Generator::initialize_generator() { std::lock_guard lg(mtx); try { std::memset(G.k, 0x00, G.k.size()); G.ctr = 0; fmt::print("Generator initialized\n"); } catch (CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); exit(1); } } 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, a.c_str(), G.k_length); ++G.ctr; fmt::print("[i] generator: reseeded\n"); } catch (std::exception& e) { fmt::print("{}", e.what()); } } 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 std::string plain{"Oh, I am fortune's fool!"}; std::string cipher, encoded_c; std::unique_lock ul(crypt_mtx); // in case we need to convert counter to string std::string str_ctr{reinterpret_cast(&G.ctr)}; // 16 bytes --> 128bit static constexpr const std::size_t ctr_length{16}; CryptoPP::FixedSizeSecBlock ctr; std::memmove(ctr, str_ctr.c_str(), ctr_length); try { CryptoPP::CTR_Mode::Encryption e; e.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( e, new CryptoPP::StringSink(cipher)) // StreamTransformationFilter ); // StringSource } catch (CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); exit(1); } // Pretty print cipher text CryptoPP::StringSource str_src2( cipher, true, new CryptoPP::HexEncoder( new CryptoPP::StringSink(encoded_c)) // HexEncoder ); // StringSource return encoded_c; } auto Generator::generate_blocks(unsigned int k_blocks) -> std::string { std::lock_guard lg(mtx); assert((G.ctr != 0) && "Counter is not 0, generator has been seeded"); std::string r{""}; while (k_blocks--) { r += Generator::do_crypto(); ++G.ctr; } return r; } auto Generator::generate_random_data(const unsigned int& n) -> std::string { std::lock_guard lg(mtx); if (n == 0) { // do not do this..? const std::string msg{"zero bytes requested, bailing\n"}; fmt::print("[*] g: error: {}", msg); // throw std::invalid_argument(msg); // TODO(me): throw or not? // perhaps just return prematurely return ""; } // pre-computed 2^20 if (n > 1048576) { const std::string msg{"n cannot be > 2^20\n"}; fmt::print("[*] error: {}", msg); throw std::invalid_argument(msg); } std::string r; try { /* do magic to compute r * r ← first-n-bytes(GenerateBlocks(G, ceil(n/16) )) */ unsigned int how_many( static_cast(std::ceil(static_cast(n) / 16))); std::string rr{generate_blocks(how_many)}; fmt::print("rr (output from generate_blocks): {}\n", rr); // since we're truncating hex, we need to get twice more characters r = rr.substr(0, n * 0x02ul); rr.clear(); } catch (std::exception& e) { fmt::print("{}", e.what()); } /* re-key */ try { std::string nu_G_k{generate_blocks(2)}; // fmt::print("nu_G_k: {}\n", nu_G_k); // debugging std::string dst; CryptoPP::HexDecoder decoder; decoder.Put(reinterpret_cast(nu_G_k.data()), nu_G_k.size()); decoder.MessageEnd(); dst.resize(G.k_length); decoder.Get(reinterpret_cast(&dst[0]), dst.size()); nu_G_k.clear(); /* clear out the old key and set a new one */ std::memset(G.k, 0x00, G.k_length); std::memmove(G.k, dst.c_str(), G.k_length); } catch (std::exception& e) { fmt::print("{}", e.what()); } return r; } } // namespace generator } // namespace fortuna #endif