Skip to content

Instantly share code, notes, and snippets.

@maciejczyzewski
Last active January 12, 2017 21:28
Show Gist options
  • Save maciejczyzewski/d3acad06cc5019b83a7bc0b3457f7c53 to your computer and use it in GitHub Desktop.
Save maciejczyzewski/d3acad06cc5019b83a7bc0b3457f7c53 to your computer and use it in GitHub Desktop.
Appendix from https://eprint.iacr.org/2016/468 rewritten to C++
#ifndef CHAOS_HH
#define CHAOS_HH
#include <iostream>
#include <stdexcept>
#include <vector>
namespace chaos { //::chaos ////////////////////////////////////////////////////
namespace engines { //::chaos::engines /////////////////////////////////////////
/* NCG written in 2015 by Maciej A. Czyzewski
To the extent possible under law, the author has dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
class ncg {
public:
// metadata
static const std::string name, authors;
// typename
const size_t __size_push = 32, __size_pull = 32;
// defaults
const size_t __default_space = 8, __default_time = 1;
const std::vector<uint8_t> __default_key = {0x14, 0x15, 0x92, 0x65,
0x35, 0x89, 0x79, 0x32};
// interface
void __push(uint32_t);
uint32_t __pull(void);
// cleaning
void __reset(void);
protected:
// costs
size_t __cost_space = 0, __cost_time = 0;
// methods
virtual void __set_key(std::vector<uint8_t> value) {
// resize key if needed
value.resize(this->__cost_space);
// set starting variable
for (std::vector<uint8_t>::size_type i = 0; i != value.size(); i++) {
// in NCG one hybrid system is build upon two cells in buffer
this->buffer[i * 2] = (value[i] >> 4) & 0x0F;
this->buffer[i * 2 + 1] = (value[i] >> 0) & 0x0F;
}
}
virtual void __set_space(size_t value) {
// set new space parameter
this->__cost_space = value;
// resize machine spaces if needed
this->buffer.resize(this->__cost_space * 2);
}
virtual void __set_time(size_t value) {
// set new time parameter
this->__cost_time = value;
}
private:
// spaces in algorithm
std::vector<uint16_t> buffer, params = {0, 0};
// variables in the algorithm
uint16_t a, b, Y;
// S - seed, I - increment, X - mask, i - temporary
uint32_t S, I, X, i;
};
////////////////////////////////////////////////////////////////////////////////
// metadata
const std::string ncg::name = "NCG (Naive Czyzewski Generator)";
const std::string ncg::authors = "Maciej A. Czyzewski";
// @1: abbreviation for getting values from the tape
#define M(i) ((i) % (this->__cost_space * 2))
// @2: bits rotation formula
#define R(x, y) (((x) << (y)) | ((x) >> (16 - (y))))
void ncg::__push(uint32_t block) {
// preparation
I = block * 0x3C6EF35F;
for (S = block, i = 0; i < this->__cost_space * 2; i++) {
// reinforcement
buffer[M(i)] ^= ((I * (S + 1)) ^ S) >> 16;
buffer[M(i)] ^= ((I * (S - 1)) ^ S) >> 00;
// finalization
I ^= ((buffer[M(I - 1)] + buffer[M(i)]) << 16) ^
((buffer[M(I + 1)] - buffer[M(i)]) << 00);
}
}
uint32_t ncg::__pull(void) {
// variables
a = buffer[M(I + 0)];
b = buffer[M(I + 1)];
// initialization
X = (a + I) * (b - S);
// chaos
Y = (buffer[M(X - b)] << (a % 9)) ^ (buffer[M(X + a)] >> (b % 9));
// rounds
for (i = 0; i < this->__cost_time * 2; i += 2) {
// absorption
params[0] ^= buffer[M(I + i - 2)];
params[1] ^= buffer[M(I + i + 2)];
// mixing + modification
params[0] ^= (params[1] ^= R(Y, params[0] % 17));
buffer[M(I + i - 2)] -= (params[1] += (X & params[0]));
params[1] += (params[0] += R(X, params[1] % 17));
buffer[M(I + i + 2)] += (params[0] += (Y & params[1]));
}
// transformation
buffer[M(I + 0)] = // chaotic map
R(params[0], X % 17) ^ R(params[1], X % 17) ^ (X & a) ^ (Y & b);
buffer[M(I + 1)] = (b >> 1) ^ (-(b & 1u) & 0xB400u); // LFSR
// finalization
X += params[0] ^ (b << 8) ^ (params[1] << 16) ^ (a & 0xFF) ^ ((a >> 8) << 24);
// cleaning
params[0] = params[1] = 0xFFFF;
// increment
I += 2;
return X;
}
void ncg::__reset(void) {
// clear parameters space
std::fill(params.begin(), params.end(), 0);
S = 0x19660D00; // seed is not defined (prime)
I = 0x3C6EF35F; // increment should be set
}
#undef M // @1
#undef R // @2
} //::chaos::engines ///////////////////////////////////////////////////////////
template <class algorithm> class machine : public virtual algorithm {
private:
// memory
std::vector<uint8_t> key;
// cache
uint_fast64_t cache[2] = {0, 0};
size_t padding[2] = {0, 0};
public:
explicit machine(std::vector<uint8_t> key = {});
// configuration
void set_key(std::vector<uint8_t>);
void set_space(size_t);
void set_time(size_t);
// I/O single
void push(uint8_t);
uint8_t pull(void);
// I/O stream
void message(std::vector<uint8_t> &);
std::vector<uint8_t> digest(size_t);
// cleaning
void reset(void);
};
/// methods ////////////////////////////////////////////////////////////////////
template <class algorithm>
machine<algorithm>::machine(std::vector<uint8_t> key) {
// check if algorithm is supported by this container
if (!(this->__size_push == 8 || this->__size_push == 16 ||
this->__size_push == 32 || this->__size_push == 64))
throw std::invalid_argument("wrong __size_push data type. Supported "
"types: uint8_t, uint16_t, uint32_t, uint64_t");
if (!(this->__size_pull == 8 || this->__size_pull == 16 ||
this->__size_pull == 32 || this->__size_pull == 64))
throw std::invalid_argument("wrong __size_pull data type. Supported "
"types: uint8_t, uint16_t, uint32_t, uint64_t");
// check if there is a initial secret key
this->key = key.size() != 0 ? key : this->__default_key;
// configure space and time parameter
this->set_space(this->key.size());
this->set_time(this->__default_time);
// set starting variable
this->reset();
}
template <class algorithm>
void machine<algorithm>::set_key(std::vector<uint8_t> key) {
// check if received key is not empty
this->key = key.size() != 0 ? key : this->__default_key;
this->set_space(this->key.size()); // set new space parameter
this->__set_key(key); // pass via algorithm
};
template <class algorithm> void machine<algorithm>::set_time(size_t value) {
this->__set_time(value); // pass via algorithm
};
template <class algorithm> void machine<algorithm>::set_space(size_t value) {
this->__set_space(value); // pass via algorithm
};
template <class algorithm> void machine<algorithm>::push(uint8_t block) {
// do not use cache if 8 bit
if (this->__size_push == 8)
return this->__push(block);
// add block to cache variable
this->cache[0] ^= block << (this->__size_push - (this->padding[0] + 1) * 8);
if (this->__size_push == (this->padding[0] + 1) * 8) {
// clear cache and push to engine
this->__push(this->cache[0]);
this->padding[0] = this->cache[0] = this->padding[1] = 0;
} else {
this->padding[0]++; // increment
}
}
template <class algorithm> uint8_t machine<algorithm>::pull(void) {
// do not use cache if 8 bit
if (this->__size_pull == 8)
return this->__pull();
// if input cache is not full
if (this->padding[0] > 0) {
this->__push(this->cache[0]); // push to engine
this->padding[0] = this->cache[0] = this->padding[1] = 0;
}
// if there is empty cache
if (this->padding[1] == 0 || this->__size_pull == this->padding[1] * 8) {
this->cache[1] = this->__pull();
this->padding[1] = 0; // reset padding
}
this->padding[1]++; // increment
return (uint8_t)(this->cache[1] >>
(this->__size_pull - this->padding[1] * 8));
}
template <class algorithm>
void machine<algorithm>::message(std::vector<uint8_t> &blocks) {
if (blocks.size() == 0)
return; // empty vector, nothing to do
// try to predict left padding (cache module)
size_t left_padding = this->__size_push / 8 - this->padding[0];
// if input cache is not full
if (this->padding[0] > 0) {
for (size_t i = this->padding[0]; i < this->__size_push / 8; i++) {
this->cache[0] ^= blocks[i - this->padding[0]]
<< (this->__size_push - (i + 1) * 8);
} // send cached bytes
this->__push(this->cache[0]);
} else { // if there is no need to pad
left_padding = 0;
}
// calculate marginal/right padded blocks
size_t right_padding =
(((blocks.size() - left_padding) * 8) % this->__size_push) / 8;
// choose correct datatype used by __push()
switch (this->__size_push) {
case 8:
for (size_t i = left_padding; // pass via algorithm's engine
i < blocks.size() - left_padding - right_padding; i += 1)
this->__push((uint8_t)(blocks[i]));
break;
case 16:
for (size_t i = left_padding; // pass via algorithm's engine
i < blocks.size() - left_padding - right_padding; i += 2)
this->__push(((uint16_t)blocks[i + 1]) | ((uint16_t)blocks[i] << 8));
break;
case 32:
for (size_t i = left_padding; // pass via algorithm's engine
i < blocks.size() - left_padding - right_padding; i += 4)
this->__push(((uint32_t)blocks[i + 3]) | ((uint32_t)blocks[i + 2] << 8) |
((uint32_t)blocks[i + 1] << 16) |
((uint32_t)blocks[i] << 24));
break;
case 64:
for (size_t i = left_padding; // pass via algorithm's engine
i < blocks.size() - left_padding - right_padding; i += 8)
this->__push(
((uint64_t)blocks[i + 7]) | ((uint64_t)blocks[i + 6] << 8) |
((uint64_t)blocks[i + 5] << 16) | ((uint64_t)blocks[i + 4] << 24) |
((uint64_t)blocks[i + 3] << 32) | ((uint64_t)blocks[i + 2] << 40) |
((uint64_t)blocks[i + 1] << 48) | ((uint64_t)blocks[i] << 56));
break;
}
// reset local cache environment
this->padding[0] = this->cache[0] = this->padding[1] = 0;
// blocks that couldn't be used by algorithm __push() function
for (size_t i = 0; i < right_padding; i++)
this->push(blocks[blocks.size() + i - right_padding]);
}
template <class algorithm>
std::vector<uint8_t> machine<algorithm>::digest(size_t length) {
// pass via interface
std::vector<uint8_t> blocks(length);
for (size_t i = 0; i < length; i++)
blocks[i] = this->pull();
return blocks;
}
template <class algorithm> void machine<algorithm>::reset(void) {
// remove cache and padding
this->cache[0] = this->cache[1] = this->padding[0] = this->padding[1] = 0;
// replace memory with our key
this->__set_key(this->key);
// call reset function in algorithm
this->__reset();
}
} //::chaos ////////////////////////////////////////////////////////////////////
#endif // CHAOS_HH
// Testing:
// $ ./fake_dev_random | xxd -l 1024
#include <ctime>
#include <iostream>
#include "chaos.hpp" // library header
// create seed on the base of time
uint8_t seed = static_cast<uint8_t>(time(NULL));
// initialize chaos machine using NCG algorithm/engine
chaos::machine<chaos::engines::ncg> x_machine;
int main(void) {
// configure machine with parameters (t=2, m=256)
x_machine.set_time(2); x_machine.set_space(256);
// initialize with seed (still pseudo-randomness)
x_machine.push(seed); // can be anything
// serve like /dev/random
while (true)
putc_unlocked(x_machine.pull(), stdout);
}
// Testing:
// $ cmp -bl <(./fake_hash_function "Lorem ipsum dolor sit...") \
// <(./fake_hash_function "Lorem ipsum bolor sit...")
#include <iostream>
#include <string>
#include <vector>
#include "chaos.hpp" // library header
// allocate std::vector (our starting variable)
std::vector<uint8_t> y_secret = {0x14, 0x15, 0x92, 0x65,
0x35, 0x89, 0x79, 0x32};
// initialize chaos machine using NCG algorithm/engine
chaos::machine<chaos::engines::ncg> x_machine(y_secret);
int main(int argc, char** argv) {
// allocate std::vector (our message/bitstrings)
std::string y_string = argv[1]; // "Lorem ipsum dolor sit..."
std::vector<uint8_t> y_vector(y_string.begin(), y_string.end());
// make use of chaos machine (push/pull interface)
x_machine.message(y_vector); // push
std::vector<uint8_t> y_result = x_machine.digest(256); // pull
// print values in hexadecimal format
for (size_t i = 0; i < y_result.size(); i++)
printf("%02x", y_result[i]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment