SecureRandom.cs 8.2 KB

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