This commit is contained in:
Louis DeJardin 2014-01-28 17:48:49 -08:00
Родитель 869a4ec267
Коммит baf338cc83
25 изменённых файлов: 395 добавлений и 225 удалений

34
DataProtection.sln Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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,
}
}
}

Просмотреть файл

@ -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);
}
}
}
}

Просмотреть файл

@ -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
}
}
}

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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,
}
}
}

Просмотреть файл

@ -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,
}
}
}

Просмотреть файл

@ -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);
}
}
}
}

Просмотреть файл

@ -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,
}
}
}

Просмотреть файл

@ -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);
}
}
}
}

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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";
}
}
}

Просмотреть файл

@ -12,4 +12,4 @@ namespace System.Security.Cryptography {
}
}
}
#endif
#endif

Просмотреть файл

@ -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 {
/// <summary>
/// Creates a new IDataProtectorFactory with a randomly-generated master key.
/// </summary>
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 {
/// <summary>
/// Creates a new IDataProtectorFactory with the provided master key.
/// </summary>
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);
}
}
}
}
}

Просмотреть файл

@ -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();
}
}
}
}

Просмотреть файл

@ -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;
}
}
}
}
}
}

Просмотреть файл

@ -1,7 +1,9 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
public interface IDataProtectionProvider : IDisposable {
namespace Microsoft.AspNet.Security.DataProtection
{
public interface IDataProtectionProvider : IDisposable
{
/// <summary>
/// Given a purpose, returns a new IDataProtector that has unique cryptographic keys tied to this purpose.
/// </summary>
@ -9,4 +11,4 @@ namespace Microsoft.AspNet.Security.DataProtection {
/// <returns>An IDataProtector.</returns>
IDataProtector CreateProtector(string purpose);
}
}
}

Просмотреть файл

@ -1,10 +1,12 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
namespace Microsoft.AspNet.Security.DataProtection
{
/// <summary>
/// Represents an object that can perform cryptographic operations.
/// </summary>
public interface IDataProtector : IDisposable {
public interface IDataProtector : IDisposable
{
/// <summary>
/// Given a subpurpose, returns a new IDataProtector that has unique cryptographic keys tied <em>both</em> the purpose
/// that was used to create this IDataProtector instance <em>and</em> the purpose that is provided as a parameter
@ -30,4 +32,4 @@ namespace Microsoft.AspNet.Security.DataProtection {
/// <remarks>Throws CryptographicException if the <em>protectedData</em> parameter has been tampered with.</remarks>
byte[] Unprotect(byte[] protectedData);
}
}
}

Просмотреть файл

@ -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")]

Просмотреть файл

@ -16,4 +16,4 @@ namespace Microsoft.Win32.SafeHandles {
}
}
}
#endif
#endif

Просмотреть файл

@ -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);
}
}
}

Просмотреть файл

@ -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 {
/// <summary>
/// Creates a new managed byte[] from unmanaged memory.
/// </summary>
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));
}
/// <summary>
/// Creates a new managed byte[] from unmanaged memory.
/// </summary>
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.
/// </summary>
[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));
}
/// <summary>
/// Clears a memory buffer.
/// </summary>
[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
}
}
}
}