tests: Check the result of all gcry_control operations.
[libgcrypt.git] / tests / bench-slope.c
index 62543bc..4ed98cb 100644 (file)
@@ -1,5 +1,5 @@
 /* bench-slope.c - for libgcrypt
- * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi>
+ * Copyright (C) 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi>
  *
  * This file is part of Libgcrypt.
  *
 #include <time.h>
 
 #ifdef _GCRYPT_IN_LIBGCRYPT
-#include "../src/gcrypt-int.h"
-#include "../compat/libcompat.h"
+# include "../src/gcrypt-int.h"
+# include "../compat/libcompat.h"
 #else
-#include <gcrypt.h>
+# 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;
 
+/* 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;
 
 
 /*************************************** Default parameters for measurements. */
@@ -55,12 +72,12 @@ static double cpu_ghz = -1;
  * (SUPERCOP). */
 #define BUF_END_SIZE                   (BUF_START_SIZE + 4096)
 
-/* With 128 byte steps, we get (4096)/128 = 32 data points. */
-#define BUF_STEP_SIZE                  128
+/* 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    32
+#define NUM_MEASUREMENT_REPETITIONS    64
 
 /**************************************************** High-resolution timers. */
 
@@ -239,7 +256,7 @@ get_slope (double (*const get_x) (unsigned int idx, void *priv),
       sumx += x;
       sumy += y;
       sumx2 += x * x;
-      //sumy2 += y * y;
+      /*sumy2 += y * y;*/
       sumxy += x * y;
     }
 
@@ -275,12 +292,12 @@ get_num_measurements (struct bench_obj *obj)
 
 
 static int
-double_cmp (const void *__a, const void *__b)
+double_cmp (const void *_a, const void *_b)
 {
   const double *a, *b;
 
-  a = __a;
-  b = __b;
+  a = _a;
+  b = _b;
 
   if (*a > *b)
     return 1;
@@ -396,12 +413,14 @@ do_slope_benchmark (struct bench_obj *obj)
       obj->max_bufsize < 1 || obj->min_bufsize > obj->max_bufsize)
     goto err_free;
 
-  real_buffer = malloc (obj->max_bufsize + 128);
+  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);
@@ -427,6 +446,7 @@ do_slope_benchmark (struct bench_obj *obj)
               &overhead);
 
   free (measurement_raw);
+  free (measurements);
   free (real_buffer);
   obj->ops->finalize (obj);
 
@@ -463,14 +483,14 @@ double_to_str (char *out, size_t outlen, double value)
 }
 
 static void
-bench_print_result (double nsecs_per_byte)
+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];
 
-  strcpy (cpbyte_buf, "-");
+  *cpbyte_buf = 0;
 
   double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte);
 
@@ -485,25 +505,124 @@ bench_print_result (double nsecs_per_byte)
     (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024);
   double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec);
 
-  strncat (nsecpbyte_buf, " ns/B", sizeof (nsecpbyte_buf) - 1);
-  strncat (mbpsec_buf, " MiB/s", sizeof (mbpsec_buf) - 1);
-  strncat (cpbyte_buf, " c/B", sizeof (cpbyte_buf) - 1);
+  /* We print two empty fields to allow for future enhancements.  */
+  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);
 
-  printf ("%14s %15s %13s\n", nsecpbyte_buf, mbpsec_buf, cpbyte_buf);
 }
 
 static void
-bench_print_header (const char *algo_name)
+bench_print_result_std (double nsecs_per_byte)
 {
-  printf (" %-14s | ", algo_name);
-  printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec",
-         "cycles/byte");
+  double cycles_per_byte, mbytes_per_sec;
+  char nsecpbyte_buf[16];
+  char mbpsec_buf[16];
+  char cpbyte_buf[16];
+
+  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 (cpu_ghz > 0.0)
+    {
+      cycles_per_byte = nsecs_per_byte * cpu_ghz;
+      double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte);
+    }
+  else
+    strcpy (cpbyte_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);
+
+  printf ("%9s ns/B %9s MiB/s %9s c/B\n",
+          nsecpbyte_buf, mbpsec_buf, cpbyte_buf);
 }
 
 static void
-bench_print_footer (void)
+bench_print_result (double nsecs_per_byte)
 {
-  printf (" %-14s =\n", "");
+  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);
+      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, "");
 }
 
 
@@ -529,7 +648,7 @@ bench_encrypt_init (struct bench_obj *obj)
   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;
+  obj->num_measure_repetitions = num_measurement_repetitions;
 
   err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0);
   if (err)
@@ -623,7 +742,6 @@ static struct bench_ops decrypt_ops = {
 };
 
 
-
 static void
 bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
 {
@@ -631,7 +749,7 @@ bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
   int err;
   char tag[8];
   char nonce[11] = { 0x80, 0x01, };
-  size_t params[3];
+  u64 params[3];
 
   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
 
@@ -675,7 +793,7 @@ bench_ccm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
   int err;
   char tag[8] = { 0, };
   char nonce[11] = { 0x80, 0x01, };
-  size_t params[3];
+  u64 params[3];
 
   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
 
@@ -722,7 +840,7 @@ bench_ccm_authenticate_do_bench (struct bench_obj *obj, void *buf,
   int err;
   char tag[8] = { 0, };
   char nonce[11] = { 0x80, 0x01, };
-  size_t params[3];
+  u64 params[3];
   char data = 0xff;
 
   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
@@ -788,6 +906,255 @@ static struct bench_ops ccm_authenticate_ops = {
 };
 
 
+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->priv;
+  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->priv;
+  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->priv;
+  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_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},
@@ -802,6 +1169,15 @@ static struct bench_cipher_mode cipher_modes[] = {
   {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_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},
 };
 
@@ -821,8 +1197,9 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   if (!blklen)
     return;
 
-  /* Stream cipher? Only test with ECB. */
-  if (blklen == 1 && mode.mode != GCRY_CIPHER_MODE_ECB)
+  /* 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)
     {
@@ -830,12 +1207,23 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
       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;
 
-  printf (" %14s | ", mode.name);
-  fflush (stdout);
+  /* GCM has restrictions for block-size */
+  if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN)
+    return;
+
+  /* Our OCB implementaion has restrictions for block-size.  */
+  if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != 16)
+    return;
+
+  bench_print_mode (14, mode.name);
 
   obj.ops = mode.ops;
   obj.priv = &mode;
