random.hpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #pragma once
  2. #include <nall/arithmetic.hpp>
  3. #include <nall/chrono.hpp>
  4. #include <nall/range.hpp>
  5. #include <nall/serializer.hpp>
  6. #include <nall/stdint.hpp>
  7. #if !defined(PLATFORM_ANDROID)
  8. #include <nall/cipher/chacha20.hpp>
  9. #endif
  10. #if defined(PLATFORM_LINUX) && __has_include(<sys/random.h>)
  11. #include <sys/random.h>
  12. #elif defined(PLATFORM_ANDROID) && __has_include(<sys/syscall.h>)
  13. #include <sys/syscall.h>
  14. #elif defined(PLATFORM_WINDOWS) && __has_include(<wincrypt.h>)
  15. #include <wincrypt.h>
  16. #else
  17. #include <stdio.h>
  18. #endif
  19. namespace nall {
  20. template<typename Base> struct RNG {
  21. template<typename T = uint64_t> auto random() -> T {
  22. T value = 0;
  23. for(uint n : range((sizeof(T) + 3) / 4)) {
  24. value = value << 32 | (uint32_t)static_cast<Base*>(this)->read();
  25. }
  26. return value;
  27. }
  28. template<typename T = uint64_t> auto bound(T range) -> T {
  29. T threshold = -range % range;
  30. while(true) {
  31. T value = random<T>();
  32. if(value >= threshold) return value % range;
  33. }
  34. }
  35. protected:
  36. auto randomSeed() -> uint256_t {
  37. uint256_t seed = 0;
  38. #if defined(PLATFORM_BSD) || defined(PLATFORM_MACOS)
  39. for(uint n : range(8)) seed = seed << 32 | (uint32_t)arc4random();
  40. #elif defined(PLATFORM_LINUX) && __has_include(<sys/random.h>)
  41. getrandom(&seed, 32, GRND_NONBLOCK);
  42. #elif defined(PLATFORM_ANDROID) && __has_include(<sys/syscall.h>)
  43. syscall(__NR_getrandom, &seed, 32, 0x0001); //GRND_NONBLOCK
  44. #elif defined(PLATFORM_WINDOWS) && __has_include(<wincrypt.h>)
  45. HCRYPTPROV provider;
  46. if(CryptAcquireContext(&provider, nullptr, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
  47. CryptGenRandom(provider, 32, (BYTE*)&seed);
  48. CryptReleaseContext(provider, 0);
  49. }
  50. #else
  51. srand(time(nullptr));
  52. for(uint n : range(32)) seed = seed << 8 | (uint8_t)rand();
  53. if(auto fp = fopen("/dev/urandom", "rb")) {
  54. fread(&seed, 32, 1, fp);
  55. fclose(fp);
  56. }
  57. #endif
  58. return seed;
  59. }
  60. };
  61. namespace PRNG {
  62. //Galois linear feedback shift register using CRC64 polynomials
  63. struct LFSR : RNG<LFSR> {
  64. LFSR() { seed(); }
  65. auto seed(maybe<uint64_t> seed = {}) -> void {
  66. lfsr = seed ? seed() : (uint64_t)randomSeed();
  67. for(uint n : range(8)) read(); //hide the CRC64 polynomial from initial output
  68. }
  69. auto serialize(serializer& s) -> void {
  70. s.integer(lfsr);
  71. }
  72. private:
  73. auto read() -> uint64_t {
  74. return lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & crc64);
  75. }
  76. static const uint64_t crc64 = 0xc96c'5795'd787'0f42;
  77. uint64_t lfsr = crc64;
  78. friend class RNG<LFSR>;
  79. };
  80. struct PCG : RNG<PCG> {
  81. PCG() { seed(); }
  82. auto seed(maybe<uint32_t> seed = {}, maybe<uint32_t> sequence = {}) -> void {
  83. if(!seed) seed = (uint32_t)randomSeed();
  84. if(!sequence) sequence = 0;
  85. state = 0;
  86. increment = sequence() << 1 | 1;
  87. read();
  88. state += seed();
  89. read();
  90. }
  91. auto serialize(serializer& s) -> void {
  92. s.integer(state);
  93. s.integer(increment);
  94. }
  95. private:
  96. auto read() -> uint32_t {
  97. uint64_t state = this->state;
  98. this->state = state * 6'364'136'223'846'793'005ull + increment;
  99. uint32_t xorshift = (state >> 18 ^ state) >> 27;
  100. uint32_t rotate = state >> 59;
  101. return xorshift >> rotate | xorshift << (-rotate & 31);
  102. }
  103. uint64_t state = 0;
  104. uint64_t increment = 0;
  105. friend class RNG<PCG>;
  106. };
  107. }
  108. #if !defined(PLATFORM_ANDROID)
  109. namespace CSPRNG {
  110. //XChaCha20 cryptographically secure pseudo-random number generator
  111. struct XChaCha20 : RNG<XChaCha20> {
  112. XChaCha20() { seed(); }
  113. auto seed(maybe<uint256_t> key = {}, maybe<uint192_t> nonce = {}) -> void {
  114. //the randomness comes from the key; the nonce just adds a bit of added entropy
  115. if(!key) key = randomSeed();
  116. if(!nonce) nonce = (uint192_t)clock() << 64 | chrono::nanosecond();
  117. context = {key(), nonce()};
  118. }
  119. private:
  120. auto read() -> uint32_t {
  121. if(!counter) { context.cipher(); context.increment(); }
  122. uint32_t value = context.block[counter++];
  123. if(counter == 16) counter = 0; //64-bytes per block; 4 bytes per read
  124. return value;
  125. }
  126. Cipher::XChaCha20 context{0, 0};
  127. uint counter = 0;
  128. friend class RNG<XChaCha20>;
  129. };
  130. }
  131. #endif
  132. //
  133. template<typename T = uint64_t> inline auto random() -> T {
  134. static PRNG::PCG pcg; //note: unseeded
  135. return pcg.random<T>();
  136. }
  137. }