From b150fde4dadd0b610b4617dc0837f6ce393c9a9e Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Mon, 17 Mar 2025 23:10:26 +0100 Subject: [PATCH] Improve the situation with #9881 - Use `unpredictableSeed` in `randomUUID()` (#10671) --- std/uuid.d | 63 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/std/uuid.d b/std/uuid.d index 09ce2f73f..2f240732d 100644 --- a/std/uuid.d +++ b/std/uuid.d @@ -1204,32 +1204,55 @@ public struct UUID * * This function is not supported at compile time. * + * Bugs: + * $(LINK2 + * https://github.com/dlang/phobos/issues/9881 + * Issue #9881 - Randomness in UUID generation is insufficient + * ) + * + * Warning: + * $(B This function must not be used for cryptographic purposes.) + * UUIDs generated by this function do not have sufficient randomness + * for all use cases. + * This especially applies to the overload that accepts a caller-provided RNG. + * At the moment, Phobos does not provide a $(I cryptographically-secure + * pseudo-random number generator (CSPRNG)) that could be supplied to this + * function. + * + * While the function overload with no parameters will attempt to use the + * system CSPRNG where available and implemented, there are no guarantees. + * See $(REF unpredictableSeed, std, random) for details. + * * Params: * randomGen = uniform RNG * See_Also: $(REF isUniformRNG, std,random) */ @nogc nothrow @safe UUID randomUUID() { - import std.random : rndGen; - // A PRNG with fewer than `n` bytes of state cannot produce - // every distinct `n` byte sequence. - static if (typeof(rndGen).sizeof >= UUID.sizeof) - { - return randomUUID(rndGen); - } - else - { - import std.random : unpredictableSeed, Xorshift192; - static assert(Xorshift192.sizeof >= UUID.sizeof); - static Xorshift192 rng; - static bool initialized; - if (!initialized) - { - rng.seed(unpredictableSeed); - initialized = true; - } - return randomUUID(rng); - } + import std.conv : bitCast; + import std.random : unpredictableSeed; + + enum bufferSize = UUID.data.sizeof; + ubyte[bufferSize] data; + + static assert(ulong.sizeof * 2 == bufferSize); + const half1 = unpredictableSeed!ulong(); + const half2 = unpredictableSeed!ulong(); + + data[0 .. ulong.sizeof] = (() @trusted => half1.bitCast!(ubyte[ulong.sizeof]))(); + data[ulong.sizeof .. $] = (() @trusted => half2.bitCast!(ubyte[ulong.sizeof]))(); + + // set variant + // must be 0b_10xxxxxx + data[8] &= 0b_10_111111; + data[8] |= 0b_10_000000; + + // set version + // must be 0b_0100xxxx + data[6] &= 0b_0100_1111; + data[6] |= 0b_0100_0000; + + return UUID(data); } /// ditto