@@ -847,19 +1235,19 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
 
 
 static void
-__cipher_bench (int algo)
+_cipher_bench (int algo)
 {
   const char *algoname;
   int i;
 
   algoname = gcry_cipher_algo_name (algo);
 
-  bench_print_header (algoname);
+  bench_print_header (14, algoname);
 
   for (i = 0; cipher_modes[i].mode; i++)
     cipher_bench_one (algo, &cipher_modes[i]);
 
-  bench_print_footer ();
+  bench_print_footer (14);
 }
 
 
@@ -868,22 +1256,22 @@ cipher_bench (char **argv, int argc)
 {
   int i, algo;
 
-  printf ("Cipher:\n");
+  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);
-       }
+        {
+          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);
+        if (!gcry_cipher_test_algo (i))
+          _cipher_bench (i);
     }
 }
 
@@ -909,7 +1297,7 @@ bench_hash_init (struct bench_obj *obj)
   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;
+  obj->num_measure_repetitions = num_measurement_repetitions;
 
   err = gcry_md_open (&hd, mode->algo, 0);
   if (err)
@@ -937,6 +1325,7 @@ bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
 {
   gcry_md_hd_t hd = obj->priv;
 
+  gcry_md_reset (hd);
   gcry_md_write (hd, buf, buflen);
   gcry_md_final (hd);
 }
@@ -964,10 +1353,9 @@ hash_bench_one (int algo, struct bench_hash_mode *pmode)
   mode.algo = algo;
 
   if (mode.name[0] == '\0')
-    printf (" %-14s | ", gcry_md_algo_name (algo));
+    bench_print_algo (-14, gcry_md_algo_name (algo));
   else
-    printf (" %14s | ", mode.name);
-  fflush (stdout);
+    bench_print_algo (14, mode.name);
 
   obj.ops = mode.ops;
   obj.priv = &mode;
@@ -978,7 +1366,7 @@ hash_bench_one (int algo, struct bench_hash_mode *pmode)
 }
 
 static void
-__hash_bench (int algo)
+_hash_bench (int algo)
 {
   int i;
 
@@ -991,9 +1379,8 @@ hash_bench (char **argv, int argc)
 {
   int i, algo;
 
-  printf ("Hash:\n");
-
-  bench_print_header ("");
+  bench_print_section ("hash", "Hash");
+  bench_print_header (14, "");
 
   if (argv && argc)
     {
@@ -1001,17 +1388,352 @@ hash_bench (char **argv, int argc)
        {
          algo = gcry_md_map_name (argv[i]);
          if (algo)
-           __hash_bench (algo);
+           _hash_bench (algo);
        }
     }
   else
     {
       for (i = 1; i < 400; i++)
        if (!gcry_md_test_algo (i))
-         __hash_bench (i);
+         _hash_bench (i);
     }
 
-  bench_print_footer ();
+  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->priv = hd;
+
+  free (key);
+  return 0;
+}
+
+static void
+bench_mac_free (struct bench_obj *obj)
+{
+  gcry_mac_hd_t hd = obj->priv;
+
+  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->priv;
+  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];
+
+  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 ? "" : "-");
+
+  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 (cpu_ghz > 0.0)
+    {
+      cycles_per_iteration = nsecs_per_iteration * cpu_ghz;
+      double_to_str (cpiter_buf, sizeof (cpiter_buf), cycles_per_iteration);
+    }
+
+  if (csv_mode)
+    {
+      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
+    {
+      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, "");
+      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);
 }
 
 
@@ -1021,13 +1743,17 @@ void
 print_help (void)
 {
   static const char *help_lines[] = {
-    "usage: bench-slope [options] [hash|cipher [algonames]]",
+    "usage: bench-slope [options] [hash|mac|cipher|kdf [algonames]]",
     "",
     " options:",
-    "     --cpu-mhz <mhz>           Set CPU speed for calculating cycles per bytes",
-    "                               results.",
-    "     --disable-hwf <features>  Disable hardware acceleration feature(s) for",
-    "                               benchmarking.",
+    "   --cpu-mhz <mhz>           Set CPU speed for calculating cycles",
+    "                             per bytes results.",
+    "   --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;
@@ -1056,7 +1782,6 @@ int
 main (int argc, char **argv)
 {
   int last_argc = -1;
-  int debug = 0;
 
   if (argc)
     {
@@ -1064,6 +1789,19 @@ main (int argc, char **argv)
       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;
@@ -1092,6 +1830,18 @@ main (int argc, char **argv)
          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--;
@@ -1120,9 +1870,28 @@ main (int argc, char **argv)
              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++;
+           }
+       }
     }
 
-  gcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
+  xgcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
 
   if (!gcry_check_version (GCRYPT_VERSION))
     {
@@ -1132,17 +1901,22 @@ main (int argc, char **argv)
     }
 
   if (debug)
-    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+    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);
 
-  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
-  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-  gcry_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"))
     {
@@ -1152,6 +1926,14 @@ main (int argc, char **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--;
@@ -1160,6 +1942,14 @@ main (int argc, char **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);