[PATCH 2/8] tests/bench-slope: add ECC benchmarking

Jussi Kivilinna jussi.kivilinna at iki.fi
Mon Apr 26 23:00:40 CEST 2021


* tests/bench-slope.c (bench_print_result_nsec_per_iteration): New.
(bench_print_header_nsec_per_iteration): New.
(kdf_bench_one, kdf_bench): Use new print helper functions.
[USE_ECC]: New ECC benchmarks.
(ecc_bench): New.
(print_help): Add 'ecc' option.
(main): Add ECC benchmarks.
--

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 tests/bench-slope.c | 692 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 640 insertions(+), 52 deletions(-)

diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index c8647b6b..9b4a139a 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -783,6 +783,54 @@ bench_print_result (double nsecs_per_byte)
     bench_print_result_std (nsecs_per_byte);
 }
 
+static void
+bench_print_result_nsec_per_iteration (double nsecs_per_iteration)
+{
+  double cycles_per_iteration;
+  char nsecpiter_buf[16];
+  char cpiter_buf[16];
+  char mhz_buf[16];
+
+  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);
+    }
+}
+
 static void
 bench_print_section (const char *section_name, const char *print_name)
 {
@@ -819,6 +867,28 @@ bench_print_header (int algo_width, const char *algo_name)
     }
 }
 
+static void
+bench_print_header_nsec_per_iteration (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 %13s %9s\n", "nanosecs/iter", "cycles/iter", "auto Mhz");
+      else
+        printf ("%14s %13s\n", "nanosecs/iter", "cycles/iter");
+    }
+}
+
 static void
 bench_print_algo (int algo_width, const char *algo_name)
 {
@@ -1991,11 +2061,7 @@ 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;
@@ -2030,45 +2096,7 @@ kdf_bench_one (int algo, int subalgo)
   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);
-    }
+  bench_print_result_nsec_per_iteration (nsecs_per_iteration);
 }
 
 void
@@ -2079,14 +2107,7 @@ kdf_bench (char **argv, int argc)
 
   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");
-    }
+  bench_print_header_nsec_per_iteration (24, "");
 
   if (argv && argc)
     {
@@ -2116,13 +2137,571 @@ kdf_bench (char **argv, int argc)
 }
 
 
