2016-01-07 17:14:33 +09:00
|
|
|
#pragma once
|
2010-08-09 22:28:56 +09:00
|
|
|
|
2018-09-02 23:06:41 +09:00
|
|
|
#include <nall/arithmetic.hpp>
|
|
|
|
#include <nall/chrono.hpp>
|
|
|
|
#include <nall/range.hpp>
|
2014-01-05 18:59:17 +09:00
|
|
|
#include <nall/serializer.hpp>
|
|
|
|
#include <nall/stdint.hpp>
|
2019-09-17 03:25:53 +09:00
|
|
|
#if !defined(PLATFORM_ANDROID)
|
2018-09-02 23:06:41 +09:00
|
|
|
#include <nall/cipher/chacha20.hpp>
|
2019-09-17 03:25:53 +09:00
|
|
|
#endif
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2010-08-09 22:28:56 +09:00
|
|
|
namespace nall {
|
2013-05-02 20:25:45 +09:00
|
|
|
|
2022-12-18 02:22:24 +09:00
|
|
|
struct RNGBase {
|
|
|
|
protected:
|
|
|
|
auto randomSeed() -> u256;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename Base> struct RNG : RNGBase {
|
2021-02-08 18:11:00 +09:00
|
|
|
template<typename T = u64> auto random() -> T {
|
2021-05-12 19:37:00 +09:00
|
|
|
u64 value = 0;
|
2021-02-08 18:11:00 +09:00
|
|
|
for(u32 n : range((sizeof(T) + 3) / 4)) {
|
|
|
|
value = value << 32 | (u32)static_cast<Base*>(this)->read();
|
2018-09-02 23:06:41 +09:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
2013-05-02 20:25:45 +09:00
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
template<typename T = u64> auto bound(T range) -> T {
|
2018-09-02 23:06:41 +09:00
|
|
|
T threshold = -range % range;
|
|
|
|
while(true) {
|
|
|
|
T value = random<T>();
|
|
|
|
if(value >= threshold) return value % range;
|
|
|
|
}
|
2010-08-09 22:28:56 +09:00
|
|
|
}
|
2018-09-02 23:06:41 +09:00
|
|
|
};
|
|
|
|
|
2018-10-04 19:11:23 +09:00
|
|
|
namespace PRNG {
|
|
|
|
|
2018-09-02 23:06:41 +09:00
|
|
|
//Galois linear feedback shift register using CRC64 polynomials
|
2018-10-04 19:11:23 +09:00
|
|
|
struct LFSR : RNG<LFSR> {
|
2019-09-13 15:28:42 +09:00
|
|
|
LFSR() { seed(); }
|
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
auto seed(maybe<u64> seed = {}) -> void {
|
|
|
|
lfsr = seed ? seed() : (u64)randomSeed();
|
|
|
|
for(u32 n : range(8)) read(); //hide the CRC64 polynomial from initial output
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
2011-03-20 22:57:55 +09:00
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto serialize(serializer& s) -> void {
|
2021-01-15 06:00:00 +09:00
|
|
|
s(lfsr);
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
2011-03-20 22:57:55 +09:00
|
|
|
|
2013-05-02 20:25:45 +09:00
|
|
|
private:
|
2021-02-08 18:11:00 +09:00
|
|
|
auto read() -> u64 {
|
2018-09-02 23:06:41 +09:00
|
|
|
return lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & crc64);
|
|
|
|
}
|
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
static const u64 crc64 = 0xc96c'5795'd787'0f42;
|
|
|
|
u64 lfsr = crc64;
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2023-11-20 18:26:35 +09:00
|
|
|
friend struct RNG<LFSR>;
|
2018-09-02 23:06:41 +09:00
|
|
|
};
|
|
|
|
|
2018-10-04 19:11:23 +09:00
|
|
|
struct PCG : RNG<PCG> {
|
2019-09-13 15:28:42 +09:00
|
|
|
PCG() { seed(); }
|
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
auto seed(maybe<u32> seed = {}, maybe<u32> sequence = {}) -> void {
|
|
|
|
if(!seed) seed = (u32)randomSeed();
|
2018-09-02 23:06:41 +09:00
|
|
|
if(!sequence) sequence = 0;
|
|
|
|
|
|
|
|
state = 0;
|
|
|
|
increment = sequence() << 1 | 1;
|
|
|
|
read();
|
|
|
|
state += seed();
|
|
|
|
read();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto serialize(serializer& s) -> void {
|
2021-01-15 06:00:00 +09:00
|
|
|
s(state);
|
|
|
|
s(increment);
|
2018-09-02 23:06:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-02-08 18:11:00 +09:00
|
|
|
auto read() -> u32 {
|
|
|
|
u64 state = this->state;
|
2018-09-02 23:06:41 +09:00
|
|
|
this->state = state * 6'364'136'223'846'793'005ull + increment;
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 xorshift = (state >> 18 ^ state) >> 27;
|
|
|
|
u32 rotate = state >> 59;
|
2018-09-02 23:06:41 +09:00
|
|
|
return xorshift >> rotate | xorshift << (-rotate & 31);
|
|
|
|
}
|
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
u64 state = 0;
|
|
|
|
u64 increment = 0;
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2023-11-20 18:26:35 +09:00
|
|
|
friend struct RNG<PCG>;
|
2013-05-02 20:25:45 +09:00
|
|
|
};
|
2011-03-20 22:57:55 +09:00
|
|
|
|
2018-10-04 19:11:23 +09:00
|
|
|
}
|
|
|
|
|
2019-09-17 03:25:53 +09:00
|
|
|
#if !defined(PLATFORM_ANDROID)
|
2018-10-04 19:11:23 +09:00
|
|
|
namespace CSPRNG {
|
|
|
|
|
2018-09-02 23:06:41 +09:00
|
|
|
//XChaCha20 cryptographically secure pseudo-random number generator
|
2018-10-04 19:11:23 +09:00
|
|
|
struct XChaCha20 : RNG<XChaCha20> {
|
|
|
|
XChaCha20() { seed(); }
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
auto seed(maybe<u256> key = {}, maybe<u192> nonce = {}) -> void {
|
2018-09-02 23:06:41 +09:00
|
|
|
//the randomness comes from the key; the nonce just adds a bit of added entropy
|
|
|
|
if(!key) key = randomSeed();
|
2021-02-08 18:11:00 +09:00
|
|
|
if(!nonce) nonce = (u192)clock() << 64 | chrono::nanosecond();
|
2018-09-02 23:06:41 +09:00
|
|
|
context = {key(), nonce()};
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-02-08 18:11:00 +09:00
|
|
|
auto read() -> u32 {
|
2018-09-02 23:06:41 +09:00
|
|
|
if(!counter) { context.cipher(); context.increment(); }
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 value = context.block[counter++];
|
2018-09-02 23:06:41 +09:00
|
|
|
if(counter == 16) counter = 0; //64-bytes per block; 4 bytes per read
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
Cipher::XChaCha20 context{0, 0};
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 counter = 0;
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2023-11-20 18:26:35 +09:00
|
|
|
friend struct RNG<XChaCha20>;
|
2018-09-02 23:06:41 +09:00
|
|
|
};
|
|
|
|
|
2018-10-04 19:11:23 +09:00
|
|
|
}
|
2019-09-17 03:25:53 +09:00
|
|
|
#endif
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2018-10-04 19:11:23 +09:00
|
|
|
//
|
2018-09-02 23:06:41 +09:00
|
|
|
|
2021-05-12 19:37:00 +09:00
|
|
|
inline auto pcgSingleton() -> PRNG::PCG& {
|
|
|
|
static PRNG::PCG pcg;
|
|
|
|
return pcg;
|
|
|
|
}
|
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
template<typename T = u64> inline auto random() -> T {
|
2021-05-12 19:37:00 +09:00
|
|
|
return pcgSingleton().random<T>();
|
2014-01-05 18:59:17 +09:00
|
|
|
}
|
|
|
|
|
2010-08-09 22:28:56 +09:00
|
|
|
}
|
2022-12-18 02:22:24 +09:00
|
|
|
|
|
|
|
#if defined(NALL_HEADER_ONLY)
|
|
|
|
#include <nall/random.cpp>
|
|
|
|
#endif
|