From 2290280617183863eb15425b8925765966723725 Mon Sep 17 00:00:00 2001
From: Clemens Lang <cllang@redhat.com>
Date: Thu, 11 Aug 2022 09:27:12 +0200
Subject: KDF: Add FIPS indicators

FIPS requires a number of restrictions on the parameters of the various
key derivation functions implemented in OpenSSL. The KDFs that use
digest algorithms usually should not allow SHAKE (due to FIPS 140-3 IG
C.C). Additionally, some application-specific KDFs have further
restrictions defined in SP 800-135r1.

Generally, all KDFs shall use a key-derivation key length of at least
112 bits due to SP 800-131Ar2 section 8. Additionally any use of a KDF
to generate and output length of less than 112 bits will also set the
indicator to unapproved.

Add explicit indicators to all KDFs usable in FIPS mode except for
PBKDF2 (which has its specific FIPS limits already implemented). The
indicator can be queried using EVP_KDF_CTX_get_params() after setting
the required parameters and keys for the KDF.

Our FIPS provider implements SHA1, SHA2 (both -256 and -512, and the
truncated variants -224 and -384) and SHA3 (-256 and -512, and the
truncated versions -224 and -384), as well as SHAKE-128 and -256.

The SHAKE functions are generally not allowed in KDFs. For the rest, the
support matrix is:

 KDF         | SHA-1 | SHA-2 | SHA-2 truncated  | SHA-3 | SHA-3 truncated
==========================================================================
KBKDF        |   x   |   x   |         x        |   x   |     x
HKDF         |   x   |   x   |         x        |   x   |     x
TLS1PRF      |       | SHA-{256,384,512} only   |       |
SSHKDF       |   x   |   x   |         x        |       |
SSKDF        |   x   |   x   |         x        |   x   |     x
X9.63KDF     |       |   x   |         x        |   x   |     x
X9.42-ASN1   |   x   |   x   |         x        |   x   |     x
TLS1.3PRF    |       | SHA-{256,384} only       |       |

Signed-off-by: Clemens Lang <cllang@redhat.com>
Resolves: rhbz#2160733 rhbz#2164763
Related: rhbz#2114772 rhbz#2141695
---
 include/crypto/evp.h                      |   7 ++
 include/openssl/kdf.h                     |   4 +
 providers/implementations/kdfs/hkdf.c     | 100 +++++++++++++++++++++-
 providers/implementations/kdfs/kbkdf.c    |  82 ++++++++++++++++--
 providers/implementations/kdfs/sshkdf.c   |  75 +++++++++++++++-
 providers/implementations/kdfs/sskdf.c    | 100 +++++++++++++++++++++-
 providers/implementations/kdfs/tls1_prf.c |  74 +++++++++++++++-
 providers/implementations/kdfs/x942kdf.c  |  66 +++++++++++++-
 util/perl/OpenSSL/paramnames.pm           |   1 +
 9 files changed, 487 insertions(+), 22 deletions(-)

diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index e70d8e9e84..76fb990de4 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -219,6 +219,13 @@ struct evp_mac_st {
     OSSL_FUNC_mac_set_ctx_params_fn *set_ctx_params;
 };
 
