Hash.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Security / Policy / Hash.cs / 1305376 / Hash.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// [....]
// 
 
//
// Hash 
//
// Evidence corresponding to a hash of the assembly bits.
//
 
using System;
using System.Diagnostics.Contracts; 
using System.Collections.Generic; 
using System.Reflection;
using System.Runtime.CompilerServices; 
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning; 
using System.Security.Cryptography;
using System.Security.Util; 
using Microsoft.Win32.SafeHandles; 

namespace System.Security.Policy 
{
    [Serializable]
    [ComVisible(true)]
    public sealed class Hash : EvidenceBase, ISerializable 
    {
        private RuntimeAssembly m_assembly; 
        private Dictionary m_hashes; 
        private WeakReference m_rawData;
 
        /// 
        ///     Deserialize a serialized hash evidence object
        /// 
        [SecurityCritical] 
        internal Hash(SerializationInfo info, StreamingContext context)
        { 
            // 
            // We have three serialization formats that we might be deserializing, the Whidbey format which
            // contains hash values directly, the Whidbey format which contains a pointer to a PEImage, and 
            // the v4 format which contains a dictionary of calculated hashes.
            //
            // If we have the Whidbey version that has built in hash values, we can convert that, but we
            // cannot do anything with the PEImage format since that is a serialized pointer into another 
            // runtime's VM.
            // 
 
            Dictionary hashes = info.GetValueNoThrow("Hashes", typeof(Dictionary)) as Dictionary;
            if (hashes != null) 
            {
                m_hashes = hashes;
            }
            else 
            {
                // If there is no hash value dictionary, then check to see if we have the Whidbey multiple 
                // hashes version of the evidence. 
                m_hashes = new Dictionary();
 
                byte[] md5 = info.GetValueNoThrow("Md5", typeof(byte[])) as byte[];
                if (md5 != null)
                {
                    m_hashes[typeof(MD5)] = md5; 
                }
 
                byte[] sha1 = info.GetValueNoThrow("Sha1", typeof(byte[])) as byte[]; 
                if (sha1 != null)
                { 
                    m_hashes[typeof(SHA1)] = sha1;
                }

                byte[] rawData = info.GetValueNoThrow("RawData", typeof(byte[])) as byte[]; 
                if (rawData != null)
                { 
                    GenerateDefaultHashes(rawData); 
                }
            } 
        }

