[coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099
[mono.git] / mcs / class / referencesource / System.Core / System / Security / Cryptography / CapiNative.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6
7 using System;
8 #if FEATURE_CORESYSTEM
9 using System.Core;
10 #endif
11 using System.Diagnostics;
12 using System.Diagnostics.CodeAnalysis;
13 using System.Diagnostics.Contracts;
14 using System.Globalization;
15 using System.Runtime.CompilerServices;
16 using System.Runtime.ConstrainedExecution;
17 using System.Runtime.InteropServices;
18 using System.Runtime.Versioning;
19 using System.Security;
20 using System.Text;
21 using Microsoft.Win32.SafeHandles;
22
23 namespace System.Security.Cryptography {
24     /// <summary>
25     ///     Native interop with CAPI. Native definitions can be found in wincrypt.h or msaxlapi.h
26     /// </summary>
27     internal static class CapiNative {
28         internal enum AlgorithmClass {
29             DataEncryption = (3 << 13),         // ALG_CLASS_DATA_ENCRYPT
30             Hash = (4 << 13)                    // ALG_CLASS_HASH
31         }
32
33         internal enum AlgorithmType {
34             Any = (0 << 9),                     // ALG_TYPE_ANY
35             Block = (3 << 9)                    // ALG_TYPE_BLOCK
36         }
37
38         internal enum AlgorithmSubId {
39             MD5 = 3,                            // ALG_SID_MD5
40             Sha1 = 4,                           // ALG_SID_SHA1
41             Sha256 = 12,                        // ALG_SID_SHA_256
42             Sha384 = 13,                        // ALG_SID_SHA_384
43             Sha512 = 14,                        // ALG_SID_SHA_512
44
45             Aes128 = 14,                        // ALG_SID_AES_128
46             Aes192 = 15,                        // ALG_SID_AES_192
47             Aes256 = 16                         // ALG_SID_AES_256
48         }
49
50         internal enum AlgorithmId {
51             None = 0,
52
53             Aes128 = (AlgorithmClass.DataEncryption | AlgorithmType.Block | AlgorithmSubId.Aes128),     // CALG_AES_128
54             Aes192 = (AlgorithmClass.DataEncryption | AlgorithmType.Block | AlgorithmSubId.Aes192),     // CALG_AES_192
55             Aes256 = (AlgorithmClass.DataEncryption | AlgorithmType.Block | AlgorithmSubId.Aes256),     // CALG_AES_256
56
57             MD5 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.MD5),                       // CALG_MD5
58             Sha1 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha1),                     // CALG_SHA1
59             Sha256 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha256),                 // CALG_SHA_256
60             Sha384 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha384),                 // CALG_SHA_384
61             Sha512 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha512)                  // CALG_SHA_512
62         }
63
64         /// <summary>
65         ///     Flags for the CryptAcquireContext API
66         /// </summary>
67         [Flags]
68         internal enum CryptAcquireContextFlags {
69             None = 0x00000000,
70             VerifyContext = unchecked((int)0xF0000000)      // CRYPT_VERIFYCONTEXT
71         }
72
73         /// <summary>
74         ///     Error codes returned from CAPI
75         /// </summary>
76         internal enum ErrorCode {
77             Success = 0x00000000,                                       // ERROR_SUCCESS
78             MoreData = 0x00000ea,                                       // ERROR_MORE_DATA
79             NoMoreItems = 0x00000103,                                   // ERROR_NO_MORE_ITEMS
80             BadData = unchecked((int)0x80090005),                       // NTE_BAD_DATA
81             BadAlgorithmId = unchecked((int)0x80090008),                // NTE_BAD_ALGID
82             ProviderTypeNotDefined = unchecked((int)0x80090017),        // NTE_PROV_TYPE_NOT_DEF
83             KeysetNotDefined = unchecked((int)0x80090019)               // NTE_KEYSET_NOT_DEF
84         }
85
86         /// <summary>
87         ///     Parameters that GetHashParam can query
88         /// </summary>
89         internal enum HashParameter {
90             None = 0x0000,
91             AlgorithmId = 0x0001,           // HP_ALGID
92             HashValue = 0x0002,             // HP_HASHVAL
93             HashSize = 0x0004               // HP_HASHSIZE
94         }
95
96         /// <summary>
97         ///     Formats of blobs that keys can appear in
98         /// </summary>
99         internal enum KeyBlobType : byte {
100             PlainText = 0x8                 // PLAINTEXTKEYBLOB
101         }
102
103         /// <summary>
104         ///     Flags for CryptGenKey and CryptImportKey
105         /// </summary>
106         [Flags]
107         internal enum KeyFlags {
108             None = 0x0000,
109             Exportable = 0x0001             // CRYPT_EXPORTABLE
110         }
111
112         /// <summary>
113         ///     Parameters of a cryptographic key used by SetKeyParameter 
114         /// </summary>
115         internal enum KeyParameter {
116             None = 0,
117             IV = 1,                         // KP_IV
118             Mode = 4,                       // KP_MODE
119             ModeBits = 5                    // KP_MODE_BITS
120         }
121
122         /// <summary>
123         ///     Well-known names of crypto service providers
124         /// </summary>
125         internal static class ProviderNames {
126             // MS_ENH_RSA_AES_PROV
127             public const string MicrosoftEnhancedRsaAes = "Microsoft Enhanced RSA and AES Cryptographic Provider";
128             public const string MicrosoftEnhancedRsaAesPrototype = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)";
129         }
130
131         /// <summary>
132         ///     Parameters exposed by a CSP
133         /// </summary>
134         internal enum ProviderParameter {
135             None = 0,
136             EnumerateAlgorithms = 1             // PP_ENUMALGS
137         }
138
139         /// <summary>
140         ///     Flags controlling information retrieved about a provider parameter
141         /// </summary>
142         [Flags]
143         internal enum ProviderParameterFlags {
144             None = 0x00000000,
145             RestartEnumeration = 0x00000001     // CRYPT_FIRST
146         }
147
148         /// <summary>
149         ///     Provider type accessed in a crypto service provider. These provide the set of algorithms
150         ///     available to use for an application.
151         /// </summary>
152         internal enum ProviderType {
153             None = 0,
154             RsaAes = 24         // PROV_RSA_AES
155         }
156
157         [StructLayout(LayoutKind.Sequential)]
158         internal struct BLOBHEADER {
159             public KeyBlobType bType;
160             public byte bVersion;
161             public short reserved;
162             public AlgorithmId aiKeyAlg;
163         }
164
165         [StructLayout(LayoutKind.Sequential)]
166         internal struct CRYPTOAPI_BLOB {
167             public int cbData;
168             public IntPtr pbData;
169         }
170
171         [StructLayout(LayoutKind.Sequential)]
172         internal unsafe struct PROV_ENUMALGS {
173             public AlgorithmId aiAlgId;
174             public int dwBitLen;
175             public int dwNameLen;
176             public fixed byte szName[20];
177         }
178
179         internal const uint ALG_CLASS_SIGNATURE = (1 << 13);
180         internal const uint ALG_TYPE_RSA = (2 << 9);
181         internal const uint ALG_SID_RSA_ANY = 0;
182         internal const uint ALG_SID_DSS_ANY = 0;
183         internal const uint ALG_TYPE_DSS = (1 << 9);
184         internal const uint ALG_CLASS_KEY_EXCHANGE = (5 << 13);
185
186         internal const uint CALG_RSA_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY);
187         internal const uint CALG_DSS_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY);
188         internal const uint CALG_RSA_KEYX = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | ALG_SID_RSA_ANY);
189         internal const uint CNG_RSA_PUBLIC_KEY_BLOB = 72;
190
191         internal const uint X509_ASN_ENCODING = 0x00000001;
192         internal const uint PKCS_7_ASN_ENCODING = 0x00010000;
193
194         internal const uint CRYPT_OID_INFO_OID_KEY = 1;
195
196         internal const uint LMEM_FIXED = 0x0000;
197         internal const uint LMEM_ZEROINIT = 0x0040;
198
199         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
200         internal struct CRYPT_OID_INFO {
201             internal CRYPT_OID_INFO(int size) {
202                 cbSize = (uint)size;
203                 pszOID = null;
204                 pwszName = null;
205                 dwGroupId = 0;
206                 Algid = 0;
207                 ExtraInfo = new CRYPTOAPI_BLOB();
208             }
209             internal uint cbSize;
210             [MarshalAs(UnmanagedType.LPStr)]
211             internal string pszOID;
212             internal string pwszName;
213             internal uint dwGroupId;
214             internal uint Algid;
215             internal CRYPTOAPI_BLOB ExtraInfo;
216         }
217
218
219 #if FEATURE_CORESYSTEM
220         [SecurityCritical]
221 #else
222 #pragma warning disable 618    // Have not migrated to v4 transparency yet
223         [SecurityCritical(SecurityCriticalScope.Everything)]
224 #pragma warning restore 618
225 #endif
226         [SuppressUnmanagedCodeSecurity]
227         internal static class UnsafeNativeMethods {
228             /// <summary>
229             ///     Calculate the public key token for a given public key
230             /// </summary>
231             [DllImport("clr")]
232 #if FEATURE_CORESYSTEM
233             [SecurityCritical]
234 #endif
235             public static extern int _AxlPublicKeyBlobToPublicKeyToken(ref CRYPTOAPI_BLOB pCspPublicKeyBlob,
236                                                                        [Out] out SafeAxlBufferHandle ppwszPublicKeyToken);
237
238             /// <summary>
239             ///     Open a crypto service provider, if a key container is specified KeyContainerPermission
240             ///     should be demanded.
241             /// </summary>
242             [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
243             [return: MarshalAs(UnmanagedType.Bool)]
244 #if FEATURE_CORESYSTEM
245             [SecurityCritical]
246 #endif
247             public static extern bool CryptAcquireContext([Out] out SafeCspHandle phProv,
248                                                           string pszContainer,
249                                                           string pszProvider,
250                                                           ProviderType dwProvType,
251                                                           CryptAcquireContextFlags dwFlags);
252
253             /// <summary>
254             ///     Prepare a new hash algorithm for use
255             /// </summary>
256             [DllImport("advapi32", SetLastError = true)]
257             [return: MarshalAs(UnmanagedType.Bool)]
258 #if FEATURE_CORESYSTEM
259             [SecurityCritical]
260 #endif
261             public static extern bool CryptCreateHash(SafeCspHandle hProv,
262                                                       AlgorithmId Algid,
263                                                       SafeCapiKeyHandle hKey,
264                                                       int dwFlags,
265                                                       [Out] out SafeCapiHashHandle phHash);
266
267             /// <summary>
268             ///     Decrypt a block of data
269             /// </summary>
270             [DllImport("advapi32", SetLastError = true)]
271             [return: MarshalAs(UnmanagedType.Bool)]
272 #if FEATURE_CORESYSTEM
273             [SecurityCritical]
274 #endif
275             public static extern bool CryptDecrypt(SafeCapiKeyHandle hKey,
276                                                    SafeCapiHashHandle hHash,
277                                                    [MarshalAs(UnmanagedType.Bool)] bool Final,
278                                                    int dwFlags,
279                                                    IntPtr pbData, // BYTE *
280                                                    [In, Out] ref int pdwDataLen);
281
282             /// <summary>
283             ///     Duplicate a key handle
284             /// </summary>
285             [DllImport("advapi32")]
286 #if !FEATURE_CORESYSTEM
287             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
288 #endif
289             [SuppressUnmanagedCodeSecurity]
290             [return: MarshalAs(UnmanagedType.Bool)]
291 #if FEATURE_CORESYSTEM
292             [SecurityCritical]
293 #endif
294             public static extern bool CryptDuplicateKey(SafeCapiKeyHandle hKey,
295                                                         IntPtr pdwReserved,
296                                                         int dwFlags,
297                                                         [Out] out SafeCapiKeyHandle phKey);
298
299             /// <summary>
300             ///     Encrypt a block of data
301             /// </summary>
302             [DllImport("advapi32", SetLastError = true)]
303             [return: MarshalAs(UnmanagedType.Bool)]
304 #if FEATURE_CORESYSTEM
305             [SecurityCritical]
306 #endif
307             public static extern bool CryptEncrypt(SafeCapiKeyHandle hKey,
308                                                    SafeCapiHashHandle hHash,
309                                                    [MarshalAs(UnmanagedType.Bool)] bool Final,
310                                                    int dwFlags,
311                                                    IntPtr pbData, // BYTE *
312                                                    [In, Out] ref int pdwDataLen,
313                                                    int dwBufLen);
314                                                    
315             /// <summary>
316             ///     Export a key into a byte array
317             /// </summary>
318             [DllImport("advapi32", SetLastError = true)]
319             [return: MarshalAs(UnmanagedType.Bool)]
320 #if FEATURE_CORESYSTEM
321             [SecurityCritical]
322 #endif
323             public static extern bool CryptExportKey(SafeCapiKeyHandle hKey,
324                                                      SafeCapiKeyHandle hExpKey,
325                                                      int dwBlobType,            // (int)KeyBlobType
326                                                      int dwExportFlags,
327                                                      [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
328                                                      [In, Out] ref int pdwDataLen);
329             /// <summary>
330             ///     Generate a random key
331             /// </summary>
332             [DllImport("advapi32", SetLastError = true)]
333             [return: MarshalAs(UnmanagedType.Bool)]
334 #if FEATURE_CORESYSTEM
335             [SecurityCritical]
336 #endif
337             public static extern bool CryptGenKey(SafeCspHandle hProv,
338                                                   AlgorithmId Algid,
339                                                   KeyFlags dwFlags,
340                                                   [Out] out SafeCapiKeyHandle phKey);
341
342             /// <summary>
343             ///     Fill a buffer with cryptographically random bytes
344             /// </summary>
345             [DllImport("advapi32", SetLastError = true)]
346             [return: MarshalAs(UnmanagedType.Bool)]
347 #if FEATURE_CORESYSTEM
348             [SecurityCritical]
349 #endif
350             public static extern bool CryptGenRandom(SafeCspHandle hProv,
351                                                      int dwLen,
352                                                      [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbBuffer);
353
354             /// <summary>
355             ///     Get information about a hash algorithm, including the current value
356             /// </summary>
357             [DllImport("advapi32", SetLastError = true)]
358             [return: MarshalAs(UnmanagedType.Bool)]
359 #if FEATURE_CORESYSTEM
360             [SecurityCritical]
361 #endif
362             public static extern bool CryptGetHashParam(SafeCapiHashHandle hHash,
363                                                         HashParameter dwParam,
364                                                         [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
365                                                         [In, Out] ref int pdwDataLen,
366                                                         int dwFlags);
367
368             /// <summary>
369             ///     Get information about a CSP
370             /// </summary>
371             [DllImport("advapi32", SetLastError = true)]
372             [return: MarshalAs(UnmanagedType.Bool)]
373 #if FEATURE_CORESYSTEM
374             [SecurityCritical]
375 #endif
376             public static extern bool CryptGetProvParam(SafeCspHandle hProv,
377                                                         ProviderParameter dwParam,
378                                                         IntPtr pbData,
379                                                         [In, Out] ref int pdwDataLen,
380                                                         ProviderParameterFlags dwFlags);
381
382             /// <summary>
383             ///     Add a block of data to a hash
384             /// </summary>
385             [DllImport("advapi32", SetLastError = true)]
386             [return: MarshalAs(UnmanagedType.Bool)]
387 #if FEATURE_CORESYSTEM
388             [SecurityCritical]
389 #endif
390             public static extern bool CryptHashData(SafeCapiHashHandle hHash,
391                                                     [MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
392                                                     int dwDataLen,
393                                                     int dwFlags);
394
395             /// <summary>
396             ///     Import a key into a CSP
397             /// </summary>
398             [DllImport("advapi32", SetLastError = true)]
399             [return: MarshalAs(UnmanagedType.Bool)]
400 #if FEATURE_CORESYSTEM
401             [SecurityCritical]
402 #endif
403             public static extern bool CryptImportKey(SafeCspHandle hProv,
404                                                      [MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
405                                                      int dwDataLen,
406                                                      SafeCapiKeyHandle hPubKey,
407                                                      KeyFlags dwFlags,
408                                                      [Out] out SafeCapiKeyHandle phKey);
409
410             /// <summary>
411             ///     Set a property of a key
412             /// </summary>
413             [DllImport("advapi32", SetLastError = true)]
414             [return: MarshalAs(UnmanagedType.Bool)]
415 #if FEATURE_CORESYSTEM
416             [SecurityCritical]
417 #endif
418             public static extern bool CryptSetKeyParam(SafeCapiKeyHandle hKey,
419                                                        KeyParameter dwParam,
420                                                        [MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
421                                                        int dwFlags);
422
423             //Added for X509Certificate extension support 
424 #if FEATURE_CORESYSTEM
425         [SecurityCritical]
426 #endif
427             [DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
428             [ResourceExposure(ResourceScope.None)]
429             internal extern static
430             IntPtr CryptFindOIDInfo(
431                 [In]     uint dwKeyType,
432                 [In]     IntPtr pvKey,
433                 [In]     OidGroup dwGroupId);
434
435 #if FEATURE_CORESYSTEM
436         [SecurityCritical]
437 #endif
438             [DllImport("CRYPT32.dll", CharSet = CharSet.Auto, SetLastError = true)]
439             [ResourceExposure(ResourceScope.None)]
440             internal extern static
441             IntPtr CryptFindOIDInfo(
442                 [In]     uint dwKeyType,
443                 [In]     SafeLocalAllocHandle pvKey,
444                 [In]     OidGroup dwGroupId);
445
446
447 #if FEATURE_CORESYSTEM
448         [SecurityCritical]
449 #endif
450             [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
451             [ResourceExposure(ResourceScope.None)]
452             internal static extern
453             bool CryptDecodeObject(
454                 [In]     uint dwCertEncodingType,
455                 [In]     IntPtr lpszStructType,
456                 [In]     IntPtr pbEncoded,
457                 [In]     uint cbEncoded,
458                 [In]     uint dwFlags,
459                 [In, Out] SafeLocalAllocHandle pvStructInfo,
460                 [In, Out] IntPtr pcbStructInfo);
461
462 #if FEATURE_CORESYSTEM
463         [SecurityCritical]
464 #endif
465             [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
466             [ResourceExposure(ResourceScope.None)]
467             internal static extern
468             bool CryptDecodeObject(
469                 [In]     uint dwCertEncodingType,
470                 [In]     IntPtr lpszStructType,
471                 [In]     byte[] pbEncoded,
472                 [In]     uint cbEncoded,
473                 [In]     uint dwFlags,
474                 [In, Out] SafeLocalAllocHandle pvStructInfo,
475                 [In, Out] IntPtr pcbStructInfo);
476 #if FEATURE_CORESYSTEM
477         [SecurityCritical]
478 #endif
479             [DllImport("KERNEL32.dll", CharSet = CharSet.Auto, SetLastError = true)]
480             [ResourceExposure(ResourceScope.None)]
481             internal static extern
482             SafeLocalAllocHandle LocalAlloc(
483                 [In] uint uFlags,
484                 [In] IntPtr sizetdwBytes);
485         }
486
487         //
488         // Utility and wrapper functions
489         //
490
491         /// <summary>
492         ///     Acquire a crypto service provider
493         /// </summary>
494         [System.Security.SecurityCritical]
495         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
496         internal static SafeCspHandle AcquireCsp(string keyContainer,
497                                                  string providerName,
498                                                  ProviderType providerType,
499                                                  CryptAcquireContextFlags flags,
500                                                  bool throwPlatformException) {
501             Contract.Ensures(Contract.Result<SafeCspHandle>() != null &&
502                              !Contract.Result<SafeCspHandle>().IsInvalid &&
503                              !Contract.Result<SafeCspHandle>().IsClosed);
504
505             SafeCspHandle cspHandle = null;
506             if (!UnsafeNativeMethods.CryptAcquireContext(out cspHandle,
507                                                          keyContainer,
508                                                          providerName,
509                                                          providerType,
510                                                          flags)) {
511                 // If the platform doesn't have the specified CSP, we'll either get a ProviderTypeNotDefined
512                 // or a KeysetNotDefined error depending on the CAPI version.
513                 int error = Marshal.GetLastWin32Error();
514                 if (throwPlatformException && (error == (int)CapiNative.ErrorCode.ProviderTypeNotDefined ||
515                                                error == (int)CapiNative.ErrorCode.KeysetNotDefined)) {
516                     throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
517                 }
518                 else {
519                     throw new CryptographicException(error);
520                 }
521             }
522
523             return cspHandle;
524         }
525
526         /// <summary>
527         ///     Export a symmetric algorithm key into a byte array
528         /// </summary>
529         [System.Security.SecurityCritical]
530         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
531         internal static byte[] ExportSymmetricKey(SafeCapiKeyHandle key) {
532             Contract.Requires(key != null);
533             Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length > 0);
534
535             //
536             // Figure out how big the key blob is, and export it
537             //
538
539             int keySize = 0;
540             if (!UnsafeNativeMethods.CryptExportKey(key,
541                                                     SafeCapiKeyHandle.InvalidHandle,
542                                                     (int)KeyBlobType.PlainText,
543                                                     0,
544                                                     null,
545                                                     ref keySize)) {
546                 int error = Marshal.GetLastWin32Error();
547
548                 if (error != (int)ErrorCode.MoreData) {
549                     throw new CryptographicException(error);
550                 }
551             }
552
553             byte[] keyBlob = new byte[keySize];
554             if (!UnsafeNativeMethods.CryptExportKey(key,
555                                                     SafeCapiKeyHandle.InvalidHandle,
556                                                     (int)KeyBlobType.PlainText,
557                                                     0,
558                                                     keyBlob,
559                                                     ref keySize)) {
560                 throw new CryptographicException(Marshal.GetLastWin32Error());
561             }
562
563             //
564             // Strip the headers from the key to access the raw data
565             //
566             // A PLAINTEXTBLOB is laid out as follows:
567             //   BLOBHEADER hdr
568             //   DWORD      cbKeySize
569             //   BYTE       rbgKeyData[]
570             //
571
572             int keyDataOffset = Marshal.SizeOf(typeof(BLOBHEADER)) + Marshal.SizeOf(typeof(int));
573             Debug.Assert(keyBlob.Length > keyDataOffset, "Key blob is in an unexpected format.");
574
575             int keyLength = BitConverter.ToInt32(keyBlob, Marshal.SizeOf(typeof(BLOBHEADER)));
576             Debug.Assert(keyLength > 0, "Unexpected key length.");
577             Debug.Assert(keyBlob.Length >= keyDataOffset + keyLength, "Key blob is in an unexpected format.");
578
579             byte[] keyData = new byte[keyLength];
580             Buffer.BlockCopy(keyBlob, keyDataOffset, keyData, 0, keyData.Length);
581             return keyData;
582         }
583
584         /// <summary>
585         ///     Map an algorithm ID to a string name
586         /// </summary>
587         internal static string GetAlgorithmName(AlgorithmId algorithm) {
588             Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
589
590             return algorithm.ToString().ToUpper(CultureInfo.InvariantCulture);
591         }
592
593         /// <summary>
594         ///     Get the value of a specific hash parameter
595         /// </summary>
596         [System.Security.SecurityCritical]
597         internal static byte[] GetHashParameter(SafeCapiHashHandle hashHandle, CapiNative.HashParameter parameter) {
598             Contract.Requires(hashHandle != null);
599             Contract.Requires(CapiNative.HashParameter.AlgorithmId <= parameter && parameter <= CapiNative.HashParameter.HashSize);
600             Contract.Ensures(Contract.Result<byte[]>() != null);
601
602             //
603             // Determine the maximum size of the parameter and retrieve it
604             //
605
606             int parameterSize = 0;
607             if (!CapiNative.UnsafeNativeMethods.CryptGetHashParam(hashHandle, parameter, null, ref parameterSize, 0)) {
608                 throw new CryptographicException(Marshal.GetLastWin32Error());
609             }
610
611             Debug.Assert(0 < parameterSize, "Invalid parameter size returned");
612             byte[] parameterValue = new byte[parameterSize];
613             if (!CapiNative.UnsafeNativeMethods.CryptGetHashParam(hashHandle, parameter, parameterValue, ref parameterSize, 0)) {
614                 throw new CryptographicException(Marshal.GetLastWin32Error());
615             }
616
617             // CAPI may have asked for a larger buffer than it used, so only copy the used bytes
618             if (parameterSize != parameterValue.Length) {
619                 byte[] realValue = new byte[parameterSize];
620                 Buffer.BlockCopy(parameterValue, 0, realValue, 0, parameterSize);
621                 parameterValue = realValue;
622             }
623
624             return parameterValue;
625         }
626
627         /// <summary>
628         ///     Get information about a CSP. This should only be used for calls where the returned information
629         ///     is in the form of a structure.
630         /// </summary>
631         [System.Security.SecurityCritical]
632         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
633         internal static T GetProviderParameterStruct<T>(SafeCspHandle provider,
634                                                         ProviderParameter parameter,
635                                                         ProviderParameterFlags flags) where T : struct {
636             Contract.Requires(provider != null);
637             Contract.Requires(parameter == ProviderParameter.EnumerateAlgorithms);
638
639             // Figure out how big the parameter is
640             int bufferSize = 0;
641             IntPtr buffer = IntPtr.Zero;
642
643             if (!UnsafeNativeMethods.CryptGetProvParam(provider, parameter, buffer, ref bufferSize, flags)) {
644                 int errorCode = Marshal.GetLastWin32Error();
645
646                 // NoMoreItems means that we've finished the enumeration we're currently working on, this is
647                 // not a real error, so return an empty structure to mark the end.
648                 if (errorCode == (int)ErrorCode.NoMoreItems) {
649                     return new T();
650                 }
651                 else if (errorCode != (int)ErrorCode.MoreData) {
652                     throw new CryptographicException(errorCode);
653                 }
654             }
655
656             Debug.Assert(Marshal.SizeOf(typeof(T)) <= bufferSize, "Buffer size does not match structure size");
657
658             //
659             // Pull the parameter back and marshal it into the return structure
660             //
661
662             RuntimeHelpers.PrepareConstrainedRegions();
663             try {
664                 // Allocate in a CER because we could fail between the alloc and the assignment
665                 RuntimeHelpers.PrepareConstrainedRegions();
666                 try { }
667                 finally {
668                     buffer = Marshal.AllocCoTaskMem(bufferSize);
669                 }
670
671                 if (!UnsafeNativeMethods.CryptGetProvParam(provider, parameter, buffer, ref bufferSize, flags)) {
672                     throw new CryptographicException(Marshal.GetLastWin32Error());
673                 }
674
675                 return (T)Marshal.PtrToStructure(buffer, typeof(T));
676             }
677             finally {
678                 if (buffer != IntPtr.Zero) {
679                     Marshal.FreeCoTaskMem(buffer);
680                 }
681             }
682         }
683
684         /// <summary>
685         ///     Map a verification result to a matching HRESULT
686         /// </summary>
687         internal static int HResultForVerificationResult(SignatureVerificationResult verificationResult) {
688             switch (verificationResult) {
689                 case SignatureVerificationResult.AssemblyIdentityMismatch:
690                 case SignatureVerificationResult.PublicKeyTokenMismatch:
691                 case SignatureVerificationResult.PublisherMismatch:
692                     return (int)SignatureVerificationResult.BadSignatureFormat;
693
694                 case SignatureVerificationResult.ContainingSignatureInvalid:
695                     return (int)SignatureVerificationResult.BadDigest;
696
697                 default:
698                     return (int)verificationResult;
699             }
700         }
701
702         /// <summary>
703         ///     Import a symmetric key into a CSP
704         /// </summary>
705         [System.Security.SecurityCritical]
706         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
707         internal static SafeCapiKeyHandle ImportSymmetricKey(SafeCspHandle provider, AlgorithmId algorithm, byte[] key) {
708             Contract.Requires(provider != null);
709             Contract.Requires(((int)algorithm & (int)AlgorithmClass.DataEncryption) == (int)AlgorithmClass.DataEncryption);
710             Contract.Requires(key != null);
711             Contract.Ensures(Contract.Result<SafeCapiKeyHandle>() != null &&
712                              !Contract.Result<SafeCapiKeyHandle>().IsInvalid &&
713                              !Contract.Result<SafeCapiKeyHandle>().IsClosed);
714
715             //
716             // Setup a PLAINTEXTKEYBLOB (v2) which has the following format:
717             //   BLOBHEADER hdr
718             //   DWORD      cbKeySize
719             //   BYTE       rbgKeyData[]
720             //
721
722             int blobSize = Marshal.SizeOf(typeof(BLOBHEADER)) + Marshal.SizeOf(typeof(int)) + key.Length;
723             byte[] keyBlob = new byte[blobSize];
724
725             unsafe {
726                 fixed (byte *pBlob = keyBlob) {
727                     BLOBHEADER* pHeader = (BLOBHEADER*)pBlob;
728                     pHeader->bType = KeyBlobType.PlainText;
729                     pHeader->bVersion = 2;
730                     pHeader->reserved = 0;
731                     pHeader->aiKeyAlg = algorithm;
732
733                     int* pSize = (int *)(pBlob + Marshal.SizeOf(*pHeader));
734                     *pSize = key.Length;
735                 }
736             }
737
738             Buffer.BlockCopy(key, 0, keyBlob, Marshal.SizeOf(typeof(BLOBHEADER)) + Marshal.SizeOf(typeof(int)), key.Length);
739
740             // Import the PLAINTEXTKEYBLOB into the CSP
741             SafeCapiKeyHandle importedKey = null;
742
743             RuntimeHelpers.PrepareConstrainedRegions();
744             try {
745                 if (!UnsafeNativeMethods.CryptImportKey(provider,
746                                                         keyBlob,
747                                                         keyBlob.Length,
748                                                         SafeCapiKeyHandle.InvalidHandle,
749                                                         KeyFlags.Exportable,
750                                                         out importedKey)) {
751                     throw new CryptographicException(Marshal.GetLastWin32Error());
752                 }
753             }
754             finally {
755                 if (importedKey != null && !importedKey.IsInvalid) {
756                     importedKey.SetParentCsp(provider);
757                 }
758             }
759             return importedKey;
760         }
761
762         /// <summary>
763         ///     Set a DWORD key parameter (KP_MODE and KP_MODE_BITS)
764         /// </summary>
765         [System.Security.SecurityCritical]
766         internal static void SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, int value) {
767             Contract.Requires(key != null);
768             Contract.Requires(parameter == KeyParameter.Mode || parameter == KeyParameter.ModeBits);
769
770             SetKeyParameter(key, parameter, BitConverter.GetBytes(value));
771         }
772
773         /// <summary>
774         ///     Set the value of one of a key's parameters
775         /// </summary>
776         [System.Security.SecurityCritical]
777         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
778         internal static void SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, byte[] value) {
779             Contract.Requires(key != null && !key.IsInvalid && !key.IsClosed);
780             Contract.Requires(value != null);
781
782             if (!UnsafeNativeMethods.CryptSetKeyParam(key, parameter, value, 0)) {
783                 throw new CryptographicException(Marshal.GetLastWin32Error());
784             }
785         }
786
787         //Wrapper methods for certificate extensions
788
789         /// <summary>
790         /// Local alloc wrapper. 
791         /// </summary>
792         /// <param name="uFlags"></param>
793         /// <param name="sizetdwBytes"></param>
794         /// <returns></returns>
795         [SecuritySafeCritical]
796         internal static SafeLocalAllocHandle LocalAlloc(uint uFlags, IntPtr sizetdwBytes) {
797             SafeLocalAllocHandle safeLocalAllocHandle = UnsafeNativeMethods.LocalAlloc(uFlags, sizetdwBytes);
798             if (safeLocalAllocHandle == null || safeLocalAllocHandle.IsInvalid) {
799                 throw new OutOfMemoryException();
800             }
801             return safeLocalAllocHandle;
802         }
803
804         /// <summary>
805         /// 
806         /// </summary>
807         /// <param name="pszStructType"></param>
808         /// <param name="pbEncoded"></param>
809         /// <param name="cbEncoded"></param>
810         /// <param name="decodedValue"></param>
811         /// <param name="cbDecodedValue"></param>
812         /// <returns></returns>
813         [SecuritySafeCritical]
814         internal static unsafe bool DecodeObject(IntPtr pszStructType,
815                   IntPtr pbEncoded,
816                   uint cbEncoded,
817                   out SafeLocalAllocHandle decodedValue,
818                   out uint cbDecodedValue)
819         {
820             // Initialize out parameters
821             decodedValue = SafeLocalAllocHandle.InvalidHandle;
822             cbDecodedValue = 0;
823
824             // Decode
825             uint cbDecoded = 0;
826             SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle;
827             if (!UnsafeNativeMethods.CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
828                                                         pszStructType,
829                                                         pbEncoded,
830                                                         cbEncoded,
831                                                         0,
832                                                         ptr,
833                                                         new IntPtr(&cbDecoded))) {
834                 return false;
835             }
836             ptr = LocalAlloc(LMEM_FIXED, new IntPtr(cbDecoded));
837
838             if (!UnsafeNativeMethods.CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
839                                                    pszStructType,
840                                                    pbEncoded,
841                                                    cbEncoded,
842                                                    0,
843                                                    ptr,
844                                                    new IntPtr(&cbDecoded))) {
845                 return false;
846             }
847             // Return decoded values
848             decodedValue = ptr;
849             cbDecodedValue = cbDecoded;
850
851             return true;
852         }
853
854         /// <summary>
855         /// 
856         /// </summary>
857         /// <param name="pszStructType"></param>
858         /// <param name="pbEncoded"></param>
859         /// <param name="decodedValue"></param>
860         /// <param name="cbDecodedValue"></param>
861         /// <returns></returns>
862         [SecuritySafeCritical]
863         internal static unsafe bool DecodeObject(IntPtr pszStructType,
864                           byte[] pbEncoded,
865                           out SafeLocalAllocHandle decodedValue,
866                           out uint cbDecodedValue)
867         {
868             // Initialize out parameters
869             decodedValue = SafeLocalAllocHandle.InvalidHandle;
870             cbDecodedValue = 0;
871
872             // Decode
873             uint cbDecoded = 0;
874             SafeLocalAllocHandle pbDecoded = SafeLocalAllocHandle.InvalidHandle;
875
876             if (!UnsafeNativeMethods.CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
877                                                pszStructType,
878                                                pbEncoded,
879                                                (uint)pbEncoded.Length,
880                                                0,
881                                                pbDecoded,
882                                                new IntPtr(&cbDecoded))) {
883                 return false;
884             }
885             pbDecoded = LocalAlloc(LMEM_FIXED, new IntPtr(cbDecoded));
886             if (!UnsafeNativeMethods.CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
887                                                pszStructType,
888                                                pbEncoded,
889                                                (uint)pbEncoded.Length,
890                                                0,
891                                                pbDecoded,
892                                                new IntPtr(&cbDecoded))) {
893                 return false;
894             }
895             // Return decoded values
896             decodedValue = pbDecoded;
897             cbDecodedValue = cbDecoded;
898
899             return true;
900         }
901
902         /// <summary>
903         /// 
904         /// </summary>
905         /// <param name="dwKeyType"></param>
906         /// <param name="pvKey"></param>
907         /// <param name="dwGroupId"></param>
908         /// <returns></returns>
909         [SecuritySafeCritical]
910         internal static CRYPT_OID_INFO CryptFindOIDInfo(
911             [In]    uint dwKeyType,
912             [In]    IntPtr pvKey,
913             [In]    OidGroup dwGroupId) {
914
915             if (pvKey == IntPtr.Zero) {
916                 throw new ArgumentNullException("pvKey");
917             }
918             CRYPT_OID_INFO pOIDInfo = new CRYPT_OID_INFO(Marshal.SizeOf(typeof(CRYPT_OID_INFO)));
919             IntPtr pv = UnsafeNativeMethods.CryptFindOIDInfo(dwKeyType,
920                                                      pvKey,
921                                                      dwGroupId);
922
923             if (pv != IntPtr.Zero) {
924                 pOIDInfo = (CRYPT_OID_INFO)Marshal.PtrToStructure(pv, typeof(CRYPT_OID_INFO));
925             }
926             return pOIDInfo;
927         }
928
929         /// <summary>
930         /// 
931         /// </summary>
932         /// <param name="dwKeyType"></param>
933         /// <param name="pvKey"></param>
934         /// <param name="dwGroupId"></param>
935         /// <returns></returns>
936         [SecuritySafeCritical]
937         internal static CRYPT_OID_INFO CryptFindOIDInfo(
938             [In]    uint dwKeyType,
939             [In]    SafeLocalAllocHandle pvKey,
940             [In]    OidGroup dwGroupId) {
941
942             if (pvKey == null) {
943                 throw new ArgumentNullException("pvKey");
944             }
945             if (pvKey.IsInvalid) {
946                 throw new CryptographicException("SR.GetString(SR.Cryptography_InvalidHandle)", "pvKey");
947             }
948             CRYPT_OID_INFO pOIDInfo = new CRYPT_OID_INFO(Marshal.SizeOf(typeof(CRYPT_OID_INFO)));
949             IntPtr pv = UnsafeNativeMethods.CryptFindOIDInfo(dwKeyType,
950                                                      pvKey,
951                                                      dwGroupId);
952
953             if (pv != IntPtr.Zero) {
954                 pOIDInfo = (CRYPT_OID_INFO)Marshal.PtrToStructure(pv, typeof(CRYPT_OID_INFO));
955             }
956             return pOIDInfo;
957         }
958     }
959
960     /// <summary>
961     /// Safe local handle class
962     /// </summary>
963     internal sealed class SafeLocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid {
964         [SecuritySafeCritical]
965         private SafeLocalAllocHandle()
966             : base(true) {
967         }
968
969         [DllImport("kernel32.dll")]
970         private static extern IntPtr LocalFree(IntPtr hMem);
971
972         [SecuritySafeCritical]
973         internal T Read<T>(int offset) where T : struct {
974             bool addedRef = false;
975             RuntimeHelpers.PrepareConstrainedRegions();
976             try {
977                 DangerousAddRef(ref addedRef);
978                 unsafe {
979                     IntPtr pBase = new IntPtr((byte*)handle.ToPointer() + offset);
980                     return (T)Marshal.PtrToStructure(pBase, typeof(T));
981                 }
982             }
983             finally {
984                 if (addedRef) {
985                     DangerousRelease();
986                 }
987             }
988         }
989
990         [SecuritySafeCritical]
991         protected override bool ReleaseHandle() {
992             return LocalFree(handle) == IntPtr.Zero;
993         }
994
995         [SecuritySafeCritical]
996         internal SafeLocalAllocHandle(IntPtr handle)
997             : base(true) {
998             SetHandle(handle);
999         }
1000
1001         internal static SafeLocalAllocHandle InvalidHandle {
1002             [SecuritySafeCritical]
1003             get { return new SafeLocalAllocHandle(IntPtr.Zero); }
1004         }
1005     }
1006
1007     /// <summary>
1008     /// 
1009     /// </summary>
1010     internal class X509Utils {
1011         
1012         [SecuritySafeCritical]
1013         internal static SafeLocalAllocHandle StringToAnsiPtr(string s) {
1014             byte[] arr = new byte[s.Length + 1];
1015             Encoding.ASCII.GetBytes(s, 0, s.Length, arr, 0);
1016             SafeLocalAllocHandle pb = CapiNative.LocalAlloc(CapiNative.LMEM_FIXED, new IntPtr(arr.Length));
1017             Marshal.Copy(arr, 0, pb.DangerousGetHandle(), arr.Length);
1018             return pb;
1019         }
1020     }
1021 }