HashSP800Drbg.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  6. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng.Drbg
  7. {
  8. /**
  9. * A SP800-90A Hash DRBG.
  10. */
  11. public class HashSP800Drbg
  12. : ISP80090Drbg
  13. {
  14. private readonly static byte[] ONE = { 0x01 };
  15. private readonly static long RESEED_MAX = 1L << (48 - 1);
  16. private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
  17. private static readonly IDictionary seedlens = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
  18. static HashSP800Drbg()
  19. {
  20. seedlens.Add("SHA-1", 440);
  21. seedlens.Add("SHA-224", 440);
  22. seedlens.Add("SHA-256", 440);
  23. seedlens.Add("SHA-512/256", 440);
  24. seedlens.Add("SHA-512/224", 440);
  25. seedlens.Add("SHA-384", 888);
  26. seedlens.Add("SHA-512", 888);
  27. }
  28. private readonly IDigest mDigest;
  29. private readonly IEntropySource mEntropySource;
  30. private readonly int mSecurityStrength;
  31. private readonly int mSeedLength;
  32. private byte[] mV;
  33. private byte[] mC;
  34. private long mReseedCounter;
  35. /**
  36. * Construct a SP800-90A Hash DRBG.
  37. * <p>
  38. * Minimum entropy requirement is the security strength requested.
  39. * </p>
  40. * @param digest source digest to use for DRB stream.
  41. * @param securityStrength security strength required (in bits)
  42. * @param entropySource source of entropy to use for seeding/reseeding.
  43. * @param personalizationString personalization string to distinguish this DRBG (may be null).
  44. * @param nonce nonce to further distinguish this DRBG (may be null).
  45. */
  46. public HashSP800Drbg(IDigest digest, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
  47. {
  48. if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(digest))
  49. throw new ArgumentException("Requested security strength is not supported by the derivation function");
  50. if (entropySource.EntropySize < securityStrength)
  51. throw new ArgumentException("Not enough entropy for security strength required");
  52. mDigest = digest;
  53. mEntropySource = entropySource;
  54. mSecurityStrength = securityStrength;
  55. mSeedLength = (int)seedlens[digest.AlgorithmName];
  56. // 1. seed_material = entropy_input || nonce || personalization_string.
  57. // 2. seed = Hash_df (seed_material, seedlen).
  58. // 3. V = seed.
  59. // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte
  60. // of zeros.
  61. // 5. reseed_counter = 1.
  62. // 6. Return V, C, and reseed_counter as the initial_working_state
  63. byte[] entropy = GetEntropy();
  64. byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
  65. byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
  66. mV = seed;
  67. byte[] subV = new byte[mV.Length + 1];
  68. Array.Copy(mV, 0, subV, 1, mV.Length);
  69. mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength);
  70. mReseedCounter = 1;
  71. }
  72. /**
  73. * Return the block size (in bits) of the DRBG.
  74. *
  75. * @return the number of bits produced on each internal round of the DRBG.
  76. */
  77. public int BlockSize
  78. {
  79. get { return mDigest.GetDigestSize () * 8; }
  80. }
  81. /**
  82. * Populate a passed in array with random data.
  83. *
  84. * @param output output array for generated bits.
  85. * @param additionalInput additional input to be added to the DRBG in this step.
  86. * @param predictionResistant true if a reseed should be forced, false otherwise.
  87. *
  88. * @return number of bits generated, -1 if a reseed required.
  89. */
  90. public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
  91. {
  92. // 1. If reseed_counter > reseed_interval, then return an indication that a
  93. // reseed is required.
  94. // 2. If (additional_input != Null), then do
  95. // 2.1 w = Hash (0x02 || V || additional_input).
  96. // 2.2 V = (V + w) mod 2^seedlen
  97. // .
  98. // 3. (returned_bits) = Hashgen (requested_number_of_bits, V).
  99. // 4. H = Hash (0x03 || V).
  100. // 5. V = (V + H + C + reseed_counter) mod 2^seedlen
  101. // .
  102. // 6. reseed_counter = reseed_counter + 1.
  103. // 7. Return SUCCESS, returned_bits, and the new values of V, C, and
  104. // reseed_counter for the new_working_state.
  105. int numberOfBits = output.Length * 8;
  106. if (numberOfBits > MAX_BITS_REQUEST)
  107. throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
  108. if (mReseedCounter > RESEED_MAX)
  109. return -1;
  110. if (predictionResistant)
  111. {
  112. Reseed(additionalInput);
  113. additionalInput = null;
  114. }
  115. // 2.
  116. if (additionalInput != null)
  117. {
  118. byte[] newInput = new byte[1 + mV.Length + additionalInput.Length];
  119. newInput[0] = 0x02;
  120. Array.Copy(mV, 0, newInput, 1, mV.Length);
  121. // TODO: inOff / inLength
  122. Array.Copy(additionalInput, 0, newInput, 1 + mV.Length, additionalInput.Length);
  123. byte[] w = Hash(newInput);
  124. AddTo(mV, w);
  125. }
  126. // 3.
  127. byte[] rv = hashgen(mV, numberOfBits);
  128. // 4.
  129. byte[] subH = new byte[mV.Length + 1];
  130. Array.Copy(mV, 0, subH, 1, mV.Length);
  131. subH[0] = 0x03;
  132. byte[] H = Hash(subH);
  133. // 5.
  134. AddTo(mV, H);
  135. AddTo(mV, mC);
  136. byte[] c = new byte[4];
  137. c[0] = (byte)(mReseedCounter >> 24);
  138. c[1] = (byte)(mReseedCounter >> 16);
  139. c[2] = (byte)(mReseedCounter >> 8);
  140. c[3] = (byte)mReseedCounter;
  141. AddTo(mV, c);
  142. mReseedCounter++;
  143. Array.Copy(rv, 0, output, 0, output.Length);
  144. return numberOfBits;
  145. }
  146. private byte[] GetEntropy()
  147. {
  148. byte[] entropy = mEntropySource.GetEntropy();
  149. if (entropy.Length < (mSecurityStrength + 7) / 8)
  150. throw new InvalidOperationException("Insufficient entropy provided by entropy source");
  151. return entropy;
  152. }
  153. // this will always add the shorter length byte array mathematically to the
  154. // longer length byte array.
  155. // be careful....
  156. private void AddTo(byte[] longer, byte[] shorter)
  157. {
  158. int off = longer.Length - shorter.Length;
  159. uint carry = 0;
  160. int i = shorter.Length;
  161. while (--i >= 0)
  162. {
  163. carry += (uint)longer[off + i] + (uint)shorter[i];
  164. longer[off + i] = (byte)carry;
  165. carry >>= 8;
  166. }
  167. i = off;
  168. while (--i >= 0)
  169. {
  170. carry += longer[i];
  171. longer[i] = (byte)carry;
  172. carry >>= 8;
  173. }
  174. }
  175. /**
  176. * Reseed the DRBG.
  177. *
  178. * @param additionalInput additional input to be added to the DRBG in this step.
  179. */
  180. public void Reseed(byte[] additionalInput)
  181. {
  182. // 1. seed_material = 0x01 || V || entropy_input || additional_input.
  183. //
  184. // 2. seed = Hash_df (seed_material, seedlen).
  185. //
  186. // 3. V = seed.
  187. //
  188. // 4. C = Hash_df ((0x00 || V), seedlen).
  189. //
  190. // 5. reseed_counter = 1.
  191. //
  192. // 6. Return V, C, and reseed_counter for the new_working_state.
  193. //
  194. // Comment: Precede with a byte of all zeros.
  195. byte[] entropy = GetEntropy();
  196. byte[] seedMaterial = Arrays.ConcatenateAll(ONE, mV, entropy, additionalInput);
  197. byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
  198. mV = seed;
  199. byte[] subV = new byte[mV.Length + 1];
  200. subV[0] = 0x00;
  201. Array.Copy(mV, 0, subV, 1, mV.Length);
  202. mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength);
  203. mReseedCounter = 1;
  204. }
  205. private byte[] Hash(byte[] input)
  206. {
  207. byte[] hash = new byte[mDigest.GetDigestSize()];
  208. DoHash(input, hash);
  209. return hash;
  210. }
  211. private void DoHash(byte[] input, byte[] output)
  212. {
  213. mDigest.BlockUpdate(input, 0, input.Length);
  214. mDigest.DoFinal(output, 0);
  215. }
  216. // 1. m = [requested_number_of_bits / outlen]
  217. // 2. data = V.
  218. // 3. W = the Null string.
  219. // 4. For i = 1 to m
  220. // 4.1 wi = Hash (data).
  221. // 4.2 W = W || wi.
  222. // 4.3 data = (data + 1) mod 2^seedlen
  223. // .
  224. // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
  225. private byte[] hashgen(byte[] input, int lengthInBits)
  226. {
  227. int digestSize = mDigest.GetDigestSize();
  228. int m = (lengthInBits / 8) / digestSize;
  229. byte[] data = new byte[input.Length];
  230. Array.Copy(input, 0, data, 0, input.Length);
  231. byte[] W = new byte[lengthInBits / 8];
  232. byte[] dig = new byte[mDigest.GetDigestSize()];
  233. for (int i = 0; i <= m; i++)
  234. {
  235. DoHash(data, dig);
  236. int bytesToCopy = ((W.Length - i * dig.Length) > dig.Length)
  237. ? dig.Length
  238. : (W.Length - i * dig.Length);
  239. Array.Copy(dig, 0, W, i * dig.Length, bytesToCopy);
  240. AddTo(data, ONE);
  241. }
  242. return W;
  243. }
  244. }
  245. }
  246. #pragma warning restore
  247. #endif