#ifndef FORTUNA_GENERATOR_CPP #define FORTUNA_GENERATOR_CPP #include "generator.h" #include "util.h" #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(){ 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 { return G; } auto Generator::time_to_reseed() const -> bool { // TODO(me): implement this if (true) { return true; } else { return false; } } auto Generator::reseed(const std::string& s) -> void { // 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::lock_guard lg(mtx); 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 */ // for the moment loosely based on // https://www.cryptopp.com/wiki/CTR_Mode using CryptoPP::StringSource; using CryptoPP::StringSink; using CryptoPP::HexEncoder; using CryptoPP::StreamTransformationFilter; using CryptoPP::Serpent; using CryptoPP::CTR_Mode; // William Shakespeare, Romeo and Juliet std::string plain{"Oh, I am fortune's fool!"}; std::string cipher, encoded_c; // 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 { 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 StringSource str_src1(plain,true, new StreamTransformationFilter(e, new StringSink(cipher) ) // StreamTransformationFilter ); // StringSource } catch(CryptoPP::Exception& e) { fmt::print(stderr, "{}\n", e.what()); exit(1); } // Pretty print cipher text StringSource str_src2(cipher,true, new HexEncoder( new StringSink(encoded_c) ) // HexEncoder ); // StringSource return encoded_c; } auto Generator::generate_blocks(unsigned int k_blocks) -> std::string { assert ((G.ctr!=0) && "Counter is not 0, generator has been seeded"); std::string r{""}; for (int i = 0; i < k_blocks; ++i) { r += do_crypto(); ++G.ctr; } return r; } auto Generator::generate_random_data(uint n) -> std::string { std::lock_guard lg(mtx); // fmt::print("n -> {}\n", n); // debugging if (n == 0) { // do not do this..? const std::string msg{"zero bytes requested, bailing\n"}; fmt::print("[*] error: {}", msg); throw std::invalid_argument(msg); } // 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) )) */ // n is number of bytes, i.e. pass n*8 to get number of bits unsigned int how_many(static_cast(ceil((n*8)/16))); // fmt::print("how_many: {}\n", how_many); // debugging 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; // FIXME: +1 for the actual null-termination, not exactly sure if needed dst.resize(G.k_length + 1); CryptoPP::StringSource str_s( nu_G_k,true,new CryptoPP::HexDecoder(new CryptoPP::StringSink(dst)) ); 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