]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
fips: provide function to manually run FIPS self-tests
authorDaiki Ueno <ueno@gnu.org>
Fri, 3 Jun 2022 06:43:00 +0000 (15:43 +0900)
committerDaiki Ueno <ueno@gnu.org>
Thu, 9 Jun 2022 11:52:36 +0000 (20:52 +0900)
FIPS140-3 IG 10.3.E Periodic Self-Testing says:

  At security levels 1 and 2, acceptable means for initiating the
  periodic self-tests include a provided service, resetting, rebooting
  or power cycling.

Neither resetting, rebooting, nor power-cycling is suitable because
those involve operations outside of the module.  Therefore this patch
adds a new API to manually run the substance of FIPS140 self-tests.

Suggeested by Richard Costa and Stephan Mueller in:
https://gitlab.com/gnutls/gnutls/-/issues/1364

Signed-off-by: Daiki Ueno <ueno@gnu.org>
NEWS
devel/libgnutls.abignore
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/fips.c
lib/global.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
tests/fips-test.c

diff --git a/NEWS b/NEWS
index 70dd8a12b5d71be734ee113960255d90e974c9f9..389be8acaa5f36b3302ea38912c5e33f80f53f33 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,11 @@ Copyright (C) 2000-2016 Free Software Foundation, Inc.
 Copyright (C) 2013-2019 Nikos Mavrogiannopoulos
 See the end for copying conditions.
 
+* Version 3.7.7 (unreleased)
+
+** API and ABI modifications:
+gnutls_fips140_run_self_tests: New function
+
 * Version 3.7.6 (released 2022-05-27)
 
 ** libgnutls: Fixed invalid write when gnutls_realloc_zero()
index c19dce38e11a1c763a5d7bb7a17888e269dc61bf..8f0c7ce584cb58a96be56e8f8108561c300f4cad 100644 (file)
@@ -70,3 +70,5 @@ name = drbg_aes_reseed
 
 # The following should be removed in the new release, after updating the
 # abi-dump repository:
+[suppress_function]
+name = gnutls_fips140_run_self_tests
index b3d7e1ea18047fd3a987dd2643685d633eee5ba0..5767de0d6b5c68539e2f7c9f45583f1747b29be9 100644 (file)
@@ -16,6 +16,7 @@ GNUTLS_3_7_2@GNUTLS_3_7_2
 GNUTLS_3_7_3@GNUTLS_3_7_3
 GNUTLS_3_7_4@GNUTLS_3_7_4
 GNUTLS_3_7_5@GNUTLS_3_7_5
+GNUTLS_3_7_7@GNUTLS_3_7_7
 _gnutls_global_init_skip@GNUTLS_3_4
 gnutls_aead_cipher_decrypt@GNUTLS_3_4
 gnutls_aead_cipher_decryptv2@GNUTLS_3_6_10
@@ -262,6 +263,7 @@ gnutls_fips140_get_operation_state@GNUTLS_3_7_3
 gnutls_fips140_mode_enabled@GNUTLS_3_4
 gnutls_fips140_pop_context@GNUTLS_3_7_3
 gnutls_fips140_push_context@GNUTLS_3_7_3
+gnutls_fips140_run_self_tests@GNUTLS_3_7_7
 gnutls_fips140_set_mode@GNUTLS_3_6_3
 gnutls_free@GNUTLS_3_4
 gnutls_get_library_config@GNUTLS_3_7_3
index d20a021d97fc1a3f66240610bfc8bbd89343ea71..34ef43866cbb4a47dd08d0ec380ffa76e2cc3e0a 100644 (file)
@@ -1096,6 +1096,8 @@ FUNCS += functions/gnutls_fips140_pop_context
 FUNCS += functions/gnutls_fips140_pop_context.short
 FUNCS += functions/gnutls_fips140_push_context
 FUNCS += functions/gnutls_fips140_push_context.short
+FUNCS += functions/gnutls_fips140_run_self_tests
+FUNCS += functions/gnutls_fips140_run_self_tests.short
 FUNCS += functions/gnutls_fips140_set_mode
 FUNCS += functions/gnutls_fips140_set_mode.short
 FUNCS += functions/gnutls_get_library_config
