diff options
Diffstat (limited to 'test/testutil')
-rw-r--r-- | test/testutil/apps_shims.c | 56 | ||||
-rw-r--r-- | test/testutil/basic_output.c | 102 | ||||
-rw-r--r-- | test/testutil/cb.c | 16 | ||||
-rw-r--r-- | test/testutil/driver.c | 473 | ||||
-rw-r--r-- | test/testutil/fake_random.c | 231 | ||||
-rw-r--r-- | test/testutil/format_output.c | 535 | ||||
-rw-r--r-- | test/testutil/load.c | 105 | ||||
-rw-r--r-- | test/testutil/main.c | 41 | ||||
-rw-r--r-- | test/testutil/options.c | 79 | ||||
-rw-r--r-- | test/testutil/output.c | 58 | ||||
-rw-r--r-- | test/testutil/output.h | 68 | ||||
-rw-r--r-- | test/testutil/provider.c | 242 | ||||
-rw-r--r-- | test/testutil/random.c | 40 | ||||
-rw-r--r-- | test/testutil/stanza.c | 159 | ||||
-rw-r--r-- | test/testutil/test_cleanup.c | 14 | ||||
-rw-r--r-- | test/testutil/test_options.c | 21 | ||||
-rw-r--r-- | test/testutil/tests.c | 472 | ||||
-rw-r--r-- | test/testutil/testutil_init.c | 145 | ||||
-rw-r--r-- | test/testutil/tu_local.h | 61 |
19 files changed, 2918 insertions, 0 deletions
diff --git a/test/testutil/apps_shims.c b/test/testutil/apps_shims.c new file mode 100644 index 000000000000..53d851ffda3b --- /dev/null +++ b/test/testutil/apps_shims.c @@ -0,0 +1,56 @@ +/* + * Copyright 2019-2021 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 <stdlib.h> +#include "apps.h" +#include "../testutil.h" + +/* shim that avoids sucking in too much from apps/apps.c */ + +void *app_malloc(size_t sz, const char *what) +{ + void *vp; + + /* + * This isn't ideal but it is what the app's app_malloc() does on failure. + * Instead of exiting with a failure, abort() is called which makes sure + * that there will be a good stack trace for debugging purposes. + */ + if (!TEST_ptr(vp = OPENSSL_malloc(sz))) { + TEST_info("Could not allocate %zu bytes for %s\n", sz, what); + abort(); + } + return vp; +} + +/* shim to prevent sucking in too much from apps */ + +int opt_legacy_okay(void) +{ + return 1; +} + +/* + * These three functions are defined here so that they don't need to come from + * the apps source code and pull in a lot of additional things. + */ +int opt_provider_option_given(void) +{ + return 0; +} + +const char *app_get0_propq(void) +{ + return NULL; +} + +OSSL_LIB_CTX *app_get0_libctx(void) +{ + return NULL; +} diff --git a/test/testutil/basic_output.c b/test/testutil/basic_output.c new file mode 100644 index 000000000000..92f3de9300cc --- /dev/null +++ b/test/testutil/basic_output.c @@ -0,0 +1,102 @@ +/* + * Copyright 2017-2020 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 "../testutil.h" +#include "output.h" +#include "tu_local.h" + +#include <openssl/crypto.h> +#include <openssl/bio.h> + +/* These are available for any test program */ +BIO *bio_out = NULL; +BIO *bio_err = NULL; + +/* These are available for TAP output only (internally) */ +static BIO *tap_out = NULL; +static BIO *tap_err = NULL; + +void test_open_streams(void) +{ + tap_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); + tap_err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); +#ifdef __VMS + tap_out = BIO_push(BIO_new(BIO_f_linebuffer()), tap_out); + tap_err = BIO_push(BIO_new(BIO_f_linebuffer()), tap_err); +#endif + tap_out = BIO_push(BIO_new(BIO_f_prefix()), tap_out); + tap_err = BIO_push(BIO_new(BIO_f_prefix()), tap_err); + + bio_out = BIO_push(BIO_new(BIO_f_prefix()), tap_out); + bio_err = BIO_push(BIO_new(BIO_f_prefix()), tap_err); + BIO_set_prefix(bio_out, "# "); + BIO_set_prefix(bio_err, "# "); + + OPENSSL_assert(bio_out != NULL); + OPENSSL_assert(bio_err != NULL); +} + +void test_adjust_streams_tap_level(int level) +{ + BIO_set_indent(tap_out, level); + BIO_set_indent(tap_err, level); +} + +void test_close_streams(void) +{ + /* + * The rest of the chain is freed by the BIO_free_all() calls below, so + * we only need to free the last one in the bio_out and bio_err chains. + */ + BIO_free(bio_out); + BIO_free(bio_err); + + BIO_free_all(tap_out); + BIO_free_all(tap_err); +} + +int test_vprintf_stdout(const char *fmt, va_list ap) +{ + return BIO_vprintf(bio_out, fmt, ap); +} + +int test_vprintf_stderr(const char *fmt, va_list ap) +{ + return BIO_vprintf(bio_err, fmt, ap); +} + +int test_flush_stdout(void) +{ + return BIO_flush(bio_out); +} + +int test_flush_stderr(void) +{ + return BIO_flush(bio_err); +} + +int test_vprintf_tapout(const char *fmt, va_list ap) +{ + return BIO_vprintf(tap_out, fmt, ap); +} + +int test_vprintf_taperr(const char *fmt, va_list ap) +{ + return BIO_vprintf(tap_err, fmt, ap); +} + +int test_flush_tapout(void) +{ + return BIO_flush(tap_out); +} + +int test_flush_taperr(void) +{ + return BIO_flush(tap_err); +} diff --git a/test/testutil/cb.c b/test/testutil/cb.c new file mode 100644 index 000000000000..5b583b147b53 --- /dev/null +++ b/test/testutil/cb.c @@ -0,0 +1,16 @@ +/* + * Copyright 2017 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 "output.h" +#include "tu_local.h" + +int openssl_error_cb(const char *str, size_t len, void *u) +{ + return test_printf_stderr("%s", str); +} diff --git a/test/testutil/driver.c b/test/testutil/driver.c new file mode 100644 index 000000000000..9a4b762f4169 --- /dev/null +++ b/test/testutil/driver.c @@ -0,0 +1,473 @@ +/* + * Copyright 2016-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 "../testutil.h" +#include "output.h" +#include "tu_local.h" + +#include <string.h> +#include <assert.h> + +#include "internal/nelem.h" +#include <openssl/bio.h> + +#include "platform.h" /* From libapps */ + +#if defined(_WIN32) && !defined(__BORLANDC__) +# define strdup _strdup +#endif + + +/* + * Declares the structures needed to register each test case function. + */ +typedef struct test_info { + const char *test_case_name; + int (*test_fn) (void); + int (*param_test_fn)(int idx); + int num; + + /* flags */ + int subtest:1; +} TEST_INFO; + +static TEST_INFO all_tests[1024]; +static int num_tests = 0; +static int show_list = 0; +static int single_test = -1; +static int single_iter = -1; +static int level = 0; +static int seed = 0; +static int rand_order = 0; + +/* + * A parameterised test runs a loop of test cases. + * |num_test_cases| counts the total number of non-subtest test cases + * across all tests. + */ +static int num_test_cases = 0; + +static int process_shared_options(void); + + +void add_test(const char *test_case_name, int (*test_fn) (void)) +{ + assert(num_tests != OSSL_NELEM(all_tests)); + all_tests[num_tests].test_case_name = test_case_name; + all_tests[num_tests].test_fn = test_fn; + all_tests[num_tests].num = -1; + ++num_tests; + ++num_test_cases; +} + +void add_all_tests(const char *test_case_name, int(*test_fn)(int idx), + int num, int subtest) +{ + assert(num_tests != OSSL_NELEM(all_tests)); + all_tests[num_tests].test_case_name = test_case_name; + all_tests[num_tests].param_test_fn = test_fn; + all_tests[num_tests].num = num; + all_tests[num_tests].subtest = subtest; + ++num_tests; + if (subtest) + ++num_test_cases; + else + num_test_cases += num; +} + +static int gcd(int a, int b) +{ + while (b != 0) { + int t = b; + b = a % b; + a = t; + } + return a; +} + +static void set_seed(int s) +{ + seed = s; + if (seed <= 0) + seed = (int)time(NULL); + test_random_seed(seed); +} + + +int setup_test_framework(int argc, char *argv[]) +{ + char *test_seed = getenv("OPENSSL_TEST_RAND_ORDER"); + char *TAP_levels = getenv("HARNESS_OSSL_LEVEL"); + + if (TAP_levels != NULL) + level = 4 * atoi(TAP_levels); + test_adjust_streams_tap_level(level); + if (test_seed != NULL) { + rand_order = 1; + set_seed(atoi(test_seed)); + } else { + set_seed(0); + } + +#if defined(OPENSSL_SYS_VMS) && defined(__DECC) + argv = copy_argv(&argc, argv); +#elif defined(_WIN32) + /* + * Replace argv[] with UTF-8 encoded strings. + */ + win32_utf8argv(&argc, &argv); +#endif + + if (!opt_init(argc, argv, test_get_options())) + return 0; + return 1; +} + + +/* + * This can only be called after setup() has run, since num_tests and + * all_tests[] are setup at this point + */ +static int check_single_test_params(char *name, char *testname, char *itname) +{ + if (name != NULL) { + int i; + for (i = 0; i < num_tests; ++i) { + if (strcmp(name, all_tests[i].test_case_name) == 0) { + single_test = 1 + i; + break; + } + } + if (i >= num_tests) + single_test = atoi(name); + } + + + /* if only iteration is specified, assume we want the first test */ + if (single_test == -1 && single_iter != -1) + single_test = 1; + + if (single_test != -1) { + if (single_test < 1 || single_test > num_tests) { + test_printf_stderr("Invalid -%s value " + "(Value must be a valid test name OR a value between %d..%d)\n", + testname, 1, num_tests); + return 0; + } + } + if (single_iter != -1) { + if (all_tests[single_test - 1].num == -1) { + test_printf_stderr("-%s option is not valid for test %d:%s\n", + itname, + single_test, + all_tests[single_test - 1].test_case_name); + return 0; + } else if (single_iter < 1 + || single_iter > all_tests[single_test - 1].num) { + test_printf_stderr("Invalid -%s value for test %d:%s\t" + "(Value must be in the range %d..%d)\n", + itname, single_test, + all_tests[single_test - 1].test_case_name, + 1, all_tests[single_test - 1].num); + return 0; + } + } + return 1; +} + +static int process_shared_options(void) +{ + OPTION_CHOICE_DEFAULT o; + int value; + int ret = -1; + char *flag_test = ""; + char *flag_iter = ""; + char *testname = NULL; + + opt_begin(); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + /* Ignore any test options at this level */ + default: + break; + case OPT_ERR: + return ret; + case OPT_TEST_HELP: + opt_help(test_get_options()); + return 0; + case OPT_TEST_LIST: + show_list = 1; + break; + case OPT_TEST_SINGLE: + flag_test = opt_flag(); + testname = opt_arg(); + break; + case OPT_TEST_ITERATION: + flag_iter = opt_flag(); + if (!opt_int(opt_arg(), &single_iter)) + goto end; + break; + case OPT_TEST_INDENT: + if (!opt_int(opt_arg(), &value)) + goto end; + level = 4 * value; + test_adjust_streams_tap_level(level); + break; + case OPT_TEST_SEED: + if (!opt_int(opt_arg(), &value)) + goto end; + set_seed(value); + break; + } + } + if (!check_single_test_params(testname, flag_test, flag_iter)) + goto end; + ret = 1; +end: + return ret; +} + + +int pulldown_test_framework(int ret) +{ + set_test_title(NULL); + return ret; +} + +static void finalize(int success) +{ + if (success) + ERR_clear_error(); + else + ERR_print_errors_cb(openssl_error_cb, NULL); +} + +static char *test_title = NULL; + +void set_test_title(const char *title) +{ + free(test_title); + test_title = title == NULL ? NULL : strdup(title); +} + +PRINTF_FORMAT(2, 3) static void test_verdict(int verdict, + const char *description, ...) +{ + va_list ap; + + test_flush_stdout(); + test_flush_stderr(); + + if (verdict == 0 && seed != 0) + test_printf_tapout("# OPENSSL_TEST_RAND_ORDER=%d\n", seed); + test_printf_tapout("%s ", verdict != 0 ? "ok" : "not ok"); + va_start(ap, description); + test_vprintf_tapout(description, ap); + va_end(ap); + if (verdict == TEST_SKIP_CODE) + test_printf_tapout(" # skipped"); + test_printf_tapout("\n"); + test_flush_tapout(); +} + +int run_tests(const char *test_prog_name) +{ + int num_failed = 0; + int verdict = 1; + int ii, i, jj, j, jstep; + int test_case_count = 0; + int subtest_case_count = 0; + int permute[OSSL_NELEM(all_tests)]; + + i = process_shared_options(); + if (i == 0) + return EXIT_SUCCESS; + if (i == -1) + return EXIT_FAILURE; + + if (num_tests < 1) { + test_printf_tapout("1..0 # Skipped: %s\n", test_prog_name); + } else if (show_list == 0 && single_test == -1) { + if (level > 0) { + test_printf_stdout("Subtest: %s\n", test_prog_name); + test_flush_stdout(); + } + test_printf_tapout("1..%d\n", num_test_cases); + } + + test_flush_tapout(); + + for (i = 0; i < num_tests; i++) + permute[i] = i; + if (rand_order != 0) + for (i = num_tests - 1; i >= 1; i--) { + j = test_random() % (1 + i); + ii = permute[j]; + permute[j] = permute[i]; + permute[i] = ii; + } + + for (ii = 0; ii != num_tests; ++ii) { + i = permute[ii]; + + if (single_test != -1 && ((i+1) != single_test)) { + continue; + } + else if (show_list) { + if (all_tests[i].num != -1) { + test_printf_tapout("%d - %s (%d..%d)\n", ii + 1, + all_tests[i].test_case_name, 1, + all_tests[i].num); + } else { + test_printf_tapout("%d - %s\n", ii + 1, + all_tests[i].test_case_name); + } + test_flush_tapout(); + } else if (all_tests[i].num == -1) { + set_test_title(all_tests[i].test_case_name); + ERR_clear_error(); + verdict = all_tests[i].test_fn(); + finalize(verdict != 0); + test_verdict(verdict, "%d - %s", test_case_count + 1, test_title); + if (verdict == 0) + num_failed++; + test_case_count++; + } else { + verdict = TEST_SKIP_CODE; + set_test_title(all_tests[i].test_case_name); + if (all_tests[i].subtest) { + level += 4; + test_adjust_streams_tap_level(level); + if (single_iter == -1) { + test_printf_stdout("Subtest: %s\n", test_title); + test_printf_tapout("%d..%d\n", 1, all_tests[i].num); + test_flush_stdout(); + test_flush_tapout(); + } + } + + j = -1; + if (rand_order == 0 || all_tests[i].num < 3) + jstep = 1; + else + do + jstep = test_random() % all_tests[i].num; + while (jstep == 0 || gcd(all_tests[i].num, jstep) != 1); + + for (jj = 0; jj < all_tests[i].num; jj++) { + int v; + + j = (j + jstep) % all_tests[i].num; + if (single_iter != -1 && ((jj + 1) != single_iter)) + continue; + ERR_clear_error(); + v = all_tests[i].param_test_fn(j); + + if (v == 0) { + verdict = 0; + } else if (v != TEST_SKIP_CODE && verdict != 0) { + verdict = 1; + } + + finalize(v != 0); + + if (all_tests[i].subtest) + test_verdict(v, "%d - iteration %d", + subtest_case_count + 1, j + 1); + else + test_verdict(v, "%d - %s - iteration %d", + test_case_count + subtest_case_count + 1, + test_title, j + 1); + subtest_case_count++; + } + + if (all_tests[i].subtest) { + level -= 4; + test_adjust_streams_tap_level(level); + } + if (verdict == 0) + ++num_failed; + if (all_tests[i].num == -1 || all_tests[i].subtest) + test_verdict(verdict, "%d - %s", test_case_count + 1, + all_tests[i].test_case_name); + test_case_count++; + } + } + if (num_failed != 0) + return EXIT_FAILURE; + return EXIT_SUCCESS; +} + +/* + * Glue an array of strings together and return it as an allocated string. + * Optionally return the whole length of this string in |out_len| + */ +char *glue_strings(const char *list[], size_t *out_len) +{ + size_t len = 0; + char *p, *ret; + int i; + + for (i = 0; list[i] != NULL; i++) + len += strlen(list[i]); + + if (out_len != NULL) + *out_len = len; + + if (!TEST_ptr(ret = p = OPENSSL_malloc(len + 1))) + return NULL; + + for (i = 0; list[i] != NULL; i++) + p += strlen(strcpy(p, list[i])); + + return ret; +} + +char *test_mk_file_path(const char *dir, const char *file) +{ +# ifndef OPENSSL_SYS_VMS + const char *sep = "/"; +# else + const char *sep = ""; + char *dir_end; + char dir_end_sep; +# endif + size_t dirlen = dir != NULL ? strlen(dir) : 0; + size_t len = dirlen + strlen(sep) + strlen(file) + 1; + char *full_file = OPENSSL_zalloc(len); + + if (full_file != NULL) { + if (dir != NULL && dirlen > 0) { + OPENSSL_strlcpy(full_file, dir, len); +# ifdef OPENSSL_SYS_VMS + /* + * If |file| contains a directory spec, we need to do some + * careful merging. + * "vol:[dir.dir]" + "[.certs]sm2-root.crt" should become + * "vol:[dir.dir.certs]sm2-root.crt" + */ + dir_end = &full_file[strlen(full_file) - 1]; + dir_end_sep = *dir_end; + if ((dir_end_sep == ']' || dir_end_sep == '>') + && (file[0] == '[' || file[0] == '<')) { + file++; + if (file[0] == '.') + *dir_end = '\0'; + else + *dir_end = '.'; + } +#else + OPENSSL_strlcat(full_file, sep, len); +#endif + } + OPENSSL_strlcat(full_file, file, len); + } + + return full_file; +} diff --git a/test/testutil/fake_random.c b/test/testutil/fake_random.c new file mode 100644 index 000000000000..f2f0e2793f76 --- /dev/null +++ b/test/testutil/fake_random.c @@ -0,0 +1,231 @@ +/* + * Copyright 2021 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 may obtain a copy of the License at + * https://www.openssl.org/source/license.html + * or in the file LICENSE in the source distribution. + */ + +#include <string.h> +#include <openssl/core_names.h> +#include <openssl/rand.h> +#include <openssl/provider.h> +#include "../include/crypto/evp.h" +#include "../../crypto/evp/evp_local.h" +#include "../testutil.h" + +typedef struct { + fake_random_generate_cb *cb; + int state; + const char *name; + EVP_RAND_CTX *ctx; +} FAKE_RAND; + +static OSSL_FUNC_rand_newctx_fn fake_rand_newctx; +static OSSL_FUNC_rand_freectx_fn fake_rand_freectx; +static OSSL_FUNC_rand_instantiate_fn fake_rand_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn fake_rand_uninstantiate; +static OSSL_FUNC_rand_generate_fn fake_rand_generate; +static OSSL_FUNC_rand_gettable_ctx_params_fn fake_rand_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn fake_rand_get_ctx_params; +static OSSL_FUNC_rand_enable_locking_fn fake_rand_enable_locking; + +static void *fake_rand_newctx(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + FAKE_RAND *r = OPENSSL_zalloc(sizeof(*r)); + + if (r != NULL) + r->state = EVP_RAND_STATE_UNINITIALISED; + return r; +} + +static void fake_rand_freectx(void *vrng) +{ + OPENSSL_free(vrng); +} + +static int fake_rand_instantiate(void *vrng, ossl_unused unsigned int strength, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *pstr, + size_t pstr_len, + ossl_unused const OSSL_PARAM params[]) +{ + FAKE_RAND *frng = (FAKE_RAND *)vrng; + + frng->state = EVP_RAND_STATE_READY; + return 1; +} + +static int fake_rand_uninstantiate(void *vrng) +{ + FAKE_RAND *frng = (FAKE_RAND *)vrng; + + frng->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int fake_rand_generate(void *vrng, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adinlen) +{ + FAKE_RAND *frng = (FAKE_RAND *)vrng; + size_t l; + uint32_t r; + + if (frng->cb != NULL) + return (*frng->cb)(out, outlen, frng->name, frng->ctx); + while (outlen > 0) { + r = test_random(); + l = outlen < sizeof(r) ? outlen : sizeof(r); + + memcpy(out, &r, l); + out += l; + outlen -= l; + } + return 1; +} + +static int fake_rand_enable_locking(void *vrng) +{ + return 1; +} + +static int fake_rand_get_ctx_params(ossl_unused void *vrng, OSSL_PARAM params[]) +{ + FAKE_RAND *frng = (FAKE_RAND *)vrng; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, frng->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, 256)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, INT_MAX)) + return 0; + return 1; +} + +static const OSSL_PARAM *fake_rand_gettable_ctx_params(ossl_unused void *vrng, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static const OSSL_DISPATCH fake_rand_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void (*)(void))fake_rand_newctx }, + { OSSL_FUNC_RAND_FREECTX, (void (*)(void))fake_rand_freectx }, + { OSSL_FUNC_RAND_INSTANTIATE, (void (*)(void))fake_rand_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, (void (*)(void))fake_rand_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void (*)(void))fake_rand_generate }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void (*)(void))fake_rand_enable_locking }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))fake_rand_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))fake_rand_get_ctx_params }, + { 0, NULL } +}; + +static const OSSL_ALGORITHM fake_rand_rand[] = { + { "FAKE", "provider=fake", fake_rand_functions }, + { NULL, NULL, NULL } +}; + +static const OSSL_ALGORITHM *fake_rand_query(void *provctx, + int operation_id, + int *no_cache) +{ + *no_cache = 0; + switch (operation_id) { + case OSSL_OP_RAND: + return fake_rand_rand; + } + return NULL; +} + +/* Functions we provide to the core */ +static const OSSL_DISPATCH fake_rand_method[] = { + { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))OSSL_LIB_CTX_free }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))fake_rand_query }, + { 0, NULL } +}; + +static int fake_rand_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, void **provctx) +{ + if (!TEST_ptr(*provctx = OSSL_LIB_CTX_new())) + return 0; + *out = fake_rand_method; + return 1; +} + +static int check_rng(EVP_RAND_CTX *rng, const char *name) +{ + FAKE_RAND *f; + + if (!TEST_ptr(rng)) { + TEST_info("random: %s", name); + return 0; + } + f = rng->algctx; + f->name = name; + f->ctx = rng; + return 1; +} + +OSSL_PROVIDER *fake_rand_start(OSSL_LIB_CTX *libctx) +{ + OSSL_PROVIDER *p; + + if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, "fake-rand", + fake_rand_provider_init)) + || !TEST_true(RAND_set_DRBG_type(libctx, "fake", NULL, NULL, NULL)) + || !TEST_ptr(p = OSSL_PROVIDER_try_load(libctx, "fake-rand", 1))) + return NULL; + + /* Ensure that the fake rand is initialized. */ + if (!TEST_true(check_rng(RAND_get0_primary(libctx), "primary")) + || !TEST_true(check_rng(RAND_get0_private(libctx), "private")) + || !TEST_true(check_rng(RAND_get0_public(libctx), "public"))) { + OSSL_PROVIDER_unload(p); + return NULL; + } + + return p; +} + +void fake_rand_finish(OSSL_PROVIDER *p) +{ + OSSL_PROVIDER_unload(p); +} + +void fake_rand_set_callback(EVP_RAND_CTX *rng, + int (*cb)(unsigned char *out, size_t outlen, + const char *name, EVP_RAND_CTX *ctx)) +{ + if (rng != NULL) + ((FAKE_RAND *)rng->algctx)->cb = cb; +} + +void fake_rand_set_public_private_callbacks(OSSL_LIB_CTX *libctx, + int (*cb)(unsigned char *out, + size_t outlen, + const char *name, + EVP_RAND_CTX *ctx)) +{ + fake_rand_set_callback(RAND_get0_private(libctx), cb); + fake_rand_set_callback(RAND_get0_public(libctx), cb); +} + diff --git a/test/testutil/format_output.c b/test/testutil/format_output.c new file mode 100644 index 000000000000..e101a7ecefb1 --- /dev/null +++ b/test/testutil/format_output.c @@ -0,0 +1,535 @@ +/* + * Copyright 2017-2020 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 "../testutil.h" +#include "output.h" +#include "tu_local.h" + +#include <string.h> +#include <ctype.h> +#include "internal/nelem.h" + +/* The size of memory buffers to display on failure */ +#define MEM_BUFFER_SIZE (2000) +#define MAX_STRING_WIDTH (80) +#define BN_OUTPUT_SIZE (8) + +/* Output a diff header */ +static void test_diff_header(const char *left, const char *right) +{ + test_printf_stderr("--- %s\n", left); + test_printf_stderr("+++ %s\n", right); +} + +/* Formatted string output routines */ +static void test_string_null_empty(const char *m, char c) +{ + if (m == NULL) + test_printf_stderr("%4s %c NULL\n", "", c); + else + test_printf_stderr("%4u:%c ''\n", 0u, c); +} + +static void test_fail_string_common(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const char *m1, size_t l1, + const char *m2, size_t l2) +{ + const size_t width = + (MAX_STRING_WIDTH - BIO_get_indent(bio_err) - 12) / 16 * 16; + char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; + char bdiff[MAX_STRING_WIDTH + 1]; + size_t n1, n2, i; + unsigned int cnt = 0, diff; + + test_fail_message_prefix(prefix, file, line, type, left, right, op); + if (m1 == NULL) + l1 = 0; + if (m2 == NULL) + l2 = 0; + if (l1 == 0 && l2 == 0) { + if ((m1 == NULL) == (m2 == NULL)) { + test_string_null_empty(m1, ' '); + } else { + test_diff_header(left, right); + test_string_null_empty(m1, '-'); + test_string_null_empty(m2, '+'); + } + goto fin; + } + + if (l1 != l2 || strncmp(m1, m2, l1) != 0) + test_diff_header(left, right); + + while (l1 > 0 || l2 > 0) { + n1 = n2 = 0; + if (l1 > 0) { + b1[n1 = l1 > width ? width : l1] = 0; + for (i = 0; i < n1; i++) + b1[i] = isprint((unsigned char)m1[i]) ? m1[i] : '.'; + } + if (l2 > 0) { + b2[n2 = l2 > width ? width : l2] = 0; + for (i = 0; i < n2; i++) + b2[i] = isprint((unsigned char)m2[i]) ? m2[i] : '.'; + } + diff = 0; + i = 0; + if (n1 > 0 && n2 > 0) { + const size_t j = n1 < n2 ? n1 : n2; + + for (; i < j; i++) + if (m1[i] == m2[i]) { + bdiff[i] = ' '; + } else { + bdiff[i] = '^'; + diff = 1; + } + bdiff[i] = '\0'; + } + if (n1 == n2 && !diff) { + test_printf_stderr("%4u: '%s'\n", cnt, n2 > n1 ? b2 : b1); + } else { + if (cnt == 0 && (m1 == NULL || *m1 == '\0')) + test_string_null_empty(m1, '-'); + else if (n1 > 0) + test_printf_stderr("%4u:- '%s'\n", cnt, b1); + if (cnt == 0 && (m2 == NULL || *m2 == '\0')) + test_string_null_empty(m2, '+'); + else if (n2 > 0) + test_printf_stderr("%4u:+ '%s'\n", cnt, b2); + if (diff && i > 0) + test_printf_stderr("%4s %s\n", "", bdiff); + } + if (m1 != NULL) + m1 += n1; + if (m2 != NULL) + m2 += n2; + l1 -= n1; + l2 -= n2; + cnt += width; + } +fin: + test_flush_stderr(); +} + +/* + * Wrapper routines so that the underlying code can be shared. + * The first is the call from inside the test utilities when a conditional + * fails. The second is the user's call to dump a string. + */ +void test_fail_string_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const char *m1, size_t l1, + const char *m2, size_t l2) +{ + test_fail_string_common(prefix, file, line, type, left, right, op, + m1, l1, m2, l2); + test_printf_stderr("\n"); +} + +void test_output_string(const char *name, const char *m, size_t l) +{ + test_fail_string_common("string", NULL, 0, NULL, NULL, NULL, name, + m, l, m, l); +} + +/* BIGNUM formatted output routines */ + +/* + * A basic memory byte to hex digit converter with allowance for spacing + * every so often. + */ +static void hex_convert_memory(const unsigned char *m, size_t n, char *b, + size_t width) +{ + size_t i; + + for (i = 0; i < n; i++) { + const unsigned char c = *m++; + + *b++ = "0123456789abcdef"[c >> 4]; + *b++ = "0123456789abcdef"[c & 15]; + if (i % width == width - 1 && i != n - 1) + *b++ = ' '; + } + *b = '\0'; +} + +/* + * Constants to define the number of bytes to display per line and the number + * of characters these take. + */ +static const int bn_bytes = (MAX_STRING_WIDTH - 9) / (BN_OUTPUT_SIZE * 2 + 1) + * BN_OUTPUT_SIZE; +static const int bn_chars = (MAX_STRING_WIDTH - 9) / (BN_OUTPUT_SIZE * 2 + 1) + * (BN_OUTPUT_SIZE * 2 + 1) - 1; + +/* + * Output the header line for the bignum + */ +static void test_bignum_header_line(void) +{ + test_printf_stderr(" %*s\n", bn_chars + 6, "bit position"); +} + +static const char *test_bignum_zero_null(const BIGNUM *bn) +{ + if (bn != NULL) + return BN_is_negative(bn) ? "-0" : "0"; + return "NULL"; +} + +/* + * Print a bignum zero taking care to include the correct sign. + * This routine correctly deals with a NULL bignum pointer as input. + */ +static void test_bignum_zero_print(const BIGNUM *bn, char sep) +{ + const char *v = test_bignum_zero_null(bn); + const char *suf = bn != NULL ? ": 0" : ""; + + test_printf_stderr("%c%*s%s\n", sep, bn_chars, v, suf); +} + +/* + * Convert a section of memory from inside a bignum into a displayable + * string with appropriate visual aid spaces inserted. + */ +static int convert_bn_memory(const unsigned char *in, size_t bytes, + char *out, int *lz, const BIGNUM *bn) +{ + int n = bytes * 2, i; + char *p = out, *q = NULL; + const char *r; + + if (bn != NULL && !BN_is_zero(bn)) { + hex_convert_memory(in, bytes, out, BN_OUTPUT_SIZE); + if (*lz) { + for (; *p == '0' || *p == ' '; p++) + if (*p == '0') { + q = p; + *p = ' '; + n--; + } + if (*p == '\0') { + /* + * in[bytes] is defined because we're converting a non-zero + * number and we've not seen a non-zero yet. + */ + if ((in[bytes] & 0xf0) != 0 && BN_is_negative(bn)) { + *lz = 0; + *q = '-'; + n++; + } + } else { + *lz = 0; + if (BN_is_negative(bn)) { + /* + * This is valid because we always convert more digits than + * the number holds. + */ + *q = '-'; + n++; + } + } + } + return n; + } + + for (i = 0; i < n; i++) { + *p++ = ' '; + if (i % (2 * BN_OUTPUT_SIZE) == 2 * BN_OUTPUT_SIZE - 1 && i != n - 1) + *p++ = ' '; + } + *p = '\0'; + if (bn == NULL) + r = "NULL"; + else + r = BN_is_negative(bn) ? "-0" : "0"; + strcpy(p - strlen(r), r); + return 0; +} + +/* + * Common code to display either one or two bignums, including the diff + * pointers for changes (only when there are two). + */ +static void test_fail_bignum_common(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, + const BIGNUM *bn1, const BIGNUM *bn2) +{ + const size_t bytes = bn_bytes; + char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; + char *p, bdiff[MAX_STRING_WIDTH + 1]; + size_t l1, l2, n1, n2, i, len; + unsigned int cnt, diff, real_diff; + unsigned char *m1 = NULL, *m2 = NULL; + int lz1 = 1, lz2 = 1; + unsigned char buffer[MEM_BUFFER_SIZE * 2], *bufp = buffer; + + test_fail_message_prefix(prefix, file, line, type, left, right, op); + l1 = bn1 == NULL ? 0 : (BN_num_bytes(bn1) + (BN_is_negative(bn1) ? 1 : 0)); + l2 = bn2 == NULL ? 0 : (BN_num_bytes(bn2) + (BN_is_negative(bn2) ? 1 : 0)); + if (l1 == 0 && l2 == 0) { + if ((bn1 == NULL) == (bn2 == NULL)) { + test_bignum_header_line(); + test_bignum_zero_print(bn1, ' '); + } else { + test_diff_header(left, right); + test_bignum_header_line(); + test_bignum_zero_print(bn1, '-'); + test_bignum_zero_print(bn2, '+'); + } + goto fin; + } + + if (l1 != l2 || bn1 == NULL || bn2 == NULL || BN_cmp(bn1, bn2) != 0) + test_diff_header(left, right); + test_bignum_header_line(); + + len = ((l1 > l2 ? l1 : l2) + bytes - 1) / bytes * bytes; + + if (len > MEM_BUFFER_SIZE && (bufp = OPENSSL_malloc(len * 2)) == NULL) { + bufp = buffer; + len = MEM_BUFFER_SIZE; + test_printf_stderr("WARNING: these BIGNUMs have been truncated\n"); + } + + if (bn1 != NULL) { + m1 = bufp; + BN_bn2binpad(bn1, m1, len); + } + if (bn2 != NULL) { + m2 = bufp + len; + BN_bn2binpad(bn2, m2, len); + } + + while (len > 0) { + cnt = 8 * (len - bytes); + n1 = convert_bn_memory(m1, bytes, b1, &lz1, bn1); + n2 = convert_bn_memory(m2, bytes, b2, &lz2, bn2); + + diff = real_diff = 0; + i = 0; + p = bdiff; + for (i=0; b1[i] != '\0'; i++) + if (b1[i] == b2[i] || b1[i] == ' ' || b2[i] == ' ') { + *p++ = ' '; + diff |= b1[i] != b2[i]; + } else { + *p++ = '^'; + real_diff = diff = 1; + } + *p++ = '\0'; + if (!diff) { + test_printf_stderr(" %s:% 5d\n", n2 > n1 ? b2 : b1, cnt); + } else { + if (cnt == 0 && bn1 == NULL) + test_printf_stderr("-%s\n", b1); + else if (cnt == 0 || n1 > 0) + test_printf_stderr("-%s:% 5d\n", b1, cnt); + if (cnt == 0 && bn2 == NULL) + test_printf_stderr("+%s\n", b2); + else if (cnt == 0 || n2 > 0) + test_printf_stderr("+%s:% 5d\n", b2, cnt); + if (real_diff && (cnt == 0 || (n1 > 0 && n2 > 0)) + && bn1 != NULL && bn2 != NULL) + test_printf_stderr(" %s\n", bdiff); + } + if (m1 != NULL) + m1 += bytes; + if (m2 != NULL) + m2 += bytes; + len -= bytes; + } +fin: + test_flush_stderr(); + if (bufp != buffer) + OPENSSL_free(bufp); +} + +/* + * Wrapper routines so that the underlying code can be shared. + * The first two are calls from inside the test utilities when a conditional + * fails. The third is the user's call to dump a bignum. + */ +void test_fail_bignum_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, + const BIGNUM *bn1, const BIGNUM *bn2) +{ + test_fail_bignum_common(prefix, file, line, type, left, right, op, bn1, bn2); + test_printf_stderr("\n"); +} + +void test_fail_bignum_mono_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const BIGNUM *bn) +{ + test_fail_bignum_common(prefix, file, line, type, left, right, op, bn, bn); + test_printf_stderr("\n"); +} + +void test_output_bignum(const char *name, const BIGNUM *bn) +{ + if (bn == NULL || BN_is_zero(bn)) { + test_printf_stderr("bignum: '%s' = %s\n", name, + test_bignum_zero_null(bn)); + } else if (BN_num_bytes(bn) <= BN_OUTPUT_SIZE) { + unsigned char buf[BN_OUTPUT_SIZE]; + char out[2 * sizeof(buf) + 1]; + char *p = out; + int n = BN_bn2bin(bn, buf); + + hex_convert_memory(buf, n, p, BN_OUTPUT_SIZE); + while (*p == '0' && *++p != '\0') + ; + test_printf_stderr("bignum: '%s' = %s0x%s\n", name, + BN_is_negative(bn) ? "-" : "", p); + } else { + test_fail_bignum_common("bignum", NULL, 0, NULL, NULL, NULL, name, + bn, bn); + } +} + +/* Memory output routines */ + +/* + * Handle zero length blocks of memory or NULL pointers to memory + */ +static void test_memory_null_empty(const unsigned char *m, char c) +{ + if (m == NULL) + test_printf_stderr("%4s %c%s\n", "", c, "NULL"); + else + test_printf_stderr("%04x %c%s\n", 0u, c, "empty"); +} + +/* + * Common code to display one or two blocks of memory. + */ +static void test_fail_memory_common(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, + const unsigned char *m1, size_t l1, + const unsigned char *m2, size_t l2) +{ + const size_t bytes = (MAX_STRING_WIDTH - 9) / 17 * 8; + char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; + char *p, bdiff[MAX_STRING_WIDTH + 1]; + size_t n1, n2, i; + unsigned int cnt = 0, diff; + + test_fail_message_prefix(prefix, file, line, type, left, right, op); + if (m1 == NULL) + l1 = 0; + if (m2 == NULL) + l2 = 0; + if (l1 == 0 && l2 == 0) { + if ((m1 == NULL) == (m2 == NULL)) { + test_memory_null_empty(m1, ' '); + } else { + test_diff_header(left, right); + test_memory_null_empty(m1, '-'); + test_memory_null_empty(m2, '+'); + } + goto fin; + } + + if (l1 != l2 || (m1 != m2 && memcmp(m1, m2, l1) != 0)) + test_diff_header(left, right); + + while (l1 > 0 || l2 > 0) { + n1 = n2 = 0; + if (l1 > 0) { + n1 = l1 > bytes ? bytes : l1; + hex_convert_memory(m1, n1, b1, 8); + } + if (l2 > 0) { + n2 = l2 > bytes ? bytes : l2; + hex_convert_memory(m2, n2, b2, 8); + } + + diff = 0; + i = 0; + p = bdiff; + if (n1 > 0 && n2 > 0) { + const size_t j = n1 < n2 ? n1 : n2; + + for (; i < j; i++) { + if (m1[i] == m2[i]) { + *p++ = ' '; + *p++ = ' '; + } else { + *p++ = '^'; + *p++ = '^'; + diff = 1; + } + if (i % 8 == 7 && i != j - 1) + *p++ = ' '; + } + *p++ = '\0'; + } + + if (n1 == n2 && !diff) { + test_printf_stderr("%04x: %s\n", cnt, b1); + } else { + if (cnt == 0 && (m1 == NULL || l1 == 0)) + test_memory_null_empty(m1, '-'); + else if (n1 > 0) + test_printf_stderr("%04x:-%s\n", cnt, b1); + if (cnt == 0 && (m2 == NULL || l2 == 0)) + test_memory_null_empty(m2, '+'); + else if (n2 > 0) + test_printf_stderr("%04x:+%s\n", cnt, b2); + if (diff && i > 0) + test_printf_stderr("%4s %s\n", "", bdiff); + } + if (m1 != NULL) + m1 += n1; + if (m2 != NULL) + m2 += n2; + l1 -= n1; + l2 -= n2; + cnt += bytes; + } +fin: + test_flush_stderr(); +} + +/* + * Wrapper routines so that the underlying code can be shared. + * The first is the call from inside the test utilities when a conditional + * fails. The second is the user's call to dump memory. + */ +void test_fail_memory_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, + const unsigned char *m1, size_t l1, + const unsigned char *m2, size_t l2) +{ + test_fail_memory_common(prefix, file, line, type, left, right, op, + m1, l1, m2, l2); + test_printf_stderr("\n"); +} + +void test_output_memory(const char *name, const unsigned char *m, size_t l) +{ + test_fail_memory_common("memory", NULL, 0, NULL, NULL, NULL, name, + m, l, m, l); +} diff --git a/test/testutil/load.c b/test/testutil/load.c new file mode 100644 index 000000000000..d776a7f167cf --- /dev/null +++ b/test/testutil/load.c @@ -0,0 +1,105 @@ +/* + * Copyright 2020-2021 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 <stdio.h> +#include <stdlib.h> + +#include <openssl/x509.h> +#include <openssl/pem.h> + +#include "../testutil.h" + +X509 *load_cert_pem(const char *file, OSSL_LIB_CTX *libctx) +{ + X509 *cert = NULL; + BIO *bio = NULL; + + if (!TEST_ptr(file) || !TEST_ptr(bio = BIO_new(BIO_s_file()))) + return NULL; + if (TEST_int_gt(BIO_read_filename(bio, file), 0) + && TEST_ptr(cert = X509_new_ex(libctx, NULL))) + (void)TEST_ptr(cert = PEM_read_bio_X509(bio, &cert, NULL, NULL)); + + BIO_free(bio); + return cert; +} + +STACK_OF(X509) *load_certs_pem(const char *file) +{ + STACK_OF(X509) *certs; + BIO *bio; + X509 *x; + + if (!TEST_ptr(file) || (bio = BIO_new_file(file, "r")) == NULL) + return NULL; + + certs = sk_X509_new_null(); + if (certs == NULL) { + BIO_free(bio); + return NULL; + } + + ERR_set_mark(); + do { + x = PEM_read_bio_X509(bio, NULL, 0, NULL); + if (x != NULL && !sk_X509_push(certs, x)) { + sk_X509_pop_free(certs, X509_free); + BIO_free(bio); + return NULL; + } else if (x == NULL) { + /* + * We probably just ran out of certs, so ignore any errors + * generated + */ + ERR_pop_to_mark(); + } + } while (x != NULL); + + BIO_free(bio); + + return certs; +} + +EVP_PKEY *load_pkey_pem(const char *file, OSSL_LIB_CTX *libctx) +{ + EVP_PKEY *key = NULL; + BIO *bio = NULL; + + if (!TEST_ptr(file) || !TEST_ptr(bio = BIO_new(BIO_s_file()))) + return NULL; + if (TEST_int_gt(BIO_read_filename(bio, file), 0)) { + unsigned long err = ERR_peek_error(); + + if (TEST_ptr(key = PEM_read_bio_PrivateKey_ex(bio, NULL, NULL, NULL, + libctx, NULL)) + && err != ERR_peek_error()) { + TEST_info("Spurious error from reading PEM"); + EVP_PKEY_free(key); + key = NULL; + } + } + + BIO_free(bio); + return key; +} + +X509_REQ *load_csr_der(const char *file, OSSL_LIB_CTX *libctx) +{ + X509_REQ *csr = NULL; + BIO *bio = NULL; + + if (!TEST_ptr(file) || !TEST_ptr(bio = BIO_new_file(file, "rb"))) + return NULL; + + csr = X509_REQ_new_ex(libctx, NULL); + if (TEST_ptr(csr)) + (void)TEST_ptr(d2i_X509_REQ_bio(bio, &csr)); + BIO_free(bio); + return csr; +} diff --git a/test/testutil/main.c b/test/testutil/main.c new file mode 100644 index 000000000000..32e32d832872 --- /dev/null +++ b/test/testutil/main.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016-2023 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 "../testutil.h" +#include "output.h" +#include "tu_local.h" + + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + int setup_res; + + test_open_streams(); + + if (!global_init()) { + test_printf_stderr("Global init failed - aborting\n"); + return ret; + } + + if (!setup_test_framework(argc, argv)) + goto end; + + if ((setup_res = setup_tests()) > 0) { + ret = run_tests(argv[0]); + cleanup_tests(); + opt_check_usage(); + } else if (setup_res == 0) { + opt_help(test_get_options()); + } +end: + ret = pulldown_test_framework(ret); + test_close_streams(); + return ret; +} diff --git a/test/testutil/options.c b/test/testutil/options.c new file mode 100644 index 000000000000..35dfa320c48b --- /dev/null +++ b/test/testutil/options.c @@ -0,0 +1,79 @@ +/* + * Copyright 2018-2020 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 "../testutil.h" +#include "internal/nelem.h" +#include "tu_local.h" +#include "output.h" + + +static int used[100] = { 0 }; + +int test_skip_common_options(void) +{ + OPTION_CHOICE_DEFAULT o; + + while ((o = (OPTION_CHOICE_DEFAULT)opt_next()) != OPT_EOF) { + switch (o) { + case OPT_TEST_CASES: + break; + default: + case OPT_ERR: + return 0; + } + } + return 1; +} + +size_t test_get_argument_count(void) +{ + return opt_num_rest(); +} + +char *test_get_argument(size_t n) +{ + char **argv = opt_rest(); + + OPENSSL_assert(n < sizeof(used)); + if ((int)n >= opt_num_rest() || argv == NULL) + return NULL; + used[n] = 1; + return argv[n]; +} + +void opt_check_usage(void) +{ + int i; + char **argv = opt_rest(); + int n, arg_count = opt_num_rest(); + + if (arg_count > (int)OSSL_NELEM(used)) + n = (int)OSSL_NELEM(used); + else + n = arg_count; + for (i = 0; i < n; i++) { + if (used[i] == 0) + test_printf_stderr("Warning ignored command-line argument %d: %s\n", + i, argv[i]); + } + if (i < arg_count) + test_printf_stderr("Warning arguments %d and later unchecked\n", i); +} + +int opt_printf_stderr(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = test_vprintf_stderr(fmt, ap); + va_end(ap); + return ret; +} + diff --git a/test/testutil/output.c b/test/testutil/output.c new file mode 100644 index 000000000000..e9446a644997 --- /dev/null +++ b/test/testutil/output.c @@ -0,0 +1,58 @@ +/* + * Copyright 2017-2020 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 "output.h" + +int test_printf_stdout(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = test_vprintf_stdout(fmt, ap); + va_end(ap); + + return ret; +} + +int test_printf_stderr(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = test_vprintf_stderr(fmt, ap); + va_end(ap); + + return ret; +} + +int test_printf_tapout(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = test_vprintf_tapout(fmt, ap); + va_end(ap); + + return ret; +} + +int test_printf_taperr(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = test_vprintf_taperr(fmt, ap); + va_end(ap); + + return ret; +} diff --git a/test/testutil/output.h b/test/testutil/output.h new file mode 100644 index 000000000000..6fbad6f5bd0a --- /dev/null +++ b/test/testutil/output.h @@ -0,0 +1,68 @@ +/* + * Copyright 2014-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 + */ + +#ifndef OSSL_TESTUTIL_OUTPUT_H +# define OSSL_TESTUTIL_OUTPUT_H + +# include <stdarg.h> + +# define ossl_test__attr__(x) +# if defined(__GNUC__) && defined(__STDC_VERSION__) \ + && !defined(__MINGW32__) && !defined(__MINGW64__) \ + && !defined(__APPLE__) + /* + * Because we support the 'z' modifier, which made its appearance in C99, + * we can't use __attribute__ with pre C99 dialects. + */ +# if __STDC_VERSION__ >= 199901L +# undef ossl_test__attr__ +# define ossl_test__attr__ __attribute__ +# if __GNUC__*10 + __GNUC_MINOR__ >= 44 +# define ossl_test__printf__ __gnu_printf__ +# else +# define ossl_test__printf__ __printf__ +# endif +# endif +# endif +/* + * The basic I/O functions used internally by the test framework. These + * can be overridden when needed. Note that if one is, then all must be. + */ +void test_open_streams(void); +void test_close_streams(void); +void test_adjust_streams_tap_level(int level); +/* The following ALL return the number of characters written */ +int test_vprintf_stdout(const char *fmt, va_list ap) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); +int test_vprintf_tapout(const char *fmt, va_list ap) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); +int test_vprintf_stderr(const char *fmt, va_list ap) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); +int test_vprintf_taperr(const char *fmt, va_list ap) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); +/* These return failure or success */ +int test_flush_stdout(void); +int test_flush_tapout(void); +int test_flush_stderr(void); +int test_flush_taperr(void); + +/* Commodity functions. There's no need to override these */ +int test_printf_stdout(const char *fmt, ...) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); +int test_printf_tapout(const char *fmt, ...) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); +int test_printf_stderr(const char *fmt, ...) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); +int test_printf_taperr(const char *fmt, ...) + ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); + +# undef ossl_test__printf__ +# undef ossl_test__attr__ + +#endif /* OSSL_TESTUTIL_OUTPUT_H */ diff --git a/test/testutil/provider.c b/test/testutil/provider.c new file mode 100644 index 000000000000..79ae13b42a1f --- /dev/null +++ b/test/testutil/provider.c @@ -0,0 +1,242 @@ +/* + * Copyright 2018-2023 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 "../testutil.h" +#include <ctype.h> +#include <openssl/provider.h> +#include <openssl/core_names.h> +#include <string.h> + +int test_get_libctx(OSSL_LIB_CTX **libctx, OSSL_PROVIDER **default_null_prov, + const char *config_file, + OSSL_PROVIDER **provider, const char *module_name) +{ + OSSL_LIB_CTX *new_libctx = NULL; + + if (libctx != NULL) { + if ((new_libctx = *libctx = OSSL_LIB_CTX_new()) == NULL) { + opt_printf_stderr("Failed to create libctx\n"); + goto err; + } + } + + if (default_null_prov != NULL + && (*default_null_prov = OSSL_PROVIDER_load(NULL, "null")) == NULL) { + opt_printf_stderr("Failed to load null provider into default libctx\n"); + goto err; + } + + if (config_file != NULL + && !OSSL_LIB_CTX_load_config(new_libctx, config_file)) { + opt_printf_stderr("Error loading config from file %s\n", config_file); + goto err; + } + + if (provider != NULL && module_name != NULL + && (*provider = OSSL_PROVIDER_load(new_libctx, module_name)) == NULL) { + opt_printf_stderr("Failed to load provider %s\n", module_name); + goto err; + } + return 1; + + err: + ERR_print_errors_fp(stderr); + return 0; +} + +int test_arg_libctx(OSSL_LIB_CTX **libctx, OSSL_PROVIDER **default_null_prov, + OSSL_PROVIDER **provider, int argn, const char *usage) +{ + const char *module_name; + + if (!TEST_ptr(module_name = test_get_argument(argn))) { + TEST_error("usage: <prog> %s", usage); + return 0; + } + if (strcmp(module_name, "none") == 0) + return 1; + return test_get_libctx(libctx, default_null_prov, + test_get_argument(argn + 1), provider, module_name); +} + +typedef struct { + int major, minor, patch; +} FIPS_VERSION; + +/* + * Query the FIPS provider to determine it's version number. + * Returns 1 if the version is retrieved correctly, 0 if the FIPS provider isn't + * loaded and -1 on error. + */ +static int fips_provider_version(OSSL_LIB_CTX *libctx, FIPS_VERSION *vers) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + OSSL_PROVIDER *fips_prov; + char *vs; + + if (!OSSL_PROVIDER_available(libctx, "fips")) + return 0; + *params = OSSL_PARAM_construct_utf8_ptr(OSSL_PROV_PARAM_VERSION, &vs, 0); + if ((fips_prov = OSSL_PROVIDER_load(libctx, "fips")) == NULL) + return -1; + if (!OSSL_PROVIDER_get_params(fips_prov, params) + || sscanf(vs, "%d.%d.%d", &vers->major, &vers->minor, &vers->patch) != 3) + goto err; + if (!OSSL_PROVIDER_unload(fips_prov)) + return -1; + return 1; + err: + OSSL_PROVIDER_unload(fips_prov); + return -1; +} + +int fips_provider_version_eq(OSSL_LIB_CTX *libctx, int major, int minor, int patch) +{ + FIPS_VERSION prov; + int res; + + if ((res = fips_provider_version(libctx, &prov)) <= 0) + return res == 0; + return major == prov.major && minor == prov.minor && patch == prov.patch; +} + +int fips_provider_version_ne(OSSL_LIB_CTX *libctx, int major, int minor, int patch) +{ + FIPS_VERSION prov; + int res; + + if ((res = fips_provider_version(libctx, &prov)) <= 0) + return res == 0; + return major != prov.major || minor != prov.minor || patch != prov.patch; +} + +int fips_provider_version_le(OSSL_LIB_CTX *libctx, int major, int minor, int patch) +{ + FIPS_VERSION prov; + int res; + + if ((res = fips_provider_version(libctx, &prov)) <= 0) + return res == 0; + return prov.major < major + || (prov.major == major + && (prov.minor < minor + || (prov.minor == minor && prov.patch <= patch))); +} + +int fips_provider_version_lt(OSSL_LIB_CTX *libctx, int major, int minor, int patch) +{ + FIPS_VERSION prov; + int res; + + if ((res = fips_provider_version(libctx, &prov)) <= 0) + return res == 0; + return prov.major < major + || (prov.major == major + && (prov.minor < minor + || (prov.minor == minor && prov.patch < patch))); +} + +int fips_provider_version_gt(OSSL_LIB_CTX *libctx, int major, int minor, int patch) +{ + FIPS_VERSION prov; + int res; + + if ((res = fips_provider_version(libctx, &prov)) <= 0) + return res == 0; + return prov.major > major + || (prov.major == major + && (prov.minor > minor + || (prov.minor == minor && prov.patch > patch))); +} + +int fips_provider_version_ge(OSSL_LIB_CTX *libctx, int major, int minor, int patch) +{ + FIPS_VERSION prov; + int res; + + if ((res = fips_provider_version(libctx, &prov)) <= 0) + return res == 0; + return prov.major > major + || (prov.major == major + && (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_LT, MODE_GT, MODE_GE + } mode; + + while (*versions != '\0') { + for (; isspace((unsigned char)(*versions)); versions++) + continue; + if (*versions == '\0') + break; + for (p = versions; *versions != '\0' && !isspace((unsigned char)(*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 == '>' && p[1] == '=') { + mode = MODE_GE; + p += 2; + } else if (*p == '<') { + mode = MODE_LT; + p++; + } else if (*p == '>') { + mode = MODE_GT; + p++; + } else if (isdigit((unsigned char)*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_LT: + r = fips_provider_version_lt(libctx, major, minor, patch); + break; + case MODE_GT: + r = fips_provider_version_gt(libctx, major, minor, patch); + break; + case MODE_GE: + r = fips_provider_version_ge(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; +} diff --git a/test/testutil/random.c b/test/testutil/random.c new file mode 100644 index 000000000000..45d0bb5f0589 --- /dev/null +++ b/test/testutil/random.c @@ -0,0 +1,40 @@ +/* + * Copyright 2019 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 "../testutil.h" + +/* + * This is an implementation of the algorithm used by the GNU C library's + * random(3) pseudorandom number generator as described: + * https://www.mscs.dal.ca/~selinger/random/ + */ +static uint32_t test_random_state[31]; + +uint32_t test_random(void) { + static unsigned int pos = 3; + + if (pos == 31) + pos = 0; + test_random_state[pos] += test_random_state[(pos + 28) % 31]; + return test_random_state[pos++] / 2; +} + +void test_random_seed(uint32_t sd) { + int i; + int32_t s; + const unsigned int mod = (1u << 31) - 1; + + test_random_state[0] = sd; + for (i = 1; i < 31; i++) { + s = (int32_t)test_random_state[i - 1]; + test_random_state[i] = (uint32_t)((16807 * (int64_t)s) % mod); + } + for (i = 34; i < 344; i++) + test_random(); +} diff --git a/test/testutil/stanza.c b/test/testutil/stanza.c new file mode 100644 index 000000000000..ba62f84517f3 --- /dev/null +++ b/test/testutil/stanza.c @@ -0,0 +1,159 @@ +/* + * Copyright 2017 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 <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "internal/nelem.h" +#include "../testutil.h" +#include "tu_local.h" + +int test_start_file(STANZA *s, const char *testfile) +{ + TEST_info("Reading %s", testfile); + set_test_title(testfile); + memset(s, 0, sizeof(*s)); + if (!TEST_ptr(s->fp = BIO_new_file(testfile, "r"))) + return 0; + s->test_file = testfile; + return 1; +} + +int test_end_file(STANZA *s) +{ + TEST_info("Completed %d tests with %d errors and %d skipped", + s->numtests, s->errors, s->numskip); + BIO_free(s->fp); + return 1; +} + +/* + * Read a PEM block. Return 1 if okay, 0 on error. + */ +static int read_key(STANZA *s) +{ + char tmpbuf[128]; + + if (s->key == NULL) { + if (!TEST_ptr(s->key = BIO_new(BIO_s_mem()))) + return 0; + } else if (!TEST_int_gt(BIO_reset(s->key), 0)) { + return 0; + } + + /* Read to PEM end line and place content in memory BIO */ + while (BIO_gets(s->fp, tmpbuf, sizeof(tmpbuf))) { + s->curr++; + if (!TEST_int_gt(BIO_puts(s->key, tmpbuf), 0)) + return 0; + if (strncmp(tmpbuf, "-----END", 8) == 0) + return 1; + } + TEST_error("Can't find key end"); + return 0; +} + + +/* + * Delete leading and trailing spaces from a string + */ +static char *strip_spaces(char *p) +{ + char *q; + + /* Skip over leading spaces */ + while (*p && isspace((unsigned char)*p)) + p++; + if (*p == '\0') + return NULL; + + for (q = p + strlen(p) - 1; q != p && isspace((unsigned char)*q); ) + *q-- = '\0'; + return *p ? p : NULL; +} + +/* + * Read next test stanza; return 1 if found, 0 on EOF or error. + */ +int test_readstanza(STANZA *s) +{ + PAIR *pp = s->pairs; + char *p, *equals, *key; + const char *value; + + for (s->numpairs = 0; BIO_gets(s->fp, s->buff, sizeof(s->buff)); ) { + s->curr++; + if (!TEST_ptr(p = strchr(s->buff, '\n'))) { + TEST_info("Line %d too long", s->curr); + return 0; + } + *p = '\0'; + + /* Blank line marks end of tests. */ + if (s->buff[0] == '\0') + break; + + /* Lines starting with a pound sign are ignored. */ + if (s->buff[0] == '#') + continue; + + /* Parse into key=value */ + if (!TEST_ptr(equals = strchr(s->buff, '='))) { + TEST_info("Missing = at line %d\n", s->curr); + return 0; + } + *equals++ = '\0'; + if (!TEST_ptr(key = strip_spaces(s->buff))) { + TEST_info("Empty field at line %d\n", s->curr); + return 0; + } + if ((value = strip_spaces(equals)) == NULL) + value = ""; + + if (strcmp(key, "Title") == 0) { + TEST_info("Starting \"%s\" tests at line %d", value, s->curr); + continue; + } + + if (s->numpairs == 0) + s->start = s->curr; + + if (strcmp(key, "PrivateKey") == 0) { + if (!read_key(s)) + return 0; + } + if (strcmp(key, "PublicKey") == 0) { + if (!read_key(s)) + return 0; + } + + if (!TEST_int_lt(s->numpairs++, TESTMAXPAIRS) + || !TEST_ptr(pp->key = OPENSSL_strdup(key)) + || !TEST_ptr(pp->value = OPENSSL_strdup(value))) + return 0; + pp++; + } + + /* If we read anything, return ok. */ + return 1; +} + +void test_clearstanza(STANZA *s) +{ + PAIR *pp = s->pairs; + int i = s->numpairs; + + for ( ; --i >= 0; pp++) { + OPENSSL_free(pp->key); + OPENSSL_free(pp->value); + } + s->numpairs = 0; +} diff --git a/test/testutil/test_cleanup.c b/test/testutil/test_cleanup.c new file mode 100644 index 000000000000..669c54ce5abb --- /dev/null +++ b/test/testutil/test_cleanup.c @@ -0,0 +1,14 @@ +/* + * Copyright 2017 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 "../testutil.h" + +void cleanup_tests(void) +{ +} diff --git a/test/testutil/test_options.c b/test/testutil/test_options.c new file mode 100644 index 000000000000..51d880399bf2 --- /dev/null +++ b/test/testutil/test_options.c @@ -0,0 +1,21 @@ +/* + * Copyright 2018-2020 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 "../testutil.h" +#include "tu_local.h" + +/* An overridable list of command line options */ +const OPTIONS *test_get_options(void) +{ + static const OPTIONS default_options[] = { + OPT_TEST_OPTIONS_DEFAULT_USAGE, + { NULL } + }; + return default_options; +} diff --git a/test/testutil/tests.c b/test/testutil/tests.c new file mode 100644 index 000000000000..ef7e224cd119 --- /dev/null +++ b/test/testutil/tests.c @@ -0,0 +1,472 @@ +/* + * Copyright 2017-2021 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 "../testutil.h" +#include "output.h" +#include "tu_local.h" + +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include "internal/nelem.h" +#include <openssl/asn1.h> + +/* + * Output a failed test first line. + * All items are optional are generally not preinted if passed as NULL. + * The special cases are for prefix where "ERROR" is assumed and for left + * and right where a non-failure message is produced if either is NULL. + */ +void test_fail_message_prefix(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op) +{ + test_printf_stderr("%s: ", prefix != NULL ? prefix : "ERROR"); + if (type) + test_printf_stderr("(%s) ", type); + if (op != NULL) { + if (left != NULL && right != NULL) + test_printf_stderr("'%s %s %s' failed", left, op, right); + else + test_printf_stderr("'%s'", op); + } + if (file != NULL) { + test_printf_stderr(" @ %s:%d", file, line); + } + test_printf_stderr("\n"); +} + +/* + * A common routine to output test failure messages. Generally this should not + * be called directly, rather it should be called by the following functions. + * + * |desc| is a printf formatted description with arguments |args| that is + * supplied by the user and |desc| can be NULL. |type| is the data type + * that was tested (int, char, ptr, ...). |fmt| is a system provided + * printf format with following arguments that spell out the failure + * details i.e. the actual values compared and the operator used. + * + * The typical use for this is from an utility test function: + * + * int test6(const char *file, int line, int n) { + * if (n != 6) { + * test_fail_message(1, file, line, "int", "value %d is not %d", n, 6); + * return 0; + * } + * return 1; + * } + * + * calling test6(3, "oops") will return 0 and produce out along the lines of: + * FAIL oops: (int) value 3 is not 6\n + */ +static void test_fail_message(const char *prefix, const char *file, int line, + const char *type, const char *left, + const char *right, const char *op, + const char *fmt, ...) + PRINTF_FORMAT(8, 9); + +static void test_fail_message_va(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const char *fmt, va_list ap) +{ + test_fail_message_prefix(prefix, file, line, type, left, right, op); + if (fmt != NULL) { + test_vprintf_stderr(fmt, ap); + test_printf_stderr("\n"); + } + test_flush_stderr(); +} + +static void test_fail_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + test_fail_message_va(prefix, file, line, type, left, right, op, fmt, ap); + va_end(ap); +} + +void test_info_c90(const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va("INFO", NULL, -1, NULL, NULL, NULL, NULL, desc, ap); + va_end(ap); +} + +void test_info(const char *file, int line, const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va("INFO", file, line, NULL, NULL, NULL, NULL, desc, ap); + va_end(ap); +} + +void test_error_c90(const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va(NULL, NULL, -1, NULL, NULL, NULL, NULL, desc, ap); + va_end(ap); + test_printf_stderr("\n"); +} + +void test_error(const char *file, int line, const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va(NULL, file, line, NULL, NULL, NULL, NULL, desc, ap); + va_end(ap); + test_printf_stderr("\n"); +} + +void test_perror(const char *s) +{ + /* + * Using openssl_strerror_r causes linking issues since it isn't + * exported from libcrypto.so + */ + TEST_error("%s: %s", s, strerror(errno)); +} + +void test_note(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start(ap, fmt); + test_vprintf_stderr(fmt, ap); + va_end(ap); + test_printf_stderr("\n"); + } + test_flush_stderr(); +} + + +int test_skip(const char *file, int line, const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va("SKIP", file, line, NULL, NULL, NULL, NULL, desc, ap); + va_end(ap); + return TEST_SKIP_CODE; +} + +int test_skip_c90(const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va("SKIP", NULL, -1, NULL, NULL, NULL, NULL, desc, ap); + va_end(ap); + test_printf_stderr("\n"); + return TEST_SKIP_CODE; +} + + +void test_openssl_errors(void) +{ + ERR_print_errors_cb(openssl_error_cb, NULL); + ERR_clear_error(); +} + +/* + * Define some comparisons between pairs of various types. + * These functions return 1 if the test is true. + * Otherwise, they return 0 and pretty-print diagnostics. + * + * In each case the functions produced are: + * int test_name_eq(const type t1, const type t2, const char *desc, ...); + * int test_name_ne(const type t1, const type t2, const char *desc, ...); + * int test_name_lt(const type t1, const type t2, const char *desc, ...); + * int test_name_le(const type t1, const type t2, const char *desc, ...); + * int test_name_gt(const type t1, const type t2, const char *desc, ...); + * int test_name_ge(const type t1, const type t2, const char *desc, ...); + * + * The t1 and t2 arguments are to be compared for equality, inequality, + * less than, less than or equal to, greater than and greater than or + * equal to respectively. If the specified condition holds, the functions + * return 1. If the condition does not hold, the functions print a diagnostic + * message and return 0. + * + * The desc argument is a printf format string followed by its arguments and + * this is included in the output if the condition being tested for is false. + */ +#define DEFINE_COMPARISON(type, name, opname, op, fmt) \ + int test_ ## name ## _ ## opname(const char *file, int line, \ + const char *s1, const char *s2, \ + const type t1, const type t2) \ + { \ + if (t1 op t2) \ + return 1; \ + test_fail_message(NULL, file, line, #type, s1, s2, #op, \ + "[" fmt "] compared to [" fmt "]", \ + t1, t2); \ + return 0; \ + } + +#define DEFINE_COMPARISONS(type, name, fmt) \ + DEFINE_COMPARISON(type, name, eq, ==, fmt) \ + DEFINE_COMPARISON(type, name, ne, !=, fmt) \ + DEFINE_COMPARISON(type, name, lt, <, fmt) \ + DEFINE_COMPARISON(type, name, le, <=, fmt) \ + DEFINE_COMPARISON(type, name, gt, >, fmt) \ + DEFINE_COMPARISON(type, name, ge, >=, fmt) + +DEFINE_COMPARISONS(int, int, "%d") +DEFINE_COMPARISONS(unsigned int, uint, "%u") +DEFINE_COMPARISONS(char, char, "%c") +DEFINE_COMPARISONS(unsigned char, uchar, "%u") +DEFINE_COMPARISONS(long, long, "%ld") +DEFINE_COMPARISONS(unsigned long, ulong, "%lu") +DEFINE_COMPARISONS(size_t, size_t, "%zu") +DEFINE_COMPARISONS(double, double, "%g") + +DEFINE_COMPARISON(void *, ptr, eq, ==, "%p") +DEFINE_COMPARISON(void *, ptr, ne, !=, "%p") + +int test_ptr_null(const char *file, int line, const char *s, const void *p) +{ + if (p == NULL) + return 1; + test_fail_message(NULL, file, line, "ptr", s, "NULL", "==", "%p", p); + return 0; +} + +int test_ptr(const char *file, int line, const char *s, const void *p) +{ + if (p != NULL) + return 1; + test_fail_message(NULL, file, line, "ptr", s, "NULL", "!=", "%p", p); + return 0; +} + +int test_true(const char *file, int line, const char *s, int b) +{ + if (b) + return 1; + test_fail_message(NULL, file, line, "bool", s, "true", "==", "false"); + return 0; +} + +int test_false(const char *file, int line, const char *s, int b) +{ + if (!b) + return 1; + test_fail_message(NULL, file, line, "bool", s, "false", "==", "true"); + return 0; +} + +int test_str_eq(const char *file, int line, const char *st1, const char *st2, + const char *s1, const char *s2) +{ + if (s1 == NULL && s2 == NULL) + return 1; + if (s1 == NULL || s2 == NULL || strcmp(s1, s2) != 0) { + test_fail_string_message(NULL, file, line, "string", st1, st2, "==", + s1, s1 == NULL ? 0 : strlen(s1), + s2, s2 == NULL ? 0 : strlen(s2)); + return 0; + } + return 1; +} + +int test_str_ne(const char *file, int line, const char *st1, const char *st2, + const char *s1, const char *s2) +{ + if ((s1 == NULL) ^ (s2 == NULL)) + return 1; + if (s1 == NULL || strcmp(s1, s2) == 0) { + test_fail_string_message(NULL, file, line, "string", st1, st2, "!=", + s1, s1 == NULL ? 0 : strlen(s1), + s2, s2 == NULL ? 0 : strlen(s2)); + return 0; + } + return 1; +} + +int test_strn_eq(const char *file, int line, const char *st1, const char *st2, + const char *s1, size_t n1, const char *s2, size_t n2) +{ + if (s1 == NULL && s2 == NULL) + return 1; + if (n1 != n2 || s1 == NULL || s2 == NULL || strncmp(s1, s2, n1) != 0) { + test_fail_string_message(NULL, file, line, "string", st1, st2, "==", + s1, s1 == NULL ? 0 : OPENSSL_strnlen(s1, n1), + s2, s2 == NULL ? 0 : OPENSSL_strnlen(s2, n2)); + return 0; + } + return 1; +} + +int test_strn_ne(const char *file, int line, const char *st1, const char *st2, + const char *s1, size_t n1, const char *s2, size_t n2) +{ + if ((s1 == NULL) ^ (s2 == NULL)) + return 1; + if (n1 != n2 || s1 == NULL || strncmp(s1, s2, n1) == 0) { + test_fail_string_message(NULL, file, line, "string", st1, st2, "!=", + s1, s1 == NULL ? 0 : OPENSSL_strnlen(s1, n1), + s2, s2 == NULL ? 0 : OPENSSL_strnlen(s2, n2)); + return 0; + } + return 1; +} + +int test_mem_eq(const char *file, int line, const char *st1, const char *st2, + const void *s1, size_t n1, const void *s2, size_t n2) +{ + if (s1 == NULL && s2 == NULL) + return 1; + if (n1 != n2 || s1 == NULL || s2 == NULL || memcmp(s1, s2, n1) != 0) { + test_fail_memory_message(NULL, file, line, "memory", st1, st2, "==", + s1, n1, s2, n2); + return 0; + } + return 1; +} + +int test_mem_ne(const char *file, int line, const char *st1, const char *st2, + const void *s1, size_t n1, const void *s2, size_t n2) +{ + if ((s1 == NULL) ^ (s2 == NULL)) + return 1; + if (n1 != n2) + return 1; + if (s1 == NULL || memcmp(s1, s2, n1) == 0) { + test_fail_memory_message(NULL, file, line, "memory", st1, st2, "!=", + s1, n1, s2, n2); + return 0; + } + return 1; +} + +#define DEFINE_BN_COMPARISONS(opname, op, zero_cond) \ + int test_BN_ ## opname(const char *file, int line, \ + const char *s1, const char *s2, \ + const BIGNUM *t1, const BIGNUM *t2) \ + { \ + if (BN_cmp(t1, t2) op 0) \ + return 1; \ + test_fail_bignum_message(NULL, file, line, "BIGNUM", s1, s2, \ + #op, t1, t2); \ + return 0; \ + } \ + int test_BN_ ## opname ## _zero(const char *file, int line, \ + const char *s, const BIGNUM *a) \ + { \ + if (a != NULL &&(zero_cond)) \ + return 1; \ + test_fail_bignum_mono_message(NULL, file, line, "BIGNUM", \ + s, "0", #op, a); \ + return 0; \ + } + +DEFINE_BN_COMPARISONS(eq, ==, BN_is_zero(a)) +DEFINE_BN_COMPARISONS(ne, !=, !BN_is_zero(a)) +DEFINE_BN_COMPARISONS(gt, >, !BN_is_negative(a) && !BN_is_zero(a)) +DEFINE_BN_COMPARISONS(ge, >=, !BN_is_negative(a) || BN_is_zero(a)) +DEFINE_BN_COMPARISONS(lt, <, BN_is_negative(a) && !BN_is_zero(a)) +DEFINE_BN_COMPARISONS(le, <=, BN_is_negative(a) || BN_is_zero(a)) + +int test_BN_eq_one(const char *file, int line, const char *s, const BIGNUM *a) +{ + if (a != NULL && BN_is_one(a)) + return 1; + test_fail_bignum_mono_message(NULL, file, line, "BIGNUM", s, "1", "==", a); + return 0; +} + +int test_BN_odd(const char *file, int line, const char *s, const BIGNUM *a) +{ + if (a != NULL && BN_is_odd(a)) + return 1; + test_fail_bignum_mono_message(NULL, file, line, "BIGNUM", "ODD(", ")", s, a); + return 0; +} + +int test_BN_even(const char *file, int line, const char *s, const BIGNUM *a) +{ + if (a != NULL && !BN_is_odd(a)) + return 1; + test_fail_bignum_mono_message(NULL, file, line, "BIGNUM", "EVEN(", ")", s, + a); + return 0; +} + +int test_BN_eq_word(const char *file, int line, const char *bns, const char *ws, + const BIGNUM *a, BN_ULONG w) +{ + BIGNUM *bw; + + if (a != NULL && BN_is_word(a, w)) + return 1; + if ((bw = BN_new()) != NULL) + BN_set_word(bw, w); + test_fail_bignum_message(NULL, file, line, "BIGNUM", bns, ws, "==", a, bw); + BN_free(bw); + return 0; +} + +int test_BN_abs_eq_word(const char *file, int line, const char *bns, + const char *ws, const BIGNUM *a, BN_ULONG w) +{ + BIGNUM *bw, *aa; + + if (a != NULL && BN_abs_is_word(a, w)) + return 1; + if ((aa = BN_dup(a)) != NULL) + BN_set_negative(aa, 0); + if ((bw = BN_new()) != NULL) + BN_set_word(bw, w); + test_fail_bignum_message(NULL, file, line, "BIGNUM", bns, ws, "abs==", + aa, bw); + BN_free(bw); + BN_free(aa); + return 0; +} + +static const char *print_time(const ASN1_TIME *t) +{ + return t == NULL ? "<null>" : (const char *)ASN1_STRING_get0_data(t); +} + +#define DEFINE_TIME_T_COMPARISON(opname, op) \ + int test_time_t_ ## opname(const char *file, int line, \ + const char *s1, const char *s2, \ + const time_t t1, const time_t t2) \ + { \ + ASN1_TIME *at1 = ASN1_TIME_set(NULL, t1); \ + ASN1_TIME *at2 = ASN1_TIME_set(NULL, t2); \ + int r = at1 != NULL && at2 != NULL \ + && ASN1_TIME_compare(at1, at2) op 0; \ + if (!r) \ + test_fail_message(NULL, file, line, "time_t", s1, s2, #op, \ + "[%s] compared to [%s]", \ + print_time(at1), print_time(at2)); \ + ASN1_STRING_free(at1); \ + ASN1_STRING_free(at2); \ + return r; \ + } +DEFINE_TIME_T_COMPARISON(eq, ==) +DEFINE_TIME_T_COMPARISON(ne, !=) +DEFINE_TIME_T_COMPARISON(gt, >) +DEFINE_TIME_T_COMPARISON(ge, >=) +DEFINE_TIME_T_COMPARISON(lt, <) +DEFINE_TIME_T_COMPARISON(le, <=) diff --git a/test/testutil/testutil_init.c b/test/testutil/testutil_init.c new file mode 100644 index 000000000000..87013694c29e --- /dev/null +++ b/test/testutil/testutil_init.c @@ -0,0 +1,145 @@ +/* + * Copyright 2017-2021 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 <string.h> +#include <openssl/opensslconf.h> +#include <openssl/trace.h> +#include "apps.h" +#include "../testutil.h" + +#ifndef OPENSSL_NO_TRACE +typedef struct tracedata_st { + BIO *bio; + unsigned int ingroup:1; +} tracedata; + +static size_t internal_trace_cb(const char *buf, size_t cnt, + int category, int cmd, void *vdata) +{ + int ret = 0; + tracedata *trace_data = vdata; + char buffer[256], *hex; + CRYPTO_THREAD_ID tid; + + switch (cmd) { + case OSSL_TRACE_CTRL_BEGIN: + trace_data->ingroup = 1; + + tid = CRYPTO_THREAD_get_current_id(); + hex = OPENSSL_buf2hexstr((const unsigned char *)&tid, sizeof(tid)); + BIO_snprintf(buffer, sizeof(buffer), "TRACE[%s]:%s: ", + hex, OSSL_trace_get_category_name(category)); + OPENSSL_free(hex); + BIO_set_prefix(trace_data->bio, buffer); + break; + case OSSL_TRACE_CTRL_WRITE: + ret = BIO_write(trace_data->bio, buf, cnt); + break; + case OSSL_TRACE_CTRL_END: + trace_data->ingroup = 0; + + BIO_set_prefix(trace_data->bio, NULL); + break; + } + + return ret < 0 ? 0 : ret; +} + +DEFINE_STACK_OF(tracedata) +static STACK_OF(tracedata) *trace_data_stack; + +static void tracedata_free(tracedata *data) +{ + BIO_free_all(data->bio); + OPENSSL_free(data); +} + +static STACK_OF(tracedata) *trace_data_stack; + +static void cleanup_trace(void) +{ + sk_tracedata_pop_free(trace_data_stack, tracedata_free); +} + +static void setup_trace_category(int category) +{ + BIO *channel; + tracedata *trace_data; + BIO *bio = NULL; + + if (OSSL_trace_enabled(category)) + return; + + bio = BIO_new(BIO_f_prefix()); + channel = BIO_push(bio, + BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT)); + trace_data = OPENSSL_zalloc(sizeof(*trace_data)); + + if (trace_data == NULL + || bio == NULL + || (trace_data->bio = channel) == NULL + || OSSL_trace_set_callback(category, internal_trace_cb, + trace_data) == 0 + || sk_tracedata_push(trace_data_stack, trace_data) == 0) { + + fprintf(stderr, + "warning: unable to setup trace callback for category '%s'.\n", + OSSL_trace_get_category_name(category)); + + OSSL_trace_set_callback(category, NULL, NULL); + BIO_free_all(channel); + } +} + +static void setup_trace(const char *str) +{ + char *val; + + /* + * We add this handler as early as possible to ensure it's executed + * as late as possible, i.e. after the TRACE code has done its cleanup + * (which happens last in OPENSSL_cleanup). + */ + atexit(cleanup_trace); + + trace_data_stack = sk_tracedata_new_null(); + val = OPENSSL_strdup(str); + + if (val != NULL) { + char *valp = val; + char *item; + + for (valp = val; (item = strtok(valp, ",")) != NULL; valp = NULL) { + int category = OSSL_trace_get_category_num(item); + + if (category == OSSL_TRACE_CATEGORY_ALL) { + while (++category < OSSL_TRACE_CATEGORY_NUM) + setup_trace_category(category); + break; + } else if (category > 0) { + setup_trace_category(category); + } else { + fprintf(stderr, + "warning: unknown trace category: '%s'.\n", item); + } + } + } + + OPENSSL_free(val); +} +#endif /* OPENSSL_NO_TRACE */ + +int global_init(void) +{ +#ifndef OPENSSL_NO_TRACE + setup_trace(getenv("OPENSSL_TRACE")); +#endif + + return 1; +} diff --git a/test/testutil/tu_local.h b/test/testutil/tu_local.h new file mode 100644 index 000000000000..ede4546948ff --- /dev/null +++ b/test/testutil/tu_local.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017-2018 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 <stdlib.h> /* size_t */ +#include <openssl/bn.h> +#include <openssl/bio.h> +#include "../testutil.h" + +#define TEST_SKIP_CODE 123 + +int subtest_level(void); +int openssl_error_cb(const char *str, size_t len, void *u); +const BIO_METHOD *BIO_f_tap(void); + +void test_fail_message_prefix(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op); + +void test_fail_string_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const char *m1, size_t l1, + const char *m2, size_t l2); + +void test_fail_bignum_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, + const BIGNUM *bn1, const BIGNUM *bn2); +void test_fail_bignum_mono_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, const BIGNUM *bn); + +void test_fail_memory_message(const char *prefix, const char *file, + int line, const char *type, + const char *left, const char *right, + const char *op, + const unsigned char *m1, size_t l1, + const unsigned char *m2, size_t l2); + +__owur int setup_test_framework(int argc, char *argv[]); +__owur int pulldown_test_framework(int ret); + +__owur int run_tests(const char *test_prog_name); +void set_test_title(const char *title); + +typedef enum OPTION_choice_default { + OPT_ERR = -1, + OPT_EOF = 0, + OPT_TEST_ENUM +} OPTION_CHOICE_DEFAULT; +void opt_check_usage(void); + |