]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add support for deferred FIPS self-tests
authorSimo Sorce <simo@redhat.com>
Mon, 1 Dec 2025 21:36:40 +0000 (16:36 -0500)
committerDmitry Belyavskiy <beldmit@gmail.com>
Fri, 13 Feb 2026 09:53:50 +0000 (10:53 +0100)
Add a new -defer_tests option to openssl fipsinstall and a corresponding
defer-tests configuration parameter for the FIPS provider.

This allows the execution of self-tests to be postponed until the
first time an algorithm is used, instead of running all tests
during module initialization. This reduces startup time.

Update the self-test framework to handle the new SELF_TEST_STATE_DEFER
state, ensuring deferred tests are skipped at load and run on demand.

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/29222)

CHANGES.md
apps/fipsinstall.c
doc/man1/openssl-fipsinstall.pod.in
doc/man5/fips_config.pod
include/openssl/fips_names.h
providers/fips/fipsprov.c
providers/fips/include/fips_selftest_params.inc
providers/fips/self_test.c
providers/fips/self_test.h
providers/fips/self_test_kats.c
test/recipes/00-prep_fipsmodule_cnf.t [changed mode: 0644->0755]

index 48a378d6185baaa9b96e656ded5cb8e48ee6a8d0..98e78e5d10a4bfd56555efc16963dbd9a5621912 100644 (file)
@@ -32,6 +32,11 @@ OpenSSL 4.0
 
 ### Changes between 3.6 and 4.0 [xx XXX xxxx]
 
+ * FIPS self tests can now be deferred and run as needed when installing
+   the fips module with the -defer_tests option.
+
+   *Simo Sorce*
+
  * OPENSSL_cleanup() now runs in a global destructor, or not at all by default.
 
    OpenSSL_cleanup() will no longer by default free global objects when run from
index ea54a00cffc107bc710fb708b3defa0f63a9794b..1341a3f826ffa1b4215c4b760f9f19dd7b4e5ebc 100644 (file)
@@ -77,7 +77,8 @@ typedef enum OPTION_choice {
     OPT_NO_PBKDF2_LOWER_BOUND_CHECK,
     OPT_ECDH_COFACTOR_CHECK,
     OPT_SELF_TEST_ONLOAD,
-    OPT_SELF_TEST_ONINSTALL
+    OPT_SELF_TEST_ONINSTALL,
+    OPT_DEFER_TESTS
 } OPTION_CHOICE;
 
 const OPTIONS fipsinstall_options[] = {
@@ -150,6 +151,7 @@ const OPTIONS fipsinstall_options[] = {
         "Disable lower bound check for PBKDF2" },
     { "ecdh_cofactor_check", OPT_ECDH_COFACTOR_CHECK, '-',
         "Enable Cofactor check for ECDH" },
+    { "defer_tests", OPT_DEFER_TESTS, '-', "Enables test deferral" },
     OPT_SECTION("Input"),
     { "in", OPT_IN, '<', "Input config file, used when verifying" },
 
@@ -197,6 +199,7 @@ typedef struct {
     unsigned int x942kdf_key_check : 1;
     unsigned int pbkdf2_lower_bound_check : 1;
     unsigned int ecdh_cofactor_check : 1;
+    unsigned int defer_tests : 1;
 } FIPS_OPTS;
 
 /* Pedantic FIPS compliance */
@@ -231,6 +234,7 @@ static const FIPS_OPTS pedantic_opts = {
     1, /* x942kdf_key_check */
     1, /* pbkdf2_lower_bound_check */
     1, /* ecdh_cofactor_check */
+    0, /* defer_tests */
 };
 
 /* Default FIPS settings for backward compatibility */
@@ -265,6 +269,7 @@ static FIPS_OPTS fips_opts = {
     0, /* x942kdf_key_check */
     1, /* pbkdf2_lower_bound_check */
     0, /* ecdh_cofactor_check */
+    0, /* defer_tests */
 };
 
 static int check_non_pedantic_fips(int pedantic, const char *name)
@@ -488,7 +493,10 @@ static int write_config_fips_section(BIO *out, const char *section,
                opts->ecdh_cofactor_check ? "1" : "0")
             <= 0
         || !print_mac(out, OSSL_PROV_FIPS_PARAM_MODULE_MAC, module_mac,
-            module_mac_len))
+            module_mac_len)
+        || BIO_printf(out, "%s = %s\n", OSSL_PROV_FIPS_PARAM_DEFER_TESTS,
+               opts->defer_tests ? "1" : "0")
+            <= 0)
         goto end;
 
     if (install_mac != NULL
@@ -802,6 +810,9 @@ int fipsinstall_main(int argc, char **argv)
             set_selftest_onload_option = 1;
             fips_opts.self_test_onload = 0;
             break;
+        case OPT_DEFER_TESTS:
+            fips_opts.defer_tests = 1;
+            break;
         }
     }
 
