X931Rng.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng
  5. {
  6. internal class X931Rng
  7. {
  8. private const long BLOCK64_RESEED_MAX = 1L << (16 - 1);
  9. private const long BLOCK128_RESEED_MAX = 1L << (24 - 1);
  10. private const int BLOCK64_MAX_BITS_REQUEST = 1 << (13 - 1);
  11. private const int BLOCK128_MAX_BITS_REQUEST = 1 << (19 - 1);
  12. private readonly IBlockCipher mEngine;
  13. private readonly IEntropySource mEntropySource;
  14. private readonly byte[] mDT;
  15. private readonly byte[] mI;
  16. private readonly byte[] mR;
  17. private byte[] mV;
  18. private long mReseedCounter = 1;
  19. /**
  20. *
  21. * @param engine
  22. * @param entropySource
  23. */
  24. internal X931Rng(IBlockCipher engine, byte[] dateTimeVector, IEntropySource entropySource)
  25. {
  26. this.mEngine = engine;
  27. this.mEntropySource = entropySource;
  28. this.mDT = new byte[engine.GetBlockSize()];
  29. Array.Copy(dateTimeVector, 0, mDT, 0, mDT.Length);
  30. this.mI = new byte[engine.GetBlockSize()];
  31. this.mR = new byte[engine.GetBlockSize()];
  32. }
  33. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  34. internal int Generate(Span<byte> output, bool predictionResistant)
  35. {
  36. int outputLen = output.Length;
  37. if (mR.Length == 8) // 64 bit block size
  38. {
  39. if (mReseedCounter > BLOCK64_RESEED_MAX)
  40. return -1;
  41. if (outputLen > BLOCK64_MAX_BITS_REQUEST / 8)
  42. throw new ArgumentException("Number of bits per request limited to " + BLOCK64_MAX_BITS_REQUEST, "output");
  43. }
  44. else
  45. {
  46. if (mReseedCounter > BLOCK128_RESEED_MAX)
  47. return -1;
  48. if (outputLen > BLOCK128_MAX_BITS_REQUEST / 8)
  49. throw new ArgumentException("Number of bits per request limited to " + BLOCK128_MAX_BITS_REQUEST, "output");
  50. }
  51. if (predictionResistant || mV == null)
  52. {
  53. mV = mEntropySource.GetEntropy();
  54. if (mV.Length != mEngine.GetBlockSize())
  55. throw new InvalidOperationException("Insufficient entropy returned");
  56. }
  57. int m = outputLen / mR.Length;
  58. for (int i = 0; i < m; i++)
  59. {
  60. mEngine.ProcessBlock(mDT, mI);
  61. Process(mR, mI, mV);
  62. Process(mV, mR, mI);
  63. mR.CopyTo(output[(i * mR.Length)..]);
  64. Increment(mDT);
  65. }
  66. int bytesToCopy = outputLen - m * mR.Length;
  67. if (bytesToCopy > 0)
  68. {
  69. mEngine.ProcessBlock(mDT, mI);
  70. Process(mR, mI, mV);
  71. Process(mV, mR, mI);
  72. mR.AsSpan(0, bytesToCopy).CopyTo(output[(m * mR.Length)..]);
  73. Increment(mDT);
  74. }
  75. mReseedCounter++;
  76. return outputLen * 8;
  77. }
  78. #else
  79. /**
  80. * Populate a passed in array with random data.
  81. *
  82. * @param output output array for generated bits.
  83. * @param predictionResistant true if a reseed should be forced, false otherwise.
  84. *
  85. * @return number of bits generated, -1 if a reseed required.
  86. */
  87. internal int Generate(byte[] output, int outputOff, int outputLen, bool predictionResistant)
  88. {
  89. if (mR.Length == 8) // 64 bit block size
  90. {
  91. if (mReseedCounter > BLOCK64_RESEED_MAX)
  92. return -1;
  93. if (outputLen > BLOCK64_MAX_BITS_REQUEST / 8)
  94. throw new ArgumentException("Number of bits per request limited to " + BLOCK64_MAX_BITS_REQUEST, "output");
  95. }
  96. else
  97. {
  98. if (mReseedCounter > BLOCK128_RESEED_MAX)
  99. return -1;
  100. if (outputLen > BLOCK128_MAX_BITS_REQUEST / 8)
  101. throw new ArgumentException("Number of bits per request limited to " + BLOCK128_MAX_BITS_REQUEST, "output");
  102. }
  103. if (predictionResistant || mV == null)
  104. {
  105. mV = mEntropySource.GetEntropy();
  106. if (mV.Length != mEngine.GetBlockSize())
  107. throw new InvalidOperationException("Insufficient entropy returned");
  108. }
  109. int m = outputLen / mR.Length;
  110. for (int i = 0; i < m; i++)
  111. {
  112. mEngine.ProcessBlock(mDT, 0, mI, 0);
  113. Process(mR, mI, mV);
  114. Process(mV, mR, mI);
  115. Array.Copy(mR, 0, output, outputOff + i * mR.Length, mR.Length);
  116. Increment(mDT);
  117. }
  118. int bytesToCopy = outputLen - m * mR.Length;
  119. if (bytesToCopy > 0)
  120. {
  121. mEngine.ProcessBlock(mDT, 0, mI, 0);
  122. Process(mR, mI, mV);
  123. Process(mV, mR, mI);
  124. Array.Copy(mR, 0, output, outputOff + m * mR.Length, bytesToCopy);
  125. Increment(mDT);
  126. }
  127. mReseedCounter++;
  128. return outputLen * 8;
  129. }
  130. #endif
  131. /**
  132. * Reseed the RNG.
  133. */
  134. internal void Reseed()
  135. {
  136. mV = mEntropySource.GetEntropy();
  137. if (mV.Length != mEngine.GetBlockSize())
  138. throw new InvalidOperationException("Insufficient entropy returned");
  139. mReseedCounter = 1;
  140. }
  141. internal IEntropySource EntropySource
  142. {
  143. get { return mEntropySource; }
  144. }
  145. private void Process(byte[] res, byte[] a, byte[] b)
  146. {
  147. for (int i = 0; i != res.Length; i++)
  148. {
  149. res[i] = (byte)(a[i] ^ b[i]);
  150. }
  151. mEngine.ProcessBlock(res, 0, res, 0);
  152. }
  153. private void Increment(byte[] val)
  154. {
  155. for (int i = val.Length - 1; i >= 0; i--)
  156. {
  157. if (++val[i] != 0)
  158. break;
  159. }
  160. }
  161. }
  162. }
  163. #pragma warning restore
  164. #endif