+#ifdef FIPS_MODULE
+/* According to NIST Special Publication 800-131Ar2, Section 8: Deriving
+ * Additional Keys from a Cryptographic Key, "[t]he length of the
+ * key-derivation key [i.e., the input key] shall be at least 112 bits". */
+# define EVP_KDF_FIPS_MIN_KEY_LEN (112 / 8)
+#endif
+
 struct evp_kdf_st {
     OSSL_PROVIDER *prov;
     int name_id;
diff --git a/include/openssl/kdf.h b/include/openssl/kdf.h
index 0983230a48..86171635ea 100644
--- a/include/openssl/kdf.h
+++ b/include/openssl/kdf.h
@@ -63,6 +63,10 @@ int EVP_KDF_names_do_all(const EVP_KDF *kdf,
 # define EVP_KDF_HKDF_MODE_EXTRACT_ONLY        1
 # define EVP_KDF_HKDF_MODE_EXPAND_ONLY         2
 
+# define EVP_KDF_REDHAT_FIPS_INDICATOR_UNDETERMINED 0
+# define EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED     1
+# define EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED 2
+
 #define EVP_KDF_SSHKDF_TYPE_INITIAL_IV_CLI_TO_SRV     65
 #define EVP_KDF_SSHKDF_TYPE_INITIAL_IV_SRV_TO_CLI     66
 #define EVP_KDF_SSHKDF_TYPE_ENCRYPTION_KEY_CLI_TO_SRV 67
diff --git a/providers/implementations/kdfs/hkdf.c b/providers/implementations/kdfs/hkdf.c
index dfa7786bde..f01e40ff5a 100644
--- a/providers/implementations/kdfs/hkdf.c
+++ b/providers/implementations/kdfs/hkdf.c
@@ -42,6 +42,7 @@ 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_newctx_fn kdf_tls1_3_new;
 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;
@@ -85,6 +86,10 @@ typedef struct {
     size_t data_len;
     unsigned char *info;
     size_t info_len;
+    int is_tls13;
+#ifdef FIPS_MODULE
+    int fips_indicator;
+#endif /* defined(FIPS_MODULE) */
 } KDF_HKDF;
 
 static void *kdf_hkdf_new(void *provctx)
@@ -170,6 +175,11 @@ static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen,
         return 0;
     }
 
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
+
     switch (ctx->mode) {
     case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND:
     default:
@@ -318,22 +318,85 @@ static int kdf_hkdf_get_ctx_params(void
 {
     KDF_HKDF *ctx = (KDF_HKDF *)vctx;
     OSSL_PARAM *p;
+    int any_valid = 0; /* set to 1 when at least one parameter was valid */
 
     if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
         size_t sz = kdf_hkdf_size(ctx);
 
+        any_valid = 1;
         if (sz == 0)
             return 0;
         return OSSL_PARAM_set_size_t(p, sz);
     }
     if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_INFO)) != NULL) {
+        any_valid = 1;
         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;
+#ifdef FIPS_MODULE
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR))
+            != NULL) {
+        int fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED;
+        const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+
+        any_valid = 1;
+
+        /* According to NIST Special Publication 800-131Ar2, Section 8:
+         * Deriving Additional Keys from a Cryptographic Key, "[t]he length of
+         * the key-derivation key [i.e., the input key] shall be at least 112
+         * bits". */
+        if (ctx->key_len < EVP_KDF_FIPS_MIN_KEY_LEN)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Verification Program, Section D.B and NIST Special Publication
+         * 800-131Ar2, Section 1.2.2 say that any algorithm at a security
+         * strength < 112 bits is legacy use only, so all derived keys should
+         * be longer than that. If a derived key has ever been shorter than
+         * that, ctx->output_keyelen_indicator will be NOT_APPROVED, and we
+         * should also set the returned FIPS indicator to unapproved. */
+        if (ctx->fips_indicator == EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        if (ctx->is_tls13) {
+            if (md != NULL
+                    && !EVP_MD_is_a(md, "SHA2-256")
+                    && !EVP_MD_is_a(md, "SHA2-384")) {
+                /* Implementation Guidance for FIPS 140-3 and the Cryptographic
+                 * Module Validation Program, Section 2.4.B, (5): "The TLS 1.3
+                 * key derivation function documented in Section 7.1 of RFC
+                 * 8446. This is considered an approved CVL because the
+                 * underlying functions performed within the TLS 1.3 KDF map to
+                 * NIST approved standards, namely: SP 800-133rev2 (Section 6.3
+                 * Option #3), SP 800-56Crev2, and SP 800-108."
+                 *
+                 * RFC 8446 appendix B.4 only lists SHA-256 and SHA-384. */
+                fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+            }
+        } else {
+            if (md != NULL
+                    && (EVP_MD_is_a(md, "SHAKE-128") ||
+                        EVP_MD_is_a(md, "SHAKE-256"))) {
+                /* HKDF is a SP 800-56Cr2 TwoStep KDF, for which all SHA-1,
+                 * SHA-2 and SHA-3 are approved. SHAKE is not approved, because
+                 * of FIPS 140-3 IG, section C.C: "The SHAKE128 and SHAKE256
+                 * extendable-output functions may only be used as the
+                 * standalone algorithms." */
+                fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+            }
+        }
+        if (!OSSL_PARAM_set_int(p, fips_indicator))
+            return 0;
+    }
+#endif /* defined(FIPS_MODULE) */
+
+    if (!any_valid)
+        return -2;
+
+    return 1;
 }
 
 static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx,
