Commit 0afbd6bfb3 for openssl.org
commit 0afbd6bfb3f6e95c6039c509135ff26b3b946502
Author: Simo Sorce <simo@redhat.com>
Date: Thu Dec 4 23:14:47 2025 -0500
Add dependency handling for FIPS self-tests
Some FIPS Known Answer Tests (KATs) rely on other cryptographic algorithms
that also have their own KATs. This change introduces a formal mechanism to
ensure these dependencies are met before a test is run.
A `depends_on` field is added to the self-test definition to declare
prerequisites. A new recursive function, `FIPS_kat_deferred_execute`,
traverses this dependency chain, executing any required tests first.
This new logic also prevents tests from being run multiple times if they are a
dependency for several other tests. The `FIPS_kat_deferred` function is
updated to use this new dependency-aware execution function.
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/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index 933125a9b9..3808eb5ad5 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -1315,6 +1315,43 @@ static void deferred_deinit(void)
}
}
+static int FIPS_kat_deferred_execute(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx,
+ self_test_id_t id)
+{
+ /*
+ * Dependency chains may cause a test to be referenced multiple times
+ * immediately return if any state is present
+ */
+ if (st_all_tests[id].state != SELF_TEST_STATE_INIT)
+ return 1;
+
+ /* Mark test as in progress */
+ st_all_tests[id].state = SELF_TEST_STATE_IN_PROGRESS;
+
+ /* check if there are dependent tests to run */
+ if (st_all_tests[id].depends_on) {
+ for (int i = 0; st_all_tests[id].depends_on[i] != ST_ID_MAX; i++) {
+ self_test_id_t dep_id = st_all_tests[id].depends_on[i];
+
+ FIPS_kat_deferred_execute(st, libctx, dep_id);
+ switch (st_all_tests[dep_id].state) {
+ case SELF_TEST_STATE_PASSED:
+ case SELF_TEST_STATE_IN_PROGRESS:
+ continue;
+ default:
+ return 0;
+ }
+ }
+ }
+
+ /* may have already been run as a dependency, recheck before executing */
+ if (st_all_tests[id].state == SELF_TEST_STATE_IN_PROGRESS)
+ if (!SELF_TEST_kats_single(st, libctx, id))
+ return 0;
+
+ return 1;
+}
+
static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
{
int *rt = NULL;
@@ -1373,7 +1410,7 @@ static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
/* mark that we are executing a test on the local thread */
if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_FIPS_DEFERRED_KEY,
- libctx, (void *)0xC001))
+ libctx, rt))
goto done;
unset_key = true;
@@ -1384,19 +1421,9 @@ static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
if ((st = OSSL_SELF_TEST_new(cb, cb_arg)) == NULL)
goto done;
- /*
- * Dependency chains may cause a test to be referenced multiple times
- * immediately return if any state is present
- */
- if (st_all_tests[id].state == SELF_TEST_STATE_INIT) {
-
- /* Mark test as in progress */
- st_all_tests[id].state = SELF_TEST_STATE_IN_PROGRESS;
-
- /* execute test */
- if (!SELF_TEST_kats_single(st, libctx, id))
- goto done;
- }
+ /* Handles dependencies via recursion */
+ if (!(ret = FIPS_kat_deferred_execute(st, libctx, id)))
+ goto done;
/*
* now mark as passed all the algorithms that have been executed by
@@ -1482,7 +1509,7 @@ int ossl_deferred_self_test(OSSL_LIB_CTX *libctx, self_test_id_t id)
* in FIPS_kat_deferred() so this race is of no real consequence.
*/
ret = FIPS_kat_deferred(libctx, id);
- if (!ret)
+ if (!ret || st_all_tests[id].state == SELF_TEST_STATE_FAILED)
deferred_test_error(st_all_tests[id].category);
return ret;
}
diff --git a/providers/fips/self_test.h b/providers/fips/self_test.h
index 2c19653b66..cff506915e 100644
--- a/providers/fips/self_test.h
+++ b/providers/fips/self_test.h
@@ -163,6 +163,7 @@ typedef struct self_test_st {
ST_KAT_KAS kas;
ST_KAT_DRBG drbg;
} u;
+ self_test_id_t *depends_on;
} ST_DEFINITION;
extern ST_DEFINITION st_all_tests[ST_ID_MAX];