Initial implementation of Microsoft.AspNet.Security.DataProtection

This commit is contained in:
Levi Broderick 2014-01-28 17:14:26 -08:00
parent fa2b8932f9
commit 869a4ec267
26 changed files with 1342 additions and 0 deletions

View File

@ -0,0 +1,65 @@
using System;
using System.Security.Cryptography;
namespace Microsoft.AspNet.Security.DataProtection {
internal unsafe static 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() {
// 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) {
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);
}
if (status != 0) {
throw new CryptographicException(status);
}
return algHandle;
}
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) {
throw new CryptographicException(status);
}
return algHandle;
}
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) {
throw new CryptographicException(status);
}
return algHandle;
}
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) {
throw new CryptographicException(status);
}
return algHandle;
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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 {
// 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;
public uint dwMagic;
public uint dwVersion;
public uint cbKeyData;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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;
}
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
// from bcrypt.h
[Flags]
internal enum BCryptAlgorithmFlags {
BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008,
BCRYPT_CAPI_AES_FLAG = 0x00000010,
BCRYPT_HASH_REUSABLE_FLAG = 0x00000020,
}
}

View File

@ -0,0 +1,16 @@
using System;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNet.Security.DataProtection {
internal sealed class BCryptAlgorithmHandle : SafeHandleZeroOrMinusOneIsInvalid {
// Called by P/Invoke when returning SafeHandles
private BCryptAlgorithmHandle()
: base(ownsHandle: true) {
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle() {
return (UnsafeNativeMethods.BCryptCloseAlgorithmProvider(handle, dwFlags: 0) == 0);
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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 {
public uint cbBuffer; // Length of buffer, in bytes
public BCryptKeyDerivationBufferType BufferType; // Buffer type
public IntPtr pvBuffer; // Pointer to buffer
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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 {
private const int BCRYPTBUFFER_VERSION = 0;
public uint ulVersion; // Version number
public uint cBuffers; // Number of buffers
public BCryptBuffer* pBuffers; // Pointer to array of buffers
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Initialize(ref BCryptBufferDesc bufferDesc) {
bufferDesc.ulVersion = BCRYPTBUFFER_VERSION;
}
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
// from bcrypt.h
[Flags]
internal enum BCryptEncryptFlags {
BCRYPT_BLOCK_PADDING = 0x00000001,
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
// from bcrypt.h
[Flags]
internal enum BCryptGenRandomFlags {
BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001,
BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002,
}
}

View File

@ -0,0 +1,16 @@
using System;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNet.Security.DataProtection {
internal sealed class BCryptHashHandle : SafeHandleZeroOrMinusOneIsInvalid {
// Called by P/Invoke when returning SafeHandles
private BCryptHashHandle()
: base(ownsHandle: true) {
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle() {
return (UnsafeNativeMethods.BCryptDestroyHash(handle) == 0);
}
}
}

View File

@ -0,0 +1,24 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
// from bcrypt.h
internal enum BCryptKeyDerivationBufferType {
KDF_HASH_ALGORITHM = 0x0,
KDF_SECRET_PREPEND = 0x1,
KDF_SECRET_APPEND = 0x2,
KDF_HMAC_KEY = 0x3,
KDF_TLS_PRF_LABEL = 0x4,
KDF_TLS_PRF_SEED = 0x5,
KDF_SECRET_HANDLE = 0x6,
KDF_TLS_PRF_PROTOCOL = 0x7,
KDF_ALGORITHMID = 0x8,
KDF_PARTYUINFO = 0x9,
KDF_PARTYVINFO = 0xA,
KDF_SUPPPUBINFO = 0xB,
KDF_SUPPPRIVINFO = 0xC,
KDF_LABEL = 0xD,
KDF_CONTEXT = 0xE,
KDF_SALT = 0xF,
KDF_ITERATION_COUNT = 0x10,
}
}

View File

@ -0,0 +1,16 @@
using System;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNet.Security.DataProtection {
internal sealed class BCryptKeyHandle : SafeHandleZeroOrMinusOneIsInvalid {
// Called by P/Invoke when returning SafeHandles
private BCryptKeyHandle()
: base(ownsHandle: true) {
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle() {
return (UnsafeNativeMethods.BCryptDestroyKey(handle) == 0);
}
}
}

View File

@ -0,0 +1,221 @@
using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using Microsoft.AspNet.Security.DataProtection.Util;
namespace Microsoft.AspNet.Security.DataProtection {
internal unsafe static class BCryptUtil {
// constant-time buffer comparison
[MethodImpl(MethodImplOptions.NoOptimization)]
public static bool BuffersAreEqualSecure(byte* p1, byte* p2, uint count) {
bool retVal = true;
while (count-- > 0) {
retVal &= (*(p1++) == *(p2++));
}
return retVal;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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);
}
// helper function to wrap BCryptCreateHash
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) {
throw new CryptographicException(status);
}
return retVal;
}
// 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) {
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) {
throw new InvalidOperationException();
}
byte* pDuplicatedIV = stackalloc byte[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) {
throw new CryptographicException(status);
}
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;
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
byte* pBuffer = stackalloc byte[TOTAL_NUM_BYTES_TO_DERIVE]; // will be freed with frame pops
byte* pNewEncryptionKey = pBuffer;
byte* pNewHmacKey = &pNewEncryptionKey[ENCRYPTION_KEY_SIZE_IN_BYTES];
byte* pNewKdfSubkey = &pNewHmacKey[HMAC_KEY_SIZE_IN_BYTES];
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)) {
// Create a buffer to hold the purpose string if it is specified (we'll treat it as UTF-16LE)
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)));
}
// .. and the header ..
BCryptBufferDesc bufferDesc = default(BCryptBufferDesc);
BCryptBufferDesc.Initialize(ref bufferDesc);
bufferDesc.cBuffers = numBuffers;
bufferDesc.pBuffers = pBCryptBuffers;
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) {
throw new CryptographicException(status);
}
}
}
// At this point, we have all the bytes we need.
encryptionKeyHandle = ImportKey(encryptionAlgorithmHandle, pNewEncryptionKey, ENCRYPTION_KEY_SIZE_IN_BYTES);
hmacHandle = CreateHash(hashAlgorithmHandle, pNewHmacKey, HMAC_KEY_SIZE_IN_BYTES);
kdfKeyHandle = ImportKey(kdfAlgorithmHandle, pNewKdfSubkey, KDF_SUBKEY_SIZE_IN_BYTES);
}
finally {
BufferUtil.ZeroMemory(pBuffer, TOTAL_NUM_BYTES_TO_DERIVE);
}
}
// helper function to wrap BCryptDuplicateHash
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) {
throw new CryptographicException(status);
}
return retVal;
}
// 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) {
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) {
throw new InvalidOperationException();
}
byte* pDuplicatedIV = stackalloc byte[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) {
throw new CryptographicException(status);
}
return checked((int)retVal);
}
// helper function that's similar to RNGCryptoServiceProvider, but works directly with pointers
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) {
throw new CryptographicException(status);
}
}
// helper function that wraps BCryptHashData / BCryptFinishHash
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) {
throw new CryptographicException(status);
}
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) {
CheckOverflowUnderflow(keyBytes);
byte[] heapAllocatedKeyDataBlob = null;
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) {
// The header is first
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;
}
BCRYPT_KEY_DATA_BLOB_HEADER.Initialize(ref *pKeyDataBlobHeader);
pKeyDataBlobHeader->cbKeyData = (uint)keyBytes;
// the raw material immediately follows the header
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);
}
finally {
// zero out the key we just copied
BufferUtil.ZeroMemory(pKeyDataRawMaterial, keyBytes);
}
}
if (status != 0 || retVal == null || retVal.IsInvalid) {
throw new CryptographicException(status);
}
return retVal;
}
}
}

