HKDFBytesGenerator.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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.Macs;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  7. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators
  8. {
  9. /**
  10. * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) implemented
  11. * according to IETF RFC 5869, May 2010 as specified by H. Krawczyk, IBM
  12. * Research & P. Eronen, Nokia. It uses a HMac internally to compute de OKM
  13. * (output keying material) and is likely to have better security properties
  14. * than KDF's based on just a hash function.
  15. */
  16. public sealed class HkdfBytesGenerator
  17. : IDerivationFunction
  18. {
  19. private HMac hMacHash;
  20. private int hashLen;
  21. private byte[] info;
  22. private byte[] currentT;
  23. private int generatedBytes;
  24. /**
  25. * Creates a HKDFBytesGenerator based on the given hash function.
  26. *
  27. * @param hash the digest to be used as the source of generatedBytes bytes
  28. */
  29. public HkdfBytesGenerator(IDigest hash)
  30. {
  31. this.hMacHash = new HMac(hash);
  32. this.hashLen = hash.GetDigestSize();
  33. }
  34. public void Init(IDerivationParameters parameters)
  35. {
  36. if (!(parameters is HkdfParameters hkdfParameters))
  37. throw new ArgumentException("HKDF parameters required for HkdfBytesGenerator", "parameters");
  38. if (hkdfParameters.SkipExtract)
  39. {
  40. // use IKM directly as PRK
  41. hMacHash.Init(new KeyParameter(hkdfParameters.GetIkm()));
  42. }
  43. else
  44. {
  45. hMacHash.Init(Extract(hkdfParameters.GetSalt(), hkdfParameters.GetIkm()));
  46. }
  47. info = hkdfParameters.GetInfo();
  48. generatedBytes = 0;
  49. currentT = new byte[hashLen];
  50. }
  51. /**
  52. * Performs the extract part of the key derivation function.
  53. *
  54. * @param salt the salt to use
  55. * @param ikm the input keying material
  56. * @return the PRK as KeyParameter
  57. */
  58. private KeyParameter Extract(byte[] salt, byte[] ikm)
  59. {
  60. if (salt == null)
  61. {
  62. // TODO check if hashLen is indeed same as HMAC size
  63. hMacHash.Init(new KeyParameter(new byte[hashLen]));
  64. }
  65. else
  66. {
  67. hMacHash.Init(new KeyParameter(salt));
  68. }
  69. hMacHash.BlockUpdate(ikm, 0, ikm.Length);
  70. byte[] prk = new byte[hashLen];
  71. hMacHash.DoFinal(prk, 0);
  72. return new KeyParameter(prk);
  73. }
  74. /**
  75. * Performs the expand part of the key derivation function, using currentT
  76. * as input and output buffer.
  77. *
  78. * @throws DataLengthException if the total number of bytes generated is larger than the one
  79. * specified by RFC 5869 (255 * HashLen)
  80. */
  81. private void ExpandNext()
  82. {
  83. int n = generatedBytes / hashLen + 1;
  84. if (n >= 256)
  85. {
  86. throw new DataLengthException(
  87. "HKDF cannot generate more than 255 blocks of HashLen size");
  88. }
  89. // special case for T(0): T(0) is empty, so no update
  90. if (generatedBytes != 0)
  91. {
  92. hMacHash.BlockUpdate(currentT, 0, hashLen);
  93. }
  94. hMacHash.BlockUpdate(info, 0, info.Length);
  95. hMacHash.Update((byte)n);
  96. hMacHash.DoFinal(currentT, 0);
  97. }
  98. public IDigest Digest => hMacHash.GetUnderlyingDigest();
  99. public int GenerateBytes(byte[] output, int outOff, int length)
  100. {
  101. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  102. return GenerateBytes(output.AsSpan(outOff, length));
  103. #else
  104. if (generatedBytes > 255 * hashLen - length)
  105. throw new DataLengthException("HKDF may only be used for 255 * HashLen bytes of output");
  106. int toGenerate = length;
  107. int posInT = generatedBytes % hashLen;
  108. if (posInT != 0)
  109. {
  110. // copy what is left in the currentT (1..hash
  111. int toCopy = System.Math.Min(hashLen - posInT, toGenerate);
  112. Array.Copy(currentT, posInT, output, outOff, toCopy);
  113. generatedBytes += toCopy;
  114. toGenerate -= toCopy;
  115. outOff += toCopy;
  116. }
  117. while (toGenerate > 0)
  118. {
  119. ExpandNext();
  120. int toCopy = System.Math.Min(hashLen, toGenerate);
  121. Array.Copy(currentT, 0, output, outOff, toCopy);
  122. generatedBytes += toCopy;
  123. toGenerate -= toCopy;
  124. outOff += toCopy;
  125. }
  126. return length;
  127. #endif
  128. }
  129. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  130. public int GenerateBytes(Span<byte> output)
  131. {
  132. int length = output.Length;
  133. if (generatedBytes > 255 * hashLen - length)
  134. throw new DataLengthException("HKDF may only be used for 255 * HashLen bytes of output");
  135. int posInT = generatedBytes % hashLen;
  136. if (posInT != 0)
  137. {
  138. // copy what is left in the currentT (1..hash
  139. int toCopy = System.Math.Min(hashLen - posInT, output.Length);
  140. currentT.AsSpan(posInT, toCopy).CopyTo(output);
  141. generatedBytes += toCopy;
  142. output = output[toCopy..];
  143. }
  144. while (!output.IsEmpty)
  145. {
  146. ExpandNext();
  147. int toCopy = System.Math.Min(hashLen, output.Length);
  148. currentT.AsSpan(0, toCopy).CopyTo(output);
  149. generatedBytes += toCopy;
  150. output = output[toCopy..];
  151. }
  152. return length;
  153. }
  154. #endif
  155. }
  156. }
  157. #pragma warning restore
  158. #endif