2350 строки
53 KiB
C
2350 строки
53 KiB
C
/* 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 */
|