        /// 
        ///     Create hash evidence for the specified assembly 
        /// 
        public Hash(Assembly assembly) 
        { 
            if (assembly == null)
                throw new ArgumentNullException("assembly"); 
            Contract.EndContractBlock();
            if (assembly.IsDynamic)
                throw new ArgumentException(Environment.GetResourceString("Security_CannotGenerateHash"), "assembly");
 
            m_hashes = new Dictionary();
            m_assembly = assembly as RuntimeAssembly; 
 
            if (m_assembly == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"), "assembly"); 
        }

        /// 
        ///     Create a copy of some hash evidence 
        /// 
        private Hash(Hash hash) 
        { 
            Contract.Assert(hash != null);
 
            m_assembly = hash.m_assembly;
            m_rawData = hash.m_rawData;
            m_hashes = new Dictionary(hash.m_hashes);
        } 

        ///  
        ///     Create a hash evidence prepopulated with a specific hash value 
        /// 
        private Hash(Type hashType, byte[] hashValue) 
        {
            Contract.Assert(hashType != null);
            Contract.Assert(hashValue != null);
 
            m_hashes = new Dictionary();
 
            byte[] hashClone = new byte[hashValue.Length]; 
            Array.Copy(hashValue, hashClone, hashClone.Length);
 
            m_hashes[hashType] = hashValue;
        }

        ///  
        ///     Build a Hash evidence that contains the specific SHA-1 hash.  The input hash is not validated,
        ///     and the resulting Hash object cannot calculate additional hash values. 
        ///  
        public static Hash CreateSHA1(byte[] sha1)
        { 
            if (sha1 == null)
                throw new ArgumentNullException("sha1");
            Contract.EndContractBlock();
 
            return new Hash(typeof(SHA1), sha1);
        } 
 
        /// 
        ///     Build a Hash evidence that contains the specific SHA-256 hash.  The input hash is not 
        ///     validated, and the resulting Hash object cannot calculate additional hash values.
        /// 
        public static Hash CreateSHA256(byte[] sha256)
        { 
            if (sha256 == null)
                throw new ArgumentNullException("sha256"); 
            Contract.EndContractBlock(); 

            return new Hash(typeof(SHA256), sha256); 
        }

        /// 
        ///     Build a Hash evidence that contains the specific MD5 hash.  The input hash is not validated, 
        ///     and the resulting Hash object cannot calculate additional hash values.
        ///  
        public static Hash CreateMD5(byte[] md5) 
        {
            if (md5 == null) 
                throw new ArgumentNullException("md5");
            Contract.EndContractBlock();

            return new Hash(typeof(MD5), md5); 
        }
 
        ///  
        ///     Make a copy of this evidence object
        ///  
        public override EvidenceBase Clone()
        {
            return new Hash(this);
        } 

        ///  
        ///     Prepare the hash evidence for serialization 
        /// 
        [OnSerializing] 
        private void OnSerializing(StreamingContext ctx)
        {
            GenerateDefaultHashes();
        } 

        ///  
        ///     Serialize the hash evidence 
        /// 
        [SecurityCritical] 
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            GenerateDefaultHashes();
 
            //
            // Backwards compatibility with Whidbey 
            // 

            byte[] sha1Hash; 
            byte[] md5Hash;

            // Whidbey expects the MD5 and SHA1 hashes stored separately.
            if (m_hashes.TryGetValue(typeof(MD5), out md5Hash)) 
            {
                info.AddValue("Md5", md5Hash); 
            } 
            if (m_hashes.TryGetValue(typeof(SHA1), out sha1Hash))
            { 
                info.AddValue("Sha1", sha1Hash);
            }

            // For perf, don't serialize the assembly binary content. 
            // This has the side-effect that the Whidbey runtime will not be able to compute any
            // hashes besides the provided MD5 and SHA1. 
            info.AddValue("RawData", null); 
            // It doesn't make sense to serialize a memory pointer cross-runtime.
            info.AddValue("PEFile", IntPtr.Zero); 

            //
            // Current implementation
            // 

            // Add all the computed hashes. While this can duplicate the MD5 and SHA1 hashes, 
            // it allows for a clean separation between legacy support and the current implementation. 
            info.AddValue("Hashes", m_hashes);
        } 

        /// 
        ///     Get the SHA-1 hash value of the assembly
        ///  
        public byte[] SHA1
        { 
            get 
            {
                byte[] sha1 = null; 
                if (!m_hashes.TryGetValue(typeof(SHA1), out sha1))
                {
                    sha1 = GenerateHash(GetDefaultHashImplementationOrFallback(typeof(SHA1), typeof(SHA1)));
                } 

                byte[] returnHash = new byte[sha1.Length]; 
                Array.Copy(sha1, returnHash, returnHash.Length); 
                return returnHash;
            } 
        }

        /// 
        ///     Get the SHA-256 hash value of the assembly 
        /// 
        public byte[] SHA256 
        { 
            get
            { 
                byte[] sha256 = null;
                if (!m_hashes.TryGetValue(typeof(SHA256), out sha256))
                {
                    sha256 = GenerateHash(GetDefaultHashImplementationOrFallback(typeof(SHA256), typeof(SHA256))); 
                }
 
                byte[] returnHash = new byte[sha256.Length]; 
                Array.Copy(sha256, returnHash, returnHash.Length);
                return returnHash; 
            }
        }

        ///  
        ///     Get the MD5 hash value of the assembly
        ///  
        public byte[] MD5 
        {
            get 
            {
                byte[] md5 = null;
                if (!m_hashes.TryGetValue(typeof(MD5), out md5))
                { 
                    md5 = GenerateHash(GetDefaultHashImplementationOrFallback(typeof(MD5), typeof(MD5)));
                } 
 
                byte[] returnHash = new byte[md5.Length];
                Array.Copy(md5, returnHash, returnHash.Length); 
                return returnHash;
            }
        }
 
        /// 
        ///     Get the hash value of the assembly when hashed with a specific algorithm.  The actual hash 
        ///     algorithm object is not used, however the same type of object will be used. 
        /// 
        public byte[] GenerateHash(HashAlgorithm hashAlg) 
        {
            if (hashAlg == null)
                throw new ArgumentNullException("hashAlg");
            Contract.EndContractBlock(); 

            byte[] hashValue = GenerateHash(hashAlg.GetType()); 
 
            byte[] returnHash = new byte[hashValue.Length];
            Array.Copy(hashValue, returnHash, returnHash.Length); 
            return returnHash;
        }

        ///  
        ///     Generate the hash value of an assembly when hashed with the specified algorithm. The result
        ///     may be a direct reference to our internal table of hashes, so it should be copied before 
        ///     returning it to user code. 
        /// 
        private byte[] GenerateHash(Type hashType) 
        {
            Contract.Assert(hashType != null && typeof(HashAlgorithm).IsAssignableFrom(hashType), "Expected a hash algorithm");

            Type indexType = GetHashIndexType(hashType); 
            byte[] hashValue = null;
            if (!m_hashes.TryGetValue(indexType, out hashValue)) 
            { 
                // If we're not attached to an assembly, then we cannot generate hashes on demand
                if (m_assembly == null) 
                {
                    throw new InvalidOperationException(Environment.GetResourceString("Security_CannotGenerateHash"));
                }
 
                hashValue = GenerateHash(hashType, GetRawData());
                m_hashes[indexType] = hashValue; 
            } 

            return hashValue; 
        }

        /// 
        ///     Generate a hash of the given type for the assembly data 
        /// 
        private static byte[] GenerateHash(Type hashType, byte[] assemblyBytes) 
        { 
            Contract.Assert(hashType != null && typeof(HashAlgorithm).IsAssignableFrom(hashType), "Expected a hash algorithm");
            Contract.Assert(assemblyBytes != null); 

            using (HashAlgorithm hash = HashAlgorithm.Create(hashType.FullName))
            {
                return hash.ComputeHash(assemblyBytes); 
            }
        } 
 

        ///  
        ///     Build the default set of hash values that will be available for all assemblies
        /// 
        private void GenerateDefaultHashes()
        { 
            // We can't generate any hash values that we don't already have if there isn't an attached
            // assembly to get the hash value of. 
            if (m_assembly != null) 
            {
                GenerateDefaultHashes(GetRawData()); 
            }
        }

        ///  
        ///     Build the default set of hash values that will be available for all assemblies given the raw
        ///     assembly data to hash. 
        ///  
        private void GenerateDefaultHashes(byte[] assemblyBytes)
        { 
            Contract.Assert(assemblyBytes != null);

            Type[] defaultHashTypes = new Type[]
            { 
                GetHashIndexType(typeof(SHA1)),
                GetHashIndexType(typeof(SHA256)), 
                GetHashIndexType(typeof(MD5)) 
            };
 
            foreach (Type defaultHashType in defaultHashTypes)
            {
                Type hashImplementationType = GetDefaultHashImplementation(defaultHashType);
                if (hashImplementationType != null) 
                {
                    if (!m_hashes.ContainsKey(defaultHashType)) 
                    { 
                        m_hashes[defaultHashType] = GenerateHash(hashImplementationType, assemblyBytes);
                    } 
                }
            }
        }
 
        /// 
        ///     Map a hash algorithm to the default implementation of that algorithm, falling back to a given 
        ///     implementation if no suitable default can be found.  This option may be used for situations 
        ///     where it is better to throw an informative exception when trying to use an unsuitable hash
        ///     algorithm than to just return null.  (For instance, throwing a FIPS not supported exception 
        ///     when trying to get the MD5 hash evidence).
        /// 
        private static Type GetDefaultHashImplementationOrFallback(Type hashAlgorithm,
                                                                   Type fallbackImplementation) 
        {
            Contract.Assert(hashAlgorithm != null && typeof(HashAlgorithm).IsAssignableFrom(hashAlgorithm)); 
            Contract.Assert(fallbackImplementation != null && GetHashIndexType(hashAlgorithm).IsAssignableFrom(fallbackImplementation)); 

            Type defaultImplementation = GetDefaultHashImplementation(hashAlgorithm); 
            return defaultImplementation != null ? defaultImplementation : fallbackImplementation;
        }