View File

@ -0,0 +1,83 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
// from bcrypt.h
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
internal const string BCRYPT_OPAQUE_KEY_BLOB = "OpaqueKeyBlob";
internal const string BCRYPT_KEY_DATA_BLOB = "KeyDataBlob";
internal const string BCRYPT_AES_WRAP_KEY_BLOB = "Rfc3565KeyWrapBlob";
// Microsoft built-in providers.
internal const string MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider";
internal const string MS_PLATFORM_CRYPTO_PROVIDER = "Microsoft Platform Crypto Provider";
// Common algorithm identifiers.
internal const string BCRYPT_RSA_ALGORITHM = "RSA";
internal const string BCRYPT_RSA_SIGN_ALGORITHM = "RSA_SIGN";
internal const string BCRYPT_DH_ALGORITHM = "DH";
internal const string BCRYPT_DSA_ALGORITHM = "DSA";
internal const string BCRYPT_RC2_ALGORITHM = "RC2";
internal const string BCRYPT_RC4_ALGORITHM = "RC4";
internal const string BCRYPT_AES_ALGORITHM = "AES";
internal const string BCRYPT_DES_ALGORITHM = "DES";
internal const string BCRYPT_DESX_ALGORITHM = "DESX";
internal const string BCRYPT_3DES_ALGORITHM = "3DES";
internal const string BCRYPT_3DES_112_ALGORITHM = "3DES_112";
internal const string BCRYPT_MD2_ALGORITHM = "MD2";
internal const string BCRYPT_MD4_ALGORITHM = "MD4";
internal const string BCRYPT_MD5_ALGORITHM = "MD5";
internal const string BCRYPT_SHA1_ALGORITHM = "SHA1";
internal const string BCRYPT_SHA256_ALGORITHM = "SHA256";
internal const string BCRYPT_SHA384_ALGORITHM = "SHA384";
internal const string BCRYPT_SHA512_ALGORITHM = "SHA512";
internal const string BCRYPT_AES_GMAC_ALGORITHM = "AES-GMAC";
internal const string BCRYPT_AES_CMAC_ALGORITHM = "AES-CMAC";
internal const string BCRYPT_ECDSA_P256_ALGORITHM = "ECDSA_P256";
internal const string BCRYPT_ECDSA_P384_ALGORITHM = "ECDSA_P384";
internal const string BCRYPT_ECDSA_P521_ALGORITHM = "ECDSA_P521";
internal const string BCRYPT_ECDH_P256_ALGORITHM = "ECDH_P256";
internal const string BCRYPT_ECDH_P384_ALGORITHM = "ECDH_P384";
internal const string BCRYPT_ECDH_P521_ALGORITHM = "ECDH_P521";
internal const string BCRYPT_RNG_ALGORITHM = "RNG";
internal const string BCRYPT_RNG_FIPS186_DSA_ALGORITHM = "FIPS186DSARNG";
internal const string BCRYPT_RNG_DUAL_EC_ALGORITHM = "DUALECRNG";
internal const string BCRYPT_SP800108_CTR_HMAC_ALGORITHM = "SP800_108_CTR_HMAC";
internal const string BCRYPT_SP80056A_CONCAT_ALGORITHM = "SP800_56A_CONCAT";
internal const string BCRYPT_PBKDF2_ALGORITHM = "PBKDF2";
internal const string BCRYPT_CAPI_KDF_ALGORITHM = "CAPI_KDF";
// BCryptGetProperty strings
internal const string BCRYPT_OBJECT_LENGTH = "ObjectLength";
internal const string BCRYPT_ALGORITHM_NAME = "AlgorithmName";
internal const string BCRYPT_PROVIDER_HANDLE = "ProviderHandle";
internal const string BCRYPT_CHAINING_MODE = "ChainingMode";
internal const string BCRYPT_BLOCK_LENGTH = "BlockLength";
internal const string BCRYPT_KEY_LENGTH = "KeyLength";
internal const string BCRYPT_KEY_OBJECT_LENGTH = "KeyObjectLength";
internal const string BCRYPT_KEY_STRENGTH = "KeyStrength";
internal const string BCRYPT_KEY_LENGTHS = "KeyLengths";
internal const string BCRYPT_BLOCK_SIZE_LIST = "BlockSizeList";
internal const string BCRYPT_EFFECTIVE_KEY_LENGTH = "EffectiveKeyLength";
internal const string BCRYPT_HASH_LENGTH = "HashDigestLength";
internal const string BCRYPT_HASH_OID_LIST = "HashOIDList";
internal const string BCRYPT_PADDING_SCHEMES = "PaddingSchemes";
internal const string BCRYPT_SIGNATURE_LENGTH = "SignatureLength";
internal const string BCRYPT_HASH_BLOCK_LENGTH = "HashBlockLength";
internal const string BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength";
internal const string BCRYPT_PRIMITIVE_TYPE = "PrimitiveType";
internal const string BCRYPT_IS_KEYED_HASH = "IsKeyedHash";
internal const string BCRYPT_IS_REUSABLE_HASH = "IsReusableHash";
internal const string BCRYPT_MESSAGE_BLOCK_LENGTH = "MessageBlockLength";
// Property Strings
internal const string BCRYPT_CHAIN_MODE_NA = "ChainingModeN/A";
internal const string BCRYPT_CHAIN_MODE_CBC = "ChainingModeCBC";
internal const string BCRYPT_CHAIN_MODE_ECB = "ChainingModeECB";
internal const string BCRYPT_CHAIN_MODE_CFB = "ChainingModeCFB";
internal const string BCRYPT_CHAIN_MODE_CCM = "ChainingModeCCM";
internal const string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM";
}
}

