You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

166 line
6.2 KiB

  1. /* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. *
  21. *
  22. * Copyright (c) Damien Guard. All rights reserved.
  23. * AlphaFS has written permission from the author to include the CRC code.
  24. */
  25. using System;
  26. using System.Collections.Generic;
  27. using System.Diagnostics.CodeAnalysis;
  28. using System.Security.Cryptography;
  29. namespace Alphaleonis.Win32.Security
  30. {
  31. /// <summary>Implements a 32-bit CRC hash algorithm compatible with Zip etc.</summary>
  32. /// <remarks>
  33. /// Crc32 should only be used for backward compatibility with older file formats
  34. /// and algorithms. It is not secure enough for new applications.
  35. /// If you need to call multiple times for the same data either use the HashAlgorithm
  36. /// interface or remember that the result of one Compute call needs to be ~ (XOR) before
  37. /// being passed in as the seed for the next Compute call.
  38. /// </remarks>
  39. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")]
  40. internal sealed class Crc32 : HashAlgorithm
  41. {
  42. private const uint DefaultPolynomial = 0xedb88320u;
  43. private const uint DefaultSeed = 0xffffffffu;
  44. private uint m_hash;
  45. private static uint[] s_defaultTable;
  46. private readonly uint m_seed;
  47. private readonly uint[] m_table;
  48. /// <summary>
  49. /// Initializes a new instance of Crc32.
  50. /// </summary>
  51. public Crc32()
  52. : this(DefaultPolynomial, DefaultSeed)
  53. {
  54. }
  55. /// <summary>Initializes a new instance of Crc32.</summary>
  56. /// <param name="polynomial">The polynomial.</param>
  57. /// <param name="seed">The seed.</param>
  58. private Crc32(uint polynomial, uint seed)
  59. {
  60. m_table = InitializeTable(polynomial);
  61. m_seed = m_hash = seed;
  62. }
  63. /// <summary>
  64. /// Initializes an implementation of the
  65. /// <see cref="T:System.Security.Cryptography.HashAlgorithm" /> class.
  66. /// </summary>
  67. public override void Initialize()
  68. {
  69. m_hash = m_seed;
  70. }
  71. /// <summary>When overridden in a derived class, routes data written to the object into the hash algorithm for computing the hash.</summary>
  72. /// <param name="array">The input to compute the hash code for..</param>
  73. /// <param name="ibStart">The offset into the byte array from which to begin using data.</param>
  74. /// <param name="cbSize">The number of bytes in the byte array to use as data.</param>
  75. protected override void HashCore(byte[] array, int ibStart, int cbSize)
  76. {
  77. m_hash = CalculateHash(m_table, m_hash, array, ibStart, cbSize);
  78. }
  79. /// <summary>
  80. /// Finalizes the hash computation after the last data is processed by the cryptographic stream
  81. /// object.
  82. /// </summary>
  83. /// <returns>
  84. /// This method finalizes any partial computation and returns the correct hash value for the data
  85. /// stream.
  86. /// </returns>
  87. protected override byte[] HashFinal()
  88. {
  89. var hashBuffer = UInt32ToBigEndianBytes(~m_hash);
  90. HashValue = hashBuffer;
  91. return hashBuffer;
  92. }
  93. /// <summary>Gets the size, in bits, of the computed hash code.</summary>
  94. /// <value>The size, in bits, of the computed hash code.</value>
  95. public override int HashSize
  96. {
  97. get { return 32; }
  98. }
  99. /// <summary>Initializes the table.</summary>
  100. /// <param name="polynomial">The polynomial.</param>
  101. /// <returns>The table.</returns>
  102. private static uint[] InitializeTable(uint polynomial)
  103. {
  104. if (polynomial == DefaultPolynomial && s_defaultTable != null)
  105. return s_defaultTable;
  106. var createTable = new uint[256];
  107. for (var i = 0; i < 256; i++)
  108. {
  109. var entry = (uint) i;
  110. for (var j = 0; j < 8; j++)
  111. if ((entry & 1) == 1)
  112. entry = (entry >> 1) ^ polynomial;
  113. else
  114. entry = entry >> 1;
  115. createTable[i] = entry;
  116. }
  117. if (polynomial == DefaultPolynomial)
  118. s_defaultTable = createTable;
  119. return createTable;
  120. }
  121. /// <summary>Calculates the hash.</summary>
  122. /// <param name="table">The table.</param>
  123. /// <param name="seed">The seed.</param>
  124. /// <param name="buffer">The buffer.</param>
  125. /// <param name="start">The start.</param>
  126. /// <param name="size">The size.</param>
  127. /// <returns>The calculated hash.</returns>
  128. private static uint CalculateHash(uint[] table, uint seed, IList<byte> buffer, int start, int size)
  129. {
  130. var hash = seed;
  131. for (var i = start; i < start + size; i++)
  132. hash = (hash >> 8) ^ table[buffer[i] ^ hash & 0xff];
  133. return hash;
  134. }
  135. /// <summary>Int 32 to big endian bytes.</summary>
  136. /// <param name="uint32">The second uint 3.</param>
  137. /// <returns>A byte[].</returns>
  138. private static byte[] UInt32ToBigEndianBytes(uint uint32)
  139. {
  140. var result = BitConverter.GetBytes(uint32);
  141. if (BitConverter.IsLittleEndian)
  142. Array.Reverse(result);
  143. return result;
  144. }
  145. }
  146. }