commit a64b52e5a4
Author: surtur <a_mirre@utb.cz>
Date: Sun Nov 21 23:39:30 2021 +0100
finalise generator
this commit adds a (nearly) complete implementation of the generator.
* wrap calls to generator in a fortuna class method random_data
* calls generator's method generate_random_data, that internally calls
generate_blocks
* use a proper 256bit key in G_state
* add reseed method implementation
* call a reseed in initialize_generator
* do_sha returns proper digest now
* add proper do_crypto implementation
* call generate_blocks internally
* handle re-keying
* optimise header includes
TODO: there are still many commented (enabled on demand) debugging statements
-> TO BE REMOVED
This commit is contained in:
parent
d942e06263
commit
0c2a1c6744
75
fortuna.cpp
75
fortuna.cpp
|
@ -19,78 +19,11 @@ namespace fortuna {
|
||||||
}
|
}
|
||||||
Fortuna::~Fortuna() = default;
|
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<Serpent>::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<Serpent>::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
|
} //random_data
|
||||||
|
|
||||||
} // namespace fortuna
|
} // namespace fortuna
|
||||||
|
|
136
generator.cpp
136
generator.cpp
|
@ -9,6 +9,12 @@
|
||||||
#include <cryptopp/hex.h>
|
#include <cryptopp/hex.h>
|
||||||
#include <cryptopp/filters.h>
|
#include <cryptopp/filters.h>
|
||||||
#include <cryptopp/sha3.h>
|
#include <cryptopp/sha3.h>
|
||||||
|
|
||||||
|
#include <cryptopp/osrng.h>
|
||||||
|
#include <cryptopp/hex.h>
|
||||||
|
#include <cryptopp/filters.h>
|
||||||
|
#include <cryptopp/serpent.h>
|
||||||
|
#include <cryptopp/ccm.h>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
#include "generator.h"
|
#include "generator.h"
|
||||||
|
@ -21,12 +27,12 @@ Generator::Generator() /*noexcept*/ {
|
||||||
}
|
}
|
||||||
Generator::~Generator() = default;
|
Generator::~Generator() = default;
|
||||||
|
|
||||||
|
|
||||||
void Generator::initialize_generator(){
|
void Generator::initialize_generator(){
|
||||||
G.k = 0;
|
std::memset(G.k, 0x00, G.k.size());
|
||||||
G.ctr = 0;
|
G.ctr = 0;
|
||||||
fmt::print("Generator initialized\n");
|
fmt::print("Generator initialized\n");
|
||||||
// TODO(me): rm this, it's here just so it's called
|
reseed("fortuna");
|
||||||
do_sha("fortuna");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto Generator::get_state() -> G_state {
|
auto Generator::get_state() -> G_state {
|
||||||
|
@ -34,11 +40,17 @@ auto Generator::get_state() -> G_state {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Generator::reseed(const std::string& s) -> void {
|
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<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
|
// TODO(me): wrap do_sha in a try-catch
|
||||||
std::string a = do_sha(to_be_hashed);
|
std::string a{do_sha(to_be_hashed)};
|
||||||
// str, size, base
|
std::memcpy(&a[0], &G.k[0], a.size());
|
||||||
G.k = stol(a,nullptr,10);
|
|
||||||
++G.ctr;
|
++G.ctr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,44 +66,101 @@ auto Generator::do_sha(const std::string& k_n_s) -> std::string {
|
||||||
CryptoPP::SHA3_256 sha3_256;
|
CryptoPP::SHA3_256 sha3_256;
|
||||||
|
|
||||||
digest.erase();
|
digest.erase();
|
||||||
const std::string to_compare{
|
// FIXME: commented to test reseeds
|
||||||
"8eccfbbbc9df48b4272e6237ce45aad8fbe59629b4963c4dcda5716e61bb34e1"
|
// const std::string to_compare{
|
||||||
};
|
// "8eccfbbbc9df48b4272e6237ce45aad8fbe59629b4963c4dcda5716e61bb34e1"
|
||||||
|
// };
|
||||||
|
|
||||||
CryptoPP::StringSource bar(k_n_s,true,
|
CryptoPP::StringSource bar(k_n_s,true,
|
||||||
new HashFilter(sha3_256,new HexEncoder(new StringSink(digest),false))
|
new HashFilter(sha3_256,new HexEncoder(new StringSink(digest),false))
|
||||||
);
|
);
|
||||||
assert(digest == to_compare);
|
// FIXME: commented to test reseeds
|
||||||
fmt::print("{}\n", digest);
|
// assert(digest == to_compare); // debugging - was used to test that hash
|
||||||
digest.erase();
|
// of "fortuna" was correctly generated
|
||||||
|
// digest.erase(); // actually do not erase now
|
||||||
|
// fmt::print("digest: {}\n", digest); // debugging
|
||||||
|
|
||||||
// return digest;
|
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Generator::do_crypto() -> std::string {
|
auto Generator::do_crypto() -> std::string {
|
||||||
/* this function calls the block cipher
|
/* this function calls the block cipher
|
||||||
* returns a string of k*(16 bytes);
|
* returns a string of k*(16 bytes);
|
||||||
* do whatever atm */
|
* 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<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 {
|
auto Generator::generate_blocks(unsigned int k_blocks) -> std::string {
|
||||||
assert ((G.ctr!=0) && "Counter is not 0, generator has been seeded");
|
assert ((G.ctr!=0) && "Counter is not 0, generator has been seeded");
|
||||||
|
// fmt::print("k_blocks -> {}\n", k_blocks); // debugging
|
||||||
std::string r{""};
|
std::string r{""};
|
||||||
for (int i = 0; i < k_blocks; ++i) {
|
for (int i = 0; i < k_blocks; ++i) {
|
||||||
r += do_crypto();
|
r += do_crypto();
|
||||||
++G.ctr;
|
++G.ctr;
|
||||||
}
|
}
|
||||||
|
// fmt::print("r from generate_blocks -> {}\n", r); // debugging
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Generator::generate_random_data(uint n) -> std::string {
|
auto Generator::generate_random_data(uint n) -> std::string {
|
||||||
std::string r{""};
|
// fmt::print("n -> {}\n", n); // debugging
|
||||||
if (n < 0){
|
if (n < 0){
|
||||||
/* this should not be possible */
|
/* this should not be possible */
|
||||||
fmt::print("[*] error: n cannot be < 0\n");
|
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");
|
fmt::print("[*] error: n cannot be > 2^20\n");
|
||||||
throw std::invalid_argument("n cannot be > 2^20");
|
throw std::invalid_argument("n cannot be > 2^20");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* do magic to compute r
|
/* do magic to compute r
|
||||||
* r ← first-n-bytes(GenerateBlocks(G, ceil(n/16) )) */
|
* r ← first-n-bytes(GenerateBlocks(G, ceil(n/16) )) */
|
||||||
std::string rr = generate_blocks(ceil(n/16));
|
// n is number of bytes, i.e. pass n*8 to get number of bits
|
||||||
r = rr.substr(0,n);
|
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 */
|
/* re-key */
|
||||||
// TODO: check conversions
|
std::string nu_G_k{generate_blocks(2)};
|
||||||
G.k = stoul(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;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
14
generator.h
14
generator.h
|
@ -1,6 +1,9 @@
|
||||||
#ifndef FORTUNA_GENERATOR_H
|
#ifndef FORTUNA_GENERATOR_H
|
||||||
#define FORTUNA_GENERATOR_H
|
#define FORTUNA_GENERATOR_H
|
||||||
|
|
||||||
|
#include <cryptopp/cryptlib.h>
|
||||||
|
#include <cryptopp/secblock.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace fortuna {
|
namespace fortuna {
|
||||||
|
@ -10,11 +13,15 @@ class Generator {
|
||||||
public:
|
public:
|
||||||
Generator(); // ad noexcept: perhaps _do_ throw*
|
Generator(); // ad noexcept: perhaps _do_ throw*
|
||||||
~Generator();
|
~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:
|
private:
|
||||||
struct G_state {
|
struct G_state {
|
||||||
int64_t k;
|
// 32*8
|
||||||
|
static constexpr const std::size_t k_length{32};
|
||||||
|
CryptoPP::FixedSizeSecBlock<CryptoPP::byte, k_length> k;
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
unsigned __int128 ctr;
|
unsigned __int128 ctr;
|
||||||
|
@ -30,8 +37,7 @@ private:
|
||||||
|
|
||||||
auto do_crypto() -> std::string;
|
auto do_crypto() -> std::string;
|
||||||
|
|
||||||
/* n is the number of random bytes to generate */
|
auto generate_blocks(unsigned int k_blocks) -> std::string;
|
||||||
auto generate_random_data(uint n) -> std::string;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
auto get_state() -> Generator::G_state;
|
auto get_state() -> Generator::G_state;
|
||||||
|
|
3
main.cpp
3
main.cpp
|
@ -1,11 +1,10 @@
|
||||||
#include "fortuna.h"
|
#include "fortuna.h"
|
||||||
#include "generator.h"
|
|
||||||
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
fmt::print("[*] doing evil stuff\n");
|
fmt::print("[*] doing evil stuff\n");
|
||||||
fortuna::Fortuna f;
|
fortuna::Fortuna f;
|
||||||
f.random_data(4);
|
f.random_data(4); // number of bytes requested
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue