#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]; } };