+/************************************************************ ECC benchmarks. */
+
+#if USE_ECC
+enum bench_ecc_algo
+{
+  ECC_ALGO_ED25519 = 0,
+  ECC_ALGO_ED448,
+  ECC_ALGO_NIST_P192,
+  ECC_ALGO_NIST_P224,
+  ECC_ALGO_NIST_P256,
+  ECC_ALGO_NIST_P384,
+  ECC_ALGO_NIST_P521,
+  ECC_ALGO_SECP256K1,
+  __MAX_ECC_ALGO
+};
+
+enum bench_ecc_operation
+{
+  ECC_OPER_MULT = 0,
+  ECC_OPER_KEYGEN,
+  ECC_OPER_SIGN,
+  ECC_OPER_VERIFY,
+  __MAX_ECC_OPER
+};
+
+struct bench_ecc_oper
+{
+  enum bench_ecc_operation oper;
+  const char *name;
+  struct bench_ops *ops;
+
+  enum bench_ecc_algo algo;
+};
+
+struct bench_ecc_mult_hd
+{
+  gcry_ctx_t ec;
+  gcry_mpi_t k, x, y;
+  gcry_mpi_point_t G, Q;
+};
+
+struct bench_ecc_hd
+{
+  gcry_sexp_t key_spec;
+  gcry_sexp_t data;
+  gcry_sexp_t pub_key;
+  gcry_sexp_t sec_key;
+  gcry_sexp_t sig;
+};
+
+
+static const char *
+ecc_algo_name (int algo)
+{
+  switch (algo)
+    {
+      case ECC_ALGO_ED25519:
+	return "Ed25519";
+      case ECC_ALGO_ED448:
+	return "Ed448";
+      case ECC_ALGO_NIST_P192:
+	return "NIST-P192";
+      case ECC_ALGO_NIST_P224:
+	return "NIST-P224";
+      case ECC_ALGO_NIST_P256:
+	return "NIST-P256";
+      case ECC_ALGO_NIST_P384:
+	return "NIST-P384";
+      case ECC_ALGO_NIST_P521:
+	return "NIST-P521";
+      case ECC_ALGO_SECP256K1:
+	return "secp256k1";
+      default:
+	return NULL;
+    }
+}
+
+static const char *
+ecc_algo_curve (int algo)
+{
+  switch (algo)
+    {
+      case ECC_ALGO_ED25519:
+	return "Ed25519";
+      case ECC_ALGO_ED448:
+	return "Ed448";
+      case ECC_ALGO_NIST_P192:
+	return "NIST P-192";
+      case ECC_ALGO_NIST_P224:
+	return "NIST P-224";
+      case ECC_ALGO_NIST_P256:
+	return "NIST P-256";
+      case ECC_ALGO_NIST_P384:
+	return "NIST P-384";
+      case ECC_ALGO_NIST_P521:
+	return "NIST P-521";
+      case ECC_ALGO_SECP256K1:
+	return "secp256k1";
+      default:
+	return NULL;
+    }
+}
+
+static int
+ecc_nbits (int algo)
+{
+  switch (algo)
+    {
+      case ECC_ALGO_ED25519:
+	return 255;
+      case ECC_ALGO_ED448:
+	return 448;
+      case ECC_ALGO_NIST_P192:
+	return 192;
+      case ECC_ALGO_NIST_P224:
+	return 224;
+      case ECC_ALGO_NIST_P256:
+	return 256;
+      case ECC_ALGO_NIST_P384:
+	return 384;
+      case ECC_ALGO_NIST_P521:
+	return 521;
+      case ECC_ALGO_SECP256K1:
+	return 256;
+      default:
+	return 0;
+    }
+}
+
+static int
+ecc_map_name (const char *name)
+{
+  int i;
+
+  for (i = 0; i < __MAX_ECC_ALGO; i++)
+    {
+      if (strcmp(ecc_algo_name(i), name) == 0)
+	{
+	  return i;
+	}
+    }
+
+  return -1;
+}
+
+
+static int
+bench_ecc_mult_init (struct bench_obj *obj)
+{
+  struct bench_ecc_oper *oper = obj->priv;
+  struct bench_ecc_mult_hd *hd;
+  int p_size = ecc_nbits (oper->algo);
+  gpg_error_t err;
+  gcry_mpi_t p;
+
+  obj->min_bufsize = 1;
+  obj->max_bufsize = 4;
+  obj->step_size = 1;
+  obj->num_measure_repetitions =
+    num_measurement_repetitions / obj->max_bufsize;
+
+  while (obj->num_measure_repetitions == 0)
+    {
+      if (obj->max_bufsize == 2)
+	{
+	  obj->num_measure_repetitions = 2;
+	}
+      else
+	{
+	  obj->max_bufsize--;
+	  obj->num_measure_repetitions =
+	    num_measurement_repetitions / obj->max_bufsize;
+	}
+    }
+
+  hd = calloc (1, sizeof(*hd));
+  if (!hd)
+    return -1;
+
+  err = gcry_mpi_ec_new (&hd->ec, NULL, ecc_algo_curve(oper->algo));
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_mpi_ec_new failed: %s\n",
+	      gpg_strerror (err));
+      exit (1);
+    }
+  hd->G = gcry_mpi_ec_get_point ("g", hd->ec, 1);
+  hd->Q = gcry_mpi_point_new (0);
+  hd->x = gcry_mpi_new (0);
+  hd->y = gcry_mpi_new (0);
+  hd->k = gcry_mpi_new (p_size);
+  gcry_mpi_randomize (hd->k, p_size, GCRY_WEAK_RANDOM);
+  p = gcry_mpi_ec_get_mpi ("p", hd->ec, 1);
+  gcry_mpi_mod (hd->k, hd->k, p);
+  gcry_mpi_release (p);
+
+  obj->hd = hd;
+  return 0;
+}
+
+static void
+bench_ecc_mult_free (struct bench_obj *obj)
+{
+  struct bench_ecc_mult_hd *hd = obj->hd;
+
+  gcry_mpi_release (hd->k);
+  gcry_mpi_release (hd->y);
+  gcry_mpi_release (hd->x);
+  gcry_mpi_point_release (hd->Q);
+  gcry_mpi_point_release (hd->G);
+  gcry_ctx_release (hd->ec);
+  free (hd);
+  obj->hd = NULL;
+}
+
+static void
+bench_ecc_mult_do_bench (struct bench_obj *obj, void *buf, size_t num_iter)
+{
+  struct bench_ecc_mult_hd *hd = obj->hd;
+  size_t i;
+
+  (void)buf;
+
+  for (i = 0; i < num_iter; i++)
+    {
+      gcry_mpi_ec_mul (hd->Q, hd->k, hd->G, hd->ec);
+      if (gcry_mpi_ec_get_affine (hd->x, hd->y, hd->Q, hd->ec))
+	{
+	  fprintf (stderr, PGM ": gcry_mpi_ec_get_affine failed\n");
+	  exit (1);
+	}
+    }
+}
+
+
+static int
+bench_ecc_init (struct bench_obj *obj)
+{
+  struct bench_ecc_oper *oper = obj->priv;
+  struct bench_ecc_hd *hd;
+  int p_size = ecc_nbits (oper->algo);
+  gpg_error_t err;
+  gcry_mpi_t x;
+
+  obj->min_bufsize = 1;
+  obj->max_bufsize = 4;
+  obj->step_size = 1;
+  obj->num_measure_repetitions =
+    num_measurement_repetitions / obj->max_bufsize;
+
+  while (obj->num_measure_repetitions == 0)
+    {
+      if (obj->max_bufsize == 2)
+	{
+	  obj->num_measure_repetitions = 2;
+	}
+      else
+	{
+	  obj->max_bufsize--;
+	  obj->num_measure_repetitions =
+	    num_measurement_repetitions / obj->max_bufsize;
+	}
+    }
+
+  hd = calloc (1, sizeof(*hd));
+  if (!hd)
+    return -1;
+
+  x = gcry_mpi_new (p_size);
+  gcry_mpi_randomize (x, p_size, GCRY_WEAK_RANDOM);
+
+  switch (oper->algo)
+    {
+      default:
+	return -1;
+
+      case ECC_ALGO_ED25519:
+        err = gcry_sexp_build (&hd->key_spec, NULL,
+                               "(genkey (ecdsa (curve \"Ed25519\")"
+                               "(flags eddsa)))");
+	if (err)
+	  break;
+        err = gcry_sexp_build (&hd->data, NULL,
+                               "(data (flags eddsa)(hash-algo sha512)"
+                               " (value %m))", x);
+	break;
+
+      case ECC_ALGO_ED448:
+        err = gcry_sexp_build (&hd->key_spec, NULL,
+                               "(genkey (ecdsa (curve \"Ed448\")"
+                               "(flags eddsa)))");
+	if (err)
+	  break;
+        err = gcry_sexp_build (&hd->data, NULL,
+                               "(data (flags eddsa)(hash-algo shake256)"
+                               " (value %m))", x);
+	break;
+
+      case ECC_ALGO_NIST_P192:
+      case ECC_ALGO_NIST_P224:
+      case ECC_ALGO_NIST_P256:
+      case ECC_ALGO_NIST_P384:
+      case ECC_ALGO_NIST_P521:
+        err = gcry_sexp_build (&hd->key_spec, NULL,
+                               "(genkey (ECDSA (nbits %d)))", p_size);
+	if (err)
+	  break;
+        err = gcry_sexp_build (&hd->data, NULL,
+			       "(data (flags raw) (value %m))", x);
+	break;
+    }
+
+  gcry_mpi_release (x);
+
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_sexp_build failed: %s\n",
+	       gpg_strerror (err));
+      exit (1);
+    }
+
+  obj->hd = hd;
+  return 0;
+}
+
+static void
+bench_ecc_free (struct bench_obj *obj)
+{
+  struct bench_ecc_hd *hd = obj->hd;
+
+  gcry_sexp_release (hd->sig);
+  gcry_sexp_release (hd->pub_key);
+  gcry_sexp_release (hd->sec_key);
+  gcry_sexp_release (hd->data);
+  gcry_sexp_release (hd->key_spec);
+  free (hd);
+  obj->hd = NULL;
+}
+
+static void
+bench_ecc_keygen (struct bench_ecc_hd *hd)
+{
+  gcry_sexp_t key_pair;
+  gpg_error_t err;
+
+  err = gcry_pk_genkey (&key_pair, hd->key_spec);
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_pk_genkey failed: %s\n",
+		gpg_strerror (err));
+      exit (1);
+    }
+
+  hd->pub_key = gcry_sexp_find_token (key_pair, "public-key", 0);
+  if (!hd->pub_key)
+    {
+      fprintf (stderr, PGM ": public part missing in key\n");
+      exit (1);
+    }
+  hd->sec_key = gcry_sexp_find_token (key_pair, "private-key", 0);
+  if (!hd->sec_key)
+    {
+      fprintf (stderr, PGM ": private part missing in key\n");
+      exit (1);
+    }
+
+  gcry_sexp_release (key_pair);
+}
+
+static void
+bench_ecc_keygen_do_bench (struct bench_obj *obj, void *buf, size_t num_iter)
+{
+  struct bench_ecc_hd *hd = obj->hd;
+  size_t i;
+
+  (void)buf;
+
+  for (i = 0; i < num_iter; i++)
+    {
+      bench_ecc_keygen (hd);
+      gcry_sexp_release (hd->pub_key);
+      gcry_sexp_release (hd->sec_key);
+    }
+
+  hd->pub_key = NULL;
+  hd->sec_key = NULL;
+}
+
+static void
+bench_ecc_sign_do_bench (struct bench_obj *obj, void *buf, size_t num_iter)
+{
+  struct bench_ecc_hd *hd = obj->hd;
+  gpg_error_t err;
+  size_t i;
+
+  (void)buf;
+
+  bench_ecc_keygen (hd);
+
+  for (i = 0; i < num_iter; i++)
+    {
+      err = gcry_pk_sign (&hd->sig, hd->data, hd->sec_key);
+      if (err)
+	{
+	  fprintf (stderr, PGM ": gcry_pk_sign failed: %s\n",
+		  gpg_strerror (err));
+	  exit (1);
+	}
+      gcry_sexp_release (hd->sig);
+    }
+
+  gcry_sexp_release (hd->pub_key);
+  gcry_sexp_release (hd->sec_key);
+  hd->sig = NULL;
+  hd->pub_key = NULL;
+  hd->sec_key = NULL;
+}
+
+static void
+bench_ecc_verify_do_bench (struct bench_obj *obj, void *buf, size_t num_iter)
+{
+  struct bench_ecc_hd *hd = obj->hd;
+  gpg_error_t err;
+  int i;
+
+  (void)buf;
+
+  bench_ecc_keygen (hd);
+  err = gcry_pk_sign (&hd->sig, hd->data, hd->sec_key);
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_pk_sign failed: %s\n",
+	      gpg_strerror (err));
+      exit (1);
+    }
+
+  for (i = 0; i < num_iter; i++)
+    {
+      err = gcry_pk_verify (hd->sig, hd->data, hd->pub_key);
+      if (err)
+	{
+	  fprintf (stderr, PGM ": gcry_pk_verify failed: %s\n",
+		  gpg_strerror (err));
+	  exit (1);
+	}
+    }
+
+  gcry_sexp_release (hd->sig);
+  gcry_sexp_release (hd->pub_key);
+  gcry_sexp_release (hd->sec_key);
+  hd->sig = NULL;
+  hd->pub_key = NULL;
+  hd->sec_key = NULL;
+}
+
+
+static struct bench_ops ecc_mult_ops = {
+  &bench_ecc_mult_init,
+  &bench_ecc_mult_free,
+  &bench_ecc_mult_do_bench
+};
+
+static struct bench_ops ecc_keygen_ops = {
+  &bench_ecc_init,
+  &bench_ecc_free,
+  &bench_ecc_keygen_do_bench
+};
+
+static struct bench_ops ecc_sign_ops = {
+  &bench_ecc_init,
+  &bench_ecc_free,
+  &bench_ecc_sign_do_bench
+};
+
+static struct bench_ops ecc_verify_ops = {
+  &bench_ecc_init,
+  &bench_ecc_free,
+  &bench_ecc_verify_do_bench
+};
+
+
+static struct bench_ecc_oper ecc_operations[] = {
+  { ECC_OPER_MULT,   "mult",   &ecc_mult_ops },
+  { ECC_OPER_KEYGEN, "keygen", &ecc_keygen_ops },
+  { ECC_OPER_SIGN,   "sign",   &ecc_sign_ops },
+  { ECC_OPER_VERIFY, "verify", &ecc_verify_ops },
+  { 0, NULL, NULL }
+};
+
+
+static void
+cipher_ecc_one (enum bench_ecc_algo algo, struct bench_ecc_oper *poper)
+{
+  struct bench_ecc_oper oper = *poper;
+  struct bench_obj obj = { 0 };
+  double result;
+
+  if (algo == ECC_ALGO_SECP256K1 && oper.oper != ECC_OPER_MULT)
+    return;
+
+  oper.algo = algo;
+
+  bench_print_mode (14, oper.name);
+
+  obj.ops = oper.ops;
+  obj.priv = &oper;
+
+  result = do_slope_benchmark (&obj);
+  bench_print_result_nsec_per_iteration (result);
+}
+
+
+static void
+_ecc_bench (int algo)
+{
+  const char *algo_name;
+  int i;
+
+  algo_name = ecc_algo_name (algo);
+
+  bench_print_header_nsec_per_iteration (14, algo_name);
+
+  for (i = 0; ecc_operations[i].name; i++)
+    cipher_ecc_one (algo, &ecc_operations[i]);
+
+  bench_print_footer (14);
+}
+#endif
+
+
+void
+ecc_bench (char **argv, int argc)
+{
+#if USE_ECC
+  int i, algo;
+
+  bench_print_section ("ecc", "ECC");
+
+  if (argv && argc)
+    {
+      for (i = 0; i < argc; i++)
+        {
+          algo = ecc_map_name (argv[i]);
+          if (algo >= 0)
+            _ecc_bench (algo);
+        }
+    }
+  else
+    {
+      for (i = 0; i < __MAX_ECC_ALGO; i++)
+        _ecc_bench (i);
+    }
+#else
+  (void)argv;
+  (void)argc;
+#endif
+}
+
 /************************************************************** Main program. */
 
 void
 print_help (void)
 {
   static const char *help_lines[] = {
-    "usage: bench-slope [options] [hash|mac|cipher|kdf [algonames]]",
+    "usage: bench-slope [options] [hash|mac|cipher|kdf|ecc [algonames]]",
     "",
     " options:",
     "   --cpu-mhz <mhz>           Set CPU speed for calculating cycles",
@@ -2304,6 +2883,7 @@ main (int argc, char **argv)
       mac_bench (NULL, 0);
       cipher_bench (NULL, 0);
       kdf_bench (NULL, 0);
+      ecc_bench (NULL, 0);
     }
   else if (!strcmp (*argv, "hash"))
     {
@@ -2337,6 +2917,14 @@ main (int argc, char **argv)
       warm_up_cpu ();
       kdf_bench ((argc == 0) ? NULL : argv, argc);
     }
+  else if (!strcmp (*argv, "ecc"))
+    {
+      argc--;
+      argv++;
+
+      warm_up_cpu ();
+      ecc_bench ((argc == 0) ? NULL : argv, argc);
+    }
   else
     {
       fprintf (stderr, PGM ": unknown argument: %s\n", *argv);
-- 
2.30.2




More information about the Gcrypt-devel mailing list