This repository has been archived on 2022-02-10. You can view files and clone it, but cannot push or open issues or pull requests.
fortuna/generator.cpp
surtur 795b9ffe54
All checks were successful
continuous-integration/drone/push Build is passing
add proper SeedFileManager implementation
a couple of fixes/necessary additions were made along the way, namely:
* add a default constructor for DoTask
* rework of the mutex/lock_guard/unique_lock logic in generator/fortuna
* add .fortuna.seed to the list of the ignored (.gitignore)
* add helper function to util for convertin bytes to blocks (16b==block)
* add a wrapper for around the SeedFileManager instance and a way to see
  if it's dead or alive (so that it can be restarted if needed)
* the timeout for saving of the seed file has been decreased to a more
  reasonable value than 10 minutes (I wouldn't want to lose potentially
  up to 10 minutes worth of entropy)
2022-01-09 11:58:38 +01:00

194 lines
4.8 KiB
C++

#ifndef FORTUNA_GENERATOR_CPP
#define FORTUNA_GENERATOR_CPP
#include "generator.h"
#include "util.h"
#include <cryptopp/hex.h>
#include <cryptopp/filters.h>
#include <cryptopp/serpent.h>
#include <cryptopp/ccm.h>
#include <fmt/core.h>
#include <cmath>
#include <cassert>
#include <mutex>
#include <stdexcept>
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 {
std::unique_lock<std::mutex> ul(reseed_mtx);
// ref: https://www.cryptopp.com/wiki/SecBlock
std::string da_key(reinterpret_cast<const char*>(&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 */
// 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<std::mutex> ul(crypt_mtx);
// in case we need to convert counter to string
std::string str_ctr{reinterpret_cast<const char*>(&G.ctr)};
// 16 bytes --> 128bit
static constexpr const std::size_t ctr_length{16};
CryptoPP::FixedSizeSecBlock<CryptoPP::byte, ctr_length> ctr;
std::memmove(ctr, str_ctr.c_str(), ctr_length);
try {
CryptoPP::CTR_Mode<CryptoPP::Serpent>::Encryption e;
e.SetKeyWithIV(G.k,G.k.size(),ctr);
ul.unlock();
// 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 {
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<std::mutex> 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<unsigned 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
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