Skip to content

Instantly share code, notes, and snippets.

@dwcullop
Created June 27, 2015 04:09
Show Gist options
  • Save dwcullop/cb6e4901fcffcdf72f12 to your computer and use it in GitHub Desktop.
Save dwcullop/cb6e4901fcffcdf72f12 to your computer and use it in GitHub Desktop.
Cryptographically Secure Random Number Generator with a bunch of useful functions
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
namespace DareWare.Utils
{
#region Enum Types
[Flags]
public enum RandomCharSets
{
None = 0x0000,
UpperCase = 0x0001,
LowerCase = 0x0002,
Numbers = 0x0004,
Symbols = 0x0008,
SymbolsExt = 0x0010,
ExtraPunc = 0x0020,
Currency = 0x0040,
GreekUpper = 0x0080,
GreekLower = 0x0100,
Dingbats = 0x0200,
CircleNums = 0x0400,
Accented = 0x0800,
Whitespace = 0x1000,
NewLine = 0x2000
}
#endregion
#region Main Class
/// <summary>
/// RandomEx class that uses the Cryptographically Secure RNG and provides some conversion
/// functionality for ease of use
/// </summary>
/// <remarks>
/// "The generation of random numbers is too important to be left to chance." --Robert R. Coveyou
/// </remarks>
public class RandomEx : IDisposable
{
#region Constructor / Destructor
public RandomEx()
: this( DEFAULT_BUFFER_SIZE )
{
}
public RandomEx( int nBufferSize )
{
this.RNG = new RNGCryptoServiceProvider();
m_RandData = new byte[nBufferSize];
GetBytes( m_RandData );
}
~RandomEx()
{
(this as IDisposable).Dispose();
}
#endregion
#region Properties
public bool Bool { get { return GetBool(); } }
public byte Byte { get { return GetByte(); } }
public char Char { get { return GetChar(); } }
public DateTime DateTime { get { return GetDateTime(); } }
public decimal Decimal { get { return GetDecimal(); } }
public double Double { get { return GetDouble(); } }
public float Float { get { return GetFloat(); } }
public Guid Guid { get { return GetGuid(); } }
public int Int { get { return GetInt(); } }
public long Long { get { return GetLong(); } }
public sbyte SByte { get { return GetSByte(); } }
public short Short { get { return GetShort(); } }
public string String { get { return GetString(); } }
public TimeSpan TimeSpan { get { return GetTime(); } }
public uint UInt { get { return GetUInt(); } }
public ulong ULong { get { return GetULong(); } }
public ushort UShort { get { return GetUShort(); } }
#endregion
#region Basic "Get" Functions
public bool GetBool() { return ((GetByte() & 0x01) == 0x01); }
public byte GetByte() { return m_RandData[EnsureBytes( sizeof( byte ) )]; }
public void GetBytes( byte[] data ) { RNG.GetBytes( data ); }
public char GetChar() { return GetChar( DEFAULT_CHAR_SETS ); }
public double GetDouble() { return BitConverter.ToDouble( m_RandData, EnsureBytes( sizeof( double ) ) ); }
public float GetFloat() { return BitConverter.ToSingle( m_RandData, EnsureBytes( sizeof( float ) ) ); }
#if DEBUG
public Guid GetGuid() { return new Guid( Int, Short, Short, Byte, Byte, Byte, Byte, Byte, Byte, Byte, Byte ); }
#else
public Guid GetGuid() { return Guid.NewGuid(); }
#endif
public int GetInt() { return BitConverter.ToInt32( m_RandData, EnsureBytes( sizeof( int ) ) ); }
public long GetLong() { return BitConverter.ToInt64( m_RandData, EnsureBytes( sizeof( long ) ) ); }
public sbyte GetSByte() { unchecked { return (sbyte)GetByte(); } }
public short GetShort() { return BitConverter.ToInt16( m_RandData, EnsureBytes( sizeof( short ) ) ); }
public uint GetUInt() { return BitConverter.ToUInt32( m_RandData, EnsureBytes( sizeof( uint ) ) ); }
public ulong GetULong() { return BitConverter.ToUInt64( m_RandData, EnsureBytes( sizeof( ulong ) ) ); }
public ushort GetUShort() { return BitConverter.ToUInt16( m_RandData, EnsureBytes( sizeof( ushort ) ) ); }
#endregion
#region Percentage Functions
public double GetPercentage()
{
return (double)((double)GetULong() / (double)UInt64.MaxValue);
}
public decimal GetPercentageDecimal()
{
return decimal.Divide( GetDecimal( false, 0 ), Decimal.MaxValue );
}
/// <summary>Performs a "Success" check boolean based on the probability for success.</summary>
/// <param name="nChanceForSuccess">Percent Change of Success (must be between 0 and 1.0)</param>
/// <returns>Boolean indicating if successful or not</returns>
public bool SuccessCheck( double nChanceForSuccess )
{
Debug.Assert( nChanceForSuccess.IsBetween( 0, 1.0, true ) );
return (GetPercentage() <= nChanceForSuccess);
}
public bool SuccessCheck( decimal nChanceForSuccess )
{
Debug.Assert( nChanceForSuccess.IsBetween( 0.0m, 1.0m, true ) );
return (GetPercentageDecimal() <= nChanceForSuccess);
}
#endregion
#region Pick Functions
public T Pick<T>( params T[] items )
{
return Pick<T>( items.AsEnumerable() );
}
public T Pick<T>( IEnumerable<T> items )
{
if ( items == null )
{
throw new ArgumentNullException( "items" );
}
int count = items.Count();
if ( count == 0 )
{
throw new ArgumentException( "Enumerator had no items", "items" );
}
return items.ElementAt( GetInt( 0, count - 1 ) );
}
#endregion
#region Advanced "Get" Functions
#region Get Char Functions
public char GetChar( string sCharSet ) { return sCharSet[GetInt( 0, sCharSet.Length - 1 )]; }
public char GetChar( string[] sCharSets ) { return GetChar( String.Join( "", sCharSets ) ); }
public char GetChar( RandomCharSets chars ) { return GetChar( chars.GetCharSets() ); }
#endregion
#region Get Decimal Functions
public decimal GetDecimal() { return GetDecimal( GetBool(), 0xff ); }
public decimal GetDecimal( byte scale ) { return GetDecimal( GetBool(), scale ); }
public decimal GetDecimal( bool bNeg ) { return GetDecimal( bNeg, 0xff ); }
public decimal GetDecimal( bool bNeg, byte scale )
{
const byte MAX_SCALE = 28;
if ( scale > MAX_SCALE )
{
scale = (byte)(GetPercentage() * MAX_SCALE);
}
return new decimal( GetInt(), GetInt(), GetInt(), bNeg, scale );
}
#endregion
#region "Ranged" Get (between a min and max)
public decimal GetDecimal( decimal min, decimal max )
{
if ( min <= max )
{
return ((max - min) * (decimal)GetPercentage()) + min;
}
else
{
return GetDecimal( max, min );
}
}
public double GetDouble( double min, double max )
{
if ( min <= max )
{
return ((max - min) * GetPercentage()) + min;
}
else
{
return GetDouble( max, min );
}
}
public float GetFloat( float min, float max )
{
if ( min <= max )
{
return (float)((double)(max - min) * GetPercentage() + (double)min);
}
else
{
return GetFloat( max, min );
}
}
public int GetInt( int min, int max )
{
if ( min <= max )
{
// + 1 So that the min and max range is INCLUSIVE
return (int)((double)((double)max - (double)min + 1.0) * GetPercentage()) + min;
}
else
{
return GetInt( max, min );
}
}
public long GetLong( long min, long max )
{
if ( min <= max )
{
// + 1 So that the min and max range is INCLUSIVE
return (long)((double)(double)(max - (double)min + 1.0) * GetPercentage()) + min;
}
else
{
return GetLong( max, min );
}
}
#endregion
#region "Max" Get Functions
public decimal GetDecimal( decimal max ) { return GetDecimal( 0, max ); }
public double GetFloat( float max ) { return GetFloat( 0, max ); }
public double GetDouble( double max ) { return GetDouble( 0, max ); }
public long GetLong( long max ) { return GetLong( 0, max ); }
public int GetInt( int max ) { return GetInt( 0, max ); }
#endregion
#region Enum Get Functions
public TEnum GetEnum<TEnum>() where TEnum : struct { return (TEnum)GetEnum( typeof( TEnum ) ); }
public object GetEnum( Type t ) { Debug.Assert( t.IsEnum ); return GetElement( Enum.GetValues( t ) ); }
public TEnum GetEnum<TEnum>( Func<TEnum, bool> fnSelector ) where TEnum : struct
{
TEnum val;
do
{
val = GetEnum<TEnum>();
}
while ( !fnSelector( val ) );
return val;
}
#endregion
#region DateTime / TimeSpan Functions
public DateTime GetDateTime( DateTime min, DateTime max )
{
return DateTime.FromBinary( GetLong( min.ToBinary(), max.ToBinary() ) );
}
public DateTime GetDateTime( DateTime start, TimeSpan span )
{
DateTime end = start + span;
if ( span.Ticks >= 0 )
{
return GetDateTime( start, end );
}
else
{
return GetDateTime( end, start );
}
}
public DateTime GetDateTime( TimeSpan span, bool bPlusAndMinus = false )
{
if ( bPlusAndMinus )
{
return GetDateTime( DateTime.Now - span, DateTime.Now + span );
}
else
{
return GetDateTime( DateTime.Today, span );
}
}
public DateTime GetDateTime( int nDaysAhead )
{
DateTime start = DateTime.Now;
return GetDateTime( start, start.AddDays( nDaysAhead ) );
}
public DateTime GetDateTime()
{
return GetDateTime( GetInt( 1, 100 ) );
}
public DateTime GetDateTimeInYear( int year )
{
DateTime start = new DateTime( year, 1, 1 );
return GetDateTime( start, start.AddYears( 1 ) );
}
public DateTime GetDateTimeToday()
{
return DateTime.Today + GetTime();
}
public TimeSpan GetTime()
{
const long ONE_DAY_TICKS = 864000000000;
return new TimeSpan( GetLong( 0, ONE_DAY_TICKS ) );
}
public TimeSpan GetTimeSpan( TimeSpan min, TimeSpan max )
{
return new TimeSpan( GetLong( min.Ticks, max.Ticks ) );
}
#endregion
#endregion
#region Object Functions
public object Get( Type t )
{
switch ( Type.GetTypeCode( t ) )
{
case TypeCode.Boolean: return GetBool();
case TypeCode.Char: return GetChar();
case TypeCode.SByte: return GetSByte();
case TypeCode.Byte: return GetByte();
case TypeCode.Int16: return GetShort();
case TypeCode.UInt16: return GetUShort();
case TypeCode.Int32: return GetInt();
case TypeCode.UInt32: return GetUInt();
case TypeCode.Int64: return GetLong();
case TypeCode.UInt64: return GetULong();
case TypeCode.Single: return GetFloat();
case TypeCode.Double: return GetDouble();
case TypeCode.Decimal: return GetDecimal();
case TypeCode.DateTime: return GetDateTime();
case TypeCode.String: return GetString();
case TypeCode.Empty: return null;
case TypeCode.DBNull: return null;
case TypeCode.Object:
default:
// Do nothing so it can be handled the hard way
break;
}
// Check these types without TypeCode values
if ( t == typeof( TimeSpan ) ) return GetTime();
if ( t == typeof( Guid ) ) return GetGuid();
if ( t.IsEnum ) return GetEnum( t );
// If all else fails, see if it can be ASSIGNED from a type this function can create,
// and return that instead
var assignable = ASSIGNABLES.Where( w => t.IsAssignableFrom( w ) );
if ( assignable.Any() )
{
return Get( assignable.First() );
}
Debug.WriteLine( String.Format( "Cannot \"Get\" a randomized value for Type: {0}", t.Name ) );
return null;
}
public Array Get( Type t, int count )
{
return System.Linq.Enumerable.Range( 0, count ).Select( s => Get( t ) ).ToArray();
}
public Array Get( Type t, int countMin, int countMax )
{
return Get( t, GetInt( countMin, countMax ) );
}
public object[] Get( params Type[] types )
{
return Get( types.AsEnumerable() ).ToArray();
}
public IEnumerable<object> Get( IEnumerable<Type> types )
{
return types.Select( t => Get( t ) );
}
public T Get<T>()
{
return (T)Get( typeof( T ) );
}
public IEnumerable<T> Get<T>( int count )
{
return Enumerable.Range( 0, count ).Select( s => Get<T>() );
}
public IEnumerable<T> Get<T>( int countMin, int countMax )
{
return Get<T>( GetInt( countMin, countMax ) );
}
public object GetElement( Array a )
{
return a.GetValue( Enumerable.Range( 0, a.Rank )
.Select( i => GetInt( a.GetLowerBound( i ), a.GetUpperBound( i ) ) )
.ToArray() );
}
public void SetElements( Type t, Array a )
{
int[] indices = new int[a.Rank];
_SetElements( t, a, indices, 0 );
}
private void _SetElements( Type t, Array a, int[] indices, int rank )
{
Action action = null;
if ( rank == (a.Rank - 1) )
{
// If this is the last rank, inside of the loop, Actually set the value
action = () => a.SetValue( Get( t ), indices );
}
else
{
// If this isn't the last rank, inside of the loop, Move on to the next rank
action = () => _SetElements( t, a, indices, rank + 1 );
}
// Loop through all values of this rank
int lower = a.GetLowerBound( rank );
int upper = a.GetUpperBound( rank );
for ( int i = lower ; i <= upper ; ++i )
{
// Set the index in this rank
indices[rank] = i;
// Perform the "action"
action();
}
}
private readonly Type[] ASSIGNABLES = new Type[]
{
typeof( Int32 ), typeof( UInt32 ),
typeof( Int64 ), typeof( UInt64 ),
typeof( Int16 ), typeof( UInt16 ),
typeof( Double ), typeof( Single ),
typeof( DateTime ), typeof( TimeSpan ),
typeof( String ), typeof( Guid ),
typeof( Byte ), typeof( SByte ),
typeof( Char ), typeof( Boolean ),
typeof( Decimal )
};
#endregion
#region Reflection / Object Configuration
public void Randomize<T>( object target, bool bRecursive = true )
{
Randomize( typeof( T ), target, bRecursive );
}
public void Randomize( Type t, object target, bool bRecursive = true )
{
var props = t.GetProperties();
if ( !props.Any() )
{
throw new ArgumentException( "No Properties", "target" );
}
foreach ( var p in props )
{
try
{
if ( p.CanWrite )
{
object value = Get( p.PropertyType );
if ( value != null )
{
p.SetValue( target, value, null );
}
else
{
if ( bRecursive && p.CanRead )
{
value = p.GetValue( target, null );
if ( value != null )
{
if ( p.PropertyType.IsArray )
{
SetElements( p.PropertyType.GetElementType(), (value as Array) );
}
else
{
Randomize( p.PropertyType, value, bRecursive );
}
}
}
}
}
}
catch ( Exception e )
{
Debug.WriteLine( "Caught Exception while Randomizing: " + e.ToString() );
}
}
}
#endregion
#region Conversion Operators
public static implicit operator bool( RandomEx rand ) { return rand.GetBool(); }
public static implicit operator byte( RandomEx rand ) { return rand.GetByte(); }
public static implicit operator float( RandomEx rand ) { return rand.GetFloat(); }
public static implicit operator uint( RandomEx rand ) { return rand.GetUInt(); }
public static implicit operator short( RandomEx rand ) { return rand.GetShort(); }
public static implicit operator string( RandomEx rand ) { return rand.GetString(); }
public static implicit operator long( RandomEx rand ) { return rand.GetLong(); }
public static implicit operator char( RandomEx rand ) { return rand.GetChar(); }
public static implicit operator double( RandomEx rand ) { return rand.GetDouble(); }
public static implicit operator DateTime( RandomEx rand ) { return rand.GetDateTime(); }
public static implicit operator TimeSpan( RandomEx rand ) { return rand.GetTime(); }
public static implicit operator ulong( RandomEx rand ) { return rand.GetULong(); }
public static implicit operator Guid( RandomEx rand ) { return rand.GetGuid(); }
public static implicit operator sbyte( RandomEx rand ) { return rand.GetSByte(); }
public static implicit operator decimal( RandomEx rand ) { return rand.GetDecimal(); }
public static implicit operator ushort( RandomEx rand ) { return rand.GetUShort(); }
public static implicit operator int( RandomEx rand ) { return rand.GetInt(); }
#endregion
#region String Functions
public string GetString( int min, int max, string sCharSet )
{
const string SINGLE_CHAR_LINE = "\n";
string result = String.Empty;
int length = GetInt( min, max );
bool bFixNewLine = false;
// If NewLine is more than one character, convert it to a single char to use
if ( Environment.NewLine != SINGLE_CHAR_LINE )
{
bFixNewLine = (sCharSet != (sCharSet = sCharSet.Replace( Environment.NewLine, SINGLE_CHAR_LINE )));
}
do
{
result += sCharSet[GetInt( 0, sCharSet.Length - 1 )];
}
while ( result.Length < length );
// If the NewLine substitution was done before, expand it back to the multi character version
if ( bFixNewLine )
{
result = result.Replace( SINGLE_CHAR_LINE, Environment.NewLine );
}
return result;
}
public string GetString( int min, int max, string[] sCharSets ) { return GetString( min, max, String.Join( "", sCharSets ) ); }
public string GetString( int min, int max, RandomCharSets chars ) { return GetString( min, max, chars.GetCharSets() ); }
public string GetString( int min, int max ) { return GetString( min, max, DEFAULT_CHAR_SETS ); }
public string GetString( int max, string sCharSet ) { return GetString( 1, max, sCharSet ); }
public string GetString( int max, string[] sCharSets ) { return GetString( 1, max, sCharSets ); }
public string GetString( int max, RandomCharSets chars ) { return GetString( 1, max, chars ); }
public string GetString( int max ) { return GetString( 1, max ); }
public string GetString() { return GetString( 1, DEFAULT_STRING_MAX ); }
#endregion
#region Extended String Functions
public string GetExtString( int max )
{
return GetExtString( 1, max );
}
public string GetExtString( int min, int max )
{
RandomCharSets cs;
do
{
cs = (RandomCharSets)(GetUShort() & (ushort)EXT_CHAR_SETS);
}
// Repeat until a useful value is generated
while ( cs == RandomCharSets.None );
// Get the string
return GetString( min, max, cs );
}
#endregion
#region Internals
private RNGCryptoServiceProvider RNG { get; set; }
/// <summary>
/// Ensures there are at least "count" bytes of random data available and indicates where in
/// the buffer to find them
/// </summary>
/// <param name="count">The number of random bytes needed</param>
/// <returns>The Index into m_RandData where the random bytes can be found</returns>
private int EnsureBytes( int count )
{
// See if there's enough data left in the buffer to fulfill this request
if ( (m_nUsedCount + count) <= m_RandData.Length )
{
// There is so mark it as used
int index = m_nUsedCount;
m_nUsedCount += count;
return index;
}
else
{
// See if they want more data than the buffer can hold
if ( count > m_RandData.Length )
{
// Grow the buffer by the size plus extra
m_RandData = new byte[(int)(count * 1.5)];
}
// Get more data
GetBytes( m_RandData );
m_nUsedCount = count;
return 0;
}
}
private int m_nUsedCount = 0;
private byte[] m_RandData;
#endregion
#region Constants
private const int DEFAULT_BUFFER_SIZE = 4096;
private const RandomCharSets DEFAULT_CHAR_SETS = (RandomCharSets.LowerCase | RandomCharSets.UpperCase |
RandomCharSets.Symbols | RandomCharSets.SymbolsExt |
RandomCharSets.Numbers);
private const int DEFAULT_STRING_MAX = 32;
private const RandomCharSets EXT_CHAR_SETS = (DEFAULT_CHAR_SETS | RandomCharSets.GreekUpper | RandomCharSets.GreekLower);
#endregion
#region IDisposable Members
void IDisposable.Dispose()
{
if ( this.RNG != null )
{
this.RNG.Dispose();
this.RNG = null;
}
}
#endregion
}
#endregion
#region Internal Helper Classes
internal static class CharSetExtensions
{
#region Public Methods
public static string GetChars( this RandomCharSets val )
{
const string UPPERALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
const string NUMERICS = "0123456789";
const string SYMBOLS1 = "!@#$%^&*()";
const string SYMBOLS2 = "-=~[]|\\?//,.<>:;'\"{}";
const string EXTRAPUNC= "¡¦©«­­®°¶·¿―‗’‛”„†‡…′″‴‹›‼‽‾⁃⁄";
const string CURRENCY = "$¢£¤¥฿₠₡₢₣₤₥₦₧₨₩₫€₪";
const string GREEKCAPITAL = "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ";
const string GREEKLOWER = "αβγδεζηθικλμνξοπρςστυφχψω";
const string DINGBATS = "☺☻☼♀♂♠♣♥♦♪♫✶";
const string CIRCNUMBERS = "❶❷❸❹❺❻❼❽❾❿";
const string ACCENTED = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóõôöøùúûüýþÿ";
const string SPACE = " ";
switch ( val )
{
case RandomCharSets.UpperCase: return UPPERALPHA;
case RandomCharSets.LowerCase: return LOWERALPHA;
case RandomCharSets.Numbers: return NUMERICS;
case RandomCharSets.Symbols: return SYMBOLS1;
case RandomCharSets.SymbolsExt: return SYMBOLS2;
case RandomCharSets.ExtraPunc: return EXTRAPUNC;
case RandomCharSets.Currency: return CURRENCY;
case RandomCharSets.GreekUpper: return GREEKCAPITAL;
case RandomCharSets.GreekLower: return GREEKLOWER;
case RandomCharSets.Dingbats: return DINGBATS;
case RandomCharSets.CircleNums: return CIRCNUMBERS;
case RandomCharSets.Accented: return ACCENTED;
case RandomCharSets.Whitespace: return SPACE;
case RandomCharSets.NewLine: return Environment.NewLine;
default: return String.Empty;
}
}
public static string[] GetCharSets( this RandomCharSets val )
{
return Enum.GetValues( typeof( RandomCharSets ) )
.OfType<RandomCharSets>()
.Where( w => val.HasFlag( w ) )
.Select( s => s.GetChars() )
.ToArray();
}
#endregion Public Methods
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment