Repository: LadybirdBrowser/ladybird
File: AK/Random.cpp (C++)

Code before:
        ret = getrandom(buf, size, 0);

        if (ret == -1 && errno != EINTR) [[unlikely]]
            return Error::from_errno(errno);

        // Per the manual:
        //
        // > On success, getrandom() returns the number of bytes that were
        // > copied to the buffer buf.  This may be less than the number of
        // > bytes requested via buflen if either GRND_RANDOM was specified in
        // > flags and insufficient entropy was present in the random source or
        // > the system call was interrupted by a signal.
        //
        // > The user of getrandom() *must* always check the return value, to
        // > determine whether either an error occurred or fewer bytes than
        // > requested were returned.
        if (ret > 0) [[likely]] {
            size -= ret;
            buf += ret;
        }
    }
#elif defined(AK_OS_WINDOWS)
    // ProcessPRNG is currently the best option:
    // - It includes very few imports and doesn't create any handles, therefore being the best for sandboxing and reducing unnecessary imports.
    // - The BCryptPrimitives DLL is already being imported by libtommath and probably other dependencies so importing it here doesn't matter.
    // - It's very simple and is documented to always return TRUE
    //
    // See:
    // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
    // https://github.com/rust-random/getrandom/blob/59d52049773a4f974da4818cf58f96edfc9159f1/src/backends/windows.rs#L1-L22
    // https://docs.google.com/document/d/13n1t5ak0yofzcadQCF7Ew5TewSUkNfQ3n-IYodjeRYc/edit
    // https://issues.chromium.org/issues/40088338
    g_system.ProcessPrng((PBYTE)buf, size);
#else
    // There shouldn't be a build target where this can happen.
    static_assert(false, "This build target doesn't have a valid CSPRNG interface specified in AK/Random.cpp. This needs to be fixed before you can build for this target.");
#endif
    return {};
}

namespace AK {

void fill_with_random(Bytes bytes)
{
    MUST(csprng(bytes.data(), bytes.size()));
}

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
    // with a group around 2**32-1 that is a bit too small. For this reason, the implementation

Review comment (suggestion):
This is all very verbose - please put your reasoning for API choices in the commit messages, and use code comments to explain what or why you're doing something if that's not immediately clear from the code itself.

Code after:
#else
    static_assert(false, "This build target doesn't have a valid CSPRNG interface specified in AK/Random.cpp.");
#endif
    return {};
}

namespace AK {

void fill_with_random(Bytes bytes)
{
    MUST(csprng(bytes.data(), bytes.size()));
}

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
    // with a group around 2**32-1 that is a bit too small. For this reason, the implementation
    // `arc4random() % max_bounds` would be insufficient. Here we compute the last number of the
    // last "full group". Note that if max_bounds is a divisor of UINT32_MAX,
    // then we end up with UINT32_MAX:
    u32 const max_usable = UINT32_MAX - (static_cast<u64>(UINT32_MAX) + 1) % max_bounds;
    auto random_value = get_random<u32>();
    for (int i = 0; i < 20 && random_value > max_usable; ++i) {
        // By chance we picked a value from the incomplete group. Note that this group has size at
        // most 2**31-1, so picking this group has a chance of less than 50%.
        // In practice, this means that for the worst possible input, there is still only a
        // once-in-a-million chance to get to iteration 20. In theory we should be able to loop
        // forever. Here we prefer marginally imperfect random numbers over weird runtime behavior.
        random_value = get_random<u32>();
    }
    return random_value % max_bounds;
}

u64 get_random_uniform_64(u64 max_bounds)
{
    // Uses the same algorithm as `get_random_uniform`,
    // by replacing u64 with u128 and u32 with u64.
    u64 const max_usable = UINT64_MAX - static_cast<u64>((static_cast<u128>(UINT64_MAX) + 1) % max_bounds);
    auto random_value = get_random<u64>();
    for (int i = 0; i < 20 && random_value > max_usable; ++i) {
        random_value = get_random<u64>();
    }
    return random_value % max_bounds;
}

XorShift128PlusRNG::XorShift128PlusRNG()
{
    // Splitmix64 is used as xorshift is sensitive to being seeded with all 0s
    u64 seed = get_random<u64>();
    m_low = splitmix64(seed);
    seed = get_random<u64>();