View File

@ -0,0 +1,15 @@
using System;
#if !NET45
namespace System.Security.Cryptography {
internal sealed class CryptographicException : Exception {
internal CryptographicException(string message)
: base(message) {
}
internal CryptographicException(int unused) {
}
}
}
#endif

View File

@ -0,0 +1,77 @@
using System;
using System.Globalization;
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;
private static readonly byte[] MASTER_SUBKEY_GENERATOR = GetMasterSubkeyGenerator();
private static byte[] GetMasterSubkeyGenerator() {
TypeInfo typeInfo = typeof(DataProtectionProvider).GetTypeInfo();
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;
#else
guids[1] = default(Guid);
#endif
}
return retVal;
}
/// <summary>
/// Creates a new IDataProtectorFactory with a randomly-generated master key.
/// </summary>
public static IDataProtectionProvider CreateNew() {
byte* masterKey = stackalloc byte[MASTER_KEY_REQUIRED_LENGTH];
try {
BCryptUtil.GenRandom(masterKey, MASTER_KEY_REQUIRED_LENGTH);
return CreateImpl(masterKey, MASTER_KEY_REQUIRED_LENGTH);
}
finally {
BufferUtil.ZeroMemory(masterKey, MASTER_KEY_REQUIRED_LENGTH);
}
}
/// <summary>
/// Creates a new IDataProtectorFactory with the provided master key.
/// </summary>
public static IDataProtectionProvider CreateFromKey(byte[] masterKey) {
if (masterKey == null) {
throw new ArgumentNullException("masterKey");
}
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) {
return CreateImpl(pMasterKey, masterKey.Length);
}
}
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)) {
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 {
BufferUtil.ZeroMemory(masterSubkey, MASTER_KEY_REQUIRED_LENGTH);
}
}
}
}