@@ -348,6 +421,9 @@ static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx,
     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),
+#ifdef FIPS_MODULE
+        OSSL_PARAM_int(OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR, NULL),
+#endif /* defined(FIPS_MODULE) */
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
@@ -677,6 +753,17 @@ static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx,
     return ret;
 }
 
+static void *kdf_tls1_3_new(void *provctx)
+{
+    KDF_HKDF *hkdf = kdf_hkdf_new(provctx);
+
+    if (hkdf != NULL)
+        hkdf->is_tls13 = 1;
+
+    return hkdf;
+}
+
+
 static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen,
                              const OSSL_PARAM params[])
 {
@@ -692,6 +779,11 @@ static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen,
         return 0;
     }
 
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
+
     switch (ctx->mode) {
     default:
         return 0;
@@ -769,7 +861,7 @@ static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx,
 }
 
 const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = {
-    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_tls1_3_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 },
diff --git a/providers/implementations/kdfs/kbkdf.c b/providers/implementations/kdfs/kbkdf.c
index a542f84dfa..6b6dfb94ac 100644
--- a/providers/implementations/kdfs/kbkdf.c
+++ b/providers/implementations/kdfs/kbkdf.c
@@ -59,6 +59,9 @@ typedef struct {
     kbkdf_mode mode;
     EVP_MAC_CTX *ctx_init;
 
+    /* HMAC digest algorithm, if any; used to compute FIPS indicator */
+    PROV_DIGEST digest;
+
     /* Names are lowercased versions of those found in SP800-108. */
     int r;
     unsigned char *ki;
@@ -73,6 +76,9 @@ typedef struct {
     int use_l;
     int is_kmac;
     int use_separator;
+#ifdef FIPS_MODULE
+    int fips_indicator;
+#endif /* defined(FIPS_MODULE) */
 } KBKDF;
 
 /* Definitions needed for typechecking. */
@@ -138,6 +144,7 @@ static void kbkdf_reset(void *vctx)
     void *provctx = ctx->provctx;
 
     EVP_MAC_CTX_free(ctx->ctx_init);
+    ossl_prov_digest_reset(&ctx->digest);
     OPENSSL_clear_free(ctx->context, ctx->context_len);
     OPENSSL_clear_free(ctx->label, ctx->label_len);
     OPENSSL_clear_free(ctx->ki, ctx->ki_len);
@@ -240,6 +247,11 @@ static int kbkdf_derive(void *vctx, unsigned char *key, size_t keylen,
         goto done;
     }
 
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
+
     h = EVP_MAC_CTX_get_mac_size(ctx->ctx_init);
     if (h == 0)
         goto done;
@@ -297,6 +309,9 @@ static int kbkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
         }
     }
 
+    if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
+        return 0;
+
     p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE);
     if (p != NULL
         && OPENSSL_strncasecmp("counter", p->data, p->data_size) == 0) {
@@ -363,20 +378,77 @@ static const OSSL_PARAM *kbkdf_settable_ctx_params(ossl_unused void *ctx,
 static int kbkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
 {
     OSSL_PARAM *p;
+    int any_valid = 0; /* set to 1 when at least one parameter was valid */
 
     p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE);
-    if (p == NULL)
+    if (p != NULL) {
+        any_valid = 1;
+
+        /* KBKDF can produce results as large as you like. */
+        if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
+            return 0;
+    }
+
+#ifdef FIPS_MODULE
+    p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR);
+    if (p != NULL) {
+        KBKDF *ctx = (KBKDF *)vctx;
+        int fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED;
+
+        any_valid = 1;
+
+        /* According to NIST Special Publication 800-131Ar2, Section 8:
+         * Deriving Additional Keys from a Cryptographic Key, "[t]he length of
+         * the key-derivation key [i.e., the input key] shall be at least 112
+         * bits". */
+        if (ctx->ki_len < EVP_KDF_FIPS_MIN_KEY_LEN)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Verification Program, Section D.B and NIST Special Publication
+         * 800-131Ar2, Section 1.2.2 say that any algorithm at a security
+         * strength < 112 bits is legacy use only, so all derived keys should
+         * be longer than that. If a derived key has ever been shorter than
+         * that, ctx->output_keyelen_indicator will be NOT_APPROVED, and we
+         * should also set the returned FIPS indicator to unapproved. */
+        if (ctx->fips_indicator == EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Validation Program, Section C.C: "The SHAKE128 and SHAKE256
+         * extendable-output functions may only be used as the standalone
+         * algorithms." Note that the digest is only used when the MAC
+         * algorithm is HMAC. */
+        if (ctx->ctx_init != NULL
+                && EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init), OSSL_MAC_NAME_HMAC)) {
+            const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+            if (md != NULL
+                    && (EVP_MD_is_a(md, "SHAKE-128") || EVP_MD_is_a(md, "SHAKE-256"))) {
+                fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+            }
+        }
+
+        if (!OSSL_PARAM_set_int(p, fips_indicator))
+            return 0;
+    }
+#endif
+
+    if (!any_valid)
         return -2;
 
