From 62a0f6e49a27b32efd2bf0d7cab58f2d20b5391d Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 15 Jan 2021 11:57:24 -0800 Subject: [PATCH] Port most hashing code from SabreTools Note that this explcitly omits both the formerly-supported RIPEMD160 as well as the currently-Aaru-specific SpamSum. --- MPF.Library/Data/Enumerations.cs | 18 +++ MPF.Library/Utilities/Hasher.cs | 125 +++++++++++++++++++++ MPF.Library/Utilities/OptimizedCRC.cs | 153 ++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 MPF.Library/Utilities/Hasher.cs create mode 100644 MPF.Library/Utilities/OptimizedCRC.cs diff --git a/MPF.Library/Data/Enumerations.cs b/MPF.Library/Data/Enumerations.cs index 4aa51422..72c216d6 100644 --- a/MPF.Library/Data/Enumerations.cs +++ b/MPF.Library/Data/Enumerations.cs @@ -2,6 +2,24 @@ namespace MPF.Data { + /// + /// Available hashing types + /// + [Flags] + public enum Hash + { + CRC = 1 << 0, + MD5 = 1 << 1, + SHA1 = 1 << 2, + SHA256 = 1 << 3, + SHA384 = 1 << 4, + SHA512 = 1 << 5, + + // Special combinations + Standard = CRC | MD5 | SHA1, + All = CRC | MD5 | SHA1 | SHA256 | SHA384 | SHA512, + } + /// /// Drive type for dumping /// diff --git a/MPF.Library/Utilities/Hasher.cs b/MPF.Library/Utilities/Hasher.cs new file mode 100644 index 00000000..133674c8 --- /dev/null +++ b/MPF.Library/Utilities/Hasher.cs @@ -0,0 +1,125 @@ +using System; +using System.Linq; +using System.Security.Cryptography; +using MPF.Data; +using OptimizedCRC; + +namespace MPF.Utilities +{ + /// + /// Async hashing class wraper + /// + public class Hasher + { + public Hash HashType { get; private set; } + private IDisposable _hasher; + + public Hasher(Hash hashType) + { + this.HashType = hashType; + GetHasher(); + } + + /// + /// Generate the correct hashing class based on the hash type + /// + private void GetHasher() + { + switch (HashType) + { + case Hash.CRC: + _hasher = new OptimizedCRC.OptimizedCRC(); + break; + + case Hash.MD5: + _hasher = MD5.Create(); + break; + + case Hash.SHA1: + _hasher = SHA1.Create(); + break; + + case Hash.SHA256: + _hasher = SHA256.Create(); + break; + + case Hash.SHA384: + _hasher = SHA384.Create(); + break; + + case Hash.SHA512: + _hasher = SHA512.Create(); + break; + } + } + + public void Dispose() + { + _hasher.Dispose(); + } + + /// + /// Process a buffer of some length with the internal hash algorithm + /// + public void Process(byte[] buffer, int size) + { + switch (HashType) + { + case Hash.CRC: + (_hasher as OptimizedCRC.OptimizedCRC).Update(buffer, 0, size); + break; + + case Hash.MD5: + case Hash.SHA1: + case Hash.SHA256: + case Hash.SHA384: + case Hash.SHA512: + (_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0); + break; + } + } + + /// + /// Finalize the internal hash algorigthm + /// + public void Finalize() + { + byte[] emptyBuffer = new byte[0]; + switch (HashType) + { + case Hash.CRC: + (_hasher as OptimizedCRC.OptimizedCRC).Update(emptyBuffer, 0, 0); + break; + + case Hash.MD5: + case Hash.SHA1: + case Hash.SHA256: + case Hash.SHA384: + case Hash.SHA512: + (_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0); + break; + } + } + + /// + /// Get internal hash as a byte array + /// + public byte[] GetHash() + { + switch (HashType) + { + case Hash.CRC: + return BitConverter.GetBytes((_hasher as OptimizedCRC.OptimizedCRC).Value).Reverse().ToArray(); + + case Hash.MD5: + case Hash.SHA1: + case Hash.SHA256: + case Hash.SHA384: + case Hash.SHA512: + return (_hasher as HashAlgorithm).Hash; + } + + return null; + } + } +} diff --git a/MPF.Library/Utilities/OptimizedCRC.cs b/MPF.Library/Utilities/OptimizedCRC.cs new file mode 100644 index 00000000..160a7757 --- /dev/null +++ b/MPF.Library/Utilities/OptimizedCRC.cs @@ -0,0 +1,153 @@ +/* + + Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +using System; + +namespace OptimizedCRC +{ + internal class OptimizedCRC : IDisposable + { + private const uint kCrcPoly = 0xEDB88320; + private const uint kInitial = 0xFFFFFFFF; + private const int CRC_NUM_TABLES = 8; + private static readonly uint[] Table; + + static OptimizedCRC() + { + unchecked + { + Table = new uint[256 * CRC_NUM_TABLES]; + int i; + for (i = 0; i < 256; i++) + { + uint r = (uint)i; + for (int j = 0; j < 8; j++) + { + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + } + Table[i] = r; + } + for (; i < 256 * CRC_NUM_TABLES; i++) + { + uint r = Table[i - 256]; + Table[i] = Table[r & 0xFF] ^ (r >> 8); + } + } + } + + public uint UnsignedValue; + + public OptimizedCRC() + { + Init(); + } + + /// + /// Reset CRC + /// + public void Init() + { + UnsignedValue = kInitial; + } + + public int Value + { + get { return (int)~UnsignedValue; } + } + + public void Update(byte[] data, int offset, int count) + { + new ArraySegment(data, offset, count); // check arguments + if (count == 0) + { + return; + } + + var table = OptimizedCRC.Table; + + uint crc = UnsignedValue; + + for (; (offset & 7) != 0 && count != 0; count--) + { + crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]]; + } + + if (count >= 8) + { + /* + * Idea from 7-zip project sources (http://7-zip.org/sdk.html) + */ + + int end = (count - 8) & ~7; + count -= end; + end += offset; + + while (offset != end) + { + crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24)); + uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24)); + offset += 8; + + crc = table[(byte)crc + 0x700] + ^ table[(byte)(crc >>= 8) + 0x600] + ^ table[(byte)(crc >>= 8) + 0x500] + ^ table[/*(byte)*/(crc >> 8) + 0x400] + ^ table[(byte)(high) + 0x300] + ^ table[(byte)(high >>= 8) + 0x200] + ^ table[(byte)(high >>= 8) + 0x100] + ^ table[/*(byte)*/(high >> 8) + 0x000]; + } + } + + while (count-- != 0) + { + crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]]; + } + + UnsignedValue = crc; + } + + static public int Compute(byte[] data, int offset, int count) + { + var crc = new OptimizedCRC(); + crc.Update(data, offset, count); + return crc.Value; + } + + static public int Compute(byte[] data) + { + return Compute(data, 0, data.Length); + } + + static public int Compute(ArraySegment block) + { + return Compute(block.Array, block.Offset, block.Count); + } + + public void Dispose() + { + UnsignedValue = 0; + } + } +} \ No newline at end of file