View File

@ -0,0 +1,26 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
internal unsafe sealed class DataProtectionProviderImpl : IDataProtectionProvider {
private readonly BCryptKeyHandle _kdfSubkeyHandle;
public DataProtectionProviderImpl(BCryptKeyHandle kdfSubkeyHandle) {
_kdfSubkeyHandle = kdfSubkeyHandle;
}
public IDataProtector CreateProtector(string purpose) {
BCryptKeyHandle newAesKeyHandle;
BCryptHashHandle newHmacHashHandle;
BCryptKeyHandle newKdfSubkeyHandle;
BCryptUtil.DeriveKeysSP800108(Algorithms.SP800108AlgorithmHandle, _kdfSubkeyHandle, purpose, Algorithms.AESAlgorithmHandle, out newAesKeyHandle, Algorithms.HMACSHA256AlgorithmHandle, out newHmacHashHandle, out newKdfSubkeyHandle);
return new DataProtectorImpl(newAesKeyHandle, newHmacHashHandle, newKdfSubkeyHandle);
}
public void Dispose() {
_kdfSubkeyHandle.Dispose();
}
}
}

View File

@ -0,0 +1,160 @@
using System;
using System.Diagnostics;
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;
private readonly BCryptKeyHandle _aesKeyHandle;
private readonly BCryptHashHandle _hmacHashHandle;
private readonly BCryptKeyHandle _kdfSubkeyHandle;
public DataProtectorImpl(BCryptKeyHandle aesKeyHandle, BCryptHashHandle hmacHashHandle, BCryptKeyHandle kdfSubkeyHandle) {
_aesKeyHandle = aesKeyHandle;
_hmacHashHandle = hmacHashHandle;
_kdfSubkeyHandle = kdfSubkeyHandle;
}
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 */);
}
private static CryptographicException CreateGenericCryptographicException() {
return new CryptographicException(Res.DataProtectorImpl_BadEncryptedData);
}
public IDataProtector CreateSubProtector(string purpose) {
BCryptKeyHandle newAesKeyHandle;
BCryptHashHandle newHmacHashHandle;
BCryptKeyHandle newKdfSubkeyHandle;
BCryptUtil.DeriveKeysSP800108(Algorithms.SP800108AlgorithmHandle, _kdfSubkeyHandle, purpose, Algorithms.AESAlgorithmHandle, out newAesKeyHandle, Algorithms.HMACSHA256AlgorithmHandle, out newHmacHashHandle, out newKdfSubkeyHandle);
return new DataProtectorImpl(newAesKeyHandle, newHmacHashHandle, newKdfSubkeyHandle);
}
public void Dispose() {
_aesKeyHandle.Dispose();
_hmacHashHandle.Dispose();
_kdfSubkeyHandle.Dispose();
}
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) {
// first, generate a random IV for CBC mode encryption
byte* pIV = pProtectedData;
BCryptUtil.GenRandom(pIV, AES_BLOCK_LENGTH_IN_BYTES);
// 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) {
int actualCiphertextLength = BCryptUtil.EncryptWithPadding(_aesKeyHandle, pPlaintext, unprotectedData.Length, pIV, AES_BLOCK_LENGTH_IN_BYTES, pCiphertext, 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)) {
// 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);
}
}
return protectedData;
}
public byte[] Unprotect(byte[] protectedData) {
if (protectedData == null) {
throw new ArgumentNullException("protectedData");
}
byte[] retVal = null;
try {
retVal = UnprotectImpl(protectedData);
}
catch {
// swallow all exceptions; we'll homogenize
}
if (retVal != null) {
return retVal;
}
else {
throw CreateGenericCryptographicException();
}
}
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) {
return null;
}
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 */;
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)) {
// 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)) {
return null; // MAC check failed
}
// next, perform the actual decryption
// 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) {
heapAllocatedPlaintext = new byte[plaintextBufferLength];
}
fixed (byte* pHeapAllocatedPlaintext = heapAllocatedPlaintext) {
byte* pPlaintextBuffer = pHeapAllocatedPlaintext;
if (pPlaintextBuffer == null) {
byte* temp = stackalloc byte[plaintextBufferLength]; // will be released when frame pops
pPlaintextBuffer = temp;
}
int actualPlaintextLength = BCryptUtil.DecryptWithPadding(_aesKeyHandle, pCiphertext, ciphertextLength, pIV, AES_BLOCK_LENGTH_IN_BYTES, pPlaintextBuffer, plaintextBufferLength);
Debug.Assert(actualPlaintextLength >= 0 && actualPlaintextLength < ciphertextLength);
// 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);
}
return retVal;
}
}
}
}
}