-    /* KBKDF can produce results as large as you like. */
-    return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+    return 1;
 }
 
 static const OSSL_PARAM *kbkdf_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_END };
+    static const OSSL_PARAM known_gettable_ctx_params[] = {
+        OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+#ifdef FIPS_MODULE
+        OSSL_PARAM_int(OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR, NULL),
+#endif /* defined(FIPS_MODULE) */
+        OSSL_PARAM_END
+    };
     return known_gettable_ctx_params;
 }
 
diff --git a/providers/implementations/kdfs/sshkdf.c b/providers/implementations/kdfs/sshkdf.c
index c592ba72f1..4a52b38266 100644
--- a/providers/implementations/kdfs/sshkdf.c
+++ b/providers/implementations/kdfs/sshkdf.c
@@ -48,6 +48,9 @@ typedef struct {
     char type; /* X */
     unsigned char *session_id;
     size_t session_id_len;
+#ifdef FIPS_MODULE
+    int fips_indicator;
+#endif /* defined(FIPS_MODULE) */
 } KDF_SSHKDF;
 
 static void *kdf_sshkdf_new(void *provctx)
@@ -126,6 +129,12 @@ static int kdf_sshkdf_derive(void *vctx, unsigned char *key, size_t keylen,
         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_TYPE);
         return 0;
     }
+
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
+
     return SSHKDF(md, ctx->key, ctx->key_len,
                   ctx->xcghash, ctx->xcghash_len,
                   ctx->session_id, ctx->session_id_len,
@@ -194,10 +203,67 @@ static const OSSL_PARAM *kdf_sshkdf_settable_ctx_params(ossl_unused void *ctx,
 static int kdf_sshkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
 {
     OSSL_PARAM *p;
+    int any_valid = 0; /* set to 1 when at least one parameter was valid */
 
-    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
-        return OSSL_PARAM_set_size_t(p, SIZE_MAX);
-    return -2;
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+        any_valid = 1;
+
+        if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
+            return 0;
+    }
+
+#ifdef FIPS_MODULE
+    p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR);
+    if (p != NULL) {
+        KDF_SSHKDF *ctx = vctx;
+        int fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED;
+
+        any_valid = 1;
+
+        /* According to NIST Special Publication 800-131Ar2, Section 8:
+         * Deriving Additional Keys from a Cryptographic Key, "[t]he length of
+         * the key-derivation key [i.e., the input key] shall be at least 112
+         * bits". */
+        if (ctx->key_len < EVP_KDF_FIPS_MIN_KEY_LEN)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Verification Program, Section D.B and NIST Special Publication
+         * 800-131Ar2, Section 1.2.2 say that any algorithm at a security
+         * strength < 112 bits is legacy use only, so all derived keys should
+         * be longer than that. If a derived key has ever been shorter than
+         * that, ctx->output_keyelen_indicator will be NOT_APPROVED, and we
+         * should also set the returned FIPS indicator to unapproved. */
+        if (ctx->fips_indicator == EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Validation Program, Section C.C: "The SHAKE128 and SHAKE256
+         * extendable-output functions may only be used as the standalone
+         * algorithms."
+         *
+         * Additionally, SP 800-135r1 section 5.2 specifies that the hash
+         * function used in SSHKDF "is one of the hash functions specified in
+         * FIPS 180-3.", which rules out SHA-3 and truncated variants of SHA-2.
+         * */
+        if (ctx->digest.md != NULL
+            && !EVP_MD_is_a(ctx->digest.md, "SHA-1")
+            && !EVP_MD_is_a(ctx->digest.md, "SHA2-224")
+            && !EVP_MD_is_a(ctx->digest.md, "SHA2-256")
+            && !EVP_MD_is_a(ctx->digest.md, "SHA2-384")
+            && !EVP_MD_is_a(ctx->digest.md, "SHA2-512")) {
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+        }
+
+        if (!OSSL_PARAM_set_int(p, fips_indicator))
+            return 0;
+    }
+#endif
+
+    if (!any_valid)
+        return -2;
+
+    return 1;
 }
 
 static const OSSL_PARAM *kdf_sshkdf_gettable_ctx_params(ossl_unused void *ctx,
@@ -205,6 +271,9 @@ static const OSSL_PARAM *kdf_sshkdf_gettable_ctx_params(ossl_unused void *ctx,
 {
     static const OSSL_PARAM known_gettable_ctx_params[] = {
         OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+#ifdef FIPS_MODULE
+        OSSL_PARAM_int(OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR, NULL),
+#endif /* defined(FIPS_MODULE) */
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
diff --git a/providers/implementations/kdfs/sskdf.c b/providers/implementations/kdfs/sskdf.c
index eb54972e1c..23865cd70f 100644
--- a/providers/implementations/kdfs/sskdf.c
+++ b/providers/implementations/kdfs/sskdf.c
@@ -64,6 +64,10 @@ typedef struct {
     size_t salt_len;
     size_t out_len; /* optional KMAC parameter */
     int is_kmac;
+    int is_x963kdf;
+#ifdef FIPS_MODULE
+    int fips_indicator;
+#endif /* defined(FIPS_MODULE) */
 } KDF_SSKDF;
 
 #define SSKDF_MAX_INLEN (1<<30)
@@ -73,6 +77,7 @@ typedef struct {
 static const unsigned char kmac_custom_str[] = { 0x4B, 0x44, 0x46 };
 
 static OSSL_FUNC_kdf_newctx_fn sskdf_new;
+static OSSL_FUNC_kdf_newctx_fn x963kdf_new;
 static OSSL_FUNC_kdf_dupctx_fn sskdf_dup;
 static OSSL_FUNC_kdf_freectx_fn sskdf_free;
 static OSSL_FUNC_kdf_reset_fn sskdf_reset;
@@ -296,6 +301,16 @@ static void *sskdf_new(void *provctx)
     return ctx;
 }
 
+static void *x963kdf_new(void *provctx)
+{
+    KDF_SSKDF *ctx = sskdf_new(provctx);
+
+    if (ctx)
+        ctx->is_x963kdf = 1;
+
+    return ctx;
+}
+
 static void sskdf_reset(void *vctx)
 {
     KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
@@ -361,6 +376,11 @@ static int sskdf_derive(void *vctx, unsigned char *key, size_t keylen,
     }
     md = ossl_prov_digest_md(&ctx->digest);
 
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
+
     if (ctx->macctx != NULL) {
         /* H(x) = KMAC or H(x) = HMAC */
         int ret;
@@ -442,6 +462,11 @@ static int x963kdf_derive(void *vctx, unsigned char *key, size_t keylen,
         return 0;
     }
 
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
+
     return SSKDF_hash_kdm(md, ctx->secret, ctx->secret_len,
                           ctx->info, ctx->info_len, 1, key, keylen);
 }
@@ -514,10 +539,74 @@ static int sskdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
 {
     KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
     OSSL_PARAM *p;
+    int any_valid = 0; /* set to 1 when at least one parameter was valid */
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+        any_valid = 1;
+
+        if (!OSSL_PARAM_set_size_t(p, sskdf_size(ctx)))
+            return 0;
+    }
 
-    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
-        return OSSL_PARAM_set_size_t(p, sskdf_size(ctx));
-    return -2;
+#ifdef FIPS_MODULE
+    p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR);
+    if (p != NULL) {
+        int fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED;
+
+        any_valid = 1;
+
+        /* According to NIST Special Publication 800-131Ar2, Section 8:
+         * Deriving Additional Keys from a Cryptographic Key, "[t]he length of
+         * the key-derivation key [i.e., the input key] shall be at least 112
+         * bits". */
+        if (ctx->secret_len < EVP_KDF_FIPS_MIN_KEY_LEN)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Verification Program, Section D.B and NIST Special Publication
+         * 800-131Ar2, Section 1.2.2 say that any algorithm at a security
+         * strength < 112 bits is legacy use only, so all derived keys should
+         * be longer than that. If a derived key has ever been shorter than
+         * that, ctx->output_keyelen_indicator will be NOT_APPROVED, and we
+         * should also set the returned FIPS indicator to unapproved. */
+        if (ctx->fips_indicator == EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Validation Program, Section C.C: "The SHAKE128 and SHAKE256
+         * extendable-output functions may only be used as the standalone
+         * algorithms." */
+        if (ctx->macctx == NULL
+                || (ctx->macctx != NULL &&
+                    EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->macctx), OSSL_MAC_NAME_HMAC))) {
+            if (ctx->digest.md != NULL
+                && (EVP_MD_is_a(ctx->digest.md, "SHAKE-128") ||
+                    EVP_MD_is_a(ctx->digest.md, "SHAKE-256"))) {
+                fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+            }
+
+            /* Table H-3 in ANS X9.63-2001 says that 160-bit hash functions
+             * should only be used for 80-bit key agreement, but FIPS 140-3
+             * requires a security strength of 112 bits, so SHA-1 cannot be
+             * used with X9.63. See the discussion in
+             * https://github.com/usnistgov/ACVP/issues/1403#issuecomment-1435300395.
+             */
+            if (ctx->is_x963kdf
+                    && ctx->digest.md != NULL
+                    && EVP_MD_is_a(ctx->digest.md, "SHA-1")) {
+                fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+            }
+        }
+
+        if (!OSSL_PARAM_set_int(p, fips_indicator))
+            return 0;
+    }
+#endif
+
+    if (!any_valid)
+        return -2;
+
+    return 1;
 }
 
 static const OSSL_PARAM *sskdf_gettable_ctx_params(ossl_unused void *ctx,
@@ -525,6 +614,9 @@ static const OSSL_PARAM *sskdf_gettable_ctx_params(ossl_unused void *ctx,
 {
     static const OSSL_PARAM known_gettable_ctx_params[] = {
         OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+#ifdef FIPS_MODULE
+        OSSL_PARAM_int(OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR, 0),
+#endif /* defined(FIPS_MODULE) */
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
@@ -545,7 +637,7 @@ const OSSL_DISPATCH ossl_kdf_sskdf_functions[] = {
 };
 
 const OSSL_DISPATCH ossl_kdf_x963_kdf_functions[] = {
-    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))sskdf_new },
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))x963kdf_new },
     { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))sskdf_dup },
     { OSSL_FUNC_KDF_FREECTX, (void(*)(void))sskdf_free },
     { OSSL_FUNC_KDF_RESET, (void(*)(void))sskdf_reset },
