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 1ec70d1161
All checks were successful
continuous-integration/drone/push Build is passing
fortuna,generator({fmt}) print with colours
2022-02-03 04:52:19 +01:00

214 lines
5.3 KiB
C++

#ifndef FORTUNA_GENERATOR_CPP
#define FORTUNA_GENERATOR_CPP
#include "generator.h"
#include "util.h"
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <cryptopp/secblock.h>
#include <fmt/color.h>
#include <fmt/core.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <mutex>
#include <stdexcept>
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<std::recursive_mutex> 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<std::recursive_mutex> lg(mtx);
return G;
}
auto Generator::time_to_reseed(
const uint64_t& pool0_len,
const unsigned int& min_p_size,
const std::chrono::duration<int64_t, std::ratio<1, 1000>>& time_elapsed,
const std::chrono::milliseconds& gen_reseed_interval) const -> bool {
std::lock_guard<std::recursive_mutex> lg(mtx);
return (pool0_len >= min_p_size && time_elapsed > gen_reseed_interval);
}
auto Generator::reseed(const std::string& s) -> void {
std::lock_guard<std::recursive_mutex> lg(mtx);
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, 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<std::recursive_mutex> 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<std::mutex> ul(crypt_mtx);
CryptoPP::FixedSizeSecBlock<CryptoPP::byte, Generator::ctr_len> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<unsigned int>(std::ceil(static_cast<double>(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<uint8_t> i{0};
while (true) {
this->G.ctr.at(i) = static_cast<std::byte>(
static_cast<uint8_t>(this->G.ctr.at(i)) + 0x01);
if (this->G.ctr.at(i) == static_cast<std::byte>(0x00) &&
++i < this->G.ctr.size()) {
continue;
}
break;
}
}
} // namespace generator
} // namespace fortuna
#endif