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 5557f0d9e6
All checks were successful
continuous-integration/drone/push Build is passing
refactor(generator): clean up includes a little
2021-12-06 00:08:32 +01:00

218 lines
5.8 KiB
C++

#ifndef FORTUNA_GENERATOR_CPP
#define FORTUNA_GENERATOR_CPP
#include "generator.h"
#include <cmath>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <cryptopp/osrng.h>
#include <cryptopp/hex.h>
#include <cryptopp/filters.h>
#include <cryptopp/serpent.h>
#include <cryptopp/sha3.h>
#include <cryptopp/ccm.h>
#include <fmt/core.h>
namespace fortuna {
namespace generator {
Generator::Generator() /*noexcept*/ {
try {
initialize_generator();
} catch(CryptoPP::Exception& e) {
fmt::print(stderr, "{}\n", e.what());
exit(1);
}
}
Generator::~Generator() = default;
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);
}
try {
// FIXME: hardcoded seed for the time being
reseed("fortuna");
} catch(CryptoPP::Exception& e) {
fmt::print(stderr, "{}\n", e.what());
exit(1);
}
};
auto Generator::get_state() -> G_state {
return G;
}
auto Generator::reseed(const std::string& s) -> void {
// TODO(me): proper concat - WIP below
// ref: https://www.cryptopp.com/wiki/SecBlock
std::string da_key(reinterpret_cast<const char*>(&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)};
std::memcpy(&a[0], &G.k[0], a.size());
++G.ctr;
}
auto Generator::do_sha(const std::string& k_n_s) -> std::string {
/* do sha256 */
using CryptoPP::HexEncoder;
using CryptoPP::HashFilter;
using CryptoPP::StringSink;
std::string digest;
// no reason not to go for Keccak
CryptoPP::SHA3_256 sha3_256;
digest.erase();
// 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))
);
// 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;
}
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;
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<const char*>(&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<CryptoPP::byte, ctr_length> ctr;
std::memcpy(&G.ctr, &ctr, sizeof(G.ctr));
try {
// fmt::print("plain text: {}\n", plain);
CTR_Mode<Serpent>::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 {
// fmt::print("n -> {}\n", n); // debugging
if (n < 0){
/* this should not be possible */
fmt::print("[*] error: n cannot be < 0\n");
throw std::invalid_argument("n cannot be < 0");
} else if (n > pow(2,20)){
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) )) */
// 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 */
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;
};
} // namespace generator
} // namespace fortuna
#endif