Fix migration guide mappings for i2o/o2i_ECPublicKey
[openssl.git] / providers / implementations / kdfs / hkdf.c
index 987f1b28bfff1a9266ef1dff987fadb4b4e2cc13..a634216419b7c710d0ce2a810f7ed35d13fad61e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
 #include <openssl/evp.h>
 #include <openssl/kdf.h>
 #include <openssl/core_names.h>
+#include <openssl/proverr.h>
 #include "internal/cryptlib.h"
 #include "internal/numbers.h"
+#include "internal/packet.h"
 #include "crypto/evp.h"
 #include "prov/provider_ctx.h"
 #include "prov/providercommon.h"
-#include "prov/providercommonerr.h"
 #include "prov/implementations.h"
 #include "prov/provider_util.h"
-#include "e_os.h"
+#include "internal/e_os.h"
+#include "internal/params.h"
 
-#define HKDF_MAXBUF 1024
+#define HKDF_MAXBUF 2048
+#define HKDF_MAXINFO (32*1024)
 
 static OSSL_FUNC_kdf_newctx_fn kdf_hkdf_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_hkdf_dup;
 static OSSL_FUNC_kdf_freectx_fn kdf_hkdf_free;
 static OSSL_FUNC_kdf_reset_fn kdf_hkdf_reset;
 static OSSL_FUNC_kdf_derive_fn kdf_hkdf_derive;
@@ -40,13 +44,16 @@ static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_hkdf_settable_ctx_params;
 static OSSL_FUNC_kdf_set_ctx_params_fn kdf_hkdf_set_ctx_params;
 static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params;
 static OSSL_FUNC_kdf_get_ctx_params_fn kdf_hkdf_get_ctx_params;
+static OSSL_FUNC_kdf_derive_fn kdf_tls1_3_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_3_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_3_set_ctx_params;
 
-static int HKDF(const EVP_MD *evp_md,
+static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
                 const unsigned char *salt, size_t salt_len,
                 const unsigned char *key, size_t key_len,
                 const unsigned char *info, size_t info_len,
                 unsigned char *okm, size_t okm_len);
-static int HKDF_Extract(const EVP_MD *evp_md,
+static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
                         const unsigned char *salt, size_t salt_len,
                         const unsigned char *ikm, size_t ikm_len,
                         unsigned char *prk, size_t prk_len);
@@ -55,6 +62,15 @@ static int HKDF_Expand(const EVP_MD *evp_md,
                        const unsigned char *info, size_t info_len,
                        unsigned char *okm, size_t okm_len);
 
+/* Settable context parameters that are common across HKDF and the TLS KDF */
+#define HKDF_COMMON_SETTABLES                                           \
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0),           \
+        OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL),                      \
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),     \
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),         \
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),           \
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0)
+
 typedef struct {
     void *provctx;
     int mode;
@@ -63,7 +79,13 @@ typedef struct {
     size_t salt_len;
     unsigned char *key;
     size_t key_len;
-    unsigned char info[HKDF_MAXBUF];
+    unsigned char *prefix;
+    size_t prefix_len;
+    unsigned char *label;
+    size_t label_len;
+    unsigned char *data;
+    size_t data_len;
+    unsigned char *info;
     size_t info_len;
 } KDF_HKDF;
 
@@ -74,9 +96,7 @@ static void *kdf_hkdf_new(void *provctx)
     if (!ossl_prov_is_running())
         return NULL;
 
-    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
-        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
-    else
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL)
         ctx->provctx = provctx;
     return ctx;
 }
@@ -98,12 +118,45 @@ static void kdf_hkdf_reset(void *vctx)
 
     ossl_prov_digest_reset(&ctx->digest);
     OPENSSL_free(ctx->salt);
+    OPENSSL_free(ctx->prefix);
+    OPENSSL_free(ctx->label);
+    OPENSSL_clear_free(ctx->data, ctx->data_len);
     OPENSSL_clear_free(ctx->key, ctx->key_len);
-    OPENSSL_cleanse(ctx->info, ctx->info_len);
+    OPENSSL_clear_free(ctx->info, ctx->info_len);
     memset(ctx, 0, sizeof(*ctx));
     ctx->provctx = provctx;
 }
 
