File: System\Security\Cryptography\BCryptNative.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
 
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Security.Cryptography.X509Certificates;
 
namespace System.Security.Cryptography {
 
    internal enum AsymmetricPaddingMode {
        /// <summary>
        ///     No padding
        /// </summary>
        None = 1,                       // BCRYPT_PAD_NONE
 
        /// <summary>
        ///     PKCS #1 padding
        /// </summary>
        Pkcs1 = 2,                      // BCRYPT_PAD_PKCS1
 
        /// <summary>
        ///     Optimal Asymmetric Encryption Padding
        /// </summary>
        Oaep = 4,                       // BCRYPT_PAD_OAEP
 
        /// <summary>
        ///     Probabilistic Signature Scheme padding
        /// </summary>
        Pss = 8                         // BCRYPT_PAD_PSS
    }
 
    [StructLayout(LayoutKind.Sequential)]
    internal struct BCRYPT_DSA_KEY_BLOB_V2
    {
        public BCryptNative.KeyBlobMagicNumber dwMagic;  // BCRYPT_DSA_PUBLIC_MAGIC_V2 or BCRYPT_DSA_PRIVATE_MAGIC_V2
        public int cbKey;               // key lengths in BYTES (e.g. for a 3072-bit key, cbKey = 3072/8 = 384)
        public HASHALGORITHM_ENUM hashAlgorithm;
        public DSAFIPSVERSION_ENUM standardVersion;
        public int cbSeedLength;        // size (in bytes) of the seed value
        public int cbGroupSize;         // size (in bytes) of the Q value
        public byte Count3;             // # of iterations used to generate Q. In big-endian format.
        public byte Count2;
        public byte Count1;
        public byte Count0;
    }
 
    internal enum HASHALGORITHM_ENUM : int
    {
        DSA_HASH_ALGORITHM_SHA1 = 0,
        DSA_HASH_ALGORITHM_SHA256 = 1,
        DSA_HASH_ALGORITHM_SHA512 = 2,
    }
 
    internal enum DSAFIPSVERSION_ENUM : int
    {
        DSA_FIPS186_2 = 0,
        DSA_FIPS186_3 = 1,
    }
 
    /// <summary>
    ///     Native interop with CNG's BCrypt layer. Native definitions can be found in bcrypt.h
    /// </summary>
    internal static class BCryptNative {
        /// <summary>
        ///     Well known algorithm names
        /// </summary>
        internal static class AlgorithmName {
            public const string ECDH = "ECDH";                  // BCRYPT_ECDH_ALGORITHM
            public const string ECDHP256 = "ECDH_P256";         // BCRYPT_ECDH_P256_ALGORITHM
            public const string ECDHP384 = "ECDH_P384";         // BCRYPT_ECDH_P384_ALGORITHM
            public const string ECDHP521 = "ECDH_P521";         // BCRYPT_ECDH_P521_ALGORITHM
            public const string ECDsa = "ECDSA";                // BCRYPT_ECDSA_ALGORITHM
            public const string ECDsaP256 = "ECDSA_P256";       // BCRYPT_ECDSA_P256_ALGORITHM
            public const string ECDsaP384 = "ECDSA_P384";       // BCRYPT_ECDSA_P384_ALGORITHM
            public const string ECDsaP521 = "ECDSA_P521";       // BCRYPT_ECDSA_P521_ALGORITHM
            public const string MD5 = "MD5";                    // BCRYPT_MD5_ALGORITHM
            public const string Sha1 = "SHA1";                  // BCRYPT_SHA1_ALGORITHM
            public const string Sha256 = "SHA256";              // BCRYPT_SHA256_ALGORITHM
            public const string Sha384 = "SHA384";              // BCRYPT_SHA384_ALGORITHM
            public const string Sha512 = "SHA512";              // BCRYPT_SHA512_ALGORITHM
            internal const string Rsa = "RSA";                  // BCRYPT_RSA_ALGORITHM
        }
 
