Add PBKDF2 support to the data protection library.

This commit is contained in:
Levi Broderick 2014-03-06 19:42:00 -08:00
parent 7aa23bfc05
commit adf2adabc0
4 changed files with 112 additions and 11 deletions

View File

@ -33,11 +33,10 @@ namespace Microsoft.AspNet.Security.DataProtection
return algHandle;
}
private static BCryptAlgorithmHandle CreateHMACSHA256AlgorithmHandle()
internal static BCryptAlgorithmHandle CreateGenericHMACHandleFromPrimitiveProvider(string algorithmName)
{
// 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);
int status = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, algorithmName, Constants.MS_PRIMITIVE_PROVIDER, dwFlags: BCryptAlgorithmFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (status != 0 || algHandle == null || algHandle.IsInvalid)
{
throw new CryptographicException(status);
@ -46,17 +45,16 @@ namespace Microsoft.AspNet.Security.DataProtection
return algHandle;
}
private static BCryptAlgorithmHandle CreateHMACSHA256AlgorithmHandle()
{
// create the HMACSHA-256 instance
return CreateGenericHMACHandleFromPrimitiveProvider(Constants.BCRYPT_SHA256_ALGORITHM);
}
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;
return CreateGenericHMACHandleFromPrimitiveProvider(Constants.BCRYPT_SHA512_ALGORITHM);
}
private static BCryptAlgorithmHandle CreateSP800108AlgorithmHandle()

View File

@ -0,0 +1,25 @@
using System;
namespace Microsoft.AspNet.Security.DataProtection
{
/// <summary>
/// Helper class to populate buffers with cryptographically random data.
/// </summary>
public static class CryptRand
{
/// <summary>
/// Populates a buffer with cryptographically random data.
/// </summary>
/// <param name="buffer">The buffer to populate.</param>
public static unsafe void FillBuffer(ArraySegment<byte> buffer)
{
// the ArraySegment<> ctor performs bounds checking
var unused = new ArraySegment<byte>(buffer.Array, buffer.Offset, buffer.Count);
fixed (byte* pBuffer = &buffer.Array[buffer.Offset])
{
BCryptUtil.GenRandom(pBuffer, buffer.Count);
}
}
}
}

View File

@ -0,0 +1,65 @@
using System;
using System.Security.Cryptography;
namespace Microsoft.AspNet.Security.DataProtection
{
/// <summary>
/// Helper class to derive keys from low-entropy passwords using the PBKDF2 algorithm.
/// </summary>
public static class PBKDF2
{
/// <summary>
/// Derives a key from a low-entropy password.
/// </summary>
/// <param name="algorithmName">The name of the PRF to use for key derivation.</param>
/// <param name="password">The low-entropy password from which to generate a key.</param>
/// <param name="salt">The salt used to randomize the key derivation.</param>
/// <param name="iterationCount">The number of iterations to perform.</param>
/// <param name="numBytesToDerive">The desired byte length of the derived key.</param>
/// <returns>A key derived from the provided password.</returns>
/// <remarks>For compatibility with the Rfc2898DeriveBytes class, specify "SHA1" for the <em>algorithmName</em> parameter.</remarks>
public unsafe static byte[] DeriveKey(string algorithmName, byte[] password, byte[] salt, ulong iterationCount, uint numBytesToDerive)
{
if (String.IsNullOrEmpty(algorithmName))
{
throw new ArgumentNullException("algorithmName");
}
if (password == null || password.Length == 0)
{
throw new ArgumentNullException("password");
}
if (salt == null || salt.Length == 0)
{
throw new ArgumentNullException("salt");
}
if (iterationCount <= 0)
{
throw new ArgumentOutOfRangeException("iterationCount");
}
byte[] derivedKey = new byte[numBytesToDerive];
int status;
using (BCryptAlgorithmHandle algHandle = Algorithms.CreateGenericHMACHandleFromPrimitiveProvider(algorithmName))
{
fixed (byte* pPassword = password)
fixed (byte* pSalt = salt)
fixed (byte* pDerivedKey = derivedKey)
{
status = UnsafeNativeMethods.BCryptDeriveKeyPBKDF2(
algHandle, pPassword, (uint)password.Length, pSalt, (uint)salt.Length, iterationCount,
pDerivedKey, numBytesToDerive, dwFlags: 0);
}
}
if (status == 0 /* STATUS_SUCCESS */)
{
return derivedKey;
}
else
{
throw new CryptographicException(status);
}
}
}
}

View File

@ -52,6 +52,19 @@ namespace Microsoft.AspNet.Security.DataProtection
[Out] out uint pcbResult,
[In] BCryptEncryptFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd433795(v=vs.85).aspx
internal static extern int BCryptDeriveKeyPBKDF2(
[In] BCryptAlgorithmHandle hPrf,
[In] byte* pbPassword,
[In] uint cbPassword,
[In] byte* pbSalt,
[In] uint cbSalt,
[In] ulong cIterations,
[In] byte* pbDerivedKey,
[In] uint cbDerivedKey,
[In] uint 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(