+static void *kdf_hkdf_dup(void *vctx)
+{
+    const KDF_HKDF *src = (const KDF_HKDF *)vctx;
+    KDF_HKDF *dest;
+
+    dest = kdf_hkdf_new(src->provctx);
+    if (dest != NULL) {
+        if (!ossl_prov_memdup(src->salt, src->salt_len, &dest->salt,
+                              &dest->salt_len)
+                || !ossl_prov_memdup(src->key, src->key_len,
+                                     &dest->key , &dest->key_len)
+                || !ossl_prov_memdup(src->prefix, src->prefix_len,
+                                     &dest->prefix, &dest->prefix_len)
+                || !ossl_prov_memdup(src->label, src->label_len,
+                                     &dest->label, &dest->label_len)
+                || !ossl_prov_memdup(src->data, src->data_len,
+                                     &dest->data, &dest->data_len)
+                || !ossl_prov_memdup(src->info, src->info_len,
+                                     &dest->info, &dest->info_len)
+                || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+            goto err;
+        dest->mode = src->mode;
+    }
+    return dest;
+
+ err:
+    kdf_hkdf_free(dest);
+    return NULL;
+}
+
 static size_t kdf_hkdf_size(KDF_HKDF *ctx)
 {
     int sz;
@@ -116,19 +169,21 @@ static size_t kdf_hkdf_size(KDF_HKDF *ctx)
         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
         return 0;
     }
-    sz = EVP_MD_size(md);
+    sz = EVP_MD_get_size(md);
     if (sz < 0)
         return 0;
 
     return sz;
 }
 
-static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen)
+static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen,
+                           const OSSL_PARAM params[])
 {
     KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+    OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
     const EVP_MD *md;
 
-    if (!ossl_prov_is_running())
+    if (!ossl_prov_is_running() || !kdf_hkdf_set_ctx_params(ctx, params))
         return 0;
 
     md = ossl_prov_digest_md(&ctx->digest);
@@ -140,43 +195,46 @@ static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen)
         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
         return 0;
     }
+    if (keylen == 0) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+        return 0;
+    }
 
     switch (ctx->mode) {
     case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND:
-        return HKDF(md, ctx->salt, ctx->salt_len, ctx->key,
-                    ctx->key_len, ctx->info, ctx->info_len, key,
-                    keylen);
+    default:
+        return HKDF(libctx, md, ctx->salt, ctx->salt_len,
+                    ctx->key, ctx->key_len, ctx->info, ctx->info_len, key, keylen);
 
     case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
-        return HKDF_Extract(md, ctx->salt, ctx->salt_len, ctx->key,
-                            ctx->key_len, key, keylen);
+        return HKDF_Extract(libctx, md, ctx->salt, ctx->salt_len,
+                            ctx->key, ctx->key_len, key, keylen);
 
     case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
         return HKDF_Expand(md, ctx->key, ctx->key_len, ctx->info,
                            ctx->info_len, key, keylen);
-
-    default:
-        return 0;
     }
 }
 
-static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+static int hkdf_common_set_ctx_params(KDF_HKDF *ctx, const OSSL_PARAM params[])
 {
+    OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
     const OSSL_PARAM *p;
-    KDF_HKDF *ctx = vctx;
-    OPENSSL_CTX *provctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx);
     int n;
 
-    if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+    if (params == NULL)
+        return 1;
+
+    if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
         return 0;
 
     if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE)) != NULL) {
         if (p->data_type == OSSL_PARAM_UTF8_STRING) {
-            if (strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) {
+            if (OPENSSL_strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) {
                 ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND;
-            } else if (strcasecmp(p->data, "EXTRACT_ONLY") == 0) {
+            } else if (OPENSSL_strcasecmp(p->data, "EXTRACT_ONLY") == 0) {
                 ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
-            } else if (strcasecmp(p->data, "EXPAND_ONLY") == 0) {
+            } else if (OPENSSL_strcasecmp(p->data, "EXPAND_ONLY") == 0) {
                 ctx->mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
             } else {
                 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
@@ -213,35 +271,33 @@ static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
                 return 0;
         }
     }
-    /* The info fields concatenate, so process them all */
-    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_INFO)) != NULL) {
-        ctx->info_len = 0;
-        for (; p != NULL; p = OSSL_PARAM_locate_const(p + 1,
-                                                      OSSL_KDF_PARAM_INFO)) {
-            const void *q = ctx->info + ctx->info_len;
-            size_t sz = 0;
-
-            if (p->data_size != 0
-                && p->data != NULL
-                && !OSSL_PARAM_get_octet_string(p, (void **)&q,
-                                                HKDF_MAXBUF - ctx->info_len,
-                                                &sz))
-                return 0;
-            ctx->info_len += sz;
-        }
-    }
+
+    return 1;
+}
+
+static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    KDF_HKDF *ctx = vctx;
+
+    if (params == NULL)
+        return 1;
+
+    if (!hkdf_common_set_ctx_params(ctx, params))
+        return 0;
+
+    if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO,
+                                            &ctx->info, &ctx->info_len,
+                                            HKDF_MAXINFO) == 0)
+        return 0;
+
     return 1;
 }
 
