/// <summary>
        /// Encrypts a block of data with cipher mode info specified.
        /// </summary>
        /// <param name="input">
        /// The input to be encrypted. 
        /// </param>
        /// <param name="nonce">
        /// Nonce.
        /// </param>
        /// <param name="authData">
        /// Auth data.
        /// </param>
        /// <param name="tagLength">
        /// Length of tag.
        /// </param>
        /// <param name="tag">
        /// Tag.
        /// </param>
        /// <returns>
        /// Encrypted output.
        /// </returns>
        public byte[] Encrypt(byte[] input, byte[] nonce, byte[] authData, int tagLength, out byte[] tag)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            ValidateKeyExists();

            tag = new byte[tagLength];

            IntPtr pbInfo = IntPtr.Zero;

            BcryptAuthenticatedCipherModeInfo info = new BcryptAuthenticatedCipherModeInfo();

            try
            {
                if (nonce != null || authData != null || tagLength > 0)
                {
                    info = new BcryptAuthenticatedCipherModeInfo
                    {
                        cbSize = (uint)Marshal.SizeOf(typeof(BcryptAuthenticatedCipherModeInfo)),
                        dwInfoVersion = 1,
                    };

                    if (nonce != null)
                    {
                        info.cbNonce = (uint)nonce.Length;
                        info.pbNonce = Marshal.AllocHGlobal(nonce.Length);
                        Marshal.Copy(nonce, 0, info.pbNonce, nonce.Length);
                    }

                    if (authData != null)
                    {
                        info.cbAuthData = (uint)authData.Length;
                        info.pbAuthData = Marshal.AllocHGlobal(authData.Length);
                        Marshal.Copy(authData, 0, info.pbAuthData, authData.Length);
                    }

                    if (tagLength > 0)
                    {
                        info.cbTag = (uint)tagLength;
                        info.pbTag = Marshal.AllocHGlobal(tagLength);
                    }

                    info.pbMacContext = IntPtr.Zero;
                    info.cbMacContext = 0;
                    info.cbAAD = 0;
                    info.cbData = 0;
                    info.dwFlags = 0;

                    pbInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(BcryptAuthenticatedCipherModeInfo)));
                    Marshal.StructureToPtr(info, pbInfo, false);
                }

                uint cbOutput;
                uint status = NativeMethods.BCryptEncrypt(
                    hKey,
                    input,
                    (uint)input.Length,
                    pbInfo,
                    null,
                    0,
                    null,
                    0,
                    out cbOutput,
                    0);
                if (status != (uint)NtStatus.STATUS_SUCCESS)
                {
                    throw new CryptographicException(GetErrorNameFromCode(status));
                }

                byte[] output = new byte[cbOutput];
                status = NativeMethods.BCryptEncrypt(
                    hKey,
                    input,
                    (uint)input.Length,
                    pbInfo,
                    null,
                    0,
                    output,
                    cbOutput,
                    out cbOutput,
                    0);
                if (status != (uint)NtStatus.STATUS_SUCCESS)
                {
                    throw new CryptographicException(GetErrorNameFromCode(status));
                }

                return output;
            }
            finally
            {
                if (pbInfo != IntPtr.Zero)
                {
                    Marshal.Copy(info.pbTag, tag, 0, tagLength);

                    Marshal.FreeHGlobal(info.pbNonce);
                    Marshal.FreeHGlobal(info.pbAuthData);
                    Marshal.FreeHGlobal(info.pbTag);
                    Marshal.FreeHGlobal(pbInfo);
                }
            }
        }
        /// <summary>
        /// Encrypts a block of data with cipher mode info specified.
        /// </summary>
        /// <param name="input">
        /// The input to be encrypted. 
        /// </param>
        /// <param name="nonce">
        /// Nonce.
        /// </param>
        /// <param name="authData">
        /// Auth data.
        /// </param>
        /// <param name="tagLength">
        /// Length of tag.
        /// </param>
        /// <param name="tag">
        /// Tag.
        /// </param>
        /// <returns>
        /// Encrypted output.
        /// </returns>
        public byte[] Encrypt(byte[] input, byte[] nonce, byte[] authData, int tagLength, out byte[] tag)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            ValidateKeyExists();

            tag = new byte[tagLength];

            IntPtr pbInfo = IntPtr.Zero;

            BcryptAuthenticatedCipherModeInfo info = new BcryptAuthenticatedCipherModeInfo();

            try
            {
                if (nonce != null || authData != null || tagLength > 0)
                {
                    info = new BcryptAuthenticatedCipherModeInfo
                    {
                        cbSize = (uint)Marshal.SizeOf(typeof(BcryptAuthenticatedCipherModeInfo)),
                        dwInfoVersion = 1,
                    };

                    if (nonce != null)
                    {
                        info.cbNonce = (uint)nonce.Length;
                        info.pbNonce = Marshal.AllocHGlobal(nonce.Length);
                        Marshal.Copy(nonce, 0, info.pbNonce, nonce.Length);
                    }

                    if (authData != null)
                    {
                        info.cbAuthData = (uint)authData.Length;
                        info.pbAuthData = Marshal.AllocHGlobal(authData.Length);
                        Marshal.Copy(authData, 0, info.pbAuthData, authData.Length);
                    }

                    if (tagLength > 0)
                    {
                        info.cbTag = (uint)tagLength;
                        info.pbTag = Marshal.AllocHGlobal(tagLength);
                    }

                    info.pbMacContext = IntPtr.Zero;
                    info.cbMacContext = 0;
                    info.cbAAD = 0;
                    info.cbData = 0;
                    info.dwFlags = 0;

                    pbInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(BcryptAuthenticatedCipherModeInfo)));
                    Marshal.StructureToPtr(info, pbInfo, false);
                }

                uint cbOutput;
                uint status = NativeMethods.BCryptEncrypt(
                    hKey,
                    input,
                    (uint)input.Length,
                    pbInfo,
                    null,
                    0,
                    null,
                    0,
                    out cbOutput,
                    0);
                if (status != (uint)NtStatus.STATUS_SUCCESS)
                {
                    throw new CryptographicException(GetErrorNameFromCode(status));
                }

                byte[] output = new byte[cbOutput];
                status = NativeMethods.BCryptEncrypt(
                    hKey,
                    input,
                    (uint)input.Length,
                    pbInfo,
                    null,
                    0,
                    output,
                    cbOutput,
                    out cbOutput,
                    0);
                if (status != (uint)NtStatus.STATUS_SUCCESS)
                {
                    throw new CryptographicException(GetErrorNameFromCode(status));
                }

                return output;
            }
            finally
            {
                if (pbInfo != IntPtr.Zero)
                {
                    Marshal.Copy(info.pbTag, tag, 0, tagLength);

                    Marshal.FreeHGlobal(info.pbNonce);
                    Marshal.FreeHGlobal(info.pbAuthData);
                    Marshal.FreeHGlobal(info.pbTag);
                    Marshal.FreeHGlobal(pbInfo);
                }
            }
        }