Commit 53fecfc80b for openssl.org

commit 53fecfc80bfe8fbef5923f4e84255f07695eb92f
Author: Simo Sorce <simo@redhat.com>
Date:   Tue Dec 9 14:29:43 2025 -0500

    Refactor FIPS integrity check to use KAT framework

    The FIPS module integrity check (HMAC-SHA256) is refactored to use the
    generic Known Answer Test (KAT) framework instead of a standalone
    function.

    - Remove `integrity_self_test` and use `ST_ID_MAC_HMAC` with
      `SELF_TEST_kats_single`.
    - Add `self_test_mac` to `self_test_kats.c` to support MAC tests.
    - Move HMAC test data to `self_test_data.c`.
    - Rename the self-test type from "KAT_Integrity" to "KAT_Mac".
    - Ensure on-demand tests reset state so they can be repeated.

    Signed-off-by: Simo Sorce <simo@redhat.com>

    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    (Merged from https://github.com/openssl/openssl/pull/29222)

diff --git a/doc/man7/OSSL_PROVIDER-FIPS.pod b/doc/man7/OSSL_PROVIDER-FIPS.pod
index befc9ad3c4..1e799d4c67 100644
--- a/doc/man7/OSSL_PROVIDER-FIPS.pod
+++ b/doc/man7/OSSL_PROVIDER-FIPS.pod
@@ -327,10 +327,11 @@ Uses HMAC SHA256 on the module file to validate that the module has not been
 modified. The integrity value is compared to a value written to a configuration
 file during installation.

-=item "KAT_Integrity" (B<OSSL_SELF_TEST_TYPE_KAT_INTEGRITY>)
+=item "KAT_Mac" (B<OSSL_SELF_TEST_TYPE_KAT_MAC>)

 Used during the Module Integrity test to perform a known answer test on
-HMAC SHA256 prior to using it.
+HMAC SHA256 prior to using it. In pre-4.0 versions
+B<OSSL_SELF_TEST_TYPE_KAT_INTEGRITY> ("KAT_Integrity") was used for this.

 =item "KAT_Cipher" (B<OSSL_SELF_TEST_TYPE_KAT_CIPHER>)

diff --git a/include/internal/fips.h b/include/internal/fips.h
index 67b5f7d151..c62865ebd9 100644
--- a/include/internal/fips.h
+++ b/include/internal/fips.h
@@ -108,6 +108,7 @@ typedef enum {
     ST_ID_ASYM_CIPHER_RSA_ENC,
     ST_ID_ASYM_CIPHER_RSA_DEC,
     ST_ID_ASYM_CIPHER_RSA_DEC_CRT,
+    ST_ID_MAC_HMAC,
     ST_ID_MAX
 } self_test_id_t;

diff --git a/include/openssl/self_test.h b/include/openssl/self_test.h
index c5e4be955e..f879370ae1 100644
--- a/include/openssl/self_test.h
+++ b/include/openssl/self_test.h
@@ -38,6 +38,7 @@ extern "C" {
 #define OSSL_SELF_TEST_TYPE_KAT_ASYM_KEYGEN "KAT_AsymmetricKeyGeneration"
 #define OSSL_SELF_TEST_TYPE_KAT_KEM "KAT_KEM"
 #define OSSL_SELF_TEST_TYPE_KAT_DIGEST "KAT_Digest"
+#define OSSL_SELF_TEST_TYPE_KAT_MAC "KAT_Mac"
 #define OSSL_SELF_TEST_TYPE_KAT_SIGNATURE "KAT_Signature"
 #define OSSL_SELF_TEST_TYPE_PCT_SIGNATURE "PCT_Signature"
 #define OSSL_SELF_TEST_TYPE_KAT_KDF "KAT_KDF"
diff --git a/providers/fips/self_test.c b/providers/fips/self_test.c
index e9f88140a6..84e65bcf4a 100644
--- a/providers/fips/self_test.c
+++ b/providers/fips/self_test.c
@@ -181,64 +181,6 @@ DEP_FINI_ATTRIBUTE void cleanup(void)
 #endif

 #if !defined(OPENSSL_NO_FIPS_POST)
-/*
- * We need an explicit HMAC-SHA-256 KAT even though it is also
- * checked as part of the KDF KATs.  Refer IG 10.3.
- */
-static const unsigned char hmac_kat_pt[] = {
-    0xdd, 0x0c, 0x30, 0x33, 0x35, 0xf9, 0xe4, 0x2e,
-    0xc2, 0xef, 0xcc, 0xbf, 0x07, 0x95, 0xee, 0xa2
-};
-static const unsigned char hmac_kat_key[] = {
-    0xf4, 0x55, 0x66, 0x50, 0xac, 0x31, 0xd3, 0x54,
-    0x61, 0x61, 0x0b, 0xac, 0x4e, 0xd8, 0x1b, 0x1a,
-    0x18, 0x1b, 0x2d, 0x8a, 0x43, 0xea, 0x28, 0x54,
-    0xcb, 0xae, 0x22, 0xca, 0x74, 0x56, 0x08, 0x13
-};
-static const unsigned char hmac_kat_digest[] = {
-    0xf5, 0xf5, 0xe5, 0xf2, 0x66, 0x49, 0xe2, 0x40,
-    0xfc, 0x9e, 0x85, 0x7f, 0x2b, 0x9a, 0xbe, 0x28,
-    0x20, 0x12, 0x00, 0x92, 0x82, 0x21, 0x3e, 0x51,
-    0x44, 0x5d, 0xe3, 0x31, 0x04, 0x01, 0x72, 0x6b
-};
-
-static int integrity_self_test(OSSL_SELF_TEST *ev, OSSL_LIB_CTX *libctx)
-{
-    int ok = 0;
-    unsigned char out[EVP_MAX_MD_SIZE];
-    size_t out_len = 0;
-
-    OSSL_PARAM params[2];
-    EVP_MAC *mac = EVP_MAC_fetch(libctx, MAC_NAME, NULL);
-    EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac);
-
-    OSSL_SELF_TEST_onbegin(ev, OSSL_SELF_TEST_TYPE_KAT_INTEGRITY,
-        OSSL_SELF_TEST_DESC_INTEGRITY_HMAC);
-
-    params[0] = OSSL_PARAM_construct_utf8_string("digest", DIGEST_NAME, 0);
-    params[1] = OSSL_PARAM_construct_end();
-
-    if (ctx == NULL
-        || mac == NULL
-        || !EVP_MAC_init(ctx, hmac_kat_key, sizeof(hmac_kat_key), params)
-        || !EVP_MAC_update(ctx, hmac_kat_pt, sizeof(hmac_kat_pt))
-        || !EVP_MAC_final(ctx, out, &out_len, MAX_MD_SIZE))
-        goto err;
-
-    /* Optional corruption */
-    OSSL_SELF_TEST_oncorrupt_byte(ev, out);
-
-    if (out_len != sizeof(hmac_kat_digest)
-        || memcmp(out, hmac_kat_digest, out_len) != 0)
-        goto err;
-    ok = 1;
-err:
-    OSSL_SELF_TEST_onend(ev, ok);
-    EVP_MAC_free(mac);
-    EVP_MAC_CTX_free(ctx);
-    return ok;
-}
-
 /*
  * Calculate the HMAC SHA256 of data read using a BIO and read_cb, and verify
  * the result matches the expected value.
@@ -257,7 +199,7 @@ static int verify_integrity(OSSL_CORE_BIO *bio, OSSL_FUNC_BIO_read_ex_fn read_ex
     EVP_MAC_CTX *ctx = NULL;
     OSSL_PARAM params[2], *p = params;

-    if (!integrity_self_test(ev, libctx))
+    if (!SELF_TEST_kats_single(ev, libctx, ST_ID_MAC_HMAC))
         goto err;

     OSSL_SELF_TEST_onbegin(ev, event_type, OSSL_SELF_TEST_DESC_INTEGRITY_HMAC);
@@ -383,6 +325,11 @@ int SELF_TEST_post(SELF_TEST_POST_PARAMS *st, int on_demand_test)
         goto end;
     }

+    if (on_demand_test)
+        /* ensure all states are cleared so all tests are repeated */
+        for (int i = 0; i < ST_ID_MAX; i++)
+            st_all_tests[i].state = SELF_TEST_STATE_INIT;
+
     if (!SELF_TEST_kats(ev, st->libctx, on_demand_test)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE);
         goto end;
diff --git a/providers/fips/self_test.h b/providers/fips/self_test.h
index 44c47e669b..2083ce5226 100644
--- a/providers/fips/self_test.h
+++ b/providers/fips/self_test.h
@@ -49,7 +49,7 @@ enum st_test_category {
     SELF_TEST_KAT_ASYM_KEYGEN,
     SELF_TEST_KAT_KEM,
     SELF_TEST_KAT_ASYM_CIPHER,
-    SELF_TEST_KAT_MAC, /* currently unused */
+    SELF_TEST_KAT_MAC,
 };

 enum st_test_state {
@@ -145,6 +145,10 @@ typedef struct st_kat_drbg_st {
     ST_BUFFER entropyaddin2;
 } ST_KAT_DRBG;

+typedef struct st_kat_mac_st {
+    const ST_KAT_PARAM *params;
+} ST_KAT_MAC;
+
 typedef struct self_test_st {
     const char *algorithm;
     const char *desc;
@@ -162,6 +166,7 @@ typedef struct self_test_st {
         ST_KAT_KDF kdf;
         ST_KAT_KAS kas;
         ST_KAT_DRBG drbg;
+        ST_KAT_MAC mac;
     } u;
     const self_test_id_t *depends_on;
 } ST_DEFINITION;
diff --git a/providers/fips/self_test_data.c b/providers/fips/self_test_data.c
index c829e3d5af..73b3ebec29 100644
--- a/providers/fips/self_test_data.c
+++ b/providers/fips/self_test_data.c
@@ -3298,6 +3298,33 @@ static const ST_KAT_PARAM ml_kem_key[] = {
 };
 #endif /* OPENSSL_NO_ML_KEM */

+/*
+ * We need an explicit HMAC-SHA-256 KAT even though it is also
+ * checked as part of the KDF KATs.  Refer IG 10.3.
+ */
+static const char hmac_kat_digest[] = "SHA256";
+static const unsigned char hmac_kat_pt[] = {
+    0xdd, 0x0c, 0x30, 0x33, 0x35, 0xf9, 0xe4, 0x2e,
+    0xc2, 0xef, 0xcc, 0xbf, 0x07, 0x95, 0xee, 0xa2
+};
+static const unsigned char hmac_kat_key[] = {
+    0xf4, 0x55, 0x66, 0x50, 0xac, 0x31, 0xd3, 0x54,
+    0x61, 0x61, 0x0b, 0xac, 0x4e, 0xd8, 0x1b, 0x1a,
+    0x18, 0x1b, 0x2d, 0x8a, 0x43, 0xea, 0x28, 0x54,
+    0xcb, 0xae, 0x22, 0xca, 0x74, 0x56, 0x08, 0x13
+};
+static const unsigned char hmac_kat_expected[] = {
+    0xf5, 0xf5, 0xe5, 0xf2, 0x66, 0x49, 0xe2, 0x40,
+    0xfc, 0x9e, 0x85, 0x7f, 0x2b, 0x9a, 0xbe, 0x28,
+    0x20, 0x12, 0x00, 0x92, 0x82, 0x21, 0x3e, 0x51,
+    0x44, 0x5d, 0xe3, 0x31, 0x04, 0x01, 0x72, 0x6b
+};
+static const ST_KAT_PARAM hmac_kat_params[] = {
+    ST_KAT_PARAM_UTF8STRING(OSSL_KDF_PARAM_DIGEST, hmac_kat_digest),
+    ST_KAT_PARAM_OCTET(OSSL_MAC_PARAM_KEY, hmac_kat_key),
+    ST_KAT_PARAM_END()
+};
+
 ST_DEFINITION st_all_tests[ST_ID_MAX] = {
     {
         "SHA1",
@@ -3921,4 +3948,16 @@ ST_DEFINITION st_all_tests[ST_ID_MAX] = {
         },
         .depends_on = rsaenc_depends_on,
     },
+    {
+        "HMAC",
+        OSSL_SELF_TEST_DESC_INTEGRITY_HMAC,
+        SELF_TEST_KAT_MAC,
+        SELF_TEST_DEFERRED,
+        SELF_TEST_STATE_INIT,
+        ITM_BUF(hmac_kat_pt),
+        ITM_BUF(hmac_kat_expected),
+        .u.mac = {
+            hmac_kat_params,
+        },
+    },
 };
diff --git a/providers/fips/self_test_kats.c b/providers/fips/self_test_kats.c
index 1750c99609..59e0346162 100644
--- a/providers/fips/self_test_kats.c
+++ b/providers/fips/self_test_kats.c
@@ -899,6 +899,55 @@ err:
     return ret;
 }

+/* Test MAC algorithms */
+static int self_test_mac(const ST_DEFINITION *t, OSSL_SELF_TEST *st,
+    OSSL_LIB_CTX *libctx)
+{
+    int ret = 0;
+    unsigned char out[EVP_MAX_MD_SIZE];
+    size_t out_len = 0;
+    EVP_MAC *mac = NULL;
+    EVP_MAC_CTX *ctx = NULL;
+    OSSL_PARAM *params = NULL;
+
+    /* Currently used for integrity */
+    OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_KAT_MAC, t->desc);
+
+    mac = EVP_MAC_fetch(libctx, t->algorithm, "");
+    if (mac == NULL)
+        goto err;
+
+    ctx = EVP_MAC_CTX_new(mac);
+    if (ctx == NULL)
+        goto err;
+
+    params = kat_params_to_ossl_params(libctx, t->u.mac.params, NULL);
+    if (params == NULL)
+        goto err;
+
+    if (t->expected.len > sizeof(out))
+        goto err;
+
+    if (!EVP_MAC_init(ctx, NULL, 0, params)
+        || !EVP_MAC_update(ctx, t->pt.buf, t->pt.len)
+        || !EVP_MAC_final(ctx, out, &out_len, EVP_MAX_MD_SIZE))
+        goto err;
+
+    OSSL_SELF_TEST_oncorrupt_byte(st, out);
+
+    if ((out_len != t->expected.len)
+        || memcmp(out, t->expected.buf, t->expected.len) != 0)
+        goto err;
+
+    ret = 1;
+err:
+    EVP_MAC_free(mac);
+    EVP_MAC_CTX_free(ctx);
+    OSSL_PARAM_free(params);
+    OSSL_SELF_TEST_onend(st, ret);
+    return ret;
+}
+
 /*
  * Swap the library context DRBG for KAT testing
  *
@@ -1065,7 +1114,7 @@ int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx, int do_deferred)
     for (i = 0; i < ST_ID_MAX; i++) {
         int res;

-        if (!do_deferred && (st_all_tests[i].deferred == SELF_TEST_DEFERRED))
+        if (!do_deferred && (st_all_tests[i].deferred == SELF_TEST_DEFERRED) && (st_all_tests[i].state != SELF_TEST_STATE_PASSED))
             continue;

         switch (st_all_tests[i].category) {
@@ -1096,6 +1145,9 @@ int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx, int do_deferred)
         case SELF_TEST_KAT_ASYM_CIPHER:
             res = self_test_asym_cipher(&st_all_tests[i], st, libctx);
             break;
+        case SELF_TEST_KAT_MAC:
+            res = self_test_mac(&st_all_tests[i], st, libctx);
+            break;
         default:
             res = 0;
             break;
@@ -1169,6 +1221,9 @@ int SELF_TEST_kats_single(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx, int id)
     case SELF_TEST_KAT_ASYM_CIPHER:
         ret = self_test_asym_cipher(&st_all_tests[id], st, libctx);
         break;
+    case SELF_TEST_KAT_MAC:
+        ret = self_test_mac(&st_all_tests[id], st, libctx);
+        break;
     default:
         ret = 0;
         break;