-static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(ossl_unused void *provctx)
+static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(ossl_unused void *ctx,
+                                                      ossl_unused void *provctx)
 {
     static const OSSL_PARAM known_settable_ctx_params[] = {
-        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0),
-        OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL),
-        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
-        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
-        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
-        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
+        HKDF_COMMON_SETTABLES,
         OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0),
         OSSL_PARAM_END
     };
@@ -253,22 +309,37 @@ static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
     KDF_HKDF *ctx = (KDF_HKDF *)vctx;
     OSSL_PARAM *p;
 
-    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
-        return OSSL_PARAM_set_size_t(p, kdf_hkdf_size(ctx));
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+        size_t sz = kdf_hkdf_size(ctx);
+
+        if (sz == 0)
+            return 0;
+        return OSSL_PARAM_set_size_t(p, sz);
+    }
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_INFO)) != NULL) {
+        if (ctx->info == NULL || ctx->info_len == 0) {
+            p->return_size = 0;
+            return 1;
+        }
+        return OSSL_PARAM_set_octet_string(p, ctx->info, ctx->info_len);
+    }
     return -2;
 }
 
-static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *provctx)
+static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx,
+                                                      ossl_unused void *provctx)
 {
     static const OSSL_PARAM known_gettable_ctx_params[] = {
         OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0),
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
 }
 