        /// <summary>
        ///     Well known key blob tyes
        /// </summary>
        internal static class KeyBlobType {
            //During Win8 Windows introduced  BCRYPT_PUBLIC_KEY_BLOB L"PUBLICBLOB"  
            //and #define BCRYPT_PRIVATE_KEY_BLOB L"PRIVATEBLOB". We should use the 
            //same on ProjectN and ProjectK 
            internal const string RsaFullPrivateBlob = "RSAFULLPRIVATEBLOB";    // BCRYPT_RSAFULLPRIVATE_BLOB
            internal const string RsaPrivateBlob = "RSAPRIVATEBLOB";            // BCRYPT_RSAPRIVATE_BLOB
            internal const string RsaPublicBlob = "RSAPUBLICBLOB";              // BCRYPT_PUBLIC_KEY_BLOB
        }
 
        [StructLayout(LayoutKind.Sequential)]
        internal struct BCRYPT_RSAKEY_BLOB {
            internal KeyBlobMagicNumber Magic;
            internal int BitLength;
            internal int cbPublicExp;
            internal int cbModulus;
            internal int cbPrime1;
            internal int cbPrime2;
        }
 
        /// <summary>
        ///     Result codes from BCrypt APIs
        /// </summary>
        internal enum ErrorCode {
            Success = 0x00000000,                               // STATUS_SUCCESS
            BufferToSmall = unchecked((int)0xC0000023),         // STATUS_BUFFER_TOO_SMALL
            ObjectNameNotFound = unchecked((int)0xC0000034)     // SATUS_OBJECT_NAME_NOT_FOUND
        }
 
        /// <summary>
        ///     Well known BCrypt hash property names
        /// </summary>
        internal static class HashPropertyName {
            public const string HashLength = "HashDigestLength";        // BCRYPT_HASH_LENGTH
        }
 
        /// <summary>
        ///     Magic numbers identifying blob types
        /// </summary>
        internal enum KeyBlobMagicNumber {
            DsaPublic = 0x42505344,                             // BCRYPT_DSA_PUBLIC_MAGIC for key lengths <= 1024 bits
            DsaPublicV2 = 0x32425044,                           // BCRYPT_DSA_PUBLIC_MAGIC_V2 for key lengths > 1024 bits
            DsaPrivate = 0x56505344,                            // BCRYPT_DSA_PRIVATE_MAGIC for key lengths <= 1024 bits
            DsaPrivateV2 = 0x32565044,                          // BCRYPT_DSA_PRIVATE_MAGIC_V2 for key lengths > 1024 bits
            ECDHPublicP256 = 0x314B4345,                        // BCRYPT_ECDH_PUBLIC_P256_MAGIC
            ECDHPublicP384 = 0x334B4345,                        // BCRYPT_ECDH_PUBLIC_P384_MAGIC
            ECDHPublicP521 = 0x354B4345,                        // BCRYPT_ECDH_PUBLIC_P521_MAGIC
            ECDsaPublicP256 = 0x31534345,                       // BCRYPT_ECDSA_PUBLIC_P256_MAGIC
            ECDsaPublicP384 = 0x33534345,                       // BCRYPT_ECDSA_PUBLIC_P384_MAGIC
            ECDsaPublicP521 = 0x35534345,                       // BCRYPT_ECDSA_PUBLIC_P521_MAGIC
            RsaPublic = 0x31415352,                             // BCRYPT_RSAPUBLIC_MAGIC
            RsaPrivate = 0x32415352,                            // BCRYPT_RSAPRIVATE_MAGIC
            RsaFullPrivateMagic = 0x33415352,                    //BCRYPT_RSAFULLPRIVATE_MAGIC   
            KeyDataBlob = 0x4d42444b                            // BCRYPT_KEY_DATA_BLOB_MAGIC
        }
 
        [StructLayout(LayoutKind.Sequential)]
        internal struct BCRYPT_OAEP_PADDING_INFO {
            [MarshalAs(UnmanagedType.LPWStr)]
            internal string pszAlgId;
 
            internal IntPtr pbLabel;
 
            internal int cbLabel;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        internal struct BCRYPT_PKCS1_PADDING_INFO {
            [MarshalAs(UnmanagedType.LPWStr)]
            internal string pszAlgId;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        internal struct BCRYPT_PSS_PADDING_INFO {
            [MarshalAs(UnmanagedType.LPWStr)]
            internal string pszAlgId;
 
            internal int cbSalt;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct BCRYPT_KEY_DATA_BLOB_HEADER
        {
            public uint dwMagic;
            public uint dwVersion;
            public uint cbKeyData;
 
            public const uint BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b;
            public const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x1;
        }
 
        /// <summary>
        ///     Well known KDF names
        /// </summary>
        internal static class KeyDerivationFunction {
            public const string Hash = "HASH";                  // BCRYPT_KDF_HASH
            public const string Hmac = "HMAC";                  // BCRYPT_KDF_HMAC
            public const string Tls = "TLS_PRF";                // BCRYPT_KDF_TLS_PRF
        }
 
        internal const string BCRYPT_ECCPUBLIC_BLOB = "ECCPUBLICBLOB";
        internal const string BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB";
 
        internal const string BCRYPT_ECC_CURVE_NISTP256 = "nistP256";
        internal const string BCRYPT_ECC_CURVE_NISTP384 = "nistP384";
        internal const string BCRYPT_ECC_CURVE_NISTP521 = "nistP521";
 
        /// <summary>
        ///     Well known BCrypt provider names
        /// </summary>
        internal static class ProviderName {
            public const string MicrosoftPrimitiveProvider = "Microsoft Primitive Provider";    // MS_PRIMITIVE_PROVIDER
        }
 
        /// <summary>
        ///     Well known BCrypt object property names
        /// </summary>
        internal static class ObjectPropertyName {
            public const string ObjectLength = "ObjectLength";          // BCRYPT_OBJECT_LENGTH
        }
 
#pragma warning disable 618    // Have not migrated to v4 transparency yet
        [SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
        [SuppressUnmanagedCodeSecurity]
        internal static class UnsafeNativeMethods {
            /// <summary>
            ///     Create a hash object
            /// </summary>
            [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm,
                                                              [Out] out SafeBCryptHashHandle phHash,
                                                              IntPtr pbHashObject,
                                                              int cbHashObject,
                                                              IntPtr pbSecret,
                                                              int cbSecret,
                                                              int dwFlags);
 
            /// <summary>
            ///     Get a property from a BCrypt algorithm object
            /// </summary>
            [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptGetProperty(SafeBCryptAlgorithmHandle hObject,
                                                               string pszProperty,
                                                               [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
                                                               int cbOutput,
                                                               [In, Out] ref int pcbResult,
                                                               int flags);
 
            /// <summary>
            ///     Get a property from a BCrypt algorithm object
            /// </summary>
            [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptGetProperty(SafeBCryptKeyHandle hObject,
                                                               string pszProperty,
                                                               [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
                                                               int cbOutput,
                                                               [Out] out int pcbResult,
                                                               int flags);
 
            /// <summary>
            ///     Get a property from a BCrypt algorithm object
            /// </summary>
            [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
                                                                        string pszProperty,
                                                                        [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
                                                                        int cbOutput,
                                                                        [In, Out] ref int pcbResult,
                                                                        int flags);
 
            /// <summary>
            ///     Get a property from a BCrypt hash object
            /// </summary>
            [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
                                                                   string pszProperty,
                                                                   [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
                                                                   int cbOutput,
                                                                   [In, Out] ref int pcbResult,
                                                                   int flags);
 
            /// <summary>
            ///     Get the hash value of the data
            /// </summary>
            [DllImport("bcrypt.dll")]
            internal static extern ErrorCode BCryptFinishHash(SafeBCryptHashHandle hHash,
                                                              [MarshalAs(UnmanagedType.LPArray), Out] byte[] pbInput,
                                                              int cbInput,
                                                              int dwFlags);
 
            /// <summary>
            ///     Hash a block of data
            /// </summary>
            [DllImport("bcrypt.dll")]
            internal static extern unsafe ErrorCode BCryptHashData(SafeBCryptHashHandle hHash,
                                                            byte* pbInput,
                                                            int cbInput,
                                                            int dwFlags);
 
            /// <summary>
            ///     Get a handle to an algorithm provider
            /// </summary>
            [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm,
                                                                         string pszAlgId,             // BCryptAlgorithm
                                                                         string pszImplementation,    // ProviderNames
                                                                         int dwFlags);
 
            [DllImport("bcrypt.dll", SetLastError = true)]
            internal static extern ErrorCode BCryptExportKey([In]SafeBCryptKeyHandle hKey,
                                                             [In]IntPtr hExportKey,
                                                             [In][MarshalAs(UnmanagedType.LPWStr)] string pszBlobType,
                                                             [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
                                                             [In]int cbOutput,
                                                             [Out]out int pcbResult,
                                                             [In] int dwFlags);
 
            [DllImport("Crypt32.dll", SetLastError = true)]
            internal static extern int CryptImportPublicKeyInfoEx2([In] uint dwCertEncodingType,
                                                   [In] ref X509Native.CERT_PUBLIC_KEY_INFO pInfo,
                                                   [In] int dwFlags,
                                                   [In] IntPtr pvAuxInfo,
                                                   [Out] out SafeBCryptKeyHandle phKey);
 
            [DllImport("bcrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern ErrorCode BCryptImportKey(
                SafeBCryptAlgorithmHandle hAlgorithm,
                IntPtr hImportKey,
                string pszBlobType,
                out SafeBCryptKeyHandle hKey,
                IntPtr pbKeyObject,
                int cbKeyObject,
                byte[] pbInput,
                int cbInput,
                int dwFlags);
 
            [DllImport("bcrypt.dll", SetLastError = true)]
            public static extern unsafe ErrorCode BCryptEncrypt(
                SafeBCryptKeyHandle hKey,
                byte* pbInput,
                int cbInput,
                IntPtr paddingInfo,
                [In, Out] byte[] pbIV,
                int cbIV,
                byte* pbOutput,
                int cbOutput,
                out int cbResult,
                int dwFlags);
 
            [DllImport("bcrypt.dll", SetLastError = true)]
            public static extern unsafe ErrorCode BCryptDecrypt(
                SafeBCryptKeyHandle hKey,
                byte* pbInput,
                int cbInput,
                IntPtr paddingInfo,
                [In, Out] byte[] pbIV,
                int cbIV,
                byte* pbOutput,
                int cbOutput,
                out int cbResult,
                int dwFlags);
 
            [DllImport("bcrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern ErrorCode BCryptSetProperty(
                SafeBCryptAlgorithmHandle hObject,
                string pszProperty,
                string pbInput,
                int cbInput,
                int dwFlags);
        }
 
        [SecuritySafeCritical]
        internal static class AesBCryptModes
        {
            [SecurityCritical]
            private static readonly SafeBCryptAlgorithmHandle s_hAlgCbc = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_CBC);
 
            [SecurityCritical]
            private static readonly SafeBCryptAlgorithmHandle s_hAlgEcb = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_ECB);
 
            internal static SafeBCryptAlgorithmHandle GetSharedHandle(CipherMode cipherMode)
            {
                // Windows 8 added support to set the CipherMode value on a key,
                // but Windows 7 requires that it be set on the algorithm before key creation.
                switch (cipherMode)
                {
                    case CipherMode.CBC:
                        return s_hAlgCbc;
                    case CipherMode.ECB:
                        return s_hAlgEcb;
                    default:
                        throw new NotSupportedException();
                }
            }
 
            private static SafeBCryptAlgorithmHandle OpenAesAlgorithm(string cipherMode)
            {
                const string BCRYPT_AES_ALGORITHM = "AES";
                SafeBCryptAlgorithmHandle hAlg = OpenAlgorithm(BCRYPT_AES_ALGORITHM, null);
                SetCipherMode(hAlg, cipherMode);
 
                return hAlg;
            }
        }
 
        [SecuritySafeCritical]
        internal static class TripleDesBCryptModes
        {
            [SecurityCritical]
            private static readonly SafeBCryptAlgorithmHandle s_hAlgCbc = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_CBC);
 
            [SecurityCritical]
            private static readonly SafeBCryptAlgorithmHandle s_hAlgEcb = OpenAesAlgorithm(Interop.BCrypt.BCRYPT_CHAIN_MODE_ECB);
 
            internal static SafeBCryptAlgorithmHandle GetSharedHandle(CipherMode cipherMode)
            {
                // Windows 8 added support to set the CipherMode value on a key,
                // but Windows 7 requires that it be set on the algorithm before key creation.
                switch (cipherMode)
                {
                    case CipherMode.CBC:
                        return s_hAlgCbc;
                    case CipherMode.ECB:
                        return s_hAlgEcb;
                    default:
                        throw new NotSupportedException();
                }
            }
 
            private static SafeBCryptAlgorithmHandle OpenAesAlgorithm(string cipherMode)
            {
                const string BCRYPT_3DES_ALGORITHM = "3DES";
                SafeBCryptAlgorithmHandle hAlg = OpenAlgorithm(BCRYPT_3DES_ALGORITHM, null);
                SetCipherMode(hAlg, cipherMode);
 
                return hAlg;
            }
        }
 
        //
        // Wrapper and utility functions
        //
 
        /// <summary>
        ///     Adapter to wrap specific BCryptGetProperty P/Invokes with a generic handle type
        /// </summary>
#pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
        [SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
        private delegate ErrorCode BCryptPropertyGetter<T>(T hObject,
                                                           string pszProperty,
                                                           byte[] pbOutput,
                                                           int cbOutput,
                                                           ref int pcbResult,
                                                           int dwFlags) where T : SafeHandle;
 
        private static volatile bool s_haveBcryptSupported;
        private static volatile bool s_bcryptSupported;
 
        /// <summary>
        ///     Determine if BCrypt is supported on the current machine
        /// </summary>
        internal static bool BCryptSupported {
            [SecuritySafeCritical]
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
            get {
                if (!s_haveBcryptSupported)
                {
                    // Attempt to load bcrypt.dll to see if the BCrypt CNG APIs are available on the machine
                    using (SafeLibraryHandle bcrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("bcrypt", IntPtr.Zero, 0)) {
                        s_bcryptSupported = !bcrypt.IsInvalid;
                        s_haveBcryptSupported = true;
                    }
                }
 
                return s_bcryptSupported;
            }
        }
 
        /// <summary>
        ///     Get the value of a DWORD property of a BCrypt object
        /// </summary>
        [System.Security.SecurityCritical]
        internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle {
            Contract.Requires(algorithm != null);
            Contract.Requires(property == HashPropertyName.HashLength ||
                              property == ObjectPropertyName.ObjectLength);
 
            return BitConverter.ToInt32(GetProperty(algorithm, property), 0);
        }
 
        /// <summary>
        ///     Get the value of a property of a BCrypt object
        /// </summary>
        [System.Security.SecurityCritical]
        internal static byte[] GetProperty<T>(T algorithm, string property) where T : SafeHandle {
            Contract.Requires(algorithm != null);
            Contract.Requires(!String.IsNullOrEmpty(property));
            Contract.Ensures(Contract.Result<byte[]>() != null);
 
            BCryptPropertyGetter<T> getter = null;
            if (typeof(T) == typeof(SafeBCryptAlgorithmHandle)) {
                getter = new BCryptPropertyGetter<SafeBCryptAlgorithmHandle>(UnsafeNativeMethods.BCryptGetAlgorithmProperty)
                    as BCryptPropertyGetter<T>;
            }
            else if (typeof(T) == typeof(SafeBCryptHashHandle)) {
                getter = new BCryptPropertyGetter<SafeBCryptHashHandle>(UnsafeNativeMethods.BCryptGetHashProperty)
                    as BCryptPropertyGetter<T>;
            }
 
            Debug.Assert(getter != null, "Unknown handle type");
 
            // Figure out how big the property is
            int bufferSize = 0;
            ErrorCode error = getter(algorithm, property, null, 0, ref bufferSize, 0);
 
            if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success) {
                throw new CryptographicException((int)error);
            }
 
            // Allocate the buffer, and return the property
            Debug.Assert(bufferSize > 0, "bufferSize > 0");
            byte[] buffer = new byte[bufferSize];
            error = getter(algorithm, property, buffer, buffer.Length, ref bufferSize, 0);
 
            if (error != ErrorCode.Success) {
                throw new CryptographicException((int)error);
            }
 
            return buffer;
        }
 
 
        /// <summary>
        ///     Map an algorithm identifier to a key size and magic number
        /// </summary>
        internal static void MapAlgorithmIdToMagic(string algorithm,
                                                   out KeyBlobMagicNumber algorithmMagic,
                                                   out int keySize) {
            Contract.Requires(!String.IsNullOrEmpty(algorithm));
 
            switch (algorithm) {
                case AlgorithmName.ECDHP256:
                    algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256;
                    keySize = 256;
                    break;
 
                case AlgorithmName.ECDHP384:
                    algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384;
                    keySize = 384;
                    break;
 
                case AlgorithmName.ECDHP521:
                    algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521;
                    keySize = 521;
                    break;
 
                case AlgorithmName.ECDsaP256:
                    algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256;
                    keySize = 256;
                    break;
 
                case AlgorithmName.ECDsaP384:
                    algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384;
                    keySize = 384;
                    break;
 
                case AlgorithmName.ECDsaP521:
                    algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521;
                    keySize = 521;
                    break;
 
                default:
                    throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm));
            }
        }
 
        /// <summary>
        ///     Open a handle to an algorithm provider
        /// </summary>
        [System.Security.SecurityCritical]
        internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm, string implementation) {
            Contract.Requires(!String.IsNullOrEmpty(algorithm));
            Contract.Requires(!String.IsNullOrEmpty(implementation));
            Contract.Ensures(Contract.Result<SafeBCryptAlgorithmHandle>() != null &&
                             !Contract.Result<SafeBCryptAlgorithmHandle>().IsInvalid &&
                             !Contract.Result<SafeBCryptAlgorithmHandle>().IsClosed);
 
            SafeBCryptAlgorithmHandle algorithmHandle = null;
            ErrorCode error = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algorithmHandle,
                                                                              algorithm,
                                                                              implementation,
                                                                              0);
 
            if (error != ErrorCode.Success) {
                throw new CryptographicException((int)error);
            }
 
            return algorithmHandle;
        }
 
        [SecuritySafeCritical]
        internal static SafeBCryptKeyHandle ImportAsymmetricPublicKey(X509Native.CERT_PUBLIC_KEY_INFO certPublicKeyInfo, int dwFlag) {
            SafeBCryptKeyHandle keyHandle = null;            
            int error = UnsafeNativeMethods.CryptImportPublicKeyInfoEx2(
                                                        X509Native.X509_ASN_ENCODING,
                                                        ref certPublicKeyInfo,
                                                        dwFlag,
                                                        IntPtr.Zero,
                                                        out keyHandle);
            if (error == 0) {
                throw new CryptographicException(Marshal.GetLastWin32Error());
            }
            return keyHandle;
        }
 
        [SecuritySafeCritical]
        internal static byte[] ExportBCryptKey(SafeBCryptKeyHandle hKey, string blobType) {
            byte[] keyBlob = null;
            int length;
 
            ErrorCode error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, null, 0, out length, 0);
 
            if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success)
            {
                throw new CryptographicException(Marshal.GetLastWin32Error());
            }
 
            keyBlob = new byte[length];
            error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, keyBlob, length, out length, 0);
            if (error != ErrorCode.Success) {
                throw new CryptographicException(Marshal.GetLastWin32Error());
            }
            return keyBlob;
        }
 
        [SecuritySafeCritical]
        internal static SafeBCryptKeyHandle BCryptImportKey(SafeBCryptAlgorithmHandle hAlg, byte[] key)
        {
            unsafe
            {
                const String BCRYPT_KEY_DATA_BLOB = "KeyDataBlob";
                int keySize = key.Length;
                int blobSize = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + keySize;
                byte[] blob = new byte[blobSize];
                fixed (byte* pbBlob = blob)
                {
                    BCRYPT_KEY_DATA_BLOB_HEADER* pBlob = (BCRYPT_KEY_DATA_BLOB_HEADER*)pbBlob;
                    pBlob->dwMagic = BCRYPT_KEY_DATA_BLOB_HEADER.BCRYPT_KEY_DATA_BLOB_MAGIC;
                    pBlob->dwVersion = BCRYPT_KEY_DATA_BLOB_HEADER.BCRYPT_KEY_DATA_BLOB_VERSION1;
                    pBlob->cbKeyData = (uint)keySize;
                }
                Buffer.BlockCopy(key, 0, blob, sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), keySize);
                SafeBCryptKeyHandle hKey;
 
                ErrorCode error = UnsafeNativeMethods.BCryptImportKey(
                    hAlg,
                    IntPtr.Zero,
                    BCRYPT_KEY_DATA_BLOB,
                    out hKey,
                    IntPtr.Zero,
                    0,
                    blob,
                    blobSize,
                    0);
 
                if (error != ErrorCode.Success)
                    throw new CryptographicException((int)error);
 
                return hKey;
            }
        }
 
        // Note: input and output are allowed to be the same buffer.
        // BCryptEncrypt will correctly do the encryption in place according to CNG documentation.
        [SecuritySafeCritical]
        public static int BCryptEncrypt(
            SafeBCryptKeyHandle hKey,
            byte[] input,
            int inputOffset,
            int inputCount,
            byte[] iv,
            byte[] output,
            int outputOffset,
            int outputCount)
        {
            Debug.Assert(input != null);
            Debug.Assert(inputOffset >= 0);
            Debug.Assert(inputCount >= 0);
            Debug.Assert(inputCount <= input.Length - inputOffset);
            Debug.Assert(output != null);
            Debug.Assert(outputOffset >= 0);
            Debug.Assert(outputCount >= 0);
            Debug.Assert(outputCount <= output.Length - outputOffset);
 
            unsafe
            {
                fixed (byte* pbInput = input)
                {
                    fixed (byte* pbOutput = output)
                    {
                        int cbResult;
                        ErrorCode error = UnsafeNativeMethods.BCryptEncrypt(
                            hKey,
                            pbInput + inputOffset,
                            inputCount,
                            IntPtr.Zero,
                            iv,
                            iv == null ? 0 : iv.Length,
                            pbOutput + outputOffset,
                            outputCount,
                            out cbResult,
                            0);
 
                        if (error != ErrorCode.Success)
                            throw new CryptographicException((int)error);
 
                        return cbResult;
                    }
                }
            }
        }
 
        // Note: input and output are allowed to be the same buffer.
        // BCryptDecrypt will correctly do the decryption in place according to CNG documentation.
        [SecuritySafeCritical]
        public static int BCryptDecrypt(
            SafeBCryptKeyHandle hKey,
            byte[] input,
            int inputOffset,
            int inputCount,
            byte[] iv,
            byte[] output,
            int outputOffset,
            int outputCount)
        {
            Debug.Assert(input != null);
            Debug.Assert(inputOffset >= 0);
            Debug.Assert(inputCount >= 0);
            Debug.Assert(inputCount <= input.Length - inputOffset);
            Debug.Assert(output != null);
            Debug.Assert(outputOffset >= 0);
            Debug.Assert(outputCount >= 0);
            Debug.Assert(outputCount <= output.Length - outputOffset);
 
            unsafe
            {
                fixed (byte* pbInput = input)
                {
                    fixed (byte* pbOutput = output)
                    {
                        int cbResult;
                        ErrorCode error = UnsafeNativeMethods.BCryptDecrypt(
                            hKey,
                            pbInput + inputOffset,
                            inputCount,
                            IntPtr.Zero,
                            iv,
                            iv == null ? 0 : iv.Length,
                            pbOutput + outputOffset,
                            outputCount,
                            out cbResult,
                            0);
 
                        if (error != ErrorCode.Success)
                            throw new CryptographicException((int)error);
 
                        return cbResult;
                    }
                }
            }
        }
 
        [SecurityCritical]
        public static void SetCipherMode(SafeBCryptAlgorithmHandle hAlg, string cipherMode)
        {
            const string BCRYPT_CHAINING_MODE = "ChainingMode";
 
            ErrorCode error = UnsafeNativeMethods.BCryptSetProperty(
                hAlg,
                BCRYPT_CHAINING_MODE,
                cipherMode,
                // Explicit \0 terminator, UCS-2
                (cipherMode.Length + 1) * 2,
                0);
 
            if (error != ErrorCode.Success)
            {
                throw new CryptographicException((int)error);
            }
        }
    }
}