From 9664352a11390d6f9171ed18c536cae9362eab68 Mon Sep 17 00:00:00 2001 From: fgenesis Date: Sat, 1 Feb 2025 05:51:54 +0100 Subject: [PATCH] Add new randomness subsystem --- Aquaria/DSQ.cpp | 3 + Aquaria/Main.cpp | 8 ++ BBGE/CMakeLists.txt | 2 + BBGE/Core.cpp | 12 +++ BBGE/Core.h | 1 + BBGE/Randomness.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++++ BBGE/Randomness.h | 74 ++++++++++++++++++ 7 files changed, 282 insertions(+) create mode 100644 BBGE/Randomness.cpp create mode 100644 BBGE/Randomness.h diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index ab3e942..e6a36b9 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -1362,6 +1362,9 @@ void DSQ::init() title(false); else enqueueJumpState("BitBlotLogo"); + + // Use the time needed to load everything as an extra source of entropy + Randomness::addEntropy(SDL_GetTicks()); } void DSQ::recreateBlackBars() diff --git a/Aquaria/Main.cpp b/Aquaria/Main.cpp index a88fba4..128b488 100644 --- a/Aquaria/Main.cpp +++ b/Aquaria/Main.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "DSQ.h" #include #include +#include "Randomness.h" extern "C" int main(int argc,char *argv[]) @@ -45,6 +46,13 @@ extern "C" int main(int argc,char *argv[]) extraDataDir = AQUARIA_EXTRA_DATA_DIR; #endif + // Couple pointers to help enhance entropy, suppported by the system's ASLR if available + { + void *p = malloc(1); + Randomness::init((uintptr_t)argv, (uintptr_t)&dsqParam, (uintptr_t)&(malloc), (uintptr_t)&p); + free(p); + } + { DSQ dsql(dsqParam, extraDataDir); dsql.init(); diff --git a/BBGE/CMakeLists.txt b/BBGE/CMakeLists.txt index 8b62c91..c1c0c0b 100644 --- a/BBGE/CMakeLists.txt +++ b/BBGE/CMakeLists.txt @@ -65,6 +65,8 @@ set(BBGE_SRCS Quad.h QuadGrid.cpp QuadGrid.h + Randomness.cpp + Randomness.h ReadXML.cpp ReadXML.h Rect.h diff --git a/BBGE/Core.cpp b/BBGE/Core.cpp index a047777..02e540e 100644 --- a/BBGE/Core.cpp +++ b/BBGE/Core.cpp @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "GLLoad.h" #include "RenderBase.h" #include "Window.h" +#include "Randomness.h" #include #include @@ -1152,6 +1153,15 @@ void Core::run(float runTime) dt = (nowTicks-thenTicks)/1000.0; thenTicks = nowTicks; + uint64_t entropy = nowTicks; + entropy <<= 3; + entropy ^= frames; + entropy <<= 3; + entropy += (mouse.buttons.left << 1) | (mouse.buttons.middle << 2) | (mouse.buttons.right << 3); + entropy ^= lastEventTimestamp; + Randomness::addEntropy(entropy); + + if (!avgFPS.empty()) { @@ -1459,6 +1469,8 @@ static const TextInputMapping textInputMap[] void Core::onEvent(const SDL_Event& event) { + lastEventTimestamp = event.common.timestamp; + const bool focus = window->hasFocus(); if(event.type == sdlUserMouseEventID) { diff --git a/BBGE/Core.h b/BBGE/Core.h index 2caed22..d7612bf 100644 --- a/BBGE/Core.h +++ b/BBGE/Core.h @@ -320,6 +320,7 @@ public: bool loopDone; Mouse mouse; + unsigned lastEventTimestamp; AfterEffectManager *afterEffectManager; diff --git a/BBGE/Randomness.cpp b/BBGE/Randomness.cpp new file mode 100644 index 0000000..e0bcd2f --- /dev/null +++ b/BBGE/Randomness.cpp @@ -0,0 +1,182 @@ +// Must be included first to put the implementation here +#include "ascon-sponge.h" + +#include "Base.h" +#include "Randomness.h" +#include +#include +#include "SDLwrap.h" + + +static SlowRand s_sysrand; +static SplitMix64 s_splitmix; +static Xoshiro128 s_xosh; +static FastRand s_fastr; + + +static inline uint32_t rotl32(const uint32_t x, uint32_t k) +{ + return (x << k) | (x >> (32u - k)); +} + +// via https://blog.bithole.dev/blogposts/random-float/ +static inline float int_to_float01(uint32_t r) +{ + union { uint32_t u32; float f; } u; + u.u32 = (r >> 9u) | 0x3f800000; + return u.f - 1.0f; +} + + + +void Randomness::init(uintptr_t a, uintptr_t b, uintptr_t c, uintptr_t d) +{ + uint64_t v[] = + { + uint64_t(a), + uint64_t(rand()), + uint64_t(rand()), + uint64_t(rand()), + uint64_t(time(NULL)), + uint64_t(clock()), + uint64_t(&SDL_GetTicks), + uint64_t(&time), + uint64_t(&s_sysrand), + // These functions should be ok to call without calling SDL_Init() first + uint64_t(SDL_GetThreadID(NULL)), +#if SDL_VERSION_ATLEAST(2, 0, 0) + uint64_t(SDL_GetPerformanceCounter()), + uint64_t(SDL_GetPerformanceFrequency()), + uint64_t(SDL_GetSystemRAM()), + uint64_t(SDL_GetCPUCount()) +#endif + }; + s_sysrand.absorb(v, Countof(v)); + s_splitmix.state = s_sysrand.duplex(b); + + s_xosh.u.q[0] = s_sysrand.duplex(c); + s_xosh.u.q[1] = s_sysrand.duplex(d); +} + +void Randomness::addEntropy(uint64_t x) +{ + s_fastr.state ^= s_sysrand.duplex(x); + s_sysrand.absorb(s_splitmix.next()); // pulling from splitmix permutes it + s_sysrand.absorb(s_xosh.next()); +} + +uint64_t Randomness::getBits64() +{ + return s_splitmix.next(); +} + +uint32_t Randomness::getBits32() +{ + return s_fastr.next(); +} + +float Randomness::getFloat01() +{ + return s_fastr.f01(); +} + + +SplitMix64::SplitMix64() + : state(s_sysrand.next()) +{ +} + +SplitMix64::SplitMix64(uint64_t seed) + : state(seed) +{ +} + +uint64_t SplitMix64::next() +{ + uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15)); + z = (z ^ (z >> 30u)) * UINT64_C(0xbf58476d1ce4e5b9); + z = (z ^ (z >> 27u)) * UINT64_C(0x94d049bb133111eb); + return z ^ (z >> 31u); +} + + + +SlowRand::SlowRand() +{ + ascon_sponge_init(&ascon); +} + + +void SlowRand::absorb(const uint64_t* seed, size_t n) +{ + ascon_sponge_absorb_blocks(&ascon, seed, n); +} + +void SlowRand::absorb(uint64_t val) +{ + ascon_sponge_absorb_block(&ascon, val); +} + +uint64_t SlowRand::duplex(uint64_t duplex) +{ + return ascon_sponge_duplex_block(&ascon, duplex); +} + +uint64_t SlowRand::next() +{ + return ascon_sponge_squeeze_block(&ascon); +} + +Xoshiro128::Xoshiro128() +{ + u.q[0] = s_splitmix.next(); + u.q[1] = s_sysrand.duplex((uintptr_t)this); +} + +Xoshiro128::Xoshiro128(uint32_t a, uint32_t b, uint32_t c, uint32_t d) +{ + u.s[0] = a; + u.s[1] = b; + u.s[2] = c; + u.s[3] = d; +} + +uint32_t Xoshiro128::next() +{ + const uint32_t result = u.s[0] + u.s[3]; + const uint32_t t = u.s[1] << 9; + u.s[2] ^= u.s[0]; + u.s[3] ^= u.s[1]; + u.s[1] ^= u.s[2]; + u.s[0] ^= u.s[3]; + u.s[2] ^= t; + u.s[3] = rotl32(u.s[3], 11); + return result; +} + +float Xoshiro128::f01() +{ + return int_to_float01(next()); +} + +FastRand::FastRand() + : state(s_sysrand.next()) +{ +} + +FastRand::FastRand(uint64_t state) + : state(state) +{ +} + +uint32_t FastRand::next() +{ + // SDL3's rand() + state = state * 0xff1cd035u + 0x05; + return state >> 32u; +} + +float FastRand::f01() +{ + return int_to_float01(next()); +} diff --git a/BBGE/Randomness.h b/BBGE/Randomness.h new file mode 100644 index 0000000..7c82df6 --- /dev/null +++ b/BBGE/Randomness.h @@ -0,0 +1,74 @@ +#ifndef RANDOMNESS_H +#define RANDOMNESS_H + +#include + +#define ASCON_SPONGE_TYPES_ONLY +#include "ascon-sponge.h" + + +namespace Randomness +{ + // Call this once on startup + void init(uintptr_t a, uintptr_t b, uintptr_t c, uintptr_t d); + + // Call this once per frame with some number + void addEntropy(uint64_t x); + + uint64_t getBits64(); + uint32_t getBits32(); + float getFloat01(); // in [0..1) +} + + +class SplitMix64 +{ +public: + SplitMix64(); + SplitMix64(uint64_t seed); + uint64_t next(); + uint64_t state; +}; + + +class SlowRand +{ +public: + SlowRand(); + void absorb(const uint64_t *seed, size_t n); + void absorb(uint64_t val); + uint64_t next(); + uint64_t duplex(uint64_t in); // extract one, put one back in +private: + ascon_state_t ascon; +}; + +// Xoshiro128+ actually +struct Xoshiro128 +{ +public: + Xoshiro128(); + Xoshiro128(uint32_t a, uint32_t b, uint32_t c, uint32_t d); + uint32_t next(); + float f01(); + + union + { + uint32_t s[4]; + uint64_t q[2]; + } u; +}; + +struct FastRand +{ + FastRand(); + FastRand(uint64_t state); + + uint32_t next(); + float f01(); + + uint64_t state; +}; + + +#endif