diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kdf.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kdf.cs new file mode 100644 index 0000000000000..082d496497289 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kdf.cs @@ -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 key, + int keyLength, + string algorithm, + ReadOnlySpan label, + int labelLength, + ReadOnlySpan context, + int contextLength, + Span destination, + int destinationLength); + + internal static void KbkdfHmacOneShot( + SafeEvpKdfHandle kdf, + ReadOnlySpan key, + string algorithm, + ReadOnlySpan label, + ReadOnlySpan context, + Span 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(); + } + } + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.KdfAlgs.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.KdfAlgs.cs new file mode 100644 index 0000000000000..0b4217e30d791 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.KdfAlgs.cs @@ -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; + } + } + } +} diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeEvpKdfHandle.Unix.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeEvpKdfHandle.Unix.cs new file mode 100644 index 0000000000000..413772050dcf9 --- /dev/null +++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeEvpKdfHandle.Unix.cs @@ -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; + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 0854abbbe0686..ec6f682444aa2 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -739,6 +739,10 @@ Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs" /> + + + - + + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdf.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdf.OpenSsl.cs new file mode 100644 index 0000000000000..a26888af06a9d --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdf.OpenSsl.cs @@ -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 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 key, + HashAlgorithmName hashAlgorithm, + ReadOnlySpan label, + ReadOnlySpan context, + Span 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 key, + HashAlgorithmName hashAlgorithm, + ReadOnlySpan label, + ReadOnlySpan context, + Span destination) + { + if (s_hasOpenSslImplementation) + { + SP800108HmacCounterKdfImplementationOpenSsl.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination); + } + else + { + SP800108HmacCounterKdfImplementationManaged.DeriveBytesOneShot(key, hashAlgorithm, label, context, destination); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdfImplementationOpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdfImplementationOpenSsl.cs new file mode 100644 index 0000000000000..c279063011996 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdfImplementationOpenSsl.cs @@ -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 key, HashAlgorithmName hashAlgorithm) + { + _hashAlgorithm = hashAlgorithm; + _keyBox = new FixedMemoryKeyBox(key); + } + + public override void Dispose() + { + _keyBox.Dispose(); + } + + internal override unsafe void DeriveBytes(ReadOnlySpan label, ReadOnlySpan context, Span 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 destination) + { + DeriveBytes(new ReadOnlySpan(label), new ReadOnlySpan(context), destination); + } + + internal override void DeriveBytes(ReadOnlySpan label, ReadOnlySpan context, Span 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 key, + HashAlgorithmName hashAlgorithm, + ReadOnlySpan label, + ReadOnlySpan context, + Span 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 key, + HashAlgorithmName hashAlgorithm, + ReadOnlySpan label, + ReadOnlySpan context, + Span 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); + } + } + } +} diff --git a/src/native/libs/System.Security.Cryptography.Native/CMakeLists.txt b/src/native/libs/System.Security.Cryptography.Native/CMakeLists.txt index 8dee7f91e9054..ca536c1697a60 100644 --- a/src/native/libs/System.Security.Cryptography.Native/CMakeLists.txt +++ b/src/native/libs/System.Security.Cryptography.Native/CMakeLists.txt @@ -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 diff --git a/src/native/libs/System.Security.Cryptography.Native/apibridge_30.h b/src/native/libs/System.Security.Cryptography.Native/apibridge_30.h index 1485a5b056abd..29bec2f1d4dc1 100644 --- a/src/native/libs/System.Security.Cryptography.Native/apibridge_30.h +++ b/src/native/libs/System.Security.Cryptography.Native/apibridge_30.h @@ -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; diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index 480fba6e0cc98..523796cd3cac5 100644 --- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c @@ -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" @@ -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) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index c3f11cdb9e216..1ce5477023521 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -46,6 +46,7 @@ #include #include #include +#include #endif #if HAVE_OPENSSL_ENGINE @@ -411,6 +412,11 @@ extern bool g_libSslUses32BitTime; LIGHTUP_FUNCTION(EVP_DigestSqueeze) \ REQUIRED_FUNCTION(EVP_DigestUpdate) \ REQUIRED_FUNCTION(EVP_get_digestbyname) \ + LIGHTUP_FUNCTION(EVP_KDF_CTX_free) \ + LIGHTUP_FUNCTION(EVP_KDF_CTX_new) \ + LIGHTUP_FUNCTION(EVP_KDF_derive) \ + LIGHTUP_FUNCTION(EVP_KDF_fetch) \ + LIGHTUP_FUNCTION(EVP_KDF_free) \ LIGHTUP_FUNCTION(EVP_MAC_fetch) \ LIGHTUP_FUNCTION(EVP_MAC_final) \ LIGHTUP_FUNCTION(EVP_MAC_free) \ @@ -545,6 +551,7 @@ extern bool g_libSslUses32BitTime; LIGHTUP_FUNCTION(OSSL_STORE_load) \ LIGHTUP_FUNCTION(OSSL_STORE_open_ex) \ LIGHTUP_FUNCTION(OSSL_PARAM_construct_octet_string) \ + LIGHTUP_FUNCTION(OSSL_PARAM_construct_utf8_string) \ LIGHTUP_FUNCTION(OSSL_PARAM_construct_int32) \ LIGHTUP_FUNCTION(OSSL_PARAM_construct_end) \ REQUIRED_FUNCTION(PKCS8_PRIV_KEY_INFO_free) \ @@ -957,6 +964,11 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define EVP_DigestUpdate EVP_DigestUpdate_ptr #define EVP_get_digestbyname EVP_get_digestbyname_ptr #define EVP_md5 EVP_md5_ptr +#define EVP_KDF_CTX_free EVP_KDF_CTX_free_ptr +#define EVP_KDF_CTX_new EVP_KDF_CTX_new_ptr +#define EVP_KDF_derive EVP_KDF_derive_ptr +#define EVP_KDF_fetch EVP_KDF_fetch_ptr +#define EVP_KDF_free EVP_KDF_free_ptr #define EVP_MAC_fetch EVP_MAC_fetch_ptr #define EVP_MAC_final EVP_MAC_final_ptr #define EVP_MAC_free EVP_MAC_free_ptr @@ -1091,6 +1103,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr; #define OSSL_STORE_load OSSL_STORE_load_ptr #define OSSL_STORE_open_ex OSSL_STORE_open_ex_ptr #define OSSL_PARAM_construct_octet_string OSSL_PARAM_construct_octet_string_ptr +#define OSSL_PARAM_construct_utf8_string OSSL_PARAM_construct_utf8_string_ptr #define OSSL_PARAM_construct_int32 OSSL_PARAM_construct_int32_ptr #define OSSL_PARAM_construct_end OSSL_PARAM_construct_end_ptr #define PKCS8_PRIV_KEY_INFO_free PKCS8_PRIV_KEY_INFO_free_ptr diff --git a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h index 3fbe98235dff6..6baa8fa143347 100644 --- a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h +++ b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h @@ -12,6 +12,12 @@ #undef EVP_PKEY_CTX_set_rsa_pss_saltlen #undef EVP_PKEY_CTX_set_signature_md +#define OSSL_KDF_PARAM_DIGEST "digest" +#define OSSL_KDF_PARAM_MAC "mac" +#define OSSL_KDF_PARAM_KEY "key" +#define OSSL_KDF_PARAM_SALT "salt" +#define OSSL_KDF_PARAM_INFO "info" + #define OSSL_MAC_PARAM_KEY "key" #define OSSL_MAC_PARAM_CUSTOM "custom" #define OSSL_MAC_PARAM_XOF "xof" @@ -41,6 +47,12 @@ void ERR_set_debug(const char *file, int line, const char *func); void ERR_set_error(int lib, int reason, const char *fmt, ...); int EVP_CIPHER_get_nid(const EVP_CIPHER *e); +EVP_KDF* EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, const char *properties); +void EVP_KDF_free(EVP_KDF *kdf); +EVP_KDF_CTX *EVP_KDF_CTX_new(EVP_KDF *kdf); +void EVP_KDF_CTX_free(EVP_KDF_CTX *ctx); +int EVP_KDF_derive(EVP_KDF_CTX *ctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]); + int EVP_MAC_CTX_set_params(EVP_MAC_CTX *ctx, const OSSL_PARAM params[]); EVP_MAC_CTX *EVP_MAC_CTX_new(EVP_MAC *mac); void EVP_MAC_CTX_free(EVP_MAC_CTX *ctx); @@ -66,6 +78,7 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_pkey( OSSL_PARAM OSSL_PARAM_construct_end(void); OSSL_PARAM OSSL_PARAM_construct_int32(const char *key, int32_t *buf); OSSL_PARAM OSSL_PARAM_construct_octet_string(const char *key, void *buf, size_t bsize); +OSSL_PARAM OSSL_PARAM_construct_utf8_string(const char *key, char *buf, size_t bsize); void OSSL_LIB_CTX_free(OSSL_LIB_CTX*); OSSL_LIB_CTX* OSSL_LIB_CTX_new(void); diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_kdf.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kdf.c new file mode 100644 index 0000000000000..5b3705e812841 --- /dev/null +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kdf.c @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "openssl.h" +#include "pal_evp_kdf.h" +#include "pal_utilities.h" + +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" +void CryptoNative_EvpKdfFree(EVP_KDF* kdf) +{ +#ifdef NEED_OPENSSL_3_0 + if (API_EXISTS(EVP_KDF_free)) + { + // No error queue impact + EVP_KDF_free(kdf); + return; + } +#else + (void)kdf; +#endif + + assert(0 && "Inconsistent EVP_KDF API availability."); +} +#pragma clang diagnostic pop + + +EVP_KDF* CryptoNative_EvpKdfFetch(const char* algorithm, int32_t* haveFeature) +{ + assert(haveFeature); + +#ifdef NEED_OPENSSL_3_0 + if (API_EXISTS(EVP_KDF_fetch)) + { + ERR_clear_error(); + EVP_KDF* kdf = EVP_KDF_fetch(NULL, algorithm, NULL); + + if (kdf) + { + *haveFeature = 1; + return kdf; + } + else + { + unsigned long error = ERR_peek_error(); + + // If the fetch failed because the algorithm is unsupported, then set + // haveFeature to 0. Otherwise, assume the algorithm exists and the + // fetch failed for another reason, and set haveFeature to 1. + *haveFeature = ERR_GET_REASON(error) == ERR_R_UNSUPPORTED ? 0 : 1; + return NULL; + } + } +#else + (void)algorithm; + (void)haveFeature; +#endif + + *haveFeature = 0; + return NULL; +} + +int32_t CryptoNative_KbkdfHmacOneShot( + EVP_KDF* kdf, + uint8_t* key, + int32_t keyLength, + char* algorithm, + uint8_t* label, + int32_t labelLength, + uint8_t* context, + int32_t contextLength, + uint8_t* destination, + int32_t destinationLength) +{ + assert(kdf); + assert(key != NULL || keyLength == 0); + assert(keyLength >= 0); + assert(algorithm); + assert(destination); + assert(destinationLength > 0); + assert(label != NULL || labelLength == 0); + assert(context != NULL || contextLength == 0); + + ERR_clear_error(); + +#ifdef NEED_OPENSSL_3_0 + if (API_EXISTS(EVP_KDF_CTX_new)) + { + assert(API_EXISTS(EVP_KDF_CTX_free)); + assert(API_EXISTS(EVP_KDF_derive)); + assert(API_EXISTS(OSSL_PARAM_construct_utf8_string)); + assert(API_EXISTS(OSSL_PARAM_construct_octet_string)); + assert(API_EXISTS(OSSL_PARAM_construct_end)); + + unsigned char zero[] = { 0 }; + + if (key == NULL || keyLength == 0) + { + // OpenSSL does not permit an empty KBKDF key. Since we know we are in HMAC mode, and HMAC keys are zero-extended, + // We can create a non-empty key that is functionally equivalent to an empty one. + key = zero; + keyLength = 1; + } + + EVP_KDF_CTX* ctx = EVP_KDF_CTX_new(kdf); + int32_t ret = 0; + + if (ctx == NULL) + { + goto cleanup; + } + + size_t keyLengthT = Int32ToSizeT(keyLength); + size_t destinationLengthT = Int32ToSizeT(destinationLength); + size_t labelLengthT = Int32ToSizeT(labelLength); + size_t contextLengthT = Int32ToSizeT(contextLength); + + OSSL_PARAM params[] = + { + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, algorithm, 0), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC, "HMAC", 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void*)key, keyLengthT), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void*)label, labelLengthT), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void*)context, contextLengthT), + OSSL_PARAM_construct_end(), + }; + + if (EVP_KDF_derive(ctx, destination, destinationLengthT, params) <= 0) + { + goto cleanup; + } + + ret = 1; + +cleanup: + if (ctx != NULL) + { + EVP_KDF_CTX_free(ctx); + } + + return ret; + } +#else + (void)kdf; + (void)key; + (void)keyLength; + (void)algorithm; + (void)label; + (void)labelLength; + (void)context; + (void)contextLength; + (void)destination; + (void)destinationLength; + assert(0 && "Inconsistent EVP_KDF API availability."); +#endif + return 0; +} diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_kdf.h b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kdf.h new file mode 100644 index 0000000000000..0841dc2b3553e --- /dev/null +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kdf.h @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_compiler.h" +#include +#include "opensslshim.h" + +/* +Shims the EVP_KDF_free function. + +kdf: The KDF to free. +note: This method will assert that the platform has EVP_KDF_free. Callers are + responsible for ensuring the platform supports EVP_KDF_free. +*/ +PALEXPORT void CryptoNative_EvpKdfFree(EVP_KDF* kdf); + +/* +Shims the EVP_KDF_fetch function. + +algorithm: The name of the algorithm to fetch. +haveFeature: A pointer to an int32_t. When this function returns, the value will + contain an integer to determine if the platform supports EVP_KDF_fetch. + 0 indicates that the platform does not support EVP_KDF_fetch or the algorithm. + 1 indicates that the platform does support EVP_KDF_fetch and the algorithm. + +return: A pointer to an EVP_KDF. This pointer may be NULL if OpenSSL failed to allocate internally, + or, if the platform does not support EVP_KDF_fetch or the algorithm. + Use the haveFeature value to determine if the NULL value is due to allocation failure + or lack of platform support. +*/ +PALEXPORT EVP_KDF* CryptoNative_EvpKdfFetch(const char* algorithm, int32_t* haveFeature); + +/* +Performs a one-shot key derivation using KBKDF-HMAC. + +kdf: A handle to the KBKDF algorithm. +key: A pointer to a key. This value is set using OSSL_KDF_PARAM_KEY. This value + may be NULL if the keyLength parameter is 0. +keyLength: The length of the key in the key parameter. This value must be zero or positive. +algorithm: A null-terminated UTF-8 string representation of the HMAC algorithm to use. + this value is set using OSSL_KDF_PARAM_DIGEST. +label: A pointer to the label. This value may be NULL if the labelLength is 0. This + value is set using OSSL_KDF_PARAM_SALT. +labelLength: The length of the label. This value must be zero or positive. +context: A pointer to the context. This value may be NULL if the contextLength is 0. This + value is set using OSSL_KDF_PARAM_INFO. +contextLength: The length of the context. This value must be zero or positive. +destination: The buffer which receives the derived key. This value may not be NULL, a destination + is required. Callers are expected to early exit for empty destinations. +destinationLength: The length of the destination buffer, and the number of bytes to + derive from the KDF. This value must be positive. +*/ +PALEXPORT int32_t CryptoNative_KbkdfHmacOneShot( + EVP_KDF* kdf, + uint8_t* key, + int32_t keyLength, + char* algorithm, + uint8_t* label, + int32_t labelLength, + uint8_t* context, + int32_t contextLength, + uint8_t* destination, + int32_t destinationLength);