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;