diff --git a/README.md b/README.md index 038751f..5227a95 100644 --- a/README.md +++ b/README.md @@ -307,3 +307,25 @@ thread-count - number of threads ```sh ./evp_kdf -o evp_shared 10 ``` + +## evp_rand + +This CLI tool generates random data with HASH-DRGB. +Runs for 5 seconds and prints the average execution time per computation. + +Two modes of operation: +- evp_shared (default): Use EVP API and allow shared data between computations +- evp_isolated: Use EVP API and don't allow shared data between computations + +``` +Usage: evp_rand [-h] [-t] [-o operation] [-V] thread-count +-h - print this help output +-t - terse output +-o operation - mode of operation. One of [evp_isolated, evp_shared] (default: evp_shared) +-V - print version information and exit +thread-count - number of thread +``` + +```sh +./evp_rand -o evp_shared 10 +``` diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index d0d27ec..a71d58f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -218,6 +218,9 @@ target_link_libraries(evp_mac PRIVATE perf) add_executable(evp_kdf evp_kdf.c) target_link_libraries(evp_kdf PRIVATE perf) +add_executable(evp_rand evp_rand.c) +target_link_libraries(evp_rand PRIVATE perf) + ## Running tests # Options set(run_tests evp_fetch @@ -225,6 +228,7 @@ set(run_tests evp_fetch evp_cipher evp_mac evp_kdf + evp_rand evp_setpeer handshake newrawkey @@ -268,6 +272,9 @@ set(run_evp_mac_operations set(run_evp_kdf_operations evp_kdf "" "" "-o evp_shared" "-o evp_isolated" "-o deprecated_shared" "-o deprecated_isolated" CACHE STRING "Modes of operation for evp_kdf") +set(run_evp_rand_operations + evp_rand "" "" "-o evp_isolated" "-o evp_shared" + CACHE STRING "Modes of operation for evp_rand") set(run_evp_setpeer_keys evp_setpeer "-k" dh ec256 ec521 x25519 all CACHE STRING "Key types for evp_setpeer") @@ -311,6 +318,7 @@ set(run_opts run_evp_fetch_pqs run_evp_cipher_algorithms run_evp_mac_operations run_evp_kdf_operations + run_evp_rand_operations run_evp_setpeer_keys run_newrawkey_algos run_pkeyread_keys diff --git a/source/evp_rand.c b/source/evp_rand.c new file mode 100644 index 0000000..952978c --- /dev/null +++ b/source/evp_rand.c @@ -0,0 +1,226 @@ +/* + * Copyright 2026 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 + */ + +/* + * This CLI tool generates random data with HASH-DRGB. + * Runs for 5 seconds and prints the average execution time per computation. + */ + +#define OPENSSL_SUPPRESS_DEPRECATED + +#include +#include +#ifndef _WIN32 +# include +#else +# include "perflib/getopt.h" +#endif /* _WIN32 */ + +#include +#include +#include +#include "perflib/perflib.h" +#include "perflib/basename.h" + +#define RUN_TIME 5 +#define DATA_SIZE 64 + +static int threadcount; +static OSSL_TIME max_time; +static size_t *counts = NULL; +static int run_err = 0; + +typedef enum { + EVP_SHARED = 0, + EVP_ISOLATED, +} operation_type; + +static int evp_isolated() +{ + EVP_RAND *rand = EVP_RAND_fetch(NULL, "HASH-DRBG", NULL); + EVP_RAND_CTX *ctx = NULL; + unsigned char out[DATA_SIZE]; + int ret = 0; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_DIGEST, SN_sha512, 0), + OSSL_PARAM_construct_end(), + }; + + if (rand == NULL + || (ctx = EVP_RAND_CTX_new(rand, NULL)) == NULL + || !EVP_RAND_CTX_set_params(ctx, params) + || !EVP_RAND_generate(ctx, out, sizeof(out), 0, 0, NULL, 0)) + goto err; + + ret = 1; +err: + EVP_RAND_free(rand); + EVP_RAND_CTX_free(ctx); + + return ret; +} + +static void do_evp_isolated(size_t num) +{ + OSSL_TIME time; + size_t count = 0; + + do { + if (!evp_isolated()) { + run_err = 1; + return; + } + + count++; + time = ossl_time_now(); + } while (time.t < max_time.t); + + counts[num] = count; +} + +static void do_evp_shared(size_t num) +{ + OSSL_TIME time; + size_t count = 0; + EVP_RAND *rand = EVP_RAND_fetch(NULL, "HASH-DRBG", NULL); + EVP_RAND_CTX *ctx = NULL; + unsigned char out[DATA_SIZE]; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_DIGEST, SN_sha512, 0), + OSSL_PARAM_construct_end(), + }; + + if (rand == NULL + || (ctx = EVP_RAND_CTX_new(rand, NULL)) == NULL + || !EVP_RAND_CTX_set_params(ctx, params)) { + run_err = 1; + goto err; + } + + do { + if (!EVP_RAND_generate(ctx, out, sizeof(out), 0, 0, NULL, 0)) { + run_err = 1; + goto err; + } + + count++; + time = ossl_time_now(); + } while (time.t < max_time.t); + counts[num] = count; + +err: + EVP_RAND_free(rand); + EVP_RAND_CTX_free(ctx); +} + +static void print_help(FILE *file) +{ + fprintf(file, "Usage: evp_rand [-h] [-t] [-o operation] [-V] thread-count\n"); + fprintf(file, "-h - print this help output\n"); + fprintf(file, "-t - terse output\n"); + fprintf(file, "-o operation - mode of operation. One of [evp_isolated, evp_shared] (default: evp_shared)\n"); + fprintf(file, "-V - print version information and exit\n"); + fprintf(file, "thread-count - number of threads\n"); +} + +int main(int argc, char *argv[]) +{ + OSSL_TIME duration; + size_t total_count = 0; + double av; + int terse = 0, operation = EVP_SHARED; + int j, opt, rc = EXIT_FAILURE; + + while ((opt = getopt(argc, argv, "Vhto:")) != -1) { + switch (opt) { + case 't': + terse = 1; + break; + case 'o': + if (strcmp(optarg, "evp_isolated") == 0) { + operation = EVP_ISOLATED; + } else if (strcmp(optarg, "evp_shared") == 0) { + operation = EVP_SHARED; + } else { + fprintf(stderr, "Invalid operation"); + print_help(stderr); + goto err; + } + break; + case 'V': + perflib_print_version(basename(argv[0])); + return EXIT_SUCCESS; + case 'h': + print_help(stdout); + return EXIT_SUCCESS; + default: + print_help(stderr); + goto err; + } + } + + if (argc - optind != 1) { + fprintf(stderr, "Incorrect number of arguments\n"); + print_help(stderr); + goto err; + } + + threadcount = atoi(argv[optind]); + if (threadcount < 1) { + fprintf(stderr, "thread-count must be a positive integer\n"); + print_help(stderr); + goto err; + } + + counts = OPENSSL_zalloc(sizeof(size_t) * threadcount); + if (counts == NULL) { + fprintf(stderr, "Failed to create counts array\n"); + goto err; + } + + max_time = ossl_time_add(ossl_time_now(), ossl_seconds2time(RUN_TIME)); + + switch (operation) { + case EVP_SHARED: + run_err = !perflib_run_multi_thread_test(do_evp_shared, threadcount, &duration) || run_err; + break; + case EVP_ISOLATED: + run_err = !perflib_run_multi_thread_test(do_evp_isolated, threadcount, &duration) || run_err; + break; + default: + goto err; + } + + if (run_err) { + fprintf(stderr, "Error during test\n"); + goto err; + } + + for (j = 0; j < threadcount; j++) + total_count += counts[j]; + + /* + * Computation is pretty fast, running in only a few us. But ossl_time2us + * does integer division and so because the average us computed above is + * less than the value of OSSL_TIME_US, we wind up with truncation to zero + * in the math. Instead, manually do the division, casting our values as + * doubles so that we compute the proper time. + */ + av = (double)RUN_TIME * 1e6 * threadcount / total_count; + + if (terse) + printf("%lf\n", av); + else + printf("Average time per computation: %lfus\n", av); + + rc = EXIT_SUCCESS; +err: + OPENSSL_free(counts); + return rc; +}