diff --git a/test/build.info b/test/build.info index 0fac691cab..bab28035a8 100644 --- a/test/build.info +++ b/test/build.info @@ -63,7 +63,7 @@ IF[{- !$disabled{tests} -}] keymgmt_internal_test hexstr_test provider_status_test defltfips_test \ bio_readbuffer_test user_property_test pkcs7_test upcallstest \ provfetchtest prov_config_test rand_test ca_internals_test \ - bio_tfo_test membio_test list_test + bio_tfo_test membio_test list_test fips_version_test IF[{- !$disabled{'deprecated-3.0'} -}] PROGRAMS{noinst}=enginetest @@ -427,6 +427,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[defltfips_test]=../include ../apps/include DEPEND[defltfips_test]=../libcrypto libtestutil.a + SOURCE[fips_version_test]=fips_version_test.c + INCLUDE[fips_version_test]=../include ../apps/include + DEPEND[fips_version_test]=../libcrypto libtestutil.a + SOURCE[ocspapitest]=ocspapitest.c INCLUDE[ocspapitest]=../include ../apps/include DEPEND[ocspapitest]=../libcrypto libtestutil.a diff --git a/test/endecode_test.c b/test/endecode_test.c index c139fe63be..14648287eb 100644 --- a/test/endecode_test.c +++ b/test/endecode_test.c @@ -47,6 +47,7 @@ OSSL_provider_init_fn ossl_legacy_provider_init; static int default_libctx = 1; static int is_fips = 0; +static int is_fips_3_0_0 = 0; static OSSL_LIB_CTX *testctx = NULL; static OSSL_LIB_CTX *keyctx = NULL; @@ -174,7 +175,7 @@ static int test_encode_decode(const char *file, const int line, output_type, output_structure, pass, pcipher))) goto end; - if ((flags & FLAG_FAIL_IF_FIPS) != 0 && is_fips) { + if ((flags & FLAG_FAIL_IF_FIPS) != 0 && is_fips && !is_fips_3_0_0) { if (TEST_false(decode_cb(file, line, (void **)&pkey2, encoded, encoded_len, output_type, output_structure, (flags & FLAG_DECODE_WITH_TYPE ? type : NULL), @@ -1323,6 +1324,11 @@ int setup_tests(void) return 0; } + /* FIPS(3.0.0): provider imports explicit params but they won't work #17998 */ + is_fips_3_0_0 = fips_provider_version_eq(testctx, 3, 0, 0); + if (is_fips_3_0_0 < 0) + return 0; + #ifdef STATIC_LEGACY /* * This test is always statically linked against libcrypto. We must not diff --git a/test/evp_test.c b/test/evp_test.c index e4350c39b4..a5f7b93cfb 100644 --- a/test/evp_test.c +++ b/test/evp_test.c @@ -1596,7 +1596,7 @@ static int mac_test_run_mac(EVP_TEST *t) goto err; } } - /* FIPS 3.0.0 can't reinitialise MAC contexts #18100 */ + /* FIPS(3.0.0): can't reinitialise MAC contexts #18100 */ if (reinit-- && fips_provider_version_gt(libctx, 3, 0, 0)) { OSSL_PARAM ivparams[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; int ret; @@ -2823,7 +2823,7 @@ static int kdf_test_run(EVP_TEST *t) t->err = "INTERNAL_ERROR"; goto err; } - /* FIPS 3.0.0 can't dup KDF contexts #17572 */ + /* FIPS(3.0.0): can't dup KDF contexts #17572 */ if (fips_provider_version_gt(libctx, 3, 0, 0) && (ctx = EVP_KDF_CTX_dup(expected->ctx)) != NULL) { EVP_KDF_CTX_free(expected->ctx); @@ -2922,7 +2922,7 @@ static int pkey_kdf_test_run(EVP_TEST *t) size_t got_len = 0; if (fips_provider_version_eq(libctx, 3, 0, 0)) { - /* FIPS 3.0.0 can't deal with oversized output buffers #18533 */ + /* FIPS(3.0.0): can't deal with oversized output buffers #18533 */ got_len = expected->output_len; } else { /* Find out the KDF output size */ @@ -3737,72 +3737,6 @@ static int prov_available(char *providers) return 0; } -static int check_fips_versions(char *versions, const EVP_TEST *t) -{ - char *p; - int major, minor, patch, r; - enum { - MODE_EQ, MODE_NE, MODE_LE, MODE_GT - } mode; - - while (*versions != '\0') { - for (; isspace(*versions); versions++) - continue; - if (*versions == '\0') - break; - for (p = versions; *versions != '\0' && !isspace(*versions); versions++) - continue; - if (*versions != '\0') - *versions++ = '\0'; - if (*p == '!') { - mode = MODE_NE; - p++; - } else if (*p == '=') { - mode = MODE_EQ; - p++; - } else if (*p == '<' && p[1] == '=') { - mode = MODE_LE; - p += 2; - } else if (*p == '>') { - mode = MODE_GT; - p++; - } else if (isdigit(*p)) { - mode = MODE_EQ; - } else { - TEST_info("Line %d: error matching FIPS version: mode %s\n", - t->s.curr, p); - return -1; - } - if (sscanf(p, "%d.%d.%d", &major, &minor, &patch) != 3) { - TEST_info("Line %d: error matching FIPS version: version %s\n", - t->s.curr, p); - return -1; - } - switch (mode) { - case MODE_EQ: - r = fips_provider_version_eq(libctx, major, minor, patch); - break; - case MODE_NE: - r = fips_provider_version_ne(libctx, major, minor, patch); - break; - case MODE_LE: - r = fips_provider_version_le(libctx, major, minor, patch); - break; - case MODE_GT: - r = fips_provider_version_gt(libctx, major, minor, patch); - break; - } - if (r < 0) { - TEST_info("Line %d: error matching FIPS version: internal error\n", - t->s.curr); - return -1; - } - if (r == 0) - return 0; - } - return 1; -} - /* Read and parse one test. Return 0 if failure, 1 if okay. */ static int parse(EVP_TEST *t) { @@ -3901,7 +3835,7 @@ start: goto start; } else if (strcmp(pp->key, "FIPSversion") == 0) { if (prov_available("fips")) { - j = check_fips_versions(pp->value, t); + j = fips_provider_version_match(libctx, pp->value); if (j < 0) { TEST_info("Line %d: error matching FIPS versions\n", t->s.curr); return 0; diff --git a/test/fips_version_test.c b/test/fips_version_test.c new file mode 100644 index 0000000000..dd23bb51e5 --- /dev/null +++ b/test/fips_version_test.c @@ -0,0 +1,78 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "testutil.h" + +static OSSL_LIB_CTX *libctx = NULL; +static OSSL_PROVIDER *libprov = NULL; + +typedef enum OPTION_choice { + OPT_ERR = -1, + OPT_EOF = 0, + OPT_CONFIG_FILE, + OPT_TEST_ENUM +} OPTION_CHOICE; + +const OPTIONS *test_get_options(void) +{ + static const OPTIONS test_options[] = { + OPT_TEST_OPTIONS_DEFAULT_USAGE, + { "config", OPT_CONFIG_FILE, '<', + "The configuration file to use for the libctx" }, + { NULL } + }; + return test_options; +} + +static int test_fips_version(int n) +{ + const char *version = test_get_argument(n); + + if (!TEST_ptr(version)) + return 0; + return TEST_int_eq(fips_provider_version_match(libctx, version), 1); +} + +int setup_tests(void) +{ + char *config_file = NULL; + OPTION_CHOICE o; + int n; + + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_CONFIG_FILE: + config_file = opt_arg(); + break; + case OPT_TEST_CASES: + break; + default: + case OPT_ERR: + return 0; + } + } + + if (!test_get_libctx(&libctx, NULL, config_file, &libprov, NULL)) + return 0; + + n = test_get_argument_count(); + if (n == 0) + return 0; + + ADD_ALL_TESTS(test_fips_version, n); + return 1; +} + +void cleanup_tests(void) +{ + OSSL_PROVIDER_unload(libprov); + OSSL_LIB_CTX_free(libctx); +} diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t index d6d25759b5..2bb77f2189 100644 --- a/test/recipes/25-test_verify.t +++ b/test/recipes/25-test_verify.t @@ -354,12 +354,18 @@ SKIP: { # Same as above but with base provider used for decoding SKIP: { my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); - skip "EC is not supported or FIPS is disabled", 3 - if disabled("ec") || $no_fips; - my $provconf = srctop_file("test", "fips-and-base.cnf"); my $provpath = bldtop_dir("providers"); my @prov = ("-provider-path", $provpath); + + skip "EC is not supported or FIPS is disabled", 3 + if disabled("ec") || $no_fips; + + run(test(["fips_version_test", "-config", $provconf, ">3.0.0"]), + capture => 1, statusvar => \my $exit); + skip "FIPS provider version is too old", 3 + if !$exit; + $ENV{OPENSSL_CONF} = $provconf; ok(!verify("ee-cert-ec-explicit", "", ["root-cert"], diff --git a/test/testutil.h b/test/testutil.h index 3a3e0f279e..0a050bb060 100644 --- a/test/testutil.h +++ b/test/testutil.h @@ -240,7 +240,7 @@ void cleanup_tests(void); /* * Helper functions to detect specific versions of the FIPS provider being in use. * Because of FIPS rules, code changes after a module has been validated are - * difficult and because we provide an hard guarantee of ABI and behavioural + * difficult and because we provide a hard guarantee of ABI and behavioural * stability going forwards, it is a requirement to have tests be conditional * on specific FIPS provider versions. Without this, bug fixes cannot be tested * in later releases. @@ -259,6 +259,17 @@ int fips_provider_version_ne(OSSL_LIB_CTX *libctx, int major, int minor, int pat int fips_provider_version_le(OSSL_LIB_CTX *libctx, int major, int minor, int patch); int fips_provider_version_gt(OSSL_LIB_CTX *libctx, int major, int minor, int patch); +/* + * This function matches fips provider version with (potentially multiple) + * maj.min.patch version strings in versions. + * The operator can be one of = ! <= or > comparison symbols. + * If the fips provider matches all the version comparisons (or if there is no + * fips provider available) the function returns 1. + * If the fips provider does not match the version comparisons, it returns 0. + * On error the function returns -1. + */ +int fips_provider_version_match(OSSL_LIB_CTX *libctx, const char *versions); + /* * Used to supply test specific command line options, * If non optional parameters are used, then the first entry in the OPTIONS[] diff --git a/test/testutil/provider.c b/test/testutil/provider.c index f9b37cca8f..c8b5dfc061 100644 --- a/test/testutil/provider.c +++ b/test/testutil/provider.c @@ -8,6 +8,7 @@ */ #include "../testutil.h" +#include #include #include #include @@ -88,7 +89,7 @@ static int fips_provider_version(OSSL_LIB_CTX *libctx, FIPS_VERSION *vers) || sscanf(vs, "%d.%d.%d", &vers->major, &vers->minor, &vers->patch) != 3) goto err; if (!OSSL_PROVIDER_unload(fips_prov)) - return -1; /* WTF do we do here??? */ + return -1; return 1; err: OSSL_PROVIDER_unload(fips_prov); @@ -140,3 +141,64 @@ int fips_provider_version_gt(OSSL_LIB_CTX *libctx, int major, int minor, int pat && (prov.minor > minor || (prov.minor == minor && prov.patch > patch))); } + +int fips_provider_version_match(OSSL_LIB_CTX *libctx, const char *versions) +{ + const char *p; + int major, minor, patch, r; + enum { + MODE_EQ, MODE_NE, MODE_LE, MODE_GT + } mode; + + while (*versions != '\0') { + for (; isspace(*versions); versions++) + continue; + if (*versions == '\0') + break; + for (p = versions; *versions != '\0' && !isspace(*versions); versions++) + continue; + if (*p == '!') { + mode = MODE_NE; + p++; + } else if (*p == '=') { + mode = MODE_EQ; + p++; + } else if (*p == '<' && p[1] == '=') { + mode = MODE_LE; + p += 2; + } else if (*p == '>') { + mode = MODE_GT; + p++; + } else if (isdigit(*p)) { + mode = MODE_EQ; + } else { + TEST_info("Error matching FIPS version: mode %s\n", p); + return -1; + } + if (sscanf(p, "%d.%d.%d", &major, &minor, &patch) != 3) { + TEST_info("Error matching FIPS version: version %s\n", p); + return -1; + } + switch (mode) { + case MODE_EQ: + r = fips_provider_version_eq(libctx, major, minor, patch); + break; + case MODE_NE: + r = fips_provider_version_ne(libctx, major, minor, patch); + break; + case MODE_LE: + r = fips_provider_version_le(libctx, major, minor, patch); + break; + case MODE_GT: + r = fips_provider_version_gt(libctx, major, minor, patch); + break; + } + if (r < 0) { + TEST_info("Error matching FIPS version: internal error\n"); + return -1; + } + if (r == 0) + return 0; + } + return 1; +}