File: System\Xml\Core\SecureStringHasher.cs
Project: ndp\fx\src\Xml\System.Xml.csproj (System.Xml)
//------------------------------------------------------------------------------
// <copyright file="SecureStringHasher.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Security;
#if !SILVERLIGHT
using System.Security.Permissions;
#endif
 
namespace System.Xml {
 
    // SecureStringHasher is a hash code provider for strings. The hash codes calculation starts with a seed (hasCodeRandomizer) which is usually
    // different for each instance of SecureStringHasher. Since the hash code depend on the seed, the chance of hashtable DoS attack in case when 
    // someone passes in lots of strings that hash to the same hash code is greatly reduced.
    // The SecureStringHasher implements IEqualityComparer for strings and therefore can be used in generic IDictionary.
    internal class SecureStringHasher : IEqualityComparer<String> {
        [SecurityCritical]
        delegate int HashCodeOfStringDelegate(string s, int sLen, long additionalEntropy);
        
        // Value is guaranteed to be null by the spec.
        // No explicit assignment because it will require adding SecurityCritical on .cctor
        // which could hurt the performance
        [SecurityCritical]
        static HashCodeOfStringDelegate hashCodeDelegate;
 
        int hashCodeRandomizer;
 
        public SecureStringHasher() {
            this.hashCodeRandomizer = Environment.TickCount;
        }
 
#if false // This is here only for debugging of hashing issues
        public SecureStringHasher( int hashCodeRandomizer ) {
            this.hashCodeRandomizer = hashCodeRandomizer;
        }
#endif
 
        public bool Equals( String x, String y ) {
            return String.Equals( x, y, StringComparison.Ordinal );
        }
 
        [SecuritySafeCritical]
        public int GetHashCode( String key ) {
            if (hashCodeDelegate == null) {
                hashCodeDelegate = GetHashCodeDelegate();
            }
            return hashCodeDelegate(key, key.Length, hashCodeRandomizer);
        }
        
        [SecurityCritical]
        private static int GetHashCodeOfString( string key, int sLen, long additionalEntropy ) {
            int hashCode = unchecked((int)additionalEntropy);
            // use key.Length to eliminate the rangecheck
            for ( int i = 0; i < key.Length; i++ ) {
                hashCode += ( hashCode << 7 ) ^ key[i];
            }
            // mix it a bit more
            hashCode -= hashCode >> 17; 
            hashCode -= hashCode >> 11; 
            hashCode -= hashCode >> 5;
            return hashCode;
        }
        
        [SecuritySafeCritical]
#if !SILVERLIGHT
        [ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
#endif
        private static HashCodeOfStringDelegate GetHashCodeDelegate() {
            // If we find the Marvin hash method, we use that
            // Otherwise, we use the old string hashing function.
 
            MethodInfo getHashCodeMethodInfo = typeof(String).GetMethod("InternalMarvin32HashString", BindingFlags.NonPublic | BindingFlags.Static);
            if (getHashCodeMethodInfo != null) {
                return (HashCodeOfStringDelegate)Delegate.CreateDelegate(typeof(HashCodeOfStringDelegate), getHashCodeMethodInfo);
            }
     
            // This will fall through and return a delegate to the old hash function
            Debug.Assert(false, "Randomized hashing is not supported.");
 
            return new HashCodeOfStringDelegate(GetHashCodeOfString);
        }
    }
}