index d8c5f2854d689384c4d76e4d3db2bda46da5c8ce..90906b057441e8e4f333415b4eb7f5b229a6ac1e 100644 (file)
@@ -380,6 +380,7 @@ APIMANS += gnutls_fips140_get_operation_state.3
 APIMANS += gnutls_fips140_mode_enabled.3
 APIMANS += gnutls_fips140_pop_context.3
 APIMANS += gnutls_fips140_push_context.3
+APIMANS += gnutls_fips140_run_self_tests.3
 APIMANS += gnutls_fips140_set_mode.3
 APIMANS += gnutls_get_library_config.3
 APIMANS += gnutls_get_system_config_file.3
index e9c27f6df69e163656debc7360696ba610ec9538..656d43e74af94bad409334b78243c3e9a768f64b 100644 (file)
@@ -419,8 +419,6 @@ int _gnutls_fips_perform_self_checks1(void)
 {
        int ret;
 
-       _gnutls_switch_lib_state(LIB_STATE_SELFTEST);
-
        /* Tests the FIPS algorithms used by nettle internally.
         * In our case we test AES-CBC since nettle's AES is used by
         * the DRBG-AES.
@@ -429,193 +427,153 @@ int _gnutls_fips_perform_self_checks1(void)
        /* ciphers - one test per cipher */
        ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_128_CBC);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        return 0;
-
-error:
-       _gnutls_switch_lib_state(LIB_STATE_ERROR);
-       _gnutls_audit_log(NULL, "FIPS140-2 self testing part1 failed\n");
-
-       return GNUTLS_E_SELF_TEST_ERROR;
 }
 
 int _gnutls_fips_perform_self_checks2(void)
 {
        int ret;
 
-       _gnutls_switch_lib_state(LIB_STATE_SELFTEST);
-
        /* Tests the FIPS algorithms */
 
        /* ciphers - one test per cipher */
        ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_3DES_CBC);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_CBC);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_GCM);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_XTS);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_CFB8);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* Digest tests */
        ret = gnutls_digest_self_test(0, GNUTLS_DIG_SHA3_224);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_digest_self_test(0, GNUTLS_DIG_SHA3_256);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_digest_self_test(0, GNUTLS_DIG_SHA3_384);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_digest_self_test(0, GNUTLS_DIG_SHA3_512);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* MAC (includes message digest test) */
        ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA1);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA224);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA256);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA384);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA512);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_mac_self_test(0, GNUTLS_MAC_AES_CMAC_256);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* PK */
        ret = gnutls_pk_self_test(0, GNUTLS_PK_RSA);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_pk_self_test(0, GNUTLS_PK_DSA);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_pk_self_test(0, GNUTLS_PK_EC);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        ret = gnutls_pk_self_test(0, GNUTLS_PK_DH);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* HKDF */
        ret = gnutls_hkdf_self_test(0, GNUTLS_MAC_SHA256);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* PBKDF2 */
        ret = gnutls_pbkdf2_self_test(0, GNUTLS_MAC_SHA256);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* TLS-PRF */
        ret = gnutls_tlsprf_self_test(0, GNUTLS_MAC_SHA256);
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        if (_gnutls_rnd_ops.self_test == NULL) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        /* this does not require rng initialization */
        ret = _gnutls_rnd_ops.self_test();
        if (ret < 0) {
-               gnutls_assert();
-               goto error;
+               return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
        }
 
        if (_skip_integrity_checks == 0) {
                ret = check_binary_integrity();
                if (ret < 0) {
-                       gnutls_assert();
-                       goto error;
+                       return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
                }
        }
 
        return 0;
-
-error:
-       _gnutls_switch_lib_state(LIB_STATE_ERROR);
-       _gnutls_audit_log(NULL, "FIPS140-2 self testing part 2 failed\n");
-
-       return GNUTLS_E_SELF_TEST_ERROR;
 }
 #endif
 
@@ -894,3 +852,48 @@ _gnutls_switch_fips_state(gnutls_fips140_operation_state_t state)
        (void)state;
 #endif
 }