        ///  
        ///     Map a hash algorithm to the default implementation of that algorithm to use for Hash
        ///     evidence, taking into account things such as FIPS support.  If there is no suitable 
        ///     implementation for the algorithm, GetDefaultHashImplementation returns null. 
        /// 
        private static Type GetDefaultHashImplementation(Type hashAlgorithm) 
        {
            Contract.Assert(hashAlgorithm != null && typeof(HashAlgorithm).IsAssignableFrom(hashAlgorithm));

            if (hashAlgorithm.IsAssignableFrom(typeof(MD5))) 
            {
                // MD5 is not a FIPS compliant algorithm, so if we need to allow only FIPS implementations, 
                // we have no way to create an MD5 hash.  Otherwise, we can just use the standard CAPI 
                // implementation since that is available on all operating systems we support.
                if (!CryptoConfig.AllowOnlyFipsAlgorithms) 
                {
                    return typeof(MD5CryptoServiceProvider);
                }
                else 
                {
                    return null; 
                } 
            }
            else if (hashAlgorithm.IsAssignableFrom(typeof(SHA256))) 
            {
                // The managed SHA256 implementation is not a FIPS certified implementation, however on
                // Windows 2003 and higher we have a FIPS alternative.  If we're on Windows 2003 or better,
                // use the CAPI implementation - otherwise, we fall back to the managed implementation if 
                // FIPS is not enabled.
                Version osVersion = Environment.OSVersion.Version; 
                bool isWin2k3OrHigher = Environment.RunningOnWinNT && 
                                        (osVersion.Major > 5 || (osVersion.Major == 5 && osVersion.Minor >= 2));
 
                if (isWin2k3OrHigher)
                {
                    return Type.GetType("System.Security.Cryptography.SHA256CryptoServiceProvider, " + AssemblyRef.SystemCore);
                } 
                else if (!CryptoConfig.AllowOnlyFipsAlgorithms)
                { 
                    return typeof(SHA256Managed); 
                }
                else 
                {
                    return null;
                }
            } 
            else
            { 
                // Otherwise we don't have a better suggestion for the algorithm, so we can just fallback to 
                // the input algorithm.
                return hashAlgorithm; 
            }
        }

        ///  
        ///     Get the type used to index into the saved hash value dictionary.  We want this to be the
        ///     class which immediately derives from HashAlgorithm so that we can reuse the same hash value 
        ///     if we're asked for (e.g.) SHA256Managed and SHA256CryptoServiceProvider 
        /// 
        private static Type GetHashIndexType(Type hashType) 
        {
            Contract.Assert(hashType != null && typeof(HashAlgorithm).IsAssignableFrom(hashType));

            Type currentType = hashType; 

            // Walk up the inheritence hierarchy looking for the first class that derives from HashAlgorithm 
            while (currentType != null && currentType.BaseType != typeof(HashAlgorithm)) 
            {
                currentType = currentType.BaseType; 
            }

            // If this is the degenerate case where we started out with HashAlgorithm, we won't find it
            // further up our inheritence tree. 
            if (currentType == null)
            { 
                BCLDebug.Assert(hashType == typeof(HashAlgorithm), "hashType == typeof(HashAlgorithm)"); 
                currentType = typeof(HashAlgorithm);
            } 

            return currentType;
        }
 
        /// 
        ///     Raw bytes of the assembly being hashed 
        ///  
        private byte[] GetRawData()
        { 
            byte[] rawData = null;

            // We can only generate hashes on demand if we're associated with an assembly
            if (m_assembly != null) 
            {
                // See if we still hold a reference to the assembly data 
                if (m_rawData != null) 
                {
                    rawData = m_rawData.Target as byte[]; 
                }

                // If not, load the raw bytes up
                if (rawData == null) 
                {
                    rawData = m_assembly.GetRawBytes(); 
                    m_rawData = new WeakReference(rawData); 
                }
            } 

            return rawData;
        }
 
        private SecurityElement ToXml()
        { 
            GenerateDefaultHashes(); 

            SecurityElement root = new SecurityElement("System.Security.Policy.Hash"); 
            // If you hit this assert then most likely you are trying to change the name of this class.
            // This is ok as long as you change the hard coded string above and change the assert below.
            BCLDebug.Assert(this.GetType().FullName.Equals("System.Security.Policy.Hash"), "Class name changed!");
 
            root.AddAttribute("version", "2");
            foreach (KeyValuePair hashValue in m_hashes) 
            { 
                SecurityElement hashElement = new SecurityElement("hash");
                hashElement.AddAttribute("algorithm", hashValue.Key.Name); 
                hashElement.AddAttribute("value", Hex.EncodeHexString(hashValue.Value));

                root.AddChild(hashElement);
            } 

            return root; 
        } 

