MD5Hash.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Security.Cryptography;
  6. namespace Renci.SshNet.Security.Cryptography
  7. {
  8. /// <summary>
  9. /// MD5 algorithm implementation
  10. /// </summary>
  11. public class MD5Hash : HashAlgorithm
  12. {
  13. private byte[] _buffer = new byte[4];
  14. private int _bufferOffset;
  15. private long _byteCount;
  16. private int H1, H2, H3, H4; // IV's
  17. private int[] _hashValue = new int[16];
  18. private int _offset;
  19. /// <summary>
  20. /// Gets the size, in bits, of the computed hash code.
  21. /// </summary>
  22. /// <returns>The size, in bits, of the computed hash code.</returns>
  23. public override int HashSize
  24. {
  25. get
  26. {
  27. return 128;
  28. }
  29. }
  30. /// <summary>
  31. /// Gets the input block size.
  32. /// </summary>
  33. /// <returns>The input block size.</returns>
  34. public override int InputBlockSize
  35. {
  36. get
  37. {
  38. return 64;
  39. }
  40. }
  41. /// <summary>
  42. /// Gets the output block size.
  43. /// </summary>
  44. /// <returns>The output block size.</returns>
  45. public override int OutputBlockSize
  46. {
  47. get
  48. {
  49. return 64;
  50. }
  51. }
  52. /// <summary>
  53. /// Gets a value indicating whether the current transform can be reused.
  54. /// </summary>
  55. /// <returns>Always true.</returns>
  56. public override bool CanReuseTransform
  57. {
  58. get
  59. {
  60. return true;
  61. }
  62. }
  63. /// <summary>
  64. /// Gets a value indicating whether multiple blocks can be transformed.
  65. /// </summary>
  66. /// <returns>true if multiple blocks can be transformed; otherwise, false.</returns>
  67. public override bool CanTransformMultipleBlocks
  68. {
  69. get
  70. {
  71. return true;
  72. }
  73. }
  74. /// <summary>
  75. /// Initializes a new instance of the <see cref="MD5Hash"/> class.
  76. /// </summary>
  77. public MD5Hash()
  78. {
  79. this.Initialize();
  80. }
  81. /// <summary>
  82. /// Routes data written to the object into the hash algorithm for computing the hash.
  83. /// </summary>
  84. /// <param name="array">The input to compute the hash code for.</param>
  85. /// <param name="ibStart">The offset into the byte array from which to begin using data.</param>
  86. /// <param name="cbSize">The number of bytes in the byte array to use as data.</param>
  87. protected override void HashCore(byte[] array, int ibStart, int cbSize)
  88. {
  89. // Fill the current word
  90. while ((this._bufferOffset != 0) && (cbSize > 0))
  91. {
  92. this.Update(array[ibStart]);
  93. ibStart++;
  94. cbSize--;
  95. }
  96. // Process whole words.
  97. while (cbSize > this._buffer.Length)
  98. {
  99. this.ProcessWord(array, ibStart);
  100. ibStart += this._buffer.Length;
  101. cbSize -= this._buffer.Length;
  102. this._byteCount += this._buffer.Length;
  103. }
  104. // Load in the remainder.
  105. while (cbSize > 0)
  106. {
  107. this.Update(array[ibStart]);
  108. ibStart++;
  109. cbSize--;
  110. }
  111. }
  112. /// <summary>
  113. /// Finalizes the hash computation after the last data is processed by the cryptographic stream object.
  114. /// </summary>
  115. /// <returns>
  116. /// The computed hash code.
  117. /// </returns>
  118. protected override byte[] HashFinal()
  119. {
  120. long bitLength = (this._byteCount << 3);
  121. // Add the pad bytes.
  122. this.Update((byte)128);
  123. while (this._bufferOffset != 0)
  124. this.Update((byte)0);
  125. if (this._offset > 14)
  126. {
  127. this.ProcessBlock();
  128. }
  129. this._hashValue[14] = (int)(bitLength & 0xffffffff);
  130. this._hashValue[15] = (int)((ulong)bitLength >> 32);
  131. this.ProcessBlock();
  132. var output = new byte[16];
  133. this.UnpackWord(H1, output, 0);
  134. this.UnpackWord(H2, output, 0 + 4);
  135. this.UnpackWord(H3, output, 0 + 8);
  136. this.UnpackWord(H4, output, 0 + 12);
  137. this.Initialize();
  138. return output;
  139. }
  140. /// <summary>
  141. /// Initializes an implementation of the <see cref="T:System.Security.Cryptography.HashAlgorithm"/> class.
  142. /// </summary>
  143. public override void Initialize()
  144. {
  145. this._byteCount = 0;
  146. this._bufferOffset = 0;
  147. Array.Clear(this._buffer, 0, this._buffer.Length);
  148. H1 = unchecked((int)0x67452301);
  149. H2 = unchecked((int)0xefcdab89);
  150. H3 = unchecked((int)0x98badcfe);
  151. H4 = unchecked((int)0x10325476);
  152. this._offset = 0;
  153. for (int i = 0; i != this._hashValue.Length; i++)
  154. {
  155. this._hashValue[i] = 0;
  156. }
  157. }
  158. private void Update(byte input)
  159. {
  160. this._buffer[this._bufferOffset++] = input;
  161. if (this._bufferOffset == this._buffer.Length)
  162. {
  163. this.ProcessWord(this._buffer, 0);
  164. this._bufferOffset = 0;
  165. }
  166. this._byteCount++;
  167. }
  168. private void ProcessWord(byte[] input, int inOff)
  169. {
  170. this._hashValue[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
  171. | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
  172. if (this._offset == 16)
  173. {
  174. ProcessBlock();
  175. }
  176. }
  177. private void UnpackWord(int word, byte[] outBytes, int outOff)
  178. {
  179. outBytes[outOff] = (byte)word;
  180. outBytes[outOff + 1] = (byte)((uint)word >> 8);
  181. outBytes[outOff + 2] = (byte)((uint)word >> 16);
  182. outBytes[outOff + 3] = (byte)((uint)word >> 24);
  183. }
  184. //
  185. // round 1 left rotates
  186. //
  187. private static readonly int S11 = 7;
  188. private static readonly int S12 = 12;
  189. private static readonly int S13 = 17;
  190. private static readonly int S14 = 22;
  191. //
  192. // round 2 left rotates
  193. //
  194. private static readonly int S21 = 5;
  195. private static readonly int S22 = 9;
  196. private static readonly int S23 = 14;
  197. private static readonly int S24 = 20;
  198. //
  199. // round 3 left rotates
  200. //
  201. private static readonly int S31 = 4;
  202. private static readonly int S32 = 11;
  203. private static readonly int S33 = 16;
  204. private static readonly int S34 = 23;
  205. //
  206. // round 4 left rotates
  207. //
  208. private static readonly int S41 = 6;
  209. private static readonly int S42 = 10;
  210. private static readonly int S43 = 15;
  211. private static readonly int S44 = 21;
  212. /*
  213. * rotate int x left n bits.
  214. */
  215. private int RotateLeft(int x, int n)
  216. {
  217. return (x << n) | (int)((uint)x >> (32 - n));
  218. }
  219. /*
  220. * F, G, H and I are the basic MD5 functions.
  221. */
  222. private int F(int u, int v, int w)
  223. {
  224. return (u & v) | (~u & w);
  225. }
  226. private int G(int u, int v, int w)
  227. {
  228. return (u & w) | (v & ~w);
  229. }
  230. private int H(int u, int v, int w)
  231. {
  232. return u ^ v ^ w;
  233. }
  234. private int K(int u, int v, int w)
  235. {
  236. return v ^ (u | ~w);
  237. }
  238. private void ProcessBlock()
  239. {
  240. int a = H1;
  241. int b = H2;
  242. int c = H3;
  243. int d = H4;
  244. //
  245. // Round 1 - F cycle, 16 times.
  246. //
  247. a = RotateLeft((a + F(b, c, d) + this._hashValue[0] + unchecked((int)0xd76aa478)), S11) + b;
  248. d = RotateLeft((d + F(a, b, c) + this._hashValue[1] + unchecked((int)0xe8c7b756)), S12) + a;
  249. c = RotateLeft((c + F(d, a, b) + this._hashValue[2] + unchecked((int)0x242070db)), S13) + d;
  250. b = RotateLeft((b + F(c, d, a) + this._hashValue[3] + unchecked((int)0xc1bdceee)), S14) + c;
  251. a = RotateLeft((a + F(b, c, d) + this._hashValue[4] + unchecked((int)0xf57c0faf)), S11) + b;
  252. d = RotateLeft((d + F(a, b, c) + this._hashValue[5] + unchecked((int)0x4787c62a)), S12) + a;
  253. c = RotateLeft((c + F(d, a, b) + this._hashValue[6] + unchecked((int)0xa8304613)), S13) + d;
  254. b = RotateLeft((b + F(c, d, a) + this._hashValue[7] + unchecked((int)0xfd469501)), S14) + c;
  255. a = RotateLeft((a + F(b, c, d) + this._hashValue[8] + unchecked((int)0x698098d8)), S11) + b;
  256. d = RotateLeft((d + F(a, b, c) + this._hashValue[9] + unchecked((int)0x8b44f7af)), S12) + a;
  257. c = RotateLeft((c + F(d, a, b) + this._hashValue[10] + unchecked((int)0xffff5bb1)), S13) + d;
  258. b = RotateLeft((b + F(c, d, a) + this._hashValue[11] + unchecked((int)0x895cd7be)), S14) + c;
  259. a = RotateLeft((a + F(b, c, d) + this._hashValue[12] + unchecked((int)0x6b901122)), S11) + b;
  260. d = RotateLeft((d + F(a, b, c) + this._hashValue[13] + unchecked((int)0xfd987193)), S12) + a;
  261. c = RotateLeft((c + F(d, a, b) + this._hashValue[14] + unchecked((int)0xa679438e)), S13) + d;
  262. b = RotateLeft((b + F(c, d, a) + this._hashValue[15] + unchecked((int)0x49b40821)), S14) + c;
  263. //
  264. // Round 2 - G cycle, 16 times.
  265. //
  266. a = RotateLeft((a + G(b, c, d) + this._hashValue[1] + unchecked((int)0xf61e2562)), S21) + b;
  267. d = RotateLeft((d + G(a, b, c) + this._hashValue[6] + unchecked((int)0xc040b340)), S22) + a;
  268. c = RotateLeft((c + G(d, a, b) + this._hashValue[11] + unchecked((int)0x265e5a51)), S23) + d;
  269. b = RotateLeft((b + G(c, d, a) + this._hashValue[0] + unchecked((int)0xe9b6c7aa)), S24) + c;
  270. a = RotateLeft((a + G(b, c, d) + this._hashValue[5] + unchecked((int)0xd62f105d)), S21) + b;
  271. d = RotateLeft((d + G(a, b, c) + this._hashValue[10] + unchecked((int)0x02441453)), S22) + a;
  272. c = RotateLeft((c + G(d, a, b) + this._hashValue[15] + unchecked((int)0xd8a1e681)), S23) + d;
  273. b = RotateLeft((b + G(c, d, a) + this._hashValue[4] + unchecked((int)0xe7d3fbc8)), S24) + c;
  274. a = RotateLeft((a + G(b, c, d) + this._hashValue[9] + unchecked((int)0x21e1cde6)), S21) + b;
  275. d = RotateLeft((d + G(a, b, c) + this._hashValue[14] + unchecked((int)0xc33707d6)), S22) + a;
  276. c = RotateLeft((c + G(d, a, b) + this._hashValue[3] + unchecked((int)0xf4d50d87)), S23) + d;
  277. b = RotateLeft((b + G(c, d, a) + this._hashValue[8] + unchecked((int)0x455a14ed)), S24) + c;
  278. a = RotateLeft((a + G(b, c, d) + this._hashValue[13] + unchecked((int)0xa9e3e905)), S21) + b;
  279. d = RotateLeft((d + G(a, b, c) + this._hashValue[2] + unchecked((int)0xfcefa3f8)), S22) + a;
  280. c = RotateLeft((c + G(d, a, b) + this._hashValue[7] + unchecked((int)0x676f02d9)), S23) + d;
  281. b = RotateLeft((b + G(c, d, a) + this._hashValue[12] + unchecked((int)0x8d2a4c8a)), S24) + c;
  282. //
  283. // Round 3 - H cycle, 16 times.
  284. //
  285. a = RotateLeft((a + H(b, c, d) + this._hashValue[5] + unchecked((int)0xfffa3942)), S31) + b;
  286. d = RotateLeft((d + H(a, b, c) + this._hashValue[8] + unchecked((int)0x8771f681)), S32) + a;
  287. c = RotateLeft((c + H(d, a, b) + this._hashValue[11] + unchecked((int)0x6d9d6122)), S33) + d;
  288. b = RotateLeft((b + H(c, d, a) + this._hashValue[14] + unchecked((int)0xfde5380c)), S34) + c;
  289. a = RotateLeft((a + H(b, c, d) + this._hashValue[1] + unchecked((int)0xa4beea44)), S31) + b;
  290. d = RotateLeft((d + H(a, b, c) + this._hashValue[4] + unchecked((int)0x4bdecfa9)), S32) + a;
  291. c = RotateLeft((c + H(d, a, b) + this._hashValue[7] + unchecked((int)0xf6bb4b60)), S33) + d;
  292. b = RotateLeft((b + H(c, d, a) + this._hashValue[10] + unchecked((int)0xbebfbc70)), S34) + c;
  293. a = RotateLeft((a + H(b, c, d) + this._hashValue[13] + unchecked((int)0x289b7ec6)), S31) + b;
  294. d = RotateLeft((d + H(a, b, c) + this._hashValue[0] + unchecked((int)0xeaa127fa)), S32) + a;
  295. c = RotateLeft((c + H(d, a, b) + this._hashValue[3] + unchecked((int)0xd4ef3085)), S33) + d;
  296. b = RotateLeft((b + H(c, d, a) + this._hashValue[6] + unchecked((int)0x04881d05)), S34) + c;
  297. a = RotateLeft((a + H(b, c, d) + this._hashValue[9] + unchecked((int)0xd9d4d039)), S31) + b;
  298. d = RotateLeft((d + H(a, b, c) + this._hashValue[12] + unchecked((int)0xe6db99e5)), S32) + a;
  299. c = RotateLeft((c + H(d, a, b) + this._hashValue[15] + unchecked((int)0x1fa27cf8)), S33) + d;
  300. b = RotateLeft((b + H(c, d, a) + this._hashValue[2] + unchecked((int)0xc4ac5665)), S34) + c;
  301. //
  302. // Round 4 - K cycle, 16 times.
  303. //
  304. a = RotateLeft((a + K(b, c, d) + this._hashValue[0] + unchecked((int)0xf4292244)), S41) + b;
  305. d = RotateLeft((d + K(a, b, c) + this._hashValue[7] + unchecked((int)0x432aff97)), S42) + a;
  306. c = RotateLeft((c + K(d, a, b) + this._hashValue[14] + unchecked((int)0xab9423a7)), S43) + d;
  307. b = RotateLeft((b + K(c, d, a) + this._hashValue[5] + unchecked((int)0xfc93a039)), S44) + c;
  308. a = RotateLeft((a + K(b, c, d) + this._hashValue[12] + unchecked((int)0x655b59c3)), S41) + b;
  309. d = RotateLeft((d + K(a, b, c) + this._hashValue[3] + unchecked((int)0x8f0ccc92)), S42) + a;
  310. c = RotateLeft((c + K(d, a, b) + this._hashValue[10] + unchecked((int)0xffeff47d)), S43) + d;
  311. b = RotateLeft((b + K(c, d, a) + this._hashValue[1] + unchecked((int)0x85845dd1)), S44) + c;
  312. a = RotateLeft((a + K(b, c, d) + this._hashValue[8] + unchecked((int)0x6fa87e4f)), S41) + b;
  313. d = RotateLeft((d + K(a, b, c) + this._hashValue[15] + unchecked((int)0xfe2ce6e0)), S42) + a;
  314. c = RotateLeft((c + K(d, a, b) + this._hashValue[6] + unchecked((int)0xa3014314)), S43) + d;
  315. b = RotateLeft((b + K(c, d, a) + this._hashValue[13] + unchecked((int)0x4e0811a1)), S44) + c;
  316. a = RotateLeft((a + K(b, c, d) + this._hashValue[4] + unchecked((int)0xf7537e82)), S41) + b;
  317. d = RotateLeft((d + K(a, b, c) + this._hashValue[11] + unchecked((int)0xbd3af235)), S42) + a;
  318. c = RotateLeft((c + K(d, a, b) + this._hashValue[2] + unchecked((int)0x2ad7d2bb)), S43) + d;
  319. b = RotateLeft((b + K(c, d, a) + this._hashValue[9] + unchecked((int)0xeb86d391)), S44) + c;
  320. H1 += a;
  321. H2 += b;
  322. H3 += c;
  323. H4 += d;
  324. //
  325. // reset the offset and clean out the word buffer.
  326. //
  327. this._offset = 0;
  328. for (int i = 0; i != this._hashValue.Length; i++)
  329. {
  330. this._hashValue[i] = 0;
  331. }
  332. }
  333. }
  334. }