1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 
7 using System;
8 using System.Collections.Generic;
9 using System.Diagnostics;
10 using System.Diagnostics.CodeAnalysis;
11 #if !MONO
12 using System.Numerics;
13 #endif
14 using System.Runtime.CompilerServices;
15 using System.Runtime.ConstrainedExecution;
16 using System.Runtime.InteropServices;
17 using System.Security.Permissions;
18 using System.Text;
19 using System.Diagnostics.Contracts;
20 using Microsoft.Win32;
21 using Microsoft.Win32.SafeHandles;
22 
23 namespace System.Security.Cryptography {
24 
25     //
26     // Public facing enumerations
27     //
28 
29     /// <summary>
30     ///     Flags to control how often and in which format a key is allowed to be exported
31     /// </summary>
32     [Flags]
33     public enum CngExportPolicies {
34         None = 0x00000000,
35         AllowExport = 0x00000001,                       // NCRYPT_ALLOW_EXPORT_FLAG
36         AllowPlaintextExport = 0x00000002,              // NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG
37         AllowArchiving = 0x00000004,                    // NCRYPT_ALLOW_ARCHIVING_FLAG
38         AllowPlaintextArchiving = 0x00000008            // NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG
39     }
40 
41     /// <summary>
42     ///     Flags controlling how the key is created
43     /// </summary>
44     [Flags]
45     public enum CngKeyCreationOptions {
46         None = 0x00000000,
47         MachineKey = 0x00000020,                        // NCRYPT_MACHINE_KEY_FLAG
48         OverwriteExistingKey = 0x00000080               // NCRYPT_OVERWRITE_KEY_FLAG
49     }
50 
51     /// <summary>
52     ///     Flags controlling how a key is opened
53     /// </summary>
54     [Flags]
55     [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "Approved API exception to have an easy way to express user keys")]
56     public enum CngKeyOpenOptions {
57         None = 0x00000000,
58         UserKey = 0x00000000,
59         MachineKey = 0x00000020,                        // NCRYPT_MACHINE_KEY_FLAG
60         Silent = 0x00000040                             // NCRYPT_SILENT_FLAG
61     }
62 
63     /// <summary>
64     ///     Flags indicating the type of key
65     /// </summary>
66     [Flags]
67     internal enum CngKeyTypes {
68         None = 0x00000000,
69         MachineKey = 0x00000020                         // NCRYPT_MACHINE_KEY_FLAG
70     }
71 
72     /// <summary>
73     ///     Bits defining what operations are valid to use a key with
74     /// </summary>
75     [Flags]
76     [SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Justification = "Flags are defined by the native ncrypt API")]
77     public enum CngKeyUsages {
78         None = 0x00000000,
79         Decryption = 0x00000001,                        // NCRYPT_ALLOW_DECRYPT_FLAG
80         Signing = 0x00000002,                           // NCRYPT_ALLOW_SIGNING_FLAG
81         KeyAgreement = 0x00000004,                      // NCRYPT_ALLOW_KEY_AGREEMENT_FLAG
82         AllUsages = 0x00ffffff                          // NCRYPT_ALLOW_ALL_USAGES
83     }
84 
85     /// <summary>
86     ///     Options affecting how a property is interpreted by CNG
87     /// </summary>
88     [Flags]
89     [SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Justification = "Flags are defined by the native ncrypt API")]
90     public enum CngPropertyOptions {
91         None = 0x00000000,
92         CustomProperty = 0x40000000,                    // NCRYPT_PERSIST_ONLY_FLAG
93         Persist = unchecked((int)0x80000000)            // NCRYPT_PERSIST_FLAG
94     }
95 
96     /// <summary>
97     ///     Levels of UI protection available for a key
98     /// </summary>
99     [Flags]
100     public enum CngUIProtectionLevels {
101         None = 0x00000000,
102         ProtectKey = 0x00000001,                        // NCRYPT_UI_PROTECT_KEY_FLAG
103         ForceHighProtection = 0x00000002                // NCRYPT_UI_FORCE_HIGH_PROTECTION_FLAG
104     }
105 #if !MONO
106     /// <summary>
107     ///     Native interop with CNG's NCrypt layer. Native definitions are in ncrypt.h
108     /// </summary>
109     internal static class NCryptNative {
110         //
111         // Enumerations
112         //
113 
114         /// <summary>
115         ///     Types of NCryptBuffers
116         /// </summary>
117         internal enum BufferType {
118             KdfHashAlgorithm = 0x00000000,              // KDF_HASH_ALGORITHM
119             KdfSecretPrepend = 0x00000001,              // KDF_SECRET_PREPEND
120             KdfSecretAppend = 0x00000002,               // KDF_SECRET_APPEND
121             KdfHmacKey = 0x00000003,                    // KDF_HMAC_KEY
122             KdfTlsLabel = 0x00000004,                   // KDF_TLS_PRF_LABEL
123             KdfTlsSeed = 0x00000005                     // KDF_TLS_PRF_SEED
124         }
125 
126         /// <summary>
127         ///     Result codes from NCrypt APIs
128         /// </summary>
129         internal enum ErrorCode {
130             Success = 0,                                            // ERROR_SUCCESS
131             BadSignature = unchecked((int)0x80090006),              // NTE_BAD_SIGNATURE
132             NotFound = unchecked((int)0x80090011),                  // NTE_NOT_FOUND
133             KeyDoesNotExist = unchecked((int)0x80090016),           // NTE_BAD_KEYSET
134             BufferTooSmall = unchecked((int)0x80090028),             // NTE_BUFFER_TOO_SMALL
135             NoMoreItems = unchecked((int)0x8009002a)               // NTE_NO_MORE_ITEMS
136         }
137 
138         /// <summary>
139         ///     Well known names of key properties
140         /// </summary>
141         internal static class KeyPropertyName {
142             internal const string Algorithm = "Algorithm Name";                 // NCRYPT_ALGORITHM_PROPERTY
143             internal const string AlgorithmGroup = "Algorithm Group";           // NCRYPT_ALGORITHM_GROUP_PROPERTY
144             internal const string ExportPolicy = "Export Policy";               // NCRYPT_EXPORT_POLICY_PROPERTY
145             internal const string KeyType = "Key Type";                         // NCRYPT_KEY_TYPE_PROPERTY
146             internal const string KeyUsage = "Key Usage";                       // NCRYPT_KEY_USAGE_PROPERTY
147             internal const string Length = "Length";                            // NCRYPT_LENGTH_PROPERTY
148             internal const string Name = "Name";                                // NCRYPT_NAME_PROPERTY
149             internal const string ParentWindowHandle = "HWND Handle";           // NCRYPT_WINDOW_HANDLE_PROPERTY
150             internal const string PublicKeyLength = "PublicKeyLength";          // NCRYPT_PUBLIC_KEY_LENGTH (Win10+)
151             internal const string ProviderHandle = "Provider Handle";           // NCRYPT_PROVIDER_HANDLE_PROPERTY
152             internal const string UIPolicy = "UI Policy";                       // NCRYPT_UI_POLICY_PROPERTY
153             internal const string UniqueName = "Unique Name";                   // NCRYPT_UNIQUE_NAME_PROPERTY
154             internal const string UseContext = "Use Context";                   // NCRYPT_USE_CONTEXT_PROPERTY
155 
156             //
157             // Properties defined by the CLR
158             //
159 
160             /// <summary>
161             ///     Is the key a CLR created ephemeral key, it will contain a single byte with value 1 if the
162             ///     key was created by the CLR as an ephemeral key.
163             /// </summary>
164             internal const string ClrIsEphemeral = "CLR IsEphemeral";
165         }
166 
167         /// <summary>
168         ///     Well known names of provider properties
169         /// </summary>
170         internal static class ProviderPropertyName {
171             internal const string Name = "Name";        // NCRYPT_NAME_PROPERTY
172         }
173 
174         /// <summary>
175         ///     Flags for code:System.Security.Cryptography.NCryptNative.UnsafeNativeMethods.NCryptSecretAgreement
176         /// </summary>
177         [Flags]
178         internal enum SecretAgreementFlags {
179             None = 0x00000000,
180             UseSecretAsHmacKey = 0x00000001             // KDF_USE_SECRET_AS_HMAC_KEY_FLAG
181         }
182 
183         //
184         // Structures
185         //
186 
187         [StructLayout(LayoutKind.Sequential)]
188         internal struct NCRYPT_UI_POLICY {
189             public int dwVersion;
190             public CngUIProtectionLevels dwFlags;
191 
192             [MarshalAs(UnmanagedType.LPWStr)]
193             public string pszCreationTitle;
194 
195             [MarshalAs(UnmanagedType.LPWStr)]
196             public string pszFriendlyName;
197 
198             [MarshalAs(UnmanagedType.LPWStr)]
199             public string pszDescription;
200         }
201 
202         [StructLayout(LayoutKind.Sequential)]
203         internal struct NCryptBuffer {
204             public int cbBuffer;
205             public BufferType BufferType;
206             public IntPtr pvBuffer;
207         }
208 
209         [StructLayout(LayoutKind.Sequential)]
210         internal struct NCryptBufferDesc {
211             public int ulVersion;
212             public int cBuffers;
213             public IntPtr pBuffers;         // NCryptBuffer[cBuffers]
214         }
215 
216         [SuppressUnmanagedCodeSecurity]
217 #pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
218         [SecurityCritical(SecurityCriticalScope.Everything)]
219 #pragma warning restore 618
220         internal static class UnsafeNativeMethods {
221             /// <summary>
222             ///     Create an NCrypt key
223             /// </summary>
224             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider, [Out] out SafeNCryptKeyHandle phKey, string pszAlgId, string pszKeyName, int dwLegacyKeySpec, CngKeyCreationOptions dwFlags)225             internal static extern ErrorCode NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider,
226                                                                       [Out] out SafeNCryptKeyHandle phKey,
227                                                                       string pszAlgId,
228                                                                       string pszKeyName,
229                                                                       int dwLegacyKeySpec,
230                                                                       CngKeyCreationOptions dwFlags);
231 
232             /// <summary>
233             ///     Delete a key
234             /// </summary>
235             [DllImport("ncrypt.dll")]
NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags)236             internal static extern ErrorCode NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags);
237 
238             /// <summary>
239             ///     Generate a key from a secret agreement
240             /// </summary>
241             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptDeriveKey(SafeNCryptSecretHandle hSharedSecret, string pwszKDF, [In] ref NCryptBufferDesc pParameterList, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbDerivedKey, int cbDerivedKey, [Out] out int pcbResult, SecretAgreementFlags dwFlags)242             internal static extern ErrorCode NCryptDeriveKey(SafeNCryptSecretHandle hSharedSecret,
243                                                              string pwszKDF,
244                                                              [In] ref NCryptBufferDesc pParameterList,
245                                                              [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbDerivedKey,
246                                                              int cbDerivedKey,
247                                                              [Out] out int pcbResult,
248                                                              SecretAgreementFlags dwFlags);
249 
250             /// <summary>
251             ///     Export a key from the KSP
252             /// </summary>
253             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags)254             internal static extern ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey,
255                                                              IntPtr hExportKey,               // NCRYPT_KEY_HANDLE
256                                                              string pszBlobType,
257                                                              IntPtr pParameterList,           // NCryptBufferDesc *
258                                                              [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
259                                                              int cbOutput,
260                                                              [Out] out int pcbResult,
261                                                              int dwFlags);
262 
263             /// <summary>
264             ///     Finalize a key to prepare it for use
265             /// </summary>
266             [DllImport("ncrypt.dll")]
NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags)267             internal static extern ErrorCode NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
268 
269             /// <summary>
270             ///     Get the value of a property of an NCrypt object
271             /// </summary>
272             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptGetProperty(SafeNCryptHandle hObject, string pszProperty, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, CngPropertyOptions dwFlags)273             internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
274                                                                string pszProperty,
275                                                                [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
276                                                                int cbOutput,
277                                                                [Out] out int pcbResult,
278                                                                CngPropertyOptions dwFlags);
279 
280             /// <summary>
281             ///     Get the value of a property of an NCrypt object
282             /// </summary>
283             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptGetProperty(SafeNCryptHandle hObject, string pszProperty, ref int pbOutput, int cbOutput, [Out] out int pcbResult, CngPropertyOptions dwFlags)284             internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
285                                                                string pszProperty,
286                                                                ref int pbOutput,
287                                                                int cbOutput,
288                                                                [Out] out int pcbResult,
289                                                                CngPropertyOptions dwFlags);
290 
291             /// <summary>
292             ///     Get the value of a pointer property of an NCrypt object
293             /// </summary>
294             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
295             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
NCryptGetProperty(SafeNCryptHandle hObject, string pszProperty, [Out] out IntPtr pbOutput, int cbOutput, [Out] out int pcbResult, CngPropertyOptions dwFlags)296             internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
297                                                                string pszProperty,
298                                                                [Out] out IntPtr pbOutput,
299                                                                int cbOutput,
300                                                                [Out] out int pcbResult,
301                                                                CngPropertyOptions dwFlags);
302 
303             /// <summary>
304             ///     Import a key into the KSP
305             /// </summary>
306             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, IntPtr pParameterList, [Out] out SafeNCryptKeyHandle phKey, [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, int cbData, int dwFlags)307             internal static extern ErrorCode NCryptImportKey(SafeNCryptProviderHandle hProvider,
308                                                              IntPtr hImportKey,     // NCRYPT_KEY_HANDLE
309                                                              string pszBlobType,
310                                                              IntPtr pParameterList, // NCryptBufferDesc *
311                                                              [Out] out SafeNCryptKeyHandle phKey,
312                                                              [MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
313                                                              int cbData,
314                                                              int dwFlags);
315 
316             /// <summary>
317             ///     Open an existing key
318             /// </summary>
319             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptOpenKey(SafeNCryptProviderHandle hProvider, [Out] out SafeNCryptKeyHandle phKey, string pszKeyName, int dwLegacyKeySpec, CngKeyOpenOptions dwFlags)320             internal static extern ErrorCode NCryptOpenKey(SafeNCryptProviderHandle hProvider,
321                                                            [Out] out SafeNCryptKeyHandle phKey,
322                                                            string pszKeyName,
323                                                            int dwLegacyKeySpec,
324                                                            CngKeyOpenOptions dwFlags);
325 
326             /// <summary>
327             ///     Acquire a handle to a key storage provider
328             /// </summary>
329             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider, string pszProviderName, int dwFlags)330             internal static extern ErrorCode NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider,
331                                                                        string pszProviderName,
332                                                                        int dwFlags);
333 
334             /// <summary>
335             ///     Generate a secret agreement for generating shared key material
336             /// </summary>
337             [DllImport("ncrypt.dll")]
NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey, SafeNCryptKeyHandle hPubKey, [Out] out SafeNCryptSecretHandle phSecret, int dwFlags)338             internal static extern ErrorCode NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey,
339                                                                    SafeNCryptKeyHandle hPubKey,
340                                                                    [Out] out SafeNCryptSecretHandle phSecret,
341                                                                    int dwFlags);
342 
343             /// <summary>
344             ///     Set a property value on an NCrypt object
345             /// </summary>
346             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, CngPropertyOptions dwFlags)347             internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject,
348                                                                string pszProperty,
349                                                                [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
350                                                                int cbInput,
351                                                                CngPropertyOptions dwFlags);
352 
353             /// <summary>
354             ///     Set a string property value on an NCrypt object
355             /// </summary>
356             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, string pbInput, int cbInput, CngPropertyOptions dwFlags)357             internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject,
358                                                                string pszProperty,
359                                                                string pbInput,
360                                                                int cbInput,
361                                                                CngPropertyOptions dwFlags);
362 
363             /// <summary>
364             ///     Set a property value on an NCrypt object when a pointer to the buffer already exists in
365             ///     managed code. To set a pointer valued property, use the ref IntPtr overload.
366             /// </summary>
367             [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, IntPtr pbInput, int cbInput, CngPropertyOptions dwFlags)368             internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject,
369                                                                string pszProperty,
370                                                                IntPtr pbInput,
371                                                                int cbInput,
372                                                                CngPropertyOptions dwFlags);
373 
374             /// <summary>
375             ///     Create a signature for a hash value
376             /// </summary>
377             [DllImport("ncrypt.dll")]
NCryptSignHash(SafeNCryptKeyHandle hKey, IntPtr pPaddingInfo, [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, [Out] out int pcbResult, int dwFlags)378             internal static extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey,
379                                                             IntPtr pPaddingInfo,
380                                                             [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
381                                                             int cbHashValue,
382                                                             [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
383                                                             int cbSignature,
384                                                             [Out] out int pcbResult,
385                                                             int dwFlags);
386 
387             /// <summary>
388             ///     Verify a signature over a hash value
389             /// </summary>
390             [DllImport("ncrypt.dll")]
NCryptVerifySignature(SafeNCryptKeyHandle hKey, IntPtr pPaddingInfo, [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, int dwFlags)391             internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey,
392                                                                    IntPtr pPaddingInfo,
393                                                                    [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
394                                                                    int cbHashValue,
395                                                                    [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
396                                                                    int cbSignature,
397                                                                    int dwFlags);
398 
399             [DllImport("ncrypt.dll")]
NCryptSignHash(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)400             internal static extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey,
401                                                             [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo,
402                                                             [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
403                                                             int cbHashValue,
404                                                             [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
405                                                             int cbSignature,
406                                                             [Out] out int pcbResult,
407                                                             AsymmetricPaddingMode dwFlags);
408 
409             [DllImport("ncrypt.dll")]
NCryptSignHash(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)410             internal static extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey,
411                                                             [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo,
412                                                             [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
413                                                             int cbHashValue,
414                                                             [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
415                                                             int cbSignature,
416                                                             [Out] out int pcbResult,
417                                                             AsymmetricPaddingMode dwFlags);
418             [DllImport("ncrypt.dll")]
NCryptVerifySignature(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags)419             internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey,
420                                                                    [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo,
421                                                                    [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
422                                                                    int cbHashValue,
423                                                                    [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
424                                                                    int cbSignature,
425                                                                    AsymmetricPaddingMode dwFlags);
426 
427             [DllImport("ncrypt.dll")]
NCryptVerifySignature(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags)428             internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey,
429                                                                    [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo,
430                                                                    [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
431                                                                    int cbHashValue,
432                                                                    [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
433                                                                    int cbSignature,
434                                                                    AsymmetricPaddingMode dwFlags);
435 
436             [DllImport("ncrypt.dll")]
NCryptDecrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)437             internal static extern ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey,
438                                                            [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
439                                                            int cbInput,
440                                                            [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding,
441                                                            [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
442                                                            int cbOutput,
443                                                            [Out] out int pcbResult,
444                                                            AsymmetricPaddingMode dwFlags);
445 
446             [DllImport("ncrypt.dll")]
NCryptDecrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, IntPtr pvPaddingZero, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)447             internal static extern ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey,
448                                                            [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
449                                                            int cbInput,
450                                                            IntPtr pvPaddingZero,
451                                                            [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
452                                                            int cbOutput,
453                                                            [Out] out int pcbResult,
454                                                            AsymmetricPaddingMode dwFlags);
455 
456             [DllImport("ncrypt.dll")]
NCryptEncrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)457             internal static extern ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey,
458                                                            [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
459                                                            int cbInput,
460                                                            [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding,
461                                                            [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
462                                                            int cbOutput,
463                                                            [Out] out int pcbResult,
464                                                            AsymmetricPaddingMode dwFlags);
465 
466             [DllImport("ncrypt.dll")]
NCryptEncrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, IntPtr pvPaddingZero, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)467             internal static extern ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey,
468                                                            [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
469                                                            int cbInput,
470                                                            IntPtr pvPaddingZero,
471                                                            [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
472                                                            int cbOutput,
473                                                            [Out] out int pcbResult,
474                                                            AsymmetricPaddingMode dwFlags);
475         }
476 
477 
478         /// <summary>
479         ///     Adapter to wrap specific NCryptDecrypt P/Invokes with specific padding info
480         /// </summary>
481         [SecuritySafeCritical]
NCryptDecryptor(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref T pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)482         private delegate ErrorCode NCryptDecryptor<T>(SafeNCryptKeyHandle hKey,
483                                                       byte[] pbInput,
484                                                       int cbInput,
485                                                       ref T pvPadding,
486                                                       byte[] pbOutput,
487                                                       int cbOutput,
488                                                       out int pcbResult,
489                                                       AsymmetricPaddingMode dwFlags);
490 
491         /// <summary>
492         ///     Adapter to wrap specific NCryptEncrypt P/Invokes with specific padding info
493         /// </summary>
494         [SecuritySafeCritical]
NCryptEncryptor(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref T pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)495         private delegate ErrorCode NCryptEncryptor<T>(SafeNCryptKeyHandle hKey,
496                                                       byte[] pbInput,
497                                                       int cbInput,
498                                                       ref T pvPadding,
499                                                       byte[] pbOutput,
500                                                       int cbOutput,
501                                                       out int pcbResult,
502                                                       AsymmetricPaddingMode dwFlags);
503 
504         /// <summary>
505         ///     Adapter to wrap specific NCryptSignHash P/Invokes with a specific padding info
506         /// </summary>
507         [SecuritySafeCritical]
NCryptHashSigner(SafeNCryptKeyHandle hKey, ref T pvPaddingInfo, byte[] pbHashValue, int cbHashValue, byte[] pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags)508         private delegate ErrorCode NCryptHashSigner<T>(SafeNCryptKeyHandle hKey,
509                                                        ref T pvPaddingInfo,
510                                                        byte[] pbHashValue,
511                                                        int cbHashValue,
512                                                        byte[] pbSignature,
513                                                        int cbSignature,
514                                                        out int pcbResult,
515                                                        AsymmetricPaddingMode dwFlags);
516 
517         /// <summary>
518         ///     Adapter to wrap specific NCryptVerifySignature P/Invokes with a specific padding info
519         /// </summary>
520         [SecuritySafeCritical]
521         private delegate ErrorCode NCryptSignatureVerifier<T>(SafeNCryptKeyHandle hKey,
522                                                               ref T pvPaddingInfo,
523                                                               byte[] pbHashValue,
524                                                               int cbHashValue,
525                                                               byte[] pbSignature,
526                                                               int cbSignature,
527                                                               AsymmetricPaddingMode dwFlags) where T : struct;
528 
529         //
530         // Utility and wrapper functions
531         //
532 
533         private static volatile bool s_haveNcryptSupported;
534         private static volatile bool s_ncryptSupported;
535 
536 
537         /// <summary>
538         ///     Generic decryption method, wrapped by decryption calls for specific padding modes
539         /// </summary>
540         [SecuritySafeCritical]
541         private static byte[] DecryptData<T>(SafeNCryptKeyHandle key,
542                                              byte[] data,
543                                              ref T paddingInfo,
544                                              AsymmetricPaddingMode paddingMode,
545                                              NCryptDecryptor<T> decryptor) where T : struct {
546             Debug.Assert(key != null, "key != null");
547             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
548             Debug.Assert(data != null, "data != null");
549             Debug.Assert(decryptor != null, "decryptor != null");
550 
551             // Figure out how big of a buffer is needed to store the decrypted data
552             int decryptedSize = 0;
553             ErrorCode error = decryptor(key,
554                                         data,
555                                         data.Length,
556                                         ref paddingInfo,
557                                         null,
558                                         0,
559                                         out decryptedSize,
560                                         paddingMode);
561             if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
562                 throw new CryptographicException((int)error);
563             }
564 
565             // Do the decryption
566             byte[] decrypted = new byte[decryptedSize];
567             error = decryptor(key,
568                               data,
569                               data.Length,
570                               ref paddingInfo,
571                               decrypted,
572                               decrypted.Length,
573                               out decryptedSize,
574                               paddingMode);
575             if (error != ErrorCode.Success) {
576                 throw new CryptographicException((int)error);
577             }
578 
579             // Sometimes decryptedSize can be less than the allocated buffer size
580             // So resize the array to the actual returned plaintext
Array.ResizeSystem.Security.Cryptography.NCryptNative.__anon1581             Array.Resize(ref decrypted, decryptedSize);
582 
583             return decrypted;
584         }
585 
586         /// <summary>
587         ///     Decrypt data using PKCS1 padding
588         /// </summary>
589         [SecuritySafeCritical]
DecryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data)590         internal static byte[] DecryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data) {
591             BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
592 
593             return DecryptData(key,
594                                data,
595                                ref pkcs1Info,
596                                AsymmetricPaddingMode.Pkcs1,
597                                Pkcs1PaddingDecryptionWrapper);
598         }
599 
600         /// <summary>
601         ///     Decrypt data using OAEP padding
602         /// </summary>
603         [SecuritySafeCritical]
DecryptDataOaep(SafeNCryptKeyHandle key, byte[] data, string hashAlgorithm)604         internal static byte[] DecryptDataOaep(SafeNCryptKeyHandle key,
605                                                byte[] data,
606                                                string hashAlgorithm) {
607             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
608 
609             BCryptNative.BCRYPT_OAEP_PADDING_INFO oaepInfo = new BCryptNative.BCRYPT_OAEP_PADDING_INFO();
610             oaepInfo.pszAlgId = hashAlgorithm;
611 
612             return DecryptData(key,
613                                data,
614                                ref oaepInfo,
615                                AsymmetricPaddingMode.Oaep,
616                                UnsafeNativeMethods.NCryptDecrypt);
617         }
618 
619         [SecurityCritical]
Pkcs1PaddingDecryptionWrapper(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)620         private static ErrorCode Pkcs1PaddingDecryptionWrapper(SafeNCryptKeyHandle hKey,
621                                                        byte[] pbInput,
622                                                        int cbInput,
623                                                        ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
624                                                        byte[] pbOutput,
625                                                        int cbOutput,
626                                                        out int pcbResult,
627                                                        AsymmetricPaddingMode dwFlags)
628         {
629             Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1");
630 
631             // This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode
632             // the value for pvPadding must be NULL with keys in the Smart Card KSP.
633             //
634             // Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value;
635             // but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified).
636 
637             return UnsafeNativeMethods.NCryptDecrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags);
638         }
639 
640         /// <summary>
641         ///     Generic encryption method, wrapped by decryption calls for specific padding modes
642         /// </summary>
643         [SecuritySafeCritical]
644         private static byte[] EncryptData<T>(SafeNCryptKeyHandle key,
645                                              byte[] data,
646                                              ref T paddingInfo,
647                                              AsymmetricPaddingMode paddingMode,
648                                              NCryptEncryptor<T> encryptor) where T : struct {
649             Debug.Assert(key != null, "key != null");
650             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
651             Debug.Assert(data != null, "data != null");
652             Debug.Assert(encryptor != null, "encryptor != null");
653 
654             // Figure out how big of a buffer is to encrypt the data
655             int encryptedSize = 0;
656             ErrorCode error = encryptor(key,
657                                         data,
658                                         data.Length,
659                                         ref paddingInfo,
660                                         null,
661                                         0,
662                                         out encryptedSize,
663                                         paddingMode);
664             if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
665                 throw new CryptographicException((int)error);
666             }
667 
668             // Do the encryption
669             byte[] encrypted = new byte[encryptedSize];
670             error = encryptor(key,
671                               data,
672                               data.Length,
673                               ref paddingInfo,
674                               encrypted,
675                               encrypted.Length,
676                               out encryptedSize,
677                               paddingMode);
678             if (error != ErrorCode.Success) {
679                 throw new CryptographicException((int)error);
680             }
681 
682             return encrypted;
683         }
684 
685         /// <summary>
686         ///     Encrypt data using OAEP padding
687         /// </summary>
688         [SecuritySafeCritical]
EncryptDataOaep(SafeNCryptKeyHandle key, byte[] data, string hashAlgorithm)689         internal static byte[] EncryptDataOaep(SafeNCryptKeyHandle key,
690                                                byte[] data,
691                                                string hashAlgorithm) {
692             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
693 
694             BCryptNative.BCRYPT_OAEP_PADDING_INFO oaepInfo = new BCryptNative.BCRYPT_OAEP_PADDING_INFO();
695             oaepInfo.pszAlgId = hashAlgorithm;
696 
697             return EncryptData(key,
698                                data,
699                                ref oaepInfo,
700                                AsymmetricPaddingMode.Oaep,
701                                UnsafeNativeMethods.NCryptEncrypt);
702         }
703 
704         /// <summary>
705         ///     Encrypt data using PKCS1 padding
706         /// </summary>
707         [SecuritySafeCritical]
EncryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data)708         internal static byte[] EncryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data) {
709             BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
710 
711             return EncryptData(key,
712                                data,
713                                ref pkcs1Info,
714                                AsymmetricPaddingMode.Pkcs1,
715                                Pkcs1PaddingEncryptionWrapper);
716         }
717 
718         [SecurityCritical]
Pkcs1PaddingEncryptionWrapper(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)719         private static ErrorCode Pkcs1PaddingEncryptionWrapper(SafeNCryptKeyHandle hKey,
720                                                                byte[] pbInput,
721                                                                int cbInput,
722                                                                ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
723                                                                byte[] pbOutput,
724                                                                int cbOutput,
725                                                                out int pcbResult,
726                                                                AsymmetricPaddingMode dwFlags) {
727             Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1");
728 
729             // This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode
730             // the value for pvPadding must be NULL with keys in the Smart Card KSP.
731             //
732             // Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value;
733             // but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified).
734 
735             return UnsafeNativeMethods.NCryptEncrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags);
736         }
737 
738         /// <summary>
739         ///     Generic signature method, wrapped by signature calls for specific padding modes
740         /// </summary>
741         [SecuritySafeCritical]
742         private static byte[] SignHash<T>(SafeNCryptKeyHandle key,
743                                           byte[] hash,
744                                           ref T paddingInfo,
745                                           AsymmetricPaddingMode paddingMode,
746                                           NCryptHashSigner<T> signer) where T : struct {
747             Debug.Assert(key != null, "key != null");
748             Debug.Assert(!key.IsInvalid && !key.IsClosed, "!key.IsInvalid && !key.IsClosed");
749             Debug.Assert(hash != null, "hash != null");
750             Debug.Assert(signer != null, "signer != null");
751 
752             // Figure out how big the signature is
753             int signatureSize = 0;
754             ErrorCode error = signer(key,
755                                      ref paddingInfo,
756                                      hash,
757                                      hash.Length,
758                                      null,
759                                      0,
760                                      out signatureSize,
761                                      paddingMode);
762             if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
763                 throw new CryptographicException((int)error);
764             }
765 
766             // Sign the hash
767             byte[] signature = new byte[signatureSize];
768             error = signer(key,
769                            ref paddingInfo,
770                            hash,
771                            hash.Length,
772                            signature,
773                            signature.Length,
774                            out signatureSize,
775                            paddingMode);
776             if (error != ErrorCode.Success) {
777                 throw new CryptographicException((int)error);
778             }
779             return signature;
780         }
781 
782         /// <summary>
783         ///     Sign a hash, using PKCS1 padding
784         /// </summary>
785         [SecuritySafeCritical]
SignHashPkcs1(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm)786         internal static byte[] SignHashPkcs1(SafeNCryptKeyHandle key,
787                                              byte[] hash,
788                                              string hashAlgorithm) {
789             Debug.Assert(key != null, "key != null");
790             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
791             Debug.Assert(hash != null, "hash != null");
792             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
793 
794             BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
795             pkcs1Info.pszAlgId = hashAlgorithm;
796 
797             return SignHash(key,
798                             hash,
799                             ref pkcs1Info,
800                             AsymmetricPaddingMode.Pkcs1,
801                             UnsafeNativeMethods.NCryptSignHash);
802         }
803 
804         /// <summary>
805         ///     Sign a hash, using PSS padding
806         /// </summary>
807         [SecuritySafeCritical]
SignHashPss(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm, int saltBytes)808         internal static byte[] SignHashPss(SafeNCryptKeyHandle key,
809                                            byte[] hash,
810                                            string hashAlgorithm,
811                                            int saltBytes) {
812             Debug.Assert(key != null, "key != null");
813             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
814             Debug.Assert(hash != null, "hash != null");
815             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
816             Debug.Assert(saltBytes >= 0, "saltBytes >= 0");
817 
818             BCryptNative.BCRYPT_PSS_PADDING_INFO pssInfo = new BCryptNative.BCRYPT_PSS_PADDING_INFO();
819             pssInfo.pszAlgId = hashAlgorithm;
820             pssInfo.cbSalt = saltBytes;
821 
822             return SignHash(key,
823                             hash,
824                             ref pssInfo,
825                             AsymmetricPaddingMode.Pss,
826                             UnsafeNativeMethods.NCryptSignHash);
827         }
828 
829         /// <summary>
830         ///     Generic signature verification method, wrapped by verification calls for specific padding modes
831         /// </summary>
832         [SecuritySafeCritical]
833         private static bool VerifySignature<T>(SafeNCryptKeyHandle key,
834                                                byte[] hash,
835                                                byte[] signature,
836                                                ref T paddingInfo,
837                                                AsymmetricPaddingMode paddingMode,
838                                                NCryptSignatureVerifier<T> verifier) where T : struct {
839             Debug.Assert(key != null, "key != null");
840             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
841             Debug.Assert(hash != null, "hash != null");
842             Debug.Assert(signature != null, "signature != null");
843             Debug.Assert(verifier != null, "verifier != null");
844 
845             ErrorCode error = verifier(key,
846                                        ref paddingInfo,
847                                        hash,
848                                        hash.Length,
849                                        signature,
850                                        signature.Length,
851                                        paddingMode);
852             return error == ErrorCode.Success;
853         }
854 
855         /// <summary>
856         ///     Verify the signature of a hash using PKCS #1 padding
857         /// </summary>
858         [SecuritySafeCritical]
VerifySignaturePkcs1(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm, byte[] signature)859         internal static bool VerifySignaturePkcs1(SafeNCryptKeyHandle key,
860                                                   byte[] hash,
861                                                   string hashAlgorithm,
862                                                   byte[] signature) {
863             Debug.Assert(key != null, "key != null");
864             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
865             Debug.Assert(hash != null, "hash != null");
866             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
867             Debug.Assert(signature != null, "signature != null");
868 
869             BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
870             pkcs1Info.pszAlgId = hashAlgorithm;
871 
872             return VerifySignature(key,
873                                    hash,
874                                    signature,
875                                    ref pkcs1Info,
876                                    AsymmetricPaddingMode.Pkcs1,
877                                    UnsafeNativeMethods.NCryptVerifySignature);
878         }
879 
880         /// <summary>
881         ///     Verify the signature of a hash using PSS padding
882         /// </summary>
883         [SecuritySafeCritical]
VerifySignaturePss(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm, int saltBytes, byte[] signature)884         internal static bool VerifySignaturePss(SafeNCryptKeyHandle key,
885                                                 byte[] hash,
886                                                 string hashAlgorithm,
887                                                 int saltBytes,
888                                                 byte[] signature) {
889             Debug.Assert(key != null, "key != null");
890             Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
891             Debug.Assert(hash != null, "hash != null");
892             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
893             Debug.Assert(signature != null, "signature != null");
894 
895             BCryptNative.BCRYPT_PSS_PADDING_INFO pssInfo = new BCryptNative.BCRYPT_PSS_PADDING_INFO();
896             pssInfo.pszAlgId = hashAlgorithm;
897             pssInfo.cbSalt = saltBytes;
898 
899             return VerifySignature(key,
900                                    hash,
901                                    signature,
902                                    ref pssInfo,
903                                    AsymmetricPaddingMode.Pss,
904                                    UnsafeNativeMethods.NCryptVerifySignature);
905         }
906 
907         /// <summary>
908         ///     Determine if NCrypt is supported on the current machine
909         /// </summary>
910         internal static bool NCryptSupported {
911             [SecuritySafeCritical]
912             [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
913             get {
914                 if (!s_haveNcryptSupported)
915                 {
916                     // Attempt to load ncrypt.dll to see if the NCrypt CNG APIs are available on the machine
917                     using (SafeLibraryHandle ncrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("ncrypt", IntPtr.Zero, 0)) {
918                         s_ncryptSupported = !ncrypt.IsInvalid;
919                         s_haveNcryptSupported = true;
920                     }
921                 }
922 
923                 return s_ncryptSupported;
924             }
925         }
926 
927         /// <summary>
928         ///     Build an ECC public key blob to represent the given parameters
929         /// </summary>
BuildEccPublicBlob(string algorithm, BigInteger x, BigInteger y)930         internal static byte[] BuildEccPublicBlob(string algorithm, BigInteger x, BigInteger y) {
931             Contract.Requires(!String.IsNullOrEmpty(algorithm));
932             Contract.Ensures(Contract.Result<byte[]>() != null);
933 
934             //
935             // #ECCPublicBlobFormat
936             // The ECC public key blob format is as follows:
937             //
938             // DWORD dwMagic
939             // DWORD cbKey
940             // X parameter (cbKey bytes long, byte-reversed)
941             // Y parameter (cbKey bytes long, byte-reversed)
942             //
943 
944             // First map the algorithm name to its magic number and key size
945             BCryptNative.KeyBlobMagicNumber algorithmMagic;
946             int keySize;
947             BCryptNative.MapAlgorithmIdToMagic(algorithm, out algorithmMagic, out keySize);
948 
949             // Next generate the public key parameters
950             byte[] xBytes = ReverseBytes(FillKeyParameter(x.ToByteArray(), keySize));
951             byte[] yBytes = ReverseBytes(FillKeyParameter(y.ToByteArray(), keySize));
952 
953             // Finally, lay out the structure itself
954             byte[] blob = new byte[2 * sizeof(int) + xBytes.Length + yBytes.Length];
955             Buffer.BlockCopy(BitConverter.GetBytes((int)algorithmMagic), 0, blob, 0, sizeof(int));
956             Buffer.BlockCopy(BitConverter.GetBytes(xBytes.Length), 0, blob, sizeof(int), sizeof(int));
957             Buffer.BlockCopy(xBytes, 0, blob, 2 * sizeof(int), xBytes.Length);
958             Buffer.BlockCopy(yBytes, 0, blob, 2 * sizeof(int) + xBytes.Length, yBytes.Length);
959 
960             return blob;
961         }
962 
963         /// <summary>
964         ///     Create a random CNG key
965         /// </summary>
966         [System.Security.SecurityCritical]
CreatePersistedKey(SafeNCryptProviderHandle provider, string algorithm, string name, CngKeyCreationOptions options)967         internal static SafeNCryptKeyHandle CreatePersistedKey(SafeNCryptProviderHandle provider,
968                                                                string algorithm,
969                                                                string name,
970                                                                CngKeyCreationOptions options) {
971             Contract.Requires(provider != null && !provider.IsInvalid && !provider.IsClosed);
972             Contract.Requires(!String.IsNullOrEmpty(algorithm));
973             Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
974                              !Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
975                              !Contract.Result<SafeNCryptKeyHandle>().IsClosed);
976 
977             SafeNCryptKeyHandle keyHandle = null;
978             ErrorCode error = UnsafeNativeMethods.NCryptCreatePersistedKey(provider,
979                                                                            out keyHandle,
980                                                                            algorithm,
981                                                                            name,
982                                                                            0,
983                                                                            options);
984             if (error != ErrorCode.Success) {
985                 throw new CryptographicException((int)error);
986             }
987 
988             return keyHandle;
989         }
990 
991         /// <summary>
992         ///     Delete a key
993         /// </summary>
994         [System.Security.SecurityCritical]
DeleteKey(SafeNCryptKeyHandle key)995         internal static void DeleteKey(SafeNCryptKeyHandle key) {
996             Contract.Requires(key != null);
997 
998             ErrorCode error = UnsafeNativeMethods.NCryptDeleteKey(key, 0);
999             if (error != ErrorCode.Success) {
1000                 throw new CryptographicException((int)error);
1001             }
1002             key.SetHandleAsInvalid();
1003         }
1004 
1005         /// <summary>
1006         ///     Derive key material from a hash or HMAC KDF
1007         /// </summary>
1008         /// <returns></returns>
1009         [System.Security.SecurityCritical]
1010         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement, string kdf, string hashAlgorithm, byte[] hmacKey, byte[] secretPrepend, byte[] secretAppend, SecretAgreementFlags flags)1011         private static byte[] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement,
1012                                                 string kdf,
1013                                                 string hashAlgorithm,
1014                                                 byte[] hmacKey,
1015                                                 byte[] secretPrepend,
1016                                                 byte[] secretAppend,
1017                                                 SecretAgreementFlags flags) {
1018             Contract.Requires(secretAgreement != null);
1019             Contract.Requires(!String.IsNullOrEmpty(kdf));
1020             Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm));
1021             Contract.Requires(hmacKey == null || kdf == BCryptNative.KeyDerivationFunction.Hmac);
1022             Contract.Ensures(Contract.Result<byte[]>() != null);
1023 
1024             List<NCryptBuffer> parameters = new List<NCryptBuffer>();
1025 
1026             // First marshal the hash algoritm
1027             IntPtr hashAlgorithmString = IntPtr.Zero;
1028 
1029             // Run in a CER so that we know we'll free the memory for the marshaled string
1030             RuntimeHelpers.PrepareConstrainedRegions();
1031             try {
1032                 // Assign in a CER so we don't fail between allocating the memory and assigning the result
1033                 // back to the string variable.
1034                 RuntimeHelpers.PrepareConstrainedRegions();
1035                 try { }
1036                 finally {
1037                     hashAlgorithmString = Marshal.StringToCoTaskMemUni(hashAlgorithm);
1038                 }
1039 
1040                 // We always need to marshal the hashing function
1041                 NCryptBuffer hashAlgorithmBuffer = new NCryptBuffer();
1042                 hashAlgorithmBuffer.cbBuffer = (hashAlgorithm.Length + 1) * sizeof(char);
1043                 hashAlgorithmBuffer.BufferType = BufferType.KdfHashAlgorithm;
1044                 hashAlgorithmBuffer.pvBuffer = hashAlgorithmString;
1045                 parameters.Add(hashAlgorithmBuffer);
1046 
1047                 unsafe {
1048                     fixed (byte* pHmacKey = hmacKey, pSecretPrepend = secretPrepend, pSecretAppend = secretAppend) {
1049                         //
1050                         // Now marshal the other parameters
1051                         //
1052 
1053                         if (pHmacKey != null) {
1054                             NCryptBuffer hmacKeyBuffer = new NCryptBuffer();
1055                             hmacKeyBuffer.cbBuffer = hmacKey.Length;
1056                             hmacKeyBuffer.BufferType = BufferType.KdfHmacKey;
1057                             hmacKeyBuffer.pvBuffer = new IntPtr(pHmacKey);
1058                             parameters.Add(hmacKeyBuffer);
1059                         }
1060 
1061                         if (pSecretPrepend != null) {
1062                             NCryptBuffer secretPrependBuffer = new NCryptBuffer();
1063                             secretPrependBuffer.cbBuffer = secretPrepend.Length;
1064                             secretPrependBuffer.BufferType = BufferType.KdfSecretPrepend;
1065                             secretPrependBuffer.pvBuffer = new IntPtr(pSecretPrepend);
1066                             parameters.Add(secretPrependBuffer);
1067                         }
1068 
1069                         if (pSecretAppend != null) {
1070                             NCryptBuffer secretAppendBuffer = new NCryptBuffer();
1071                             secretAppendBuffer.cbBuffer = secretAppend.Length;
1072                             secretAppendBuffer.BufferType = BufferType.KdfSecretAppend;
1073                             secretAppendBuffer.pvBuffer = new IntPtr(pSecretAppend);
1074                             parameters.Add(secretAppendBuffer);
1075                         }
1076 
1077                         return DeriveKeyMaterial(secretAgreement,
1078                                                  kdf,
1079                                                  parameters.ToArray(),
1080                                                  flags);
1081                     }
1082                 }
1083             }
1084             finally {
1085                 if (hashAlgorithmString != IntPtr.Zero) {
1086                     Marshal.FreeCoTaskMem(hashAlgorithmString);
1087                 }
1088             }
1089         }
1090 
1091         /// <summary>
1092         ///     Derive key material using a given KDF and secret agreement
1093         /// </summary>
1094         [System.Security.SecurityCritical]
DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement, string kdf, NCryptBuffer[] parameters, SecretAgreementFlags flags)1095         private static byte[] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement,
1096                                                 string kdf,
1097                                                 NCryptBuffer[] parameters,
1098                                                 SecretAgreementFlags flags) {
1099             Contract.Requires(secretAgreement != null);
1100             Contract.Requires(!String.IsNullOrEmpty(kdf));
1101             Contract.Requires(parameters != null);
1102             Contract.Ensures(Contract.Result<byte[]>() != null);
1103 
1104             unsafe {
1105                 fixed (NCryptBuffer* pParameters = parameters) {
1106                     NCryptBufferDesc parameterDesc = new NCryptBufferDesc();
1107                     parameterDesc.ulVersion = 0;
1108                     parameterDesc.cBuffers = parameters.Length;
1109                     parameterDesc.pBuffers = new IntPtr(pParameters);
1110 
1111                     // Figure out how big the key material is
1112                     int keySize = 0;
1113                     ErrorCode error = UnsafeNativeMethods.NCryptDeriveKey(secretAgreement,
1114                                                                           kdf,
1115                                                                           ref parameterDesc,
1116                                                                           null,
1117                                                                           0,
1118                                                                           out keySize,
1119                                                                           flags);
1120                     if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
1121                         throw new CryptographicException((int)error);
1122                     }
1123 
1124                     // Allocate memory for the key material and generate it
1125                     byte[] keyMaterial = new byte[keySize];
1126                     error = UnsafeNativeMethods.NCryptDeriveKey(secretAgreement,
1127                                                                 kdf,
1128                                                                 ref parameterDesc,
1129                                                                 keyMaterial,
1130                                                                 keyMaterial.Length,
1131                                                                 out keySize,
1132                                                                 flags);
1133 
1134                     if (error != ErrorCode.Success) {
1135                         throw new CryptographicException((int)error);
1136                     }
1137 
1138                     return keyMaterial;
1139                 }
1140             }
1141         }
1142 
1143         /// <summary>
1144         ///     Derive key material from a secret agreement using a hash KDF
1145         /// </summary>
1146         [System.Security.SecurityCritical]
DeriveKeyMaterialHash(SafeNCryptSecretHandle secretAgreement, string hashAlgorithm, byte[] secretPrepend, byte[] secretAppend, SecretAgreementFlags flags)1147         internal static byte[] DeriveKeyMaterialHash(SafeNCryptSecretHandle secretAgreement,
1148                                                      string hashAlgorithm,
1149                                                      byte[] secretPrepend,
1150                                                      byte[] secretAppend,
1151                                                      SecretAgreementFlags flags) {
1152             Contract.Requires(secretAgreement != null);
1153             Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm));
1154             Contract.Ensures(Contract.Result<byte[]>() != null);
1155 
1156             return DeriveKeyMaterial(secretAgreement,
1157                                      BCryptNative.KeyDerivationFunction.Hash,
1158                                      hashAlgorithm,
1159                                      null,
1160                                      secretPrepend,
1161                                      secretAppend,
1162                                      flags);
1163         }
1164 
1165         /// <summary>
1166         ///     Derive key material from a secret agreement using a HMAC KDF
1167         /// </summary>
1168         [System.Security.SecurityCritical]
DeriveKeyMaterialHmac(SafeNCryptSecretHandle secretAgreement, string hashAlgorithm, byte[] hmacKey, byte[] secretPrepend, byte[] secretAppend, SecretAgreementFlags flags)1169         internal static byte[] DeriveKeyMaterialHmac(SafeNCryptSecretHandle secretAgreement,
1170                                                      string hashAlgorithm,
1171                                                      byte[] hmacKey,
1172                                                      byte[] secretPrepend,
1173                                                      byte[] secretAppend,
1174                                                      SecretAgreementFlags flags) {
1175             Contract.Requires(secretAgreement != null);
1176             Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm));
1177             Contract.Ensures(Contract.Result<byte[]>() != null);
1178 
1179             return DeriveKeyMaterial(secretAgreement,
1180                                      BCryptNative.KeyDerivationFunction.Hmac,
1181                                      hashAlgorithm,
1182                                      hmacKey,
1183                                      secretPrepend,
1184                                      secretAppend,
1185                                      flags);
1186         }
1187 
1188         /// <summary>
1189         ///     Derive key material from a secret agreeement using the TLS KDF
1190         /// </summary>
1191         [System.Security.SecurityCritical]
DeriveKeyMaterialTls(SafeNCryptSecretHandle secretAgreement, byte[] label, byte[] seed, SecretAgreementFlags flags)1192         internal static byte[] DeriveKeyMaterialTls(SafeNCryptSecretHandle secretAgreement,
1193                                                     byte[] label,
1194                                                     byte[] seed,
1195                                                     SecretAgreementFlags flags) {
1196             Contract.Requires(secretAgreement != null);
1197             Contract.Requires(label != null && seed != null);
1198             Contract.Ensures(Contract.Result<byte[]>() != null);
1199 
1200             NCryptBuffer[] buffers = new NCryptBuffer[2];
1201 
1202             unsafe {
1203                 fixed (byte* pLabel = label, pSeed = seed) {
1204                     NCryptBuffer labelBuffer = new NCryptBuffer();
1205                     labelBuffer.cbBuffer = label.Length;
1206                     labelBuffer.BufferType = BufferType.KdfTlsLabel;
1207                     labelBuffer.pvBuffer = new IntPtr(pLabel);
1208                     buffers[0] = labelBuffer;
1209 
1210                     NCryptBuffer seedBuffer = new NCryptBuffer();
1211                     seedBuffer.cbBuffer = seed.Length;
1212                     seedBuffer.BufferType = BufferType.KdfTlsSeed;
1213                     seedBuffer.pvBuffer = new IntPtr(pSeed);
1214                     buffers[1] = seedBuffer;
1215 
1216                     return DeriveKeyMaterial(secretAgreement,
1217                                              BCryptNative.KeyDerivationFunction.Tls,
1218                                              buffers,
1219                                              flags);
1220                 }
1221             }
1222         }
1223 
1224         /// <summary>
1225         ///     Generate a secret agreement value for between two parties
1226         /// </summary>
1227         [System.Security.SecurityCritical]
DeriveSecretAgreement(SafeNCryptKeyHandle privateKey, SafeNCryptKeyHandle otherPartyPublicKey)1228         internal static SafeNCryptSecretHandle DeriveSecretAgreement(SafeNCryptKeyHandle privateKey,
1229                                                                      SafeNCryptKeyHandle otherPartyPublicKey) {
1230             Contract.Requires(privateKey != null);
1231             Contract.Requires(otherPartyPublicKey != null);
1232             Contract.Ensures(Contract.Result<SafeNCryptSecretHandle>() != null &&
1233                              !Contract.Result<SafeNCryptSecretHandle>().IsClosed &&
1234                              !Contract.Result<SafeNCryptSecretHandle>().IsInvalid);
1235 
1236             SafeNCryptSecretHandle secretAgreement;
1237             ErrorCode error = UnsafeNativeMethods.NCryptSecretAgreement(privateKey,
1238                                                                         otherPartyPublicKey,
1239                                                                         out secretAgreement,
1240                                                                         0);
1241 
1242             if (error != ErrorCode.Success) {
1243                 throw new CryptographicException((int)error);
1244             }
1245 
1246             return secretAgreement;
1247         }
1248 
1249         /// <summary>
1250         ///     Export a key from the KSP
1251         /// </summary>
1252         [System.Security.SecurityCritical]
ExportKey(SafeNCryptKeyHandle key, string format)1253         internal static byte[] ExportKey(SafeNCryptKeyHandle key, string format) {
1254             Contract.Requires(key != null);
1255             Contract.Requires(!String.IsNullOrEmpty(format));
1256             Contract.Ensures(Contract.Result<byte[]>() != null);
1257 
1258             // Figure out how big of a buffer we need to export into
1259             int bufferSize = 0;
1260             ErrorCode error = UnsafeNativeMethods.NCryptExportKey(key,
1261                                                                   IntPtr.Zero,
1262                                                                   format,
1263                                                                   IntPtr.Zero,
1264                                                                   null,
1265                                                                   0,
1266                                                                   out bufferSize,
1267                                                                   0);
1268 
1269             if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
1270                 throw new CryptographicException((int)error);
1271             }
1272 
1273             // Export the key
1274             Debug.Assert(bufferSize > 0, "bufferSize > 0");
1275             byte[] keyBlob = new byte[bufferSize];
1276             error = UnsafeNativeMethods.NCryptExportKey(key,
1277                                                         IntPtr.Zero,
1278                                                         format,
1279                                                         IntPtr.Zero,
1280                                                         keyBlob,
1281                                                         keyBlob.Length,
1282                                                         out bufferSize,
1283                                                         0);
1284 
1285             if (error != ErrorCode.Success) {
1286                 throw new CryptographicException((int)error);
1287             }
1288 
1289             return keyBlob;
1290         }
1291 
1292         /// <summary>
1293         ///     Make sure that a key is padded out to be its full size
1294         /// </summary>
FillKeyParameter(byte[] key, int keySize)1295         private static byte[] FillKeyParameter(byte[] key, int keySize) {
1296             Contract.Requires(key != null);
1297             Contract.Requires(keySize > 0);
1298             Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length >= keySize / 8);
1299 
1300             int bytesRequired = (keySize / 8) + (keySize % 8 == 0 ? 0 : 1);
1301             if (key.Length == bytesRequired) {
1302                 return key;
1303             }
1304 
1305 #if DEBUG
1306             // If the key is longer than required, it should have been padded out with zeros
1307             if (key.Length > bytesRequired) {
1308                 for (int i = bytesRequired; i < key.Length; i++) {
1309                     Debug.Assert(key[i] == 0, "key[i] == 0");
1310                 }
1311             }
1312 #endif
1313             byte[] fullKey = new byte[bytesRequired];
1314             Buffer.BlockCopy(key, 0, fullKey, 0, Math.Min(key.Length, fullKey.Length));
1315             return fullKey;
1316         }
1317 
1318         /// <summary>
1319         ///     Finalize a key and prepare it for use
1320         /// </summary>
1321         [System.Security.SecurityCritical]
FinalizeKey(SafeNCryptKeyHandle key)1322         internal static void FinalizeKey(SafeNCryptKeyHandle key) {
1323             Contract.Requires(key != null && !key.IsInvalid && !key.IsClosed);
1324 
1325             ErrorCode error = UnsafeNativeMethods.NCryptFinalizeKey(key, 0);
1326             if (error != ErrorCode.Success) {
1327                 throw new CryptographicException((int)error);
1328             }
1329         }
1330 
1331         /// <summary>
1332         ///     Get the value of an NCrypt property
1333         /// </summary>
1334         [System.Security.SecurityCritical]
GetProperty(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions, out bool foundProperty)1335         internal static byte[] GetProperty(SafeNCryptHandle ncryptObject,
1336                                            string propertyName,
1337                                            CngPropertyOptions propertyOptions,
1338                                            out bool foundProperty) {
1339             Contract.Requires(ncryptObject != null);
1340             Contract.Requires(propertyName != null);
1341 
1342             // Find out how big of a buffer we need to store the property in
1343             int bufferSize = 0;
1344             ErrorCode error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject,
1345                                                                     propertyName,
1346                                                                     null,
1347                                                                     0,
1348                                                                     out bufferSize,
1349                                                                     propertyOptions);
1350 
1351             //
1352             // NTE_NOT_FOUND means this property does not exist, any other error besides NTE_BUFFER_TOO_SMALL
1353             // indicates a real problem.
1354             //
1355 
1356             if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall && error != ErrorCode.NotFound) {
1357                     throw new CryptographicException((int)error);
1358             }
1359 
1360             foundProperty = error != ErrorCode.NotFound;
1361 
1362             // Pull back the property value
1363             byte[] value = null;
1364             if (error != ErrorCode.NotFound && bufferSize > 0) {
1365                 value = new byte[bufferSize];
1366                 error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject,
1367                                                               propertyName,
1368                                                               value,
1369                                                               value.Length,
1370                                                               out bufferSize,
1371                                                               propertyOptions);
1372                 if (error != ErrorCode.Success) {
1373                     throw new CryptographicException((int)error);
1374                 }
1375 
1376                 foundProperty = true;
1377             }
1378 
1379             return value;
1380         }
1381 
1382         /// <summary>
1383         ///     Get the value of a DWORD NCrypt property
1384         /// </summary>
1385         [System.Security.SecurityCritical]
GetPropertyAsDWord(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions)1386         internal static int GetPropertyAsDWord(SafeNCryptHandle ncryptObject,
1387                                                string propertyName,
1388                                                CngPropertyOptions propertyOptions) {
1389             Contract.Requires(ncryptObject != null);
1390             Contract.Requires(propertyName != null);
1391 
1392             bool foundProperty;
1393             byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty);
1394 
1395             if (!foundProperty || valueBytes == null) {
1396                 return 0;
1397             }
1398             else {
1399                 return BitConverter.ToInt32(valueBytes, 0);
1400             }
1401         }
1402 
1403         [SecurityCritical]
GetPropertyAsInt(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions, ref int propertyValue)1404         internal static ErrorCode GetPropertyAsInt(SafeNCryptHandle ncryptObject,
1405                                                    string propertyName,
1406                                                    CngPropertyOptions propertyOptions,
1407                                                    ref int propertyValue) {
1408             Contract.Requires(ncryptObject != null);
1409             Contract.Requires(propertyName != null);
1410 
1411             int cbResult;
1412 
1413             ErrorCode errorCode = UnsafeNativeMethods.NCryptGetProperty(
1414                 ncryptObject,
1415                 propertyName,
1416                 ref propertyValue,
1417                 sizeof(int),
1418                 out cbResult,
1419                 propertyOptions);
1420 
1421             if (errorCode == ErrorCode.Success)
1422             {
1423                 System.Diagnostics.Debug.Assert(cbResult == sizeof(int), "Expected cbResult=4, got " + cbResult);
1424             }
1425 
1426             return errorCode;
1427         }
1428 
1429         /// <summary>
1430         ///     Get the value of a pointer NCrypt property
1431         /// </summary>
1432         [System.Security.SecurityCritical]
1433         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
GetPropertyAsIntPtr(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions)1434         internal static IntPtr GetPropertyAsIntPtr(SafeNCryptHandle ncryptObject,
1435                                                    string propertyName,
1436                                                    CngPropertyOptions propertyOptions) {
1437             Contract.Requires(ncryptObject != null);
1438             Contract.Requires(propertyName != null);
1439 
1440             // Find out how big of a buffer we need to store the property in
1441             int bufferSize = IntPtr.Size;
1442             IntPtr value = IntPtr.Zero;
1443             ErrorCode error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject,
1444                                                                     propertyName,
1445                                                                     out value,
1446                                                                     IntPtr.Size,
1447                                                                     out bufferSize,
1448                                                                     propertyOptions);
1449 
1450             // NTE_NOT_FOUND means this property was not set, so return a NULL pointer
1451             if (error == ErrorCode.NotFound) {
1452                 return IntPtr.Zero;
1453             }
1454 
1455             if (error != ErrorCode.Success) {
1456                 throw new CryptographicException((int)error);
1457             }
1458 
1459             return value;
1460         }
1461 
1462         /// <summary>
1463         ///     Get the value of a string NCrypt property
1464         /// </summary>
1465         [System.Security.SecurityCritical]
1466         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
GetPropertyAsString(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions)1467         internal static string GetPropertyAsString(SafeNCryptHandle ncryptObject,
1468                                                    string propertyName,
1469                                                    CngPropertyOptions propertyOptions) {
1470             Contract.Requires(ncryptObject != null);
1471             Contract.Requires(propertyName != null);
1472 
1473             bool foundProperty;
1474             byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty);
1475 
1476             if (!foundProperty || valueBytes == null) {
1477                 return null;
1478             }
1479             else if (valueBytes.Length == 0) {
1480                 return String.Empty;
1481             }
1482             else {
1483                 unsafe {
1484                     fixed (byte* pValueBytes = valueBytes) {
1485                         return Marshal.PtrToStringUni(new IntPtr(pValueBytes));
1486                     }
1487                 }
1488             }
1489         }
1490 
1491         /// <summary>
1492         ///     Get the value of an NCrypt structure property -- this will return an empty structure if the
1493         ///     property does not exist.
1494         /// </summary>
1495         [System.Security.SecurityCritical]
1496         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
1497         internal static T GetPropertyAsStruct<T>(SafeNCryptHandle ncryptObject,
1498                                                  string propertyName,
1499                                                  CngPropertyOptions propertyOptions) where T : struct {
1500             Contract.Requires(ncryptObject != null);
1501             Contract.Requires(propertyName != null);
1502 
1503             bool foundProperty;
1504             byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty);
1505 
1506             if (!foundProperty || valueBytes == null) {
1507                 return new T();
1508             }
1509 
1510             unsafe {
1511                 fixed (byte *pValue = valueBytes) {
1512                     return (T)Marshal.PtrToStructure(new IntPtr(pValue), typeof(T));
1513                 }
1514             }
1515         }
1516 
1517         /// <summary>
1518         ///     Import a key into the KSP
1519         /// </summary>
1520         [System.Security.SecurityCritical]
ImportKey(SafeNCryptProviderHandle provider, byte[] keyBlob, string format)1521         internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider,
1522                                                       byte[] keyBlob,
1523                                                       string format) {
1524             Contract.Requires(provider != null);
1525             Contract.Requires(keyBlob != null);
1526             Contract.Requires(!String.IsNullOrEmpty(format));
1527             Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
1528                              !Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
1529                              !Contract.Result<SafeNCryptKeyHandle>().IsClosed);
1530 
1531             SafeNCryptKeyHandle keyHandle = null;
1532             ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider,
1533                                                                   IntPtr.Zero,
1534                                                                   format,
1535                                                                   IntPtr.Zero,
1536                                                                   out keyHandle,
1537                                                                   keyBlob,
1538                                                                   keyBlob.Length,
1539                                                                   0);
1540 
1541             if (error != ErrorCode.Success) {
1542                 throw new CryptographicException((int)error);
1543             }
1544 
1545             return keyHandle;
1546         }
1547 
1548         [System.Security.SecurityCritical]
ImportKey(SafeNCryptProviderHandle provider, byte[] keyBlob, string format, IntPtr pParametersList)1549         internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider,
1550                                                       byte[] keyBlob,
1551                                                       string format,
1552                                                       IntPtr pParametersList) {
1553             Contract.Requires(provider != null);
1554             Contract.Requires(keyBlob != null);
1555             Contract.Requires(!String.IsNullOrEmpty(format));
1556             Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
1557                  !Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
1558                  !Contract.Result<SafeNCryptKeyHandle>().IsClosed);
1559 
1560             SafeNCryptKeyHandle keyHandle = null;
1561             ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider,
1562                                                                   IntPtr.Zero,
1563                                                                   format,
1564                                                                   pParametersList,
1565                                                                   out keyHandle,
1566                                                                   keyBlob,
1567                                                                   keyBlob.Length,
1568                                                                   0);
1569 
1570             if (error != ErrorCode.Success)
1571             {
1572                 throw new CryptographicException((int)error);
1573             }
1574 
1575             return keyHandle;
1576         }
1577 
1578         /// <summary>
1579         ///     Open an existing key
1580         /// </summary>
1581         [System.Security.SecurityCritical]
OpenKey(SafeNCryptProviderHandle provider, string name, CngKeyOpenOptions options)1582         internal static SafeNCryptKeyHandle OpenKey(SafeNCryptProviderHandle provider,
1583                                                     string name,
1584                                                     CngKeyOpenOptions options) {
1585             Contract.Requires(provider != null && !provider.IsInvalid && !provider.IsClosed);
1586             Contract.Requires(name != null);
1587 
1588             SafeNCryptKeyHandle key = null;
1589             ErrorCode error = UnsafeNativeMethods.NCryptOpenKey(provider, out key, name, 0, options);
1590 
1591             if (error != ErrorCode.Success) {
1592                 throw new CryptographicException((int)error);
1593             }
1594 
1595             return key;
1596         }
1597 
1598         /// <summary>
1599         ///     Open the specified key storage provider
1600         /// </summary>
1601         [System.Security.SecurityCritical]
OpenStorageProvider(string providerName)1602         internal static SafeNCryptProviderHandle OpenStorageProvider(string providerName) {
1603             Contract.Requires(!String.IsNullOrEmpty(providerName));
1604             Contract.Ensures(Contract.Result<SafeNCryptProviderHandle>() != null &&
1605                              !Contract.Result<SafeNCryptProviderHandle>().IsInvalid &&
1606                              !Contract.Result<SafeNCryptProviderHandle>().IsClosed);
1607 
1608             SafeNCryptProviderHandle providerHandle = null;
1609             ErrorCode error = UnsafeNativeMethods.NCryptOpenStorageProvider(out providerHandle,
1610                                                                             providerName,
1611                                                                             0);
1612 
1613             if (error != ErrorCode.Success) {
1614                 throw new CryptographicException((int)error);
1615             }
1616 
1617             return providerHandle;
1618         }
1619 
1620         /// <summary>
1621         ///     Reverse the bytes in a buffer
1622         /// </summary>
ReverseBytes(byte[] buffer)1623         private static byte[] ReverseBytes(byte[] buffer) {
1624             Contract.Requires(buffer != null);
1625             Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length == buffer.Length);
1626             return ReverseBytes(buffer, 0, buffer.Length, false);
1627         }
1628 
1629         /// <summary>
1630         ///     Reverse a section of bytes within a buffer
1631         /// </summary>
ReverseBytes(byte[] buffer, int offset, int count)1632         private static byte[] ReverseBytes(byte[] buffer, int offset, int count) {
1633             return ReverseBytes(buffer, offset, count, false);
1634         }
1635 
ReverseBytes(byte[] buffer, int offset, int count, bool padWithZeroByte)1636         private static byte[] ReverseBytes(byte[] buffer, int offset, int count, bool padWithZeroByte) {
1637             Contract.Requires(buffer != null);
1638             Contract.Requires(offset >= 0 && offset < buffer.Length);
1639             Contract.Requires(count >= 0 && buffer.Length - count >= offset);
1640             Contract.Ensures(Contract.Result<byte[]>() != null);
1641             Contract.Ensures(Contract.Result<byte[]>().Length == (padWithZeroByte ? count + 1 : count));
1642             Contract.Ensures(padWithZeroByte ? Contract.Result<byte[]>()[count] == 0 : true);
1643 
1644             byte[] reversed;
1645             if(padWithZeroByte)
1646             {
1647                 reversed = new byte[count+1]; // the last (most-significant) byte will be left as 0x00
1648             }
1649             else
1650             {
1651                 reversed = new byte[count];
1652             }
1653 
1654             int lastByte = offset + count - 1;
1655             for (int i = 0; i < count; i++) {
1656                 reversed[i] = buffer[lastByte - i];
1657             }
1658 
1659             return reversed;
1660         }
1661 
1662         /// <summary>
1663         ///     Set a DWORD property on an NCrypt object
1664         /// </summary>
1665         [System.Security.SecurityCritical]
SetProperty(SafeNCryptHandle ncryptObject, string propertyName, int value, CngPropertyOptions propertyOptions)1666         internal static void SetProperty(SafeNCryptHandle ncryptObject,
1667                                          string propertyName,
1668                                          int value,
1669                                          CngPropertyOptions propertyOptions) {
1670             Contract.Requires(ncryptObject != null);
1671             Contract.Requires(propertyName != null);
1672 
1673             SetProperty(ncryptObject, propertyName, BitConverter.GetBytes(value), propertyOptions);
1674         }
1675 
1676         /// <summary>
1677         ///     Set a string property
1678         /// </summary>
1679         [System.Security.SecurityCritical]
SetProperty(SafeNCryptHandle ncryptObject, string propertyName, string value, CngPropertyOptions propertyOptions)1680         internal static void SetProperty(SafeNCryptHandle ncryptObject,
1681                                          string propertyName,
1682                                          string value,
1683                                          CngPropertyOptions propertyOptions) {
1684             Contract.Requires(ncryptObject != null);
1685             Contract.Requires(propertyName != null);
1686 
1687             ErrorCode error = UnsafeNativeMethods.NCryptSetProperty(ncryptObject,
1688                                                                     propertyName,
1689                                                                     value,
1690                                                                     (value.Length + 1) * sizeof(char),
1691                                                                     propertyOptions);
1692 
1693             if (error != ErrorCode.Success) {
1694                 throw new CryptographicException((int)error);
1695             }
1696         }
1697 
1698         /// <summary>
1699         ///     Set a structure property
1700         /// </summary>
1701         [System.Security.SecurityCritical]
1702         internal static void SetProperty<T>(SafeNCryptHandle ncryptObject,
1703                                             string propertyName,
1704                                             T value,
1705                                             CngPropertyOptions propertyOptions) where T : struct {
1706             Contract.Requires(ncryptObject != null);
1707             Contract.Requires(propertyName != null);
1708 
1709             byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
1710 
1711             unsafe {
1712                 fixed (byte *pBuffer = buffer) {
1713 
1714                     bool marshaledStructure = false;
1715                     RuntimeHelpers.PrepareConstrainedRegions();
1716                     try {
1717                         // If we successfully marshal into the buffer, make sure to destroy the buffer when we're done
1718                         RuntimeHelpers.PrepareConstrainedRegions();
1719                         try { }
1720                         finally {
1721                             Marshal.StructureToPtr(value, new IntPtr(pBuffer), false);
1722                             marshaledStructure = true;
1723                         }
1724 
1725                         SetProperty(ncryptObject, propertyName, buffer, propertyOptions);
1726                     }
1727                     finally {
1728                         if (marshaledStructure) {
1729                             Marshal.DestroyStructure(new IntPtr(pBuffer), typeof(T));
1730                         }
1731                     }
1732                 }
1733             }
1734         }
1735 
1736         /// <summary>
1737         ///     Set a property on an NCrypt object
1738         /// </summary>
1739         [System.Security.SecurityCritical]
SetProperty(SafeNCryptHandle ncryptObject, string propertyName, byte[] value, CngPropertyOptions propertyOptions)1740         internal static void SetProperty(SafeNCryptHandle ncryptObject,
1741                                          string propertyName,
1742                                          byte[] value,
1743                                          CngPropertyOptions propertyOptions) {
1744             Contract.Requires(ncryptObject != null);
1745             Contract.Requires(propertyName != null);
1746 
1747             ErrorCode error = UnsafeNativeMethods.NCryptSetProperty(ncryptObject,
1748                                                                     propertyName,
1749                                                                     value,
1750                                                                     value != null ? value.Length : 0,
1751                                                                     propertyOptions);
1752 
1753             if (error != ErrorCode.Success) {
1754                 throw new CryptographicException((int)error);
1755             }
1756         }
1757 
1758         /// <summary>
1759         ///     Sign a hash using no padding
1760         /// </summary>
1761         [System.Security.SecurityCritical]
SignHash(SafeNCryptKeyHandle key, byte[] hash)1762         internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash) {
1763             Contract.Requires(key != null);
1764             Contract.Requires(hash != null);
1765             Contract.Ensures(Contract.Result<byte[]>() != null);
1766 
1767             // Figure out how big the signature is
1768             int signatureSize = 0;
1769             ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key,
1770                                                                  IntPtr.Zero,
1771                                                                  hash,
1772                                                                  hash.Length,
1773                                                                  null,
1774                                                                  0,
1775                                                                  out signatureSize,
1776                                                                  0);
1777 
1778             if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
1779                 throw new CryptographicException((int)error);
1780             }
1781 
1782             // Sign the data
1783             Debug.Assert(signatureSize > 0, "signatureSize > 0");
1784             byte[] signature = new byte[signatureSize];
1785 
1786             error = UnsafeNativeMethods.NCryptSignHash(key,
1787                                                        IntPtr.Zero,
1788                                                        hash,
1789                                                        hash.Length,
1790                                                        signature,
1791                                                        signature.Length,
1792                                                        out signatureSize,
1793                                                        0);
1794 
1795             if (error != ErrorCode.Success) {
1796                 throw new CryptographicException((int)error);
1797             }
1798 
1799             return signature;
1800         }
1801 
1802         /// <summary>
1803         ///     Sign a hash using no padding
1804         /// </summary>
1805         [System.Security.SecurityCritical]
SignHash(SafeNCryptKeyHandle key, byte[] hash, int expectedSize)1806         internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash, int expectedSize)
1807         {
1808             Contract.Requires(key != null);
1809             Contract.Requires(hash != null);
1810             Contract.Ensures(Contract.Result<byte[]>() != null);
1811 
1812 #if DEBUG
1813             expectedSize = 1;
1814 #endif
1815 
1816             // Figure out how big the signature is
1817             byte[] signature = new byte[expectedSize];
1818             int signatureSize = 0;
1819             ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key,
1820                                                                  IntPtr.Zero,
1821                                                                  hash,
1822                                                                  hash.Length,
1823                                                                  signature,
1824                                                                  signature.Length,
1825                                                                  out signatureSize,
1826                                                                  0);
1827 
1828             if (error == ErrorCode.BufferTooSmall)
1829             {
1830                 signature = new byte[signatureSize];
1831 
1832                 error = UnsafeNativeMethods.NCryptSignHash(key,
1833                                                            IntPtr.Zero,
1834                                                            hash,
1835                                                            hash.Length,
1836                                                            signature,
1837                                                            signature.Length,
1838                                                            out signatureSize,
1839                                                            0);
1840             }
1841 
1842             if (error != ErrorCode.Success)
1843             {
1844                 throw new CryptographicException((int)error);
1845             }
1846 
1847             Array.Resize(ref signature, signatureSize);
1848             return signature;
1849         }
1850 
1851         /// <summary>
1852         ///     Unpack a key blob in ECC public blob format into its X and Y parameters
1853         ///
1854         ///     This method expects that the blob be in the correct format -- blobs accepted from partially
1855         ///     trusted code need to be validated before being unpacked.
1856         /// </summary>
UnpackEccPublicBlob(byte[] blob, out BigInteger x, out BigInteger y)1857         internal static void UnpackEccPublicBlob(byte[] blob, out BigInteger x, out BigInteger y) {
1858             Contract.Requires(blob != null && blob.Length > 2 * sizeof(int));
1859 
1860             //
1861             // See code:System.Security.Cryptography.NCryptNative#ECCPublicBlobFormat  for details about the
1862             // format of the ECC public key blob.
1863             //
1864 
1865             // read the size of each parameter
1866             int parameterSize = BitConverter.ToInt32(blob, sizeof(int));
1867             Debug.Assert(parameterSize > 0, "parameterSize > 0");
1868             Debug.Assert(blob.Length >= 2 * sizeof(int) + 2 * parameterSize, "blob.Length >= 2 * sizeof(int) + 2 * parameterSize");
1869 
1870             // read out the X and Y parameters, in memory reversed form
1871             // add 0x00 padding to force BigInteger to interpret these as positive numbers
1872             x = new BigInteger(ReverseBytes(blob, 2 * sizeof(int), parameterSize, true));
1873             y = new BigInteger(ReverseBytes(blob, 2 * sizeof(int) + parameterSize, parameterSize, true));
1874         }
1875 
1876         /// <summary>
1877         ///     Verify a signature created with no padding
1878         /// </summary>
1879         [System.Security.SecurityCritical]
VerifySignature(SafeNCryptKeyHandle key, byte[] hash, byte[] signature)1880         internal static bool VerifySignature(SafeNCryptKeyHandle key, byte[] hash, byte[] signature) {
1881             Contract.Requires(key != null);
1882             Contract.Requires(hash != null);
1883             Contract.Requires(signature != null);
1884 
1885             ErrorCode error = UnsafeNativeMethods.NCryptVerifySignature(key,
1886                                                                         IntPtr.Zero,
1887                                                                         hash,
1888                                                                         hash.Length,
1889                                                                         signature,
1890                                                                         signature.Length,
1891                                                                         0);
1892 
1893             return error == ErrorCode.Success;
1894         }
1895     }
1896 #endif
1897 }
1898