SecureRandom.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Threading;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  8. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Security
  9. {
  10. public class SecureRandom
  11. : Random
  12. {
  13. private static long counter = DateTime.UtcNow.Ticks;
  14. private static long NextCounterValue()
  15. {
  16. return Interlocked.Increment(ref counter);
  17. }
  18. private static readonly SecureRandom MasterRandom = new SecureRandom(new CryptoApiRandomGenerator());
  19. internal static readonly SecureRandom ArbitraryRandom = new SecureRandom(new VmpcRandomGenerator(), 16);
  20. private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
  21. {
  22. IDigest digest = DigestUtilities.GetDigest(digestName);
  23. if (digest == null)
  24. return null;
  25. DigestRandomGenerator prng = new DigestRandomGenerator(digest);
  26. if (autoSeed)
  27. {
  28. AutoSeed(prng, digest.GetDigestSize());
  29. }
  30. return prng;
  31. }
  32. public static byte[] GetNextBytes(SecureRandom secureRandom, int length)
  33. {
  34. byte[] result = new byte[length];
  35. secureRandom.NextBytes(result);
  36. return result;
  37. }
  38. /// <summary>
  39. /// Create and auto-seed an instance based on the given algorithm.
  40. /// </summary>
  41. /// <remarks>Equivalent to GetInstance(algorithm, true)</remarks>
  42. /// <param name="algorithm">e.g. "SHA256PRNG"</param>
  43. public static SecureRandom GetInstance(string algorithm)
  44. {
  45. return GetInstance(algorithm, true);
  46. }
  47. /// <summary>
  48. /// Create an instance based on the given algorithm, with optional auto-seeding
  49. /// </summary>
  50. /// <param name="algorithm">e.g. "SHA256PRNG"</param>
  51. /// <param name="autoSeed">If true, the instance will be auto-seeded.</param>
  52. public static SecureRandom GetInstance(string algorithm, bool autoSeed)
  53. {
  54. if (algorithm == null)
  55. throw new ArgumentNullException(nameof(algorithm));
  56. if (algorithm.EndsWith("PRNG", StringComparison.OrdinalIgnoreCase))
  57. {
  58. string digestName = algorithm.Substring(0, algorithm.Length - "PRNG".Length);
  59. DigestRandomGenerator prng = CreatePrng(digestName, autoSeed);
  60. if (prng != null)
  61. return new SecureRandom(prng);
  62. }
  63. throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm");
  64. }
  65. protected readonly IRandomGenerator generator;
  66. public SecureRandom()
  67. : this(CreatePrng("SHA256", true))
  68. {
  69. }
  70. /// <summary>Use the specified instance of IRandomGenerator as random source.</summary>
  71. /// <remarks>
  72. /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the
  73. /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide
  74. /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c>
  75. /// implementation.
  76. /// </remarks>
  77. /// <param name="generator">The source to generate all random bytes from.</param>
  78. public SecureRandom(IRandomGenerator generator)
  79. : base(0)
  80. {
  81. this.generator = generator;
  82. }
  83. public SecureRandom(IRandomGenerator generator, int autoSeedLengthInBytes)
  84. : base(0)
  85. {
  86. AutoSeed(generator, autoSeedLengthInBytes);
  87. this.generator = generator;
  88. }
  89. public virtual byte[] GenerateSeed(int length)
  90. {
  91. return GetNextBytes(MasterRandom, length);
  92. }
  93. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  94. public virtual void GenerateSeed(Span<byte> seed)
  95. {
  96. MasterRandom.NextBytes(seed);
  97. }
  98. #endif
  99. public virtual void SetSeed(byte[] seed)
  100. {
  101. generator.AddSeedMaterial(seed);
  102. }
  103. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  104. public virtual void SetSeed(Span<byte> seed)
  105. {
  106. generator.AddSeedMaterial(seed);
  107. }
  108. #endif
  109. public virtual void SetSeed(long seed)
  110. {
  111. generator.AddSeedMaterial(seed);
  112. }
  113. public override int Next()
  114. {
  115. return NextInt() & int.MaxValue;
  116. }
  117. public override int Next(int maxValue)
  118. {
  119. if (maxValue < 2)
  120. {
  121. if (maxValue < 0)
  122. throw new ArgumentOutOfRangeException("maxValue", "cannot be negative");
  123. return 0;
  124. }
  125. int bits;
  126. // Test whether maxValue is a power of 2
  127. if ((maxValue & (maxValue - 1)) == 0)
  128. {
  129. bits = NextInt() & int.MaxValue;
  130. return (int)(((long)bits * maxValue) >> 31);
  131. }
  132. int result;
  133. do
  134. {
  135. bits = NextInt() & int.MaxValue;
  136. result = bits % maxValue;
  137. }
  138. while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow
  139. return result;
  140. }
  141. public override int Next(int minValue, int maxValue)
  142. {
  143. if (maxValue <= minValue)
  144. {
  145. if (maxValue == minValue)
  146. return minValue;
  147. throw new ArgumentException("maxValue cannot be less than minValue");
  148. }
  149. int diff = maxValue - minValue;
  150. if (diff > 0)
  151. return minValue + Next(diff);
  152. for (;;)
  153. {
  154. int i = NextInt();
  155. if (i >= minValue && i < maxValue)
  156. return i;
  157. }
  158. }
  159. public override void NextBytes(byte[] buf)
  160. {
  161. generator.NextBytes(buf);
  162. }
  163. public virtual void NextBytes(byte[] buf, int off, int len)
  164. {
  165. generator.NextBytes(buf, off, len);
  166. }
  167. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  168. public override void NextBytes(Span<byte> buffer)
  169. {
  170. if (generator != null)
  171. {
  172. generator.NextBytes(buffer);
  173. }
  174. else
  175. {
  176. byte[] tmp = new byte[buffer.Length];
  177. NextBytes(tmp);
  178. tmp.CopyTo(buffer);
  179. }
  180. }
  181. #endif
  182. private static readonly double DoubleScale = 1.0 / Convert.ToDouble(1L << 53);
  183. public override double NextDouble()
  184. {
  185. ulong x = (ulong)NextLong() >> 11;
  186. return Convert.ToDouble(x) * DoubleScale;
  187. }
  188. public virtual int NextInt()
  189. {
  190. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  191. Span<byte> bytes = stackalloc byte[4];
  192. #else
  193. byte[] bytes = new byte[4];
  194. #endif
  195. NextBytes(bytes);
  196. return (int)Pack.BE_To_UInt32(bytes);
  197. }
  198. public virtual long NextLong()
  199. {
  200. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  201. Span<byte> bytes = stackalloc byte[8];
  202. #else
  203. byte[] bytes = new byte[8];
  204. #endif
  205. NextBytes(bytes);
  206. return (long)Pack.BE_To_UInt64(bytes);
  207. }
  208. private static void AutoSeed(IRandomGenerator generator, int seedLength)
  209. {
  210. generator.AddSeedMaterial(NextCounterValue());
  211. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  212. Span<byte> seed = seedLength <= 128
  213. ? stackalloc byte[seedLength]
  214. : new byte[seedLength];
  215. #else
  216. byte[] seed = new byte[seedLength];
  217. #endif
  218. MasterRandom.NextBytes(seed);
  219. generator.AddSeedMaterial(seed);
  220. }
  221. }
  222. }
  223. #pragma warning restore
  224. #endif