diff --git a/providers/implementations/kdfs/tls1_prf.c b/providers/implementations/kdfs/tls1_prf.c
index a4d64b9352..f6782a6ca2 100644
--- a/providers/implementations/kdfs/tls1_prf.c
+++ b/providers/implementations/kdfs/tls1_prf.c
@@ -93,6 +93,13 @@ typedef struct {
     /* Buffer of concatenated seed data */
     unsigned char seed[TLS1_PRF_MAXBUF];
     size_t seedlen;
+
+    /* MAC digest algorithm; used to compute FIPS indicator */
+    PROV_DIGEST digest;
+
+#ifdef FIPS_MODULE
+    int fips_indicator;
+#endif /* defined(FIPS_MODULE) */
 } TLS1_PRF;
 
 static void *kdf_tls1_prf_new(void *provctx)
@@ -129,6 +136,7 @@ static void kdf_tls1_prf_reset(void *vctx)
     EVP_MAC_CTX_free(ctx->P_sha1);
     OPENSSL_clear_free(ctx->sec, ctx->seclen);
     OPENSSL_cleanse(ctx->seed, ctx->seedlen);
+    ossl_prov_digest_reset(&ctx->digest);
     memset(ctx, 0, sizeof(*ctx));
     ctx->provctx = provctx;
 }
