Skip to content

Commit

Permalink
Use OpenSSL 3's KBKDF for SP800-108 if it is available (#106893)
Browse files Browse the repository at this point in the history
  • Loading branch information
vcsjones authored Aug 23, 2024
1 parent 6e017ba commit 2937bf3
Show file tree
Hide file tree
Showing 13 changed files with 591 additions and 1 deletion.
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

0 comments on commit 2937bf3

Please sign in to comment.