未验证 提交 d7bf6030 编写于 作者: K Kevin Jones 提交者: GitHub

Add support for SP800-108 CTR Key Derivation Function

This introduces a class for producing values per the "KDF in Counter Mode" section in NIST Special Publication 800-108r1 (Recommendation for Key Derivation Using Pseudorandom Functions).

The `SP800108HmacCounterKdf` class is part of the inbox cryptography library on .NET 8.  Based on demonstrated need for older TFMs, the type is also being exposed via a NuGet package: Microsoft.Bcl.Cryptography.  This package may, in the future, contain other types that belong as part of inbox cryptography but have a demonstrated need to be available to older TFMs.
上级 32ea3396
......@@ -25,6 +25,15 @@ public enum BCryptAlgPseudoHandle : uint
BCRYPT_PBKDF2_ALG_HANDLE = 0x00000331,
}
internal static bool PseudoHandlesSupported { get; } = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 0);
internal static bool PseudoHandlesSupported { get; } =
#if NET
OperatingSystem.IsWindowsVersionAtLeast(10, 0, 0);
#elif NETSTANDARD2_0_OR_GREATER
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major >= 10;
#elif NETFRAMEWORK
Environment.OSVersion.Version.Major >= 10;
#else
#error Unhandled platform targets
#endif
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Security.Cryptography
{
internal abstract class SP800108HmacCounterKdfImplementationBase : IDisposable
{
internal abstract void DeriveBytes(ReadOnlySpan<byte> label, ReadOnlySpan<byte> context, Span<byte> destination);
internal abstract void DeriveBytes(byte[] label, byte[] context, Span<byte> destination);
internal abstract void DeriveBytes(ReadOnlySpan<char> label, ReadOnlySpan<char> context, Span<byte> destination);
public abstract void Dispose();
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using BCryptBuffer = Interop.BCrypt.BCryptBuffer;
using CngBufferDescriptors = Interop.BCrypt.CngBufferDescriptors;
using NTSTATUS = Interop.BCrypt.NTSTATUS;
namespace System.Security.Cryptography
{
internal sealed partial class SP800108HmacCounterKdfImplementationCng : SP800108HmacCounterKdfImplementationBase
{
private const string BCRYPT_SP800108_CTR_HMAC_ALGORITHM = "SP800_108_CTR_HMAC";
private const nuint BCRYPT_SP800108_CTR_HMAC_ALG_HANDLE = 0x00000341;
private const int CharToBytesStackBufferSize = 256;
// A cached algorithm handle. On Windows 10 this is null if we are using a psuedo handle.
private static readonly SafeBCryptAlgorithmHandle? s_sp800108CtrHmacAlgorithmHandle = OpenAlgorithmHandle();
private readonly SafeBCryptKeyHandle _keyHandle;
private readonly HashAlgorithmName _hashAlgorithm;
public override void Dispose()
{
_keyHandle.Dispose();
}
internal override void DeriveBytes(byte[] label, byte[] context, Span<byte> destination)
{
DeriveBytes(new ReadOnlySpan<byte>(label), new ReadOnlySpan<byte>(context), destination);
}
internal override unsafe void DeriveBytes(ReadOnlySpan<byte> label, ReadOnlySpan<byte> context, Span<byte> destination)
{
if (destination.Length == 0)
{
return;
}
Debug.Assert(destination.Length <= 0x1FFFFFFF);
Debug.Assert(_hashAlgorithm.Name is not null);
fixed (byte* pLabel = label)
fixed (byte* pContext = context)
fixed (byte* pDestination = destination)
fixed (char* pHashAlgorithm = _hashAlgorithm.Name)
{
const int BCryptBufferLength = 3;
BCryptBuffer* buffers = stackalloc BCryptBuffer[BCryptBufferLength];
buffers[0].BufferType = CngBufferDescriptors.KDF_LABEL;
buffers[0].pvBuffer = (IntPtr)pLabel;
buffers[0].cbBuffer = label.Length;
buffers[1].BufferType = CngBufferDescriptors.KDF_CONTEXT;
buffers[1].pvBuffer = (IntPtr)pContext;
buffers[1].cbBuffer = context.Length;
buffers[2].BufferType = CngBufferDescriptors.KDF_HASH_ALGORITHM;
buffers[2].pvBuffer = (IntPtr)pHashAlgorithm;
buffers[2].cbBuffer = (_hashAlgorithm.Name.Length + 1) * 2; // +1 for the null terminator.
Interop.BCrypt.BCryptBufferDesc bufferDesc;
bufferDesc.ulVersion = Interop.BCrypt.BCRYPTBUFFER_VERSION;
bufferDesc.cBuffers = BCryptBufferLength;
bufferDesc.pBuffers = (IntPtr)buffers;
NTSTATUS deriveStatus = Interop.BCrypt.BCryptKeyDerivation(
_keyHandle,
&bufferDesc,
pDestination,
destination.Length,
out uint resultLength,
dwFlags: 0);
if (deriveStatus != NTSTATUS.STATUS_SUCCESS)
{
throw Interop.BCrypt.CreateCryptographicException(deriveStatus);
}
if (destination.Length != resultLength)
{
Debug.Fail("BCryptKeyDerivation resultLength != destination.Length");
throw new CryptographicException();
}
}
}
internal override void DeriveBytes(ReadOnlySpan<char> label, ReadOnlySpan<char> context, Span<byte> destination)
{
using (Utf8DataEncoding labelData = new Utf8DataEncoding(label, stackalloc byte[CharToBytesStackBufferSize]))
using (Utf8DataEncoding contextData = new Utf8DataEncoding(context, stackalloc byte[CharToBytesStackBufferSize]))
{
DeriveBytes(labelData.Utf8Bytes, contextData.Utf8Bytes, destination);
}
}
internal static void DeriveBytesOneShot(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
Debug.Assert(destination.Length <= 0x1FFFFFFF);
using (SP800108HmacCounterKdfImplementationCng kdf = new SP800108HmacCounterKdfImplementationCng(key, hashAlgorithm))
{
kdf.DeriveBytes(label, context, destination);
}
}
internal static void DeriveBytesOneShot(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<char> label,
ReadOnlySpan<char> context,
Span<byte> destination)
{
if (destination.Length == 0)
{
return;
}
using (Utf8DataEncoding labelData = new Utf8DataEncoding(label, stackalloc byte[CharToBytesStackBufferSize]))
using (Utf8DataEncoding contextData = new Utf8DataEncoding(context, stackalloc byte[CharToBytesStackBufferSize]))
{
DeriveBytesOneShot(key, hashAlgorithm, labelData.Utf8Bytes, contextData.Utf8Bytes, destination);
}
}
private static unsafe SafeBCryptKeyHandle CreateSymmetricKey(byte* symmetricKey, int symmetricKeyLength)
{
NTSTATUS generateKeyStatus;
SafeBCryptKeyHandle keyHandle;
if (s_sp800108CtrHmacAlgorithmHandle is not null)
{
generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
s_sp800108CtrHmacAlgorithmHandle,
out keyHandle,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
symmetricKey,
symmetricKeyLength,
dwFlags: 0);
}
else
{
generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
BCRYPT_SP800108_CTR_HMAC_ALG_HANDLE,
out keyHandle,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
symmetricKey,
symmetricKeyLength,
dwFlags: 0);
}
if (generateKeyStatus != NTSTATUS.STATUS_SUCCESS)
{
keyHandle.Dispose();
throw Interop.BCrypt.CreateCryptographicException(generateKeyStatus);
}
Debug.Assert(!keyHandle.IsInvalid);
return keyHandle;
}
// Returns null if the platform is Windows 10+ and psuedo handles should be used.
private static SafeBCryptAlgorithmHandle? OpenAlgorithmHandle()
{
if (!Interop.BCrypt.PseudoHandlesSupported)
{
NTSTATUS openStatus = Interop.BCrypt.BCryptOpenAlgorithmProvider(
out SafeBCryptAlgorithmHandle sp800108CtrHmacAlgorithmHandle,
BCRYPT_SP800108_CTR_HMAC_ALGORITHM,
null,
Interop.BCrypt.BCryptOpenAlgorithmProviderFlags.None);
if (openStatus != NTSTATUS.STATUS_SUCCESS)
{
sp800108CtrHmacAlgorithmHandle.Dispose();
throw Interop.BCrypt.CreateCryptographicException(openStatus);
}
return sp800108CtrHmacAlgorithmHandle;
}
return null;
}
private static int GetHashBlockSize(string hashAlgorithmName)
{
// Block sizes per NIST FIPS pub 180-4.
switch (hashAlgorithmName)
{
case HashAlgorithmNames.SHA1:
case HashAlgorithmNames.SHA256:
return 512 / 8;
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
return 1024 / 8;
default:
Debug.Fail($"Unexpected hash algorithm '{hashAlgorithmName}'");
throw new CryptographicException();
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers.Binary;
using System.Diagnostics;
using System.Threading;
using System.Runtime.Versioning;
#pragma warning disable CA1513
namespace System.Security.Cryptography
{
#if !NET7_0_OR_GREATER && NET
[UnsupportedOSPlatform("browser")]
#endif
internal sealed partial class SP800108HmacCounterKdfImplementationManaged : SP800108HmacCounterKdfImplementationBase
{
private byte[] _key;
private int _keyReferenceCount = 1;
private int _disposed;
private readonly HashAlgorithmName _hashAlgorithm;
internal override void DeriveBytes(ReadOnlySpan<byte> label, ReadOnlySpan<byte> context, Span<byte> destination)
{
byte[] key = IncrementAndAcquireKey();
try
{
DeriveBytesOneShot(key, _hashAlgorithm, label, context, destination);
}
finally
{
ReleaseKey();
}
}
internal override void DeriveBytes(ReadOnlySpan<char> label, ReadOnlySpan<char> context, Span<byte> destination)
{
byte[] key = IncrementAndAcquireKey();
try
{
DeriveBytesOneShot(key, _hashAlgorithm, label, context, destination);
}
finally
{
ReleaseKey();
}
}
internal override void DeriveBytes(byte[] label, byte[] context, Span<byte> destination)
{
byte[] key = IncrementAndAcquireKey();
try
{
DeriveBytesOneShot(key, _hashAlgorithm, label, context, destination);
}
finally
{
ReleaseKey();
}
}
public override void Dispose()
{
if (Interlocked.Exchange(ref _disposed, 1) == 0)
{
ReleaseKey();
}
}
private byte[] IncrementAndAcquireKey()
{
while (true)
{
int current = Volatile.Read(ref _keyReferenceCount);
if (current == 0)
{
throw new ObjectDisposedException(nameof(SP800108HmacCounterKdfImplementationManaged));
}
Debug.Assert(current > 0);
int incrementedCount = checked(current + 1);
if (Interlocked.CompareExchange(ref _keyReferenceCount, incrementedCount, current) == current)
{
return _key;
}
}
}
public void ReleaseKey()
{
int newReferenceCount = Interlocked.Decrement(ref _keyReferenceCount);
Debug.Assert(newReferenceCount >= 0, newReferenceCount.ToString());
if (newReferenceCount == 0)
{
ZeroKey();
}
}
private void ZeroKey()
{
CryptographicOperations.ZeroMemory(_key);
_key = null!;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text;
namespace System.Security.Cryptography
{
internal readonly ref struct Utf8DataEncoding
{
internal static Encoding ThrowingUtf8Encoding { get; } = new UTF8Encoding(false, true);
private readonly byte[]? _rented;
private readonly Span<byte> _buffer;
internal Utf8DataEncoding(ReadOnlySpan<char> data, Span<byte> stackBuffer)
{
int maxLength = ThrowingUtf8Encoding.GetMaxByteCount(data.Length);
_buffer = (uint)maxLength <= stackBuffer.Length ?
stackBuffer :
(_rented = CryptoPool.Rent(maxLength));
int written = ThrowingUtf8Encoding.GetBytes(data, _buffer);
_buffer = _buffer.Slice(0, written);
}
internal ReadOnlySpan<byte> Utf8Bytes => _buffer;
internal void Dispose()
{
CryptographicOperations.ZeroMemory(_buffer);
if (_rented is not null)
{
CryptoPool.Return(_rented, clearSize: 0);
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text;
using Xunit;
namespace System.Security.Cryptography.Tests
{
public static partial class SP800108HmacCounterKdfTests
{
[Fact]
public static void DeriveBytes_Allocating_ArrayBytes_ArgValidation()
{
Assert.Throws<ArgumentNullException>("key", () =>
SP800108HmacCounterKdf.DeriveBytes(key: (byte[])null, HashAlgorithmName.SHA256, s_labelBytes, s_contextBytes, 42));
Assert.Throws<ArgumentNullException>("label", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, label: (byte[])null, s_contextBytes, 42));
Assert.Throws<ArgumentNullException>("context", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, s_labelBytes, context: (byte[])null, 42));
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_nullHash, s_labelBytes, s_contextBytes, 42));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_emptyHash, s_labelBytes, s_contextBytes, 42));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_unknownHash, s_labelBytes, s_contextBytes, 42));
Assert.Contains(s_unknownHash.Name, ex.Message);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, s_labelBytes, s_contextBytes, -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, s_labelBytes, s_contextBytes, 0x20000000));
}
[Fact]
public static void DeriveBytes_Allocating_String_ArgValidation()
{
Assert.Throws<ArgumentNullException>("key", () =>
SP800108HmacCounterKdf.DeriveBytes(key: (byte[])null, HashAlgorithmName.SHA256, Label, Context, 42));
Assert.Throws<ArgumentNullException>("label", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, label: (string)null, Context, 42));
Assert.Throws<ArgumentNullException>("context", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label, context: (string)null, 42));
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_nullHash, Label, Context, 42));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_emptyHash, Label, Context, 42));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_unknownHash, Label, Context, 42));
Assert.Contains(s_unknownHash.Name, ex.Message);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label, Context, -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label, Context, 0x20000000));
}
[Fact]
public static void DeriveBytes_Allocating_SpanBytes_ArgValidation()
{
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk.AsSpan(), s_nullHash, s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), 42));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk.AsSpan(), s_emptyHash, s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), 42));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk.AsSpan(), s_unknownHash, s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), 42));
Assert.Contains(s_unknownHash.Name, ex.Message);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk.AsSpan(), HashAlgorithmName.SHA256, s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk.AsSpan(), HashAlgorithmName.SHA256, s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), 0x20000000));
}
[Fact]
public static void DeriveBytes_BufferFill_SpanBytes_ArgValidation()
{
byte[] destination = new byte[42];
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_nullHash, s_labelBytes, s_contextBytes, destination));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_emptyHash, s_labelBytes, s_contextBytes, destination));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_unknownHash, s_labelBytes, s_contextBytes, destination));
Assert.Contains(s_unknownHash.Name, ex.Message);
Assert.Throws<ArgumentOutOfRangeException>("destination", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, s_labelBytes, s_contextBytes, GetOversizedSpan()));
}
[Fact]
public static void DeriveBytes_Allocating_SpanChars_ArgValidation()
{
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_nullHash, Label.AsSpan(), Context.AsSpan(), 42));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_emptyHash, Label.AsSpan(), Context.AsSpan(), 42));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_unknownHash, Label.AsSpan(), Context.AsSpan(), 42));
Assert.Contains(s_unknownHash.Name, ex.Message);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label.AsSpan(), Context.AsSpan(), -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label.AsSpan(), Context.AsSpan(), 0x20000000));
}
[Fact]
public static void DeriveBytes_BufferFill_SpanChars_ArgValidation()
{
byte[] destination = new byte[42];
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_nullHash, Label.AsSpan(), Context.AsSpan(), destination));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_emptyHash, Label.AsSpan(), Context.AsSpan(), destination));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, s_unknownHash, Label.AsSpan(), Context.AsSpan(), destination));
Assert.Contains(s_unknownHash.Name, ex.Message);
Assert.Throws<ArgumentOutOfRangeException>("destination", () =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label.AsSpan(), Context.AsSpan(), GetOversizedSpan()));
}
[Fact]
public static void Ctor_KeyArray_ArgValidation()
{
Assert.Throws<ArgumentNullException>("key", () =>
new SP800108HmacCounterKdf((byte[])null, HashAlgorithmName.SHA256));
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
new SP800108HmacCounterKdf(s_kdk, s_nullHash));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
new SP800108HmacCounterKdf(s_kdk, s_emptyHash));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
new SP800108HmacCounterKdf(s_kdk, s_unknownHash));
Assert.Contains(s_unknownHash.Name, ex.Message);
}
[Fact]
public static void Ctor_KeySpan_ArgValidation()
{
Assert.Throws<ArgumentNullException>("hashAlgorithm", () =>
new SP800108HmacCounterKdf(s_kdk.AsSpan(), s_nullHash));
Assert.Throws<ArgumentException>("hashAlgorithm", () =>
new SP800108HmacCounterKdf(s_kdk.AsSpan(), s_emptyHash));
CryptographicException ex = Assert.Throws<CryptographicException>(() =>
new SP800108HmacCounterKdf(s_kdk.AsSpan(), s_unknownHash));
Assert.Contains(s_unknownHash.Name, ex.Message);
}
[Fact]
public static void DeriveKey_Allocating_ArrayBytes_ArgValidation()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<ArgumentNullException>("label", () =>
kdf.DeriveKey((byte[])null, s_contextBytes, 42));
Assert.Throws<ArgumentNullException>("context", () =>
kdf.DeriveKey(s_labelBytes, (byte[])null, 42));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(s_labelBytes, s_contextBytes, -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(s_labelBytes, s_contextBytes, 0x20000000));
}
[Fact]
public static void DeriveKey_Allocating_SpanBytes_ArgValidation()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), 0x20000000));
}
[Fact]
public static void DeriveKey_BufferFill_SpanBytes_ArgValidation()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<ArgumentOutOfRangeException>("destination", () =>
kdf.DeriveKey(s_labelBytes.AsSpan(), s_contextBytes.AsSpan(), GetOversizedSpan()));
}
[Fact]
public static void DeriveKey_Allocating_SpanChars_ArgValidation()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(Label.AsSpan(), Context.AsSpan(), -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(Label.AsSpan(), Context.AsSpan(), 0x20000000));
}
[Fact]
public static void DeriveKey_BufferFill_SpanChars_ArgValidation()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<ArgumentOutOfRangeException>("destination", () =>
kdf.DeriveKey(Label.AsSpan(), Context.AsSpan(), GetOversizedSpan()));
}
[Fact]
public static void DeriveKey_Allocating_String_ArgValidation()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(Label, Context, -1));
Assert.Throws<ArgumentOutOfRangeException>("derivedKeyLengthInBytes", () =>
kdf.DeriveKey(Label, Context, 0x20000000));
}
[Fact]
public static void DeriveKey_Allocating_String_InvalidUTF8()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<EncoderFallbackException>(() =>
kdf.DeriveKey("\uD800", Context, 42));
Assert.Throws<EncoderFallbackException>(() =>
kdf.DeriveKey(Label, "\uD800", 42));
}
[Fact]
public static void DeriveKey_BufferFill_SpanChars_InvalidUTF8()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
byte[] derivedKey = new byte[42];
Assert.Throws<EncoderFallbackException>(() =>
kdf.DeriveKey("\uD800".AsSpan(), Context.AsSpan(), derivedKey));
Assert.Throws<EncoderFallbackException>(() =>
kdf.DeriveKey(Label.AsSpan(), "\uD800".AsSpan(), derivedKey));
}
[Fact]
public static void DeriveKey_Allocating_SpanChars_InvalidUTF8()
{
using SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
Assert.Throws<EncoderFallbackException>(() =>
kdf.DeriveKey("\uD800".AsSpan(), Context.AsSpan(), 42));
Assert.Throws<EncoderFallbackException>(() =>
kdf.DeriveKey(Label.AsSpan(), "\uD800".AsSpan(), 42));
}
[Fact]
public static void DeriveBytes_BufferFill_SpanChars_InvalidUTF8()
{
byte[] destination = new byte[42];
Assert.Throws<EncoderFallbackException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label.AsSpan(), "\uD800".AsSpan(), destination));
Assert.Throws<EncoderFallbackException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, "\uD800".AsSpan(), Context.AsSpan(), destination));
}
[Fact]
public static void DeriveBytes_Allocating_SpanChars_InvalidUTF8()
{
Assert.Throws<EncoderFallbackException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label.AsSpan(), "\uD800".AsSpan(), 42));
Assert.Throws<EncoderFallbackException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, "\uD800".AsSpan(), Context.AsSpan(), 42));
}
[Fact]
public static void DeriveBytes_Allocating_String_InvalidUTF8()
{
Assert.Throws<EncoderFallbackException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, Label, "\uD800", 42));
Assert.Throws<EncoderFallbackException>(() =>
SP800108HmacCounterKdf.DeriveBytes(s_kdk, HashAlgorithmName.SHA256, "\uD800", Context, 42));
}
private unsafe static Span<byte> GetOversizedSpan()
{
// This creates an very large span over some address space. The memory in this span should never be read
// or written to; it should only be used for length checking of the span for argument validation.
return new Span<byte>((void*)0, 0x20000000);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Globalization;
using Xunit;
namespace System.Security.Cryptography.Tests
{
public static partial class SP800108HmacCounterKdfTests
{
[Theory]
[InlineData("V47WmHzPSkdC2vkLAomIjCzZlDOAetll3yJLcSvon7LJFjJpEN+KnSNp+gIpeydKMsENkflbrIZ/3s6GkEaH")]
[InlineData("mVaFM4deXLl610CmnCteNzxgbM/VkmKznAlPauHcDBn0le06uOjAKLHx0LfoU2/Ttq9nd78Y6Nk6wArmdwJgJg==")]
[InlineData("GaHPeqdUxriFpjRtkYQYWr5/iqneD/+hPhVJQt4rXblxSpB1UUqGqL00DMU/FJkX0iMCfqUjQXtXyfks+p++Ev4=")]
public static void AspNetCoreTestVectors_Basic(string expectedBase64)
{
// These tests are from the dotnet/aspnetcore repo.
byte[] expected = Convert.FromBase64String(expectedBase64);
VerifyKbkdf(expected, s_kdk, HashAlgorithmName.SHA512, Label.ToCharArray(), Context.ToCharArray());
}
[Theory]
[InlineData("rt2hM6kkQ8hAXmkHx0TU4o3Q+S7fie6b3S1LAq107k++P9v8uSYA2G+WX3pJf9ZkpYrTKD7WUIoLkgA1R9lk")]
[InlineData("RKiXmHSrWq5gkiRSyNZWNJrMR0jDyYHJMt9odOayRAE5wLSX2caINpQmfzTH7voJQi3tbn5MmD//dcspghfBiw==")]
[InlineData("KedXO0zAIZ3AfnPqY1NnXxpC3HDHIxefG4bwD3g6nWYEc5+q7pjbam71Yqj0zgHMNC9Z7BX3wS1/tajFocRWZUk=")]
public static void AspNetCoreTestVectors_LargeKdk(string expectedBase64)
{
// These tests are from the dotnet/aspnetcore repo.
// Win32 BCryptKeyDerivation doesn't perform RFC 2104, section 2 key adjustment for the KDK.
// We do this for CNG so that there is no functional limit on the KDK size.
byte[] kdk = new byte[50000];
for (int i = 0; i < kdk.Length; i++)
{
kdk[i] = (byte)i;
}
byte[] expected = Convert.FromBase64String(expectedBase64);
VerifyKbkdf(expected, kdk, HashAlgorithmName.SHA512, Label.ToCharArray(), Context.ToCharArray());
}
[Theory]
[MemberData(nameof(GetRfc8009TestVectors))]
public static void Rfc8009Tests(byte[] kdk, byte[] expected, HashAlgorithmName hashAlgorithm)
{
VerifyKbkdf(expected, kdk, hashAlgorithm, "prf".ToCharArray(), "test".ToCharArray());
}
[Theory]
[InlineData(new byte[] { 0xcf, 0x4b, 0xfe, 0x4f, 0x85, 0xa1, 0x0b, 0xad }, nameof(HashAlgorithmName.SHA1))]
[InlineData(new byte[] { 0x00, 0x26, 0x4b, 0xbb, 0x14, 0x97, 0x40, 0x54 }, nameof(HashAlgorithmName.SHA256))]
[InlineData(new byte[] { 0xc7, 0x10, 0x27, 0x87, 0xd8, 0x96, 0xbc, 0x89 }, nameof(HashAlgorithmName.SHA384))]
[InlineData(new byte[] { 0xdb, 0x3a, 0x18, 0xd9, 0x6c, 0x4a, 0xd4, 0x1e }, nameof(HashAlgorithmName.SHA512))]
public static void SymCryptTestVectors(byte[] expected, string hashAlgorithm)
{
// These test vectors come from https://github.com/microsoft/SymCrypt.
// See sp800_108_hmacsha1.c, sp800_108_hmacsha256.c, and sp800_108_hmacsha512.c.
byte[] symCryptKey = new byte[]
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
};
byte[] key = symCryptKey.AsSpan(0, 8).ToArray();
byte[] label = "Label"u8.ToArray();
byte[] context = symCryptKey.AsSpan(16, 16).ToArray();
HashAlgorithmName hashAlgorithmName = new HashAlgorithmName(hashAlgorithm);
VerifyKbkdfBytes(expected, key, hashAlgorithmName, label, context);
}
[Theory]
[InlineData(new byte[] { }, "label!", "context?", new byte[] { 0xb6, 0xff, 0x26, 0x61, 0xea, 0x43, 0x76, 0xd2 })]
[InlineData(new byte[] { 0xFE }, "", "context?", new byte[] { 0xed, 0xdf, 0x50, 0x06, 0x3c, 0x26, 0x3e, 0xd9 })]
[InlineData(new byte[] { 0xFE }, "label!", "", new byte[] { 0x98, 0x83, 0x67, 0x41, 0x3f, 0x2d, 0x90, 0x72 })]
[InlineData(new byte[] { }, "", "", new byte[] { 0x18, 0x0f, 0xf7, 0xa2, 0xbc, 0x8d, 0x6e, 0x98 })]
[InlineData(new byte[] { }, "", "", new byte[] { })]
public static void EmptyTests(byte[] key, string label, string context, byte[] expected)
{
VerifyKbkdf(expected, key, HashAlgorithmName.SHA256, label.ToCharArray(), context.ToCharArray());
}
[Theory]
[InlineData(nameof(HashAlgorithmName.SHA1), 512 / 8 - 1, new byte[] { 0xc9, 0x0f, 0x9d, 0x91, 0x85, 0xe5, 0xeb, 0x9b })]
[InlineData(nameof(HashAlgorithmName.SHA1), 512 / 8, new byte[] { 0x7b, 0xdb, 0x38, 0x28, 0xc0, 0x9f, 0x49, 0x05 })]
[InlineData(nameof(HashAlgorithmName.SHA1), 512 / 8 + 1, new byte[] { 0x6c, 0x3a, 0xba, 0x28, 0x38, 0xad, 0x51, 0x2c })]
[InlineData(nameof(HashAlgorithmName.SHA256), 512 / 8 - 1, new byte[] { 0x88, 0xaa, 0xc7, 0xee, 0x05, 0x65, 0xfd, 0xda })]
[InlineData(nameof(HashAlgorithmName.SHA256), 512 / 8, new byte[] { 0x3d, 0xdc, 0x7d, 0xec, 0x0a, 0xfd, 0x7a, 0xc0 })]
[InlineData(nameof(HashAlgorithmName.SHA256), 512 / 8 + 1, new byte[] { 0x47, 0x95, 0x00, 0xd5, 0x55, 0x1f, 0xb3, 0x85 })]
[InlineData(nameof(HashAlgorithmName.SHA384), 1024 / 8 - 1, new byte[] { 0x84, 0xd8, 0xfd, 0x33, 0x4f, 0x07, 0x81, 0x9b })]
[InlineData(nameof(HashAlgorithmName.SHA384), 1024 / 8, new byte[] { 0x6c, 0xa2, 0x5d, 0x4f, 0x61, 0x2d, 0x0f, 0x20 })]
[InlineData(nameof(HashAlgorithmName.SHA384), 1024 / 8 + 1, new byte[] { 0xe4, 0x0e, 0xbd, 0x41, 0x14, 0xe6, 0x80, 0x59 })]
[InlineData(nameof(HashAlgorithmName.SHA512), 1024 / 8 - 1, new byte[] { 0xa4, 0xe5, 0x24, 0xe8, 0x56, 0x2b, 0x48, 0xa4 })]
[InlineData(nameof(HashAlgorithmName.SHA512), 1024 / 8, new byte[] { 0xba, 0xf6, 0xed, 0xa7, 0x3a, 0xf7, 0x12, 0x27 })]
[InlineData(nameof(HashAlgorithmName.SHA512), 1024 / 8 + 1, new byte[] { 0x34, 0xdf, 0x2d, 0x21, 0xfd, 0xf1, 0x0e, 0x13 })]
public static void Kdk_HmacBlockBoundarySizes(string hashAlgorithmName, int kdkSize, byte[] expected)
{
// We do HMAC key adjust for the CNG implementation when the kdk exceeds the block size of the HMAC algorithm.
// This tests one byte below, at, and above the block size for each HMAC algorithm.
// Verified against OpenSSL 3. Example command used below. Adjust the digest and the seq upper boundary as needed.
// Note that OpenSSL calls the label "salt" and the context "info".
//
// openssl kdf -keylen 8 -kdfopt mac:HMAC -kdfopt digest:SHA1 \
// -kdfopt hexkey:$(seq 1 63 | awk '{ printf "%02x", $1 }') -kdfopt salt:icecream \
// -kdfopt info:sandwiches -binary KBKDF | xxd -i
byte[] kdk = new byte[kdkSize];
for (int i = 0; i < kdkSize; i++)
{
kdk[i] = (byte)checked(i + 1);
}
HashAlgorithmName alg = new HashAlgorithmName(hashAlgorithmName);
VerifyKbkdf(expected, kdk, alg, "icecream".ToCharArray(), "sandwiches".ToCharArray());
}
[Theory]
[MemberData(nameof(GetOutputLengthBoundaries))]
public static void OutputLength_AroundPrfOutputBoundaries(string hashAlgorithmName, byte[] expected)
{
byte[] kdk = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
HashAlgorithmName alg = new HashAlgorithmName(hashAlgorithmName);
VerifyKbkdf(expected, kdk, alg, "mustard".ToCharArray(), "ketchup".ToCharArray());
}
[Fact]
public static void MultipleDisposes_NoThrow()
{
SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(s_kdk, HashAlgorithmName.SHA256);
kdf.Dispose();
Assert.Throws<ObjectDisposedException>(() => kdf.DeriveKey(s_labelBytes, s_contextBytes, 42));
kdf.Dispose();
}
public static IEnumerable<object[]> GetOutputLengthBoundaries()
{
// All outputs assume a kdk of 1, 2, 3, 4, 5, 6, 7, 8 with a label of "mustard" and context of "ketchup".
// Outputs were generated from the "openssl kdf" command.
// HMACSHA1 output size is 20 bytes
yield return new object[]
{
nameof(HashAlgorithmName.SHA1),
new byte[39]
{
0x05, 0xed, 0x32, 0x78, 0xfe, 0x72, 0x9e, 0x7a, 0x1b, 0x49, 0x2c, 0x1b, 0x40, 0x3b, 0x31, 0xd8,
0xef, 0x74, 0xfc, 0xe6, 0x1f, 0x75, 0x4b, 0xa5, 0x47, 0x8e, 0xb9, 0x1d, 0x9e, 0x0c, 0x4f, 0x03,
0xf7, 0x92, 0x68, 0x8a, 0x94, 0x0c, 0xea,
},
};
yield return new object[]
{
nameof (HashAlgorithmName.SHA1),
new byte[40]
{
0xa5, 0x4a, 0x74, 0xe5, 0x01, 0x0c, 0x69, 0x0e, 0xc8, 0x9e, 0x24, 0x95, 0x94, 0x6a, 0x57, 0x06,
0xe8, 0x88, 0x63, 0x3b, 0xae, 0x1d, 0x13, 0xd8, 0x9d, 0x80, 0x4c, 0xcd, 0x60, 0x2b, 0x15, 0x3a,
0xb9, 0xd3, 0x77, 0xc7, 0xc4, 0xb7, 0x2a, 0xe6,
}
};
yield return new object[]
{
nameof(HashAlgorithmName.SHA1),
new byte[41]
{
0x44, 0xdc, 0x8a, 0x94, 0x18, 0xd1, 0x3a, 0x35, 0x39, 0xb8, 0xfb, 0x33, 0x0d, 0xf8, 0x1d, 0x3d,
0x95, 0xa3, 0x3a, 0xfb, 0x0c, 0x7f, 0x54, 0x20, 0x32, 0x75, 0x3c, 0x9c, 0xf0, 0xe2, 0xce, 0x4c,
0xe0, 0x98, 0xbe, 0x6b, 0x61, 0x19, 0xd0, 0x79, 0xea,
}
};
// HMACSHA256 output size is 32 bytes
yield return new object[]
{
nameof(HashAlgorithmName.SHA256),
new byte[63]
{
0x86, 0x02, 0xc7, 0x0a, 0x80, 0xf3, 0xc9, 0x46, 0xb4, 0x10, 0xa8, 0xde, 0x36, 0x03, 0x5a, 0xb2,
0x29, 0x48, 0x16, 0xc6, 0x97, 0x99, 0xa4, 0x48, 0x85, 0x55, 0x34, 0xbc, 0xa6, 0x29, 0x68, 0x88,
0x8a, 0xd3, 0x42, 0x42, 0x22, 0x55, 0xa1, 0xf9, 0xaf, 0x1b, 0xb4, 0xfb, 0x69, 0xd3, 0x9d, 0x2e,
0x89, 0x4f, 0x0e, 0x19, 0x5a, 0x98, 0xce, 0x5c, 0x7e, 0xfd, 0xba, 0xc7, 0x51, 0x18, 0xdf,
},
};
yield return new object[]
{
nameof (HashAlgorithmName.SHA256),
new byte[64]
{
0xcc, 0x06, 0x77, 0xaf, 0x45, 0xae, 0x66, 0x3f, 0x58, 0x8f, 0xd4, 0xa5, 0x6f, 0x31, 0xef, 0xd7,
0x29, 0x9e, 0x45, 0xb1, 0xac, 0x5b, 0x27, 0xb4, 0x10, 0xc1, 0xaf, 0x63, 0xe9, 0xbb, 0xb2, 0xe6,
0x76, 0x65, 0xbf, 0x2d, 0xd3, 0x14, 0xfb, 0xdf, 0xab, 0x6b, 0x95, 0xf4, 0x67, 0x53, 0xe0, 0xd7,
0x46, 0xd3, 0x4c, 0x72, 0xf3, 0x08, 0x95, 0x37, 0xbf, 0xa4, 0x67, 0xb0, 0xe0, 0x00, 0x46, 0x79,
}
};
yield return new object[]
{
nameof(HashAlgorithmName.SHA256),
new byte[65]
{
0x2b, 0x57, 0x00, 0xe5, 0xef, 0xda, 0x41, 0x7b, 0x75, 0x04, 0xe8, 0x37, 0x7a, 0x7f, 0xfd, 0x30,
0xd6, 0x56, 0x07, 0x69, 0x00, 0x75, 0x7b, 0xb9, 0x64, 0x15, 0x51, 0xac, 0x88, 0x55, 0x87, 0x24,
0xcc, 0xb9, 0x8b, 0xb2, 0x55, 0xcc, 0x02, 0xda, 0xf1, 0x4e, 0xc9, 0xa2, 0x40, 0x95, 0xfb, 0xff,
0xa0, 0x57, 0x73, 0x51, 0x66, 0x4d, 0x65, 0xd4, 0xc9, 0xc0, 0xe6, 0xf4, 0x40, 0xf4, 0x30, 0x17,
0x7b,
}
};
// HMACSHA384 output size is 48 bytes
yield return new object[]
{
nameof(HashAlgorithmName.SHA384),
new byte[95]
{
0x82, 0x1d, 0x9b, 0x3c, 0x7f, 0xad, 0xd4, 0x1b, 0x91, 0xdc, 0x6e, 0x4f, 0xf5, 0xd8, 0xf7, 0xc8,
0x33, 0x18, 0xc8, 0xf8, 0x23, 0x3f, 0x5d, 0xf4, 0x95, 0x32, 0x81, 0x72, 0x96, 0xbd, 0xb9, 0xcc,
0xc1, 0x91, 0x0c, 0x5b, 0x5c, 0x86, 0x2c, 0x0d, 0x5b, 0xe4, 0xfb, 0xc6, 0x70, 0xc4, 0x20, 0xd6,
0x9c, 0xfd, 0x67, 0x56, 0x86, 0x16, 0xd6, 0xf8, 0x05, 0x86, 0x5c, 0xa0, 0x64, 0x5f, 0x72, 0xe0,
0xa5, 0x52, 0x5d, 0x72, 0xe8, 0x5e, 0x07, 0xf1, 0xf5, 0xcf, 0xf9, 0x63, 0x85, 0xc2, 0x77, 0x87,
0x89, 0x75, 0x9d, 0xd2, 0xc6, 0x2b, 0xf3, 0x23, 0x73, 0xd9, 0x1d, 0x01, 0x17, 0x9c, 0x01,
},
};
yield return new object[]
{
nameof (HashAlgorithmName.SHA384),
new byte[96]
{
0xe4, 0x30, 0x8b, 0x7e, 0x5b, 0x64, 0xcd, 0xd7, 0x3d, 0x27, 0xd9, 0x3a, 0x9e, 0xee, 0xcc, 0xc6,
0x79, 0xa7, 0x39, 0xca, 0x91, 0xb8, 0x93, 0xcd, 0xe8, 0xb8, 0xb7, 0x8a, 0x48, 0xad, 0xb4, 0x3d,
0x3a, 0x02, 0xb9, 0xba, 0x81, 0x81, 0x01, 0x5f, 0xef, 0x8a, 0xc1, 0xcd, 0x6b, 0xae, 0x99, 0xb9,
0xfd, 0xaf, 0x28, 0x18, 0xcf, 0x48, 0xa1, 0xfa, 0x57, 0xce, 0x0a, 0x79, 0x1f, 0xbf, 0xc8, 0x7f,
0xd8, 0x34, 0x34, 0x16, 0x27, 0xc1, 0x12, 0x6e, 0x4c, 0x8c, 0x62, 0xc6, 0x11, 0x01, 0xb8, 0xb8,
0xa5, 0x06, 0xc8, 0x4a, 0x2f, 0xf2, 0x91, 0x08, 0x1a, 0x02, 0x5e, 0x72, 0x48, 0xd1, 0x11, 0x6a,
}
};
yield return new object[]
{
nameof(HashAlgorithmName.SHA384),
new byte[97]
{
0xa1, 0x01, 0x9f, 0x83, 0x55, 0x8f, 0x4a, 0x67, 0x2f, 0xbf, 0xf7, 0x7b, 0xbb, 0xfd, 0x22, 0x35,
0x35, 0x97, 0x01, 0x53, 0x5d, 0x9f, 0x7e, 0xa4, 0xbf, 0xb9, 0xfd, 0xdf, 0x7d, 0x86, 0x8f, 0x86,
0x98, 0x9d, 0x87, 0x21, 0x41, 0x75, 0x4e, 0x2c, 0xaf, 0x10, 0x25, 0x40, 0xe8, 0x26, 0xb8, 0x4d,
0x37, 0xe2, 0x43, 0x5f, 0x2b, 0x30, 0x03, 0xde, 0xb3, 0x1e, 0x31, 0x8c, 0x59, 0x2d, 0x26, 0xd5,
0x08, 0xe4, 0x55, 0x46, 0x0d, 0x76, 0xe7, 0x58, 0xe5, 0xa9, 0xaf, 0xe8, 0xe7, 0x7c, 0xa3, 0x05,
0x61, 0x7b, 0xcb, 0x98, 0x6e, 0xaa, 0xea, 0xd0, 0x7e, 0x52, 0x20, 0xf8, 0xfe, 0x49, 0x02, 0x08,
0xee,
}
};
// HMACSHA512 output size is 64 bytes
yield return new object[]
{
nameof(HashAlgorithmName.SHA512),
new byte[127]
{
0x3d, 0x72, 0x04, 0x07, 0x9c, 0x28, 0x34, 0x33, 0x7a, 0x2f, 0x8e, 0x22, 0x00, 0x1f, 0x1f, 0x22,
0xd9, 0x24, 0x38, 0xd1, 0x56, 0xa4, 0x03, 0x46, 0x89, 0x09, 0xde, 0x13, 0xb3, 0x9c, 0xa9, 0x24,
0x0d, 0x3e, 0x4c, 0x97, 0x7f, 0xd5, 0x9f, 0x86, 0x45, 0x36, 0xcd, 0xc8, 0x0f, 0x44, 0x9a, 0xad,
0xe3, 0xba, 0xfc, 0x93, 0x08, 0x21, 0xa8, 0xfd, 0xde, 0xef, 0x4f, 0xe3, 0xaa, 0xb3, 0xcf, 0xc1,
0x81, 0x1b, 0x44, 0xf9, 0xae, 0xf6, 0x73, 0xc7, 0xf0, 0x71, 0xd6, 0x14, 0x8e, 0x18, 0x5d, 0x43,
0xa1, 0xfb, 0x09, 0x11, 0x24, 0x84, 0x56, 0x9c, 0x97, 0x6e, 0x2e, 0x5a, 0xcd, 0xd3, 0xa5, 0xfb,
0x81, 0x33, 0x6a, 0x3d, 0x95, 0xa5, 0xd9, 0xcd, 0x04, 0x36, 0x76, 0xc2, 0x4c, 0xed, 0x65, 0x81,
0x6f, 0x8c, 0xec, 0xfd, 0xde, 0xdd, 0x3c, 0xd9, 0x1a, 0xe1, 0xf1, 0x02, 0x7e, 0xb8, 0x3a,
},
};
yield return new object[]
{
nameof (HashAlgorithmName.SHA512),
new byte[128]
{
0x0e, 0x6d, 0x21, 0x43, 0xca, 0xa7, 0x88, 0x13, 0x70, 0x0e, 0xc5, 0x7b, 0x5e, 0x5a, 0x41, 0x21,
0x03, 0x07, 0x30, 0x35, 0x10, 0xe9, 0x42, 0x12, 0x80, 0x64, 0x10, 0x71, 0x5d, 0x41, 0xb9, 0xf5,
0x3a, 0xb2, 0xcd, 0xf8, 0x71, 0x52, 0x01, 0xf8, 0xc5, 0x27, 0x65, 0xc0, 0x6b, 0x31, 0x35, 0xfc,
0x0d, 0x38, 0xbb, 0xf4, 0xc2, 0xeb, 0x9a, 0x85, 0x3f, 0x16, 0xf0, 0x25, 0x40, 0x33, 0x57, 0xc1,
0x08, 0x25, 0xcf, 0x31, 0x10, 0xbf, 0x78, 0x3a, 0x37, 0x64, 0x13, 0xa7, 0x2f, 0xd8, 0x32, 0x2b,
0x93, 0x1f, 0x0b, 0x6d, 0x6c, 0x6c, 0x45, 0x9f, 0x6a, 0xdb, 0x97, 0x8b, 0x33, 0x7d, 0x31, 0xa8,
0xd9, 0x92, 0xe4, 0x50, 0x38, 0x25, 0x22, 0x09, 0x98, 0x11, 0xce, 0x55, 0xf8, 0x6d, 0x27, 0x36,
0x5f, 0xab, 0xca, 0x4b, 0x54, 0x78, 0x11, 0xf9, 0xaf, 0x57, 0xfa, 0x02, 0x31, 0x27, 0x52, 0x69,
}
};
yield return new object[]
{
nameof(HashAlgorithmName.SHA512),
new byte[129]
{
0x9e, 0xd0, 0xad, 0x78, 0x5d, 0xb3, 0x50, 0xce, 0x0a, 0x1b, 0xa2, 0xd1, 0x1d, 0xb8, 0x43, 0xc0,
0xc1, 0x0f, 0x2f, 0x13, 0xbe, 0x6b, 0x56, 0x53, 0x4e, 0x9f, 0x18, 0x76, 0xcf, 0xf2, 0xf0, 0xa1,
0xa6, 0xe8, 0x57, 0x2b, 0x05, 0xf2, 0x2e, 0x3e, 0xbf, 0xfe, 0x5c, 0x20, 0x93, 0x5e, 0x73, 0xca,
0x23, 0xda, 0x63, 0x24, 0xdf, 0x6c, 0xb7, 0x5c, 0xe7, 0xe9, 0x6e, 0x2a, 0x0c, 0x3a, 0xa9, 0xb7,
0x65, 0x25, 0x8a, 0x8c, 0xf0, 0xae, 0x0e, 0x53, 0x84, 0x82, 0x8b, 0xa6, 0xd9, 0xed, 0x3d, 0xfe,
0x6f, 0x1d, 0xbf, 0x36, 0x7c, 0xfd, 0xa2, 0xf9, 0x22, 0x79, 0x79, 0x54, 0x5f, 0xed, 0x3b, 0xb9,
0xab, 0x84, 0x75, 0xba, 0xb0, 0x12, 0x22, 0x6a, 0x07, 0xee, 0x35, 0xc4, 0x9a, 0xca, 0xd8, 0x28,
0x3c, 0x0b, 0xcd, 0x85, 0xbb, 0x6e, 0x7b, 0x0e, 0xaa, 0xcb, 0xf9, 0xb1, 0xa4, 0xcd, 0x65, 0x87,
0x61,
}
};
}
public static IEnumerable<object[]> GetRfc8009TestVectors()
{
// See RFC 8009 Appendix A "Sample pseudorandom function (PRF) invocations"
// section. These vectors are also used by OpenSSL 3 for KBKDF.
// Section 5 of RFC 8009 defines KDF-HMAC-SHA2 as always using "prf" as the label.
// The appendix defines the context as "test".
// (kdk, expected, hmac algorithm)
yield return new object[]
{
new byte[]
{
0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28,
0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C,
},
new byte[]
{
0x9D, 0x18, 0x86, 0x16, 0xF6, 0x38, 0x52, 0xFE,
0x86, 0x91, 0x5B, 0xB8, 0x40, 0xB4, 0xA8, 0x86,
0xFF, 0x3E, 0x6B, 0xB0, 0xF8, 0x19, 0xB4, 0x9B,
0x89, 0x33, 0x93, 0xD3, 0x93, 0x85, 0x42, 0x95,
},
HashAlgorithmName.SHA256,
};
yield return new object[]
{
new byte[]
{
0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D,
0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98,
0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0,
0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52,
},
new byte[]
{
0x98, 0x01, 0xF6, 0x9A, 0x36, 0x8C, 0x2B, 0xF6,
0x75, 0xE5, 0x95, 0x21, 0xE1, 0x77, 0xD9, 0xA0,
0x7F, 0x67, 0xEF, 0xE1, 0xCF, 0xDE, 0x8D, 0x3C,
0x8D, 0x6F, 0x6A, 0x02, 0x56, 0xE3, 0xB1, 0x7D,
0xB3, 0xC1, 0xB6, 0x2A, 0xD1, 0xB8, 0x55, 0x33,
0x60, 0xD1, 0x73, 0x67, 0xEB, 0x15, 0x14, 0xD2,
},
HashAlgorithmName.SHA384,
};
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using Xunit;
namespace System.Security.Cryptography.Tests
{
public static partial class SP800108HmacCounterKdfTests
{
private const string Label = "label";
private const string Context = "contextHeadercontext";
private static readonly byte[] s_labelBytes = "label"u8.ToArray();
private static readonly byte[] s_kdk = "kdk"u8.ToArray();
private static readonly byte[] s_contextBytes = "contextHeadercontext"u8.ToArray();
private static readonly HashAlgorithmName s_unknownHash = HashAlgorithmName.MD5;
private static readonly HashAlgorithmName s_nullHash = new HashAlgorithmName(null);
private static readonly HashAlgorithmName s_emptyHash = new HashAlgorithmName("");
private static void VerifyKbkdfBytes(byte[] expected, byte[] key, HashAlgorithmName hashAlgorithm, byte[] labelBytes, byte[] contextBytes)
{
byte[] result;
using (SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(key, hashAlgorithm))
{
result = kdf.DeriveKey(labelBytes, contextBytes, expected.Length);
Assert.Equal(expected, result);
}
using (SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(key, hashAlgorithm))
{
result = kdf.DeriveKey(new ReadOnlySpan<byte>(labelBytes), new ReadOnlySpan<byte>(contextBytes), expected.Length);
Assert.Equal(expected, result);
}
using (SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(key, hashAlgorithm))
{
result = new byte[expected.Length];
kdf.DeriveKey(new ReadOnlySpan<byte>(labelBytes), new ReadOnlySpan<byte>(contextBytes), result);
Assert.Equal(expected, result);
}
result = SP800108HmacCounterKdf.DeriveBytes(
key,
hashAlgorithm,
labelBytes,
contextBytes,
expected.Length);
Assert.Equal(expected, result);
result = SP800108HmacCounterKdf.DeriveBytes(
new ReadOnlySpan<byte>(key),
hashAlgorithm,
new ReadOnlySpan<byte>(labelBytes),
new ReadOnlySpan<byte>(contextBytes),
expected.Length);
Assert.Equal(expected, result);
result = new byte[expected.Length];
SP800108HmacCounterKdf.DeriveBytes(
new ReadOnlySpan<byte>(key),
hashAlgorithm,
new ReadOnlySpan<byte>(labelBytes),
new ReadOnlySpan<byte>(contextBytes),
result);
Assert.Equal(expected, result);
}
private static void VerifyKbkdf(byte[] expected, byte[] key, HashAlgorithmName hashAlgorithm, char[] label, char[] context)
{
// The actual implementation uses a stricter UTF8 encoding/decoding but we know our test data does not contain
// invalid UTF8.
byte[] labelBytes = System.Text.Encoding.UTF8.GetBytes(label);
byte[] contextBytes = System.Text.Encoding.UTF8.GetBytes(context);
byte[] result;
VerifyKbkdfBytes(expected, key, hashAlgorithm, labelBytes, contextBytes);
using (SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(key, hashAlgorithm))
{
result = kdf.DeriveKey(new ReadOnlySpan<char>(label), new ReadOnlySpan<char>(context), expected.Length);
Assert.Equal(expected, result);
}
using (SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(key, hashAlgorithm))
{
result = new byte[expected.Length];
kdf.DeriveKey(new ReadOnlySpan<char>(label), new ReadOnlySpan<char>(context), result);
Assert.Equal(expected, result);
}
using (SP800108HmacCounterKdf kdf = new SP800108HmacCounterKdf(key, hashAlgorithm))
{
result = kdf.DeriveKey(new string(label), new string(context), expected.Length);
Assert.Equal(expected, result);
}
result = SP800108HmacCounterKdf.DeriveBytes(
key,
hashAlgorithm,
new string(label),
new string(context),
expected.Length);
Assert.Equal(expected, result);
result = SP800108HmacCounterKdf.DeriveBytes(
new ReadOnlySpan<byte>(key),
hashAlgorithm,
new ReadOnlySpan<char>(label),
new ReadOnlySpan<char>(context),
expected.Length);
Assert.Equal(expected, result);
result = new byte[expected.Length];
SP800108HmacCounterKdf.DeriveBytes(
new ReadOnlySpan<byte>(key),
hashAlgorithm,
new ReadOnlySpan<char>(label),
new ReadOnlySpan<char>(context),
result);
Assert.Equal(expected, result);
}
private static void RaceCalls(byte[] expected1, byte[] expected2, bool isDisposing, Func<int, byte[]> call1, Func<int, byte[]> call2)
{
const int Iterations = 1_000;
void ThreadCallback(object state)
{
(Func<int, byte[]> act, byte[] expected) = ((Func<int, byte[]>, byte[]))state;
byte[][] results = new byte[Iterations][];
for (int i = 0; i < Iterations; i++)
{
// defer asserting until after the loop so that the assert doesn't dominate the work of the
// threads interacting.
try
{
results[i] = act(i);
}
catch (ObjectDisposedException) when (isDisposing)
{
results[i] = null;
}
}
for (int i = 0; i < Iterations; i++)
{
byte[] result = results[i];
if (result is not null)
{
Assert.Equal(expected, result);
}
}
}
Thread t1 = new Thread(ThreadCallback);
Thread t2 = new Thread(ThreadCallback);
t1.Start((call1, expected1));
t2.Start((call2, expected2));
t1.Join();
t2.Join();
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Xunit;
namespace System.Security.Cryptography.Tests
{
public static partial class SP800108HmacCounterKdfTests
{
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68162", TestPlatforms.Browser)] // wasm threading support
public static void Race_ReusingOneInstance_Allocating()
{
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => kdf.DeriveKey("label"u8, "context"u8, derivedKeyLengthInBytes: 16),
_ => kdf.DeriveKey("label"u8, "bananas"u8, derivedKeyLengthInBytes: 16));
}
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => kdf.DeriveKey("label"u8.ToArray(), "context"u8.ToArray(), derivedKeyLengthInBytes: 16),
_ => kdf.DeriveKey("label"u8.ToArray(), "bananas"u8.ToArray(), derivedKeyLengthInBytes: 16));
}
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => kdf.DeriveKey("label", "context", derivedKeyLengthInBytes: 16),
_ => kdf.DeriveKey("label", "bananas", derivedKeyLengthInBytes: 16));
}
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => kdf.DeriveKey("label".AsSpan(), "context".AsSpan(), derivedKeyLengthInBytes: 16),
_ => kdf.DeriveKey("label".AsSpan(), "bananas".AsSpan(), derivedKeyLengthInBytes: 16));
}
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68162", TestPlatforms.Browser)] // wasm threading support
public static void Race_ReusingOneInstance_Buffering()
{
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => {
byte[] result = new byte[16];
kdf.DeriveKey("label"u8, "context"u8, result);
return result;
},
_ => {
byte[] result = new byte[16];
kdf.DeriveKey("label"u8, "bananas"u8, result);
return result;
});
}
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => {
byte[] result = new byte[16];
kdf.DeriveKey("label"u8.ToArray(), "context"u8.ToArray(), result);
return result;
},
_ => {
byte[] result = new byte[16];
kdf.DeriveKey("label"u8.ToArray(), "bananas"u8.ToArray(), result);
return result;
});
}
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => {
return kdf.DeriveKey("label", "context", 16);
},
_ => {
return kdf.DeriveKey("label", "bananas", 16);
});
}
using (SP800108HmacCounterKdf kdf = new("kdf"u8, HashAlgorithmName.SHA256))
{
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: false,
_ => {
byte[] result = new byte[16];
kdf.DeriveKey("label".AsSpan(), "context".AsSpan(), result);
return result;
},
_ => {
byte[] result = new byte[16];
kdf.DeriveKey("label".AsSpan(), "bananas".AsSpan(), result);
return result;
});
}
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68162", TestPlatforms.Browser)] // wasm threading support
public static void Race_UseAndDisposeOneInstance_Allocating()
{
SP800108HmacCounterKdf kdf;
kdf = new SP800108HmacCounterKdf("kdf"u8, HashAlgorithmName.SHA256);
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: true,
_ => kdf.DeriveKey("label"u8, "context"u8, derivedKeyLengthInBytes: 16),
i => {
if (i == 50)
{
kdf.Dispose();
kdf.Dispose();
}
return null;
});
kdf = new SP800108HmacCounterKdf("kdf"u8, HashAlgorithmName.SHA256);
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: true,
_ => kdf.DeriveKey("label"u8.ToArray(), "context"u8.ToArray(), derivedKeyLengthInBytes: 16),
i => {
if (i == 50)
{
kdf.Dispose();
kdf.Dispose();
}
return null;
});
kdf = new SP800108HmacCounterKdf("kdf"u8, HashAlgorithmName.SHA256);
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: true,
_ => kdf.DeriveKey("label", "context", derivedKeyLengthInBytes: 16),
i => {
if (i == 50)
{
kdf.Dispose();
kdf.Dispose();
}
return null;
});
kdf = new SP800108HmacCounterKdf("kdf"u8, HashAlgorithmName.SHA256);
RaceCalls(
new byte[] { 0xDC, 0xD6, 0x23, 0xE8, 0x59, 0xB8, 0x4B, 0x95, 0xBF, 0x44, 0x32, 0x6E, 0x2B, 0xA6, 0x34, 0xF0 },
new byte[] { 0x92, 0xB0, 0xD7, 0xDA, 0x2C, 0xB1, 0xAA, 0x8C, 0xD5, 0xDF, 0x97, 0x9E, 0x61, 0xA3, 0x57, 0xD6 },
isDisposing: true,
_ => kdf.DeriveKey("label".AsSpan(), "context".AsSpan(), derivedKeyLengthInBytes: 16),
i => {
if (i == 50)
{
kdf.Dispose();
kdf.Dispose();
}
return null;
});
}
}
}
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{7E9F6DE1-771B-4E25-A603-EC43D0291C8B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Cryptography", "ref\Microsoft.Bcl.Cryptography.csproj", "{63655B2E-6A06-4E48-9F01-D0B910063165}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{98708A22-7268-4EDB-AE37-70AA958A772A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Cryptography", "src\Microsoft.Bcl.Cryptography.csproj", "{B0716D7E-B824-4866-A1ED-DF31BA2970B9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8C3BD4AD-1A56-4204-9826-F8B74251D19F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Cryptography.Tests", "tests\Microsoft.Bcl.Cryptography.Tests.csproj", "{E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{34897637-11A1-48A4-AF1F-E11463A61D0B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{E9271403-BEF5-46E9-B68B-16EF69AA7149}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63655B2E-6A06-4E48-9F01-D0B910063165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63655B2E-6A06-4E48-9F01-D0B910063165}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63655B2E-6A06-4E48-9F01-D0B910063165}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63655B2E-6A06-4E48-9F01-D0B910063165}.Release|Any CPU.Build.0 = Release|Any CPU
{B0716D7E-B824-4866-A1ED-DF31BA2970B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0716D7E-B824-4866-A1ED-DF31BA2970B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0716D7E-B824-4866-A1ED-DF31BA2970B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0716D7E-B824-4866-A1ED-DF31BA2970B9}.Release|Any CPU.Build.0 = Release|Any CPU
{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}.Release|Any CPU.Build.0 = Release|Any CPU
{E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191}.Release|Any CPU.Build.0 = Release|Any CPU
{A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2}.Release|Any CPU.Build.0 = Release|Any CPU
{E9271403-BEF5-46E9-B68B-16EF69AA7149}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9271403-BEF5-46E9-B68B-16EF69AA7149}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9271403-BEF5-46E9-B68B-16EF69AA7149}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9271403-BEF5-46E9-B68B-16EF69AA7149}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{63655B2E-6A06-4E48-9F01-D0B910063165} = {7E9F6DE1-771B-4E25-A603-EC43D0291C8B}
{B0716D7E-B824-4866-A1ED-DF31BA2970B9} = {98708A22-7268-4EDB-AE37-70AA958A772A}
{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F}
{E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F}
{A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2} = {34897637-11A1-48A4-AF1F-E11463A61D0B}
{E9271403-BEF5-46E9-B68B-16EF69AA7149} = {34897637-11A1-48A4-AF1F-E11463A61D0B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EAA35B12-9858-4428-8510-F09B19933FB9}
EndGlobalSection
EndGlobal
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.SP800108HmacCounterKdf))]
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// ------------------------------------------------------------------------------
// Changes to this file must follow the https://aka.ms/api-review process.
// ------------------------------------------------------------------------------
namespace System.Security.Cryptography
{
public sealed partial class SP800108HmacCounterKdf : System.IDisposable
{
public SP800108HmacCounterKdf(byte[] key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public SP800108HmacCounterKdf(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public static byte[] DeriveBytes(byte[] key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, byte[] label, byte[] context, int derivedKeyLengthInBytes) { throw null; }
public static byte[] DeriveBytes(byte[] key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, string label, string context, int derivedKeyLengthInBytes) { throw null; }
public static byte[] DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, int derivedKeyLengthInBytes) { throw null; }
public static void DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, System.Span<byte> destination) { }
public static byte[] DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, int derivedKeyLengthInBytes) { throw null; }
public static void DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, System.Span<byte> destination) { }
public byte[] DeriveKey(byte[] label, byte[] context, int derivedKeyLengthInBytes) { throw null; }
public byte[] DeriveKey(System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, int derivedKeyLengthInBytes) { throw null; }
public void DeriveKey(System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, System.Span<byte> destination) { }
public byte[] DeriveKey(System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, int derivedKeyLengthInBytes) { throw null; }
public void DeriveKey(System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, System.Span<byte> destination) { }
public byte[] DeriveKey(string label, string context, int derivedKeyLengthInBytes) { throw null; }
public void Dispose() { }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<Compile Include="Microsoft.Bcl.Cryptography.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<Compile Include="Microsoft.Bcl.Cryptography.Forwards.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>true</IsPackable>
<!-- Disabling baseline validation since this is a brand new package.
Once this package has shipped a stable version, the following line
should be removed in order to re-enable validation. -->
<DisablePackageBaselineValidation>true</DisablePackageBaselineValidation>
<PackageDescription>Provides support for some cryptographic primitives for .NET Framework and .NET Standard.
Commonly Used Types:
System.Security.Cryptography.SP800108HmacCounterKdf</PackageDescription>
</PropertyGroup>
<PropertyGroup>
<IsPartialFacadeAssembly Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsPartialFacadeAssembly>
<OmitResources Condition="'$(IsPartialFacadeAssembly)' == 'true'">true</OmitResources>
</PropertyGroup>
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true'">
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeBCryptAlgorithmHandle.cs"
Link="Microsoft\Win32\SafeHandles\SafeBCryptAlgorithmHandle.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeBCryptHandle.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeBCryptHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptAlgPseudoHandle.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptAlgPseudoHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptCloseAlgorithmProvider.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptCloseAlgorithmProvider.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.CreateCryptographicException.cs"
Link="Common\Interop\Windows\BCrypt\Interop.CreateCryptographicException.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.NTSTATUS.cs"
Link="Common\Interop\Windows\BCrypt\Interop.NTSTATUS.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\CryptoThrowHelper.Windows.cs"
Link="Common\System\Security\Cryptography\CryptoThrowHelper.Windows.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.FormatMessage.cs"
Link="Common\Internal\Windows\Kernel32\Interop.FormatMessage.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptOpenAlgorithmProvider.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptOpenAlgorithmProvider.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptGenerateSymmetricKey.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptGenerateSymmetricKey.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeBCryptKeyHandle.cs"
Link="Microsoft\Win32\SafeHandles\SafeBCryptKeyHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptDestroyKey.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptDestroyKey.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.Blobs.cs"
Link="Common\Interop\Windows\BCrypt\Interop.Blobs.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptKeyDerivation.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptKeyDerivation.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\CryptoPool.cs"
Link="Common\System\Security\Cryptography\CryptoPool.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdf.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdf.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationBase.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationBase.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationCng.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationCng.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\Utf8DataEncoding.cs"
Link="Common\System\Security\Cryptography\Utf8DataEncoding.cs" />
<Compile Include="System\Security\Cryptography\HashAlgorithmNames.cs" />
<Compile Include="System\Security\Cryptography\NetStandardShims.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationCng.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="$(SystemSecurityCryptographyAlgorithmsVersion)" />
</ItemGroup>
</Project>
<root>
<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="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
<value>Non-negative number required.</value>
</data>
<data name="ArgumentOutOfRange_KOut_Too_Large" xml:space="preserve">
<value>The number of bytes requested is too large. The number of bytes produced by SP800108HmacCounterKdf cannot exceed 536,870,911 bytes.</value>
</data>
<data name="Argument_EmptyString" xml:space="preserve">
<value>The value cannot be an empty string.</value>
</data>
<data name="Cryptography_UnknownHashAlgorithm" xml:space="preserve">
<value>'{0}' is not a known hash algorithm.</value>
</data>
</root>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Security.Cryptography
{
internal static class HashAlgorithmNames
{
internal const string SHA1 = nameof(SHA1);
internal const string SHA256 = nameof(SHA256);
internal const string SHA384 = nameof(SHA384);
internal const string SHA512 = nameof(SHA512);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text;
using System.Runtime.CompilerServices;
namespace System.Security.Cryptography
{
internal static class NetStandardShims
{
internal static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> str, Span<byte> destination)
{
if (str.IsEmpty)
{
return 0;
}
fixed (char* pStr = str)
fixed (byte* pDestination = destination)
{
return encoding.GetBytes(pStr, str.Length, pDestination, destination.Length);
}
}
}
internal static class CryptographicOperations
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
internal static void ZeroMemory(Span<byte> buffer)
{
buffer.Clear();
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
namespace System.Security.Cryptography
{
public sealed partial class SP800108HmacCounterKdf : IDisposable
{
private static readonly bool s_useCngKeyDerivation = IsWindows8OrGreater();
private static partial SP800108HmacCounterKdfImplementationBase CreateImplementation(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm)
{
if (s_useCngKeyDerivation)
{
return new SP800108HmacCounterKdfImplementationCng(key, hashAlgorithm);
}
else
{
return new SP800108HmacCounterKdfImplementationManaged(key, hashAlgorithm);
}
}
private static partial byte[] DeriveBytesCore(
byte[] key,
HashAlgorithmName hashAlgorithm,
byte[] label,
byte[] context,
int derivedKeyLengthInBytes)
{
byte[] result = new byte[derivedKeyLengthInBytes];
if (s_useCngKeyDerivation)
{
SP800108HmacCounterKdfImplementationCng.DeriveBytesOneShot(key, hashAlgorithm, label, context, result);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, result);
}
return result;
}
private static partial void DeriveBytesCore(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
if (s_useCngKeyDerivation)
{
SP800108HmacCounterKdfImplementationCng.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
}
private static partial void DeriveBytesCore(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<char> label,
ReadOnlySpan<char> context,
Span<byte> destination)
{
if (s_useCngKeyDerivation)
{
SP800108HmacCounterKdfImplementationCng.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
}
private static bool IsWindows8OrGreater()
{
#if NET
return OperatingSystem.IsWindowsVersionAtLeast(6, 2);
#elif NETSTANDARD
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
Version version = Environment.OSVersion.Version;
return isWindows && (version.Major > 6 || (version.Major == 6 && version.Minor >= 2));
#elif NETFRAMEWORK
Version version = Environment.OSVersion.Version;
return version.Major > 6 || (version.Major == 6 && version.Minor >= 2);
#else
#error Unhandled platform target
#endif
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
namespace System.Security.Cryptography
{
internal sealed partial class SP800108HmacCounterKdfImplementationCng
{
internal unsafe SP800108HmacCounterKdfImplementationCng(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
{
Debug.Assert(hashAlgorithm.Name is not null);
scoped ReadOnlySpan<byte> symmetricKeyMaterial;
scoped Span<byte> clearSpan = default;
int symmetricKeyMaterialLength;
int hashAlgorithmBlockSize = GetHashBlockSize(hashAlgorithm.Name);
if (key.Length > hashAlgorithmBlockSize)
{
byte[] keyArray = new byte[key.Length];
fixed (byte* pKeyArray = keyArray)
{
key.CopyTo(keyArray);
clearSpan = HashOneShot(hashAlgorithm, keyArray);
CryptographicOperations.ZeroMemory(keyArray);
}
symmetricKeyMaterial = clearSpan;
symmetricKeyMaterialLength = symmetricKeyMaterial.Length;
}
else if (!key.IsEmpty)
{
symmetricKeyMaterial = key;
symmetricKeyMaterialLength = key.Length;
}
else
{
// CNG requires a non-null pointer even when the length is zero.
symmetricKeyMaterial = stackalloc byte[] { 0 };
symmetricKeyMaterialLength = 0;
}
try
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
_keyHandle = CreateSymmetricKey(pSymmetricKeyMaterial, symmetricKeyMaterialLength);
}
}
finally
{
CryptographicOperations.ZeroMemory(clearSpan);
}
_hashAlgorithm = hashAlgorithm;
}
// For .NET Standard / .NET Framework, provide a byte overload so that we don't go from array->span->array
// when we need to adjust keys that are too large.
internal unsafe SP800108HmacCounterKdfImplementationCng(byte[] key, HashAlgorithmName hashAlgorithm)
{
Debug.Assert(hashAlgorithm.Name is not null);
scoped ReadOnlySpan<byte> symmetricKeyMaterial;
scoped Span<byte> clearSpan = default;
int symmetricKeyMaterialLength;
int hashAlgorithmBlockSize = GetHashBlockSize(hashAlgorithm.Name);
if (key.Length > hashAlgorithmBlockSize)
{
clearSpan = HashOneShot(hashAlgorithm, key);
symmetricKeyMaterial = clearSpan;
symmetricKeyMaterialLength = symmetricKeyMaterial.Length;
}
else if (key.Length > 0)
{
symmetricKeyMaterial = key;
symmetricKeyMaterialLength = key.Length;
}
else
{
// CNG requires a non-null pointer even when the length is zero.
symmetricKeyMaterial = stackalloc byte[] { 0 };
symmetricKeyMaterialLength = 0;
}
try
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
_keyHandle = CreateSymmetricKey(pSymmetricKeyMaterial, symmetricKeyMaterialLength);
}
}
finally
{
CryptographicOperations.ZeroMemory(clearSpan);
}
_hashAlgorithm = hashAlgorithm;
}
private static byte[] HashOneShot(HashAlgorithmName hashAlgorithm, byte[] data)
{
using (IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm))
{
hash.AppendData(data);
return hash.GetHashAndReset();
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Text;
namespace System.Security.Cryptography
{
internal sealed partial class SP800108HmacCounterKdfImplementationManaged
{
public SP800108HmacCounterKdfImplementationManaged(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
{
_key = key.ToArray();
_hashAlgorithm = hashAlgorithm;
}
internal static unsafe void DeriveBytesOneShot(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
if (destination.IsEmpty)
{
return;
}
// IncrementalHash needs an array of the correct size, so we can't rent for the key.
byte[] keyBuffer = new byte[key.Length];
byte[] labelBuffer = CryptoPool.Rent(label.Length);
byte[] contextBuffer = CryptoPool.Rent(context.Length);
// Fixed to prevent GC moves.
fixed (byte* pKeyBuffer = keyBuffer)
{
try
{
key.CopyTo(keyBuffer);
label.CopyTo(labelBuffer);
context.CopyTo(contextBuffer);
DeriveBytesOneShot(
keyBuffer,
hashAlgorithm,
labelBuffer,
label.Length,
contextBuffer,
context.Length,
destination);
}
finally
{
CryptographicOperations.ZeroMemory(keyBuffer);
CryptoPool.Return(labelBuffer, clearSize: label.Length);
CryptoPool.Return(contextBuffer, clearSize: context.Length);
}
}
}
internal static void DeriveBytesOneShot(
byte[] key,
HashAlgorithmName hashAlgorithm,
byte[] label,
byte[] context,
Span<byte> destination)
{
DeriveBytesOneShot(key, hashAlgorithm, label, label.Length, context, context.Length, destination);
}
internal static unsafe void DeriveBytesOneShot(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<char> label,
ReadOnlySpan<char> context,
Span<byte> destination)
{
if (destination.Length == 0)
{
return;
}
// The netstandard implementation needs arrays for all inputs, so always rent and don't perform
// the UTF8 encoding on the stack since that will just end up renting again anyway.
Encoding utf8ThrowingEncoding = Utf8DataEncoding.ThrowingUtf8Encoding;
byte[] labelBuffer = CryptoPool.Rent(utf8ThrowingEncoding.GetMaxByteCount(label.Length));
byte[] contextBuffer = CryptoPool.Rent(utf8ThrowingEncoding.GetMaxByteCount(context.Length));
int labelWritten = 0;
int contextWritten = 0;
byte[] keyBuffer = new byte[key.Length];
fixed (byte* pKeyBuffer = keyBuffer)
{
try
{
labelWritten = utf8ThrowingEncoding.GetBytes(label, labelBuffer);
contextWritten = utf8ThrowingEncoding.GetBytes(context, contextBuffer);
key.CopyTo(keyBuffer);
DeriveBytesOneShot(
keyBuffer,
hashAlgorithm,
labelBuffer,
labelWritten,
contextBuffer,
contextWritten,
destination);
}
finally
{
CryptographicOperations.ZeroMemory(keyBuffer);
CryptoPool.Return(labelBuffer, labelWritten);
CryptoPool.Return(contextBuffer, contextWritten);
}
}
}
private static void DeriveBytesOneShot(
byte[] key,
HashAlgorithmName hashAlgorithm,
byte[] label,
int labelLength,
byte[] context,
int contextLength,
Span<byte> destination)
{
if (destination.Length == 0)
{
return;
}
Debug.Assert(destination.Length <= 0x1FFFFFFF);
// Do everything as checked. Over/underflows are never expected.
checked
{
// The KDF is defined as K(i) := PRF (KI, [i]2 || Label || 0x00 || Context || [L]2)
// We know L is already less than 0x1FFFFFFF. h = ceil(L / h) where H is the hash length in bits.
// So we don't expect i to overflow.
using (IncrementalHash hash = IncrementalHash.CreateHMAC(hashAlgorithm, key))
{
// We use this rented buffer for three things. The first two uints for i and L, and last byte
// for the zero separator. So this is
// uint(L) | uint(i) | byte(0)
// This lets us use a single rent for the L, i, and 0x00.
const int LOffset = 0;
const int LLength = sizeof(uint);
const int IOffset = LLength + LOffset;
const int ILength = sizeof(uint);
const int ZeroOffset = IOffset + ILength;
const int ZeroLength = sizeof(byte);
const int RentSize = ZeroOffset + ZeroLength;
byte[]? rentedBuffer = null;
try
{
rentedBuffer = CryptoPool.Rent(RentSize);
WriteUInt32BigEndian((uint)destination.Length * 8U, rentedBuffer.AsSpan(LOffset, LLength));
rentedBuffer[ZeroOffset] = 0;
for (uint i = 1; !destination.IsEmpty; i++)
{
WriteUInt32BigEndian(i, rentedBuffer.AsSpan(IOffset, ILength));
hash.AppendData(rentedBuffer, IOffset, ILength);
hash.AppendData(label, 0, labelLength);
hash.AppendData(rentedBuffer, ZeroOffset, ZeroLength);
hash.AppendData(context, 0, contextLength);
hash.AppendData(rentedBuffer, LOffset, LLength);
byte[] hmac = hash.GetHashAndReset();
int needed = Math.Min(destination.Length, hmac.Length);
hmac.AsSpan(0, needed).CopyTo(destination);
destination = destination.Slice(needed);
// Best effort to zero out the key material.
CryptographicOperations.ZeroMemory(hmac);
}
}
finally
{
if (rentedBuffer is not null)
{
CryptoPool.Return(rentedBuffer, clearSize: RentSize);
}
}
}
}
}
private static void WriteUInt32BigEndian(uint value, Span<byte> destination)
{
Debug.Assert(destination.Length == sizeof(uint));
destination[0] = (byte)(value >> 24);
destination[1] = (byte)(value >> 16);
destination[2] = (byte)(value >> 8);
destination[3] = (byte)(value);
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkMinimum)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.ArgValidation.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.ArgValidation.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.Functional.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.Functional.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.Helpers.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.Helpers.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.ThreadSafety.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.ThreadSafety.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Microsoft.Bcl.Cryptography.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" />
</ItemGroup>
</Project>
......@@ -2340,6 +2340,24 @@ public partial class SignatureDescription
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("CreateFormatter is not trim compatible because the algorithm implementation referenced by FormatterAlgorithm might be removed.")]
public virtual System.Security.Cryptography.AsymmetricSignatureFormatter CreateFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { throw null; }
}
public sealed partial class SP800108HmacCounterKdf : System.IDisposable
{
public SP800108HmacCounterKdf(byte[] key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public SP800108HmacCounterKdf(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public static byte[] DeriveBytes(byte[] key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, byte[] label, byte[] context, int derivedKeyLengthInBytes) { throw null; }
public static byte[] DeriveBytes(byte[] key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, string label, string context, int derivedKeyLengthInBytes) { throw null; }
public static byte[] DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, int derivedKeyLengthInBytes) { throw null; }
public static void DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, System.Span<byte> destination) { }
public static byte[] DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, int derivedKeyLengthInBytes) { throw null; }
public static void DeriveBytes(System.ReadOnlySpan<byte> key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, System.Span<byte> destination) { }
public byte[] DeriveKey(byte[] label, byte[] context, int derivedKeyLengthInBytes) { throw null; }
public byte[] DeriveKey(System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, int derivedKeyLengthInBytes) { throw null; }
public void DeriveKey(System.ReadOnlySpan<byte> label, System.ReadOnlySpan<byte> context, System.Span<byte> destination) { }
public byte[] DeriveKey(System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, int derivedKeyLengthInBytes) { throw null; }
public void DeriveKey(System.ReadOnlySpan<char> label, System.ReadOnlySpan<char> context, System.Span<byte> destination) { }
public byte[] DeriveKey(string label, string context, int derivedKeyLengthInBytes) { throw null; }
public void Dispose() { }
}
public abstract partial class SymmetricAlgorithm : System.IDisposable
{
protected int BlockSizeValue;
......
......@@ -93,6 +93,9 @@
<data name="Argument_DestinationTooShort" xml:space="preserve">
<value>Destination is too short.</value>
</data>
<data name="Argument_EmptyString" xml:space="preserve">
<value>The value cannot be an empty string.</value>
</data>
<data name="Argument_Invalid_SafeHandleInvalidOrClosed" xml:space="preserve">
<value>The method cannot be called with an invalid or closed SafeHandle.</value>
</data>
......@@ -159,6 +162,9 @@
<data name="ArgumentOutOfRange_IndexMustBeLessOrEqual" xml:space="preserve">
<value>Index was out of range. Must be non-negative and less than or equal to the size of the collection.</value>
</data>
<data name="ArgumentOutOfRange_KOut_Too_Large" xml:space="preserve">
<value>The number of bytes requested is too large. The number of bytes produced by SP800108HmacCounterKdf cannot exceed 536,870,911 bytes.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
<value>Non-negative number required.</value>
</data>
......
......@@ -261,6 +261,12 @@
Link="Common\System\Security\Cryptography\RSAKeyFormatHelper.Encrypted.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RsaPaddingProcessor.cs"
Link="Common\System\Security\Cryptography\RsaPaddingProcessor.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdf.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdf.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationBase.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationBase.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\Utf8DataEncoding.cs"
Link="Common\System\Security\Cryptography\Utf8DataEncoding.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs"
Link="Common\System\Text\ValueStringBuilder.cs" />
<Compile Include="$(CommonPath)System\Threading\Tasks\TaskToApm.cs"
......@@ -557,6 +563,8 @@
Link="Common\Interop\Browser\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)System\Sha1ForNonSecretPurposes.cs"
Link="Common\System\Sha1ForNonSecretPurposes.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesImplementation.NotSupported.cs" />
......@@ -583,6 +591,8 @@
<Compile Include="System\Security\Cryptography\RC2Implementation.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\RSACryptoServiceProvider.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\RSA.Create.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.Managed.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\SHAHashProvider.Browser.Managed.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCryptoServiceProvider.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.NotSupported.cs" />
......@@ -719,6 +729,8 @@
Link="Common\System\Security\Cryptography\ECOpenSsl.ImportExport.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RSAOpenSsl.cs"
Link="Common\System\Security\Cryptography\RSAOpenSsl.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\DigestInfoAsn.xml">
<Link>Common\System\Security\Cryptography\Asn1\DigestInfoAsn.xml</Link>
</AsnXml>
......@@ -821,6 +833,8 @@
<Compile Include="System\Security\Cryptography\RSAOpenSsl.cs" />
<Compile Include="System\Security\Cryptography\RSAWrapper.cs" />
<Compile Include="System\Security\Cryptography\SafeEvpPKeyHandle.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.Managed.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCryptoServiceProvider.Wrap.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.OpenSsl.cs" />
<AsnXml Include="System\Security\Cryptography\X509Certificates\Asn1\DistributionPointAsn.xml" />
......@@ -937,6 +951,8 @@
Link="Common\System\Security\Cryptography\ECDsaAndroid.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RSAAndroid.cs"
Link="Common\System\Security\Cryptography\RSAAndroid.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\DigestInfoAsn.xml">
<Link>Common\System\Security\Cryptography\Asn1\DigestInfoAsn.xml</Link>
</AsnXml>
......@@ -1027,6 +1043,8 @@
<Compile Include="System\Security\Cryptography\RC2Implementation.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\RSA.Create.Android.cs" />
<Compile Include="System\Security\Cryptography\RSACryptoServiceProvider.Unix.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.Managed.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCryptoServiceProvider.Wrap.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\AndroidCertificatePal.cs" />
......@@ -1172,6 +1190,8 @@
Link="Common\System\Security\Cryptography\ECDsaSecurityTransforms.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RSASecurityTransforms.cs"
Link="Common\System\Security\Cryptography\RSASecurityTransforms.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SecKeyPair.cs"
Link="Common\System\Security\Cryptography\SecKeyPair.cs" />
<Compile Include="System\Security\Cryptography\AesImplementation.Apple.cs" />
......@@ -1200,6 +1220,8 @@
<Compile Include="System\Security\Cryptography\RC2Implementation.Apple.cs" />
<Compile Include="System\Security\Cryptography\RSA.Create.SecurityTransforms.cs" />
<Compile Include="System\Security\Cryptography\RSACryptoServiceProvider.Unix.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.Managed.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCryptoServiceProvider.Wrap.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.Apple.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\AppleCertificatePal.cs" />
......@@ -1721,6 +1743,10 @@
Link="Common\System\Security\Cryptography\RSACng.ImportExport.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RSACng.SignVerify.cs"
Link="Common\System\Security\Cryptography\RSACng.SignVerify.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationCng.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationCng.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs"
Link="Common\System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="Microsoft\Win32\SafeHandles\NCryptSafeHandles.cs" />
<Compile Include="System\Security\Cryptography\AeadCommon.Windows.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.Windows.cs" />
......@@ -1797,6 +1823,9 @@
<Compile Include="System\Security\Cryptography\RSACng.cs" />
<Compile Include="System\Security\Cryptography\RSACng.ImportExport.cs" />
<Compile Include="System\Security\Cryptography\RSACng.Key.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.Windows.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationCng.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCng.Windows.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCryptoServiceProvider.Wrap.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.Windows.cs" />
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
namespace System.Security.Cryptography
{
public sealed partial class SP800108HmacCounterKdf : IDisposable
{
private static partial SP800108HmacCounterKdfImplementationBase CreateImplementation(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm)
{
return new SP800108HmacCounterKdfImplementationManaged(key, hashAlgorithm);
}
private static partial byte[] DeriveBytesCore(
byte[] key,
HashAlgorithmName hashAlgorithm,
byte[] label,
byte[] context,
int derivedKeyLengthInBytes)
{
byte[] result = new byte[derivedKeyLengthInBytes];
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, result);
return result;
}
private static partial void DeriveBytesCore(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
private static partial void DeriveBytesCore(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<char> label,
ReadOnlySpan<char> context,
Span<byte> destination)
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
namespace System.Security.Cryptography
{
public sealed partial class SP800108HmacCounterKdf : IDisposable
{
private static readonly bool s_isWindows8OrGreater = OperatingSystem.IsWindowsVersionAtLeast(6, 2);
private static partial SP800108HmacCounterKdfImplementationBase CreateImplementation(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm)
{
if (s_isWindows8OrGreater)
{
return new SP800108HmacCounterKdfImplementationCng(key, hashAlgorithm);
}
else
{
return new SP800108HmacCounterKdfImplementationManaged(key, hashAlgorithm);
}
}
private static partial byte[] DeriveBytesCore(
byte[] key,
HashAlgorithmName hashAlgorithm,
byte[] label,
byte[] context,
int derivedKeyLengthInBytes)
{
byte[] result = new byte[derivedKeyLengthInBytes];
if (s_isWindows8OrGreater)
{
SP800108HmacCounterKdfImplementationCng.DeriveBytesOneShot(key, hashAlgorithm, label, context, result);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, result);
}
return result;
}
private static partial void DeriveBytesCore(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
if (s_isWindows8OrGreater)
{
SP800108HmacCounterKdfImplementationCng.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
}
private static partial void DeriveBytesCore(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<char> label,
ReadOnlySpan<char> context,
Span<byte> destination)
{
if (s_isWindows8OrGreater)
{
SP800108HmacCounterKdfImplementationCng.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
namespace System.Security.Cryptography
{
internal sealed partial class SP800108HmacCounterKdfImplementationCng
{
internal unsafe SP800108HmacCounterKdfImplementationCng(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
{
Debug.Assert(hashAlgorithm.Name is not null);
scoped ReadOnlySpan<byte> symmetricKeyMaterial;
scoped Span<byte> clearSpan = default;
int symmetricKeyMaterialLength;
int hashAlgorithmBlockSize = GetHashBlockSize(hashAlgorithm.Name);
if (key.Length > hashAlgorithmBlockSize)
{
Span<byte> buffer = stackalloc byte[512 / 8]; // Largest supported digest is SHA512.
symmetricKeyMaterialLength = HashOneShot(hashAlgorithm, key, buffer);
clearSpan = buffer.Slice(0, symmetricKeyMaterialLength);
symmetricKeyMaterial = clearSpan;
}
else if (!key.IsEmpty)
{
symmetricKeyMaterial = key;
symmetricKeyMaterialLength = key.Length;
}
else
{
// CNG requires a non-null pointer even when the length is zero.
symmetricKeyMaterial = stackalloc byte[] { 0 };
symmetricKeyMaterialLength = 0;
}
try
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
_keyHandle = CreateSymmetricKey(pSymmetricKeyMaterial, symmetricKeyMaterialLength);
}
}
finally
{
CryptographicOperations.ZeroMemory(clearSpan);
}
_hashAlgorithm = hashAlgorithm;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "Weak algorithms are used as instructed by the caller")]
private static int HashOneShot(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> data, Span<byte> destination)
{
Debug.Assert(hashAlgorithm.Name is not null);
switch (hashAlgorithm.Name)
{
case HashAlgorithmNames.SHA1:
return SHA1.HashData(data, destination);
case HashAlgorithmNames.SHA256:
return SHA256.HashData(data, destination);
case HashAlgorithmNames.SHA384:
return SHA384.HashData(data, destination);
case HashAlgorithmNames.SHA512:
return SHA512.HashData(data, destination);
default:
Debug.Fail($"Unexpected hash algorithm '{hashAlgorithm.Name}'");
throw new CryptographicException();
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers.Binary;
using System.Diagnostics;
namespace System.Security.Cryptography
{
internal sealed partial class SP800108HmacCounterKdfImplementationManaged
{
private const int CharToBytesStackBufferSize = 256;
public SP800108HmacCounterKdfImplementationManaged(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
{
// Use the POH if we can so the key doesn't get moved around by the GC.
_key = GC.AllocateArray<byte>(key.Length, pinned: true);
key.CopyTo(_key);
_hashAlgorithm = hashAlgorithm;
}
internal static void DeriveBytesOneShot(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
if (destination.Length == 0)
{
return;
}
Debug.Assert(destination.Length <= 0x1FFFFFFF);
// Do everything as checked. Over/underflows are never expected.
checked
{
// The KDF is defined as K(i) := PRF (KI, [i]2 || Label || 0x00 || Context || [L]2)
// We know L is already less than 0x1FFFFFFF. h = ceil(L / h) where H is the hash length in bits.
// So we don't expect i to overflow.
using (IncrementalHash hash = IncrementalHash.CreateHMAC(hashAlgorithm, key))
{
Span<byte> iBuffer = stackalloc byte[sizeof(uint)];
Span<byte> lBuffer = stackalloc byte[sizeof(uint)];
ReadOnlySpan<byte> zero = stackalloc byte[] { 0 };
Span<byte> hmacBuffer = stackalloc byte[512 / 8]; // Largest HMAC supported is SHA512
int hmacBufferWritten = 0;
BinaryPrimitives.WriteUInt32BigEndian(lBuffer, (uint)destination.Length * 8U);
for (uint i = 1; !destination.IsEmpty; i++)
{
BinaryPrimitives.WriteUInt32BigEndian(iBuffer, i);
hash.AppendData(iBuffer);
hash.AppendData(label);
hash.AppendData(zero);
hash.AppendData(context);
hash.AppendData(lBuffer);
if (destination.Length >= hash.HashLengthInBytes)
{
int written = hash.GetHashAndReset(destination);
destination = destination.Slice(written);
}
else
{
hmacBufferWritten = hash.GetHashAndReset(hmacBuffer);
Debug.Assert(hmacBufferWritten > destination.Length);
hmacBuffer.Slice(0, destination.Length).CopyTo(destination);
destination = default;
}
}
// Get derived key material off the stack, if any.
CryptographicOperations.ZeroMemory(hmacBuffer.Slice(0, hmacBufferWritten));
}
}
}
internal static void DeriveBytesOneShot(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm,
ReadOnlySpan<char> label,
ReadOnlySpan<char> context,
Span<byte> destination)
{
if (destination.Length == 0)
{
return;
}
using (Utf8DataEncoding labelData = new Utf8DataEncoding(label, stackalloc byte[CharToBytesStackBufferSize]))
using (Utf8DataEncoding contextData = new Utf8DataEncoding(context, stackalloc byte[CharToBytesStackBufferSize]))
{
DeriveBytesOneShot(key, hashAlgorithm, labelData.Utf8Bytes, contextData.Utf8Bytes, destination);
}
}
}
}
......@@ -39,6 +39,14 @@
Link="ProductionCode\Common\System\Net\MultiArrayBuffer.cs" />
<Compile Include="$(CommonPath)System\Net\StreamBuffer.cs"
Link="ProductionCode\Common\System\Net\StreamBuffer.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.ArgValidation.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.ArgValidation.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.Functional.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.Functional.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.Helpers.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.Helpers.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SP800108HmacCounterKdfTests.ThreadSafety.cs"
Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.ThreadSafety.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SignatureSupport.cs"
Link="CommonTest\System\Security\Cryptography\SignatureSupport.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\AES\AesCipherTests.Data.cs"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册