diff --git a/ChaPRNG.h b/ChaPRNG.h new file mode 100644 index 0000000..78738d4 --- /dev/null +++ b/ChaPRNG.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include + +#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) +#define QR(a, b, c, d) ( \ + a += b, d ^= a, d = ROTL(d,16), \ + c += d, b ^= c, b = ROTL(b,12), \ + a += b, d ^= a, d = ROTL(d, 8), \ + c += d, b ^= c, b = ROTL(b, 7)) + +// +// Based on code from RFC 7539 +// +template +void chacha_do_rounds(std::array& out) { + static_assert(ROUNDS%2 == 0, "ChaPRNG: Rounds count MUST be even."); + for (int i = 0; i < ROUNDS; i += 2) { + // Odd round + QR(out[0], out[4], out[ 8], out[12]); // column 0 + QR(out[1], out[5], out[ 9], out[13]); // column 1 + QR(out[2], out[6], out[10], out[14]); // column 2 + QR(out[3], out[7], out[11], out[15]); // column 3 + // Even round + QR(out[0], out[5], out[10], out[15]); // diagonal 1 (main diagonal) + QR(out[1], out[6], out[11], out[12]); // diagonal 2 + QR(out[2], out[7], out[ 8], out[13]); // diagonal 3 + QR(out[3], out[4], out[ 9], out[14]); // diagonal 4 + } +} + +template +std::array chacha_block(std::array& in) +{ + std::array out; + int i; + + if constexpr(DIRECT) { + chacha_do_rounds(in); + return out; + }else { + for (i = 0; i < 16; ++i) + out[i] = in[i]; + + chacha_do_rounds(out); + + if constexpr(REMIX) { + for (i = 0; i < 16; ++i) + out[i] += in[i]; + } + + return out; + } +} + + +template +class ChaPRNG { + std::array source; + std::array current; + uint32_t it = 0; +public: + ChaPRNG(std::array seed = {0}) { + source[0] = 'expa'; + source[1] = 'nd 3'; + source[2] = '2-by'; + source[3] = 'te k'; + std::copy(seed.begin(), seed.end(), source.begin()+4); + } + + uint32_t operator()() { + auto spot = it & 0b1111; + if(spot == 0) { + source[12]++; + if constexpr (DIRECT) { + chacha_block(source); + } else { + current = chacha_block(source); + } + } + ++it; + if constexpr (DIRECT) + return source[spot]; + else + return current[spot]; + } +}; \ No newline at end of file