View File

@ -0,0 +1,12 @@
using System;
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>
/// <param name="purpose">The consumer of the IDataProtector.</param>
/// <returns>An IDataProtector.</returns>
IDataProtector CreateProtector(string purpose);
}
}

View File

@ -0,0 +1,33 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection {
/// <summary>
/// Represents an object that can perform cryptographic operations.
/// </summary>
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
/// to this method.
/// </summary>
/// <param name="purpose">The sub-consumer of the IDataProtector.</param>
/// <returns>An IDataProtector.</returns>
IDataProtector CreateSubProtector(string purpose);
/// <summary>
/// Cryptographically protects some input data.
/// </summary>
/// <param name="unprotectedData">The data to be protected.</param>
/// <returns>An array containing cryptographically protected data.</returns>
/// <remarks>To retrieve the original data, call Unprotect on the protected data.</remarks>
byte[] Protect(byte[] unprotectedData);
/// <summary>
/// Retrieves the original data that was protected by a call to Protect.
/// </summary>
/// <param name="protectedData">The protected data to be decrypted.</param>
/// <returns>The original data.</returns>
/// <remarks>Throws CryptographicException if the <em>protectedData</em> parameter has been tampered with.</remarks>
byte[] Unprotect(byte[] protectedData);
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Reflection;
using System.Resources;
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("")]
[assembly: AssemblyProduct("Microsoft.AspNet.Security.DataProtection")]
// 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: 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")]

