surtur
c4dcab3046
All checks were successful
continuous-integration/drone/push Build is passing
in fortuna, print the random bytes as they come, without a newline, to stdout. the idea is it can be directly piped to, say, dieharder, for, e.g. testing purposes. also print info msg to stderr as is now customary throughout the program.
217 lines
5.3 KiB
C++
217 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/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: reseeded\n");
|
|
}
|
|
catch (std::exception& e) {
|
|
fmt::print(stderr, "{}\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) )) */
|
|
|
|
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();
|
|
}
|
|
catch (std::exception& e) {
|
|
fmt::print(stderr, "{}", e.what());
|
|
}
|
|
|
|
/* re-key */
|
|
try {
|
|
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);
|
|
nu_G_k.clear();
|
|
}
|
|
catch (std::exception& e) {
|
|
fmt::print(stderr, "{}", 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
|