Add unit tests for CngAuthenticatedEncryptorBase, PBKDF2, and SP800_108-CTR-HMACSHA512.
This commit is contained in:
parent
796acc0e34
commit
cd33cbfc8f
|
|
@ -219,7 +219,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
|
|||
try
|
||||
{
|
||||
_keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment<byte>(decryptedKdk));
|
||||
DeriveKeysWithContextHeader(
|
||||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader(
|
||||
kdk: decryptedKdk,
|
||||
label: additionalAuthenticatedData,
|
||||
contextHeader: _contextHeader,
|
||||
|
|
@ -285,14 +285,6 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
|
|||
}
|
||||
}
|
||||
|
||||
private static void DeriveKeysWithContextHeader(byte[] kdk, ArraySegment<byte> label, byte[] contextHeader, ArraySegment<byte> context, Func<byte[], HashAlgorithm> prfFactory, ArraySegment<byte> output)
|
||||
{
|
||||
byte[] combinedContext = new byte[checked(contextHeader.Length + context.Count)];
|
||||
Buffer.BlockCopy(contextHeader, 0, combinedContext, 0, contextHeader.Length);
|
||||
Buffer.BlockCopy(context.Array, context.Offset, combinedContext, contextHeader.Length, context.Count);
|
||||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(kdk, label, new ArraySegment<byte>(combinedContext), prfFactory, output);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_keyDerivationKey.Dispose();
|
||||
|
|
@ -336,7 +328,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
|
|||
try
|
||||
{
|
||||
_keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment<byte>(decryptedKdk));
|
||||
DeriveKeysWithContextHeader(
|
||||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader(
|
||||
kdk: decryptedKdk,
|
||||
label: additionalAuthenticatedData,
|
||||
contextHeader: _contextHeader,
|
||||
|
|
|
|||
|
|
@ -53,5 +53,13 @@ namespace Microsoft.AspNet.Security.DataProtection.SP800_108
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeriveKeysWithContextHeader(byte[] kdk, ArraySegment<byte> label, byte[] contextHeader, ArraySegment<byte> context, Func<byte[], HashAlgorithm> prfFactory, ArraySegment<byte> output)
|
||||
{
|
||||
byte[] combinedContext = new byte[checked(contextHeader.Length + context.Count)];
|
||||
Buffer.BlockCopy(contextHeader, 0, combinedContext, 0, contextHeader.Length);
|
||||
Buffer.BlockCopy(context.Array, context.Offset, combinedContext, contextHeader.Length, context.Count);
|
||||
DeriveKeys(kdk, label, new ArraySegment<byte>(combinedContext), prfFactory, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Security.DataProtection.SafeHandles;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Microsoft.AspNet.Security.DataProtection.Cng;
|
||||
|
||||
namespace Microsoft.AspNet.Security.DataProtection.SP800_108
|
||||
{
|
||||
|
|
@ -16,8 +15,6 @@ namespace Microsoft.AspNet.Security.DataProtection.SP800_108
|
|||
/// </remarks>
|
||||
internal unsafe static class SP800_108_CTR_HMACSHA512Util
|
||||
{
|
||||
private static readonly bool _isWin8OrLater = GetIsRunningWin8OrLater();
|
||||
|
||||
// Creates a provider with an empty key.
|
||||
public static ISP800_108_CTR_HMACSHA512Provider CreateEmptyProvider()
|
||||
{
|
||||
|
|
@ -28,9 +25,14 @@ namespace Microsoft.AspNet.Security.DataProtection.SP800_108
|
|||
// Creates a provider from the given key.
|
||||
public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(byte* pbKdk, uint cbKdk)
|
||||
{
|
||||
return (_isWin8OrLater)
|
||||
? (ISP800_108_CTR_HMACSHA512Provider)new Win8SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk)
|
||||
: (ISP800_108_CTR_HMACSHA512Provider)new Win7SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk);
|
||||
if (OSVersionUtil.IsBCryptOnWin8OrLaterAvailable())
|
||||
{
|
||||
return new Win7SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Win8SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a provider from the given secret.
|
||||
|
|
@ -57,37 +59,5 @@ namespace Microsoft.AspNet.Security.DataProtection.SP800_108
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GetIsRunningWin8OrLater()
|
||||
{
|
||||
// In priority order, our three implementations are Win8, Win7, and "other".
|
||||
|
||||
const string BCRYPT_LIB = "bcrypt.dll";
|
||||
|
||||
SafeLibraryHandle bcryptLibHandle = null;
|
||||
try
|
||||
{
|
||||
bcryptLibHandle = SafeLibraryHandle.Open(BCRYPT_LIB);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// BCrypt not available? We'll fall back to managed code paths.
|
||||
}
|
||||
|
||||
if (bcryptLibHandle != null)
|
||||
{
|
||||
using (bcryptLibHandle)
|
||||
{
|
||||
if (bcryptLibHandle.DoesProcExist("BCryptKeyDerivation"))
|
||||
{
|
||||
// We're running on Win8+.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not running on Win8+
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNet.Security.DataProtection.SP800_108
|
|||
{
|
||||
hashHandle.HashData(pbKdk, cbKdk, pbHashedKey, SHA512_DIGEST_SIZE_IN_BYTES);
|
||||
}
|
||||
return CachedAlgorithmHandles.SP800_108_CTR_HMAC.GenerateSymmetricKey(pbKdk, cbKdk);
|
||||
return CachedAlgorithmHandles.SP800_108_CTR_HMAC.GenerateSymmetricKey(pbHashedKey, SHA512_DIGEST_SIZE_IN_BYTES);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ namespace Microsoft.AspNet.Security.DataProtection.SafeHandles
|
|||
public BCryptHashHandle CreateHmac(byte* pbKey, uint cbKey)
|
||||
{
|
||||
Debug.Assert(pbKey != null);
|
||||
Debug.Assert(cbKey != 0);
|
||||
|
||||
return CreateHashImpl(pbKey, cbKey);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Security.DataProtection.Cng;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
|
||||
{
|
||||
public unsafe class CngAuthenticatedEncryptorBaseTests
|
||||
{
|
||||
[Fact]
|
||||
public void Decrypt_ForwardsArraySegment()
|
||||
{
|
||||
// Arrange
|
||||
var ciphertext = new ArraySegment<byte>(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, 3, 2);
|
||||
var aad = new ArraySegment<byte>(new byte[] { 0x10, 0x11, 0x12, 0x13, 0x14 }, 1, 4);
|
||||
|
||||
var encryptorMock = new Mock<MockableEncryptor>();
|
||||
encryptorMock
|
||||
.Setup(o => o.DecryptHook(It.IsAny<IntPtr>(), 2, It.IsAny<IntPtr>(), 4))
|
||||
.Returns((IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) =>
|
||||
{
|
||||
// ensure that pointers started at the right place
|
||||
Assert.Equal((byte)0x03, *(byte*)pbCiphertext);
|
||||
Assert.Equal((byte)0x11, *(byte*)pbAdditionalAuthenticatedData);
|
||||
return new byte[] { 0x20, 0x21, 0x22 };
|
||||
});
|
||||
|
||||
// Act
|
||||
var retVal = encryptorMock.Object.Decrypt(ciphertext, aad);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decrypt_HandlesEmptyAADPointerFixup()
|
||||
{
|
||||
// Arrange
|
||||
var ciphertext = new ArraySegment<byte>(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, 3, 2);
|
||||
var aad = new ArraySegment<byte>(new byte[0]);
|
||||
|
||||
var encryptorMock = new Mock<MockableEncryptor>();
|
||||
encryptorMock
|
||||
.Setup(o => o.DecryptHook(It.IsAny<IntPtr>(), 2, It.IsAny<IntPtr>(), 0))
|
||||
.Returns((IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) =>
|
||||
{
|
||||
// ensure that pointers started at the right place
|
||||
Assert.Equal((byte)0x03, *(byte*)pbCiphertext);
|
||||
Assert.NotEqual(IntPtr.Zero, pbAdditionalAuthenticatedData); // CNG will complain if this pointer is zero
|
||||
return new byte[] { 0x20, 0x21, 0x22 };
|
||||
});
|
||||
|
||||
// Act
|
||||
var retVal = encryptorMock.Object.Decrypt(ciphertext, aad);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decrypt_HandlesEmptyCiphertextPointerFixup()
|
||||
{
|
||||
// Arrange
|
||||
var ciphertext = new ArraySegment<byte>(new byte[0]);
|
||||
var aad = new ArraySegment<byte>(new byte[] { 0x10, 0x11, 0x12, 0x13, 0x14 }, 1, 4);
|
||||
|
||||
var encryptorMock = new Mock<MockableEncryptor>();
|
||||
encryptorMock
|
||||
.Setup(o => o.DecryptHook(It.IsAny<IntPtr>(), 0, It.IsAny<IntPtr>(), 4))
|
||||
.Returns((IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) =>
|
||||
{
|
||||
// ensure that pointers started at the right place
|
||||
Assert.NotEqual(IntPtr.Zero, pbCiphertext); // CNG will complain if this pointer is zero
|
||||
Assert.Equal((byte)0x11, *(byte*)pbAdditionalAuthenticatedData);
|
||||
return new byte[] { 0x20, 0x21, 0x22 };
|
||||
});
|
||||
|
||||
// Act
|
||||
var retVal = encryptorMock.Object.Decrypt(ciphertext, aad);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
|
||||
}
|
||||
|
||||
internal abstract class MockableEncryptor : CngAuthenticatedEncryptorBase
|
||||
{
|
||||
public override void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract byte[] DecryptHook(IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData);
|
||||
|
||||
protected override sealed unsafe byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData)
|
||||
{
|
||||
return DecryptHook((IntPtr)pbCiphertext, cbCiphertext, (IntPtr)pbAdditionalAuthenticatedData, cbAdditionalAuthenticatedData);
|
||||
}
|
||||
|
||||
public abstract byte[] EncryptHook(IntPtr pbPlaintext, uint cbPlaintext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
|
||||
|
||||
protected override sealed unsafe byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer)
|
||||
{
|
||||
return EncryptHook((IntPtr)pbPlaintext, cbPlaintext, (IntPtr)pbAdditionalAuthenticatedData, cbAdditionalAuthenticatedData, cbPreBuffer, cbPostBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,8 +22,5 @@
|
|||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.Security.DataProtection.PBKDF2;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Security.DataProtection.Test.PBKDF2
|
||||
{
|
||||
public class Pbkdf2Tests
|
||||
{
|
||||
// The 'numBytesRequested' parameters below are chosen to exercise code paths where
|
||||
// this value straddles the digest length of the PRF. We only use 5 iterations so
|
||||
// that our unit tests are fast.
|
||||
[Theory]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha1, 5, 160 / 8 - 1, "efmxNcKD/U1urTEDGvsThlPnHA==")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha1, 5, 160 / 8 + 0, "efmxNcKD/U1urTEDGvsThlPnHDI=")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha1, 5, 160 / 8 + 1, "efmxNcKD/U1urTEDGvsThlPnHDLk")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha256, 5, 256 / 8 - 1, "JRNz8bPKS02EG1vf7eWjA64IeeI+TI8gBEwb1oVvRA==")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha256, 5, 256 / 8 + 0, "JRNz8bPKS02EG1vf7eWjA64IeeI+TI8gBEwb1oVvRLo=")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha256, 5, 256 / 8 + 1, "JRNz8bPKS02EG1vf7eWjA64IeeI+TI8gBEwb1oVvRLpk")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha512, 5, 512 / 8 - 1, "ZTallQJrFn0279xIzaiA1XqatVTGei+ZjKngA7bIMtKMDUw6YJeGUQpFG8iGTgN+ri3LNDktNbzwfcSyZmm9")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha512, 5, 512 / 8 + 0, "ZTallQJrFn0279xIzaiA1XqatVTGei+ZjKngA7bIMtKMDUw6YJeGUQpFG8iGTgN+ri3LNDktNbzwfcSyZmm90Q==")]
|
||||
[InlineData("my-password", KeyDerivationPrf.Sha512, 5, 512 / 8 + 1, "ZTallQJrFn0279xIzaiA1XqatVTGei+ZjKngA7bIMtKMDUw6YJeGUQpFG8iGTgN+ri3LNDktNbzwfcSyZmm90Wk=")]
|
||||
public void RunTest_Normal(string password, KeyDerivationPrf prf, int iterationCount, int numBytesRequested, string expectedValueAsBase64)
|
||||
{
|
||||
// Arrange
|
||||
byte[] salt = new byte[256];
|
||||
for (int i = 0; i < salt.Length; i++)
|
||||
{
|
||||
salt[i] = (byte)i;
|
||||
}
|
||||
|
||||
// Act & assert - fully managed, Win7, and Win8
|
||||
TestProvider<ManagedPbkdf2Provider>(password, salt, prf, iterationCount, numBytesRequested, expectedValueAsBase64);
|
||||
TestProvider<Win7Pbkdf2Provider>(password, salt, prf, iterationCount, numBytesRequested, expectedValueAsBase64);
|
||||
TestProvider<Win8Pbkdf2Provider>(password, salt, prf, iterationCount, numBytesRequested, expectedValueAsBase64);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RunTest_WithLongPassword()
|
||||
{
|
||||
// Arrange
|
||||
string password = new String('x', 50000); // 50,000 char password
|
||||
byte[] salt = Encoding.UTF8.GetBytes("salt");
|
||||
const string expectedDerivedKeyBase64 = "Sc+V/c3fiZq5Z5qH3iavAiojTsW97FAp2eBNmCQAwCNzA8hfhFFYyQLIMK65qPnBFHOHXQPwAxNQNhaEAH9hzfiaNBSRJpF9V4rpl02d5ZpI6cZbsQFF7TJW7XJzQVpYoPDgJlg0xVmYLhn1E9qMtUVUuXsBjOOdd7K1M+ZI00c=";
|
||||
const KeyDerivationPrf prf = KeyDerivationPrf.Sha256;
|
||||
const int iterationCount = 5;
|
||||
const int numBytesRequested = 128;
|
||||
|
||||
// Act & assert - fully managed, Win7, and Win8
|
||||
TestProvider<ManagedPbkdf2Provider>(password, salt, prf, iterationCount, numBytesRequested, expectedDerivedKeyBase64);
|
||||
TestProvider<Win7Pbkdf2Provider>(password, salt, prf, iterationCount, numBytesRequested, expectedDerivedKeyBase64);
|
||||
TestProvider<Win8Pbkdf2Provider>(password, salt, prf, iterationCount, numBytesRequested, expectedDerivedKeyBase64);
|
||||
}
|
||||
|
||||
private static void TestProvider<TProvider>(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested, string expectedDerivedKeyAsBase64)
|
||||
where TProvider : IPbkdf2Provider, new()
|
||||
{
|
||||
byte[] derivedKey = new TProvider().DeriveKey(password, salt, prf, iterationCount, numBytesRequested);
|
||||
Assert.Equal(numBytesRequested, derivedKey.Length);
|
||||
Assert.Equal(expectedDerivedKeyAsBase64, Convert.ToBase64String(derivedKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// for unit testing
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.Security.DataProtection.SP800_108;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Security.DataProtection.Test.SP800_108
|
||||
{
|
||||
public unsafe class SP800_108Tests
|
||||
{
|
||||
private delegate ISP800_108_CTR_HMACSHA512Provider ProviderFactory(byte* pbKdk, uint cbKdk);
|
||||
|
||||
// The 'numBytesRequested' parameters below are chosen to exercise code paths where
|
||||
// this value straddles the digest length of the PRF (which is hardcoded to HMACSHA512).
|
||||
[Theory]
|
||||
[InlineData(512 / 8 - 1, "V47WmHzPSkdC2vkLAomIjCzZlDOAetll3yJLcSvon7LJFjJpEN+KnSNp+gIpeydKMsENkflbrIZ/3s6GkEaH")]
|
||||
[InlineData(512 / 8 + 0, "mVaFM4deXLl610CmnCteNzxgbM/VkmKznAlPauHcDBn0le06uOjAKLHx0LfoU2/Ttq9nd78Y6Nk6wArmdwJgJg==")]
|
||||
[InlineData(512 / 8 + 1, "GaHPeqdUxriFpjRtkYQYWr5/iqneD/+hPhVJQt4rXblxSpB1UUqGqL00DMU/FJkX0iMCfqUjQXtXyfks+p++Ev4=")]
|
||||
public void DeriveKeyWithContextHeader_Normal(int numDerivedBytes, string expectedDerivedSubkeyAsBase64)
|
||||
{
|
||||
// Arrange
|
||||
byte[] kdk = Encoding.UTF8.GetBytes("kdk");
|
||||
byte[] label = Encoding.UTF8.GetBytes("label");
|
||||
byte[] contextHeader = Encoding.UTF8.GetBytes("contextHeader");
|
||||
byte[] context = Encoding.UTF8.GetBytes("context");
|
||||
|
||||
// Act & assert - managed, Win7, Win8
|
||||
TestManagedKeyDerivation(kdk, label, contextHeader, context, numDerivedBytes, expectedDerivedSubkeyAsBase64);
|
||||
TestCngKeyDerivation((pbKdk, cbKdk) => new Win7SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk), kdk, label, contextHeader, context, numDerivedBytes, expectedDerivedSubkeyAsBase64);
|
||||
TestCngKeyDerivation((pbKdk, cbKdk) => new Win8SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk), kdk, label, contextHeader, context, numDerivedBytes, expectedDerivedSubkeyAsBase64);
|
||||
}
|
||||
|
||||
// The 'numBytesRequested' parameters below are chosen to exercise code paths where
|
||||
// this value straddles the digest length of the PRF (which is hardcoded to HMACSHA512).
|
||||
[Theory]
|
||||
[InlineData(512 / 8 - 1, "rt2hM6kkQ8hAXmkHx0TU4o3Q+S7fie6b3S1LAq107k++P9v8uSYA2G+WX3pJf9ZkpYrTKD7WUIoLkgA1R9lk")]
|
||||
[InlineData(512 / 8 + 0, "RKiXmHSrWq5gkiRSyNZWNJrMR0jDyYHJMt9odOayRAE5wLSX2caINpQmfzTH7voJQi3tbn5MmD//dcspghfBiw==")]
|
||||
[InlineData(512 / 8 + 1, "KedXO0zAIZ3AfnPqY1NnXxpC3HDHIxefG4bwD3g6nWYEc5+q7pjbam71Yqj0zgHMNC9Z7BX3wS1/tajFocRWZUk=")]
|
||||
public void DeriveKeyWithContextHeader_LongKey(int numDerivedBytes, string expectedDerivedSubkeyAsBase64)
|
||||
{
|
||||
// Arrange
|
||||
byte[] kdk = new byte[50000]; // CNG can't normally handle a 50,000 byte KDK, but we coerce it into working :)
|
||||
for (int i = 0; i < kdk.Length; i++)
|
||||
{
|
||||
kdk[i] = (byte)i;
|
||||
}
|
||||
|
||||
byte[] label = Encoding.UTF8.GetBytes("label");
|
||||
byte[] contextHeader = Encoding.UTF8.GetBytes("contextHeader");
|
||||
byte[] context = Encoding.UTF8.GetBytes("context");
|
||||
|
||||
// Act & assert - managed, Win7, Win8
|
||||
TestManagedKeyDerivation(kdk, label, contextHeader, context, numDerivedBytes, expectedDerivedSubkeyAsBase64);
|
||||
TestCngKeyDerivation((pbKdk, cbKdk) => new Win7SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk), kdk, label, contextHeader, context, numDerivedBytes, expectedDerivedSubkeyAsBase64);
|
||||
TestCngKeyDerivation((pbKdk, cbKdk) => new Win8SP800_108_CTR_HMACSHA512Provider(pbKdk, cbKdk), kdk, label, contextHeader, context, numDerivedBytes, expectedDerivedSubkeyAsBase64);
|
||||
}
|
||||
|
||||
private static void TestCngKeyDerivation(ProviderFactory factory, byte[] kdk, byte[] label, byte[] contextHeader, byte[] context, int numDerivedBytes, string expectedDerivedSubkeyAsBase64)
|
||||
{
|
||||
byte[] derivedSubkey = new byte[numDerivedBytes];
|
||||
|
||||
fixed (byte* pbKdk = kdk)
|
||||
fixed (byte* pbLabel = label)
|
||||
fixed (byte* pbContext = context)
|
||||
fixed (byte* pbDerivedSubkey = derivedSubkey)
|
||||
{
|
||||
ISP800_108_CTR_HMACSHA512Provider provider = factory(pbKdk, (uint)kdk.Length);
|
||||
provider.DeriveKeyWithContextHeader(pbLabel, (uint)label.Length, contextHeader, pbContext, (uint)context.Length, pbDerivedSubkey, (uint)derivedSubkey.Length);
|
||||
}
|
||||
|
||||
Assert.Equal(expectedDerivedSubkeyAsBase64, Convert.ToBase64String(derivedSubkey));
|
||||
}
|
||||
|
||||
private static void TestManagedKeyDerivation(byte[] kdk, byte[] label, byte[] contextHeader, byte[] context, int numDerivedBytes, string expectedDerivedSubkeyAsBase64)
|
||||
{
|
||||
var labelSegment = new ArraySegment<byte>(new byte[label.Length + 10], 3, label.Length);
|
||||
Buffer.BlockCopy(label, 0, labelSegment.Array, labelSegment.Offset, labelSegment.Count);
|
||||
var contextSegment = new ArraySegment<byte>(new byte[context.Length + 10], 5, context.Length);
|
||||
Buffer.BlockCopy(context, 0, contextSegment.Array, contextSegment.Offset, contextSegment.Count);
|
||||
var derivedSubkeySegment = new ArraySegment<byte>(new byte[numDerivedBytes + 10], 4, numDerivedBytes);
|
||||
|
||||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader(kdk, labelSegment, contextHeader, contextSegment,
|
||||
bytes => new HMACSHA512(bytes), derivedSubkeySegment);
|
||||
Assert.Equal(expectedDerivedSubkeyAsBase64, Convert.ToBase64String(derivedSubkeySegment.AsStandaloneArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue