11using System ;
2+ using System . Diagnostics ;
3+ using System . Runtime . InteropServices ;
24using System . Security . Cryptography ;
5+ using System . Threading ;
36
47namespace SecurityDriven . Inferno
58{
@@ -13,12 +16,47 @@ namespace SecurityDriven.Inferno
1316 /// </summary>
1417 public class CryptoRandom : Random
1518 {
16- static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider ( ) ;
17- const int BUFFER_SIZE = 1024 * 4 ; // 4k buffer seems to work best (empirical experimentation)
18- const int BUFFERED_THRESHOLD = 100 ; // non-buffered approach seems faster beyond this point (empirical experimentation)
19- byte [ ] _buffer = new byte [ BUFFER_SIZE ] ;
20- int _bufferPosition = BUFFER_SIZE ;
21- object lockObj = new Object ( ) ;
19+ static readonly int CACHE_THRESHOLD ; // non-buffered approach seems faster beyond this threshold (empirical experimentation).
20+ const int BYTE_CACHE_SIZE = 4096 ; // 4k buffer seems to work best (empirical experimentation). Buffer must be larger than CACHE_THRESHOLD.
21+ readonly byte [ ] _byteCache = new byte [ BYTE_CACHE_SIZE ] ;
22+ volatile int _byteCachePosition = BYTE_CACHE_SIZE ;
23+
24+ static readonly Action < byte [ ] > _fillBufferWithRandomBytes ;
25+ static readonly BCrypt . BCryptAlgorithmHandle _bcryptAgorithm ;
26+
27+ static CryptoRandom ( )
28+ {
29+ try { _bcryptAgorithm = BCrypt . OpenAlgorithm ( BCrypt . BCRYPT_RNG_ALGORITHM , BCrypt . MS_PRIMITIVE_PROVIDER ) ; }
30+ catch { _bcryptAgorithm = null ; }
31+
32+ if ( _bcryptAgorithm == null )
33+ {
34+ _fillBufferWithRandomBytes = new RNGCryptoServiceProvider ( ) . GetBytes ;
35+ CACHE_THRESHOLD = 104 ;
36+ }
37+ else
38+ {
39+ _fillBufferWithRandomBytes = _bCryptGetBytes ;
40+ CACHE_THRESHOLD = 64 ;
41+ }
42+ } // static ctor
43+
44+ public CryptoRandom ( ) : base ( Seed : 0 )
45+ {
46+ // Minimize the wasted time of calling default System.Random base ctor.
47+ // We can't avoid calling at least some base ctor, ie. 2~3 milliseconds are wasted anyway.
48+ // That's the price of inheriting from System.Random (doesn't implement an interface).
49+ } // ctor
50+
51+ static void _bCryptGetBytes ( byte [ ] buffer )
52+ {
53+ Debug . Assert ( _bcryptAgorithm != null , "algorithm != null" ) ;
54+ Debug . Assert ( ! _bcryptAgorithm . IsClosed && ! _bcryptAgorithm . IsInvalid , "!algorithm.IsClosed && !algorithm.IsInvalid" ) ;
55+ Debug . Assert ( buffer != null , "buffer != null" ) ;
56+
57+ BCrypt . ErrorCode errorCode = BCrypt . DllImportedNativeMethods . BCryptGenRandom ( _bcryptAgorithm , buffer , buffer . Length , 0 ) ;
58+ if ( errorCode != BCrypt . ErrorCode . Success ) throw new CryptographicException ( ( int ) errorCode ) ;
59+ } // _bCryptGetBytes()
2260
2361 #region NextLong()
2462 /// <summary>
@@ -71,13 +109,13 @@ public long NextLong(long minValue, long maxValue)
71109 throw new ArgumentOutOfRangeException ( "minValue" ) ;
72110
73111 ulong diff = decimal . ToUInt64 ( ( decimal ) maxValue - minValue ) ;
74- ulong upperBound = ulong . MaxValue / diff * diff - 1 ;
112+ ulong upperBound = ulong . MaxValue / diff * diff ;
75113
76114 ulong ul ;
77115 do
78116 {
79117 ul = GetRandomULong ( ) ;
80- } while ( ul > upperBound ) ;
118+ } while ( ul >= upperBound ) ;
81119 return decimal . ToInt64 ( ( decimal ) minValue + ul % diff ) ;
82120 } //NextLong()
83121 #endregion
@@ -133,13 +171,13 @@ public override int Next(int minValue, int maxValue)
133171 throw new ArgumentOutOfRangeException ( "minValue" ) ;
134172
135173 long diff = ( long ) maxValue - minValue ;
136- long upperBound = uint . MaxValue / diff * diff - 1 ;
174+ long upperBound = uint . MaxValue / diff * diff ;
137175
138176 uint ui ;
139177 do
140178 {
141179 ui = GetRandomUInt ( ) ;
142- } while ( ui > upperBound ) ;
180+ } while ( ui >= upperBound ) ;
143181 return ( int ) ( minValue + ( ui % diff ) ) ;
144182 } //Next()
145183 #endregion
@@ -156,6 +194,18 @@ public override double NextDouble()
156194 return GetRandomUInt ( ) / max ;
157195 } //NextDouble()
158196
197+ /// <summary>
198+ /// Returns a new count-sized byte array filled with random bytes.
199+ /// </summary>
200+ /// <param name="count">Array length.</param>
201+ /// <returns>Random byte array.</returns>
202+ public byte [ ] NextBytes ( int count )
203+ {
204+ byte [ ] bytes = new byte [ count ] ;
205+ this . NextBytes ( bytes ) ;
206+ return bytes ;
207+ } //NextBytes()
208+
159209 /// <summary>
160210 /// Fills the elements of a specified array of bytes with random numbers.
161211 /// </summary>
@@ -166,76 +216,148 @@ public override double NextDouble()
166216 public override void NextBytes ( byte [ ] buffer )
167217 {
168218 var bufferLength = buffer . Length ;
169- if ( bufferLength > BUFFERED_THRESHOLD )
170- {
171- _rng . GetBytes ( buffer ) ;
172- return ;
173- }
219+ if ( bufferLength == 0 ) return ;
220+ if ( bufferLength > CACHE_THRESHOLD ) { _fillBufferWithRandomBytes ( buffer ) ; return ; }
174221
175- lock ( lockObj )
222+ while ( true )
176223 {
177- if ( ( BUFFER_SIZE - _bufferPosition ) < bufferLength )
224+ int currentByteCachePosition = Interlocked . Add ( ref _byteCachePosition , bufferLength ) ;
225+ if ( currentByteCachePosition <= BYTE_CACHE_SIZE && currentByteCachePosition > 0 )
178226 {
179- _rng . GetBytes ( _buffer ) ;
180- _bufferPosition = 0 ;
227+ Utils . BlockCopy ( _byteCache , currentByteCachePosition - bufferLength , buffer , 0 , bufferLength ) ; return ;
181228 }
182229
183- Utils . BlockCopy ( _buffer , _bufferPosition , buffer , 0 , bufferLength ) ;
184- _bufferPosition += bufferLength ;
185- }
186- } //NextBytes()
187-
188- /// <summary>
189- /// Returns a new count-sized byte array filled with random bytes.
190- /// </summary>
191- /// <param name="count">Array length.</param>
192- /// <returns>Random byte array.</returns>
193- public byte [ ] NextBytes ( int count )
194- {
195- if ( count < 0 ) throw new ArgumentOutOfRangeException ( "count" , "count must be non-negative." ) ;
196- byte [ ] bytes = new byte [ count ] ;
197- this . NextBytes ( bytes ) ;
198- return bytes ;
230+ lock ( _byteCache )
231+ {
232+ currentByteCachePosition = _byteCachePosition ; // atomic read
233+ if ( currentByteCachePosition > ( BYTE_CACHE_SIZE - bufferLength ) || currentByteCachePosition <= 0 )
234+ {
235+ _fillBufferWithRandomBytes ( _byteCache ) ;
236+ _byteCachePosition = bufferLength ; // atomic write
237+ Utils . BlockCopy ( _byteCache , 0 , buffer , 0 , bufferLength ) ;
238+ return ;
239+ }
240+ } // lock
241+ } // while(true)
199242 } //NextBytes()
200243
201244 /// <summary>
202245 /// Gets one random unsigned 32bit integer in a thread safe manner.
203246 /// </summary>
204247 uint GetRandomUInt ( )
205248 {
206- uint rand ;
207- lock ( lockObj )
249+ while ( true )
208250 {
209- if ( ( BUFFER_SIZE - _bufferPosition ) < sizeof ( uint ) )
210- {
211- _rng . GetBytes ( _buffer ) ;
212- _bufferPosition = 0 ;
213- }
251+ int currentByteCachePosition = Interlocked . Add ( ref _byteCachePosition , sizeof ( uint ) ) ;
252+ if ( currentByteCachePosition <= BYTE_CACHE_SIZE && currentByteCachePosition > 0 )
253+ return BitConverter . ToUInt32 ( _byteCache , currentByteCachePosition - sizeof ( uint ) ) ;
214254
215- rand = BitConverter . ToUInt32 ( _buffer , _bufferPosition ) ;
216- _bufferPosition += sizeof ( uint ) ;
217- }
218- return rand ;
255+ lock ( _byteCache )
256+ {
257+ currentByteCachePosition = _byteCachePosition ; // atomic read
258+ if ( currentByteCachePosition > ( BYTE_CACHE_SIZE - sizeof ( uint ) ) || currentByteCachePosition <= 0 )
259+ {
260+ _fillBufferWithRandomBytes ( _byteCache ) ;
261+ _byteCachePosition = sizeof ( uint ) ; // atomic write
262+ return BitConverter . ToUInt32 ( _byteCache , 0 ) ;
263+ }
264+ } // lock
265+ } // while(true)
219266 } //GetRandomUInt()
220267
221268 /// <summary>
222269 /// Gets one random unsigned 64bit integer in a thread safe manner.
223270 /// </summary>
224271 ulong GetRandomULong ( )
225272 {
226- ulong rand ;
227- lock ( lockObj )
273+ while ( true )
228274 {
229- if ( ( BUFFER_SIZE - _bufferPosition ) < sizeof ( ulong ) )
230- {
231- _rng . GetBytes ( _buffer ) ;
232- _bufferPosition = 0 ;
233- }
275+ int currentByteCachePosition = Interlocked . Add ( ref _byteCachePosition , sizeof ( ulong ) ) ;
276+ if ( currentByteCachePosition <= BYTE_CACHE_SIZE && currentByteCachePosition > 0 )
277+ return BitConverter . ToUInt64 ( _byteCache , currentByteCachePosition - sizeof ( ulong ) ) ;
234278
235- rand = BitConverter . ToUInt64 ( _buffer , _bufferPosition ) ;
236- _bufferPosition += sizeof ( ulong ) ;
237- }
238- return rand ;
279+ lock ( _byteCache )
280+ {
281+ currentByteCachePosition = _byteCachePosition ; // atomic read
282+ if ( currentByteCachePosition > ( BYTE_CACHE_SIZE - sizeof ( ulong ) ) || currentByteCachePosition <= 0 )
283+ {
284+ _fillBufferWithRandomBytes ( _byteCache ) ;
285+ _byteCachePosition = sizeof ( ulong ) ; // atomic write
286+ return BitConverter . ToUInt64 ( _byteCache , 0 ) ;
287+ }
288+ } // lock
289+ } // while(true)
239290 } //GetRandomULong()
240291 } //class CryptoRandom
292+
293+ #region BCrypt
294+ internal static class BCrypt
295+ {
296+ internal const string MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider" ; // MS_PRIMITIVE_PROVIDER -- https://msdn.microsoft.com/en-us/library/windows/desktop/aa375479(v=vs.85).aspx
297+ internal const string BCRYPT_RNG_ALGORITHM = "RNG" ; // BCRYPT_RNG_ALGORITHM -- https://msdn.microsoft.com/en-us/library/windows/desktop/aa375534(v=vs.85).aspx
298+
299+ /// <summary>
300+ /// Open a handle to a BCrypt algorithm provider.
301+ /// </summary>
302+ internal static BCryptAlgorithmHandle OpenAlgorithm ( string algorithm , string implementation )
303+ {
304+ Debug . Assert ( ! string . IsNullOrEmpty ( algorithm ) , "!String.IsNullOrEmpty(algorithm)" ) ;
305+ Debug . Assert ( ! string . IsNullOrEmpty ( implementation ) , "!String.IsNullOrEmpty(implementation)" ) ;
306+
307+ BCryptAlgorithmHandle algorithmHandle = null ;
308+ ErrorCode error = DllImportedNativeMethods . BCryptOpenAlgorithmProvider ( out algorithmHandle , algorithm , implementation , AlgorithmProviderOptions . None ) ;
309+ if ( error != ErrorCode . Success ) throw new CryptographicException ( error . ToString ( ) ) ;
310+ return algorithmHandle ;
311+ }
312+
313+ internal sealed class BCryptAlgorithmHandle : Microsoft . Win32 . SafeHandles . SafeHandleZeroOrMinusOneIsInvalid
314+ {
315+ BCryptAlgorithmHandle ( ) : base ( ownsHandle : true ) { }
316+ protected override bool ReleaseHandle ( )
317+ {
318+ return DllImportedNativeMethods . BCryptCloseAlgorithmProvider ( handle , AlgorithmProviderOptions . None ) == ErrorCode . Success ;
319+ }
320+ } // class BCryptAlgorithmHandle
321+
322+ /// <summary>
323+ /// Result codes from BCrypt APIs.
324+ /// </summary>
325+ internal enum ErrorCode
326+ {
327+ Success = 0x00000000 , // STATUS_SUCCESS
328+ AuthenticationTagMismatch = unchecked ( ( int ) 0xC000A002 ) , // STATUS_AUTH_TAG_MISMATCH
329+ BufferToSmall = unchecked ( ( int ) 0xC0000023 ) // STATUS_BUFFER_TOO_SMALL
330+ } // enum ErrorCode
331+
332+ /// <summary>
333+ /// Flags for BCryptOpenAlgorithmProvider.
334+ /// </summary>
335+ [ Flags ] internal enum AlgorithmProviderOptions { None = 0x00000000 }
336+
337+ internal static class DllImportedNativeMethods
338+ {
339+ const string bcrypt_dll = "bcrypt.dll" ;
340+
341+ [ DllImport ( bcrypt_dll ) ]
342+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375458(v=vs.85).aspx
343+ internal static extern ErrorCode BCryptGenRandom (
344+ BCryptAlgorithmHandle hAlgorithm ,
345+ [ In , Out , MarshalAs ( UnmanagedType . LPArray ) ] byte [ ] pbBuffer ,
346+ int cbBuffer ,
347+ int dwFlags ) ;
348+
349+ [ DllImport ( bcrypt_dll ) ]
350+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375479(v=vs.85).aspx
351+ internal static extern ErrorCode BCryptOpenAlgorithmProvider (
352+ [ Out ] out BCryptAlgorithmHandle phAlgorithm ,
353+ [ MarshalAs ( UnmanagedType . LPWStr ) ] string pszAlgId ,
354+ [ MarshalAs ( UnmanagedType . LPWStr ) ] string pszImplementation ,
355+ AlgorithmProviderOptions dwFlags ) ;
356+
357+ [ DllImport ( bcrypt_dll ) ]
358+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375377(v=vs.85).aspx
359+ internal static extern ErrorCode BCryptCloseAlgorithmProvider ( IntPtr hAlgorithm , AlgorithmProviderOptions dwFlags ) ;
360+ } // class DllImportedNativeMathods
361+ } // class BCrypt
362+ #endregion
241363} //ns
0 commit comments