index 2db5acd242947a5924014c2fd9fff3e2a178e8e7..a66945952a0d6c46b53aef960ef693e84b1dc1a1 100644 (file)
@@ -54,6 +54,7 @@ B<openssl fipsinstall>
 [B<-corrupt_desc> I<selftest_description>]
 [B<-corrupt_type> I<selftest_type>]
 [B<-config> I<parent_config>]
+[B<-defer_tests>]
 
 =head1 DESCRIPTION
 
@@ -396,6 +397,12 @@ data that is included by the base C<parent_config> configuration file.
 See L<config(5)> for further information on how to set up a provider section.
 All other options are ignored if '-config' is used.
 
+=item B<-defer_tests>
+
+Configure the module to not run all self-tests at startup and allow tests
+execution to be deferred to the first time the algorithm to be tested is
+invoked.
+
 =back
 
 =head1 NOTES
@@ -487,6 +494,10 @@ B<-x963kdf_key_check>,
 B<-x942kdf_key_check>,
 B<-ecdh_cofactor_check>
 
+The following options was added in OpenSSL 4.0:
+
+B<-defer_tests>
+
 =head1 COPYRIGHT
 
 Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
index c3f7b8f3ab6b03e26b09ff6fa6a2e2d9c1b0034b..786e567b39cfb0fecf7c3ae13b303c393a5b0871 100644 (file)
@@ -52,6 +52,13 @@ Regardless of the value, the operation (e.g., key generation) that called the
 continuous test will return an error code if its continuous test fails. The
 operation may then be retried if the error mode has not been triggered.
 
+=item B<defer-tests>
+
+If set to C<1>, the module will not run the self tests during initialization.
+Instead, the self tests will be run on demand when a particular algorithm is
+used.
+The default value of C<0> runs the self tests during initialization.
+
 =item B<module-mac>
 
 The calculated MAC of the FIPS provider file.