-const OSSL_DISPATCH kdf_hkdf_functions[] = {
+const OSSL_DISPATCH ossl_kdf_hkdf_functions[] = {
     { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
+    { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
     { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
     { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
     { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_hkdf_derive },
@@ -278,7 +349,7 @@ const OSSL_DISPATCH kdf_hkdf_functions[] = {
     { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
       (void(*)(void))kdf_hkdf_gettable_ctx_params },
     { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params },
-    { 0, NULL }
+    OSSL_DISPATCH_END
 };
 
 /*
@@ -306,7 +377,7 @@ const OSSL_DISPATCH kdf_hkdf_functions[] = {
  *   2.3.  Step 2: Expand
  *     HKDF-Expand(PRK, info, L) -> OKM
  */
-static int HKDF(const EVP_MD *evp_md,
+static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
                 const unsigned char *salt, size_t salt_len,
                 const unsigned char *ikm, size_t ikm_len,
                 const unsigned char *info, size_t info_len,
@@ -316,13 +387,14 @@ static int HKDF(const EVP_MD *evp_md,
     int ret, sz;
     size_t prk_len;
 
-    sz = EVP_MD_size(evp_md);
+    sz = EVP_MD_get_size(evp_md);
     if (sz < 0)
         return 0;
     prk_len = (size_t)sz;
 
     /* Step 1: HKDF-Extract(salt, IKM) -> PRK */
-    if (!HKDF_Extract(evp_md, salt, salt_len, ikm, ikm_len, prk, prk_len))
+    if (!HKDF_Extract(libctx, evp_md,
+                      salt, salt_len, ikm, ikm_len, prk, prk_len))
         return 0;
 
     /* Step 2: HKDF-Expand(PRK, info, L) -> OKM */
@@ -356,12 +428,12 @@ static int HKDF(const EVP_MD *evp_md,
  *
  *   PRK = HMAC-Hash(salt, IKM)
  */
-static int HKDF_Extract(const EVP_MD *evp_md,
+static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
                         const unsigned char *salt, size_t salt_len,
                         const unsigned char *ikm, size_t ikm_len,
                         unsigned char *prk, size_t prk_len)
 {
-    int sz = EVP_MD_size(evp_md);
+    int sz = EVP_MD_get_size(evp_md);
 
     if (sz < 0)
         return 0;
@@ -370,7 +442,10 @@ static int HKDF_Extract(const EVP_MD *evp_md,
         return 0;
     }
     /* calc: PRK = HMAC-Hash(salt, IKM) */
-    return HMAC(evp_md, salt, salt_len, ikm, ikm_len, prk, NULL) != NULL;
+    return
+        EVP_Q_mac(libctx, "HMAC", NULL, EVP_MD_get0_name(evp_md), NULL, salt,
+                  salt_len, ikm, ikm_len, prk, EVP_MD_get_size(evp_md), NULL)
+        != NULL;
 }
 
 /*
@@ -423,7 +498,7 @@ static int HKDF_Expand(const EVP_MD *evp_md,
     unsigned char prev[EVP_MAX_MD_SIZE];
     size_t done_len = 0, dig_len, n;
 
-    sz = EVP_MD_size(evp_md);
+    sz = EVP_MD_get_size(evp_md);
     if (sz <= 0)
         return 0;
     dig_len = (size_t)sz;
@@ -464,7 +539,7 @@ static int HKDF_Expand(const EVP_MD *evp_md,
         if (!HMAC_Final(hmac, prev, NULL))
             goto err;
 
-        copy_len = (done_len + dig_len > okm_len) ?
+        copy_len = (dig_len > okm_len - done_len) ?
                        okm_len - done_len :
                        dig_len;
 
@@ -479,3 +554,216 @@ static int HKDF_Expand(const EVP_MD *evp_md,
     HMAC_CTX_free(hmac);
     return ret;
 }
+
+/*
+ * TLS uses slight variations of the above and for FIPS validation purposes,
+ * they need to be present here.
+ * Refer to RFC 8446 section 7 for specific details.
+ */
+
+/*
+ * Given a |secret|; a |label| of length |labellen|; and |data| of length
+ * |datalen| (e.g. typically a hash of the handshake messages), derive a new
+ * secret |outlen| bytes long and store it in the location pointed to be |out|.
+ * The |data| value may be zero length. Returns 1 on success and 0 on failure.
+ */
+static int prov_tls13_hkdf_expand(const EVP_MD *md,
+                                  const unsigned char *key, size_t keylen,
+                                  const unsigned char *prefix, size_t prefixlen,
+                                  const unsigned char *label, size_t labellen,
+                                  const unsigned char *data, size_t datalen,
+                                  unsigned char *out, size_t outlen)
+{
+    size_t hkdflabellen;
+    unsigned char hkdflabel[HKDF_MAXBUF];
+    WPACKET pkt;
+
+    /*
+     * 2 bytes for length of derived secret + 1 byte for length of combined
+     * prefix and label + bytes for the label itself + 1 byte length of hash
+     * + bytes for the hash itself.  We've got the maximum the KDF can handle
+     * which should always be sufficient.
+     */
+    if (!WPACKET_init_static_len(&pkt, hkdflabel, sizeof(hkdflabel), 0)
+            || !WPACKET_put_bytes_u16(&pkt, outlen)
+            || !WPACKET_start_sub_packet_u8(&pkt)
+            || !WPACKET_memcpy(&pkt, prefix, prefixlen)
+            || !WPACKET_memcpy(&pkt, label, labellen)
+            || !WPACKET_close(&pkt)
+            || !WPACKET_sub_memcpy_u8(&pkt, data, (data == NULL) ? 0 : datalen)
+            || !WPACKET_get_total_written(&pkt, &hkdflabellen)
+            || !WPACKET_finish(&pkt)) {
+        WPACKET_cleanup(&pkt);
+        return 0;
+    }
+
+    return HKDF_Expand(md, key, keylen, hkdflabel, hkdflabellen,
+                       out, outlen);
+}
+
+static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx,
+                                           const EVP_MD *md,
+                                           const unsigned char *prevsecret,
+                                           size_t prevsecretlen,
+                                           const unsigned char *insecret,
+                                           size_t insecretlen,
+                                           const unsigned char *prefix,
+                                           size_t prefixlen,
+                                           const unsigned char *label,
+                                           size_t labellen,
+                                           unsigned char *out, size_t outlen)
+{
+    size_t mdlen;
+    int ret;
+    unsigned char preextractsec[EVP_MAX_MD_SIZE];
+    /* Always filled with zeros */
+    static const unsigned char default_zeros[EVP_MAX_MD_SIZE];
+
+    ret = EVP_MD_get_size(md);
+    /* Ensure cast to size_t is safe */
+    if (ret <= 0)
+        return 0;
+    mdlen = (size_t)ret;
+
+    if (insecret == NULL) {
+        insecret = default_zeros;
+        insecretlen = mdlen;
+    }
+    if (prevsecret == NULL) {
+        prevsecret = default_zeros;
+        prevsecretlen = 0;
+    } else {
+        EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+        unsigned char hash[EVP_MAX_MD_SIZE];
+
+        /* The pre-extract derive step uses a hash of no messages */
+        if (mctx == NULL
+                || EVP_DigestInit_ex(mctx, md, NULL) <= 0
+                || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) {
+            EVP_MD_CTX_free(mctx);
+            return 0;
+        }
+        EVP_MD_CTX_free(mctx);
+
+        /* Generate the pre-extract secret */
+        if (!prov_tls13_hkdf_expand(md, prevsecret, mdlen,
+                                    prefix, prefixlen, label, labellen,
+                                    hash, mdlen, preextractsec, mdlen))
+            return 0;
+        prevsecret = preextractsec;
+        prevsecretlen = mdlen;
+    }
+
+    ret = HKDF_Extract(libctx, md, prevsecret, prevsecretlen,
+                       insecret, insecretlen, out, outlen);
+
+    if (prevsecret == preextractsec)
+        OPENSSL_cleanse(preextractsec, mdlen);
+    return ret;
+}
+
+static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen,
+                             const OSSL_PARAM params[])
+{
+    KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+    const EVP_MD *md;
+
+    if (!ossl_prov_is_running() || !kdf_tls1_3_set_ctx_params(ctx, params))
+        return 0;
+
+    md = ossl_prov_digest_md(&ctx->digest);
+    if (md == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+        return 0;
+    }
+
+    switch (ctx->mode) {
+    default:
+        return 0;
+
+    case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
+        return prov_tls13_hkdf_generate_secret(PROV_LIBCTX_OF(ctx->provctx),
+                                               md,
+                                               ctx->salt, ctx->salt_len,
+                                               ctx->key, ctx->key_len,
+                                               ctx->prefix, ctx->prefix_len,
+                                               ctx->label, ctx->label_len,
+                                               key, keylen);
+
+    case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
+        return prov_tls13_hkdf_expand(md, ctx->key, ctx->key_len,
+                                      ctx->prefix, ctx->prefix_len,
+                                      ctx->label, ctx->label_len,
+                                      ctx->data, ctx->data_len,
+                                      key, keylen);
+    }
+}
+
+static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *p;
+    KDF_HKDF *ctx = vctx;
+
+    if (params == NULL)
+        return 1;
+
+    if (!hkdf_common_set_ctx_params(ctx, params))
+        return 0;
+
+    if (ctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+        return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PREFIX)) != NULL) {
+        OPENSSL_free(ctx->prefix);
+        ctx->prefix = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->prefix, 0,
+                                         &ctx->prefix_len))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_LABEL)) != NULL) {
+        OPENSSL_free(ctx->label);
+        ctx->label = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->label, 0,
+                                         &ctx->label_len))
+            return 0;
+    }
+
+    OPENSSL_clear_free(ctx->data, ctx->data_len);
+    ctx->data = NULL;
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DATA)) != NULL
+            && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->data, 0,
+                                            &ctx->data_len))
+        return 0;
+    return 1;
+}
+
+static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx,
+                                                        ossl_unused void *provctx)
+{
+    static const OSSL_PARAM known_settable_ctx_params[] = {
+        HKDF_COMMON_SETTABLES,
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_DATA, NULL, 0),
+        OSSL_PARAM_END
+    };
+    return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = {
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
+    { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
+    { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
+    { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
+    { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_3_derive },
+    { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_tls1_3_settable_ctx_params },
+    { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_set_ctx_params },
+    { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_hkdf_gettable_ctx_params },
+    { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params },
+    OSSL_DISPATCH_END
+};