diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs index e403b0a7c220cb..e00508d66c237f 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs @@ -7,6 +7,15 @@ namespace System.Security.Cryptography { internal sealed partial class RandomNumberGeneratorImplementation : RandomNumberGenerator { + // a singleton which always calls into a thread-safe implementation + // and whose Dispose method no-ops + internal static readonly RandomNumberGeneratorImplementation s_singleton = new RandomNumberGeneratorImplementation(); + + // private ctor used only by singleton + private RandomNumberGeneratorImplementation() + { + } + // As long as each implementation can provide a static GetBytes(ref byte buf, int length) // they can share this one implementation of FillSpan. internal static unsafe void FillSpan(Span data) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RandomNumberGenerator.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RandomNumberGenerator.cs index a5c708092aac4d..baad2d1e5c6a20 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RandomNumberGenerator.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RandomNumberGenerator.cs @@ -12,10 +12,7 @@ public abstract class RandomNumberGenerator : IDisposable { protected RandomNumberGenerator() { } - public static RandomNumberGenerator Create() - { - return new RandomNumberGeneratorImplementation(); - } + public static RandomNumberGenerator Create() => RandomNumberGeneratorImplementation.s_singleton; [UnsupportedOSPlatform("browser")] [RequiresUnreferencedCode(CryptoConfig.CreateFromNameUnreferencedCodeMessage)] diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/RandomNumberGeneratorTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/RandomNumberGeneratorTests.cs index 4c9d81f44773c3..d0f2ad8e3e9ff5 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/RandomNumberGeneratorTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/RandomNumberGeneratorTests.cs @@ -12,6 +12,30 @@ namespace System.Security.Cryptography.RNG.Tests { public class RandomNumberGeneratorTests { + [Fact] + public static void Create_ReturnsSingleton() + { + RandomNumberGenerator rng1 = RandomNumberGenerator.Create(); + RandomNumberGenerator rng2 = RandomNumberGenerator.Create(); + + Assert.Same(rng1, rng2); + } + + [Fact] + public static void Singleton_NoopsDispose() + { + byte[] random = new byte[1024]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(random); + RandomDataGenerator.VerifyRandomDistribution(random); + rng.Dispose(); // should no-op if called once + + random = new byte[1024]; + rng.GetBytes(random); // should still work even after earlier Dispose call + RandomDataGenerator.VerifyRandomDistribution(random); + rng.Dispose(); // should no-op if called twice + } + [Theory] [InlineData(2048)] [InlineData(65536)]