From 20662f0dc926bffc212d8dc6f6e59a60ec5275ab Mon Sep 17 00:00:00 2001 From: R-Goc Date: Fri, 11 Apr 2025 22:14:59 +0200 Subject: [PATCH] AK: Add windows support in AK/Random This commit adds support in AK/Random for a high quality RNG on windows. This requires moving the code into a cpp file not to spread windows headers around. --- AK/CMakeLists.txt | 1 + AK/Random.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++ AK/Random.h | 33 +--------------------------- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/AK/CMakeLists.txt b/AK/CMakeLists.txt index 31901bad716..e3c960f8d97 100644 --- a/AK/CMakeLists.txt +++ b/AK/CMakeLists.txt @@ -72,4 +72,5 @@ endif() if (WIN32) # FIXME: Windows on ARM target_link_libraries(AK PRIVATE clang_rt.builtins-x86_64.lib) + target_link_libraries(AK PRIVATE Bcrypt.lib) endif() diff --git a/AK/Random.cpp b/AK/Random.cpp index 5f89c77d496..a10c692e192 100644 --- a/AK/Random.cpp +++ b/AK/Random.cpp @@ -4,12 +4,67 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include +#if defined(AK_OS_WINDOWS) +# include +# include +# include +# include +#endif + namespace AK { +// NOTE: This function is supposed to always give a random number. If possible it is of good quality, but it can fall +// back to rand() if it fails on some systems. For high speed you should probably use a different generator. +// See MathObject::random() from LibJS. Where cryptographic security is needed use LibCrypto/SecureRandom.h. +void fill_with_random([[maybe_unused]] Bytes bytes) +{ +#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) || AK_LIBC_GLIBC_PREREQ(2, 36) + arc4random_buf(bytes.data(), bytes.size()); +#elif defined(OSS_FUZZ) +#else + auto fill_with_random_fallback = [&]() { + for (auto& byte : bytes) + byte = rand(); + }; + +# if defined(__unix__) + // The maximum permitted value for the getentropy length argument. + static constexpr size_t getentropy_length_limit = 256; + auto iterations = bytes.size() / getentropy_length_limit; + + for (size_t i = 0; i < iterations; ++i) { + if (getentropy(bytes.data(), getentropy_length_limit) != 0) { + fill_with_random_fallback(); + return; + } + + bytes = bytes.slice(getentropy_length_limit); + } + + if (bytes.is_empty() || getentropy(bytes.data(), bytes.size()) == 0) + return; +# elif defined(AK_OS_WINDOWS) + + if (bytes.size() > NumericLimits::max()) [[unlikely]] { + fill_with_random_fallback(); + return; + } + + // NOTE: This is more secure than needed. But on modern hardware it be should more than fast enough. + NTSTATUS result = ::BCryptGenRandom(NULL, bytes.data(), bytes.size(), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (result == STATUS_SUCCESS) + return; +# endif + + fill_with_random_fallback(); +#endif +} + u32 get_random_uniform(u32 max_bounds) { // If we try to divide all 2**32 numbers into groups of "max_bounds" numbers, we may end up diff --git a/AK/Random.h b/AK/Random.h index 0d786f8f2ee..8cdf73ca1bb 100644 --- a/AK/Random.h +++ b/AK/Random.h @@ -18,38 +18,7 @@ namespace AK { -inline void fill_with_random([[maybe_unused]] Bytes bytes) -{ -#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) || AK_LIBC_GLIBC_PREREQ(2, 36) - arc4random_buf(bytes.data(), bytes.size()); -#elif defined(OSS_FUZZ) -#else - auto fill_with_random_fallback = [&]() { - for (auto& byte : bytes) - byte = rand(); - }; - -# if defined(__unix__) - // The maximum permitted value for the getentropy length argument. - static constexpr size_t getentropy_length_limit = 256; - auto iterations = bytes.size() / getentropy_length_limit; - - for (size_t i = 0; i < iterations; ++i) { - if (getentropy(bytes.data(), getentropy_length_limit) != 0) { - fill_with_random_fallback(); - return; - } - - bytes = bytes.slice(getentropy_length_limit); - } - - if (bytes.is_empty() || getentropy(bytes.data(), bytes.size()) == 0) - return; -# endif - - fill_with_random_fallback(); -#endif -} +void fill_with_random([[maybe_unused]] Bytes bytes); template inline T get_random()