+
+/**
+ * gnutls_fips140_run_self_tests:
+ *
+ * Manually perform the second round of the FIPS140 self-tests,
+ * including:
+ *
+ * - Known answer tests (KAT) for the selected set of symmetric
+ *   cipher, MAC, public key, KDF, and DRBG
+ * - Library integrity checks
+ *
+ * Upon failure with FIPS140 mode enabled, it makes the library
+ * unusable.  This function is not thread-safe.
+ *
+ * Returns: 0 upon success, a negative error code otherwise
+ *
+ * Since: 3.7.7
+ */
+int
+gnutls_fips140_run_self_tests(void)
+{
+#ifdef ENABLE_FIPS140
+       int ret;
+       unsigned prev_lib_state;
+
+       /* Temporarily switch to LIB_STATE_SELFTEST as some of the
+        * algorithms are implemented using special constructs in
+        * self-tests (such as deterministic variants) */
+       prev_lib_state = _gnutls_get_lib_state();
+       _gnutls_switch_lib_state(LIB_STATE_SELFTEST);
+
+       ret = _gnutls_fips_perform_self_checks2();
+       if (gnutls_fips140_mode_enabled() != GNUTLS_FIPS140_DISABLED &&
+           ret < 0) {
+               _gnutls_switch_lib_state(LIB_STATE_ERROR);
+               _gnutls_audit_log(NULL, "FIPS140-2 self testing part 2 failed\n");
+       } else {
+               /* Restore the previous library state */
+               _gnutls_switch_lib_state(prev_lib_state);
+       }
+       return ret;
+#else
+       return 0;
+#endif
+}
index faa7f0afb2d28c265cbffd601e62d9c326c0cdd9..1b372c15bda5263b3a6934ab7acde35ce597e884 100644 (file)
@@ -336,9 +336,12 @@ static int _gnutls_global_init(unsigned constructor)
 
                /* first round of self checks, these are done on the
                 * nettle algorithms which are used internally */
+               _gnutls_switch_lib_state(LIB_STATE_SELFTEST);
                ret = _gnutls_fips_perform_self_checks1();
-               if (res != 2) {
-                       if (ret < 0) {
+               if (ret < 0) {
+                       _gnutls_switch_lib_state(LIB_STATE_ERROR);
+                       _gnutls_audit_log(NULL, "FIPS140-2 self testing part1 failed\n");
+                       if (res != 2) {
                                gnutls_assert();
                                goto out;
                        }
@@ -355,9 +358,12 @@ static int _gnutls_global_init(unsigned constructor)
         * (e.g., AESNI overridden AES). They are after _gnutls_register_accel_crypto()
         * intentionally */
        if (res != 0) {
+               _gnutls_switch_lib_state(LIB_STATE_SELFTEST);
                ret = _gnutls_fips_perform_self_checks2();
-               if (res != 2) {
-                       if (ret < 0) {
+               if (ret < 0) {
+                       _gnutls_switch_lib_state(LIB_STATE_ERROR);
+                       _gnutls_audit_log(NULL, "FIPS140-2 self testing part 2 failed\n");
+                       if (res != 2) {
                                gnutls_assert();
                                goto out;
                        }
index f7fc5d114a2fc2b6f12b4529b8224f41e7166534..5840f331e914404b0f17a3f53326e7f3f4c3f47c 100644 (file)
@@ -3416,6 +3416,8 @@ gnutls_fips140_get_operation_state(gnutls_fips140_context_t context);
 int gnutls_fips140_push_context(gnutls_fips140_context_t context);
 int gnutls_fips140_pop_context(void);
 
+int gnutls_fips140_run_self_tests(void);
+
   /* Gnutls error codes. The mapping to a TLS alert is also shown in
    * comments.
    */
index 0241946c8adf353569a8a386b09523bb8e61c339..f42d5f9fae5da9c2695299419ed6409bf7038153 100644 (file)
@@ -1399,6 +1399,14 @@ GNUTLS_3_7_5
        *;
 } GNUTLS_3_7_4;
 
+GNUTLS_3_7_7
+{
+ global:
+       gnutls_fips140_run_self_tests;
+ local:
+       *;
+} GNUTLS_3_7_5;
+
 GNUTLS_FIPS140_3_4 {
   global:
        gnutls_cipher_self_test;
index a6a283fa67b63577dd224e0dfbc014d135730198..31a5e26111440957ea01930f0651ff160bf1ff1b 100644 (file)
@@ -525,6 +525,13 @@ void doit(void)
        }
 
        gnutls_fips140_context_deinit(fips_context);
+
+       /* run self-tests manually */
+       ret = gnutls_fips140_run_self_tests();
+       if (ret < 0) {
+               fail("gnutls_fips140_run_self_tests failed\n");
+       }
+
        gnutls_global_deinit();
        return;
 }