diff --git a/ShareX.HelpersLib/Helpers/Helpers.cs b/ShareX.HelpersLib/Helpers/Helpers.cs index ef32ae6d7..b6c9555cc 100644 --- a/ShareX.HelpersLib/Helpers/Helpers.cs +++ b/ShareX.HelpersLib/Helpers/Helpers.cs @@ -243,7 +243,7 @@ public static string HourTo12(int hour) public static char GetRandomChar(string chars) { - return chars[MathHelpers.Random(chars.Length - 1)]; + return chars[MathHelpers.CryptoRandom(chars.Length - 1)]; } public static string GetRandomString(string chars, int length) @@ -283,7 +283,7 @@ public static string GetRandomLine(string text) string[] lines = text.Trim().Lines(); if (lines != null && lines.Length > 0) { - return lines[MathHelpers.Random(0, lines.Length - 1)]; + return lines[MathHelpers.CryptoRandom(0, lines.Length - 1)]; } return null; } diff --git a/ShareX.HelpersLib/Helpers/MathHelpers.cs b/ShareX.HelpersLib/Helpers/MathHelpers.cs index 0286f6051..5a68486e3 100644 --- a/ShareX.HelpersLib/Helpers/MathHelpers.cs +++ b/ShareX.HelpersLib/Helpers/MathHelpers.cs @@ -24,6 +24,7 @@ #endregion License Information (GPL v3) using System; +using System.Security.Cryptography; namespace ShareX.HelpersLib { @@ -36,6 +37,19 @@ public static class MathHelpers private static readonly object randomLock = new object(); private static readonly Random random = new Random(); + private static readonly object cryptoRandomLock = new object(); + private static readonly RNGCryptoServiceProvider cryptoRandom = new RNGCryptoServiceProvider(); + private static byte[] rngBuf = new byte[4]; + + /// + /// Returns a random number between 0 and max (inclusive). + /// + /// + /// This uses System.Random(), which does not provide safe random numbers. This function + /// should not be used to generate things that should be unique, like random file names. + /// + /// The upper limit of the number (inclusive). + /// A random number. public static int Random(int max) { lock (randomLock) @@ -52,6 +66,54 @@ public static int Random(int min, int max) } } + /// + /// Returns a random number between 0 and max (inclusive) generated with a cryptographic PRNG. + /// + /// The upper limit of the number (inclusive). + /// A cryptographically random number. + public static int CryptoRandom(int max) + { + return CryptoRandom(0, max); + } + + /// + /// Returns a random number between min and max (inclusive) generated with a cryptographic PRNG. + /// + /// The lower limit of the number. + /// The upper limit of the number (inclusive). + /// A cryptographically random number. + public static int CryptoRandom(int min, int max) + { + // this code avoids bias in random number generation, which is important when generating random filenames, etc. + // adapted from https://web.archive.org/web/20150114085328/http://msdn.microsoft.com:80/en-us/magazine/cc163367.aspx + if (min > max) + { + throw new ArgumentOutOfRangeException("min"); + } + + if (min == max) + { + return min; + } + + lock (cryptoRandomLock) + { + var diff = (long)max - min; + long ceiling = (1 + (long)uint.MaxValue); + long remainder = ceiling % diff; + // this should only iterate once unless we generate really large numbers + uint r; + + do + { + cryptoRandom.GetBytes(rngBuf); + r = BitConverter.ToUInt32(rngBuf, 0); + } while (r >= ceiling - remainder); + + return (int)(min + (r % diff)); + } + } + public static float RadianToDegree(float radian) { return radian * RadianPI;