HMacSP800Drbg.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  6. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng.Drbg
  7. {
  8. /**
  9. * A SP800-90A HMAC DRBG.
  10. */
  11. public sealed class HMacSP800Drbg
  12. : ISP80090Drbg
  13. {
  14. private readonly static long RESEED_MAX = 1L << (48 - 1);
  15. private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
  16. private readonly byte[] mK;
  17. private readonly byte[] mV;
  18. private readonly IEntropySource mEntropySource;
  19. private readonly IMac mHMac;
  20. private readonly int mSecurityStrength;
  21. private long mReseedCounter;
  22. /**
  23. * Construct a SP800-90A Hash DRBG.
  24. * <p>
  25. * Minimum entropy requirement is the security strength requested.
  26. * </p>
  27. * @param hMac Hash MAC to base the DRBG on.
  28. * @param securityStrength security strength required (in bits)
  29. * @param entropySource source of entropy to use for seeding/reseeding.
  30. * @param personalizationString personalization string to distinguish this DRBG (may be null).
  31. * @param nonce nonce to further distinguish this DRBG (may be null).
  32. */
  33. public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource,
  34. byte[] personalizationString, byte[] nonce)
  35. {
  36. if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(hMac))
  37. throw new ArgumentException("Requested security strength is not supported by the derivation function");
  38. if (entropySource.EntropySize < securityStrength)
  39. throw new ArgumentException("Not enough entropy for security strength required");
  40. mHMac = hMac;
  41. mSecurityStrength = securityStrength;
  42. mEntropySource = entropySource;
  43. byte[] entropy = GetEntropy();
  44. byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
  45. mK = new byte[hMac.GetMacSize()];
  46. mV = new byte[mK.Length];
  47. Arrays.Fill(mV, (byte)1);
  48. hmac_DRBG_Update(seedMaterial);
  49. mReseedCounter = 1;
  50. }
  51. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  52. private void hmac_DRBG_Update()
  53. {
  54. hmac_DRBG_Update_Func(ReadOnlySpan<byte>.Empty, 0x00);
  55. }
  56. private void hmac_DRBG_Update(ReadOnlySpan<byte> seedMaterial)
  57. {
  58. hmac_DRBG_Update_Func(seedMaterial, 0x00);
  59. hmac_DRBG_Update_Func(seedMaterial, 0x01);
  60. }
  61. private void hmac_DRBG_Update_Func(ReadOnlySpan<byte> seedMaterial, byte vValue)
  62. {
  63. mHMac.Init(new KeyParameter(mK));
  64. mHMac.BlockUpdate(mV);
  65. mHMac.Update(vValue);
  66. if (!seedMaterial.IsEmpty)
  67. {
  68. mHMac.BlockUpdate(seedMaterial);
  69. }
  70. mHMac.DoFinal(mK);
  71. mHMac.Init(new KeyParameter(mK));
  72. mHMac.BlockUpdate(mV);
  73. mHMac.DoFinal(mV);
  74. }
  75. #else
  76. private void hmac_DRBG_Update(byte[] seedMaterial)
  77. {
  78. hmac_DRBG_Update_Func(seedMaterial, 0x00);
  79. if (seedMaterial != null)
  80. {
  81. hmac_DRBG_Update_Func(seedMaterial, 0x01);
  82. }
  83. }
  84. private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
  85. {
  86. mHMac.Init(new KeyParameter(mK));
  87. mHMac.BlockUpdate(mV, 0, mV.Length);
  88. mHMac.Update(vValue);
  89. if (seedMaterial != null)
  90. {
  91. mHMac.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
  92. }
  93. mHMac.DoFinal(mK, 0);
  94. mHMac.Init(new KeyParameter(mK));
  95. mHMac.BlockUpdate(mV, 0, mV.Length);
  96. mHMac.DoFinal(mV, 0);
  97. }
  98. #endif
  99. /**
  100. * Return the block size (in bits) of the DRBG.
  101. *
  102. * @return the number of bits produced on each round of the DRBG.
  103. */
  104. public int BlockSize
  105. {
  106. get { return mV.Length * 8; }
  107. }
  108. /**
  109. * Populate a passed in array with random data.
  110. *
  111. * @param output output array for generated bits.
  112. * @param additionalInput additional input to be added to the DRBG in this step.
  113. * @param predictionResistant true if a reseed should be forced, false otherwise.
  114. *
  115. * @return number of bits generated, -1 if a reseed required.
  116. */
  117. public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput,
  118. bool predictionResistant)
  119. {
  120. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  121. return additionalInput == null
  122. ? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
  123. : GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
  124. #else
  125. int numberOfBits = outputLen * 8;
  126. if (numberOfBits > MAX_BITS_REQUEST)
  127. throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
  128. if (mReseedCounter > RESEED_MAX)
  129. return -1;
  130. if (predictionResistant)
  131. {
  132. Reseed(additionalInput);
  133. additionalInput = null;
  134. }
  135. // 2.
  136. if (additionalInput != null)
  137. {
  138. hmac_DRBG_Update(additionalInput);
  139. }
  140. // 3.
  141. byte[] rv = new byte[outputLen];
  142. int m = outputLen / mV.Length;
  143. mHMac.Init(new KeyParameter(mK));
  144. for (int i = 0; i < m; i++)
  145. {
  146. mHMac.BlockUpdate(mV, 0, mV.Length);
  147. mHMac.DoFinal(mV, 0);
  148. Array.Copy(mV, 0, rv, i * mV.Length, mV.Length);
  149. }
  150. if (m * mV.Length < rv.Length)
  151. {
  152. mHMac.BlockUpdate(mV, 0, mV.Length);
  153. mHMac.DoFinal(mV, 0);
  154. Array.Copy(mV, 0, rv, m * mV.Length, rv.Length - (m * mV.Length));
  155. }
  156. hmac_DRBG_Update(additionalInput);
  157. mReseedCounter++;
  158. Array.Copy(rv, 0, output, outputOff, outputLen);
  159. return numberOfBits;
  160. #endif
  161. }
  162. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  163. public int Generate(Span<byte> output, bool predictionResistant)
  164. {
  165. int numberOfBits = output.Length * 8;
  166. if (numberOfBits > MAX_BITS_REQUEST)
  167. throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
  168. if (mReseedCounter > RESEED_MAX)
  169. return -1;
  170. if (predictionResistant)
  171. {
  172. Reseed(ReadOnlySpan<byte>.Empty);
  173. }
  174. // 3.
  175. ImplGenerate(output);
  176. hmac_DRBG_Update();
  177. mReseedCounter++;
  178. return numberOfBits;
  179. }
  180. public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
  181. {
  182. int numberOfBits = output.Length * 8;
  183. if (numberOfBits > MAX_BITS_REQUEST)
  184. throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
  185. if (mReseedCounter > RESEED_MAX)
  186. return -1;
  187. if (predictionResistant)
  188. {
  189. Reseed(additionalInput);
  190. }
  191. else
  192. {
  193. // 2.
  194. hmac_DRBG_Update(additionalInput);
  195. }
  196. // 3.
  197. ImplGenerate(output);
  198. if (predictionResistant)
  199. {
  200. hmac_DRBG_Update();
  201. }
  202. else
  203. {
  204. hmac_DRBG_Update(additionalInput);
  205. }
  206. mReseedCounter++;
  207. return numberOfBits;
  208. }
  209. private void ImplGenerate(Span<byte> output)
  210. {
  211. int outputLen = output.Length;
  212. int m = outputLen / mV.Length;
  213. mHMac.Init(new KeyParameter(mK));
  214. for (int i = 0; i < m; i++)
  215. {
  216. mHMac.BlockUpdate(mV);
  217. mHMac.DoFinal(mV);
  218. mV.CopyTo(output[(i * mV.Length)..]);
  219. }
  220. int remaining = outputLen - m * mV.Length;
  221. if (remaining > 0)
  222. {
  223. mHMac.BlockUpdate(mV);
  224. mHMac.DoFinal(mV);
  225. mV[..remaining].CopyTo(output[(m * mV.Length)..]);
  226. }
  227. }
  228. #endif
  229. /**
  230. * Reseed the DRBG.
  231. *
  232. * @param additionalInput additional input to be added to the DRBG in this step.
  233. */
  234. public void Reseed(byte[] additionalInput)
  235. {
  236. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  237. Reseed(Spans.FromNullableReadOnly(additionalInput));
  238. #else
  239. byte[] entropy = GetEntropy();
  240. byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
  241. hmac_DRBG_Update(seedMaterial);
  242. mReseedCounter = 1;
  243. #endif
  244. }
  245. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  246. public void Reseed(ReadOnlySpan<byte> additionalInput)
  247. {
  248. int entropyLength = GetEntropyLength();
  249. int seedMaterialLength = entropyLength + additionalInput.Length;
  250. Span<byte> seedMaterial = seedMaterialLength <= 256
  251. ? stackalloc byte[seedMaterialLength]
  252. : new byte[seedMaterialLength];
  253. GetEntropy(seedMaterial);
  254. additionalInput.CopyTo(seedMaterial[entropyLength..]);
  255. hmac_DRBG_Update(seedMaterial);
  256. mReseedCounter = 1;
  257. }
  258. #endif
  259. private byte[] GetEntropy()
  260. {
  261. byte[] entropy = mEntropySource.GetEntropy();
  262. if (entropy.Length < (mSecurityStrength + 7) / 8)
  263. throw new InvalidOperationException("Insufficient entropy provided by entropy source");
  264. return entropy;
  265. }
  266. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  267. private int GetEntropy(Span<byte> output)
  268. {
  269. int length = mEntropySource.GetEntropy(output);
  270. if (length < (mSecurityStrength + 7) / 8)
  271. throw new InvalidOperationException("Insufficient entropy provided by entropy source");
  272. return length;
  273. }
  274. private int GetEntropyLength()
  275. {
  276. return (mEntropySource.EntropySize + 7) / 8;
  277. }
  278. #endif
  279. }
  280. }
  281. #pragma warning restore
  282. #endif