Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/9.0] Use OpenSSL 3's KBKDF for SP800-108 if it is available #106893

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class Crypto
{
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKdfFree")]
internal static partial void EvpKdfFree(IntPtr kdf);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_KbkdfHmacOneShot", StringMarshalling = StringMarshalling.Utf8)]
private static unsafe partial int CryptoNative_KbkdfHmacOneShot(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> key,
int keyLength,
string algorithm,
ReadOnlySpan<byte> label,
int labelLength,
ReadOnlySpan<byte> context,
int contextLength,
Span<byte> destination,
int destinationLength);

internal static void KbkdfHmacOneShot(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> key,
string algorithm,
ReadOnlySpan<byte> label,
ReadOnlySpan<byte> context,
Span<byte> destination)
{
const int Success = 1;
int ret = CryptoNative_KbkdfHmacOneShot(
kdf,
key,
key.Length,
algorithm,
label,
label.Length,
context,
context.Length,
destination,
destination.Length);

if (ret != Success)
{
Debug.Assert(ret == 0);
throw CreateOpenSslCryptographicException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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.Runtime.InteropServices;
using System.Security.Cryptography;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class Crypto
{
internal static partial class EvpKdfAlgs
{
private const string KbkdfAlgorithmName = "KBKDF";

internal static SafeEvpKdfHandle? Kbkdf { get; }

static EvpKdfAlgs()
{
CryptoInitializer.Initialize();

// Do not use property initializers for these because we need to ensure CryptoInitializer.Initialize
// is called first. Property initializers happen before cctors, so instead set the property after the
// initializer is run.
Kbkdf = EvpKdfFetch(KbkdfAlgorithmName);
}

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKdfFetch", StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpKdfHandle CryptoNative_EvpKdfFetch(string algorithm, out int haveFeature);

private static SafeEvpKdfHandle? EvpKdfFetch(string algorithm)
{
SafeEvpKdfHandle kdf = CryptoNative_EvpKdfFetch(algorithm, out int haveFeature);

if (haveFeature == 0)
{
Debug.Assert(kdf.IsInvalid);
kdf.Dispose();
return null;
}

if (kdf.IsInvalid)
{
kdf.Dispose();
throw CreateOpenSslCryptographicException();
}

return kdf;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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.Security;
using System.Runtime.InteropServices;

namespace Microsoft.Win32.SafeHandles
{
internal sealed class SafeEvpKdfHandle : SafeHandle
{
public SafeEvpKdfHandle() : base(0, ownsHandle: true)
{
}

protected override bool ReleaseHandle()
{
Interop.Crypto.EvpKdfFree(handle);
handle = 0;
return true;
}

public override bool IsInvalid => handle == 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,10 @@
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs"
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Kdf.cs"
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Kdf.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.KdfAlgs.cs"
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.KdfAlgs.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Mac.cs"
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Mac.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.MacAlgs.cs"
Expand Down Expand Up @@ -791,6 +795,8 @@
Link="Common\Microsoft\Win32\SafeHandles\SafeDsaHandle.Unix.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeEcKeyHandle.Unix.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeEcKeyHandle.Unix.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeEvpKdfHandle.Unix.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeEvpKdfHandle.Unix.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeEvpMacHandle.Unix.cs"
Expand Down Expand Up @@ -878,8 +884,9 @@
<Compile Include="System\Security\Cryptography\SafeEvpPKeyHandle.OpenSsl.Unix.cs" />
<Compile Include="System\Security\Cryptography\Shake128.NonWindows.cs" />
<Compile Include="System\Security\Cryptography\Shake256.NonWindows.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.Managed.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdf.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationManaged.cs" />
<Compile Include="System\Security\Cryptography\SP800108HmacCounterKdfImplementationOpenSsl.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" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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
{
public sealed partial class SP800108HmacCounterKdf : IDisposable
{
private static readonly bool s_hasOpenSslImplementation = Interop.Crypto.EvpKdfAlgs.Kbkdf is not null;

private static partial SP800108HmacCounterKdfImplementationBase CreateImplementation(
ReadOnlySpan<byte> key,
HashAlgorithmName hashAlgorithm)
{
if (s_hasOpenSslImplementation)
{
return new SP800108HmacCounterKdfImplementationOpenSsl(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_hasOpenSslImplementation)
{
SP800108HmacCounterKdfImplementationOpenSsl.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_hasOpenSslImplementation)
{
SP800108HmacCounterKdfImplementationOpenSsl.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_hasOpenSslImplementation)
{
SP800108HmacCounterKdfImplementationOpenSsl.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
else
{
SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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 Microsoft.Win32.SafeHandles;

namespace System.Security.Cryptography
{
internal sealed class SP800108HmacCounterKdfImplementationOpenSsl : SP800108HmacCounterKdfImplementationBase
{
private const int CharToBytesStackBufferSize = 256;

private readonly HashAlgorithmName _hashAlgorithm;
private readonly FixedMemoryKeyBox _keyBox;

internal unsafe SP800108HmacCounterKdfImplementationOpenSsl(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
{
_hashAlgorithm = hashAlgorithm;
_keyBox = new FixedMemoryKeyBox(key);
}

public override void Dispose()
{
_keyBox.Dispose();
}

internal override unsafe void DeriveBytes(ReadOnlySpan<byte> label, ReadOnlySpan<byte> context, Span<byte> destination)
{
Debug.Assert(Interop.Crypto.EvpKdfAlgs.Kbkdf is { IsInvalid: false });

if (destination.IsEmpty)
{
return;
}

bool acquired = false;

try
{
_keyBox.DangerousAddRef(ref acquired);
Interop.Crypto.KbkdfHmacOneShot(
Interop.Crypto.EvpKdfAlgs.Kbkdf,
_keyBox.DangerousKeySpan,
_hashAlgorithm.Name!,
label,
context,
destination);
}
finally
{
if (acquired)
{
_keyBox.DangerousRelease();
}
}
}

internal override void DeriveBytes(byte[] label, byte[] context, Span<byte> destination)
{
DeriveBytes(new ReadOnlySpan<byte>(label), new ReadOnlySpan<byte>(context), destination);
}

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(Interop.Crypto.EvpKdfAlgs.Kbkdf is { IsInvalid: false });

if (destination.IsEmpty)
{
return;
}

Interop.Crypto.KbkdfHmacOneShot(
Interop.Crypto.EvpKdfAlgs.Kbkdf,
key,
hashAlgorithm.Name!,
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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set(NATIVECRYPTO_SOURCES
pal_err.c
pal_evp.c
pal_evp_cipher.c
pal_evp_kdf.c
pal_evp_mac.c
pal_evp_pkey.c
pal_evp_pkey_dsa.c
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#pragma once
#include "pal_types.h"

typedef struct evp_kdf_st EVP_KDF;
typedef struct evp_kdf_ctx_st EVP_KDF_CTX;
typedef struct evp_mac_st EVP_MAC;
typedef struct evp_mac_ctx_st EVP_MAC_CTX;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "pal_err.h"
#include "pal_evp.h"
#include "pal_evp_cipher.h"
#include "pal_evp_kdf.h"
#include "pal_evp_mac.h"
#include "pal_evp_pkey.h"
#include "pal_evp_pkey_dsa.h"
Expand Down Expand Up @@ -144,6 +145,9 @@ static const Entry s_cryptoNative[] =
DllImportEntry(CryptoNative_EvpDigestSqueeze)
DllImportEntry(CryptoNative_EvpDigestUpdate)
DllImportEntry(CryptoNative_EvpDigestXOFOneShot)
DllImportEntry(CryptoNative_KbkdfHmacOneShot)
DllImportEntry(CryptoNative_EvpKdfFetch)
DllImportEntry(CryptoNative_EvpKdfFree)
DllImportEntry(CryptoNative_EvpMacCtxDup)
DllImportEntry(CryptoNative_EvpMacCtxNew)
DllImportEntry(CryptoNative_EvpMacCtxFree)
Expand Down
Loading
Loading