diff --git a/DataProtection.sln b/DataProtection.sln new file mode 100644 index 00000000000..e33ff9ff386 --- /dev/null +++ b/DataProtection.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Security.DataProtection.net45", "src\Microsoft.AspNet.Security.DataProtection\Microsoft.AspNet.Security.DataProtection.net45.csproj", "{106E3A4A-BD7A-40DC-90D3-2E0683D1E525}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Security.DataProtection.k10", "src\Microsoft.AspNet.Security.DataProtection\Microsoft.AspNet.Security.DataProtection.k10.csproj", "{E646E4FE-167B-42EB-831B-BBC2AB07C3AC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {106E3A4A-BD7A-40DC-90D3-2E0683D1E525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {106E3A4A-BD7A-40DC-90D3-2E0683D1E525}.Debug|Any CPU.Build.0 = Debug|Any CPU + {106E3A4A-BD7A-40DC-90D3-2E0683D1E525}.Release|Any CPU.ActiveCfg = Release|Any CPU + {106E3A4A-BD7A-40DC-90D3-2E0683D1E525}.Release|Any CPU.Build.0 = Release|Any CPU + {E646E4FE-167B-42EB-831B-BBC2AB07C3AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E646E4FE-167B-42EB-831B-BBC2AB07C3AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E646E4FE-167B-42EB-831B-BBC2AB07C3AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E646E4FE-167B-42EB-831B-BBC2AB07C3AC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E646E4FE-167B-42EB-831B-BBC2AB07C3AC} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA} + {106E3A4A-BD7A-40DC-90D3-2E0683D1E525} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA} + EndGlobalSection +EndGlobal diff --git a/DataProtection.sln.DotSettings b/DataProtection.sln.DotSettings new file mode 100644 index 00000000000..c843b27a2b6 --- /dev/null +++ b/DataProtection.sln.DotSettings @@ -0,0 +1,2 @@ + + False \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs b/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs index 850a7415f7d..58985150bb6 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/Algorithms.cs @@ -1,65 +1,75 @@ using System; using System.Security.Cryptography; -namespace Microsoft.AspNet.Security.DataProtection { - - internal unsafe static class Algorithms { - +namespace Microsoft.AspNet.Security.DataProtection +{ + internal static unsafe class Algorithms + { public static readonly BCryptAlgorithmHandle AESAlgorithmHandle = CreateAESAlgorithmHandle(); public static readonly BCryptAlgorithmHandle HMACSHA256AlgorithmHandle = CreateHMACSHA256AlgorithmHandle(); public static readonly BCryptAlgorithmHandle HMACSHA512AlgorithmHandle = CreateHMACSHA512AlgorithmHandle(); public static readonly BCryptAlgorithmHandle SP800108AlgorithmHandle = CreateSP800108AlgorithmHandle(); - private static BCryptAlgorithmHandle CreateAESAlgorithmHandle() { + private static BCryptAlgorithmHandle CreateAESAlgorithmHandle() + { // create the AES instance BCryptAlgorithmHandle algHandle; int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, Constants.BCRYPT_AES_ALGORITHM, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: 0); - if (status != 0 || algHandle == null || algHandle.IsInvalid) { + if (status != 0 || algHandle == null || algHandle.IsInvalid) + { throw new CryptographicException(status); } // change it to use CBC chaining; it already uses PKCS7 padding by default - fixed (char* pCbcMode = Constants.BCRYPT_CHAIN_MODE_CBC) { - status = UnsafeNativeMethods.BCryptSetProperty(algHandle, Constants.BCRYPT_CHAINING_MODE, (IntPtr)pCbcMode, (uint)((Constants.BCRYPT_CHAIN_MODE_CBC.Length + 1 /* trailing null */) * sizeof(char)), dwFlags: 0); + fixed (char* pCbcMode = Constants.BCRYPT_CHAIN_MODE_CBC) + { + status = UnsafeNativeMethods.BCryptSetProperty(algHandle, Constants.BCRYPT_CHAINING_MODE, (IntPtr) pCbcMode, (uint) ((Constants.BCRYPT_CHAIN_MODE_CBC.Length + 1 /* trailing null */)*sizeof (char)), dwFlags: 0); } - if (status != 0) { + if (status != 0) + { throw new CryptographicException(status); } return algHandle; } - private static BCryptAlgorithmHandle CreateHMACSHA256AlgorithmHandle() { + + private static BCryptAlgorithmHandle CreateHMACSHA256AlgorithmHandle() + { // create the HMACSHA-256 instance BCryptAlgorithmHandle algHandle; int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, Constants.BCRYPT_SHA256_ALGORITHM, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: BCryptAlgorithmFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG); - if (status != 0 || algHandle == null || algHandle.IsInvalid) { + if (status != 0 || algHandle == null || algHandle.IsInvalid) + { throw new CryptographicException(status); } return algHandle; } - private static BCryptAlgorithmHandle CreateHMACSHA512AlgorithmHandle() { + private static BCryptAlgorithmHandle CreateHMACSHA512AlgorithmHandle() + { // create the HMACSHA-512 instance BCryptAlgorithmHandle algHandle; int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, Constants.BCRYPT_SHA512_ALGORITHM, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: BCryptAlgorithmFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG); - if (status != 0 || algHandle == null || algHandle.IsInvalid) { + if (status != 0 || algHandle == null || algHandle.IsInvalid) + { throw new CryptographicException(status); } return algHandle; } - private static BCryptAlgorithmHandle CreateSP800108AlgorithmHandle() { + private static BCryptAlgorithmHandle CreateSP800108AlgorithmHandle() + { // create the SP800-108 instance BCryptAlgorithmHandle algHandle; int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, Constants.BCRYPT_SP800108_CTR_HMAC_ALGORITHM, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: 0); - if (status != 0 || algHandle == null || algHandle.IsInvalid) { + if (status != 0 || algHandle == null || algHandle.IsInvalid) + { throw new CryptographicException(status); } return algHandle; } - } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCRYPT_KEY_DATA_BLOB_HEADER.cs b/src/Microsoft.AspNet.Security.DataProtection/BCRYPT_KEY_DATA_BLOB_HEADER.cs index 25fbecc1d2b..3bc50731dff 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCRYPT_KEY_DATA_BLOB_HEADER.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCRYPT_KEY_DATA_BLOB_HEADER.cs @@ -2,10 +2,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375524(v=vs.85).aspx [StructLayout(LayoutKind.Sequential)] - internal struct BCRYPT_KEY_DATA_BLOB_HEADER { + internal struct BCRYPT_KEY_DATA_BLOB_HEADER + { // from bcrypt.h private const uint BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b; //Key Data Blob Magic (KDBM) private const uint BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x1; @@ -15,9 +17,10 @@ namespace Microsoft.AspNet.Security.DataProtection { public uint cbKeyData; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Initialize(ref BCRYPT_KEY_DATA_BLOB_HEADER pHeader) { + public static void Initialize(ref BCRYPT_KEY_DATA_BLOB_HEADER pHeader) + { pHeader.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; pHeader.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmFlags.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmFlags.cs index aa091e9c25f..25bbe91cfa3 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmFlags.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmFlags.cs @@ -1,11 +1,13 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // from bcrypt.h [Flags] - internal enum BCryptAlgorithmFlags { + internal enum BCryptAlgorithmFlags + { BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008, BCRYPT_CAPI_AES_FLAG = 0x00000010, BCRYPT_HASH_REUSABLE_FLAG = 0x00000020, } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmHandle.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmHandle.cs index 6346215a9a7..f5a54fa9f8f 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmHandle.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptAlgorithmHandle.cs @@ -1,16 +1,20 @@ using System; using Microsoft.Win32.SafeHandles; -namespace Microsoft.AspNet.Security.DataProtection { - internal sealed class BCryptAlgorithmHandle : SafeHandleZeroOrMinusOneIsInvalid { +namespace Microsoft.AspNet.Security.DataProtection +{ + internal sealed class BCryptAlgorithmHandle : SafeHandleZeroOrMinusOneIsInvalid + { // Called by P/Invoke when returning SafeHandles private BCryptAlgorithmHandle() - : base(ownsHandle: true) { + : base(ownsHandle: true) + { } // Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you. - protected override bool ReleaseHandle() { + protected override bool ReleaseHandle() + { return (UnsafeNativeMethods.BCryptCloseAlgorithmProvider(handle, dwFlags: 0) == 0); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptBuffer.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptBuffer.cs index 3ffbc5156f6..0a73118bbb4 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptBuffer.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptBuffer.cs @@ -2,12 +2,14 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375368(v=vs.85).aspx [StructLayout(LayoutKind.Sequential)] - internal struct BCryptBuffer { + internal struct BCryptBuffer + { public uint cbBuffer; // Length of buffer, in bytes public BCryptKeyDerivationBufferType BufferType; // Buffer type public IntPtr pvBuffer; // Pointer to buffer } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptBufferDesc.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptBufferDesc.cs index 0e5336f4969..32eed76657f 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptBufferDesc.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptBufferDesc.cs @@ -2,10 +2,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375370(v=vs.85).aspx [StructLayout(LayoutKind.Sequential)] - internal unsafe struct BCryptBufferDesc { + internal unsafe struct BCryptBufferDesc + { private const int BCRYPTBUFFER_VERSION = 0; public uint ulVersion; // Version number @@ -13,8 +15,9 @@ namespace Microsoft.AspNet.Security.DataProtection { public BCryptBuffer* pBuffers; // Pointer to array of buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Initialize(ref BCryptBufferDesc bufferDesc) { + public static void Initialize(ref BCryptBufferDesc bufferDesc) + { bufferDesc.ulVersion = BCRYPTBUFFER_VERSION; } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptEncryptFlags.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptEncryptFlags.cs index 55a2af83004..dfc3d86d71b 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptEncryptFlags.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptEncryptFlags.cs @@ -1,9 +1,11 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // from bcrypt.h [Flags] - internal enum BCryptEncryptFlags { + internal enum BCryptEncryptFlags + { BCRYPT_BLOCK_PADDING = 0x00000001, } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptGenRandomFlags.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptGenRandomFlags.cs index 250d7e0ee6e..8fdce726b28 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptGenRandomFlags.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptGenRandomFlags.cs @@ -1,10 +1,12 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // from bcrypt.h [Flags] - internal enum BCryptGenRandomFlags { + internal enum BCryptGenRandomFlags + { BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001, BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002, } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptHashHandle.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptHashHandle.cs index 317a5f4bf10..ab59352f9d8 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptHashHandle.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptHashHandle.cs @@ -1,16 +1,20 @@ using System; using Microsoft.Win32.SafeHandles; -namespace Microsoft.AspNet.Security.DataProtection { - internal sealed class BCryptHashHandle : SafeHandleZeroOrMinusOneIsInvalid { +namespace Microsoft.AspNet.Security.DataProtection +{ + internal sealed class BCryptHashHandle : SafeHandleZeroOrMinusOneIsInvalid + { // Called by P/Invoke when returning SafeHandles private BCryptHashHandle() - : base(ownsHandle: true) { + : base(ownsHandle: true) + { } // Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you. - protected override bool ReleaseHandle() { + protected override bool ReleaseHandle() + { return (UnsafeNativeMethods.BCryptDestroyHash(handle) == 0); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyDerivationBufferType.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyDerivationBufferType.cs index 0fb2e843479..143c811529a 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyDerivationBufferType.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyDerivationBufferType.cs @@ -1,8 +1,10 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // from bcrypt.h - internal enum BCryptKeyDerivationBufferType { + internal enum BCryptKeyDerivationBufferType + { KDF_HASH_ALGORITHM = 0x0, KDF_SECRET_PREPEND = 0x1, KDF_SECRET_APPEND = 0x2, @@ -21,4 +23,4 @@ namespace Microsoft.AspNet.Security.DataProtection { KDF_SALT = 0xF, KDF_ITERATION_COUNT = 0x10, } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyHandle.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyHandle.cs index cd6e48fc881..16ab238e5a7 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyHandle.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptKeyHandle.cs @@ -1,16 +1,20 @@ using System; using Microsoft.Win32.SafeHandles; -namespace Microsoft.AspNet.Security.DataProtection { - internal sealed class BCryptKeyHandle : SafeHandleZeroOrMinusOneIsInvalid { +namespace Microsoft.AspNet.Security.DataProtection +{ + internal sealed class BCryptKeyHandle : SafeHandleZeroOrMinusOneIsInvalid + { // Called by P/Invoke when returning SafeHandles private BCryptKeyHandle() - : base(ownsHandle: true) { + : base(ownsHandle: true) + { } // Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you. - protected override bool ReleaseHandle() { + protected override bool ReleaseHandle() + { return (UnsafeNativeMethods.BCryptDestroyKey(handle) == 0); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/BCryptUtil.cs b/src/Microsoft.AspNet.Security.DataProtection/BCryptUtil.cs index 4ba82374362..3c28aaceecc 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/BCryptUtil.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/BCryptUtil.cs @@ -3,36 +3,43 @@ using System.Runtime.CompilerServices; using System.Security.Cryptography; using Microsoft.AspNet.Security.DataProtection.Util; -namespace Microsoft.AspNet.Security.DataProtection { - internal unsafe static class BCryptUtil { - +namespace Microsoft.AspNet.Security.DataProtection +{ + internal static unsafe class BCryptUtil + { // constant-time buffer comparison [MethodImpl(MethodImplOptions.NoOptimization)] - public static bool BuffersAreEqualSecure(byte* p1, byte* p2, uint count) { + public static bool BuffersAreEqualSecure(byte* p1, byte* p2, uint count) + { bool retVal = true; - while (count-- > 0) { + while (count-- > 0) + { retVal &= (*(p1++) == *(p2++)); } return retVal; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CheckOverflowUnderflow(int input) { - var unused = checked((uint)input); + private static void CheckOverflowUnderflow(int input) + { + var unused = checked((uint) input); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CheckOverflowUnderflow(uint input) { - var unused = checked((int)input); + private static void CheckOverflowUnderflow(uint input) + { + var unused = checked((int) input); } // helper function to wrap BCryptCreateHash - public static BCryptHashHandle CreateHash(BCryptAlgorithmHandle algorithmHandle, byte* key, int keyLengthInBytes) { + public static BCryptHashHandle CreateHash(BCryptAlgorithmHandle algorithmHandle, byte* key, int keyLengthInBytes) + { CheckOverflowUnderflow(keyLengthInBytes); BCryptHashHandle retVal; - int status = UnsafeNativeMethods.BCryptCreateHash(algorithmHandle, out retVal, IntPtr.Zero, 0, key, (uint)keyLengthInBytes, dwFlags: 0); - if (status != 0 || retVal == null || retVal.IsInvalid) { + int status = UnsafeNativeMethods.BCryptCreateHash(algorithmHandle, out retVal, IntPtr.Zero, 0, key, (uint) keyLengthInBytes, dwFlags: 0); + if (status != 0 || retVal == null || retVal.IsInvalid) + { throw new CryptographicException(status); } @@ -41,32 +48,36 @@ namespace Microsoft.AspNet.Security.DataProtection { // helper function to wrap BCryptEncrypt; returns number of bytes written to 'output' // assumes the output buffer is large enough to hold the ciphertext + any necessary padding - public static int DecryptWithPadding(BCryptKeyHandle keyHandle, byte* input, int inputLength, byte* iv, int ivLength, byte* output, int outputLength) { + public static int DecryptWithPadding(BCryptKeyHandle keyHandle, byte* input, int inputLength, byte* iv, int ivLength, byte* output, int outputLength) + { CheckOverflowUnderflow(inputLength); CheckOverflowUnderflow(ivLength); CheckOverflowUnderflow(outputLength); // BCryptEncrypt destroys the 'iv' parameter, so we need to pass a duplicate instead of the original - if (ivLength > Constants.MAX_STACKALLOC_BYTES) { + if (ivLength > Constants.MAX_STACKALLOC_BYTES) + { throw new InvalidOperationException(); } byte* pDuplicatedIV = stackalloc byte[ivLength]; - BufferUtil.BlockCopy(from: (IntPtr)iv, to: (IntPtr)pDuplicatedIV, byteCount: ivLength); + BufferUtil.BlockCopy(from: (IntPtr) iv, to: (IntPtr) pDuplicatedIV, byteCount: ivLength); uint retVal; - int status = UnsafeNativeMethods.BCryptDecrypt(keyHandle, input, (uint)inputLength, IntPtr.Zero, pDuplicatedIV, (uint)ivLength, output, (uint)outputLength, out retVal, BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); - if (status != 0) { + int status = UnsafeNativeMethods.BCryptDecrypt(keyHandle, input, (uint) inputLength, IntPtr.Zero, pDuplicatedIV, (uint) ivLength, output, (uint) outputLength, out retVal, BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); + if (status != 0) + { throw new CryptographicException(status); } - return checked((int)retVal); + return checked((int) retVal); } // helper function to wrap BCryptKeyDerivation using SP800-108-CTR-HMAC-SHA512 - public static void DeriveKeysSP800108(BCryptAlgorithmHandle kdfAlgorithmHandle, BCryptKeyHandle keyHandle, string purpose, BCryptAlgorithmHandle encryptionAlgorithmHandle, out BCryptKeyHandle encryptionKeyHandle, BCryptAlgorithmHandle hashAlgorithmHandle, out BCryptHashHandle hmacHandle, out BCryptKeyHandle kdfKeyHandle) { - const int ENCRYPTION_KEY_SIZE_IN_BYTES = 256 / 8; - const int HMAC_KEY_SIZE_IN_BYTES = 256 / 8; - const int KDF_SUBKEY_SIZE_IN_BYTES = 512 / 8; + public static void DeriveKeysSP800108(BCryptAlgorithmHandle kdfAlgorithmHandle, BCryptKeyHandle keyHandle, string purpose, BCryptAlgorithmHandle encryptionAlgorithmHandle, out BCryptKeyHandle encryptionKeyHandle, BCryptAlgorithmHandle hashAlgorithmHandle, out BCryptHashHandle hmacHandle, out BCryptKeyHandle kdfKeyHandle) + { + const int ENCRYPTION_KEY_SIZE_IN_BYTES = 256/8; + const int HMAC_KEY_SIZE_IN_BYTES = 256/8; + const int KDF_SUBKEY_SIZE_IN_BYTES = 512/8; const int TOTAL_NUM_BYTES_TO_DERIVE = ENCRYPTION_KEY_SIZE_IN_BYTES + HMAC_KEY_SIZE_IN_BYTES + KDF_SUBKEY_SIZE_IN_BYTES; // keep our buffers on the stack while we're generating key material @@ -75,21 +86,25 @@ namespace Microsoft.AspNet.Security.DataProtection { byte* pNewHmacKey = &pNewEncryptionKey[ENCRYPTION_KEY_SIZE_IN_BYTES]; byte* pNewKdfSubkey = &pNewHmacKey[HMAC_KEY_SIZE_IN_BYTES]; - try { - fixed (char* pszPrfAlgorithmName = Constants.BCRYPT_SHA512_ALGORITHM) { + try + { + fixed (char* pszPrfAlgorithmName = Constants.BCRYPT_SHA512_ALGORITHM) + { // Create a buffer to hold the hash algorithm name, currently hardcoded to HMACSHA512 uint numBuffers = 1; BCryptBuffer* pBCryptBuffers = stackalloc BCryptBuffer[2]; pBCryptBuffers[0].BufferType = BCryptKeyDerivationBufferType.KDF_HASH_ALGORITHM; - pBCryptBuffers[0].pvBuffer = (IntPtr)pszPrfAlgorithmName; - pBCryptBuffers[0].cbBuffer = (uint)((Constants.BCRYPT_SHA512_ALGORITHM.Length + 1) * sizeof(char)); // per http://msdn.microsoft.com/en-us/library/windows/desktop/aa375368(v=vs.85).aspx, need to include terminating null - fixed (char* pszPurpose = (String.IsNullOrEmpty(purpose) ? (string)null : purpose)) { + pBCryptBuffers[0].pvBuffer = (IntPtr) pszPrfAlgorithmName; + pBCryptBuffers[0].cbBuffer = (uint) ((Constants.BCRYPT_SHA512_ALGORITHM.Length + 1)*sizeof (char)); // per http://msdn.microsoft.com/en-us/library/windows/desktop/aa375368(v=vs.85).aspx, need to include terminating null + fixed (char* pszPurpose = (String.IsNullOrEmpty(purpose) ? (string) null : purpose)) + { // Create a buffer to hold the purpose string if it is specified (we'll treat it as UTF-16LE) - if (pszPurpose != null) { + if (pszPurpose != null) + { numBuffers = 2; pBCryptBuffers[1].BufferType = BCryptKeyDerivationBufferType.KDF_LABEL; - pBCryptBuffers[1].pvBuffer = (IntPtr)pszPurpose; - pBCryptBuffers[1].cbBuffer = checked((uint)(purpose.Length * sizeof(char))); + pBCryptBuffers[1].pvBuffer = (IntPtr) pszPurpose; + pBCryptBuffers[1].cbBuffer = checked((uint) (purpose.Length*sizeof (char))); } // .. and the header .. @@ -100,7 +115,8 @@ namespace Microsoft.AspNet.Security.DataProtection { uint numBytesDerived; int status = UnsafeNativeMethods.BCryptKeyDerivation(keyHandle, &bufferDesc, pBuffer, TOTAL_NUM_BYTES_TO_DERIVE, out numBytesDerived, dwFlags: 0); - if (status != 0 || numBytesDerived != TOTAL_NUM_BYTES_TO_DERIVE) { + if (status != 0 || numBytesDerived != TOTAL_NUM_BYTES_TO_DERIVE) + { throw new CryptographicException(status); } } @@ -111,16 +127,19 @@ namespace Microsoft.AspNet.Security.DataProtection { hmacHandle = CreateHash(hashAlgorithmHandle, pNewHmacKey, HMAC_KEY_SIZE_IN_BYTES); kdfKeyHandle = ImportKey(kdfAlgorithmHandle, pNewKdfSubkey, KDF_SUBKEY_SIZE_IN_BYTES); } - finally { + finally + { BufferUtil.ZeroMemory(pBuffer, TOTAL_NUM_BYTES_TO_DERIVE); } } // helper function to wrap BCryptDuplicateHash - public static BCryptHashHandle DuplicateHash(BCryptHashHandle hashHandle) { + public static BCryptHashHandle DuplicateHash(BCryptHashHandle hashHandle) + { BCryptHashHandle retVal; int status = UnsafeNativeMethods.BCryptDuplicateHash(hashHandle, out retVal, IntPtr.Zero, 0, dwFlags: 0); - if (status != 0 || retVal == null || retVal.IsInvalid) { + if (status != 0 || retVal == null || retVal.IsInvalid) + { throw new CryptographicException(status); } @@ -129,93 +148,107 @@ namespace Microsoft.AspNet.Security.DataProtection { // helper function to wrap BCryptEncrypt; returns number of bytes written to 'output' // assumes the output buffer is large enough to hold the ciphertext + any necessary padding - public static int EncryptWithPadding(BCryptKeyHandle keyHandle, byte* input, int inputLength, byte* iv, int ivLength, byte* output, int outputLength) { + public static int EncryptWithPadding(BCryptKeyHandle keyHandle, byte* input, int inputLength, byte* iv, int ivLength, byte* output, int outputLength) + { CheckOverflowUnderflow(inputLength); CheckOverflowUnderflow(ivLength); CheckOverflowUnderflow(outputLength); // BCryptEncrypt destroys the 'iv' parameter, so we need to pass a duplicate instead of the original - if (ivLength > Constants.MAX_STACKALLOC_BYTES) { + if (ivLength > Constants.MAX_STACKALLOC_BYTES) + { throw new InvalidOperationException(); } byte* pDuplicatedIV = stackalloc byte[ivLength]; - BufferUtil.BlockCopy(from: (IntPtr)iv, to: (IntPtr)pDuplicatedIV, byteCount: ivLength); + BufferUtil.BlockCopy(from: (IntPtr) iv, to: (IntPtr) pDuplicatedIV, byteCount: ivLength); uint retVal; - int status = UnsafeNativeMethods.BCryptEncrypt(keyHandle, input, (uint)inputLength, IntPtr.Zero, pDuplicatedIV, (uint)ivLength, output, (uint)outputLength, out retVal, BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); - if (status != 0) { + int status = UnsafeNativeMethods.BCryptEncrypt(keyHandle, input, (uint) inputLength, IntPtr.Zero, pDuplicatedIV, (uint) ivLength, output, (uint) outputLength, out retVal, BCryptEncryptFlags.BCRYPT_BLOCK_PADDING); + if (status != 0) + { throw new CryptographicException(status); } - return checked((int)retVal); + return checked((int) retVal); } // helper function that's similar to RNGCryptoServiceProvider, but works directly with pointers - public static void GenRandom(byte* buffer, int bufferBytes) { + public static void GenRandom(byte* buffer, int bufferBytes) + { CheckOverflowUnderflow(bufferBytes); - int status = UnsafeNativeMethods.BCryptGenRandom(IntPtr.Zero, buffer, (uint)bufferBytes, BCryptGenRandomFlags.BCRYPT_USE_SYSTEM_PREFERRED_RNG); - if (status != 0) { + int status = UnsafeNativeMethods.BCryptGenRandom(IntPtr.Zero, buffer, (uint) bufferBytes, BCryptGenRandomFlags.BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (status != 0) + { throw new CryptographicException(status); } } // helper function that wraps BCryptHashData / BCryptFinishHash - public static void HashData(BCryptHashHandle hashHandle, byte* input, int inputBytes, byte* output, int outputBytes) { + public static void HashData(BCryptHashHandle hashHandle, byte* input, int inputBytes, byte* output, int outputBytes) + { CheckOverflowUnderflow(inputBytes); CheckOverflowUnderflow(outputBytes); - int status = UnsafeNativeMethods.BCryptHashData(hashHandle, input, (uint)inputBytes, dwFlags: 0); - if (status != 0) { + int status = UnsafeNativeMethods.BCryptHashData(hashHandle, input, (uint) inputBytes, dwFlags: 0); + if (status != 0) + { throw new CryptographicException(status); } - status = UnsafeNativeMethods.BCryptFinishHash(hashHandle, output, (uint)outputBytes, dwFlags: 0); - if (status != 0) { + status = UnsafeNativeMethods.BCryptFinishHash(hashHandle, output, (uint) outputBytes, dwFlags: 0); + if (status != 0) + { throw new CryptographicException(status); } } // helper function that wraps BCryptImportKey with a key data blob - public static BCryptKeyHandle ImportKey(BCryptAlgorithmHandle algHandle, byte* key, int keyBytes) { + public static BCryptKeyHandle ImportKey(BCryptAlgorithmHandle algHandle, byte* key, int keyBytes) + { CheckOverflowUnderflow(keyBytes); byte[] heapAllocatedKeyDataBlob = null; - int numBytesRequiredForKeyDataBlob = checked(keyBytes + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER)); - if (numBytesRequiredForKeyDataBlob > Constants.MAX_STACKALLOC_BYTES) { + int numBytesRequiredForKeyDataBlob = checked(keyBytes + sizeof (BCRYPT_KEY_DATA_BLOB_HEADER)); + if (numBytesRequiredForKeyDataBlob > Constants.MAX_STACKALLOC_BYTES) + { heapAllocatedKeyDataBlob = new byte[numBytesRequiredForKeyDataBlob]; // allocate on heap if we cannot allocate on stack } int status; BCryptKeyHandle retVal; - fixed (byte* pHeapAllocatedKeyDataBlob = heapAllocatedKeyDataBlob) { + fixed (byte* pHeapAllocatedKeyDataBlob = heapAllocatedKeyDataBlob) + { // The header is first - BCRYPT_KEY_DATA_BLOB_HEADER* pKeyDataBlobHeader = (BCRYPT_KEY_DATA_BLOB_HEADER*)pHeapAllocatedKeyDataBlob; - if (pKeyDataBlobHeader == null) { + BCRYPT_KEY_DATA_BLOB_HEADER* pKeyDataBlobHeader = (BCRYPT_KEY_DATA_BLOB_HEADER*) pHeapAllocatedKeyDataBlob; + if (pKeyDataBlobHeader == null) + { byte* temp = stackalloc byte[numBytesRequiredForKeyDataBlob]; // won't be released until frame pops - pKeyDataBlobHeader = (BCRYPT_KEY_DATA_BLOB_HEADER*)temp; + pKeyDataBlobHeader = (BCRYPT_KEY_DATA_BLOB_HEADER*) temp; } BCRYPT_KEY_DATA_BLOB_HEADER.Initialize(ref *pKeyDataBlobHeader); - pKeyDataBlobHeader->cbKeyData = (uint)keyBytes; + pKeyDataBlobHeader->cbKeyData = (uint) keyBytes; // the raw material immediately follows the header - byte* pKeyDataRawMaterial = (byte*)(&pKeyDataBlobHeader[1]); + byte* pKeyDataRawMaterial = (byte*) (&pKeyDataBlobHeader[1]); - try { - BufferUtil.BlockCopy(from: (IntPtr)key, to: (IntPtr)pKeyDataRawMaterial, byteCount: keyBytes); - status = UnsafeNativeMethods.BCryptImportKey(algHandle, IntPtr.Zero, Constants.BCRYPT_KEY_DATA_BLOB, out retVal, IntPtr.Zero, 0, (byte*)pKeyDataBlobHeader, (uint)numBytesRequiredForKeyDataBlob, dwFlags: 0); + try + { + BufferUtil.BlockCopy(from: (IntPtr) key, to: (IntPtr) pKeyDataRawMaterial, byteCount: keyBytes); + status = UnsafeNativeMethods.BCryptImportKey(algHandle, IntPtr.Zero, Constants.BCRYPT_KEY_DATA_BLOB, out retVal, IntPtr.Zero, 0, (byte*) pKeyDataBlobHeader, (uint) numBytesRequiredForKeyDataBlob, dwFlags: 0); } - finally { + finally + { // zero out the key we just copied BufferUtil.ZeroMemory(pKeyDataRawMaterial, keyBytes); } } - if (status != 0 || retVal == null || retVal.IsInvalid) { + if (status != 0 || retVal == null || retVal.IsInvalid) + { throw new CryptographicException(status); } return retVal; } - } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/Constants.cs b/src/Microsoft.AspNet.Security.DataProtection/Constants.cs index c6ca8cbb7c3..059590c7e5d 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/Constants.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/Constants.cs @@ -1,8 +1,10 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ // from bcrypt.h - internal static class Constants { + internal static class Constants + { internal const int MAX_STACKALLOC_BYTES = 256; // greatest number of bytes that we'll ever allow to stackalloc in a single frame // BCrypt(Import/Export)Key BLOB types @@ -80,4 +82,4 @@ namespace Microsoft.AspNet.Security.DataProtection { internal const string BCRYPT_CHAIN_MODE_CCM = "ChainingModeCCM"; internal const string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"; } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/CryptographicException.cs b/src/Microsoft.AspNet.Security.DataProtection/CryptographicException.cs index 1737c90f38b..19e72c0c2ff 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/CryptographicException.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/CryptographicException.cs @@ -12,4 +12,4 @@ namespace System.Security.Cryptography { } } } -#endif +#endif \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProvider.cs b/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProvider.cs index 75320aee41e..e90ce870808 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProvider.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProvider.cs @@ -4,19 +4,22 @@ using System.Reflection; using Microsoft.AspNet.Security.DataProtection.Resources; using Microsoft.AspNet.Security.DataProtection.Util; -namespace Microsoft.AspNet.Security.DataProtection { - public unsafe static class DataProtectionProvider { - - const int MASTER_KEY_REQUIRED_LENGTH = 512 / 8; +namespace Microsoft.AspNet.Security.DataProtection +{ + public static unsafe class DataProtectionProvider + { + private const int MASTER_KEY_REQUIRED_LENGTH = 512/8; private static readonly byte[] MASTER_SUBKEY_GENERATOR = GetMasterSubkeyGenerator(); - private static byte[] GetMasterSubkeyGenerator() { - TypeInfo typeInfo = typeof(DataProtectionProvider).GetTypeInfo(); + private static byte[] GetMasterSubkeyGenerator() + { + TypeInfo typeInfo = typeof (DataProtectionProvider).GetTypeInfo(); - byte[] retVal = new byte[sizeof(Guid) * 2]; - fixed (byte* pRetVal = retVal) { - Guid* guids = (Guid*)pRetVal; + byte[] retVal = new byte[sizeof (Guid)*2]; + fixed (byte* pRetVal = retVal) + { + Guid* guids = (Guid*) pRetVal; guids[0] = typeInfo.GUID; #if NET45 guids[1] = typeInfo.Module.ModuleVersionId; @@ -30,13 +33,16 @@ namespace Microsoft.AspNet.Security.DataProtection { /// /// Creates a new IDataProtectorFactory with a randomly-generated master key. /// - public static IDataProtectionProvider CreateNew() { + public static IDataProtectionProvider CreateNew() + { byte* masterKey = stackalloc byte[MASTER_KEY_REQUIRED_LENGTH]; - try { + try + { BCryptUtil.GenRandom(masterKey, MASTER_KEY_REQUIRED_LENGTH); return CreateImpl(masterKey, MASTER_KEY_REQUIRED_LENGTH); } - finally { + finally + { BufferUtil.ZeroMemory(masterKey, MASTER_KEY_REQUIRED_LENGTH); } } @@ -44,34 +50,41 @@ namespace Microsoft.AspNet.Security.DataProtection { /// /// Creates a new IDataProtectorFactory with the provided master key. /// - public static IDataProtectionProvider CreateFromKey(byte[] masterKey) { - if (masterKey == null) { + public static IDataProtectionProvider CreateFromKey(byte[] masterKey) + { + if (masterKey == null) + { throw new ArgumentNullException("masterKey"); } - if (masterKey.Length < MASTER_KEY_REQUIRED_LENGTH) { + if (masterKey.Length < MASTER_KEY_REQUIRED_LENGTH) + { string errorMessage = String.Format(CultureInfo.CurrentCulture, Res.DataProtectorFactory_MasterKeyTooShort, MASTER_KEY_REQUIRED_LENGTH); throw new ArgumentOutOfRangeException("masterKey", errorMessage); } - fixed (byte* pMasterKey = masterKey) { + fixed (byte* pMasterKey = masterKey) + { return CreateImpl(pMasterKey, masterKey.Length); } } - private static DataProtectionProviderImpl CreateImpl(byte* masterKey, int masterKeyLengthInBytes) { + private static DataProtectionProviderImpl CreateImpl(byte* masterKey, int masterKeyLengthInBytes) + { // We don't use the master key directly. We derive a master subkey via HMAC_{master_key}(MASTER_SUBKEY_GENERATOR). byte* masterSubkey = stackalloc byte[MASTER_KEY_REQUIRED_LENGTH]; - try { - using (var hashHandle = BCryptUtil.CreateHash(Algorithms.HMACSHA512AlgorithmHandle, masterKey, masterKeyLengthInBytes)) { + try + { + using (var hashHandle = BCryptUtil.CreateHash(Algorithms.HMACSHA512AlgorithmHandle, masterKey, masterKeyLengthInBytes)) + { BCryptUtil.HashData(hashHandle, masterKey, masterKeyLengthInBytes, masterSubkey, MASTER_KEY_REQUIRED_LENGTH); } BCryptKeyHandle kdfSubkeyHandle = BCryptUtil.ImportKey(Algorithms.SP800108AlgorithmHandle, masterSubkey, MASTER_KEY_REQUIRED_LENGTH); return new DataProtectionProviderImpl(kdfSubkeyHandle); } - finally { + finally + { BufferUtil.ZeroMemory(masterSubkey, MASTER_KEY_REQUIRED_LENGTH); } } - } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProviderImpl.cs b/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProviderImpl.cs index f78cde3fc6d..2165b21fed6 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProviderImpl.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/DataProtectionProviderImpl.cs @@ -1,15 +1,18 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { - internal unsafe sealed class DataProtectionProviderImpl : IDataProtectionProvider { - +namespace Microsoft.AspNet.Security.DataProtection +{ + internal sealed unsafe class DataProtectionProviderImpl : IDataProtectionProvider + { private readonly BCryptKeyHandle _kdfSubkeyHandle; - public DataProtectionProviderImpl(BCryptKeyHandle kdfSubkeyHandle) { + public DataProtectionProviderImpl(BCryptKeyHandle kdfSubkeyHandle) + { _kdfSubkeyHandle = kdfSubkeyHandle; } - public IDataProtector CreateProtector(string purpose) { + public IDataProtector CreateProtector(string purpose) + { BCryptKeyHandle newAesKeyHandle; BCryptHashHandle newHmacHashHandle; BCryptKeyHandle newKdfSubkeyHandle; @@ -18,9 +21,9 @@ namespace Microsoft.AspNet.Security.DataProtection { return new DataProtectorImpl(newAesKeyHandle, newHmacHashHandle, newKdfSubkeyHandle); } - public void Dispose() { + public void Dispose() + { _kdfSubkeyHandle.Dispose(); } - } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/DataProtectorImpl.cs b/src/Microsoft.AspNet.Security.DataProtection/DataProtectorImpl.cs index 1aeacc31b89..d6489d56c38 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/DataProtectorImpl.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/DataProtectorImpl.cs @@ -4,35 +4,40 @@ using System.Security.Cryptography; using Microsoft.AspNet.Security.DataProtection.Resources; using Microsoft.AspNet.Security.DataProtection.Util; -namespace Microsoft.AspNet.Security.DataProtection { - internal unsafe sealed class DataProtectorImpl : IDataProtector { - - private const int AES_BLOCK_LENGTH_IN_BYTES = 128 / 8; - private const int MAC_LENGTH_IN_BYTES = 256 / 8; +namespace Microsoft.AspNet.Security.DataProtection +{ + internal sealed unsafe class DataProtectorImpl : IDataProtector + { + private const int AES_BLOCK_LENGTH_IN_BYTES = 128/8; + private const int MAC_LENGTH_IN_BYTES = 256/8; private readonly BCryptKeyHandle _aesKeyHandle; private readonly BCryptHashHandle _hmacHashHandle; private readonly BCryptKeyHandle _kdfSubkeyHandle; - public DataProtectorImpl(BCryptKeyHandle aesKeyHandle, BCryptHashHandle hmacHashHandle, BCryptKeyHandle kdfSubkeyHandle) { + public DataProtectorImpl(BCryptKeyHandle aesKeyHandle, BCryptHashHandle hmacHashHandle, BCryptKeyHandle kdfSubkeyHandle) + { _aesKeyHandle = aesKeyHandle; _hmacHashHandle = hmacHashHandle; _kdfSubkeyHandle = kdfSubkeyHandle; } - private static int CalculateTotalProtectedDataSize(int unprotectedDataSize) { + private static int CalculateTotalProtectedDataSize(int unprotectedDataSize) + { Debug.Assert(unprotectedDataSize >= 0); // Calculates - int numFullBlocks = unprotectedDataSize / AES_BLOCK_LENGTH_IN_BYTES; - return checked(AES_BLOCK_LENGTH_IN_BYTES /* IV */ + (numFullBlocks + 1) * AES_BLOCK_LENGTH_IN_BYTES /* ciphertext w/ padding */ + MAC_LENGTH_IN_BYTES /* HMAC */); + int numFullBlocks = unprotectedDataSize/AES_BLOCK_LENGTH_IN_BYTES; + return checked(AES_BLOCK_LENGTH_IN_BYTES /* IV */+ (numFullBlocks + 1)*AES_BLOCK_LENGTH_IN_BYTES /* ciphertext w/ padding */+ MAC_LENGTH_IN_BYTES /* HMAC */); } - private static CryptographicException CreateGenericCryptographicException() { + private static CryptographicException CreateGenericCryptographicException() + { return new CryptographicException(Res.DataProtectorImpl_BadEncryptedData); } - public IDataProtector CreateSubProtector(string purpose) { + public IDataProtector CreateSubProtector(string purpose) + { BCryptKeyHandle newAesKeyHandle; BCryptHashHandle newHmacHashHandle; BCryptKeyHandle newKdfSubkeyHandle; @@ -41,21 +46,25 @@ namespace Microsoft.AspNet.Security.DataProtection { return new DataProtectorImpl(newAesKeyHandle, newHmacHashHandle, newKdfSubkeyHandle); } - public void Dispose() { + public void Dispose() + { _aesKeyHandle.Dispose(); _hmacHashHandle.Dispose(); _kdfSubkeyHandle.Dispose(); } - public byte[] Protect(byte[] unprotectedData) { - if (unprotectedData == null) { + public byte[] Protect(byte[] unprotectedData) + { + if (unprotectedData == null) + { throw new ArgumentNullException("unprotectedData"); } // When this method finishes, protectedData will contain { IV || ciphertext || HMAC(IV || ciphertext) } byte[] protectedData = new byte[CalculateTotalProtectedDataSize(unprotectedData.Length)]; - fixed (byte* pProtectedData = protectedData) { + fixed (byte* pProtectedData = protectedData) + { // first, generate a random IV for CBC mode encryption byte* pIV = pProtectedData; BCryptUtil.GenRandom(pIV, AES_BLOCK_LENGTH_IN_BYTES); @@ -63,16 +72,19 @@ namespace Microsoft.AspNet.Security.DataProtection { // then, encrypt the plaintext contents byte* pCiphertext = &pIV[AES_BLOCK_LENGTH_IN_BYTES]; int expectedCiphertextLength = protectedData.Length - AES_BLOCK_LENGTH_IN_BYTES - MAC_LENGTH_IN_BYTES; - fixed (byte* pPlaintext = unprotectedData) { + fixed (byte* pPlaintext = unprotectedData) + { int actualCiphertextLength = BCryptUtil.EncryptWithPadding(_aesKeyHandle, pPlaintext, unprotectedData.Length, pIV, AES_BLOCK_LENGTH_IN_BYTES, pCiphertext, expectedCiphertextLength); - if (actualCiphertextLength != expectedCiphertextLength) { + if (actualCiphertextLength != expectedCiphertextLength) + { throw new InvalidOperationException("Unexpected error while encrypting data."); } } // finally, calculate an HMAC over { IV || ciphertext } byte* pMac = &pCiphertext[expectedCiphertextLength]; - using (var clonedHashHandle = BCryptUtil.DuplicateHash(_hmacHashHandle)) { + using (var clonedHashHandle = BCryptUtil.DuplicateHash(_hmacHashHandle)) + { // Use a cloned hash handle since IDataProtector instances could be singletons, but BCryptHashHandle instances contain // state hence aren't thread-safe. Our own perf testing shows that duplicating existing hash handles is very fast. BCryptUtil.HashData(clonedHashHandle, pProtectedData, AES_BLOCK_LENGTH_IN_BYTES + expectedCiphertextLength, pMac, MAC_LENGTH_IN_BYTES); @@ -82,49 +94,60 @@ namespace Microsoft.AspNet.Security.DataProtection { return protectedData; } - public byte[] Unprotect(byte[] protectedData) { - if (protectedData == null) { + public byte[] Unprotect(byte[] protectedData) + { + if (protectedData == null) + { throw new ArgumentNullException("protectedData"); } byte[] retVal = null; - try { + try + { retVal = UnprotectImpl(protectedData); } - catch { + catch + { // swallow all exceptions; we'll homogenize } - if (retVal != null) { + if (retVal != null) + { return retVal; } - else { + else + { throw CreateGenericCryptographicException(); } } - private byte[] UnprotectImpl(byte[] protectedData) { + private byte[] UnprotectImpl(byte[] protectedData) + { Debug.Assert(protectedData != null); // is the protected data even long enough to be valid? - if (protectedData.Length < AES_BLOCK_LENGTH_IN_BYTES /* IV */ + AES_BLOCK_LENGTH_IN_BYTES /* min ciphertext size = 1 block */ + MAC_LENGTH_IN_BYTES) { + if (protectedData.Length < AES_BLOCK_LENGTH_IN_BYTES /* IV */+ AES_BLOCK_LENGTH_IN_BYTES /* min ciphertext size = 1 block */+ MAC_LENGTH_IN_BYTES) + { return null; } - fixed (byte* pProtectedData = protectedData) { + fixed (byte* pProtectedData = protectedData) + { // calculate pointer offsets byte* pIV = pProtectedData; byte* pCiphertext = &pProtectedData[AES_BLOCK_LENGTH_IN_BYTES]; - int ciphertextLength = protectedData.Length - AES_BLOCK_LENGTH_IN_BYTES /* IV */ - MAC_LENGTH_IN_BYTES /* MAC */; + int ciphertextLength = protectedData.Length - AES_BLOCK_LENGTH_IN_BYTES /* IV */- MAC_LENGTH_IN_BYTES /* MAC */; byte* pSuppliedMac = &pCiphertext[ciphertextLength]; // first, ensure that the MAC is valid byte* pCalculatedMac = stackalloc byte[MAC_LENGTH_IN_BYTES]; - using (var clonedHashHandle = BCryptUtil.DuplicateHash(_hmacHashHandle)) { + using (var clonedHashHandle = BCryptUtil.DuplicateHash(_hmacHashHandle)) + { // see comments in Protect(byte[]) for why we duplicate the hash BCryptUtil.HashData(clonedHashHandle, pProtectedData, AES_BLOCK_LENGTH_IN_BYTES + ciphertextLength, pCalculatedMac, MAC_LENGTH_IN_BYTES); } - if (!BCryptUtil.BuffersAreEqualSecure(pSuppliedMac, pCalculatedMac, MAC_LENGTH_IN_BYTES)) { + if (!BCryptUtil.BuffersAreEqualSecure(pSuppliedMac, pCalculatedMac, MAC_LENGTH_IN_BYTES)) + { return null; // MAC check failed } @@ -132,13 +155,16 @@ namespace Microsoft.AspNet.Security.DataProtection { // we don't know the actual plaintext length, but we know it must be strictly less than the ciphertext length int plaintextBufferLength = ciphertextLength; byte[] heapAllocatedPlaintext = null; - if (ciphertextLength > Constants.MAX_STACKALLOC_BYTES) { + if (ciphertextLength > Constants.MAX_STACKALLOC_BYTES) + { heapAllocatedPlaintext = new byte[plaintextBufferLength]; } - fixed (byte* pHeapAllocatedPlaintext = heapAllocatedPlaintext) { + fixed (byte* pHeapAllocatedPlaintext = heapAllocatedPlaintext) + { byte* pPlaintextBuffer = pHeapAllocatedPlaintext; - if (pPlaintextBuffer == null) { + if (pPlaintextBuffer == null) + { byte* temp = stackalloc byte[plaintextBufferLength]; // will be released when frame pops pPlaintextBuffer = temp; } @@ -148,13 +174,13 @@ namespace Microsoft.AspNet.Security.DataProtection { // truncate the return value to accomodate the plaintext size perfectly byte[] retVal = new byte[actualPlaintextLength]; - fixed (byte* pRetVal = retVal) { - BufferUtil.BlockCopy(from: (IntPtr)pPlaintextBuffer, to: (IntPtr)pRetVal, byteCount: actualPlaintextLength); + fixed (byte* pRetVal = retVal) + { + BufferUtil.BlockCopy(from: (IntPtr) pPlaintextBuffer, to: (IntPtr) pRetVal, byteCount: actualPlaintextLength); } return retVal; } } } - } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/IDataProtectionProvider.cs b/src/Microsoft.AspNet.Security.DataProtection/IDataProtectionProvider.cs index 520359930ec..99bc6e32853 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/IDataProtectionProvider.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/IDataProtectionProvider.cs @@ -1,7 +1,9 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { - public interface IDataProtectionProvider : IDisposable { +namespace Microsoft.AspNet.Security.DataProtection +{ + public interface IDataProtectionProvider : IDisposable + { /// /// Given a purpose, returns a new IDataProtector that has unique cryptographic keys tied to this purpose. /// @@ -9,4 +11,4 @@ namespace Microsoft.AspNet.Security.DataProtection { /// An IDataProtector. IDataProtector CreateProtector(string purpose); } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/IDataProtector.cs b/src/Microsoft.AspNet.Security.DataProtection/IDataProtector.cs index c932d4522cd..5577a3cbdb9 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/IDataProtector.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/IDataProtector.cs @@ -1,10 +1,12 @@ using System; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ /// /// Represents an object that can perform cryptographic operations. /// - public interface IDataProtector : IDisposable { + public interface IDataProtector : IDisposable + { /// /// Given a subpurpose, returns a new IDataProtector that has unique cryptographic keys tied both the purpose /// that was used to create this IDataProtector instance and the purpose that is provided as a parameter @@ -30,4 +32,4 @@ namespace Microsoft.AspNet.Security.DataProtection { /// Throws CryptographicException if the protectedData parameter has been tampered with. byte[] Unprotect(byte[] protectedData); } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Security.DataProtection/Properties/AssemblyInfo.cs index 38a4928ff03..d4b33b324e4 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/Properties/AssemblyInfo.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. + [assembly: AssemblyTitle("Microsoft.AspNet.Security.DataProtection")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] @@ -14,17 +15,18 @@ using System.Runtime.InteropServices; // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. + [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("130d9afa-6535-42bf-ba70-610b677d5acf")] +[assembly: Guid("130d9afa-6535-42bf-ba70-610b677d5acf")] [assembly: AssemblyCompany("Microsoft Corporation")] [assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] - [assembly: NeutralResourcesLanguage("en-US")] // for OOB servicing -[assembly: AssemblyMetadata("Serviceable", "True")] + +[assembly: AssemblyMetadata("Serviceable", "True")] \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/SafeHandleZeroOrMinusOneIsInvalid.cs b/src/Microsoft.AspNet.Security.DataProtection/SafeHandleZeroOrMinusOneIsInvalid.cs index 244f118df06..ea76877d139 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/SafeHandleZeroOrMinusOneIsInvalid.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/SafeHandleZeroOrMinusOneIsInvalid.cs @@ -16,4 +16,4 @@ namespace Microsoft.Win32.SafeHandles { } } } -#endif +#endif \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs b/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs index 34689a99178..c34dd599d0c 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/UnsafeNativeMethods.cs @@ -6,12 +6,13 @@ using System.Security; using System.Text; using System.Threading.Tasks; -namespace Microsoft.AspNet.Security.DataProtection { +namespace Microsoft.AspNet.Security.DataProtection +{ #if NET45 [SuppressUnmanagedCodeSecurity] #endif - internal unsafe static class UnsafeNativeMethods { - + internal static unsafe class UnsafeNativeMethods + { private const string BCRYPT_LIB = "bcrypt.dll"; private const string KERNEL32_LIB = "kernel32.dll"; @@ -156,4 +157,4 @@ namespace Microsoft.AspNet.Security.DataProtection { [In] IntPtr Destination, [In] UIntPtr /* SIZE_T */ Length); } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Security.DataProtection/Util/BufferUtil.cs b/src/Microsoft.AspNet.Security.DataProtection/Util/BufferUtil.cs index 36a781aa17a..e982d35f9e1 100644 --- a/src/Microsoft.AspNet.Security.DataProtection/Util/BufferUtil.cs +++ b/src/Microsoft.AspNet.Security.DataProtection/Util/BufferUtil.cs @@ -1,24 +1,30 @@ using System; using System.Runtime.CompilerServices; -namespace Microsoft.AspNet.Security.DataProtection.Util { - internal unsafe static class BufferUtil { +namespace Microsoft.AspNet.Security.DataProtection.Util +{ + internal static unsafe class BufferUtil + { private static readonly byte[] _emptyArray = new byte[0]; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void BlockCopy(IntPtr from, IntPtr to, int byteCount) { - BlockCopy(from, to, checked((uint)byteCount)); // will be checked before invoking the delegate + public static void BlockCopy(IntPtr from, IntPtr to, int byteCount) + { + BlockCopy(from, to, checked((uint) byteCount)); // will be checked before invoking the delegate } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void BlockCopy(IntPtr from, IntPtr to, uint byteCount) { - BlockCopySlow((byte*)from, (byte*)to, byteCount); + public static void BlockCopy(IntPtr from, IntPtr to, uint byteCount) + { + BlockCopySlow((byte*) from, (byte*) to, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void BlockCopySlow(byte* from, byte* to, uint byteCount) { + private static void BlockCopySlow(byte* from, byte* to, uint byteCount) + { // slow, but works - while (byteCount-- != 0) { + while (byteCount-- != 0) + { *(to++) = *(from++); } } @@ -26,21 +32,26 @@ namespace Microsoft.AspNet.Security.DataProtection.Util { /// /// Creates a new managed byte[] from unmanaged memory. /// - public static byte[] ToManagedByteArray(byte* ptr, int byteCount) { - return ToManagedByteArray(ptr, checked((uint)byteCount)); + public static byte[] ToManagedByteArray(byte* ptr, int byteCount) + { + return ToManagedByteArray(ptr, checked((uint) byteCount)); } /// /// Creates a new managed byte[] from unmanaged memory. /// - public static byte[] ToManagedByteArray(byte* ptr, uint byteCount) { - if (byteCount == 0) { + public static byte[] ToManagedByteArray(byte* ptr, uint byteCount) + { + if (byteCount == 0) + { return _emptyArray; // degenerate case } - else { + else + { byte[] bytes = new byte[byteCount]; - fixed (byte* pBytes = bytes) { - BlockCopy(from: (IntPtr)ptr, to: (IntPtr)pBytes, byteCount: byteCount); + fixed (byte* pBytes = bytes) + { + BlockCopy(from: (IntPtr) ptr, to: (IntPtr) pBytes, byteCount: byteCount); } return bytes; } @@ -50,17 +61,18 @@ namespace Microsoft.AspNet.Security.DataProtection.Util { /// Clears a memory buffer. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ZeroMemory(byte* buffer, int byteCount) { - ZeroMemory(buffer, checked((uint)byteCount)); + public static void ZeroMemory(byte* buffer, int byteCount) + { + ZeroMemory(buffer, checked((uint) byteCount)); } /// /// Clears a memory buffer. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ZeroMemory(byte* buffer, uint byteCount) { - UnsafeNativeMethods.RtlZeroMemory((IntPtr)buffer, (UIntPtr)byteCount); // don't require 'checked': uint -> UIntPtr always guaranteed to succeed + public static void ZeroMemory(byte* buffer, uint byteCount) + { + UnsafeNativeMethods.RtlZeroMemory((IntPtr) buffer, (UIntPtr) byteCount); // don't require 'checked': uint -> UIntPtr always guaranteed to succeed } - } -} +} \ No newline at end of file