1 //
2 // System.Collections.SecureHashCodeProvider.cs
3 //
4 // Authors:
5 //   Sergey Chaban (serge@wildwestsoftware.com)
6 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //   Sebastien Pouliot  <sebastien@ximian.com>
8 //
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 // Copyright 2012 Xamarin, Inc (http://xamarin.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.Globalization;
34 
35 namespace System.Web.Util
36 {
37 	class SecureHashCodeProvider : IHashCodeProvider
38 	{
39 		static readonly SecureHashCodeProvider singletonInvariant = new SecureHashCodeProvider (CultureInfo.InvariantCulture);
40 		static SecureHashCodeProvider singleton;
41 		static readonly object sync = new object ();
42 		static readonly int seed;
43 
44 		TextInfo m_text; // must match MS name for serialization
45 
46 		public static SecureHashCodeProvider Default {
47 			get {
48 				lock (sync) {
49 					if (singleton == null) {
50 						singleton = new SecureHashCodeProvider ();
51 					} else if (singleton.m_text == null) {
52 						if (!AreEqual (CultureInfo.CurrentCulture, CultureInfo.InvariantCulture))
53 							singleton = new SecureHashCodeProvider ();
54 					} else if (!AreEqual (singleton.m_text, CultureInfo.CurrentCulture)) {
55 						singleton = new SecureHashCodeProvider ();
56 					}
57 					return singleton;
58 				}
59 			}
60 		}
61 
62 		public static SecureHashCodeProvider DefaultInvariant {
63 			get { return singletonInvariant; }
64 		}
65 
SecureHashCodeProvider()66 		static SecureHashCodeProvider ()
67 		{
68 			// It should be enough to fend off the attack described in
69 			// https://bugzilla.novell.com/show_bug.cgi?id=739119
70 			// In order to predict value of the seed, the attacker would have to know the exact time when
71 			// the server process started and since it's a remote attack, this is next to impossible.
72 			// Using milliseconds instead of ticks here would make it easier for the attackers since there
73 			// would only be as many as 1000 possible values
74 			seed = (int)DateTime.UtcNow.Ticks;
75 		}
76 
77 		// Public instance constructor
SecureHashCodeProvider()78 		public SecureHashCodeProvider ()
79 		{
80 			CultureInfo culture = CultureInfo.CurrentCulture;
81 			if (!AreEqual (culture, CultureInfo.InvariantCulture))
82 				m_text = CultureInfo.CurrentCulture.TextInfo;
83 		}
84 
SecureHashCodeProvider(CultureInfo culture)85 		public SecureHashCodeProvider (CultureInfo culture)
86 		{
87 			if (culture == null)
88  				throw new ArgumentNullException ("culture");
89 			if (!AreEqual (culture, CultureInfo.InvariantCulture))
90 				m_text = culture.TextInfo;
91 		}
92 
AreEqual(CultureInfo a, CultureInfo b)93 		static bool AreEqual (CultureInfo a, CultureInfo b)
94 		{
95 			return a.LCID == b.LCID;
96 		}
97 
AreEqual(TextInfo info, CultureInfo culture)98 		static bool AreEqual (TextInfo info, CultureInfo culture)
99 		{
100 			return info.LCID == culture.LCID;
101 		}
102 
GetHashCode(object obj)103 		public int GetHashCode (object obj)
104 		{
105 			if (obj == null)
106 				throw new ArgumentNullException ("obj");
107 
108 			string str = obj as string;
109 
110 			if (str == null)
111 				return obj.GetHashCode ();
112 
113 			int h = seed;
114 			char c;
115 
116 			if ((m_text != null) && !AreEqual (m_text, CultureInfo.InvariantCulture)) {
117 				str = m_text.ToLower (str);
118 				for (int i = 0; i < str.Length; i++) {
119 					c = str [i];
120 					h = h * 31 + c;
121 				}
122 			} else {
123 				for (int i = 0; i < str.Length; i++) {
124 					c = Char.ToLower (str [i], CultureInfo.InvariantCulture);
125 					h = h * 31 + c;
126 				}
127 			}
128 			return h;
129 		}
130 	}
131 }
132