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