Blake2xsDigest.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  5. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests
  6. {
  7. /*
  8. The BLAKE2 cryptographic hash function was designed by Jean-
  9. Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
  10. Winnerlein.
  11. Reference Implementation and Description can be found at: https://blake2.net/blake2x.pdf
  12. */
  13. /**
  14. * Implementation of the eXtendable Output Function (XOF) BLAKE2xs.
  15. * <p/>
  16. * BLAKE2xs offers a built-in keying mechanism to be used directly
  17. * for authentication ("Prefix-MAC") rather than a HMAC construction.
  18. * <p/>
  19. * BLAKE2xs offers a built-in support for a salt for randomized hashing
  20. * and a personal string for defining a unique hash function for each application.
  21. * <p/>
  22. * BLAKE2xs is optimized for 32-bit platforms and produces digests of any size
  23. * between 1 and 2^16-2 bytes. The length can also be unknown and then the maximum
  24. * length will be 2^32 blocks of 32 bytes.
  25. */
  26. public sealed class Blake2xsDigest
  27. : IXof
  28. {
  29. /**
  30. * Magic number to indicate an unknown length of digest
  31. */
  32. public const int UnknownDigestLength = 65535;
  33. private const int DigestLength = 32;
  34. private const long MaxNumberBlocks = 1L << 32;
  35. /**
  36. * Expected digest length for the xof. It can be unknown.
  37. */
  38. private int digestLength;
  39. /**
  40. * Root hash that will take the updates
  41. */
  42. private Blake2sDigest hash;
  43. /**
  44. * Digest of the root hash
  45. */
  46. private byte[] h0 = null;
  47. /**
  48. * Digest of each round of the XOF
  49. */
  50. private byte[] buf = new byte[32];
  51. /**
  52. * Current position for a round
  53. */
  54. private int bufPos = 32;
  55. /**
  56. * Overall position of the digest. It is useful when the length is known
  57. * in advance to get last block length.
  58. */
  59. private int digestPos = 0;
  60. /**
  61. * Keep track of the round number to detect the end of the digest after
  62. * 2^32 blocks of 32 bytes.
  63. */
  64. private long blockPos = 0;
  65. /**
  66. * Current node offset incremented by 1 every round.
  67. */
  68. private long nodeOffset;
  69. /**
  70. * BLAKE2xs for hashing with unknown digest length
  71. */
  72. public Blake2xsDigest()
  73. : this(UnknownDigestLength)
  74. {
  75. }
  76. /**
  77. * BLAKE2xs for hashing
  78. *
  79. * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
  80. */
  81. public Blake2xsDigest(int digestBytes)
  82. : this(digestBytes, null, null, null)
  83. {
  84. }
  85. /**
  86. * BLAKE2xs with key
  87. *
  88. * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
  89. * @param key A key up to 32 bytes or null
  90. */
  91. public Blake2xsDigest(int digestBytes, byte[] key)
  92. : this(digestBytes, key, null, null)
  93. {
  94. }
  95. /**
  96. * BLAKE2xs with key, salt and personalization
  97. *
  98. * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
  99. * @param key A key up to 32 bytes or null
  100. * @param salt 8 bytes or null
  101. * @param personalization 8 bytes or null
  102. */
  103. public Blake2xsDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization)
  104. {
  105. if (digestBytes < 1 || digestBytes > UnknownDigestLength)
  106. throw new ArgumentException("BLAKE2xs digest length must be between 1 and 2^16-1");
  107. digestLength = digestBytes;
  108. nodeOffset = ComputeNodeOffset();
  109. hash = new Blake2sDigest(DigestLength, key, salt, personalization, nodeOffset);
  110. }
  111. public Blake2xsDigest(Blake2xsDigest digest)
  112. {
  113. digestLength = digest.digestLength;
  114. hash = new Blake2sDigest(digest.hash);
  115. h0 = Arrays.Clone(digest.h0);
  116. buf = Arrays.Clone(digest.buf);
  117. bufPos = digest.bufPos;
  118. digestPos = digest.digestPos;
  119. blockPos = digest.blockPos;
  120. nodeOffset = digest.nodeOffset;
  121. }
  122. /**
  123. * Return the algorithm name.
  124. *
  125. * @return the algorithm name
  126. */
  127. public string AlgorithmName => "BLAKE2xs";
  128. /**
  129. * Return the size in bytes of the digest produced by this message digest.
  130. *
  131. * @return the size in bytes of the digest produced by this message digest.
  132. */
  133. public int GetDigestSize() => digestLength;
  134. /**
  135. * Return the size in bytes of the internal buffer the digest applies its
  136. * compression function to.
  137. *
  138. * @return byte length of the digest's internal buffer.
  139. */
  140. public int GetByteLength() => hash.GetByteLength();
  141. /**
  142. * Return the maximum size in bytes the digest can produce when the length
  143. * is unknown
  144. *
  145. * @return byte length of the largest digest with unknown length
  146. */
  147. public long GetUnknownMaxLength()
  148. {
  149. return MaxNumberBlocks * DigestLength;
  150. }
  151. /**
  152. * Update the message digest with a single byte.
  153. *
  154. * @param in the input byte to be entered.
  155. */
  156. public void Update(byte b)
  157. {
  158. hash.Update(b);
  159. }
  160. /**
  161. * Update the message digest with a block of bytes.
  162. *
  163. * @param in the byte array containing the data.
  164. * @param inOff the offset into the byte array where the data starts.
  165. * @param len the length of the data.
  166. */
  167. public void BlockUpdate(byte[] input, int inOff, int inLen)
  168. {
  169. hash.BlockUpdate(input, inOff, inLen);
  170. }
  171. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  172. public void BlockUpdate(ReadOnlySpan<byte> input)
  173. {
  174. hash.BlockUpdate(input);
  175. }
  176. #endif
  177. /**
  178. * Reset the digest back to its initial state. The key, the salt and the
  179. * personal string will remain for further computations.
  180. */
  181. public void Reset()
  182. {
  183. hash.Reset();
  184. h0 = null;
  185. bufPos = DigestLength;
  186. digestPos = 0;
  187. blockPos = 0;
  188. nodeOffset = ComputeNodeOffset();
  189. }
  190. /**
  191. * Close the digest, producing the final digest value. The doFinal() call
  192. * leaves the digest reset. Key, salt and personal string remain.
  193. *
  194. * @param out the array the digest is to be copied into.
  195. * @param outOffset the offset into the out array the digest is to start at.
  196. */
  197. public int DoFinal(byte[] output, int outOff)
  198. {
  199. return OutputFinal(output, outOff, digestLength);
  200. }
  201. /**
  202. * Close the digest, producing the final digest value. The doFinal() call
  203. * leaves the digest reset. Key, salt, personal string remain.
  204. *
  205. * @param out output array to write the output bytes to.
  206. * @param outOff offset to start writing the bytes at.
  207. * @param outLen the number of output bytes requested.
  208. */
  209. public int OutputFinal(byte[] output, int outOff, int outLen)
  210. {
  211. int ret = Output(output, outOff, outLen);
  212. Reset();
  213. return ret;
  214. }
  215. /**
  216. * Start outputting the results of the final calculation for this digest. Unlike doFinal, this method
  217. * will continue producing output until the Xof is explicitly reset, or signals otherwise.
  218. *
  219. * @param out output array to write the output bytes to.
  220. * @param outOff offset to start writing the bytes at.
  221. * @param outLen the number of output bytes requested.
  222. * @return the number of bytes written
  223. */
  224. public int Output(byte[] output, int outOff, int outLen)
  225. {
  226. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  227. return Output(output.AsSpan(outOff, outLen));
  228. #else
  229. if (h0 == null)
  230. {
  231. h0 = new byte[hash.GetDigestSize()];
  232. hash.DoFinal(h0, 0);
  233. }
  234. if (digestLength != UnknownDigestLength)
  235. {
  236. if (digestPos + outLen > digestLength)
  237. throw new ArgumentException("Output length is above the digest length");
  238. }
  239. else if (blockPos << 5 >= GetUnknownMaxLength())
  240. {
  241. throw new ArgumentException("Maximum length is 2^32 blocks of 32 bytes");
  242. }
  243. for (int i = 0; i < outLen; i++)
  244. {
  245. if (bufPos >= DigestLength)
  246. {
  247. Blake2sDigest h = new Blake2sDigest(ComputeStepLength(), DigestLength, nodeOffset);
  248. h.BlockUpdate(h0, 0, h0.Length);
  249. Arrays.Fill(buf, 0);
  250. h.DoFinal(buf, 0);
  251. bufPos = 0;
  252. nodeOffset++;
  253. blockPos++;
  254. }
  255. output[outOff + i] = buf[bufPos];
  256. bufPos++;
  257. digestPos++;
  258. }
  259. return outLen;
  260. #endif
  261. }
  262. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  263. public int DoFinal(Span<byte> output)
  264. {
  265. return OutputFinal(output[..digestLength]);
  266. }
  267. public int OutputFinal(Span<byte> output)
  268. {
  269. int ret = Output(output);
  270. Reset();
  271. return ret;
  272. }
  273. public int Output(Span<byte> output)
  274. {
  275. int outLen = output.Length;
  276. if (h0 == null)
  277. {
  278. h0 = new byte[hash.GetDigestSize()];
  279. hash.DoFinal(h0);
  280. }
  281. if (digestLength != UnknownDigestLength)
  282. {
  283. if (digestPos + outLen > digestLength)
  284. throw new ArgumentException("Output length is above the digest length");
  285. }
  286. else if (blockPos << 5 >= GetUnknownMaxLength())
  287. {
  288. throw new ArgumentException("Maximum length is 2^32 blocks of 32 bytes");
  289. }
  290. for (int i = 0; i < outLen; i++)
  291. {
  292. if (bufPos >= DigestLength)
  293. {
  294. Blake2sDigest h = new Blake2sDigest(ComputeStepLength(), DigestLength, nodeOffset);
  295. h.BlockUpdate(h0);
  296. Arrays.Fill(buf, 0);
  297. h.DoFinal(buf);
  298. bufPos = 0;
  299. nodeOffset++;
  300. blockPos++;
  301. }
  302. output[i] = buf[bufPos];
  303. bufPos++;
  304. digestPos++;
  305. }
  306. return outLen;
  307. }
  308. #endif
  309. // get the next round length. If the length is unknown, the digest length is always the maximum.
  310. private int ComputeStepLength()
  311. {
  312. if (digestLength == UnknownDigestLength)
  313. return DigestLength;
  314. return System.Math.Min(DigestLength, digestLength - digestPos);
  315. }
  316. private long ComputeNodeOffset()
  317. {
  318. return digestLength * 0x100000000L;
  319. }
  320. }
  321. }
  322. #pragma warning restore
  323. #endif