@@ -157,6 +165,10 @@ static int kdf_tls1_prf_derive(void *vctx, unsigned char *key, size_t keylen,
         ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
         return 0;
     }
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
 
     /*
      * The seed buffer is prepended with a label.
@@ -191,6 +203,9 @@ static int kdf_tls1_prf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
         }
     }
 
+    if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
+        return 0;
+
     if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET)) != NULL) {
         OPENSSL_clear_free(ctx->sec, ctx->seclen);
         ctx->sec = NULL;
@@ -232,10 +247,60 @@ static const OSSL_PARAM *kdf_tls1_prf_settable_ctx_params(
 static int kdf_tls1_prf_get_ctx_params(void *vctx, OSSL_PARAM params[])
 {
     OSSL_PARAM *p;
+#ifdef FIPS_MODULE
+    TLS1_PRF *ctx = vctx;
+#endif /* defined(FIPS_MODULE) */
+    int any_valid = 0; /* set to 1 when at least one parameter was valid */
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+        any_valid = 1;
+
+        if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
+            return 0;
+    }
+
+#ifdef FIPS_MODULE
+    p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR);
+    if (p != NULL) {
+        int fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED;
+
+        any_valid = 1;
+
+        /* According to NIST Special Publication 800-131Ar2, Section 8:
+         * Deriving Additional Keys from a Cryptographic Key, "[t]he length of
+         * the key-derivation key [i.e., the input key] shall be at least 112
+         * bits". */
+        if (ctx->seclen < EVP_KDF_FIPS_MIN_KEY_LEN)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Verification Program, Section D.B and NIST Special Publication
+         * 800-131Ar2, Section 1.2.2 say that any algorithm at a security
+         * strength < 112 bits is legacy use only, so all derived keys should
+         * be longer than that. If a derived key has ever been shorter than
+         * that, ctx->output_keyelen_indicator will be NOT_APPROVED, and we
+         * should also set the returned FIPS indicator to unapproved. */
+        if (ctx->fips_indicator == EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* SP 800-135r1 section 4.2.2 says TLS 1.2 KDF is approved when "(3)
+         * P_HASH uses either SHA-256, SHA-384 or SHA-512." */
+        if (ctx->digest.md != NULL
+                && !EVP_MD_is_a(ctx->digest.md, "SHA2-256")
+                && !EVP_MD_is_a(ctx->digest.md, "SHA2-384")
+                && !EVP_MD_is_a(ctx->digest.md, "SHA2-512")) {
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+        }
+
+        if (!OSSL_PARAM_set_int(p, fips_indicator))
+            return 0;
+    }
+#endif
 
-    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
-        return OSSL_PARAM_set_size_t(p, SIZE_MAX);
-    return -2;
+    if (!any_valid)
+        return -2;
+
+    return 1;
 }
 
 static const OSSL_PARAM *kdf_tls1_prf_gettable_ctx_params(
@@ -243,6 +308,9 @@ static const OSSL_PARAM *kdf_tls1_prf_gettable_ctx_params(
 {
     static const OSSL_PARAM known_gettable_ctx_params[] = {
         OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+#ifdef FIPS_MODULE
+        OSSL_PARAM_int(OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR, 0),
+#endif /* defined(FIPS_MODULE) */
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
diff --git a/providers/implementations/kdfs/x942kdf.c b/providers/implementations/kdfs/x942kdf.c
index b1bc6f7e1b..8173fc2cc7 100644
--- a/providers/implementations/kdfs/x942kdf.c
+++ b/providers/implementations/kdfs/x942kdf.c
@@ -13,11 +13,13 @@
 #include <openssl/core_dispatch.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/kdf.h>
 #include <openssl/params.h>
 #include <openssl/proverr.h>
 #include "internal/packet.h"
 #include "internal/der.h"
 #include "internal/nelem.h"
+#include "crypto/evp.h"
 #include "prov/provider_ctx.h"
 #include "prov/providercommon.h"
 #include "prov/implementations.h"
@@ -47,6 +50,9 @@ typedef struct {
     const unsigned char *cek_oid;
     size_t cek_oid_len;
     int use_keybits;
+#ifdef FIPS_MODULE
+    int fips_indicator;
+#endif /* defined(FIPS_MODULE) */
 } KDF_X942;
 
 /*
@@ -460,6 +466,10 @@ static int x942kdf_derive(void *vctx, unsigned char *key, size_t keylen,
         ERR_raise(ERR_LIB_PROV, PROV_R_BAD_ENCODING);
         return 0;
     }
+#ifdef FIPS_MODULE
+    if (keylen < EVP_KDF_FIPS_MIN_KEY_LEN)
+        ctx->fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+#endif /* defined(FIPS_MODULE) */
     ret = x942kdf_hash_kdm(md, ctx->secret, ctx->secret_len,
                            der, der_len, ctr, key, keylen);
     OPENSSL_free(der);
@@ -563,10 +573,58 @@ static int x942kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
 {
     KDF_X942 *ctx = (KDF_X942 *)vctx;
     OSSL_PARAM *p;
+    int any_valid = 0; /* set to 1 when at least one parameter was valid */
 
-    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
-        return OSSL_PARAM_set_size_t(p, x942kdf_size(ctx));
-    return -2;
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+        any_valid = 1;
+
+        if (!OSSL_PARAM_set_size_t(p, x942kdf_size(ctx)))
+            return 0;
+    }
+
+#ifdef FIPS_MODULE
+    p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR);
+    if (p != NULL) {
+        int fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_APPROVED;
+
+        any_valid = 1;
+
+        /* According to NIST Special Publication 800-131Ar2, Section 8:
+         * Deriving Additional Keys from a Cryptographic Key, "[t]he length of
+         * the key-derivation key [i.e., the input key] shall be at least 112
+         * bits". */
+        if (ctx->secret_len < EVP_KDF_FIPS_MIN_KEY_LEN)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Verification Program, Section D.B and NIST Special Publication
+         * 800-131Ar2, Section 1.2.2 say that any algorithm at a security
+         * strength < 112 bits is legacy use only, so all derived keys should
+         * be longer than that. If a derived key has ever been shorter than
+         * that, ctx->output_keyelen_indicator will be NOT_APPROVED, and we
+         * should also set the returned FIPS indicator to unapproved. */
+        if (ctx->fips_indicator == EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED)
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+
+        /* Implementation Guidance for FIPS 140-3 and the Cryptographic Module
+         * Validation Program, Section C.C: "The SHAKE128 and SHAKE256
+         * extendable-output functions may only be used as the standalone
+         * algorithms." */
+        if (ctx->digest.md != NULL
+                && (EVP_MD_is_a(ctx->digest.md, "SHAKE-128") ||
+                    EVP_MD_is_a(ctx->digest.md, "SHAKE-256"))) {
+            fips_indicator = EVP_KDF_REDHAT_FIPS_INDICATOR_NOT_APPROVED;
+        }
+
+        if (!OSSL_PARAM_set_int(p, fips_indicator))
+            return 0;
+    }
+#endif
+
+    if (!any_valid)
+        return -2;
+
+    return 1;
 }
 
 static const OSSL_PARAM *x942kdf_gettable_ctx_params(ossl_unused void *ctx,
@@ -574,6 +632,9 @@ static const OSSL_PARAM *x942kdf_gettable_ctx_params(ossl_unused void *ctx,
 {
     static const OSSL_PARAM known_gettable_ctx_params[] = {
         OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+#ifdef FIPS_MODULE
+        OSSL_PARAM_int(OSSL_KDF_PARAM_REDHAT_FIPS_INDICATOR, 0),
+#endif /* defined(FIPS_MODULE) */
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm
index 70f7c50fe4..6618122417 100644
--- a/util/perl/OpenSSL/paramnames.pm
+++ b/util/perl/OpenSSL/paramnames.pm
@@ -183,6 +183,7 @@ my %params = (
     'KDF_PARAM_X942_SUPP_PUBINFO' =>    "supp-pubinfo",
     'KDF_PARAM_X942_SUPP_PRIVINFO' =>   "supp-privinfo",
     'KDF_PARAM_X942_USE_KEYBITS' =>     "use-keybits",
+    'KDF_PARAM_REDHAT_FIPS_INDICATOR' =>     "redhat-fips-indicator",
     'KDF_PARAM_HMACDRBG_ENTROPY' =>     "entropy",
     'KDF_PARAM_HMACDRBG_NONCE' =>       "nonce",
     'KDF_PARAM_THREADS' =>        "threads",                # uint32_t
-- 
2.39.2