View File

@ -0,0 +1,82 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34003
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.AspNet.Security.DataProtection.Resources {
using System;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Res {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Res() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Security.DataProtection.Res.resources", typeof(Res).GetTypeInfo().Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to The master key is too short. It must be at least {0} bytes in length..
/// </summary>
internal static string DataProtectorFactory_MasterKeyTooShort {
get {
return ResourceManager.GetString("DataProtectorFactory_MasterKeyTooShort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The data to decrypt is invalid..
/// </summary>
internal static string DataProtectorImpl_BadEncryptedData {
get {
return ResourceManager.GetString("DataProtectorImpl_BadEncryptedData", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DataProtectorFactory_MasterKeyTooShort" xml:space="preserve">
<value>The master key is too short. It must be at least {0} bytes in length.</value>
</data>
<data name="DataProtectorImpl_BadEncryptedData" xml:space="preserve">
<value>The data to decrypt is invalid.</value>
</data>
</root>

View File

@ -0,0 +1,19 @@
using System;
using System.Runtime.InteropServices;
#if !NET45
namespace Microsoft.Win32.SafeHandles {
internal abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle {
// Called by P/Invoke when returning SafeHandles
protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle)
: base(IntPtr.Zero, ownsHandle) {
}
public override bool IsInvalid {
get {
return (handle == IntPtr.Zero || handle == (IntPtr)(-1));
}
}
}
}
#endif

View File

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Security.DataProtection {
#if NET45
[SuppressUnmanagedCodeSecurity]
#endif
internal unsafe static class UnsafeNativeMethods {
private const string BCRYPT_LIB = "bcrypt.dll";
private const string KERNEL32_LIB = "kernel32.dll";
/*
* BCRYPT.DLL
*/
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375377(v=vs.85).aspx
internal static extern int BCryptCloseAlgorithmProvider(
[In] IntPtr hAlgorithm,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375383(v=vs.85).aspx
internal static extern int BCryptCreateHash(
[In] BCryptAlgorithmHandle hAlgorithm,
[Out] out BCryptHashHandle phHash,
[In] IntPtr pbHashObject,
[In] uint cbHashObject,
[In] byte* pbSecret,
[In] uint cbSecret,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375391(v=vs.85).aspx
internal static extern int BCryptDecrypt(
[In] BCryptKeyHandle hKey,
[In] byte* pbInput,
[In] uint cbInput,
[In] IntPtr pPaddingInfo,
[In] byte* pbIV,
[In] uint cbIV,
[In] byte* pbOutput,
[In] uint cbOutput,
[Out] out uint pcbResult,
[In] BCryptEncryptFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375399(v=vs.85).aspx
internal static extern int BCryptDestroyHash(
[In] IntPtr hHash);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375404(v=vs.85).aspx
internal static extern int BCryptDestroyKey(
[In] IntPtr hKey);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375413(v=vs.85).aspx
internal static extern int BCryptDuplicateHash(
[In] BCryptHashHandle hHash,
[Out] out BCryptHashHandle phNewHash,
[In] IntPtr pbHashObject,
[In] uint cbHashObject,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375421(v=vs.85).aspx
internal static extern int BCryptEncrypt(
[In] BCryptKeyHandle hKey,
[In] byte* pbInput,
[In] uint cbInput,
[In] IntPtr pPaddingInfo,
[In] byte* pbIV,
[In] uint cbIV,
[In] byte* pbOutput,
[In] uint cbOutput,
[Out] out uint pcbResult,
[In] BCryptEncryptFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375443(v=vs.85).aspx
internal static extern int BCryptFinishHash(
[In] BCryptHashHandle hHash,
[In] byte* pbOutput,
[In] uint cbOutput,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375458(v=vs.85).aspx
internal static extern int BCryptGenRandom(
[In] IntPtr hAlgorithm,
[In] byte* pbBuffer,
[In] uint cbBuffer,
[In] BCryptGenRandomFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375468(v=vs.85).aspx
internal static extern int BCryptHashData(
[In] BCryptHashHandle hHash,
[In] byte* pbInput,
[In] uint cbInput,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375475(v=vs.85).aspx
internal static extern int BCryptImportKey(
[In] BCryptAlgorithmHandle hAlgorithm,
[In] IntPtr hImportKey, // unused
[In, MarshalAs(UnmanagedType.LPWStr)] string pszBlobType,
[Out] out BCryptKeyHandle phKey,
[In] IntPtr pbKeyObject, // unused
[In] uint cbKeyObject,
[In] byte* pbInput,
[In] uint cbInput,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh448506(v=vs.85).aspx
internal static extern int BCryptKeyDerivation(
[In] BCryptKeyHandle hKey,
[In] BCryptBufferDesc* pParameterList,
[In] byte* pbDerivedKey,
[In] uint cbDerivedKey,
[Out] out uint pcbResult,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375479(v=vs.85).aspx
internal static extern int BCryptOpenAlgorithmProvider(
[Out] out BCryptAlgorithmHandle phAlgorithm,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAlgId,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszImplementation,
[In] BCryptAlgorithmFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375504(v=vs.85).aspx
internal static extern int BCryptSetProperty(
[In] SafeHandle hObject,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszProperty,
[In] IntPtr pbInput,
[In] uint cbInput,
[In] uint dwFlags);
/*
* KERNEL32.DLL
*/
[DllImport(KERNEL32_LIB, CallingConvention = CallingConvention.Winapi)]
internal static extern void RtlZeroMemory(
[In] IntPtr Destination,
[In] UIntPtr /* SIZE_T */ Length);
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNet.Security.DataProtection.Util {
internal unsafe static 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
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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) {
// slow, but works
while (byteCount-- != 0) {
*(to++) = *(from++);
}
}
/// <summary>
/// Creates a new managed byte[] from unmanaged memory.
/// </summary>
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) {
return _emptyArray; // degenerate case
}
else {
byte[] bytes = new byte[byteCount];
fixed (byte* pBytes = bytes) {
BlockCopy(from: (IntPtr)ptr, to: (IntPtr)pBytes, byteCount: byteCount);
}
return bytes;
}
}
/// <summary>
/// Clears a memory buffer.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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
}
}
}

View File

@ -0,0 +1,10 @@
{
"version": "0.1-alpha-*",
"configurations": {
"net45" : {},
"k10" : {}
},
"compilationOptions": {
"allowUnsafe": true
}
}