diff --git a/fortuna.cpp b/fortuna.cpp index b8147f1..cd943d1 100644 --- a/fortuna.cpp +++ b/fortuna.cpp @@ -19,78 +19,11 @@ namespace fortuna { } Fortuna::~Fortuna() = default; - auto Fortuna::random_data(unsigned int n_blocks) -> void { - // 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; - - CryptoPP::AutoSeededRandomPool prng; - // use 256bit key - CryptoPP::SecByteBlock key(CryptoPP::Serpent::MAX_KEYLENGTH); - - prng.GenerateBlock(key,key.size()); - - CryptoPP::byte ctr[Serpent::BLOCKSIZE]; - prng.GenerateBlock(ctr,sizeof(ctr)); - - // William Shakespeare, Romeo and Juliet - std::string plain{"Oh, I am fortune's fool!"}; - std::string cipher, encoded_c, decrypted; - - try { - fmt::print("plain text: {}\n", plain); - - CTR_Mode::Encryption e; - e.SetKeyWithIV(key,key.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 - fmt::print("cipher text: {}\n", encoded_c); - - try { - CTR_Mode::Decryption d; - d.SetKeyWithIV(key,key.size(),ctr); - - // The StreamTransformationFilter removes padding as required. - StringSource str_src3(cipher, true, - new StreamTransformationFilter(d, - new StringSink(decrypted) - ) // StreamTransformationFilter - ); // StringSource - - fmt::print("decrypted text: {}\n", decrypted); - } - catch(CryptoPP::Exception& e) { - fmt::print(stderr, "{}\n", e.what()); - exit(1); - } - + auto Fortuna::random_data(unsigned int n_bytes) -> void { + std::string n{gen.generate_random_data(n_bytes)}; + fmt::print("got you {} proper bytes from generate_random_data -> {}\n", + n_bytes, n); } //random_data } // namespace fortuna diff --git a/generator.cpp b/generator.cpp index 6bedb47..cdadcd5 100644 --- a/generator.cpp +++ b/generator.cpp @@ -9,6 +9,12 @@ #include #include #include + +#include +#include +#include +#include +#include #include #include "generator.h" @@ -21,12 +27,12 @@ Generator::Generator() /*noexcept*/ { } Generator::~Generator() = default; + void Generator::initialize_generator(){ - G.k = 0; + std::memset(G.k, 0x00, G.k.size()); G.ctr = 0; fmt::print("Generator initialized\n"); - // TODO(me): rm this, it's here just so it's called - do_sha("fortuna"); + reseed("fortuna"); }; auto Generator::get_state() -> G_state { @@ -34,11 +40,17 @@ auto Generator::get_state() -> G_state { } auto Generator::reseed(const std::string& s) -> void { - std::string to_be_hashed = std::to_string(G.k) + s; + // TODO(me): proper concat - WIP below + // ref: https://www.cryptopp.com/wiki/SecBlock + std::string da_key(reinterpret_cast(&G.k[0]), G.k.size()); + std::string to_be_hashed{da_key+s}; + // fmt::print("s -> {}\n", s); // debugging + // fmt::print("da_key -> {}\n", da_key); // debugging + // fmt::print("concat \"da_key + s\" -> {}\n", to_be_hashed); // debugging + // TODO(me): wrap do_sha in a try-catch - std::string a = do_sha(to_be_hashed); - // str, size, base - G.k = stol(a,nullptr,10); + std::string a{do_sha(to_be_hashed)}; + std::memcpy(&a[0], &G.k[0], a.size()); ++G.ctr; } @@ -54,44 +66,101 @@ auto Generator::do_sha(const std::string& k_n_s) -> std::string { CryptoPP::SHA3_256 sha3_256; digest.erase(); - const std::string to_compare{ - "8eccfbbbc9df48b4272e6237ce45aad8fbe59629b4963c4dcda5716e61bb34e1" - }; + // FIXME: commented to test reseeds + // const std::string to_compare{ + // "8eccfbbbc9df48b4272e6237ce45aad8fbe59629b4963c4dcda5716e61bb34e1" + // }; CryptoPP::StringSource bar(k_n_s,true, new HashFilter(sha3_256,new HexEncoder(new StringSink(digest),false)) ); - assert(digest == to_compare); - fmt::print("{}\n", digest); - digest.erase(); + // FIXME: commented to test reseeds + // assert(digest == to_compare); // debugging - was used to test that hash + // of "fortuna" was correctly generated + // digest.erase(); // actually do not erase now + // fmt::print("digest: {}\n", digest); // debugging - // return digest; - // TODO(me) - // do not return the proper digest just yet, too long to fit into - // int64_t. G.k has to be a 256bit-wide type. for now just return a - // small enough number - return "42"; + return digest; } auto Generator::do_crypto() -> std::string { /* this function calls the block cipher * returns a string of k*(16 bytes); * do whatever atm */ - return ""; + + // 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; + + CryptoPP::AutoSeededRandomPool prng; + // use 256bit key + CryptoPP::SecByteBlock key(CryptoPP::Serpent::MAX_KEYLENGTH); + + prng.GenerateBlock(key,key.size()); + + // 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.k[0]), G.k.size()}; + // std::string str_ctr{(G.ctr)}; + // 16 bytes --> 128bit + static constexpr const std::size_t ctr_length{16}; + CryptoPP::FixedSizeSecBlock ctr; + std::memcpy(&G.ctr, &ctr, sizeof(G.ctr)); + + try { + // fmt::print("plain text: {}\n", plain); + + 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 + // fmt::print("cipher text: {}\n", encoded_c); + + 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"); + // fmt::print("k_blocks -> {}\n", k_blocks); // debugging std::string r{""}; for (int i = 0; i < k_blocks; ++i) { r += do_crypto(); ++G.ctr; } + // fmt::print("r from generate_blocks -> {}\n", r); // debugging return r; } auto Generator::generate_random_data(uint n) -> std::string { - std::string r{""}; + // fmt::print("n -> {}\n", n); // debugging if (n < 0){ /* this should not be possible */ fmt::print("[*] error: n cannot be < 0\n"); @@ -100,14 +169,31 @@ auto Generator::generate_random_data(uint n) -> std::string { fmt::print("[*] error: n cannot be > 2^20\n"); throw std::invalid_argument("n cannot be > 2^20"); } + /* do magic to compute r * r ← first-n-bytes(GenerateBlocks(G, ceil(n/16) )) */ - std::string rr = generate_blocks(ceil(n/16)); - r = rr.substr(0,n); + // n is number of bytes, i.e. pass n*8 to get number of bits + unsigned int how_many = (int)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 + std::string r{rr.substr(0,n*2)}; + rr.erase(); /* re-key */ - // TODO: check conversions - G.k = stoul(generate_blocks(2)); + std::string nu_G_k{generate_blocks(2)}; + // fmt::print("nu_G_k: {}\n", nu_G_k); // debugging + std::string dst; + CryptoPP::StringSource str_s( + nu_G_k,true,new CryptoPP::HexDecoder(new CryptoPP::StringSink(dst)) + ); + + nu_G_k.erase(); + + /* clear out the old key and set a new one */ + std::memset(G.k, 0x00, G.k.size()); + std::memcpy(&dst[0], &G.k[0], dst.size()); return r; }; diff --git a/generator.h b/generator.h index b57744a..f8eadb9 100644 --- a/generator.h +++ b/generator.h @@ -1,6 +1,9 @@ #ifndef FORTUNA_GENERATOR_H #define FORTUNA_GENERATOR_H +#include +#include + #include namespace fortuna { @@ -10,11 +13,15 @@ class Generator { public: Generator(); // ad noexcept: perhaps _do_ throw* ~Generator(); - auto generate_blocks(unsigned int k_blocks) -> std::string; + + /* n is the number of random bytes to generate */ + auto generate_random_data(uint n) -> std::string; private: struct G_state { - int64_t k; + // 32*8 + static constexpr const std::size_t k_length{32}; + CryptoPP::FixedSizeSecBlock k; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" unsigned __int128 ctr; @@ -30,8 +37,7 @@ private: auto do_crypto() -> std::string; - /* n is the number of random bytes to generate */ - auto generate_random_data(uint n) -> std::string; + auto generate_blocks(unsigned int k_blocks) -> std::string; protected: auto get_state() -> Generator::G_state; diff --git a/main.cpp b/main.cpp index 54dd29a..7059199 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,10 @@ #include "fortuna.h" -#include "generator.h" #include int main() { fmt::print("[*] doing evil stuff\n"); fortuna::Fortuna f; - f.random_data(4); + f.random_data(4); // number of bytes requested return 0; }