Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Security / CryptoHelper.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.ServiceModel.Security
6 {
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.ServiceModel.Channels;
10     using System.ServiceModel;
11     using System.Reflection;
12     using System.Threading;
13     using System.IO;
14
15     using System.Runtime.InteropServices;
16     using System.IdentityModel.Tokens;
17     using System.Text;
18     using System.Xml;
19     using System.Diagnostics;
20     using System.Diagnostics.CodeAnalysis;
21     using System.Security.Cryptography;
22
23     using Psha1DerivedKeyGenerator = System.IdentityModel.Psha1DerivedKeyGenerator;
24     using CryptoAlgorithms = System.IdentityModel.CryptoHelper;
25
26     static class CryptoHelper
27     {
28         static byte[] emptyBuffer;
29         static readonly RandomNumberGenerator random = new RNGCryptoServiceProvider();
30
31         enum CryptoAlgorithmType
32         {
33             Unknown,
34             Symmetric,
35             Asymmetric
36         }
37
38         internal static byte[] EmptyBuffer
39         {
40             get
41             {
42                 if (emptyBuffer == null)
43                 {
44                     byte[] tmp = new byte[0];
45                     emptyBuffer = tmp;
46                 }
47                 return emptyBuffer;
48             }
49         }
50
51         internal static HashAlgorithm NewSha1HashAlgorithm()
52         {
53             return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha1Digest);
54         }
55
56         internal static HashAlgorithm NewSha256HashAlgorithm()
57         {
58             return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha256Digest);
59         }
60
61         [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. Required as SOAP spec requires supporting SHA1.")]
62         internal static HashAlgorithm CreateHashAlgorithm(string digestMethod)
63         {
64             object algorithmObject = CryptoAlgorithms.GetAlgorithmFromConfig(digestMethod);
65             if (algorithmObject != null)
66             {
67                 HashAlgorithm hashAlgorithm = algorithmObject as HashAlgorithm;
68                 if (hashAlgorithm != null)
69                     return hashAlgorithm;
70                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidHashAlgorithm, digestMethod)));
71             }
72
73             switch (digestMethod)
74             {
75                 case SecurityAlgorithms.Sha1Digest:
76                     if (SecurityUtilsEx.RequiresFipsCompliance)
77                         return new SHA1CryptoServiceProvider();
78                     else
79                         return new SHA1Managed();
80                 case SecurityAlgorithms.Sha256Digest:
81                     if (SecurityUtilsEx.RequiresFipsCompliance)
82                         return new SHA256CryptoServiceProvider();
83                     else
84                         return new SHA256Managed();
85                 default:
86                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.UnsupportedCryptoAlgorithm, digestMethod)));
87
88             }
89         }
90
91         [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. Required as SOAP spec requires supporting SHA1.")]
92         internal static HashAlgorithm CreateHashForAsymmetricSignature(string signatureMethod)
93         {
94             object algorithmObject = CryptoAlgorithms.GetAlgorithmFromConfig(signatureMethod);
95             if (algorithmObject != null)
96             {
97                 HashAlgorithm hashAlgorithm;
98                 SignatureDescription signatureDescription = algorithmObject as SignatureDescription;
99
100                 if (signatureDescription != null)
101                 {
102                     hashAlgorithm = signatureDescription.CreateDigest();
103                     if (hashAlgorithm != null)
104                         return hashAlgorithm;
105                 }
106
107                 hashAlgorithm = algorithmObject as HashAlgorithm;
108                 if (hashAlgorithm != null)
109                     return hashAlgorithm;
110
111                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidAsymmetricSignature, signatureMethod)));
112             }
113
114             switch (signatureMethod)
115             {
116                 case SecurityAlgorithms.RsaSha1Signature:
117                 case SecurityAlgorithms.DsaSha1Signature:
118                     if (SecurityUtilsEx.RequiresFipsCompliance)
119                         return new SHA1CryptoServiceProvider();
120                     else
121                         return new SHA1Managed();
122
123                 case SecurityAlgorithms.RsaSha256Signature:
124                     if (SecurityUtilsEx.RequiresFipsCompliance)
125                         return new SHA256CryptoServiceProvider();
126                     else
127                         return new SHA256Managed();
128
129                 default:
130                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.UnsupportedCryptoAlgorithm, signatureMethod)));
131             }
132         }
133
134         internal static byte[] ExtractIVAndDecrypt(SymmetricAlgorithm algorithm, byte[] cipherText, int offset, int count)
135         {
136             if (cipherText == null)
137             {
138                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("cipherText");
139             }
140             if (count < 0 || count > cipherText.Length)
141             {
142                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeInRange, 0, cipherText.Length)));
143
144             }
145             if (offset < 0 || offset > cipherText.Length - count)
146             {
147                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeInRange, 0, cipherText.Length - count)));
148             }
149
150             int ivSize = algorithm.BlockSize / 8;
151             byte[] iv = new byte[ivSize];
152             Buffer.BlockCopy(cipherText, offset, iv, 0, iv.Length);
153             algorithm.Padding = PaddingMode.ISO10126;
154             algorithm.Mode = CipherMode.CBC;
155             try
156             {
157                 using (ICryptoTransform decrTransform = algorithm.CreateDecryptor(algorithm.Key, iv))
158                 {
159                     return decrTransform.TransformFinalBlock(cipherText, offset + iv.Length, count - iv.Length);
160                 }
161             }
162             catch (CryptographicException ex)
163             {
164                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.DecryptionFailed), ex));
165             }
166         }
167
168         internal static void FillRandomBytes(byte[] buffer)
169         {
170             random.GetBytes(buffer);
171         }
172
173         static CryptoAlgorithmType GetAlgorithmType(string algorithm)
174         {
175             object algorithmObject = null;
176
177             try
178             {
179                 algorithmObject = CryptoAlgorithms.GetAlgorithmFromConfig(algorithm);
180             }
181             catch (InvalidOperationException)
182             {
183                 algorithmObject = null;
184                 // We ---- the exception and continue.
185             }
186             if (algorithmObject != null)
187             {
188                 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
189                 KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
190                 if (symmetricAlgorithm != null || keyedHashAlgorithm != null)
191                     return CryptoAlgorithmType.Symmetric;
192
193                 // NOTE: A KeyedHashAlgorithm is symmetric in nature.
194
195                 AsymmetricAlgorithm asymmetricAlgorithm = algorithmObject as AsymmetricAlgorithm;
196                 SignatureDescription signatureDescription = algorithmObject as SignatureDescription;
197                 if (asymmetricAlgorithm != null || signatureDescription != null)
198                     return CryptoAlgorithmType.Asymmetric;
199
200                 return CryptoAlgorithmType.Unknown;
201             }
202
203             switch (algorithm)
204             {
205                 case SecurityAlgorithms.DsaSha1Signature:
206                 case SecurityAlgorithms.RsaSha1Signature:
207                 case SecurityAlgorithms.RsaSha256Signature:
208                 case SecurityAlgorithms.RsaOaepKeyWrap:
209                 case SecurityAlgorithms.RsaV15KeyWrap:
210                     return CryptoAlgorithmType.Asymmetric;
211                 case SecurityAlgorithms.HmacSha1Signature:
212                 case SecurityAlgorithms.HmacSha256Signature:
213                 case SecurityAlgorithms.Aes128Encryption:
214                 case SecurityAlgorithms.Aes192Encryption:
215                 case SecurityAlgorithms.Aes256Encryption:
216                 case SecurityAlgorithms.TripleDesEncryption:
217                 case SecurityAlgorithms.Aes128KeyWrap:
218                 case SecurityAlgorithms.Aes192KeyWrap:
219                 case SecurityAlgorithms.Aes256KeyWrap:
220                 case SecurityAlgorithms.TripleDesKeyWrap:
221                 case SecurityAlgorithms.Psha1KeyDerivation:
222                 case SecurityAlgorithms.Psha1KeyDerivationDec2005:
223                     return CryptoAlgorithmType.Symmetric;
224                 default:
225                     return CryptoAlgorithmType.Unknown;
226             }
227         }
228
229         internal static byte[] GenerateIVAndEncrypt(SymmetricAlgorithm algorithm, byte[] plainText, int offset, int count)
230         {
231             byte[] iv;
232             byte[] cipherText;
233             GenerateIVAndEncrypt(algorithm, new ArraySegment<byte>(plainText, offset, count), out iv, out cipherText);
234             byte[] output = DiagnosticUtility.Utility.AllocateByteArray(checked(iv.Length + cipherText.Length));
235             Buffer.BlockCopy(iv, 0, output, 0, iv.Length);
236             Buffer.BlockCopy(cipherText, 0, output, iv.Length, cipherText.Length);
237             return output;
238         }
239
240         internal static void GenerateIVAndEncrypt(SymmetricAlgorithm algorithm, ArraySegment<byte> plainText, out byte[] iv, out byte[] cipherText)
241         {
242             int ivSize = algorithm.BlockSize / 8;
243             iv = new byte[ivSize];
244             FillRandomBytes(iv);
245             algorithm.Padding = PaddingMode.PKCS7;
246             algorithm.Mode = CipherMode.CBC;
247             using (ICryptoTransform encrTransform = algorithm.CreateEncryptor(algorithm.Key, iv))
248             {
249                 cipherText = encrTransform.TransformFinalBlock(plainText.Array, plainText.Offset, plainText.Count);
250             }
251         }
252
253         internal static bool IsEqual(byte[] a, byte[] b)
254         {
255             if (a == null || b == null || a.Length != b.Length)
256             {
257                 return false;
258             }
259
260             for (int i = 0; i < a.Length; i++)
261             {
262                 if (a[i] != b[i])
263                 {
264                     return false;
265                 }
266             }
267             return true;
268         }
269
270         internal static bool IsSymmetricAlgorithm(string algorithm)
271         {
272             return GetAlgorithmType(algorithm) == CryptoAlgorithmType.Symmetric;
273         }
274
275         internal static bool IsSymmetricSupportedAlgorithm(string algorithm, int keySize)
276         {
277             bool found = false;
278             object algorithmObject = null;
279
280             try
281             {
282                 algorithmObject = CryptoAlgorithms.GetAlgorithmFromConfig(algorithm);
283             }
284             catch (InvalidOperationException)
285             {
286                 algorithmObject = null;
287                 // We ---- the exception and continue.
288             }
289             if (algorithmObject != null)
290             {
291                 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
292                 KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
293                 if (symmetricAlgorithm != null || keyedHashAlgorithm != null)
294                     found = true;
295             }
296
297             switch (algorithm)
298             {
299                 case SecurityAlgorithms.DsaSha1Signature:
300                 case SecurityAlgorithms.RsaSha1Signature:
301                 case SecurityAlgorithms.RsaSha256Signature:
302                 case SecurityAlgorithms.RsaOaepKeyWrap:
303                 case SecurityAlgorithms.RsaV15KeyWrap:
304                     return false;
305                 case SecurityAlgorithms.HmacSha1Signature:
306                 case SecurityAlgorithms.HmacSha256Signature:
307                 case SecurityAlgorithms.Psha1KeyDerivation:
308                 case SecurityAlgorithms.Psha1KeyDerivationDec2005:
309                     return true;
310                 case SecurityAlgorithms.Aes128Encryption:
311                 case SecurityAlgorithms.Aes128KeyWrap:
312                     return keySize == 128;
313                 case SecurityAlgorithms.Aes192Encryption:
314                 case SecurityAlgorithms.Aes192KeyWrap:
315                     return keySize == 192;
316                 case SecurityAlgorithms.Aes256Encryption:
317                 case SecurityAlgorithms.Aes256KeyWrap:
318                     return keySize == 256;
319                 case SecurityAlgorithms.TripleDesEncryption:
320                 case SecurityAlgorithms.TripleDesKeyWrap:
321                     return keySize == 128 || keySize == 192;
322                 default:
323                     if (found)
324                         return true;
325                     return false;
326             }
327         }
328
329         internal static void ValidateBufferBounds(Array buffer, int offset, int count)
330         {
331             if (buffer == null)
332             {
333                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer"));
334             }
335             if (count < 0 || count > buffer.Length)
336             {
337                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeInRange, 0, buffer.Length)));
338             }
339             if (offset < 0 || offset > buffer.Length - count)
340             {
341                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeInRange, 0, buffer.Length - count)));
342             }
343         }
344
345         internal static void ValidateSymmetricKeyLength(int keyLength, SecurityAlgorithmSuite algorithmSuite)
346         {
347             if (!algorithmSuite.IsSymmetricKeyLengthSupported(keyLength))
348             {
349                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ArgumentOutOfRangeException("algorithmSuite",
350                    SR.GetString(SR.UnsupportedKeyLength, keyLength, algorithmSuite.ToString())));
351             }
352             if (keyLength % 8 != 0)
353             {
354                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ArgumentOutOfRangeException("algorithmSuite",
355                    SR.GetString(SR.KeyLengthMustBeMultipleOfEight, keyLength)));
356             }
357         }
358
359     }
360 }