1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 
7 using System;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Runtime.CompilerServices;
11 using System.Runtime.ConstrainedExecution;
12 using System.Runtime.InteropServices;
13 using System.Diagnostics.Contracts;
14 using Microsoft.Win32;
15 using Microsoft.Win32.SafeHandles;
16 using System.Security.Cryptography.X509Certificates;
17 
18 namespace System.Security.Cryptography {
19 
20     internal enum AsymmetricPaddingMode {
21         /// <summary>
22         ///     No padding
23         /// </summary>
24         None = 1,                       // BCRYPT_PAD_NONE
25 
26         /// <summary>
27         ///     PKCS #1 padding
28         /// </summary>
29         Pkcs1 = 2,                      // BCRYPT_PAD_PKCS1
30 
31         /// <summary>
32         ///     Optimal Asymmetric Encryption Padding
33         /// </summary>
34         Oaep = 4,                       // BCRYPT_PAD_OAEP
35 
36         /// <summary>
37         ///     Probabilistic Signature Scheme padding
38         /// </summary>
39         Pss = 8                         // BCRYPT_PAD_PSS
40     }
41 #if !MONO
42     [StructLayout(LayoutKind.Sequential)]
43     internal struct BCRYPT_DSA_KEY_BLOB_V2
44     {
45         public BCryptNative.KeyBlobMagicNumber dwMagic;  // BCRYPT_DSA_PUBLIC_MAGIC_V2 or BCRYPT_DSA_PRIVATE_MAGIC_V2
46         public int cbKey;               // key lengths in BYTES (e.g. for a 3072-bit key, cbKey = 3072/8 = 384)
47         public HASHALGORITHM_ENUM hashAlgorithm;
48         public DSAFIPSVERSION_ENUM standardVersion;
49         public int cbSeedLength;        // size (in bytes) of the seed value
50         public int cbGroupSize;         // size (in bytes) of the Q value
51         public byte Count3;             // # of iterations used to generate Q. In big-endian format.
52         public byte Count2;
53         public byte Count1;
54         public byte Count0;
55     }
56 #endif
57     internal enum HASHALGORITHM_ENUM : int
58     {
59         DSA_HASH_ALGORITHM_SHA1 = 0,
60         DSA_HASH_ALGORITHM_SHA256 = 1,
61         DSA_HASH_ALGORITHM_SHA512 = 2,
62     }
63 
64     internal enum DSAFIPSVERSION_ENUM : int
65     {
66         DSA_FIPS186_2 = 0,
67         DSA_FIPS186_3 = 1,
68     }
69 
70     /// <summary>
71     ///     Native interop with CNG's BCrypt layer. Native definitions can be found in bcrypt.h
72     /// </summary>
73     internal static class BCryptNative {
74         /// <summary>
75         ///     Well known algorithm names
76         /// </summary>
77         internal static class AlgorithmName {
78             public const string ECDH = "ECDH";                  // BCRYPT_ECDH_ALGORITHM
79             public const string ECDHP256 = "ECDH_P256";         // BCRYPT_ECDH_P256_ALGORITHM
80             public const string ECDHP384 = "ECDH_P384";         // BCRYPT_ECDH_P384_ALGORITHM
81             public const string ECDHP521 = "ECDH_P521";         // BCRYPT_ECDH_P521_ALGORITHM
82             public const string ECDsa = "ECDSA";                // BCRYPT_ECDSA_ALGORITHM
83             public const string ECDsaP256 = "ECDSA_P256";       // BCRYPT_ECDSA_P256_ALGORITHM
84             public const string ECDsaP384 = "ECDSA_P384";       // BCRYPT_ECDSA_P384_ALGORITHM
85             public const string ECDsaP521 = "ECDSA_P521";       // BCRYPT_ECDSA_P521_ALGORITHM
86             public const string MD5 = "MD5";                    // BCRYPT_MD5_ALGORITHM
87             public const string Sha1 = "SHA1";                  // BCRYPT_SHA1_ALGORITHM
88             public const string Sha256 = "SHA256";              // BCRYPT_SHA256_ALGORITHM
89             public const string Sha384 = "SHA384";              // BCRYPT_SHA384_ALGORITHM
90             public const string Sha512 = "SHA512";              // BCRYPT_SHA512_ALGORITHM
91             internal const string Rsa = "RSA";                  // BCRYPT_RSA_ALGORITHM
92         }
93 #if !MONO
94         /// <summary>
95         ///     Well known key blob tyes
96         /// </summary>
97         internal static class KeyBlobType {
98             //During Win8 Windows introduced  BCRYPT_PUBLIC_KEY_BLOB L"PUBLICBLOB"
99             //and #define BCRYPT_PRIVATE_KEY_BLOB L"PRIVATEBLOB". We should use the
100             //same on ProjectN and ProjectK
101             internal const string RsaFullPrivateBlob = "RSAFULLPRIVATEBLOB";    // BCRYPT_RSAFULLPRIVATE_BLOB
102             internal const string RsaPrivateBlob = "RSAPRIVATEBLOB";            // BCRYPT_RSAPRIVATE_BLOB
103             internal const string RsaPublicBlob = "RSAPUBLICBLOB";              // BCRYPT_PUBLIC_KEY_BLOB
104         }
105 
106         [StructLayout(LayoutKind.Sequential)]
107         internal struct BCRYPT_RSAKEY_BLOB {
108             internal KeyBlobMagicNumber Magic;
109             internal int BitLength;
110             internal int cbPublicExp;
111             internal int cbModulus;
112             internal int cbPrime1;
113             internal int cbPrime2;
114         }
115 
116         /// <summary>
117         ///     Result codes from BCrypt APIs
118         /// </summary>
119         internal enum ErrorCode {
120             Success = 0x00000000,                               // STATUS_SUCCESS
121             BufferToSmall = unchecked((int)0xC0000023),         // STATUS_BUFFER_TOO_SMALL
122             ObjectNameNotFound = unchecked((int)0xC0000034)     // SATUS_OBJECT_NAME_NOT_FOUND
123         }
124 
125         /// <summary>
126         ///     Well known BCrypt hash property names
127         /// </summary>
128         internal static class HashPropertyName {
129             public const string HashLength = "HashDigestLength";        // BCRYPT_HASH_LENGTH
130         }
131 
132         /// <summary>
133         ///     Magic numbers identifying blob types
134         /// </summary>
135         internal enum KeyBlobMagicNumber {
136             DsaPublic = 0x42505344,                             // BCRYPT_DSA_PUBLIC_MAGIC for key lengths <= 1024 bits
137             DsaPublicV2 = 0x32425044,                           // BCRYPT_DSA_PUBLIC_MAGIC_V2 for key lengths > 1024 bits
138             DsaPrivate = 0x56505344,                            // BCRYPT_DSA_PRIVATE_MAGIC for key lengths <= 1024 bits
139             DsaPrivateV2 = 0x32565044,                          // BCRYPT_DSA_PRIVATE_MAGIC_V2 for key lengths > 1024 bits
140             ECDHPublicP256 = 0x314B4345,                        // BCRYPT_ECDH_PUBLIC_P256_MAGIC
141             ECDHPublicP384 = 0x334B4345,                        // BCRYPT_ECDH_PUBLIC_P384_MAGIC
142             ECDHPublicP521 = 0x354B4345,                        // BCRYPT_ECDH_PUBLIC_P521_MAGIC
143             ECDsaPublicP256 = 0x31534345,                       // BCRYPT_ECDSA_PUBLIC_P256_MAGIC
144             ECDsaPublicP384 = 0x33534345,                       // BCRYPT_ECDSA_PUBLIC_P384_MAGIC
145             ECDsaPublicP521 = 0x35534345,                       // BCRYPT_ECDSA_PUBLIC_P521_MAGIC
146             RsaPublic = 0x31415352,                             // BCRYPT_RSAPUBLIC_MAGIC
147             RsaPrivate = 0x32415352,                            // BCRYPT_RSAPRIVATE_MAGIC
148             RsaFullPrivateMagic = 0x33415352,                    //BCRYPT_RSAFULLPRIVATE_MAGIC
149             KeyDataBlob = 0x4d42444b                            // BCRYPT_KEY_DATA_BLOB_MAGIC
150         }
151 
152         [StructLayout(LayoutKind.Sequential)]
153         internal struct BCRYPT_OAEP_PADDING_INFO {
154             [MarshalAs(UnmanagedType.LPWStr)]
155             internal string pszAlgId;
156 
157             internal IntPtr pbLabel;
158 
159             internal int cbLabel;
160         }
161 
162         [StructLayout(LayoutKind.Sequential)]
163         internal struct BCRYPT_PKCS1_PADDING_INFO {
164             [MarshalAs(UnmanagedType.LPWStr)]
165             internal string pszAlgId;
166         }
167 
168         [StructLayout(LayoutKind.Sequential)]
169         internal struct BCRYPT_PSS_PADDING_INFO {
170             [MarshalAs(UnmanagedType.LPWStr)]
171             internal string pszAlgId;
172 
173             internal int cbSalt;
174         }
175 
176         [StructLayout(LayoutKind.Sequential)]
177         private struct BCRYPT_KEY_DATA_BLOB_HEADER
178         {
179             public uint dwMagic;
180             public uint dwVersion;
181             public uint cbKeyData;
182 
183             public const uint BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b;
184             public const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x1;
185         }
186 
187         /// <summary>
188         ///     Well known KDF names
189         /// </summary>
190         internal static class KeyDerivationFunction {
191             public const string Hash = "HASH";                  // BCRYPT_KDF_HASH
192             public const string Hmac = "HMAC";                  // BCRYPT_KDF_HMAC
193             public const string Tls = "TLS_PRF";                // BCRYPT_KDF_TLS_PRF
194         }
195 
196         internal const string BCRYPT_ECCPUBLIC_BLOB = "ECCPUBLICBLOB";
197         internal const string BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB";
198 
199         internal const string BCRYPT_ECC_CURVE_NISTP256 = "nistP256";
200         internal const string BCRYPT_ECC_CURVE_NISTP384 = "nistP384";
201         internal const string BCRYPT_ECC_CURVE_NISTP521 = "nistP521";
202 
203         /// <summary>
204         ///     Well known BCrypt provider names
205         /// </summary>
206         internal static class ProviderName {
207             public const string MicrosoftPrimitiveProvider = "Microsoft Primitive Provider";    // MS_PRIMITIVE_PROVIDER
208         }
209 
210         /// <summary>
211         ///     Well known BCrypt object property names
212         /// </summary>
213         internal static class ObjectPropertyName {
214             public const string ObjectLength = "ObjectLength";          // BCRYPT_OBJECT_LENGTH
215         }
216 
217 #pragma warning disable 618    // Have not migrated to v4 transparency yet
218         [SecurityCritical(SecurityCriticalScope.Everything)]
219 #pragma warning restore 618
220         [SuppressUnmanagedCodeSecurity]
221         internal static class UnsafeNativeMethods {
222             /// <summary>
223             ///     Create a hash object
224             /// </summary>
225             [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm, [Out] out SafeBCryptHashHandle phHash, IntPtr pbHashObject, int cbHashObject, IntPtr pbSecret, int cbSecret, int dwFlags)226             internal static extern ErrorCode BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm,
227                                                               [Out] out SafeBCryptHashHandle phHash,
228                                                               IntPtr pbHashObject,
229                                                               int cbHashObject,
230                                                               IntPtr pbSecret,
231                                                               int cbSecret,
232                                                               int dwFlags);
233 
234             /// <summary>
235             ///     Get a property from a BCrypt algorithm object
236             /// </summary>
237             [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
BCryptGetProperty(SafeBCryptAlgorithmHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput, int cbOutput, [In, Out] ref int pcbResult, int flags)238             internal static extern ErrorCode BCryptGetProperty(SafeBCryptAlgorithmHandle hObject,
239                                                                string pszProperty,
240                                                                [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
241                                                                int cbOutput,
242                                                                [In, Out] ref int pcbResult,
243                                                                int flags);
244 
245             /// <summary>
246             ///     Get a property from a BCrypt algorithm object
247             /// </summary>
248             [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput, int cbOutput, [In, Out] ref int pcbResult, int flags)249             internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
250                                                                         string pszProperty,
251                                                                         [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
252                                                                         int cbOutput,
253                                                                         [In, Out] ref int pcbResult,
254                                                                         int flags);
255 
256             /// <summary>
257             ///     Get a property from a BCrypt hash object
258             /// </summary>
259             [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
BCryptGetHashProperty(SafeBCryptHashHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput, int cbOutput, [In, Out] ref int pcbResult, int flags)260             internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
261                                                                    string pszProperty,
262                                                                    [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
263                                                                    int cbOutput,
264                                                                    [In, Out] ref int pcbResult,
265                                                                    int flags);
266 
267             /// <summary>
268             ///     Get the hash value of the data
269             /// </summary>
270             [DllImport("bcrypt.dll")]
BCryptFinishHash(SafeBCryptHashHandle hHash, [MarshalAs(UnmanagedType.LPArray), Out] byte[] pbInput, int cbInput, int dwFlags)271             internal static extern ErrorCode BCryptFinishHash(SafeBCryptHashHandle hHash,
272                                                               [MarshalAs(UnmanagedType.LPArray), Out] byte[] pbInput,
273                                                               int cbInput,
274                                                               int dwFlags);
275 
276             /// <summary>
277             ///     Hash a block of data
278             /// </summary>
279             [DllImport("bcrypt.dll")]
BCryptHashData(SafeBCryptHashHandle hHash, [MarshalAs(UnmanagedType.LPArray), In] byte[] pbInput, int cbInput, int dwFlags)280             internal static extern ErrorCode BCryptHashData(SafeBCryptHashHandle hHash,
281                                                             [MarshalAs(UnmanagedType.LPArray), In] byte[] pbInput,
282                                                             int cbInput,
283                                                             int dwFlags);
284 
285             /// <summary>
286             ///     Get a handle to an algorithm provider
287             /// </summary>
288             [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm, string pszAlgId, string pszImplementation, int dwFlags)289             internal static extern ErrorCode BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm,
290                                                                          string pszAlgId,             // BCryptAlgorithm
291                                                                          string pszImplementation,    // ProviderNames
292                                                                          int dwFlags);
293 
294             [DllImport("bcrypt.dll", SetLastError = true)]
BCryptExportKey([In]SafeBCryptKeyHandle hKey, [In]IntPtr hExportKey, [In][MarshalAs(UnmanagedType.LPWStr)] string pszBlobType, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, [In]int cbOutput, [In]ref int pcbResult, [In] int dwFlags)295             internal static extern ErrorCode BCryptExportKey([In]SafeBCryptKeyHandle hKey,
296                                                              [In]IntPtr hExportKey,
297                                                              [In][MarshalAs(UnmanagedType.LPWStr)] string pszBlobType,
298                                                              [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
299                                                              [In]int cbOutput,
300                                                              [In]ref int pcbResult,
301                                                              [In] int dwFlags);
302 
303             [DllImport("Crypt32.dll", SetLastError = true)]
CryptImportPublicKeyInfoEx2([In] uint dwCertEncodingType, [In] ref X509Native.CERT_PUBLIC_KEY_INFO pInfo, [In] int dwFlags, [In] IntPtr pvAuxInfo, [Out] out SafeBCryptKeyHandle phKey)304             internal static extern int CryptImportPublicKeyInfoEx2([In] uint dwCertEncodingType,
305                                                    [In] ref X509Native.CERT_PUBLIC_KEY_INFO pInfo,
306                                                    [In] int dwFlags,
307                                                    [In] IntPtr pvAuxInfo,
308                                                    [Out] out SafeBCryptKeyHandle phKey);
309 
310             [DllImport("bcrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
BCryptImportKey( SafeBCryptAlgorithmHandle hAlgorithm, IntPtr hImportKey, string pszBlobType, out SafeBCryptKeyHandle hKey, IntPtr pbKeyObject, int cbKeyObject, byte[] pbInput, int cbInput, int dwFlags)311             internal static extern ErrorCode BCryptImportKey(
312                 SafeBCryptAlgorithmHandle hAlgorithm,
313                 IntPtr hImportKey,
314                 string pszBlobType,
315                 out SafeBCryptKeyHandle hKey,
316                 IntPtr pbKeyObject,
317                 int cbKeyObject,
318                 byte[] pbInput,
319                 int cbInput,
320                 int dwFlags);
321 
322             [DllImport("bcrypt.dll", SetLastError = true)]
BCryptEncrypt( SafeBCryptKeyHandle hKey, byte* pbInput, int cbInput, IntPtr paddingInfo, [In, Out] byte[] pbIV, int cbIV, byte* pbOutput, int cbOutput, out int cbResult, int dwFlags)323             public static extern unsafe ErrorCode BCryptEncrypt(
324                 SafeBCryptKeyHandle hKey,
325                 byte* pbInput,
326                 int cbInput,
327                 IntPtr paddingInfo,
328                 [In, Out] byte[] pbIV,
329                 int cbIV,
330                 byte* pbOutput,
331                 int cbOutput,
332                 out int cbResult,
333                 int dwFlags);
334 
335             [DllImport("bcrypt.dll", SetLastError = true)]
BCryptDecrypt( SafeBCryptKeyHandle hKey, byte* pbInput, int cbInput, IntPtr paddingInfo, [In, Out] byte[] pbIV, int cbIV, byte* pbOutput, int cbOutput, out int cbResult, int dwFlags)336             public static extern unsafe ErrorCode BCryptDecrypt(
337                 SafeBCryptKeyHandle hKey,
338                 byte* pbInput,
339                 int cbInput,
340                 IntPtr paddingInfo,
341                 [In, Out] byte[] pbIV,
342                 int cbIV,
343                 byte* pbOutput,
344                 int cbOutput,
345                 out int cbResult,
346                 int dwFlags);
347 
348             [DllImport("bcrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
BCryptSetProperty( SafeBCryptAlgorithmHandle hObject, string pszProperty, string pbInput, int cbInput, int dwFlags)349             public static extern ErrorCode BCryptSetProperty(
350                 SafeBCryptAlgorithmHandle hObject,
351                 string pszProperty,
352                 string pbInput,
353                 int cbInput,
354                 int dwFlags);
355         }
356 
357         [SecuritySafeCritical]
358         internal static class AesBCryptModes
359         {
360             [SecurityCritical]
361             private static readonly SafeBCryptAlgorithmHandle s_hAlgCbc = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_CBC);
362 
363             [SecurityCritical]
364             private static readonly SafeBCryptAlgorithmHandle s_hAlgEcb = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_ECB);
365 
GetSharedHandle(CipherMode cipherMode)366             internal static SafeBCryptAlgorithmHandle GetSharedHandle(CipherMode cipherMode)
367             {
368                 // Windows 8 added support to set the CipherMode value on a key,
369                 // but Windows 7 requires that it be set on the algorithm before key creation.
370                 switch (cipherMode)
371                 {
372                     case CipherMode.CBC:
373                         return s_hAlgCbc;
374                     case CipherMode.ECB:
375                         return s_hAlgEcb;
376                     default:
377                         throw new NotSupportedException();
378                 }
379             }
380 
OpenAesAlgorithm(string cipherMode)381             private static SafeBCryptAlgorithmHandle OpenAesAlgorithm(string cipherMode)
382             {
383                 const string BCRYPT_AES_ALGORITHM = "AES";
384                 SafeBCryptAlgorithmHandle hAlg = OpenAlgorithm(BCRYPT_AES_ALGORITHM, null);
385                 SetCipherMode(hAlg, cipherMode);
386 
387                 return hAlg;
388             }
389         }
390 
391         [SecuritySafeCritical]
392         internal static class TripleDesBCryptModes
393         {
394             [SecurityCritical]
395             private static readonly SafeBCryptAlgorithmHandle s_hAlgCbc = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_CBC);
396 
397             [SecurityCritical]
398             private static readonly SafeBCryptAlgorithmHandle s_hAlgEcb = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_ECB);
399 
GetSharedHandle(CipherMode cipherMode)400             internal static SafeBCryptAlgorithmHandle GetSharedHandle(CipherMode cipherMode)
401             {
402                 // Windows 8 added support to set the CipherMode value on a key,
403                 // but Windows 7 requires that it be set on the algorithm before key creation.
404                 switch (cipherMode)
405                 {
406                     case CipherMode.CBC:
407                         return s_hAlgCbc;
408                     case CipherMode.ECB:
409                         return s_hAlgEcb;
410                     default:
411                         throw new NotSupportedException();
412                 }
413             }
414 
OpenAesAlgorithm(string cipherMode)415             private static SafeBCryptAlgorithmHandle OpenAesAlgorithm(string cipherMode)
416             {
417                 const string BCRYPT_3DES_ALGORITHM = "3DES";
418                 SafeBCryptAlgorithmHandle hAlg = OpenAlgorithm(BCRYPT_3DES_ALGORITHM, null);
419                 SetCipherMode(hAlg, cipherMode);
420 
421                 return hAlg;
422             }
423         }
424 
425         //
426         // Wrapper and utility functions
427         //
428 
429         /// <summary>
430         ///     Adapter to wrap specific BCryptGetProperty P/Invokes with a generic handle type
431         /// </summary>
432 #pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
433         [SecurityCritical(SecurityCriticalScope.Everything)]
434 #pragma warning restore 618
435         private delegate ErrorCode BCryptPropertyGetter<T>(T hObject,
436                                                            string pszProperty,
437                                                            byte[] pbOutput,
438                                                            int cbOutput,
439                                                            ref int pcbResult,
440                                                            int dwFlags) where T : SafeHandle;
441 
442         private static volatile bool s_haveBcryptSupported;
443         private static volatile bool s_bcryptSupported;
444 
445         /// <summary>
446         ///     Determine if BCrypt is supported on the current machine
447         /// </summary>
448         internal static bool BCryptSupported {
449             [SecuritySafeCritical]
450             [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
451             get {
452                 if (!s_haveBcryptSupported)
453                 {
454                     // Attempt to load bcrypt.dll to see if the BCrypt CNG APIs are available on the machine
455                     using (SafeLibraryHandle bcrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("bcrypt", IntPtr.Zero, 0)) {
456                         s_bcryptSupported = !bcrypt.IsInvalid;
457                         s_haveBcryptSupported = true;
458                     }
459                 }
460 
461                 return s_bcryptSupported;
462             }
463         }
464 
465         /// <summary>
466         ///     Get the value of a DWORD property of a BCrypt object
467         /// </summary>
468         [System.Security.SecurityCritical]
469         internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle {
470             Contract.Requires(algorithm != null);
471             Contract.Requires(property == HashPropertyName.HashLength ||
472                               property == ObjectPropertyName.ObjectLength);
473 
474             return BitConverter.ToInt32(GetProperty(algorithm, property), 0);
475         }
476 
477         /// <summary>
478         ///     Get the value of a property of a BCrypt object
479         /// </summary>
480         [System.Security.SecurityCritical]
481         internal static byte[] GetProperty<T>(T algorithm, string property) where T : SafeHandle {
482             Contract.Requires(algorithm != null);
483             Contract.Requires(!String.IsNullOrEmpty(property));
484             Contract.Ensures(Contract.Result<byte[]>() != null);
485 
486             BCryptPropertyGetter<T> getter = null;
487             if (typeof(T) == typeof(SafeBCryptAlgorithmHandle)) {
488                 getter = new BCryptPropertyGetter<SafeBCryptAlgorithmHandle>(UnsafeNativeMethods.BCryptGetAlgorithmProperty)
489                     as BCryptPropertyGetter<T>;
490             }
491             else if (typeof(T) == typeof(SafeBCryptHashHandle)) {
492                 getter = new BCryptPropertyGetter<SafeBCryptHashHandle>(UnsafeNativeMethods.BCryptGetHashProperty)
493                     as BCryptPropertyGetter<T>;
494             }
495 
496             Debug.Assert(getter != null, "Unknown handle type");
497 
498             // Figure out how big the property is
499             int bufferSize = 0;
500             ErrorCode error = getter(algorithm, property, null, 0, ref bufferSize, 0);
501 
502             if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success) {
503                 throw new CryptographicException((int)error);
504             }
505 
506             // Allocate the buffer, and return the property
507             Debug.Assert(bufferSize > 0, "bufferSize > 0");
508             byte[] buffer = new byte[bufferSize];
509             error = getter(algorithm, property, buffer, buffer.Length, ref bufferSize, 0);
510 
511             if (error != ErrorCode.Success) {
512                 throw new CryptographicException((int)error);
513             }
514 
515             return buffer;
516         }
517 
518 
519         /// <summary>
520         ///     Map an algorithm identifier to a key size and magic number
521         /// </summary>
MapAlgorithmIdToMagic(string algorithm, out KeyBlobMagicNumber algorithmMagic, out int keySize)522         internal static void MapAlgorithmIdToMagic(string algorithm,
523                                                    out KeyBlobMagicNumber algorithmMagic,
524                                                    out int keySize) {
525             Contract.Requires(!String.IsNullOrEmpty(algorithm));
526 
527             switch (algorithm) {
528                 case AlgorithmName.ECDHP256:
529                     algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256;
530                     keySize = 256;
531                     break;
532 
533                 case AlgorithmName.ECDHP384:
534                     algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384;
535                     keySize = 384;
536                     break;
537 
538                 case AlgorithmName.ECDHP521:
539                     algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521;
540                     keySize = 521;
541                     break;
542 
543                 case AlgorithmName.ECDsaP256:
544                     algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256;
545                     keySize = 256;
546                     break;
547 
548                 case AlgorithmName.ECDsaP384:
549                     algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384;
550                     keySize = 384;
551                     break;
552 
553                 case AlgorithmName.ECDsaP521:
554                     algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521;
555                     keySize = 521;
556                     break;
557 
558                 default:
559                     throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm));
560             }
561         }
562 
563         /// <summary>
564         ///     Open a handle to an algorithm provider
565         /// </summary>
566         [System.Security.SecurityCritical]
OpenAlgorithm(string algorithm, string implementation)567         internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm, string implementation) {
568             Contract.Requires(!String.IsNullOrEmpty(algorithm));
569             Contract.Requires(!String.IsNullOrEmpty(implementation));
570             Contract.Ensures(Contract.Result<SafeBCryptAlgorithmHandle>() != null &&
571                              !Contract.Result<SafeBCryptAlgorithmHandle>().IsInvalid &&
572                              !Contract.Result<SafeBCryptAlgorithmHandle>().IsClosed);
573 
574             SafeBCryptAlgorithmHandle algorithmHandle = null;
575             ErrorCode error = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algorithmHandle,
576                                                                               algorithm,
577                                                                               implementation,
578                                                                               0);
579 
580             if (error != ErrorCode.Success) {
581                 throw new CryptographicException((int)error);
582             }
583 
584             return algorithmHandle;
585         }
586 
587         [SecuritySafeCritical]
ImportAsymmetricPublicKey(X509Native.CERT_PUBLIC_KEY_INFO certPublicKeyInfo, int dwFlag)588         internal static SafeBCryptKeyHandle ImportAsymmetricPublicKey(X509Native.CERT_PUBLIC_KEY_INFO certPublicKeyInfo, int dwFlag) {
589             SafeBCryptKeyHandle keyHandle = null;
590             int error = UnsafeNativeMethods.CryptImportPublicKeyInfoEx2(
591                                                         X509Native.X509_ASN_ENCODING,
592                                                         ref certPublicKeyInfo,
593                                                         dwFlag,
594                                                         IntPtr.Zero,
595                                                         out keyHandle);
596             if (error == 0) {
597                 throw new CryptographicException(Marshal.GetLastWin32Error());
598             }
599             return keyHandle;
600         }
601 
602         [SecuritySafeCritical]
ExportBCryptKey(SafeBCryptKeyHandle hKey, string blobType)603         internal static byte[] ExportBCryptKey(SafeBCryptKeyHandle hKey, string blobType) {
604             byte[] keyBlob = null;
605             int length = 0;
606 
607             ErrorCode error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, null, 0, ref length, 0);
608 
609             if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success)
610             {
611                 throw new CryptographicException(Marshal.GetLastWin32Error());
612             }
613 
614             keyBlob = new byte[length];
615             error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, keyBlob, length, ref length, 0);
616             if (error != ErrorCode.Success) {
617                 throw new CryptographicException(Marshal.GetLastWin32Error());
618             }
619             return keyBlob;
620         }
621 
622 
623         [SecuritySafeCritical]
BCryptImportKey(SafeBCryptAlgorithmHandle hAlg, byte[] key)624         internal static SafeBCryptKeyHandle BCryptImportKey(SafeBCryptAlgorithmHandle hAlg, byte[] key)
625         {
626             unsafe
627             {
628                 const String BCRYPT_KEY_DATA_BLOB = "KeyDataBlob";
629                 int keySize = key.Length;
630                 int blobSize = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) keySize;
631                 byte[] blob = new byte[blobSize];
632                 fixed (byte* pbBlob = blob)
633                 {
634                     BCRYPT_KEY_DATA_BLOB_HEADER* pBlob = (BCRYPT_KEY_DATA_BLOB_HEADER*)pbBlob;
635                     pBlob->dwMagic = BCRYPT_KEY_DATA_BLOB_HEADER.BCRYPT_KEY_DATA_BLOB_MAGIC;
636                     pBlob->dwVersion = BCRYPT_KEY_DATA_BLOB_HEADER.BCRYPT_KEY_DATA_BLOB_VERSION1;
637                     pBlob->cbKeyData = (uint)keySize;
638                 }
639                 Buffer.BlockCopy(key, 0, blob, sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), keySize);
640                 SafeBCryptKeyHandle hKey;
641 
642                 ErrorCode error = UnsafeNativeMethods.BCryptImportKey(
643                     hAlg,
644                     IntPtr.Zero,
645                     BCRYPT_KEY_DATA_BLOB,
646                     out hKey,
647                     IntPtr.Zero,
648                     0,
649                     blob,
650                     blobSize,
651                     0);
652 
653                 if (error != ErrorCode.Success)
654                     throw new CryptographicException((int)error);
655 
656                 return hKey;
657             }
658         }
659 
660         // Note: input and output are allowed to be the same buffer.
661         // BCryptEncrypt will correctly do the encryption in place according to CNG documentation.
662         [SecuritySafeCritical]
BCryptEncrypt( SafeBCryptKeyHandle hKey, byte[] input, int inputOffset, int inputCount, byte[] iv, byte[] output, int outputOffset, int outputCount)663         public static int BCryptEncrypt(
664             SafeBCryptKeyHandle hKey,
665             byte[] input,
666             int inputOffset,
667             int inputCount,
668             byte[] iv,
669             byte[] output,
670             int outputOffset,
671             int outputCount)
672         {
673             Debug.Assert(input != null);
674             Debug.Assert(inputOffset >= 0);
675             Debug.Assert(inputCount >= 0);
676             Debug.Assert(inputCount <= input.Length - inputOffset);
677             Debug.Assert(output != null);
678             Debug.Assert(outputOffset >= 0);
679             Debug.Assert(outputCount >= 0);
680             Debug.Assert(outputCount <= output.Length - outputOffset);
681 
682             unsafe
683             {
684                 fixed (byte* pbInput = input)
685                 {
686                     fixed (byte* pbOutput = output)
687                     {
688                         int cbResult;
689                         ErrorCode error = UnsafeNativeMethods.BCryptEncrypt(
690                             hKey,
691                             pbInput inputOffset,
692                             inputCount,
693                             IntPtr.Zero,
694                             iv,
695                             iv == null ? 0 : iv.Length,
696                             pbOutput outputOffset,
697                             outputCount,
698                             out cbResult,
699                             0);
700 
701                         if (error != ErrorCode.Success)
702                             throw new CryptographicException((int)error);
703 
704                         return cbResult;
705                     }
706                 }
707             }
708         }
709 
710         // Note: input and output are allowed to be the same buffer.
711         // BCryptDecrypt will correctly do the decryption in place according to CNG documentation.
712         [SecuritySafeCritical]
BCryptDecrypt( SafeBCryptKeyHandle hKey, byte[] input, int inputOffset, int inputCount, byte[] iv, byte[] output, int outputOffset, int outputCount)713         public static int BCryptDecrypt(
714             SafeBCryptKeyHandle hKey,
715             byte[] input,
716             int inputOffset,
717             int inputCount,
718             byte[] iv,
719             byte[] output,
720             int outputOffset,
721             int outputCount)
722         {
723             Debug.Assert(input != null);
724             Debug.Assert(inputOffset >= 0);
725             Debug.Assert(inputCount >= 0);
726             Debug.Assert(inputCount <= input.Length - inputOffset);
727             Debug.Assert(output != null);
728             Debug.Assert(outputOffset >= 0);
729             Debug.Assert(outputCount >= 0);
730             Debug.Assert(outputCount <= output.Length - outputOffset);
731 
732             unsafe
733             {
734                 fixed (byte* pbInput = input)
735                 {
736                     fixed (byte* pbOutput = output)
737                     {
738                         int cbResult;
739                         ErrorCode error = UnsafeNativeMethods.BCryptDecrypt(
740                             hKey,
741                             pbInput inputOffset,
742                             inputCount,
743                             IntPtr.Zero,
744                             iv,
745                             iv == null ? 0 : iv.Length,
746                             pbOutput outputOffset,
747                             outputCount,
748                             out cbResult,
749                              0);
750 
751                         if (error != ErrorCode.Success)
752                             throw new CryptographicException((int)error);
753 
754                         return cbResult;
755                     }
756                 }
757             }
758         }
759 
760         [SecurityCritical]
SetCipherMode(SafeBCryptAlgorithmHandle hAlg, string cipherMode)761         public static void SetCipherMode(SafeBCryptAlgorithmHandle hAlg, string cipherMode)
762         {
763             const string BCRYPT_CHAINING_MODE = "ChainingMode";
764 
765             ErrorCode error = UnsafeNativeMethods.BCryptSetProperty(
766                 hAlg,
767                 BCRYPT_CHAINING_MODE,
768                 cipherMode,
769                 // Explicit \0 terminator, UCS-2
770                 (cipherMode.Length 1) * 2,
771                 0);
772 
773             if (error != ErrorCode.Success)
774             {
775                 throw new CryptographicException((int)error);
776             }
777         }
778 #endif
779     }
780 }
781