| @ -0,0 +1,88 @@ | |||
| #pragma once | |||
| #include <array> | |||
| #include <algorithm> | |||
| #include <cstdint> | |||
| #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<int ROUNDS> | |||
| void chacha_do_rounds(std::array<uint32_t, 16>& 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<int ROUNDS, bool REMIX = true, bool DIRECT = false> | |||
| std::array<uint32_t, 16> chacha_block(std::array<uint32_t, 16>& in) | |||
| { | |||
| std::array<uint32_t, 16> out; | |||
| int i; | |||
| if constexpr(DIRECT) { | |||
| chacha_do_rounds<ROUNDS>(in); | |||
| return out; | |||
| }else { | |||
| for (i = 0; i < 16; ++i) | |||
| out[i] = in[i]; | |||
| chacha_do_rounds<ROUNDS>(out); | |||
| if constexpr(REMIX) { | |||
| for (i = 0; i < 16; ++i) | |||
| out[i] += in[i]; | |||
| } | |||
| return out; | |||
| } | |||
| } | |||
| template<int ROUNDS, bool REMIX = true, bool DIRECT = false> | |||
| class ChaPRNG { | |||
| std::array<uint32_t, 16> source; | |||
| std::array<uint32_t, 16> current; | |||
| uint32_t it = 0; | |||
| public: | |||
| ChaPRNG(std::array<uint32_t, 12> 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<ROUNDS, REMIX, DIRECT>(source); | |||
| } else { | |||
| current = chacha_block<ROUNDS, REMIX, DIRECT>(source); | |||
| } | |||
| } | |||
| ++it; | |||
| if constexpr (DIRECT) | |||
| return source[spot]; | |||
| else | |||
| return current[spot]; | |||
| } | |||
| }; | |||