Initial implementation of Microsoft.AspNet.Security.DataProtection
This commit is contained in:
parent
fa2b8932f9
commit
869a4ec267
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Security.DataProtection {
|
||||
// from bcrypt.h
|
||||
[Flags]
|
||||
internal enum BCryptEncryptFlags {
|
||||
BCRYPT_BLOCK_PADDING = 0x00000001,
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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")]
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"version": "0.1-alpha-*",
|
||||
"configurations": {
|
||||
"net45" : {},
|
||||
"k10" : {}
|
||||
},
|
||||
"compilationOptions": {
|
||||
"allowUnsafe": true
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue