The fastest, thread-safe way to create crypto-variant numbers in C #?

It is noted that the crypto-random number generator is not thread-safe when generating random numbers in parallel across several streams. The generator used is RNGCryptoServiceProvider and seems to repeat long pieces of random bits (128 bits). Code for reproducing this is shown below.

With the exception of using locks to protect access to the RNGCryptoServiceProvider instance (which kills the entire speed point here), does anyone have a faster way to generate crypto numbers?

 using System; using System.Runtime.Caching; using System.Security.Cryptography; using System.Threading.Tasks; namespace ParallelRandomness { class Program { static void Main(string[] args) { var test = new Test(); Console.Write("Serialized verion running ... "); test.Run(false); Console.WriteLine(); Console.Write("Parallelized verion running ... "); test.Run(true); Console.WriteLine(Environment.NewLine + "Done."); Console.ReadLine(); } } class Test { private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider(); private readonly byte[] _randomBytes = new byte[128 / 8]; private int collisionCount = 0; private readonly object collisionCountLock = new object(); public void Run(bool parallel) { const int numOfRuns = 100000; const int startInclusive = 1; const int endExclusive = numOfRuns + startInclusive; if (parallel) Parallel.For(startInclusive, endExclusive, x => GenRandomByteArrays(x)); else { for (var i = startInclusive; i < endExclusive; i++) GenRandomByteArrays(i); } } private void GenRandomByteArrays(long instance) { _rng.GetBytes(_randomBytes); var randomString = Convert.ToBase64String(_randomBytes); var cache = MemoryCache.Default; if (cache.Contains(randomString)) { // uh-oh! lock (collisionCountLock) { Console.WriteLine(Environment.NewLine + "Instance {0}: Collision count={1}. key={2} already in cache. ", instance, ++collisionCount, randomString); } } else { MemoryCache.Default.Add(randomString, true, DateTimeOffset.UtcNow.AddMinutes(5)); Console.Write(instance % 2 == 0 ? "\b-" : "\b|"); // poor man activity indicator } } } } 
+1
source share
1 answer

The documentation for RNGCryptoServiceProvider states:

This type is thread safe.

Your code does not show that RNGCryptoServiceProvider not thread safe, since you are using the same array in multiple threads. Reusing this array is not thread safe, even if RNGCryptoServiceProvider .

Regarding performance, I want to note that creating a new instance of RNGCryptoServiceProvider very cheap. The dear part is the overhead for each GetBytes call.

So, if you have performance problems, the first thing I will try is to ask for more data at a time and break it myself. If this is still not enough, use the stream cipher planted by the CSPRNG system.

+6
source

Source: https://habr.com/ru/post/1204685/


All Articles