index 3e310bb4a8f2ac5994f26e0d3823e908c9dc2a44..ef84955a9f26b44faef56dec9fcb094b570ff60b 100644 (file)
@@ -38,6 +38,12 @@ extern "C" {
  */
 #define OSSL_PROV_FIPS_PARAM_CONDITIONAL_ERRORS "conditional-errors"
 
+/*
+ * A boolean that determines if all the FIPS conditional self-test are executed
+ * at module startup or deferred and run only when an algorithm is invoked
+ */
+#define OSSL_PROV_FIPS_PARAM_DEFER_TESTS "defer-tests"
+
 /* The following are provided for backwards compatibility */
 #define OSSL_PROV_FIPS_PARAM_SECURITY_CHECKS OSSL_PROV_PARAM_SECURITY_CHECKS
 #define OSSL_PROV_FIPS_PARAM_TLS1_PRF_EMS_CHECK OSSL_PROV_PARAM_TLS1_PRF_EMS_CHECK
index 1a50d459268afae7e45eb9fcd148833b8bbf8aea..768e6aedc5f6a16bebab7f5c48ab974dac30873a 100644 (file)
@@ -157,7 +157,7 @@ static int fips_random_bytes(ossl_unused void *vprov, int which,
  */
 static int fips_get_params_from_core(FIPS_GLOBAL *fgbl)
 {
-    OSSL_PARAM core_params[32], *p = core_params;
+    OSSL_PARAM core_params[33], *p = core_params;
 
 #define OSSL_FIPS_PARAM(structname, paramname)                 \
     *p++ = OSSL_PARAM_construct_utf8_ptr(                      \
@@ -1326,7 +1326,7 @@ static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
          * record this test as invoked by the original test, for marking
          * it later as also satisfied
          */
-        if (st_all_tests[id].state == SELF_TEST_STATE_INIT)
+        if (st_all_tests[id].state == SELF_TEST_STATE_DEFER)
             st_all_tests[id].state = SELF_TEST_STATE_IMPLICIT;
         /*
          * A self test is in progress for this thread so we let this
@@ -1347,7 +1347,7 @@ static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
          * test and marked it as passed
          */
         switch (st_all_tests[id].state) {
-        case SELF_TEST_STATE_INIT:
+        case SELF_TEST_STATE_DEFER:
             break;
         case SELF_TEST_STATE_PASSED:
             ret = 1;
@@ -1438,7 +1438,7 @@ int ossl_deferred_self_test(OSSL_LIB_CTX *libctx, self_test_id_t id)
      * Immediately mark it and return.
      */
     if (ossl_fips_self_testing()) {
-        if (st_all_tests[id].state == SELF_TEST_STATE_INIT)
+        if (st_all_tests[id].state == SELF_TEST_STATE_DEFER)
             st_all_tests[id].state = SELF_TEST_STATE_IMPLICIT;
         return 1;
     }
index df942d9cea613d32ae7c3319536b2354a2af1b40..170ebc4c71ad9d91d04d694b85f1a5a1966b4e9e 100644 (file)
@@ -1,3 +1,4 @@
 OSSL_FIPS_PARAM(module_filename, OSSL_PROV_PARAM_CORE_MODULE_FILENAME)
 OSSL_FIPS_PARAM(module_checksum_data, OSSL_PROV_FIPS_PARAM_MODULE_MAC)
 OSSL_FIPS_PARAM(conditional_error_check, OSSL_PROV_FIPS_PARAM_CONDITIONAL_ERRORS)
+OSSL_FIPS_PARAM(defer_tests, OSSL_PROV_FIPS_PARAM_DEFER_TESTS)
index 2630129795be9e5d271fb0bfe77855eef2fa13e6..58d5936d7e2d3d5a4a083bd2ce039231de3ff55c 100644 (file)
@@ -325,12 +325,24 @@ 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++)
+    if ((st->defer_tests != NULL)
+        && strcmp(st->defer_tests, "1") == 0) {
+        /* Mark all non executed tests as deferred */
+        for (int i = 0; i < ST_ID_MAX; i++) {
+            if (st_all_tests[i].state == SELF_TEST_STATE_INIT)
+                st_all_tests[i].state = SELF_TEST_STATE_DEFER;
+        }
+    }
+
+    if (on_demand_test) {
+        /* ensure all states are cleared so all tests are forcibly
+         * repeated */
+        for (int i = 0; i < ST_ID_MAX; i++) {
             st_all_tests[i].state = SELF_TEST_STATE_INIT;
+        }
+    }
 
-    if (on_demand_test && !SELF_TEST_kats(ev, st->libctx)) {
+    if (!SELF_TEST_kats(ev, st->libctx)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE);
         goto end;
     }
index f9b6a2531adc89bea6924f827151b6cbbab5ba97..387c5fb0f9b25611bb2c12f04758751c85504292 100644 (file)
@@ -20,6 +20,9 @@ typedef struct self_test_post_params_st {
     /* Used for continuous tests */
     const char *conditional_error_check;
 
+    /* Used to decide whether to defer tests or not */
+    const char *defer_tests;
+
     /* BIO callbacks supplied to the FIPS provider */
     OSSL_FUNC_BIO_new_file_fn *bio_new_file_cb;
     OSSL_FUNC_BIO_new_membuf_fn *bio_new_buffer_cb;
@@ -54,11 +57,12 @@ enum st_test_category {
 };
 
 enum st_test_state {
-    SELF_TEST_STATE_INIT = 0,
-    SELF_TEST_STATE_IN_PROGRESS,
-    SELF_TEST_STATE_PASSED,
-    SELF_TEST_STATE_FAILED,
-    SELF_TEST_STATE_IMPLICIT,
+    SELF_TEST_STATE_INIT = 0, /* Test has not been execute yet */
+    SELF_TEST_STATE_IN_PROGRESS, /* Test is currently being executed */
+    SELF_TEST_STATE_PASSED, /* Test is marked as passed */
+    SELF_TEST_STATE_FAILED, /* Test failed */
+    SELF_TEST_STATE_IMPLICIT, /* Marks test as implicitly handled */
+    SELF_TEST_STATE_DEFER, /* Like INIT, but mark test as deferred */
 };
 
 /* used to store raw parameters for keys and algorithms */
index fd6363a31586ac40e9c4d027732e9b063abe94a8..f017bc47bf9b2a5fe80d0f7ffe1c1d9e56fecbf4 100644 (file)
@@ -1175,6 +1175,7 @@ int SELF_TEST_kats_execute(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx,
      */
     switch (st_all_tests[id].state) {
     case SELF_TEST_STATE_INIT:
+    case SELF_TEST_STATE_DEFER:
         break;
     case SELF_TEST_STATE_FAILED:
         return 0;
@@ -1277,8 +1278,9 @@ int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
     }
 
     for (i = 0; i < ST_ID_MAX; i++)
-        if (!SELF_TEST_kats_execute(st, libctx, i, 0))
-            ret = 0;
+        if (st_all_tests[i].state == SELF_TEST_STATE_INIT)
+            if (!SELF_TEST_kats_execute(st, libctx, i, 0))
+                ret = 0;
 
     RAND_set0_private(libctx, saved_rand);
     /* The above call will cause main_rand to be freed */
old mode 100644 (file)
new mode 100755 (executable)
index 4e3a6d8..e959009
@@ -30,7 +30,7 @@ my $fipsmoduleconf = bldtop_file('test', 'fipsmodule.cnf');
 plan tests => 1;
 
 # Create the $fipsmoduleconf file
-ok(run(app(['openssl', 'fipsinstall', '-pedantic',
+ok(run(app(['openssl', 'fipsinstall', '-pedantic', '-defer_tests',
             '-module', $fipsmodule, '-provider_name', 'fips',
             '-section_name', 'fips_sect', '-out', $fipsmoduleconf])),
    "fips install");