7
votes

Today, I was doing some tests in .NET Core, and I have come across some interesting thing.

Before (~ .NET Framework 4), Random used Environment.TickCount, but now I believe this has changed.

Consider the following code:

while (true)
{
    Random random = new Random();
    Console.WriteLine(random.Next(0, 10000));
}

In older .NET Framework versions, the new Random() empty constructor would use Environment.TickCount, which would lead to repetition of pseudo-random values.

So you could expect results like:

542
4211
5244
5244
5244
9501
9501

so on and so fourth.

On the latest .NET Core version using the latest compiler, I have received the following result:

5332
220
3928
524
2973
2840
4965
5667
657
6434
3170
3046
7044

Which is definitely improved.

Other S.O questions demonstrating this behaviour in older versions:

How do I generate a random int number?

generate random numbers with no repeat in c#

Non-repetitive random number

Is C# Random Number Generator thread safe?


My setup: .NET Core 2.2 / latest C# compiler.


The actual question

So my question is, has the PRNG really improved or they just changed constructor to use another default seeds, and, if so, what they're using as a seed? Is it safer now for cryptography (if they actually changed the implementation)?

1
Is it safer now for cryptography? No. As in docs: "To generate a cryptographically secure random number, such as one that's suitable for creating a random password, use the RNGCryptoServiceProvider class or derive a class from System.Security.Cryptography.RandomNumberGenerator" - user2956272
The problem is that you´re recreating a new Random again and again. You should create it once and only once. - HimBromBeere
@HimBromBeere I know about it, and it's exactly what I'm saying. On older versions, it would repeat the number multiple times, but on newer version it's not. - Lucca Ferri
@dyukha I thought as much, but if its internals has changed (which I'm not sure about), then maybe they have made something extra. Not certain. - Lucca Ferri
@AxelKemper I believe the referenceseource is outdated. It's using the default constructor as TickCount, and if I use Environment.TickCount directly I'll have the old and repeated result. - Lucca Ferri

1 Answers

7
votes

In the latest version of dotnet core, the Random default constructor assigns its seed from a hidden private instance of Random. The private instance uses Interop.GetRandomBytes for its seed. New instances use the private instances's Next() result as its seed.

This basically makes it 'safe' to create several random instances in a loop.

Read more on the corefx GitHub:

Related code files: Private Random Instance, Default Constructor - Generate Seed and Private Random Instance - Generate Seed.

Seed change pull request Parameterless constructor seeding improvement #1919