Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I cache RNGCryptoServiceProvider/RandomNumberGenerator instances?

I've read lots of times about the efficiency of the Regex class and how important it is to either call it's static methods or cache the regex instance.

I'd like to know if the same problems would be observable if I instantiated the RNGCryptoServiceProvider class multiple times instead of caching a single instance of the class and calling GetBytes on it.

Instantiating it every time I need a random number would simplify my code a bit since I don't have to worry about a disposable instance hanging and propagating an IDisposable interface across a bunch of classes.

The only thing I found about this is that creating an instance of a RNGCryptoServiceProvider should be very fast, but I'd still like to see confirmation and what would the best practices be.

Would there also be any differences in the random numbers being generated if I instantiated the class every time versus using the same instance?

like image 525
julealgon Avatar asked Oct 14 '14 23:10

julealgon


1 Answers

Repeated construction using the default constructor shouldn't have any ill effects for performance or randomness quality.

Let's take a look at the source code...

#if !FEATURE_PAL
        [System.Security.SecuritySafeCritical]  // auto-generated
        public RNGCryptoServiceProvider() : this((CspParameters) null) {} 
#else // !FEATURE_PAL
        public RNGCryptoServiceProvider() { } 
#endif // !FEATURE_PAL 

The FEATURE_PAL directive has to do with windows vs. non-windows platforms. But we don't need to know the details; let's just look at both true and false cases.

First, it's clear that if FEATURE_PAL is enabled, there's no code in the default constructor.

In the other case, the constructor calls a specific constructor with null CspParameters. That other constructor looks like this:

[System.Security.SecuritySafeCritical]  // auto-generated 
public RNGCryptoServiceProvider(CspParameters cspParams) {
   if (cspParams != null) { 
         m_safeProvHandle = Utils.AcquireProvHandle(cspParams);
         m_ownsHandle = true;
   }
   else { 
         m_safeProvHandle = Utils.StaticProvHandle;
         m_ownsHandle = false; 
   } 
}

cspParams will always be null, so the constructor is getting the value of Utils.StaticProvHandle. That getter looks like this:

#if !FEATURE_PAL 
        [System.Security.SecurityCritical /*auto-generated*/] 
        private static SafeProvHandle _safeProvHandle = null;
        internal static SafeProvHandle StaticProvHandle { 
            [System.Security.SecurityCritical]  // auto-generated
            get {
                if (_safeProvHandle == null) {
                    lock (InternalSyncObject) { 
                        if (_safeProvHandle == null) {
                            SafeProvHandle safeProvHandle = AcquireProvHandle(new CspParameters(DefaultRsaProviderType)); 
                            Thread.MemoryBarrier(); 
                            _safeProvHandle = safeProvHandle;
                        } 
                    }
                }
                return _safeProvHandle;
            } 
        }
#endif // !FEATURE_PAL 

It's backed by a static variable. The getter uses some locks during first initialization, but subsequent calls simply return the static variable.

Now let's look back at RNGCryptoServiceProvider.cs and look at the Dispose method:

[System.Security.SecuritySafeCritical]  // auto-generated
protected override void Dispose(bool disposing) {
   base.Dispose(disposing);

   if (disposing && m_ownsHandle) {
         m_safeProvHandle.Dispose(); 
   } 
}

m_ownsHandle is false if the default constructor was called, so it never disposes anything.

So all that happens during each construction+disposal is some simple variable access.

like image 72
Mike Asdf Avatar answered Sep 26 '22 01:09

Mike Asdf