Skip to content

Commit

Permalink
[release/9.0] Use OpenSSL 3's HKDF if it is available (#107085)
Browse files Browse the repository at this point in the history
* Split out managed implementation

* Misc. cleanup

* Introduce a PAL

* Start a Unix implementation

* Get DeriveKey working with OpenSSL

* Extract and Expand individual calls

* Fix build

* Fix tests on Azure Linux

* Use defined consts rather than magic strings

---------

Co-authored-by: Kevin Jones <kevin@vcsjones.com>
  • Loading branch information
github-actions[bot] and vcsjones authored Aug 29, 2024
1 parent 7cf8f4a commit 996ce3b
Show file tree
Hide file tree
Showing 14 changed files with 633 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,41 @@ internal static partial class Crypto
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKdfFree")]
internal static partial void EvpKdfFree(IntPtr kdf);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_HkdfDeriveKey", StringMarshalling = StringMarshalling.Utf8)]
private static partial int CryptoNative_HkdfDeriveKey(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> ikm,
int ikmLength,
string algorithm,
ReadOnlySpan<byte> salt,
int saltLength,
ReadOnlySpan<byte> info,
int infoLength,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_HkdfExpand", StringMarshalling = StringMarshalling.Utf8)]
private static partial int CryptoNative_HkdfExpand(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> prk,
int prkLength,
string algorithm,
ReadOnlySpan<byte> info,
int infoLength,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_HkdfExtract", StringMarshalling = StringMarshalling.Utf8)]
private static partial int CryptoNative_HkdfExtract(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> ikm,
int ikmLength,
string algorithm,
ReadOnlySpan<byte> salt,
int saltLength,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_KbkdfHmacOneShot", StringMarshalling = StringMarshalling.Utf8)]
private static unsafe partial int CryptoNative_KbkdfHmacOneShot(
SafeEvpKdfHandle kdf,
Expand All @@ -26,6 +61,84 @@ private static unsafe partial int CryptoNative_KbkdfHmacOneShot(
Span<byte> destination,
int destinationLength);

internal static void HkdfDeriveKey(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> ikm,
string algorithm,
ReadOnlySpan<byte> salt,
ReadOnlySpan<byte> info,
Span<byte> destination)
{
const int Success = 1;
int ret = CryptoNative_HkdfDeriveKey(
kdf,
ikm,
ikm.Length,
algorithm,
salt,
salt.Length,
info,
info.Length,
destination,
destination.Length);

if (ret != Success)
{
Debug.Assert(ret == 0);
throw CreateOpenSslCryptographicException();
}
}

internal static void HkdfExpand(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> prk,
string algorithm,
ReadOnlySpan<byte> info,
Span<byte> destination)
{
const int Success = 1;
int ret = CryptoNative_HkdfExpand(
kdf,
prk,
prk.Length,
algorithm,
info,
info.Length,
destination,
destination.Length);

if (ret != Success)
{
Debug.Assert(ret == 0);
throw CreateOpenSslCryptographicException();
}
}

internal static void HkdfExtract(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> ikm,
string algorithm,
ReadOnlySpan<byte> salt,
Span<byte> destination)
{
const int Success = 1;
int ret = CryptoNative_HkdfExtract(
kdf,
ikm,
ikm.Length,
algorithm,
salt,
salt.Length,
destination,
destination.Length);

if (ret != Success)
{
Debug.Assert(ret == 0);
throw CreateOpenSslCryptographicException();
}
}

internal static void KbkdfHmacOneShot(
SafeEvpKdfHandle kdf,
ReadOnlySpan<byte> key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ internal static partial class Crypto
internal static partial class EvpKdfAlgs
{
private const string KbkdfAlgorithmName = "KBKDF";
private const string HkdfAlgorithmName = "HKDF";

internal static SafeEvpKdfHandle? Kbkdf { get; }
internal static SafeEvpKdfHandle? Hkdf { get; }

static EvpKdfAlgs()
{
Expand All @@ -24,6 +26,7 @@ static EvpKdfAlgs()
// is called first. Property initializers happen before cctors, so instead set the property after the
// initializer is run.
Kbkdf = EvpKdfFetch(KbkdfAlgorithmName);
Hkdf = EvpKdfFetch(HkdfAlgorithmName);
}

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKdfFetch", StringMarshalling = StringMarshalling.Utf8)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@
<Compile Include="System\Security\Cryptography\HashProvider.cs" />
<Compile Include="System\Security\Cryptography\Helpers.cs" />
<Compile Include="System\Security\Cryptography\HKDF.cs" />
<Compile Include="System\Security\Cryptography\HKDFManagedImplementation.cs" />
<Compile Include="System\Security\Cryptography\HMAC.cs" />
<Compile Include="System\Security\Cryptography\HMACCommon.cs" />
<Compile Include="System\Security\Cryptography\HMACMD5.cs" />
Expand Down Expand Up @@ -668,6 +669,7 @@
<Compile Include="System\Security\Cryptography\ECDiffieHellman.Create.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\ECDsa.Create.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Browser.cs" />
<Compile Include="System\Security\Cryptography\HKDF.Managed.cs" />
<Compile Include="System\Security\Cryptography\HMACHashProvider.Browser.Managed.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Browser.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Kmac.Unsupported.cs" />
Expand Down Expand Up @@ -864,6 +866,7 @@
<Compile Include="System\Security\Cryptography\ECDiffieHellmanWrapper.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Unix.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\HKDF.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Unix.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\OidLookup.OpenSsl.cs" />
Expand Down Expand Up @@ -1024,6 +1027,7 @@
<Compile Include="System\Security\Cryptography\ECDsa.Create.Android.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Unix.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Android.cs" />
<Compile Include="System\Security\Cryptography\HKDF.Managed.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Unix.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Kmac.Unsupported.cs" />
<Compile Include="System\Security\Cryptography\OidLookup.NoFallback.cs" />
Expand Down Expand Up @@ -1154,6 +1158,7 @@
<Compile Include="System\Security\Cryptography\ECDsa.Create.SecurityTransforms.cs" />
<Compile Include="System\Security\Cryptography\HashAlgorithmNames.Apple.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Apple.cs" />
<Compile Include="System\Security\Cryptography\HKDF.Managed.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Apple.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Kmac.Unsupported.cs" />
<Compile Include="System\Security\Cryptography\OidLookup.NoFallback.cs" />
Expand Down Expand Up @@ -1745,6 +1750,7 @@
<Compile Include="System\Security\Cryptography\ECDiffieHellmanWrapper.cs" />
<Compile Include="System\Security\Cryptography\HashProviderCng.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Windows.cs" />
<Compile Include="System\Security\Cryptography\HKDF.Managed.cs" />
<Compile Include="System\Security\Cryptography\ICngSymmetricAlgorithm.cs" />
<Compile Include="System\Security\Cryptography\KeyPropertyName.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Windows.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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 static partial class HKDF
{
private static void Extract(
HashAlgorithmName hashAlgorithmName,
int hashLength,
ReadOnlySpan<byte> ikm,
ReadOnlySpan<byte> salt,
Span<byte> prk)
{
HKDFManagedImplementation.Extract(hashAlgorithmName, hashLength, ikm, salt, prk);
}

private static void Expand(
HashAlgorithmName hashAlgorithmName,
int hashLength,
ReadOnlySpan<byte> prk,
Span<byte> output,
ReadOnlySpan<byte> info)
{
HKDFManagedImplementation.Expand(hashAlgorithmName, hashLength, prk, output, info);
}

private static void DeriveKeyCore(
HashAlgorithmName hashAlgorithmName,
int hashLength,
ReadOnlySpan<byte> ikm,
Span<byte> output,
ReadOnlySpan<byte> salt,
ReadOnlySpan<byte> info)
{
HKDFManagedImplementation.DeriveKey(hashAlgorithmName, hashLength, ikm, output, salt, info);
}
}
}
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.

using System.Diagnostics;

namespace System.Security.Cryptography
{
public static partial class HKDF
{
private static readonly bool s_hasOpenSslImplementation = Interop.Crypto.EvpKdfAlgs.Hkdf is not null;

private static void Extract(
HashAlgorithmName hashAlgorithmName,
int hashLength,
ReadOnlySpan<byte> ikm,
ReadOnlySpan<byte> salt,
Span<byte> prk)
{
if (s_hasOpenSslImplementation)
{
Debug.Assert(Interop.Crypto.EvpKdfAlgs.Hkdf is not null);
Debug.Assert(hashAlgorithmName.Name is not null);

Interop.Crypto.HkdfExtract(Interop.Crypto.EvpKdfAlgs.Hkdf, ikm, hashAlgorithmName.Name, salt, prk);
}
else
{
HKDFManagedImplementation.Extract(hashAlgorithmName, hashLength, ikm, salt, prk);
}
}

private static void Expand(
HashAlgorithmName hashAlgorithmName,
int hashLength,
ReadOnlySpan<byte> prk,
Span<byte> output,
ReadOnlySpan<byte> info)
{
if (s_hasOpenSslImplementation)
{
Debug.Assert(Interop.Crypto.EvpKdfAlgs.Hkdf is not null);
Debug.Assert(hashAlgorithmName.Name is not null);

Interop.Crypto.HkdfExpand(Interop.Crypto.EvpKdfAlgs.Hkdf, prk, hashAlgorithmName.Name, info, output);
}
else
{
HKDFManagedImplementation.Expand(hashAlgorithmName, hashLength, prk, output, info);
}
}

private static void DeriveKeyCore(
HashAlgorithmName hashAlgorithmName,
int hashLength,
ReadOnlySpan<byte> ikm,
Span<byte> output,
ReadOnlySpan<byte> salt,
ReadOnlySpan<byte> info)
{
if (s_hasOpenSslImplementation)
{
Debug.Assert(Interop.Crypto.EvpKdfAlgs.Hkdf is not null);
Debug.Assert(hashAlgorithmName.Name is not null);

Interop.Crypto.HkdfDeriveKey(
Interop.Crypto.EvpKdfAlgs.Hkdf,
ikm,
hashAlgorithmName.Name,
salt,
info,
output);
}
else
{
HKDFManagedImplementation.DeriveKey(hashAlgorithmName, hashLength, ikm, output, salt, info);
}
}
}
}
Loading

0 comments on commit 996ce3b

Please sign in to comment.