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