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