9

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)?

Lucca Ferri
  • 1,308
  • 12
  • 23
  • 2
    `Is it safer now for cryptography?` No. As in [docs](https://learn.microsoft.com/en-us/dotnet/api/system.random?redirectedfrom=MSDN&view=netframework-4.8): "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" –  Sep 12 '19 at 10:57
  • The problem is that you´re recreating a new `Random` again and again. You should create it once and only once. – MakePeaceGreatAgain Sep 12 '19 at 11:00
  • 1
    @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 Sep 12 '19 at 11:01
  • @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 Sep 12 '19 at 11:02
  • @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 Sep 12 '19 at 11:03
  • @bradbury9 that source is for .Net Framework, not dotnet core. – Luke Parker Sep 12 '19 at 11:22

1 Answers1

8

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

Cleptus
  • 3,446
  • 4
  • 28
  • 34
Luke Parker
  • 299
  • 1
  • 10
  • This is smart. Thanks for your answer, I wasn't aware of this change. Is it only for corefx I presume? – Lucca Ferri Sep 12 '19 at 11:06
  • 1
    As of .Net Framework 4.8, the default seed is still `Environment.TickCount` – Luke Parker Sep 12 '19 at 11:08
  • 1
    Any ideas why corefx has a different implementation? – Lucca Ferri Sep 12 '19 at 11:09
  • 1
    Not sure, but my guess is that Microsoft is mainly working on dotnet core, and it is *safer* to allow multiple instances, instead of leaving unwanted results from having multiple instances. Even if multiple instances aren't in a tight loop, there might be several instances in separate classes. – Luke Parker Sep 12 '19 at 11:14
  • 1
    Maybe the wanted to get rid of the enviroment? .net core is supposed to be platform agnostic so they would not relay on `Enviroment.TickCount`. – Cleptus Sep 12 '19 at 11:14
  • 3
    After some digging through the corefx issues on GitHub, I found [this one related to the random seed](https://github.com/dotnet/coreclr/issues/1919), which references [this discussion](https://github.com/dotnet/coreclr/pull/2192) – Luke Parker Sep 12 '19 at 11:18