diff options
Diffstat (limited to 'comm/third_party/libgcrypt/tests/bench-slope.c')
-rw-r--r-- | comm/third_party/libgcrypt/tests/bench-slope.c | 2349 |
1 files changed, 2349 insertions, 0 deletions
diff --git a/comm/third_party/libgcrypt/tests/bench-slope.c b/comm/third_party/libgcrypt/tests/bench-slope.c new file mode 100644 index 0000000000..c8647b6b98 --- /dev/null +++ b/comm/third_party/libgcrypt/tests/bench-slope.c @@ -0,0 +1,2349 @@ +/* bench-slope.c - for libgcrypt + * Copyright (C) 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi> + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser general Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <assert.h> +#include <time.h> + +#ifdef _GCRYPT_IN_LIBGCRYPT +# include "../src/gcrypt-int.h" +# include "../compat/libcompat.h" +#else +# include <gcrypt.h> +#endif + +#ifndef STR +#define STR(v) #v +#define STR2(v) STR(v) +#endif + +#define PGM "bench-slope" +#include "t-common.h" + +static int verbose; +static int csv_mode; +static int unaligned_mode; +static int num_measurement_repetitions; + +/* CPU Ghz value provided by user, allows constructing cycles/byte and other + results. */ +static double cpu_ghz = -1; + +/* Attempt to autodetect CPU Ghz. */ +static int auto_ghz; + +/* Whether we are running as part of the regression test suite. */ +static int in_regression_test; + +/* The name of the currently printed section. */ +static char *current_section_name; +/* The name of the currently printed algorithm. */ +static char *current_algo_name; +/* The name of the currently printed mode. */ +static char *current_mode_name; + + +/* Currently used CPU Ghz (either user input or auto-detected. */ +static double bench_ghz; + +/* Current accuracy of auto-detected CPU Ghz. */ +static double bench_ghz_diff; + + +/*************************************** Default parameters for measurements. */ + +/* Start at small buffer size, to get reasonable timer calibration for fast + * implementations (AES-NI etc). Sixteen selected to support the largest block + * size of current set cipher blocks. */ +#define BUF_START_SIZE 16 + +/* From ~0 to ~4kbytes give comparable results with results from academia + * (SUPERCOP). */ +#define BUF_END_SIZE (BUF_START_SIZE + 4096) + +/* With 128 byte steps, we get (4096)/64 = 64 data points. */ +#define BUF_STEP_SIZE 64 + +/* Number of repeated measurements at each data point. The median of these + * measurements is selected as data point further analysis. */ +#define NUM_MEASUREMENT_REPETITIONS 64 + +/* Target accuracy for auto-detected CPU Ghz. */ +#define AUTO_GHZ_TARGET_DIFF (5e-5) + +/**************************************************** High-resolution timers. */ + +/* This benchmarking module needs needs high resolution timer. */ +#undef NO_GET_NSEC_TIME +#if defined(_WIN32) +struct nsec_time +{ + LARGE_INTEGER perf_count; +}; + +static void +get_nsec_time (struct nsec_time *t) +{ + BOOL ok; + + ok = QueryPerformanceCounter (&t->perf_count); + assert (ok); +} + +static double +get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end) +{ + static double nsecs_per_count = 0.0; + double nsecs; + + if (nsecs_per_count == 0.0) + { + LARGE_INTEGER perf_freq; + BOOL ok; + + /* Get counts per second. */ + ok = QueryPerformanceFrequency (&perf_freq); + assert (ok); + + nsecs_per_count = 1.0 / perf_freq.QuadPart; + nsecs_per_count *= 1000000.0 * 1000.0; /* sec => nsec */ + + assert (nsecs_per_count > 0.0); + } + + nsecs = end->perf_count.QuadPart - start->perf_count.QuadPart; /* counts */ + nsecs *= nsecs_per_count; /* counts * (nsecs / count) => nsecs */ + + return nsecs; +} +#elif defined(HAVE_CLOCK_GETTIME) +struct nsec_time +{ + struct timespec ts; +}; + +static void +get_nsec_time (struct nsec_time *t) +{ + int err; + + err = clock_gettime (CLOCK_REALTIME, &t->ts); + assert (err == 0); +} + +static double +get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end) +{ + double nsecs; + + nsecs = end->ts.tv_sec - start->ts.tv_sec; + nsecs *= 1000000.0 * 1000.0; /* sec => nsec */ + + /* This way we don't have to care if tv_nsec unsigned or signed. */ + if (end->ts.tv_nsec >= start->ts.tv_nsec) + nsecs += end->ts.tv_nsec - start->ts.tv_nsec; + else + nsecs -= start->ts.tv_nsec - end->ts.tv_nsec; + + return nsecs; +} +#elif defined(HAVE_GETTIMEOFDAY) +struct nsec_time +{ + struct timeval tv; +}; + +static void +get_nsec_time (struct nsec_time *t) +{ + int err; + + err = gettimeofday (&t->tv, NULL); + assert (err == 0); +} + +static double +get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end) +{ + double nsecs; + + nsecs = end->tv.tv_sec - start->tv.tv_sec; + nsecs *= 1000000; /* sec => µsec */ + + /* This way we don't have to care if tv_usec unsigned or signed. */ + if (end->tv.tv_usec >= start->tv.tv_usec) + nsecs += end->tv.tv_usec - start->tv.tv_usec; + else + nsecs -= start->tv.tv_usec - end->tv.tv_usec; + + nsecs *= 1000; /* µsec => nsec */ + + return nsecs; +} +#else +#define NO_GET_NSEC_TIME 1 +#endif + + +/* If no high resolution timer found, provide dummy bench-slope. */ +#ifdef NO_GET_NSEC_TIME + + +int +main (void) +{ + /* No nsec timer => SKIP test. */ + return 77; +} + + +#else /* !NO_GET_NSEC_TIME */ + + +/********************************************** Slope benchmarking framework. */ + +struct bench_obj +{ + const struct bench_ops *ops; + + unsigned int num_measure_repetitions; + unsigned int min_bufsize; + unsigned int max_bufsize; + unsigned int step_size; + + void *priv; + void *hd; +}; + +typedef int (*const bench_initialize_t) (struct bench_obj * obj); +typedef void (*const bench_finalize_t) (struct bench_obj * obj); +typedef void (*const bench_do_run_t) (struct bench_obj * obj, void *buffer, + size_t buflen); + +struct bench_ops +{ + bench_initialize_t initialize; + bench_finalize_t finalize; + bench_do_run_t do_run; +}; + + +double +get_slope (double (*const get_x) (unsigned int idx, void *priv), + void *get_x_priv, double y_points[], unsigned int npoints, + double *overhead) +{ + double sumx, sumy, sumx2, sumy2, sumxy; + unsigned int i; + double b, a; + + sumx = sumy = sumx2 = sumy2 = sumxy = 0; + + for (i = 0; i < npoints; i++) + { + double x, y; + + x = get_x (i, get_x_priv); /* bytes */ + y = y_points[i]; /* nsecs */ + + sumx += x; + sumy += y; + sumx2 += x * x; + /*sumy2 += y * y;*/ + sumxy += x * y; + } + + b = (npoints * sumxy - sumx * sumy) / (npoints * sumx2 - sumx * sumx); + a = (sumy - b * sumx) / npoints; + + if (overhead) + *overhead = a; /* nsecs */ + + return b; /* nsecs per byte */ +} + + +double +get_bench_obj_point_x (unsigned int idx, void *priv) +{ + struct bench_obj *obj = priv; + return (double) (obj->min_bufsize + (idx * obj->step_size)); +} + + +unsigned int +get_num_measurements (struct bench_obj *obj) +{ + unsigned int buf_range = obj->max_bufsize - obj->min_bufsize; + unsigned int num = buf_range / obj->step_size + 1; + + while (obj->min_bufsize + (num * obj->step_size) > obj->max_bufsize) + num--; + + return num + 1; +} + + +static int +double_cmp (const void *_a, const void *_b) +{ + const double *a, *b; + + a = _a; + b = _b; + + if (*a > *b) + return 1; + if (*a < *b) + return -1; + return 0; +} + + +double +do_bench_obj_measurement (struct bench_obj *obj, void *buffer, size_t buflen, + double *measurement_raw, + unsigned int loop_iterations) +{ + const unsigned int num_repetitions = obj->num_measure_repetitions; + const bench_do_run_t do_run = obj->ops->do_run; + struct nsec_time start, end; + unsigned int rep, loop; + double res; + + if (num_repetitions < 1 || loop_iterations < 1) + return 0.0; + + for (rep = 0; rep < num_repetitions; rep++) + { + get_nsec_time (&start); + + for (loop = 0; loop < loop_iterations; loop++) + do_run (obj, buffer, buflen); + + get_nsec_time (&end); + + measurement_raw[rep] = get_time_nsec_diff (&start, &end); + } + + /* Return median of repeated measurements. */ + qsort (measurement_raw, num_repetitions, sizeof (measurement_raw[0]), + double_cmp); + + if (num_repetitions % 2 == 1) + return measurement_raw[num_repetitions / 2]; + + res = measurement_raw[num_repetitions / 2] + + measurement_raw[num_repetitions / 2 - 1]; + return res / 2; +} + + +unsigned int +adjust_loop_iterations_to_timer_accuracy (struct bench_obj *obj, void *buffer, + double *measurement_raw) +{ + const double increase_thres = 3.0; + double tmp, nsecs; + unsigned int loop_iterations; + unsigned int test_bufsize; + + test_bufsize = obj->min_bufsize; + if (test_bufsize == 0) + test_bufsize += obj->step_size; + + loop_iterations = 0; + do + { + /* Increase loop iterations until we get other results than zero. */ + nsecs = + do_bench_obj_measurement (obj, buffer, test_bufsize, + measurement_raw, ++loop_iterations); + } + while (nsecs < 1.0 - 0.1); + do + { + /* Increase loop iterations until we get reasonable increase for elapsed time. */ + tmp = + do_bench_obj_measurement (obj, buffer, test_bufsize, + measurement_raw, ++loop_iterations); + } + while (tmp < nsecs * (increase_thres - 0.1)); + + return loop_iterations; +} + + +/* Benchmark and return linear regression slope in nanoseconds per byte. */ +double +slope_benchmark (struct bench_obj *obj) +{ + unsigned int num_measurements; + double *measurements = NULL; + double *measurement_raw = NULL; + double slope, overhead; + unsigned int loop_iterations, midx, i; + unsigned char *real_buffer = NULL; + unsigned char *buffer; + size_t cur_bufsize; + int err; + + err = obj->ops->initialize (obj); + if (err < 0) + return -1; + + num_measurements = get_num_measurements (obj); + measurements = calloc (num_measurements, sizeof (*measurements)); + if (!measurements) + goto err_free; + + measurement_raw = + calloc (obj->num_measure_repetitions, sizeof (*measurement_raw)); + if (!measurement_raw) + goto err_free; + + if (num_measurements < 1 || obj->num_measure_repetitions < 1 || + obj->max_bufsize < 1 || obj->min_bufsize > obj->max_bufsize) + goto err_free; + + real_buffer = malloc (obj->max_bufsize + 128 + unaligned_mode); + if (!real_buffer) + goto err_free; + /* Get aligned buffer */ + buffer = real_buffer; + buffer += 128 - ((real_buffer - (unsigned char *) 0) & (128 - 1)); + if (unaligned_mode) + buffer += unaligned_mode; /* Make buffer unaligned */ + + for (i = 0; i < obj->max_bufsize; i++) + buffer[i] = 0x55 ^ (-i); + + /* Adjust number of loop iterations up to timer accuracy. */ + loop_iterations = adjust_loop_iterations_to_timer_accuracy (obj, buffer, + measurement_raw); + + /* Perform measurements */ + for (midx = 0, cur_bufsize = obj->min_bufsize; + cur_bufsize <= obj->max_bufsize; cur_bufsize += obj->step_size, midx++) + { + measurements[midx] = + do_bench_obj_measurement (obj, buffer, cur_bufsize, measurement_raw, + loop_iterations); + measurements[midx] /= loop_iterations; + } + + assert (midx == num_measurements); + + slope = + get_slope (&get_bench_obj_point_x, obj, measurements, num_measurements, + &overhead); + + free (measurement_raw); + free (measurements); + free (real_buffer); + obj->ops->finalize (obj); + + return slope; + +err_free: + if (measurement_raw) + free (measurement_raw); + if (measurements) + free (measurements); + if (real_buffer) + free (real_buffer); + obj->ops->finalize (obj); + + return -1; +} + +/********************************************* CPU frequency auto-detection. */ + +static int +auto_ghz_init (struct bench_obj *obj) +{ + obj->min_bufsize = 16; + obj->max_bufsize = 64 + obj->min_bufsize; + obj->step_size = 8; + obj->num_measure_repetitions = 16; + + return 0; +} + +static void +auto_ghz_free (struct bench_obj *obj) +{ + (void)obj; +} + +static void +auto_ghz_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + (void)obj; + (void)buf; + + buflen *= 1024; + + /* Turbo frequency detection benchmark. Without CPU turbo-boost, this + * function will give cycles/iteration result 1024.0 on high-end CPUs. + * With turbo, result will be less and can be used detect turbo-clock. */ + +#ifdef HAVE_GCC_ASM_VOLATILE_MEMORY + /* Auto-ghz operation takes two CPU cycles to perform. Memory barriers + * are used to prevent compiler from optimizing this loop away. */ + #define AUTO_GHZ_OPERATION \ + asm volatile ("":"+r"(buflen)::"memory"); \ + buflen ^= 1; \ + asm volatile ("":"+r"(buflen)::"memory"); \ + buflen -= 2 +#else + /* TODO: Needs alternative way of preventing compiler optimizations. + * Mix of XOR and subtraction appears to do the trick for now. */ + #define AUTO_GHZ_OPERATION \ + buflen ^= 1; \ + buflen -= 2 +#endif + +#define AUTO_GHZ_OPERATION_2 \ + AUTO_GHZ_OPERATION; \ + AUTO_GHZ_OPERATION + +#define AUTO_GHZ_OPERATION_4 \ + AUTO_GHZ_OPERATION_2; \ + AUTO_GHZ_OPERATION_2 + +#define AUTO_GHZ_OPERATION_8 \ + AUTO_GHZ_OPERATION_4; \ + AUTO_GHZ_OPERATION_4 + +#define AUTO_GHZ_OPERATION_16 \ + AUTO_GHZ_OPERATION_8; \ + AUTO_GHZ_OPERATION_8 + +#define AUTO_GHZ_OPERATION_32 \ + AUTO_GHZ_OPERATION_16; \ + AUTO_GHZ_OPERATION_16 + +#define AUTO_GHZ_OPERATION_64 \ + AUTO_GHZ_OPERATION_32; \ + AUTO_GHZ_OPERATION_32 + +#define AUTO_GHZ_OPERATION_128 \ + AUTO_GHZ_OPERATION_64; \ + AUTO_GHZ_OPERATION_64 + + do + { + /* 1024 auto-ghz operations per loop, total 2048 instructions. */ + AUTO_GHZ_OPERATION_128; AUTO_GHZ_OPERATION_128; + AUTO_GHZ_OPERATION_128; AUTO_GHZ_OPERATION_128; + AUTO_GHZ_OPERATION_128; AUTO_GHZ_OPERATION_128; + AUTO_GHZ_OPERATION_128; AUTO_GHZ_OPERATION_128; + } + while (buflen); +} + +static struct bench_ops auto_ghz_detect_ops = { + &auto_ghz_init, + &auto_ghz_free, + &auto_ghz_bench +}; + + +double +get_auto_ghz (void) +{ + struct bench_obj obj = { 0 }; + double nsecs_per_iteration; + double cycles_per_iteration; + + obj.ops = &auto_ghz_detect_ops; + + nsecs_per_iteration = slope_benchmark (&obj); + + cycles_per_iteration = nsecs_per_iteration * cpu_ghz; + + /* Adjust CPU Ghz so that cycles per iteration would give '1024.0'. */ + + return cpu_ghz * 1024 / cycles_per_iteration; +} + + +double +do_slope_benchmark (struct bench_obj *obj) +{ + double ret; + + if (!auto_ghz) + { + /* Perform measurement without autodetection of CPU frequency. */ + + ret = slope_benchmark (obj); + + bench_ghz = cpu_ghz; + bench_ghz_diff = 0; + } + else + { + double target_diff = AUTO_GHZ_TARGET_DIFF; + double cpu_auto_ghz_before; + double cpu_auto_ghz_after; + double nsecs_per_iteration; + double diff; + unsigned int try_count = 0; + + /* Perform measurement with CPU frequency autodetection. */ + + do + { + /* Repeat measurement until CPU turbo frequency has stabilized. */ + + if (try_count++ > 4) + { + /* Too much frequency instability on the system, relax target + * accuracy. */ + + try_count = 0; + target_diff *= 2; + } + + cpu_auto_ghz_before = get_auto_ghz (); + + nsecs_per_iteration = slope_benchmark (obj); + + cpu_auto_ghz_after = get_auto_ghz (); + + diff = 1.0 - (cpu_auto_ghz_before / cpu_auto_ghz_after); + diff = diff < 0 ? -diff : diff; + } + while (diff > target_diff); + + ret = nsecs_per_iteration; + + bench_ghz = (cpu_auto_ghz_before + cpu_auto_ghz_after) / 2; + bench_ghz_diff = diff; + } + + return ret; +} + + +/********************************************************** Printing results. */ + +static void +double_to_str (char *out, size_t outlen, double value) +{ + const char *fmt; + + if (value < 1.0) + fmt = "%.3f"; + else if (value < 100.0) + fmt = "%.2f"; + else if (value < 1000.0) + fmt = "%.1f"; + else + fmt = "%.0f"; + + snprintf (out, outlen, fmt, value); +} + +static void +bench_print_result_csv (double nsecs_per_byte) +{ + double cycles_per_byte, mbytes_per_sec; + char nsecpbyte_buf[16]; + char mbpsec_buf[16]; + char cpbyte_buf[16]; + char mhz_buf[16]; + char mhz_diff_buf[32]; + + strcpy (mhz_diff_buf, ""); + *cpbyte_buf = 0; + *mhz_buf = 0; + + double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte); + + /* If user didn't provide CPU speed, we cannot show cycles/byte results. */ + if (bench_ghz > 0.0) + { + cycles_per_byte = nsecs_per_byte * bench_ghz; + double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte); + double_to_str (mhz_buf, sizeof (mhz_buf), bench_ghz * 1000); + if (auto_ghz && bench_ghz_diff * 1000 >= 1) + { + snprintf(mhz_diff_buf, sizeof(mhz_diff_buf), ",%.0f,Mhz-diff", + bench_ghz_diff * 1000); + } + } + + mbytes_per_sec = + (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024); + double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec); + + /* We print two empty fields to allow for future enhancements. */ + if (auto_ghz) + { + printf ("%s,%s,%s,,,%s,ns/B,%s,MiB/s,%s,c/B,%s,Mhz%s\n", + current_section_name, + current_algo_name? current_algo_name : "", + current_mode_name? current_mode_name : "", + nsecpbyte_buf, + mbpsec_buf, + cpbyte_buf, + mhz_buf, + mhz_diff_buf); + } + else + { + printf ("%s,%s,%s,,,%s,ns/B,%s,MiB/s,%s,c/B\n", + current_section_name, + current_algo_name? current_algo_name : "", + current_mode_name? current_mode_name : "", + nsecpbyte_buf, + mbpsec_buf, + cpbyte_buf); + } +} + +static void +bench_print_result_std (double nsecs_per_byte) +{ + double cycles_per_byte, mbytes_per_sec; + char nsecpbyte_buf[16]; + char mbpsec_buf[16]; + char cpbyte_buf[16]; + char mhz_buf[16]; + char mhz_diff_buf[32]; + + strcpy (mhz_diff_buf, ""); + + double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte); + + /* If user didn't provide CPU speed, we cannot show cycles/byte results. */ + if (bench_ghz > 0.0) + { + cycles_per_byte = nsecs_per_byte * bench_ghz; + double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte); + double_to_str (mhz_buf, sizeof (mhz_buf), bench_ghz * 1000); + if (auto_ghz && bench_ghz_diff * 1000 >= 0.5) + { + snprintf(mhz_diff_buf, sizeof(mhz_diff_buf), "±%.0f", + bench_ghz_diff * 1000); + } + } + else + { + strcpy (cpbyte_buf, "-"); + strcpy (mhz_buf, "-"); + } + + mbytes_per_sec = + (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024); + double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec); + + if (auto_ghz) + { + printf ("%9s ns/B %9s MiB/s %9s c/B %9s%s\n", + nsecpbyte_buf, mbpsec_buf, cpbyte_buf, mhz_buf, mhz_diff_buf); + } + else + { + printf ("%9s ns/B %9s MiB/s %9s c/B\n", + nsecpbyte_buf, mbpsec_buf, cpbyte_buf); + } +} + +static void +bench_print_result (double nsecs_per_byte) +{ + if (csv_mode) + bench_print_result_csv (nsecs_per_byte); + else + bench_print_result_std (nsecs_per_byte); +} + +static void +bench_print_section (const char *section_name, const char *print_name) +{ + if (csv_mode) + { + gcry_free (current_section_name); + current_section_name = gcry_xstrdup (section_name); + } + else + printf ("%s:\n", print_name); +} + +static void +bench_print_header (int algo_width, const char *algo_name) +{ + if (csv_mode) + { + gcry_free (current_algo_name); + current_algo_name = gcry_xstrdup (algo_name); + } + else + { + if (algo_width < 0) + printf (" %-*s | ", -algo_width, algo_name); + else + printf (" %-*s | ", algo_width, algo_name); + + if (auto_ghz) + printf ("%14s %15s %13s %9s\n", "nanosecs/byte", "mebibytes/sec", + "cycles/byte", "auto Mhz"); + else + printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec", + "cycles/byte"); + } +} + +static void +bench_print_algo (int algo_width, const char *algo_name) +{ + if (csv_mode) + { + gcry_free (current_algo_name); + current_algo_name = gcry_xstrdup (algo_name); + } + else + { + if (algo_width < 0) + printf (" %-*s | ", -algo_width, algo_name); + else + printf (" %-*s | ", algo_width, algo_name); + } +} + +static void +bench_print_mode (int width, const char *mode_name) +{ + if (csv_mode) + { + gcry_free (current_mode_name); + current_mode_name = gcry_xstrdup (mode_name); + } + else + { + if (width < 0) + printf (" %-*s | ", -width, mode_name); + else + printf (" %*s | ", width, mode_name); + fflush (stdout); + } +} + +static void +bench_print_footer (int algo_width) +{ + if (!csv_mode) + printf (" %-*s =\n", algo_width, ""); +} + + +/********************************************************* Cipher benchmarks. */ + +struct bench_cipher_mode +{ + int mode; + const char *name; + struct bench_ops *ops; + + int algo; +}; + + +static int +bench_encrypt_init (struct bench_obj *obj) +{ + struct bench_cipher_mode *mode = obj->priv; + gcry_cipher_hd_t hd; + int err, keylen; + + obj->min_bufsize = BUF_START_SIZE; + obj->max_bufsize = BUF_END_SIZE; + obj->step_size = BUF_STEP_SIZE; + obj->num_measure_repetitions = num_measurement_repetitions; + + err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0); + if (err) + { + fprintf (stderr, PGM ": error opening cipher `%s'\n", + gcry_cipher_algo_name (mode->algo)); + exit (1); + } + + keylen = gcry_cipher_get_algo_keylen (mode->algo); + if (keylen) + { + char key[keylen]; + int i; + + for (i = 0; i < keylen; i++) + key[i] = 0x33 ^ (11 - i); + + err = gcry_cipher_setkey (hd, key, keylen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + } + else + { + fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n", + gcry_cipher_algo_name (mode->algo)); + gcry_cipher_close (hd); + exit (1); + } + + obj->hd = hd; + + return 0; +} + +static void +bench_encrypt_free (struct bench_obj *obj) +{ + gcry_cipher_hd_t hd = obj->hd; + + gcry_cipher_close (hd); +} + +static void +bench_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + + err = gcry_cipher_reset (hd); + if (!err) + err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static void +bench_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + + err = gcry_cipher_reset (hd); + if (!err) + err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static struct bench_ops encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_encrypt_do_bench +}; + +static struct bench_ops decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_decrypt_do_bench +}; + + +static int +bench_xts_encrypt_init (struct bench_obj *obj) +{ + struct bench_cipher_mode *mode = obj->priv; + gcry_cipher_hd_t hd; + int err, keylen; + + obj->min_bufsize = BUF_START_SIZE; + obj->max_bufsize = BUF_END_SIZE; + obj->step_size = BUF_STEP_SIZE; + obj->num_measure_repetitions = num_measurement_repetitions; + + err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0); + if (err) + { + fprintf (stderr, PGM ": error opening cipher `%s'\n", + gcry_cipher_algo_name (mode->algo)); + exit (1); + } + + /* Double key-length for XTS. */ + keylen = gcry_cipher_get_algo_keylen (mode->algo) * 2; + if (keylen) + { + char key[keylen]; + int i; + + for (i = 0; i < keylen; i++) + key[i] = 0x33 ^ (11 - i); + + err = gcry_cipher_setkey (hd, key, keylen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + } + else + { + fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n", + gcry_cipher_algo_name (mode->algo)); + gcry_cipher_close (hd); + exit (1); + } + + obj->hd = hd; + + return 0; +} + +static struct bench_ops xts_encrypt_ops = { + &bench_xts_encrypt_init, + &bench_encrypt_free, + &bench_encrypt_do_bench +}; + +static struct bench_ops xts_decrypt_ops = { + &bench_xts_encrypt_init, + &bench_encrypt_free, + &bench_decrypt_do_bench +}; + + +static void +bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + char tag[8]; + char nonce[11] = { 0x80, 0x01, }; + u64 params[3]; + + gcry_cipher_setiv (hd, nonce, sizeof (nonce)); + + /* Set CCM lengths */ + params[0] = buflen; + params[1] = 0; /*aadlen */ + params[2] = sizeof (tag); + err = + gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_gettag (hd, tag, sizeof (tag)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static void +bench_ccm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + char tag[8] = { 0, }; + char nonce[11] = { 0x80, 0x01, }; + u64 params[3]; + + gcry_cipher_setiv (hd, nonce, sizeof (nonce)); + + /* Set CCM lengths */ + params[0] = buflen; + params[1] = 0; /*aadlen */ + params[2] = sizeof (tag); + err = + gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_checktag (hd, tag, sizeof (tag)); + if (gpg_err_code (err) == GPG_ERR_CHECKSUM) + err = gpg_error (GPG_ERR_NO_ERROR); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static void +bench_ccm_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + char tag[8] = { 0, }; + char nonce[11] = { 0x80, 0x01, }; + u64 params[3]; + char data = 0xff; + + gcry_cipher_setiv (hd, nonce, sizeof (nonce)); + + /* Set CCM lengths */ + params[0] = sizeof (data); /*datalen */ + params[1] = buflen; /*aadlen */ + params[2] = sizeof (tag); + err = + gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_authenticate (hd, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_gettag (hd, tag, sizeof (tag)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static struct bench_ops ccm_encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_ccm_encrypt_do_bench +}; + +static struct bench_ops ccm_decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_ccm_decrypt_do_bench +}; + +static struct bench_ops ccm_authenticate_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_ccm_authenticate_do_bench +}; + + +static void +bench_aead_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen, + const char *nonce, size_t noncelen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + char tag[16]; + + gcry_cipher_setiv (hd, nonce, noncelen); + + gcry_cipher_final (hd); + err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_gettag (hd, tag, sizeof (tag)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static void +bench_aead_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen, + const char *nonce, size_t noncelen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + char tag[16] = { 0, }; + + gcry_cipher_setiv (hd, nonce, noncelen); + + gcry_cipher_final (hd); + err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_checktag (hd, tag, sizeof (tag)); + if (gpg_err_code (err) == GPG_ERR_CHECKSUM) + err = gpg_error (GPG_ERR_NO_ERROR); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + +static void +bench_aead_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen, const char *nonce, + size_t noncelen) +{ + gcry_cipher_hd_t hd = obj->hd; + int err; + char tag[16] = { 0, }; + char data = 0xff; + + err = gcry_cipher_setiv (hd, nonce, noncelen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_authenticate (hd, buf, buflen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + gcry_cipher_final (hd); + err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } + + err = gcry_cipher_gettag (hd, tag, sizeof (tag)); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } +} + + +static void +bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 }; + bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 }; + bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 }; + bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static struct bench_ops gcm_encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_gcm_encrypt_do_bench +}; + +static struct bench_ops gcm_decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_gcm_decrypt_do_bench +}; + +static struct bench_ops gcm_authenticate_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_gcm_authenticate_do_bench +}; + + +static void +bench_ocb_encrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[15] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, + 0x00, 0x00, 0x01 }; + bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_ocb_decrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[15] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, + 0x00, 0x00, 0x01 }; + bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_ocb_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[15] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, + 0x00, 0x00, 0x01 }; + bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static struct bench_ops ocb_encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_ocb_encrypt_do_bench +}; + +static struct bench_ops ocb_decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_ocb_decrypt_do_bench +}; + +static struct bench_ops ocb_authenticate_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_ocb_authenticate_do_bench +}; + +static void +bench_eax_encrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, + 0x00, 0x00, 0x01, 0x00 }; + bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_eax_decrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, + 0x00, 0x00, 0x01, 0x00 }; + bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_eax_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, + 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, + 0x00, 0x00, 0x01, 0x00 }; + bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static struct bench_ops eax_encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_eax_encrypt_do_bench +}; + +static struct bench_ops eax_decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_eax_decrypt_do_bench +}; + +static struct bench_ops eax_authenticate_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_eax_authenticate_do_bench +}; + +static void +bench_poly1305_encrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad }; + bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_poly1305_decrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad }; + bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static void +bench_poly1305_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad }; + bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce)); +} + +static struct bench_ops poly1305_encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_poly1305_encrypt_do_bench +}; + +static struct bench_ops poly1305_decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_poly1305_decrypt_do_bench +}; + +static struct bench_ops poly1305_authenticate_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_poly1305_authenticate_do_bench +}; + + +static struct bench_cipher_mode cipher_modes[] = { + {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops}, + {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops}, + {GCRY_CIPHER_MODE_CBC, "CBC enc", &encrypt_ops}, + {GCRY_CIPHER_MODE_CBC, "CBC dec", &decrypt_ops}, + {GCRY_CIPHER_MODE_CFB, "CFB enc", &encrypt_ops}, + {GCRY_CIPHER_MODE_CFB, "CFB dec", &decrypt_ops}, + {GCRY_CIPHER_MODE_OFB, "OFB enc", &encrypt_ops}, + {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops}, + {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops}, + {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops}, + {GCRY_CIPHER_MODE_XTS, "XTS enc", &xts_encrypt_ops}, + {GCRY_CIPHER_MODE_XTS, "XTS dec", &xts_decrypt_ops}, + {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops}, + {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops}, + {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops}, + {GCRY_CIPHER_MODE_EAX, "EAX enc", &eax_encrypt_ops}, + {GCRY_CIPHER_MODE_EAX, "EAX dec", &eax_decrypt_ops}, + {GCRY_CIPHER_MODE_EAX, "EAX auth", &eax_authenticate_ops}, + {GCRY_CIPHER_MODE_GCM, "GCM enc", &gcm_encrypt_ops}, + {GCRY_CIPHER_MODE_GCM, "GCM dec", &gcm_decrypt_ops}, + {GCRY_CIPHER_MODE_GCM, "GCM auth", &gcm_authenticate_ops}, + {GCRY_CIPHER_MODE_OCB, "OCB enc", &ocb_encrypt_ops}, + {GCRY_CIPHER_MODE_OCB, "OCB dec", &ocb_decrypt_ops}, + {GCRY_CIPHER_MODE_OCB, "OCB auth", &ocb_authenticate_ops}, + {GCRY_CIPHER_MODE_POLY1305, "POLY1305 enc", &poly1305_encrypt_ops}, + {GCRY_CIPHER_MODE_POLY1305, "POLY1305 dec", &poly1305_decrypt_ops}, + {GCRY_CIPHER_MODE_POLY1305, "POLY1305 auth", &poly1305_authenticate_ops}, + {0}, +}; + + +static void +cipher_bench_one (int algo, struct bench_cipher_mode *pmode) +{ + struct bench_cipher_mode mode = *pmode; + struct bench_obj obj = { 0 }; + double result; + unsigned int blklen; + + mode.algo = algo; + + /* Check if this mode is ok */ + blklen = gcry_cipher_get_algo_blklen (algo); + if (!blklen) + return; + + /* Stream cipher? Only test with "ECB" and POLY1305. */ + if (blklen == 1 && (mode.mode != GCRY_CIPHER_MODE_ECB && + mode.mode != GCRY_CIPHER_MODE_POLY1305)) + return; + if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB) + { + mode.mode = GCRY_CIPHER_MODE_STREAM; + mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec"; + } + + /* Poly1305 has restriction for cipher algorithm */ + if (mode.mode == GCRY_CIPHER_MODE_POLY1305 && algo != GCRY_CIPHER_CHACHA20) + return; + + /* CCM has restrictions for block-size */ + if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN) + return; + + /* GCM has restrictions for block-size */ + if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN) + return; + + /* XTS has restrictions for block-size */ + if (mode.mode == GCRY_CIPHER_MODE_XTS && blklen != GCRY_XTS_BLOCK_LEN) + return; + + /* Our OCB implementation has restrictions for block-size. */ + if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != GCRY_OCB_BLOCK_LEN) + return; + + bench_print_mode (14, mode.name); + + obj.ops = mode.ops; + obj.priv = &mode; + + result = do_slope_benchmark (&obj); + + bench_print_result (result); +} + + +static void +_cipher_bench (int algo) +{ + const char *algoname; + int i; + + algoname = gcry_cipher_algo_name (algo); + + bench_print_header (14, algoname); + + for (i = 0; cipher_modes[i].mode; i++) + cipher_bench_one (algo, &cipher_modes[i]); + + bench_print_footer (14); +} + + +void +cipher_bench (char **argv, int argc) +{ + int i, algo; + + bench_print_section ("cipher", "Cipher"); + + if (argv && argc) + { + for (i = 0; i < argc; i++) + { + algo = gcry_cipher_map_name (argv[i]); + if (algo) + _cipher_bench (algo); + } + } + else + { + for (i = 1; i < 400; i++) + if (!gcry_cipher_test_algo (i)) + _cipher_bench (i); + } +} + + +/*********************************************************** Hash benchmarks. */ + +struct bench_hash_mode +{ + const char *name; + struct bench_ops *ops; + + int algo; +}; + + +static int +bench_hash_init (struct bench_obj *obj) +{ + struct bench_hash_mode *mode = obj->priv; + gcry_md_hd_t hd; + int err; + + obj->min_bufsize = BUF_START_SIZE; + obj->max_bufsize = BUF_END_SIZE; + obj->step_size = BUF_STEP_SIZE; + obj->num_measure_repetitions = num_measurement_repetitions; + + err = gcry_md_open (&hd, mode->algo, 0); + if (err) + { + fprintf (stderr, PGM ": error opening hash `%s'\n", + gcry_md_algo_name (mode->algo)); + exit (1); + } + + obj->hd = hd; + + return 0; +} + +static void +bench_hash_free (struct bench_obj *obj) +{ + gcry_md_hd_t hd = obj->hd; + + gcry_md_close (hd); +} + +static void +bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + gcry_md_hd_t hd = obj->hd; + + gcry_md_reset (hd); + gcry_md_write (hd, buf, buflen); + gcry_md_final (hd); +} + +static struct bench_ops hash_ops = { + &bench_hash_init, + &bench_hash_free, + &bench_hash_do_bench +}; + + +static struct bench_hash_mode hash_modes[] = { + {"", &hash_ops}, + {0}, +}; + + +static void +hash_bench_one (int algo, struct bench_hash_mode *pmode) +{ + struct bench_hash_mode mode = *pmode; + struct bench_obj obj = { 0 }; + double result; + + mode.algo = algo; + + if (mode.name[0] == '\0') + bench_print_algo (-14, gcry_md_algo_name (algo)); + else + bench_print_algo (14, mode.name); + + obj.ops = mode.ops; + obj.priv = &mode; + + result = do_slope_benchmark (&obj); + + bench_print_result (result); +} + +static void +_hash_bench (int algo) +{ + int i; + + for (i = 0; hash_modes[i].name; i++) + hash_bench_one (algo, &hash_modes[i]); +} + +void +hash_bench (char **argv, int argc) +{ + int i, algo; + + bench_print_section ("hash", "Hash"); + bench_print_header (14, ""); + + if (argv && argc) + { + for (i = 0; i < argc; i++) + { + algo = gcry_md_map_name (argv[i]); + if (algo) + _hash_bench (algo); + } + } + else + { + for (i = 1; i < 400; i++) + if (!gcry_md_test_algo (i)) + _hash_bench (i); + } + + bench_print_footer (14); +} + + +/************************************************************ MAC benchmarks. */ + +struct bench_mac_mode +{ + const char *name; + struct bench_ops *ops; + + int algo; +}; + + +static int +bench_mac_init (struct bench_obj *obj) +{ + struct bench_mac_mode *mode = obj->priv; + gcry_mac_hd_t hd; + int err; + unsigned int keylen; + void *key; + + obj->min_bufsize = BUF_START_SIZE; + obj->max_bufsize = BUF_END_SIZE; + obj->step_size = BUF_STEP_SIZE; + obj->num_measure_repetitions = num_measurement_repetitions; + + keylen = gcry_mac_get_algo_keylen (mode->algo); + if (keylen == 0) + keylen = 32; + key = malloc (keylen); + if (!key) + { + fprintf (stderr, PGM ": couldn't allocate %d bytes\n", keylen); + exit (1); + } + memset(key, 42, keylen); + + err = gcry_mac_open (&hd, mode->algo, 0, NULL); + if (err) + { + fprintf (stderr, PGM ": error opening mac `%s'\n", + gcry_mac_algo_name (mode->algo)); + free (key); + exit (1); + } + + err = gcry_mac_setkey (hd, key, keylen); + if (err) + { + fprintf (stderr, PGM ": error setting key for mac `%s'\n", + gcry_mac_algo_name (mode->algo)); + free (key); + exit (1); + } + + switch (mode->algo) + { + default: + break; + case GCRY_MAC_POLY1305_AES: + case GCRY_MAC_POLY1305_CAMELLIA: + case GCRY_MAC_POLY1305_TWOFISH: + case GCRY_MAC_POLY1305_SERPENT: + case GCRY_MAC_POLY1305_SEED: + gcry_mac_setiv (hd, key, 16); + break; + } + + obj->hd = hd; + + free (key); + return 0; +} + +static void +bench_mac_free (struct bench_obj *obj) +{ + gcry_mac_hd_t hd = obj->hd; + + gcry_mac_close (hd); +} + +static void +bench_mac_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + gcry_mac_hd_t hd = obj->hd; + size_t bs; + char b; + + gcry_mac_reset (hd); + gcry_mac_write (hd, buf, buflen); + bs = sizeof(b); + gcry_mac_read (hd, &b, &bs); +} + +static struct bench_ops mac_ops = { + &bench_mac_init, + &bench_mac_free, + &bench_mac_do_bench +}; + + +static struct bench_mac_mode mac_modes[] = { + {"", &mac_ops}, + {0}, +}; + + +static void +mac_bench_one (int algo, struct bench_mac_mode *pmode) +{ + struct bench_mac_mode mode = *pmode; + struct bench_obj obj = { 0 }; + double result; + + mode.algo = algo; + + if (mode.name[0] == '\0') + bench_print_algo (-18, gcry_mac_algo_name (algo)); + else + bench_print_algo (18, mode.name); + + obj.ops = mode.ops; + obj.priv = &mode; + + result = do_slope_benchmark (&obj); + + bench_print_result (result); +} + +static void +_mac_bench (int algo) +{ + int i; + + for (i = 0; mac_modes[i].name; i++) + mac_bench_one (algo, &mac_modes[i]); +} + +void +mac_bench (char **argv, int argc) +{ + int i, algo; + + bench_print_section ("mac", "MAC"); + bench_print_header (18, ""); + + if (argv && argc) + { + for (i = 0; i < argc; i++) + { + algo = gcry_mac_map_name (argv[i]); + if (algo) + _mac_bench (algo); + } + } + else + { + for (i = 1; i < 600; i++) + if (!gcry_mac_test_algo (i)) + _mac_bench (i); + } + + bench_print_footer (18); +} + + +/************************************************************ KDF benchmarks. */ + +struct bench_kdf_mode +{ + struct bench_ops *ops; + + int algo; + int subalgo; +}; + + +static int +bench_kdf_init (struct bench_obj *obj) +{ + struct bench_kdf_mode *mode = obj->priv; + + if (mode->algo == GCRY_KDF_PBKDF2) + { + obj->min_bufsize = 2; + obj->max_bufsize = 2 * 32; + obj->step_size = 2; + } + + obj->num_measure_repetitions = num_measurement_repetitions; + + return 0; +} + +static void +bench_kdf_free (struct bench_obj *obj) +{ + (void)obj; +} + +static void +bench_kdf_do_bench (struct bench_obj *obj, void *buf, size_t buflen) +{ + struct bench_kdf_mode *mode = obj->priv; + char keybuf[16]; + + (void)buf; + + if (mode->algo == GCRY_KDF_PBKDF2) + { + gcry_kdf_derive("qwerty", 6, mode->algo, mode->subalgo, "01234567", 8, + buflen, sizeof(keybuf), keybuf); + } +} + +static struct bench_ops kdf_ops = { + &bench_kdf_init, + &bench_kdf_free, + &bench_kdf_do_bench +}; + + +static void +kdf_bench_one (int algo, int subalgo) +{ + struct bench_kdf_mode mode = { &kdf_ops }; + struct bench_obj obj = { 0 }; + double nsecs_per_iteration; + double cycles_per_iteration; + char algo_name[32]; + char nsecpiter_buf[16]; + char cpiter_buf[16]; + char mhz_buf[16]; + + mode.algo = algo; + mode.subalgo = subalgo; + + switch (subalgo) + { + case GCRY_MD_CRC32: + case GCRY_MD_CRC32_RFC1510: + case GCRY_MD_CRC24_RFC2440: + case GCRY_MD_MD4: + /* Skip CRC32s. */ + return; + } + + if (gcry_md_get_algo_dlen (subalgo) == 0) + { + /* Skip XOFs */ + return; + } + + *algo_name = 0; + + if (algo == GCRY_KDF_PBKDF2) + { + snprintf (algo_name, sizeof(algo_name), "PBKDF2-HMAC-%s", + gcry_md_algo_name (subalgo)); + } + + bench_print_algo (-24, algo_name); + + obj.ops = mode.ops; + obj.priv = &mode; + + nsecs_per_iteration = do_slope_benchmark (&obj); + + strcpy(cpiter_buf, csv_mode ? "" : "-"); + strcpy(mhz_buf, csv_mode ? "" : "-"); + + double_to_str (nsecpiter_buf, sizeof (nsecpiter_buf), nsecs_per_iteration); + + /* If user didn't provide CPU speed, we cannot show cycles/iter results. */ + if (bench_ghz > 0.0) + { + cycles_per_iteration = nsecs_per_iteration * bench_ghz; + double_to_str (cpiter_buf, sizeof (cpiter_buf), cycles_per_iteration); + double_to_str (mhz_buf, sizeof (mhz_buf), bench_ghz * 1000); + } + + if (csv_mode) + { + if (auto_ghz) + printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter,%s,Mhz\n", + current_section_name, + current_algo_name ? current_algo_name : "", + current_mode_name ? current_mode_name : "", + nsecpiter_buf, + cpiter_buf, + mhz_buf); + else + printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter\n", + current_section_name, + current_algo_name ? current_algo_name : "", + current_mode_name ? current_mode_name : "", + nsecpiter_buf, + cpiter_buf); + } + else + { + if (auto_ghz) + printf ("%14s %13s %9s\n", nsecpiter_buf, cpiter_buf, mhz_buf); + else + printf ("%14s %13s\n", nsecpiter_buf, cpiter_buf); + } +} + +void +kdf_bench (char **argv, int argc) +{ + char algo_name[32]; + int i, j; + + bench_print_section ("kdf", "KDF"); + + if (!csv_mode) + { + printf (" %-*s | ", 24, ""); + if (auto_ghz) + printf ("%14s %13s %9s\n", "nanosecs/iter", "cycles/iter", "auto Mhz"); + else + printf ("%14s %13s\n", "nanosecs/iter", "cycles/iter"); + } + + if (argv && argc) + { + for (i = 0; i < argc; i++) + { + for (j = 1; j < 400; j++) + { + if (gcry_md_test_algo (j)) + continue; + + snprintf (algo_name, sizeof(algo_name), "PBKDF2-HMAC-%s", + gcry_md_algo_name (j)); + + if (!strcmp(argv[i], algo_name)) + kdf_bench_one (GCRY_KDF_PBKDF2, j); + } + } + } + else + { + for (i = 1; i < 400; i++) + if (!gcry_md_test_algo (i)) + kdf_bench_one (GCRY_KDF_PBKDF2, i); + } + + bench_print_footer (24); +} + + +/************************************************************** Main program. */ + +void +print_help (void) +{ + static const char *help_lines[] = { + "usage: bench-slope [options] [hash|mac|cipher|kdf [algonames]]", + "", + " options:", + " --cpu-mhz <mhz> Set CPU speed for calculating cycles", + " per bytes results. Set as \"auto\"", + " for auto-detection of CPU speed.", + " --disable-hwf <features> Disable hardware acceleration feature(s)", + " for benchmarking.", + " --repetitions <n> Use N repetitions (default " + STR2(NUM_MEASUREMENT_REPETITIONS) ")", + " --unaligned Use unaligned input buffers.", + " --csv Use CSV output format", + NULL + }; + const char **line; + + for (line = help_lines; *line; line++) + fprintf (stdout, "%s\n", *line); +} + + +/* Warm up CPU. */ +static void +warm_up_cpu (void) +{ + struct nsec_time start, end; + + get_nsec_time (&start); + do + { + get_nsec_time (&end); + } + while (get_time_nsec_diff (&start, &end) < 1000.0 * 1000.0 * 1000.0); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) + { + argc--; + argv++; + } + + /* We skip this test if we are running under the test suite (no args + and srcdir defined) and GCRYPT_NO_BENCHMARKS is set. */ + if (!argc && getenv ("srcdir") && getenv ("GCRYPT_NO_BENCHMARKS")) + exit (77); + + if (getenv ("GCRYPT_IN_REGRESSION_TEST")) + { + in_regression_test = 1; + num_measurement_repetitions = 2; + } + else + num_measurement_repetitions = NUM_MEASUREMENT_REPETITIONS; + + while (argc && last_argc != argc) + { + last_argc = argc; + + if (!strcmp (*argv, "--")) + { + argc--; + argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + print_help (); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; + argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; + argv++; + } + else if (!strcmp (*argv, "--csv")) + { + csv_mode = 1; + argc--; + argv++; + } + else if (!strcmp (*argv, "--unaligned")) + { + unaligned_mode = 1; + argc--; + argv++; + } + else if (!strcmp (*argv, "--disable-hwf")) + { + argc--; + argv++; + if (argc) + { + if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL)) + fprintf (stderr, + PGM + ": unknown hardware feature `%s' - option ignored\n", + *argv); + argc--; + argv++; + } + } + else if (!strcmp (*argv, "--cpu-mhz")) + { + argc--; + argv++; + if (argc) + { + if (!strcmp (*argv, "auto")) + { + auto_ghz = 1; + } + else + { + cpu_ghz = atof (*argv); + cpu_ghz /= 1000; /* Mhz => Ghz */ + } + + argc--; + argv++; + } + } + else if (!strcmp (*argv, "--repetitions")) + { + argc--; + argv++; + if (argc) + { + num_measurement_repetitions = atof (*argv); + if (num_measurement_repetitions < 2) + { + fprintf (stderr, + PGM + ": value for --repetitions too small - using %d\n", + NUM_MEASUREMENT_REPETITIONS); + num_measurement_repetitions = NUM_MEASUREMENT_REPETITIONS; + } + argc--; + argv++; + } + } + } + + xgcry_control ((GCRYCTL_SET_VERBOSITY, (int) verbose)); + + if (!gcry_check_version (GCRYPT_VERSION)) + { + fprintf (stderr, PGM ": version mismatch; pgm=%s, library=%s\n", + GCRYPT_VERSION, gcry_check_version (NULL)); + exit (1); + } + + if (debug) + xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u, 0)); + + xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); + xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); + xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); + + if (in_regression_test) + fputs ("Note: " PGM " running in quick regression test mode.\n", stdout); + + if (!argc) + { + warm_up_cpu (); + hash_bench (NULL, 0); + mac_bench (NULL, 0); + cipher_bench (NULL, 0); + kdf_bench (NULL, 0); + } + else if (!strcmp (*argv, "hash")) + { + argc--; + argv++; + + warm_up_cpu (); + hash_bench ((argc == 0) ? NULL : argv, argc); + } + else if (!strcmp (*argv, "mac")) + { + argc--; + argv++; + + warm_up_cpu (); + mac_bench ((argc == 0) ? NULL : argv, argc); + } + else if (!strcmp (*argv, "cipher")) + { + argc--; + argv++; + + warm_up_cpu (); + cipher_bench ((argc == 0) ? NULL : argv, argc); + } + else if (!strcmp (*argv, "kdf")) + { + argc--; + argv++; + + warm_up_cpu (); + kdf_bench ((argc == 0) ? NULL : argv, argc); + } + else + { + fprintf (stderr, PGM ": unknown argument: %s\n", *argv); + print_help (); + } + + return 0; +} + +#endif /* !NO_GET_NSEC_TIME */ |