// dnlib: See LICENSE.txt for more info using System; using System.IO; using System.Security.Cryptography; namespace dnlib.DotNet { /// /// Hashes some data according to a /// readonly struct AssemblyHash : IDisposable { readonly HashAlgorithm hasher; /// /// Constructor /// /// If is an unsupported hash algorithm, then /// will be used as the hash algorithm. /// The algorithm to use public AssemblyHash(AssemblyHashAlgorithm hashAlgo) => hasher = hashAlgo switch { AssemblyHashAlgorithm.MD5 => MD5.Create(), AssemblyHashAlgorithm.SHA_256 => SHA256.Create(), AssemblyHashAlgorithm.SHA_384 => SHA384.Create(), AssemblyHashAlgorithm.SHA_512 => SHA512.Create(), _ => SHA1.Create(), }; /// public void Dispose() { if (hasher is not null) ((IDisposable)hasher).Dispose(); } /// /// Hash data /// /// If is an unsupported hash algorithm, then /// will be used as the hash algorithm. /// The data /// The algorithm to use /// Hashed data or null if was null public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) { if (data is null) return null; using (var asmHash = new AssemblyHash(hashAlgo)) { asmHash.Hash(data); return asmHash.ComputeHash(); } } /// /// Hash data /// /// Data public void Hash(byte[] data) => Hash(data, 0, data.Length); /// /// Hash data /// /// Data /// Offset /// Length public void Hash(byte[] data, int offset, int length) { if (hasher.TransformBlock(data, offset, length, data, offset) != length) throw new IOException("Could not calculate hash"); } /// /// Hash stream data /// /// Stream /// Number of bytes to hash /// Temp buffer public void Hash(Stream stream, uint length, byte[] buffer) { while (length > 0) { int len = length > (uint)buffer.Length ? buffer.Length : (int)length; if (stream.Read(buffer, 0, len) != len) throw new IOException("Could not read data"); Hash(buffer, 0, len); length -= (uint)len; } } /// /// Computes the hash /// public byte[] ComputeHash() { hasher.TransformFinalBlock(Array2.Empty(), 0, 0); return hasher.Hash; } /// /// Creates a public key token from the hash of some /// /// A public key is hashed, and the last 8 bytes of the hash, in reverse /// order, is used as the public key token /// The data /// A new instance public static PublicKeyToken CreatePublicKeyToken(byte[] publicKeyData) { if (publicKeyData is null) return new PublicKeyToken(); var hash = Hash(publicKeyData, AssemblyHashAlgorithm.SHA1); var pkt = new byte[8]; for (int i = 0; i < pkt.Length && i < hash.Length; i++) pkt[i] = hash[hash.Length - i - 1]; return new PublicKeyToken(pkt); } } }