        public override String ToString() 
        {
            return ToXml().ToString();
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// [....]
// 
 
//
// Hash 
//
// Evidence corresponding to a hash of the assembly bits.
//
 
using System;
using System.Diagnostics.Contracts; 
using System.Collections.Generic; 
using System.Reflection;
using System.Runtime.CompilerServices; 
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning; 
using System.Security.Cryptography;
using System.Security.Util; 
using Microsoft.Win32.SafeHandles; 

namespace System.Security.Policy 
{
    [Serializable]
    [ComVisible(true)]
    public sealed class Hash : EvidenceBase, ISerializable 
    {
        private RuntimeAssembly m_assembly; 
        private Dictionary m_hashes; 
        private WeakReference m_rawData;
 
        /// 
        ///     Deserialize a serialized hash evidence object
        /// 
        [SecurityCritical] 
        internal Hash(SerializationInfo info, StreamingContext context)
        { 
            // 
            // We have three serialization formats that we might be deserializing, the Whidbey format which
            // contains hash values directly, the Whidbey format which contains a pointer to a PEImage, and 
            // the v4 format which contains a dictionary of calculated hashes.
            //
            // If we have the Whidbey version that has built in hash values, we can convert that, but we
            // cannot do anything with the PEImage format since that is a serialized pointer into another 
            // runtime's VM.
            // 
 
            Dictionary hashes = info.GetValueNoThrow("Hashes", typeof(Dictionary)) as Dictionary;
            if (hashes != null) 
            {
                m_hashes = hashes;
            }
            else 
            {
                // If there is no hash value dictionary, then check to see if we have the Whidbey multiple 
                // hashes version of the evidence. 
                m_hashes = new Dictionary();
 
                byte[] md5 = info.GetValueNoThrow("Md5", typeof(byte[])) as byte[];
                if (md5 != null)
                {
                    m_hashes[typeof(MD5)] = md5; 
                }
 
                byte[] sha1 = info.GetValueNoThrow("Sha1", typeof(byte[])) as byte[]; 
                if (sha1 != null)
                { 
                    m_hashes[typeof(SHA1)] = sha1;
                }

                byte[] rawData = info.GetValueNoThrow("RawData", typeof(byte[])) as byte[]; 
                if (rawData != null)
                { 
                    GenerateDefaultHashes(rawData); 
                }
            } 
        }

        /// 
        ///     Create hash evidence for the specified assembly 
        /// 
        public Hash(Assembly assembly) 
        { 
            if (assembly == null)
                throw new ArgumentNullException("assembly"); 
            Contract.EndContractBlock();
            if (assembly.IsDynamic)
                throw new ArgumentException(Environment.GetResourceString("Security_CannotGenerateHash"), "assembly");
 
            m_hashes = new Dictionary();
            m_assembly = assembly as RuntimeAssembly; 
 
            if (m_assembly == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"), "assembly"); 
        }

        /// 
        ///     Create a copy of some hash evidence 
        /// 
        private Hash(Hash hash) 
        { 
            Contract.Assert(hash != null);
 
            m_assembly = hash.m_assembly;
            m_rawData = hash.m_rawData;
            m_hashes = new Dictionary(hash.m_hashes);
        } 

        ///  
        ///     Create a hash evidence prepopulated with a specific hash value 
        /// 
        private Hash(Type hashType, byte[] hashValue) 
        {
            Contract.Assert(hashType != null);
            Contract.Assert(hashValue != null);
 
            m_hashes = new Dictionary();
 
            byte[] hashClone = new byte[hashValue.Length]; 
            Array.Copy(hashValue, hashClone, hashClone.Length);
 
            m_hashes[hashType] = hashValue;
        }

        ///  
        ///     Build a Hash evidence that contains the specific SHA-1 hash.  The input hash is not validated,
        ///     and the resulting Hash object cannot calculate additional hash values. 
        ///  
        public static Hash CreateSHA1(byte[] sha1)
        { 
            if (sha1 == null)
                throw new ArgumentNullException("sha1");
            Contract.EndContractBlock();
 
            return new Hash(typeof(SHA1), sha1);
        } 
 
        /// 
        ///     Build a Hash evidence that contains the specific SHA-256 hash.  The input hash is not 
        ///     validated, and the resulting Hash object cannot calculate additional hash values.
        /// 
        public static Hash CreateSHA256(byte[] sha256)
        { 
            if (sha256 == null)
                throw new ArgumentNullException("sha256"); 
            Contract.EndContractBlock(); 

            return new Hash(typeof(SHA256), sha256); 
        }

        /// 
        ///     Build a Hash evidence that contains the specific MD5 hash.  The input hash is not validated, 
        ///     and the resulting Hash object cannot calculate additional hash values.
        ///  
        public static Hash CreateMD5(byte[] md5) 
        {
            if (md5 == null) 
                throw new ArgumentNullException("md5");
            Contract.EndContractBlock();

            return new Hash(typeof(MD5), md5); 
        }
 
        ///  
        ///     Make a copy of this evidence object
        ///  
        public override EvidenceBase Clone()
        {
            return new Hash(this);
        } 

        ///  
        ///     Prepare the hash evidence for serialization 
        /// 
        [OnSerializing] 
        private void OnSerializing(StreamingContext ctx)
        {
            GenerateDefaultHashes();
        } 

        ///  
        ///     Serialize the hash evidence 
        /// 
        [SecurityCritical] 
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            GenerateDefaultHashes();
 
            //
            // Backwards compatibility with Whidbey 
            // 

            byte[] sha1Hash; 
            byte[] md5Hash;

            // Whidbey expects the MD5 and SHA1 hashes stored separately.
            if (m_hashes.TryGetValue(typeof(MD5), out md5Hash)) 
            {
                info.AddValue("Md5", md5Hash); 
            } 
            if (m_hashes.TryGetValue(typeof(SHA1), out sha1Hash))
            { 
                info.AddValue("Sha1", sha1Hash);
            }

            // For perf, don't serialize the assembly binary content. 
            // This has the side-effect that the Whidbey runtime will not be able to compute any
            // hashes besides the provided MD5 and SHA1. 
            info.AddValue("RawData", null); 
            // It doesn't make sense to serialize a memory pointer cross-runtime.
            info.AddValue("PEFile", IntPtr.Zero); 

            //
            // Current implementation
            // 

            // Add all the computed hashes. While this can duplicate the MD5 and SHA1 hashes, 
            // it allows for a clean separation between legacy support and the current implementation. 
            info.AddValue("Hashes", m_hashes);
        } 

        /// 
        ///     Get the SHA-1 hash value of the assembly
        ///  
        public byte[] SHA1
        { 
            get 
            {
                byte[] sha1 = null; 
                if (!m_hashes.TryGetValue(typeof(SHA1), out sha1))
                {
                    sha1 = GenerateHash(GetDefaultHashImplementationOrFallback(typeof(SHA1), typeof(SHA1)));
                } 

                byte[] returnHash = new byte[sha1.Length]; 
                Array.Copy(sha1, returnHash, returnHash.Length); 
                return returnHash;
            } 
        }

        /// 
        ///     Get the SHA-256 hash value of the assembly 
        /// 
        public byte[] SHA256 
        { 
            get
            { 
                byte[] sha256 = null;
                if (!m_hashes.TryGetValue(typeof(SHA256), out sha256))
                {
                    sha256 = GenerateHash(GetDefaultHashImplementationOrFallback(typeof(SHA256), typeof(SHA256))); 
                }
 
                byte[] returnHash = new byte[sha256.Length]; 
                Array.Copy(sha256, returnHash, returnHash.Length);
                return returnHash; 
            }
        }

        ///  
        ///     Get the MD5 hash value of the assembly
        ///  
        public byte[] MD5 
        {
            get 
            {
                byte[] md5 = null;
                if (!m_hashes.TryGetValue(typeof(MD5), out md5))
                { 
                    md5 = GenerateHash(GetDefaultHashImplementationOrFallback(typeof(MD5), typeof(MD5)));
                } 
 
                byte[] returnHash = new byte[md5.Length];
                Array.Copy(md5, returnHash, returnHash.Length); 
                return returnHash;
            }
        }
 
        /// 
        ///     Get the hash value of the assembly when hashed with a specific algorithm.  The actual hash 
        ///     algorithm object is not used, however the same type of object will be used. 
        /// 
        public byte[] GenerateHash(HashAlgorithm hashAlg) 
        {
            if (hashAlg == null)
                throw new ArgumentNullException("hashAlg");
            Contract.EndContractBlock(); 

            byte[] hashValue = GenerateHash(hashAlg.GetType()); 
 
            byte[] returnHash = new byte[hashValue.Length];
            Array.Copy(hashValue, returnHash, returnHash.Length); 
            return returnHash;
        }

        ///  
        ///     Generate the hash value of an assembly when hashed with the specified algorithm. The result
        ///     may be a direct reference to our internal table of hashes, so it should be copied before 
        ///     returning it to user code. 
        /// 
        private byte[] GenerateHash(Type hashType) 
        {
            Contract.Assert(hashType != null && typeof(HashAlgorithm).IsAssignableFrom(hashType), "Expected a hash algorithm");

            Type indexType = GetHashIndexType(hashType); 
            byte[] hashValue = null;
            if (!m_hashes.TryGetValue(indexType, out hashValue)) 
            { 
                // If we're not attached to an assembly, then we cannot generate hashes on demand
                if (m_assembly == null) 
                {
                    throw new InvalidOperationException(Environment.GetResourceString("Security_CannotGenerateHash"));
                }
 
                hashValue = GenerateHash(hashType, GetRawData());
                m_hashes[indexType] = hashValue; 
            } 

            return hashValue; 
        }

        /// 
        ///     Generate a hash of the given type for the assembly data 
        /// 
        private static byte[] GenerateHash(Type hashType, byte[] assemblyBytes) 
        { 
            Contract.Assert(hashType != null && typeof(HashAlgorithm).IsAssignableFrom(hashType), "Expected a hash algorithm");
            Contract.Assert(assemblyBytes != null); 

            using (HashAlgorithm hash = HashAlgorithm.Create(hashType.FullName))
            {
                return hash.ComputeHash(assemblyBytes); 
            }
        } 
 

        ///  
        ///     Build the default set of hash values that will be available for all assemblies
        /// 
        private void GenerateDefaultHashes()
        { 
            // We can't generate any hash values that we don't already have if there isn't an attached
            // assembly to get the hash value of. 
            if (m_assembly != null) 
            {
                GenerateDefaultHashes(GetRawData()); 
            }
        }

        ///  
        ///     Build the default set of hash values that will be available for all assemblies given the raw
        ///     assembly data to hash. 
        ///  
        private void GenerateDefaultHashes(byte[] assemblyBytes)
        { 
            Contract.Assert(assemblyBytes != null);

            Type[] defaultHashTypes = new Type[]
            { 
                GetHashIndexType(typeof(SHA1)),
                GetHashIndexType(typeof(SHA256)), 
                GetHashIndexType(typeof(MD5)) 
            };
 
            foreach (Type defaultHashType in defaultHashTypes)
            {
                Type hashImplementationType = GetDefaultHashImplementation(defaultHashType);
                if (hashImplementationType != null) 
                {
                    if (!m_hashes.ContainsKey(defaultHashType)) 
                    { 
                        m_hashes[defaultHashType] = GenerateHash(hashImplementationType, assemblyBytes);
                    } 
                }
            }
        }
 
        /// 
        ///     Map a hash algorithm to the default implementation of that algorithm, falling back to a given 
        ///     implementation if no suitable default can be found.  This option may be used for situations 
        ///     where it is better to throw an informative exception when trying to use an unsuitable hash
        ///     algorithm than to just return null.  (For instance, throwing a FIPS not supported exception 
        ///     when trying to get the MD5 hash evidence).
        /// 
        private static Type GetDefaultHashImplementationOrFallback(Type hashAlgorithm,
                                                                   Type fallbackImplementation) 
        {
            Contract.Assert(hashAlgorithm != null && typeof(HashAlgorithm).IsAssignableFrom(hashAlgorithm)); 
            Contract.Assert(fallbackImplementation != null && GetHashIndexType(hashAlgorithm).IsAssignableFrom(fallbackImplementation)); 

            Type defaultImplementation = GetDefaultHashImplementation(hashAlgorithm); 
            return defaultImplementation != null ? defaultImplementation : fallbackImplementation;
        }

        ///  
        ///     Map a hash algorithm to the default implementation of that algorithm to use for Hash
        ///     evidence, taking into account things such as FIPS support.  If there is no suitable 
        ///     implementation for the algorithm, GetDefaultHashImplementation returns null. 
        /// 
        private static Type GetDefaultHashImplementation(Type hashAlgorithm) 
        {
            Contract.Assert(hashAlgorithm != null && typeof(HashAlgorithm).IsAssignableFrom(hashAlgorithm));

            if (hashAlgorithm.IsAssignableFrom(typeof(MD5))) 
            {
                // MD5 is not a FIPS compliant algorithm, so if we need to allow only FIPS implementations, 
                // we have no way to create an MD5 hash.  Otherwise, we can just use the standard CAPI 
                // implementation since that is available on all operating systems we support.
                if (!CryptoConfig.AllowOnlyFipsAlgorithms) 
                {
                    return typeof(MD5CryptoServiceProvider);
                }
                else 
                {
                    return null; 
                } 
            }
            else if (hashAlgorithm.IsAssignableFrom(typeof(SHA256))) 
            {
                // The managed SHA256 implementation is not a FIPS certified implementation, however on
                // Windows 2003 and higher we have a FIPS alternative.  If we're on Windows 2003 or better,
                // use the CAPI implementation - otherwise, we fall back to the managed implementation if 
                // FIPS is not enabled.
                Version osVersion = Environment.OSVersion.Version; 
                bool isWin2k3OrHigher = Environment.RunningOnWinNT && 
                                        (osVersion.Major > 5 || (osVersion.Major == 5 && osVersion.Minor >= 2));
 
                if (isWin2k3OrHigher)
                {
                    return Type.GetType("System.Security.Cryptography.SHA256CryptoServiceProvider, " + AssemblyRef.SystemCore);
                } 
                else if (!CryptoConfig.AllowOnlyFipsAlgorithms)
                { 
                    return typeof(SHA256Managed); 
                }
                else 
                {
                    return null;
                }
            } 
            else
            { 
                // Otherwise we don't have a better suggestion for the algorithm, so we can just fallback to 
                // the input algorithm.
                return hashAlgorithm; 
            }
        }

        ///  
        ///     Get the type used to index into the saved hash value dictionary.  We want this to be the
        ///     class which immediately derives from HashAlgorithm so that we can reuse the same hash value 
        ///     if we're asked for (e.g.) SHA256Managed and SHA256CryptoServiceProvider 
        /// 
        private static Type GetHashIndexType(Type hashType) 
        {
            Contract.Assert(hashType != null && typeof(HashAlgorithm).IsAssignableFrom(hashType));

            Type currentType = hashType; 

            // Walk up the inheritence hierarchy looking for the first class that derives from HashAlgorithm 
            while (currentType != null && currentType.BaseType != typeof(HashAlgorithm)) 
            {
                currentType = currentType.BaseType; 
            }

            // If this is the degenerate case where we started out with HashAlgorithm, we won't find it
            // further up our inheritence tree. 
            if (currentType == null)
            { 
                BCLDebug.Assert(hashType == typeof(HashAlgorithm), "hashType == typeof(HashAlgorithm)"); 
                currentType = typeof(HashAlgorithm);
            } 

            return currentType;
        }
 
        /// 
        ///     Raw bytes of the assembly being hashed 
        ///  
        private byte[] GetRawData()
        { 
            byte[] rawData = null;

            // We can only generate hashes on demand if we're associated with an assembly
            if (m_assembly != null) 
            {
                // See if we still hold a reference to the assembly data 
                if (m_rawData != null) 
                {
                    rawData = m_rawData.Target as byte[]; 
                }

                // If not, load the raw bytes up
                if (rawData == null) 
                {
                    rawData = m_assembly.GetRawBytes(); 
                    m_rawData = new WeakReference(rawData); 
                }
            } 

            return rawData;
        }
 
        private SecurityElement ToXml()
        { 
            GenerateDefaultHashes(); 

            SecurityElement root = new SecurityElement("System.Security.Policy.Hash"); 
            // If you hit this assert then most likely you are trying to change the name of this class.
            // This is ok as long as you change the hard coded string above and change the assert below.
            BCLDebug.Assert(this.GetType().FullName.Equals("System.Security.Policy.Hash"), "Class name changed!");
 
            root.AddAttribute("version", "2");
            foreach (KeyValuePair hashValue in m_hashes) 
            { 
                SecurityElement hashElement = new SecurityElement("hash");
                hashElement.AddAttribute("algorithm", hashValue.Key.Name); 
                hashElement.AddAttribute("value", Hex.EncodeHexString(hashValue.Value));

                root.AddChild(hashElement);
            } 

            return root; 
        } 

        public override String ToString() 
        {
            return ToXml().ToString();
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK