using System; #if NET462_OR_GREATER || NETCOREAPP using System.IO.Hashing; #endif #if NET35_OR_GREATER || NETCOREAPP using System.Linq; #endif using System.Security.Cryptography; using Aaru.Checksums; using Aaru.CommonTypes.Interfaces; namespace SabreTools.Hashing { /// /// Wrapper for a single hashing algorithm /// public class HashWrapper : IDisposable { #region Properties /// /// Hash type associated with the current state /// #if NETFRAMEWORK || NETCOREAPP3_1 public HashType HashType { get; private set; } #else public HashType HashType { get; init; } #endif /// /// Current hash in bytes /// public byte[]? CurrentHashBytes { get { return _hasher switch { HashAlgorithm ha => ha.Hash, IChecksum ic => ic.Final(), #if NET462_OR_GREATER || NETCOREAPP NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash().Reverse().ToArray(), #endif _ => null, }; } } /// /// Current hash as a string /// public string? CurrentHashString { get { return _hasher switch { IChecksum ic => ic.End(), _ => ByteArrayToString(CurrentHashBytes), }; } } #endregion #region Private Fields /// /// Internal hasher being used for processing /// /// May be either a HashAlgorithm or NonCryptographicHashAlgorithm private object? _hasher; #endregion #region Constructors /// /// Constructor /// /// Hash type to instantiate public HashWrapper(HashType hashType) { this.HashType = hashType; GetHasher(); } /// /// Generate the correct hashing class based on the hash type /// private void GetHasher() { _hasher = HashType switch { #if NET462_OR_GREATER || NETCOREAPP HashType.CRC32 => new Crc32(), HashType.CRC64 => new Crc64(), #else HashType.CRC32 => new OptimizedCRC.OptimizedCRC(), #endif HashType.MD5 => MD5.Create(), HashType.SHA1 => SHA1.Create(), HashType.SHA256 => SHA256.Create(), HashType.SHA384 => SHA384.Create(), HashType.SHA512 => SHA512.Create(), HashType.SpamSum => new SpamSumContext(), #if NET462_OR_GREATER || NETCOREAPP HashType.XxHash32 => new XxHash32(), HashType.XxHash64 => new XxHash64(), HashType.XxHash3 => new XxHash3(), HashType.XxHash128 => new XxHash128(), #endif _ => null, }; } /// public void Dispose() { if (_hasher is IDisposable disposable) disposable.Dispose(); } #endregion #region Hashing /// /// Process a buffer of some length with the internal hash algorithm /// public void Process(byte[] buffer, int size) { switch (_hasher) { case HashAlgorithm ha: ha.TransformBlock(buffer, 0, size, null, 0); break; case IChecksum ic: ic.Update(buffer); break; #if NET462_OR_GREATER || NETCOREAPP case NonCryptographicHashAlgorithm ncha: var bufferSpan = new ReadOnlySpan(buffer, 0, size); ncha.Append(bufferSpan); break; #else case OptimizedCRC.OptimizedCRC oc: oc.Update(buffer, 0, size); break; #endif } } /// /// Finalize the internal hash algorigthm /// /// NonCryptographicHashAlgorithm implementations do not need finalization public void Terminate() { byte[] emptyBuffer = []; switch (_hasher) { case HashAlgorithm ha: ha.TransformFinalBlock(emptyBuffer, 0, 0); break; #if NET20_OR_GREATER || NETCOREAPP3_1 || NET5_0 case OptimizedCRC.OptimizedCRC oc: oc.Update([], 0, 0); break; #endif } } #endregion #region Helpers /// /// Convert a byte array to a hex string /// /// Byte array to convert /// Hex string representing the byte array /// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa private static string? ByteArrayToString(byte[]? bytes) { // If we get null in, we send null out if (bytes == null) return null; try { string hex = BitConverter.ToString(bytes); return hex.Replace("-", string.Empty).ToLowerInvariant(); } catch { return null; } } #endregion } }