690034bc65c06827f91625316bbfa83f58eba7f6
[mono.git] / mcs / class / referencesource / mscorlib / system / security / policy / hashmembershipcondition.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 // HashMembershipCondition.cs
11 //
12 // Implementation of membership condition for hashes of assemblies.
13 //
14
15 namespace System.Security.Policy {
16     using System.Collections;
17     using System.Runtime.Serialization;
18     using System.Security;
19     using System.Security.Cryptography;
20     using System.Security.Util;
21     using System.Security.Permissions;
22     using System.Threading;
23     using System.Globalization;
24     using System.Diagnostics.Contracts;
25
26     [Serializable]
27     [System.Runtime.InteropServices.ComVisible(true)]
28     public sealed class HashMembershipCondition : ISerializable, IDeserializationCallback, IMembershipCondition, IReportMatchMembershipCondition {
29         private byte[] m_value = null;
30         private HashAlgorithm m_hashAlg = null;
31         private SecurityElement m_element = null;
32
33         private object s_InternalSyncObject = null;
34         private object InternalSyncObject {
35             get {
36                 if (s_InternalSyncObject == null) {
37                     Object o = new Object();
38                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
39                 }
40                 return s_InternalSyncObject;
41             }
42         }
43
44         internal HashMembershipCondition() {}
45         private HashMembershipCondition (SerializationInfo info, StreamingContext context) {
46             m_value = (byte[]) info.GetValue("HashValue", typeof(byte[]));
47             string hashAlgorithm = (string) info.GetValue("HashAlgorithm", typeof(string));
48             if (hashAlgorithm != null)
49                 m_hashAlg = HashAlgorithm.Create(hashAlgorithm);
50             else
51                 m_hashAlg = new SHA1Managed();
52         }
53
54         public HashMembershipCondition(HashAlgorithm hashAlg, byte[] value) {
55             if (value == null)
56                 throw new ArgumentNullException("value");
57             if (hashAlg == null)
58                 throw new ArgumentNullException("hashAlg");
59             Contract.EndContractBlock();
60
61             m_value = new byte[value.Length];
62             Array.Copy(value, m_value, value.Length);
63             m_hashAlg = hashAlg;
64         }
65
66         /// <internalonly/>
67         [System.Security.SecurityCritical]
68         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
69         {
70             info.AddValue("HashValue", this.HashValue);
71             info.AddValue("HashAlgorithm", this.HashAlgorithm.ToString());
72         }
73
74         /// <internalonly/>
75         void IDeserializationCallback.OnDeserialization (Object sender) {}
76
77         public HashAlgorithm HashAlgorithm {
78             set {
79                 if (value == null)
80                     throw new ArgumentNullException("HashAlgorithm");
81                 Contract.EndContractBlock();
82                 m_hashAlg = value;
83             }
84             get {
85                 if (m_hashAlg == null && m_element != null)
86                     ParseHashAlgorithm();
87                 return m_hashAlg;
88             }
89         }
90
91         public byte[] HashValue {
92             set {
93                 if (value == null)
94                     throw new ArgumentNullException("value");
95                 Contract.EndContractBlock();
96                 m_value = new byte[value.Length];
97                 Array.Copy(value, m_value, value.Length);
98             }
99             get {
100                 if (m_value == null && m_element != null)
101                     ParseHashValue();
102                 if (m_value == null)
103                     return null;
104
105                 byte[] value = new byte[m_value.Length];
106                 Array.Copy(m_value, value, m_value.Length);
107                 return value;
108             }
109         }
110
111         public bool Check(Evidence evidence) {
112             object usedEvidence = null;
113             return (this as IReportMatchMembershipCondition).Check(evidence, out usedEvidence);
114         }
115
116         bool IReportMatchMembershipCondition.Check(Evidence evidence, out object usedEvidence) {
117             usedEvidence = null;
118
119             if (evidence == null)
120                 return false;
121
122             Hash hash = evidence.GetHostEvidence<Hash>();
123             if (hash != null) {
124                 if (m_value == null && m_element != null)
125                     ParseHashValue();
126
127                 if (m_hashAlg == null && m_element != null)
128                     ParseHashAlgorithm();
129
130                 byte[] asmHash = null;
131                 lock (InternalSyncObject) {
132                     asmHash = hash.GenerateHash(m_hashAlg);
133                 }
134
135                 if (asmHash != null && CompareArrays(asmHash, m_value)) {
136                     usedEvidence = hash;
137                     return true;
138                 }
139             }
140
141             return false;
142         }
143
144         public IMembershipCondition Copy() {
145             if (m_value == null && m_element != null)
146                 ParseHashValue();
147
148             if (m_hashAlg == null && m_element != null)
149                 ParseHashAlgorithm();
150
151             return new HashMembershipCondition(m_hashAlg, m_value);
152         }
153
154         public SecurityElement ToXml() {
155             return ToXml(null);
156         }
157
158         public void FromXml(SecurityElement e) {
159             FromXml(e, null);
160         }
161
162         public SecurityElement ToXml(PolicyLevel level) {
163             if (m_value == null && m_element != null)
164                 ParseHashValue();
165
166             if (m_hashAlg == null && m_element != null)
167                 ParseHashAlgorithm();
168
169             SecurityElement root = new SecurityElement("IMembershipCondition");
170             XMLUtil.AddClassAttribute(root, this.GetType(), "System.Security.Policy.HashMembershipCondition");
171             // If you hit this assert then most likely you are trying to change the name of this class. 
172             // This is ok as long as you change the hard coded string above and change the assert below.
173             Contract.Assert(this.GetType().FullName.Equals("System.Security.Policy.HashMembershipCondition"), "Class name changed!");
174
175             root.AddAttribute("version", "1");
176             if (m_value != null)
177                 root.AddAttribute(s_tagHashValue, Hex.EncodeHexString(HashValue));
178             if (m_hashAlg != null)
179                 root.AddAttribute(s_tagHashAlgorithm, HashAlgorithm.GetType().FullName);
180             return root;
181         }
182
183         public void FromXml(SecurityElement e, PolicyLevel level) {
184             if (e == null)
185                 throw new ArgumentNullException("e");
186
187             if (!e.Tag.Equals("IMembershipCondition"))
188                 throw new ArgumentException(Environment.GetResourceString("Argument_MembershipConditionElement"));
189             Contract.EndContractBlock();
190
191             lock (InternalSyncObject) {
192                 m_element = e;
193                 m_value = null;
194                 m_hashAlg = null;
195             }
196         }
197
198         public override bool Equals(Object o) {
199             HashMembershipCondition that = (o as HashMembershipCondition);
200             if (that != null) {
201                 if (this.m_hashAlg == null && this.m_element != null)
202                     this.ParseHashAlgorithm();
203                 if (that.m_hashAlg == null && that.m_element != null)
204                     that.ParseHashAlgorithm();
205
206                 if (this.m_hashAlg != null && that.m_hashAlg != null &&
207                     this.m_hashAlg.GetType() == that.m_hashAlg.GetType()) {
208                     if (this.m_value == null && this.m_element != null)
209                         this.ParseHashValue();
210                     if (that.m_value == null && that.m_element != null)
211                         that.ParseHashValue();
212
213                     if (this.m_value.Length != that.m_value.Length)
214                         return false;
215
216                     for (int i = 0; i < m_value.Length; i++) {
217                         if (this.m_value[i] != that.m_value[i])
218                             return false;
219                     }
220                     return true;
221                 }
222             }
223             return false;
224         }
225
226         public override int GetHashCode() {
227             if (this.m_hashAlg == null && this.m_element != null)
228                 this.ParseHashAlgorithm();
229
230             int accumulator = this.m_hashAlg != null ? this.m_hashAlg.GetType().GetHashCode() : 0;
231             if (this.m_value == null && this.m_element != null)
232                 this.ParseHashValue();
233
234             accumulator = accumulator ^ GetByteArrayHashCode(this.m_value);
235             return accumulator;
236         }
237
238         public override string ToString() {
239             if (m_hashAlg == null)
240                 ParseHashAlgorithm();
241
242             return Environment.GetResourceString("Hash_ToString", m_hashAlg.GetType().AssemblyQualifiedName, Hex.EncodeHexString(HashValue));
243         }
244
245         private const string s_tagHashValue = "HashValue";
246         private const string s_tagHashAlgorithm = "HashAlgorithm";
247
248         private void ParseHashValue() {
249             lock (InternalSyncObject) {
250                 if (m_element == null)
251                     return;
252
253                 string elHash = m_element.Attribute(s_tagHashValue);
254                 if (elHash != null)
255                     m_value = Hex.DecodeHexString(elHash);
256                 else
257                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidXMLElement", s_tagHashValue, this.GetType().FullName));
258
259                 if (m_value != null && m_hashAlg != null) {
260                     m_element = null;
261                 }
262             }
263         }
264
265         private void ParseHashAlgorithm() {
266             lock (InternalSyncObject) {
267                 if (m_element == null)
268                     return;
269
270                 string elHashAlg = m_element.Attribute(s_tagHashAlgorithm);
271                 if (elHashAlg != null)
272                     m_hashAlg = HashAlgorithm.Create(elHashAlg);
273                 else
274                     m_hashAlg = new SHA1Managed();
275
276                 if (m_value != null && m_hashAlg != null)
277                     m_element = null;
278             }
279         }
280
281         private static bool CompareArrays(byte[] first, byte[] second) {
282             if (first.Length != second.Length)
283                 return false;
284
285             int count = first.Length;
286             for (int i = 0; i < count; ++i) {
287                 if (first[i] != second[i])
288                     return false;
289             }
290
291             return true;
292         }
293
294         private static int GetByteArrayHashCode(byte[] baData) {
295             if (baData == null)
296                 return 0;
297
298             int accumulator = 0;
299             for (int i = 0; i < baData.Length; ++i) {
300                 accumulator = (accumulator << 8) ^ (int)baData[i] ^ (accumulator >> 24);
301             }
302             return accumulator;
303         }
304     }
305 }