Merge branch 'master' into LIBGCRYPT-1-7-BRANCH
[libgcrypt.git] / tests / bench-slope.c
1 /* bench-slope.c - for libgcrypt
2  * Copyright (C) 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi>
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser general Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <assert.h>
27 #include <time.h>
28
29 #ifdef _GCRYPT_IN_LIBGCRYPT
30 # include "../src/gcrypt-int.h"
31 # include "../compat/libcompat.h"
32 #else
33 # include <gcrypt.h>
34 #endif
35
36 #ifndef STR
37 #define STR(v) #v
38 #define STR2(v) STR(v)
39 #endif
40
41 #define PGM "bench-slope"
42
43 static int verbose;
44 static int csv_mode;
45 static int unaligned_mode;
46 static int num_measurement_repetitions;
47
48 /* CPU Ghz value provided by user, allows constructing cycles/byte and other
49    results.  */
50 static double cpu_ghz = -1;
51
52 /* Whether we are running as part of the regression test suite.  */
53 static int in_regression_test;
54
55 /* The name of the currently printed section.  */
56 static char *current_section_name;
57 /* The name of the currently printed algorithm.  */
58 static char *current_algo_name;
59 /* The name of the currently printed mode.  */
60 static char *current_mode_name;
61
62
63 /*************************************** Default parameters for measurements. */
64
65 /* Start at small buffer size, to get reasonable timer calibration for fast
66  * implementations (AES-NI etc). Sixteen selected to support the largest block
67  * size of current set cipher blocks. */
68 #define BUF_START_SIZE                  16
69
70 /* From ~0 to ~4kbytes give comparable results with results from academia
71  * (SUPERCOP). */
72 #define BUF_END_SIZE                    (BUF_START_SIZE + 4096)
73
74 /* With 128 byte steps, we get (4096)/64 = 64 data points. */
75 #define BUF_STEP_SIZE                   64
76
77 /* Number of repeated measurements at each data point. The median of these
78  * measurements is selected as data point further analysis. */
79 #define NUM_MEASUREMENT_REPETITIONS     64
80
81 /**************************************************** High-resolution timers. */
82
83 /* This benchmarking module needs needs high resolution timer.  */
84 #undef NO_GET_NSEC_TIME
85 #if defined(_WIN32)
86 struct nsec_time
87 {
88   LARGE_INTEGER perf_count;
89 };
90
91 static void
92 get_nsec_time (struct nsec_time *t)
93 {
94   BOOL ok;
95
96   ok = QueryPerformanceCounter (&t->perf_count);
97   assert (ok);
98 }
99
100 static double
101 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
102 {
103   static double nsecs_per_count = 0.0;
104   double nsecs;
105
106   if (nsecs_per_count == 0.0)
107     {
108       LARGE_INTEGER perf_freq;
109       BOOL ok;
110
111       /* Get counts per second. */
112       ok = QueryPerformanceFrequency (&perf_freq);
113       assert (ok);
114
115       nsecs_per_count = 1.0 / perf_freq.QuadPart;
116       nsecs_per_count *= 1000000.0 * 1000.0;    /* sec => nsec */
117
118       assert (nsecs_per_count > 0.0);
119     }
120
121   nsecs = end->perf_count.QuadPart - start->perf_count.QuadPart;        /* counts */
122   nsecs *= nsecs_per_count;     /* counts * (nsecs / count) => nsecs */
123
124   return nsecs;
125 }
126 #elif defined(HAVE_CLOCK_GETTIME)
127 struct nsec_time
128 {
129   struct timespec ts;
130 };
131
132 static void
133 get_nsec_time (struct nsec_time *t)
134 {
135   int err;
136
137   err = clock_gettime (CLOCK_REALTIME, &t->ts);
138   assert (err == 0);
139 }
140
141 static double
142 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
143 {
144   double nsecs;
145
146   nsecs = end->ts.tv_sec - start->ts.tv_sec;
147   nsecs *= 1000000.0 * 1000.0;  /* sec => nsec */
148
149   /* This way we don't have to care if tv_nsec unsigned or signed. */
150   if (end->ts.tv_nsec >= start->ts.tv_nsec)
151     nsecs += end->ts.tv_nsec - start->ts.tv_nsec;
152   else
153     nsecs -= start->ts.tv_nsec - end->ts.tv_nsec;
154
155   return nsecs;
156 }
157 #elif defined(HAVE_GETTIMEOFDAY)
158 struct nsec_time
159 {
160   struct timeval tv;
161 };
162
163 static void
164 get_nsec_time (struct nsec_time *t)
165 {
166   int err;
167
168   err = gettimeofday (&t->tv, NULL);
169   assert (err == 0);
170 }
171
172 static double
173 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
174 {
175   double nsecs;
176
177   nsecs = end->tv.tv_sec - start->tv.tv_sec;
178   nsecs *= 1000000;             /* sec => ┬Ásec */
179
180   /* This way we don't have to care if tv_usec unsigned or signed. */
181   if (end->tv.tv_usec >= start->tv.tv_usec)
182     nsecs += end->tv.tv_usec - start->tv.tv_usec;
183   else
184     nsecs -= start->tv.tv_usec - end->tv.tv_usec;
185
186   nsecs *= 1000;                /* ┬Ásec => nsec */
187
188   return nsecs;
189 }
190 #else
191 #define NO_GET_NSEC_TIME 1
192 #endif
193
194
195 /* If no high resolution timer found, provide dummy bench-slope.  */
196 #ifdef NO_GET_NSEC_TIME
197
198
199 int
200 main (void)
201 {
202   /* No nsec timer => SKIP test. */
203   return 77;
204 }
205
206
207 #else /* !NO_GET_NSEC_TIME */
208
209
210 /********************************************** Slope benchmarking framework. */
211
212 struct bench_obj
213 {
214   const struct bench_ops *ops;
215
216   unsigned int num_measure_repetitions;
217   unsigned int min_bufsize;
218   unsigned int max_bufsize;
219   unsigned int step_size;
220
221   void *priv;
222 };
223
224 typedef int (*const bench_initialize_t) (struct bench_obj * obj);
225 typedef void (*const bench_finalize_t) (struct bench_obj * obj);
226 typedef void (*const bench_do_run_t) (struct bench_obj * obj, void *buffer,
227                                       size_t buflen);
228
229 struct bench_ops
230 {
231   bench_initialize_t initialize;
232   bench_finalize_t finalize;
233   bench_do_run_t do_run;
234 };
235
236
237 double
238 get_slope (double (*const get_x) (unsigned int idx, void *priv),
239            void *get_x_priv, double y_points[], unsigned int npoints,
240            double *overhead)
241 {
242   double sumx, sumy, sumx2, sumy2, sumxy;
243   unsigned int i;
244   double b, a;
245
246   sumx = sumy = sumx2 = sumy2 = sumxy = 0;
247
248   for (i = 0; i < npoints; i++)
249     {
250       double x, y;
251
252       x = get_x (i, get_x_priv);        /* bytes */
253       y = y_points[i];          /* nsecs */
254
255       sumx += x;
256       sumy += y;
257       sumx2 += x * x;
258       /*sumy2 += y * y;*/
259       sumxy += x * y;
260     }
261
262   b = (npoints * sumxy - sumx * sumy) / (npoints * sumx2 - sumx * sumx);
263   a = (sumy - b * sumx) / npoints;
264
265   if (overhead)
266     *overhead = a;              /* nsecs */
267
268   return b;                     /* nsecs per byte */
269 }
270
271
272 double
273 get_bench_obj_point_x (unsigned int idx, void *priv)
274 {
275   struct bench_obj *obj = priv;
276   return (double) (obj->min_bufsize + (idx * obj->step_size));
277 }
278
279
280 unsigned int
281 get_num_measurements (struct bench_obj *obj)
282 {
283   unsigned int buf_range = obj->max_bufsize - obj->min_bufsize;
284   unsigned int num = buf_range / obj->step_size + 1;
285
286   while (obj->min_bufsize + (num * obj->step_size) > obj->max_bufsize)
287     num--;
288
289   return num + 1;
290 }
291
292
293 static int
294 double_cmp (const void *_a, const void *_b)
295 {
296   const double *a, *b;
297
298   a = _a;
299   b = _b;
300
301   if (*a > *b)
302     return 1;
303   if (*a < *b)
304     return -1;
305   return 0;
306 }
307
308
309 double
310 do_bench_obj_measurement (struct bench_obj *obj, void *buffer, size_t buflen,
311                           double *measurement_raw,
312                           unsigned int loop_iterations)
313 {
314   const unsigned int num_repetitions = obj->num_measure_repetitions;
315   const bench_do_run_t do_run = obj->ops->do_run;
316   struct nsec_time start, end;
317   unsigned int rep, loop;
318   double res;
319
320   if (num_repetitions < 1 || loop_iterations < 1)
321     return 0.0;
322
323   for (rep = 0; rep < num_repetitions; rep++)
324     {
325       get_nsec_time (&start);
326
327       for (loop = 0; loop < loop_iterations; loop++)
328         do_run (obj, buffer, buflen);
329
330       get_nsec_time (&end);
331
332       measurement_raw[rep] = get_time_nsec_diff (&start, &end);
333     }
334
335   /* Return median of repeated measurements. */
336   qsort (measurement_raw, num_repetitions, sizeof (measurement_raw[0]),
337          double_cmp);
338
339   if (num_repetitions % 2 == 1)
340     return measurement_raw[num_repetitions / 2];
341
342   res = measurement_raw[num_repetitions / 2]
343     + measurement_raw[num_repetitions / 2 - 1];
344   return res / 2;
345 }
346
347
348 unsigned int
349 adjust_loop_iterations_to_timer_accuracy (struct bench_obj *obj, void *buffer,
350                                           double *measurement_raw)
351 {
352   const double increase_thres = 3.0;
353   double tmp, nsecs;
354   unsigned int loop_iterations;
355   unsigned int test_bufsize;
356
357   test_bufsize = obj->min_bufsize;
358   if (test_bufsize == 0)
359     test_bufsize += obj->step_size;
360
361   loop_iterations = 0;
362   do
363     {
364       /* Increase loop iterations until we get other results than zero.  */
365       nsecs =
366         do_bench_obj_measurement (obj, buffer, test_bufsize,
367                                   measurement_raw, ++loop_iterations);
368     }
369   while (nsecs < 1.0 - 0.1);
370   do
371     {
372       /* Increase loop iterations until we get reasonable increase for elapsed time.  */
373       tmp =
374         do_bench_obj_measurement (obj, buffer, test_bufsize,
375                                   measurement_raw, ++loop_iterations);
376     }
377   while (tmp < nsecs * (increase_thres - 0.1));
378
379   return loop_iterations;
380 }
381
382
383 /* Benchmark and return linear regression slope in nanoseconds per byte.  */
384 double
385 do_slope_benchmark (struct bench_obj *obj)
386 {
387   unsigned int num_measurements;
388   double *measurements = NULL;
389   double *measurement_raw = NULL;
390   double slope, overhead;
391   unsigned int loop_iterations, midx, i;
392   unsigned char *real_buffer = NULL;
393   unsigned char *buffer;
394   size_t cur_bufsize;
395   int err;
396
397   err = obj->ops->initialize (obj);
398   if (err < 0)
399     return -1;
400
401   num_measurements = get_num_measurements (obj);
402   measurements = calloc (num_measurements, sizeof (*measurements));
403   if (!measurements)
404     goto err_free;
405
406   measurement_raw =
407     calloc (obj->num_measure_repetitions, sizeof (*measurement_raw));
408   if (!measurement_raw)
409     goto err_free;
410
411   if (num_measurements < 1 || obj->num_measure_repetitions < 1 ||
412       obj->max_bufsize < 1 || obj->min_bufsize > obj->max_bufsize)
413     goto err_free;
414
415   real_buffer = malloc (obj->max_bufsize + 128 + unaligned_mode);
416   if (!real_buffer)
417     goto err_free;
418   /* Get aligned buffer */
419   buffer = real_buffer;
420   buffer += 128 - ((real_buffer - (unsigned char *) 0) & (128 - 1));
421   if (unaligned_mode)
422     buffer += unaligned_mode; /* Make buffer unaligned */
423
424   for (i = 0; i < obj->max_bufsize; i++)
425     buffer[i] = 0x55 ^ (-i);
426
427   /* Adjust number of loop iterations up to timer accuracy.  */
428   loop_iterations = adjust_loop_iterations_to_timer_accuracy (obj, buffer,
429                                                               measurement_raw);
430
431   /* Perform measurements */
432   for (midx = 0, cur_bufsize = obj->min_bufsize;
433        cur_bufsize <= obj->max_bufsize; cur_bufsize += obj->step_size, midx++)
434     {
435       measurements[midx] =
436         do_bench_obj_measurement (obj, buffer, cur_bufsize, measurement_raw,
437                                   loop_iterations);
438       measurements[midx] /= loop_iterations;
439     }
440
441   assert (midx == num_measurements);
442
443   slope =
444     get_slope (&get_bench_obj_point_x, obj, measurements, num_measurements,
445                &overhead);
446
447   free (measurement_raw);
448   free (measurements);
449   free (real_buffer);
450   obj->ops->finalize (obj);
451
452   return slope;
453
454 err_free:
455   if (measurement_raw)
456     free (measurement_raw);
457   if (measurements)
458     free (measurements);
459   if (real_buffer)
460     free (real_buffer);
461   obj->ops->finalize (obj);
462
463   return -1;
464 }
465
466
467 /********************************************************** Printing results. */
468
469 static void
470 double_to_str (char *out, size_t outlen, double value)
471 {
472   const char *fmt;
473
474   if (value < 1.0)
475     fmt = "%.3f";
476   else if (value < 100.0)
477     fmt = "%.2f";
478   else
479     fmt = "%.1f";
480
481   snprintf (out, outlen, fmt, value);
482 }
483
484 static void
485 bench_print_result_csv (double nsecs_per_byte)
486 {
487   double cycles_per_byte, mbytes_per_sec;
488   char nsecpbyte_buf[16];
489   char mbpsec_buf[16];
490   char cpbyte_buf[16];
491
492   *cpbyte_buf = 0;
493
494   double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte);
495
496   /* If user didn't provide CPU speed, we cannot show cycles/byte results.  */
497   if (cpu_ghz > 0.0)
498     {
499       cycles_per_byte = nsecs_per_byte * cpu_ghz;
500       double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte);
501     }
502
503   mbytes_per_sec =
504     (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024);
505   double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec);
506
507   /* We print two empty fields to allow for future enhancements.  */
508   printf ("%s,%s,%s,,,%s,ns/B,%s,MiB/s,%s,c/B\n",
509           current_section_name,
510           current_algo_name? current_algo_name : "",
511           current_mode_name? current_mode_name : "",
512           nsecpbyte_buf,
513           mbpsec_buf,
514           cpbyte_buf);
515
516 }
517
518 static void
519 bench_print_result_std (double nsecs_per_byte)
520 {
521   double cycles_per_byte, mbytes_per_sec;
522   char nsecpbyte_buf[16];
523   char mbpsec_buf[16];
524   char cpbyte_buf[16];
525
526   double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte);
527
528   /* If user didn't provide CPU speed, we cannot show cycles/byte results.  */
529   if (cpu_ghz > 0.0)
530     {
531       cycles_per_byte = nsecs_per_byte * cpu_ghz;
532       double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte);
533     }
534   else
535     strcpy (cpbyte_buf, "-");
536
537   mbytes_per_sec =
538     (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024);
539   double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec);
540
541   printf ("%9s ns/B %9s MiB/s %9s c/B\n",
542           nsecpbyte_buf, mbpsec_buf, cpbyte_buf);
543 }
544
545 static void
546 bench_print_result (double nsecs_per_byte)
547 {
548   if (csv_mode)
549     bench_print_result_csv (nsecs_per_byte);
550   else
551     bench_print_result_std (nsecs_per_byte);
552 }
553
554 static void
555 bench_print_section (const char *section_name, const char *print_name)
556 {
557   if (csv_mode)
558     {
559       gcry_free (current_section_name);
560       current_section_name = gcry_xstrdup (section_name);
561     }
562   else
563     printf ("%s:\n", print_name);
564 }
565
566 static void
567 bench_print_header (int algo_width, const char *algo_name)
568 {
569   if (csv_mode)
570     {
571       gcry_free (current_algo_name);
572       current_algo_name = gcry_xstrdup (algo_name);
573     }
574   else
575     {
576       if (algo_width < 0)
577         printf (" %-*s | ", -algo_width, algo_name);
578       else
579         printf (" %-*s | ", algo_width, algo_name);
580       printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec",
581               "cycles/byte");
582     }
583 }
584
585 static void
586 bench_print_algo (int algo_width, const char *algo_name)
587 {
588   if (csv_mode)
589     {
590       gcry_free (current_algo_name);
591       current_algo_name = gcry_xstrdup (algo_name);
592     }
593   else
594     {
595       if (algo_width < 0)
596         printf (" %-*s | ", -algo_width, algo_name);
597       else
598         printf (" %-*s | ", algo_width, algo_name);
599     }
600 }
601
602 static void
603 bench_print_mode (int width, const char *mode_name)
604 {
605   if (csv_mode)
606     {
607       gcry_free (current_mode_name);
608       current_mode_name = gcry_xstrdup (mode_name);
609     }
610   else
611     {
612       if (width < 0)
613         printf (" %-*s | ", -width, mode_name);
614       else
615         printf (" %*s | ", width, mode_name);
616       fflush (stdout);
617     }
618 }
619
620 static void
621 bench_print_footer (int algo_width)
622 {
623   if (!csv_mode)
624     printf (" %-*s =\n", algo_width, "");
625 }
626
627
628 /********************************************************* Cipher benchmarks. */
629
630 struct bench_cipher_mode
631 {
632   int mode;
633   const char *name;
634   struct bench_ops *ops;
635
636   int algo;
637 };
638
639
640 static int
641 bench_encrypt_init (struct bench_obj *obj)
642 {
643   struct bench_cipher_mode *mode = obj->priv;
644   gcry_cipher_hd_t hd;
645   int err, keylen;
646
647   obj->min_bufsize = BUF_START_SIZE;
648   obj->max_bufsize = BUF_END_SIZE;
649   obj->step_size = BUF_STEP_SIZE;
650   obj->num_measure_repetitions = num_measurement_repetitions;
651
652   err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0);
653   if (err)
654     {
655       fprintf (stderr, PGM ": error opening cipher `%s'\n",
656                gcry_cipher_algo_name (mode->algo));
657       exit (1);
658     }
659
660   keylen = gcry_cipher_get_algo_keylen (mode->algo);
661   if (keylen)
662     {
663       char key[keylen];
664       int i;
665
666       for (i = 0; i < keylen; i++)
667         key[i] = 0x33 ^ (11 - i);
668
669       err = gcry_cipher_setkey (hd, key, keylen);
670       if (err)
671         {
672           fprintf (stderr, PGM ": gcry_cipher_setkey failed: %s\n",
673                    gpg_strerror (err));
674           gcry_cipher_close (hd);
675           exit (1);
676         }
677     }
678   else
679     {
680       fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n",
681                gcry_cipher_algo_name (mode->algo));
682       gcry_cipher_close (hd);
683       exit (1);
684     }
685
686   obj->priv = hd;
687
688   return 0;
689 }
690
691 static void
692 bench_encrypt_free (struct bench_obj *obj)
693 {
694   gcry_cipher_hd_t hd = obj->priv;
695
696   gcry_cipher_close (hd);
697 }
698
699 static void
700 bench_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
701 {
702   gcry_cipher_hd_t hd = obj->priv;
703   int err;
704
705   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
706   if (err)
707     {
708       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
709                gpg_strerror (err));
710       gcry_cipher_close (hd);
711       exit (1);
712     }
713 }
714
715 static void
716 bench_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
717 {
718   gcry_cipher_hd_t hd = obj->priv;
719   int err;
720
721   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
722   if (err)
723     {
724       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
725                gpg_strerror (err));
726       gcry_cipher_close (hd);
727       exit (1);
728     }
729 }
730
731 static struct bench_ops encrypt_ops = {
732   &bench_encrypt_init,
733   &bench_encrypt_free,
734   &bench_encrypt_do_bench
735 };
736
737 static struct bench_ops decrypt_ops = {
738   &bench_encrypt_init,
739   &bench_encrypt_free,
740   &bench_decrypt_do_bench
741 };
742
743
744 static void
745 bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
746 {
747   gcry_cipher_hd_t hd = obj->priv;
748   int err;
749   char tag[8];
750   char nonce[11] = { 0x80, 0x01, };
751   u64 params[3];
752
753   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
754
755   /* Set CCM lengths */
756   params[0] = buflen;
757   params[1] = 0;                /*aadlen */
758   params[2] = sizeof (tag);
759   err =
760     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
761   if (err)
762     {
763       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
764                gpg_strerror (err));
765       gcry_cipher_close (hd);
766       exit (1);
767     }
768
769   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
770   if (err)
771     {
772       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
773                gpg_strerror (err));
774       gcry_cipher_close (hd);
775       exit (1);
776     }
777
778   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
779   if (err)
780     {
781       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
782                gpg_strerror (err));
783       gcry_cipher_close (hd);
784       exit (1);
785     }
786 }
787
788 static void
789 bench_ccm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
790 {
791   gcry_cipher_hd_t hd = obj->priv;
792   int err;
793   char tag[8] = { 0, };
794   char nonce[11] = { 0x80, 0x01, };
795   u64 params[3];
796
797   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
798
799   /* Set CCM lengths */
800   params[0] = buflen;
801   params[1] = 0;                /*aadlen */
802   params[2] = sizeof (tag);
803   err =
804     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
805   if (err)
806     {
807       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
808                gpg_strerror (err));
809       gcry_cipher_close (hd);
810       exit (1);
811     }
812
813   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
814   if (err)
815     {
816       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
817                gpg_strerror (err));
818       gcry_cipher_close (hd);
819       exit (1);
820     }
821
822   err = gcry_cipher_checktag (hd, tag, sizeof (tag));
823   if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
824     err = gpg_error (GPG_ERR_NO_ERROR);
825   if (err)
826     {
827       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
828                gpg_strerror (err));
829       gcry_cipher_close (hd);
830       exit (1);
831     }
832 }
833
834 static void
835 bench_ccm_authenticate_do_bench (struct bench_obj *obj, void *buf,
836                                  size_t buflen)
837 {
838   gcry_cipher_hd_t hd = obj->priv;
839   int err;
840   char tag[8] = { 0, };
841   char nonce[11] = { 0x80, 0x01, };
842   u64 params[3];
843   char data = 0xff;
844
845   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
846
847   /* Set CCM lengths */
848   params[0] = sizeof (data);    /*datalen */
849   params[1] = buflen;           /*aadlen */
850   params[2] = sizeof (tag);
851   err =
852     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
853   if (err)
854     {
855       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
856                gpg_strerror (err));
857       gcry_cipher_close (hd);
858       exit (1);
859     }
860
861   err = gcry_cipher_authenticate (hd, buf, buflen);
862   if (err)
863     {
864       fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n",
865                gpg_strerror (err));
866       gcry_cipher_close (hd);
867       exit (1);
868     }
869
870   err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
871   if (err)
872     {
873       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
874                gpg_strerror (err));
875       gcry_cipher_close (hd);
876       exit (1);
877     }
878
879   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
880   if (err)
881     {
882       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
883                gpg_strerror (err));
884       gcry_cipher_close (hd);
885       exit (1);
886     }
887 }
888
889 static struct bench_ops ccm_encrypt_ops = {
890   &bench_encrypt_init,
891   &bench_encrypt_free,
892   &bench_ccm_encrypt_do_bench
893 };
894
895 static struct bench_ops ccm_decrypt_ops = {
896   &bench_encrypt_init,
897   &bench_encrypt_free,
898   &bench_ccm_decrypt_do_bench
899 };
900
901 static struct bench_ops ccm_authenticate_ops = {
902   &bench_encrypt_init,
903   &bench_encrypt_free,
904   &bench_ccm_authenticate_do_bench
905 };
906
907
908 static void
909 bench_aead_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen,
910                              const char *nonce, size_t noncelen)
911 {
912   gcry_cipher_hd_t hd = obj->priv;
913   int err;
914   char tag[16];
915
916   gcry_cipher_setiv (hd, nonce, noncelen);
917
918   gcry_cipher_final (hd);
919   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
920   if (err)
921     {
922       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
923            gpg_strerror (err));
924       gcry_cipher_close (hd);
925       exit (1);
926     }
927
928   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
929   if (err)
930     {
931       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
932            gpg_strerror (err));
933       gcry_cipher_close (hd);
934       exit (1);
935     }
936 }
937
938 static void
939 bench_aead_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen,
940                              const char *nonce, size_t noncelen)
941 {
942   gcry_cipher_hd_t hd = obj->priv;
943   int err;
944   char tag[16] = { 0, };
945
946   gcry_cipher_setiv (hd, nonce, noncelen);
947
948   gcry_cipher_final (hd);
949   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
950   if (err)
951     {
952       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
953            gpg_strerror (err));
954       gcry_cipher_close (hd);
955       exit (1);
956     }
957
958   err = gcry_cipher_checktag (hd, tag, sizeof (tag));
959   if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
960     err = gpg_error (GPG_ERR_NO_ERROR);
961   if (err)
962     {
963       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
964            gpg_strerror (err));
965       gcry_cipher_close (hd);
966       exit (1);
967     }
968 }
969
970 static void
971 bench_aead_authenticate_do_bench (struct bench_obj *obj, void *buf,
972                                   size_t buflen, const char *nonce,
973                                   size_t noncelen)
974 {
975   gcry_cipher_hd_t hd = obj->priv;
976   int err;
977   char tag[16] = { 0, };
978   char data = 0xff;
979
980   err = gcry_cipher_setiv (hd, nonce, noncelen);
981   if (err)
982     {
983       fprintf (stderr, PGM ": gcry_cipher_setiv failed: %s\n",
984            gpg_strerror (err));
985       gcry_cipher_close (hd);
986       exit (1);
987     }
988
989   err = gcry_cipher_authenticate (hd, buf, buflen);
990   if (err)
991     {
992       fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n",
993            gpg_strerror (err));
994       gcry_cipher_close (hd);
995       exit (1);
996     }
997
998   gcry_cipher_final (hd);
999   err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
1000   if (err)
1001     {
1002       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
1003            gpg_strerror (err));
1004       gcry_cipher_close (hd);
1005       exit (1);
1006     }
1007
1008   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
1009   if (err)
1010     {
1011       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
1012            gpg_strerror (err));
1013       gcry_cipher_close (hd);
1014       exit (1);
1015     }
1016 }
1017
1018
1019 static void
1020 bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf,
1021                             size_t buflen)
1022 {
1023   char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
1024                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 };
1025   bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1026 }
1027
1028 static void
1029 bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf,
1030                             size_t buflen)
1031 {
1032   char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
1033                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 };
1034   bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1035 }
1036
1037 static void
1038 bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf,
1039                                  size_t buflen)
1040 {
1041   char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
1042                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 };
1043   bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1044 }
1045
1046 static struct bench_ops gcm_encrypt_ops = {
1047   &bench_encrypt_init,
1048   &bench_encrypt_free,
1049   &bench_gcm_encrypt_do_bench
1050 };
1051
1052 static struct bench_ops gcm_decrypt_ops = {
1053   &bench_encrypt_init,
1054   &bench_encrypt_free,
1055   &bench_gcm_decrypt_do_bench
1056 };
1057
1058 static struct bench_ops gcm_authenticate_ops = {
1059   &bench_encrypt_init,
1060   &bench_encrypt_free,
1061   &bench_gcm_authenticate_do_bench
1062 };
1063
1064
1065 static void
1066 bench_ocb_encrypt_do_bench (struct bench_obj *obj, void *buf,
1067                             size_t buflen)
1068 {
1069   char nonce[15] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
1070                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
1071                      0x00, 0x00, 0x01 };
1072   bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1073 }
1074
1075 static void
1076 bench_ocb_decrypt_do_bench (struct bench_obj *obj, void *buf,
1077                             size_t buflen)
1078 {
1079   char nonce[15] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
1080                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
1081                      0x00, 0x00, 0x01 };
1082   bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1083 }
1084
1085 static void
1086 bench_ocb_authenticate_do_bench (struct bench_obj *obj, void *buf,
1087                                  size_t buflen)
1088 {
1089   char nonce[15] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
1090                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
1091                      0x00, 0x00, 0x01 };
1092   bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1093 }
1094
1095 static struct bench_ops ocb_encrypt_ops = {
1096   &bench_encrypt_init,
1097   &bench_encrypt_free,
1098   &bench_ocb_encrypt_do_bench
1099 };
1100
1101 static struct bench_ops ocb_decrypt_ops = {
1102   &bench_encrypt_init,
1103   &bench_encrypt_free,
1104   &bench_ocb_decrypt_do_bench
1105 };
1106
1107 static struct bench_ops ocb_authenticate_ops = {
1108   &bench_encrypt_init,
1109   &bench_encrypt_free,
1110   &bench_ocb_authenticate_do_bench
1111 };
1112
1113
1114 static void
1115 bench_poly1305_encrypt_do_bench (struct bench_obj *obj, void *buf,
1116                                  size_t buflen)
1117 {
1118   char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
1119   bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1120 }
1121
1122 static void
1123 bench_poly1305_decrypt_do_bench (struct bench_obj *obj, void *buf,
1124                                  size_t buflen)
1125 {
1126   char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
1127   bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1128 }
1129
1130 static void
1131 bench_poly1305_authenticate_do_bench (struct bench_obj *obj, void *buf,
1132                                       size_t buflen)
1133 {
1134   char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
1135   bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
1136 }
1137
1138 static struct bench_ops poly1305_encrypt_ops = {
1139   &bench_encrypt_init,
1140   &bench_encrypt_free,
1141   &bench_poly1305_encrypt_do_bench
1142 };
1143
1144 static struct bench_ops poly1305_decrypt_ops = {
1145   &bench_encrypt_init,
1146   &bench_encrypt_free,
1147   &bench_poly1305_decrypt_do_bench
1148 };
1149
1150 static struct bench_ops poly1305_authenticate_ops = {
1151   &bench_encrypt_init,
1152   &bench_encrypt_free,
1153   &bench_poly1305_authenticate_do_bench
1154 };
1155
1156
1157 static struct bench_cipher_mode cipher_modes[] = {
1158   {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
1159   {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops},
1160   {GCRY_CIPHER_MODE_CBC, "CBC enc", &encrypt_ops},
1161   {GCRY_CIPHER_MODE_CBC, "CBC dec", &decrypt_ops},
1162   {GCRY_CIPHER_MODE_CFB, "CFB enc", &encrypt_ops},
1163   {GCRY_CIPHER_MODE_CFB, "CFB dec", &decrypt_ops},
1164   {GCRY_CIPHER_MODE_OFB, "OFB enc", &encrypt_ops},
1165   {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops},
1166   {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops},
1167   {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops},
1168   {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
1169   {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
1170   {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
1171   {GCRY_CIPHER_MODE_GCM, "GCM enc", &gcm_encrypt_ops},
1172   {GCRY_CIPHER_MODE_GCM, "GCM dec", &gcm_decrypt_ops},
1173   {GCRY_CIPHER_MODE_GCM, "GCM auth", &gcm_authenticate_ops},
1174   {GCRY_CIPHER_MODE_OCB, "OCB enc",  &ocb_encrypt_ops},
1175   {GCRY_CIPHER_MODE_OCB, "OCB dec",  &ocb_decrypt_ops},
1176   {GCRY_CIPHER_MODE_OCB, "OCB auth", &ocb_authenticate_ops},
1177   {GCRY_CIPHER_MODE_POLY1305, "POLY1305 enc", &poly1305_encrypt_ops},
1178   {GCRY_CIPHER_MODE_POLY1305, "POLY1305 dec", &poly1305_decrypt_ops},
1179   {GCRY_CIPHER_MODE_POLY1305, "POLY1305 auth", &poly1305_authenticate_ops},
1180   {0},
1181 };
1182
1183
1184 static void
1185 cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
1186 {
1187   struct bench_cipher_mode mode = *pmode;
1188   struct bench_obj obj = { 0 };
1189   double result;
1190   unsigned int blklen;
1191
1192   mode.algo = algo;
1193
1194   /* Check if this mode is ok */
1195   blklen = gcry_cipher_get_algo_blklen (algo);
1196   if (!blklen)
1197     return;
1198
1199   /* Stream cipher? Only test with "ECB" and POLY1305. */
1200   if (blklen == 1 && (mode.mode != GCRY_CIPHER_MODE_ECB &&
1201                       mode.mode != GCRY_CIPHER_MODE_POLY1305))
1202     return;
1203   if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB)
1204     {
1205       mode.mode = GCRY_CIPHER_MODE_STREAM;
1206       mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec";
1207     }
1208
1209   /* Poly1305 has restriction for cipher algorithm */
1210   if (mode.mode == GCRY_CIPHER_MODE_POLY1305 && algo != GCRY_CIPHER_CHACHA20)
1211     return;
1212
1213   /* CCM has restrictions for block-size */
1214   if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
1215     return;
1216
1217   /* GCM has restrictions for block-size */
1218   if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN)
1219     return;
1220
1221   /* Our OCB implementaion has restrictions for block-size.  */
1222   if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != 16)
1223     return;
1224
1225   bench_print_mode (14, mode.name);
1226
1227   obj.ops = mode.ops;
1228   obj.priv = &mode;
1229
1230   result = do_slope_benchmark (&obj);
1231
1232   bench_print_result (result);
1233 }
1234
1235
1236 static void
1237 _cipher_bench (int algo)
1238 {
1239   const char *algoname;
1240   int i;
1241
1242   algoname = gcry_cipher_algo_name (algo);
1243
1244   bench_print_header (14, algoname);
1245
1246   for (i = 0; cipher_modes[i].mode; i++)
1247     cipher_bench_one (algo, &cipher_modes[i]);
1248
1249   bench_print_footer (14);
1250 }
1251
1252
1253 void
1254 cipher_bench (char **argv, int argc)
1255 {
1256   int i, algo;
1257
1258   bench_print_section ("cipher", "Cipher");
1259
1260   if (argv && argc)
1261     {
1262       for (i = 0; i < argc; i++)
1263         {
1264           algo = gcry_cipher_map_name (argv[i]);
1265           if (algo)
1266             _cipher_bench (algo);
1267         }
1268     }
1269   else
1270     {
1271       for (i = 1; i < 400; i++)
1272         if (!gcry_cipher_test_algo (i))
1273           _cipher_bench (i);
1274     }
1275 }
1276
1277
1278 /*********************************************************** Hash benchmarks. */
1279
1280 struct bench_hash_mode
1281 {
1282   const char *name;
1283   struct bench_ops *ops;
1284
1285   int algo;
1286 };
1287
1288
1289 static int
1290 bench_hash_init (struct bench_obj *obj)
1291 {
1292   struct bench_hash_mode *mode = obj->priv;
1293   gcry_md_hd_t hd;
1294   int err;
1295
1296   obj->min_bufsize = BUF_START_SIZE;
1297   obj->max_bufsize = BUF_END_SIZE;
1298   obj->step_size = BUF_STEP_SIZE;
1299   obj->num_measure_repetitions = num_measurement_repetitions;
1300
1301   err = gcry_md_open (&hd, mode->algo, 0);
1302   if (err)
1303     {
1304       fprintf (stderr, PGM ": error opening hash `%s'\n",
1305                gcry_md_algo_name (mode->algo));
1306       exit (1);
1307     }
1308
1309   obj->priv = hd;
1310
1311   return 0;
1312 }
1313
1314 static void
1315 bench_hash_free (struct bench_obj *obj)
1316 {
1317   gcry_md_hd_t hd = obj->priv;
1318
1319   gcry_md_close (hd);
1320 }
1321
1322 static void
1323 bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
1324 {
1325   gcry_md_hd_t hd = obj->priv;
1326
1327   gcry_md_reset (hd);
1328   gcry_md_write (hd, buf, buflen);
1329   gcry_md_final (hd);
1330 }
1331
1332 static struct bench_ops hash_ops = {
1333   &bench_hash_init,
1334   &bench_hash_free,
1335   &bench_hash_do_bench
1336 };
1337
1338
1339 static struct bench_hash_mode hash_modes[] = {
1340   {"", &hash_ops},
1341   {0},
1342 };
1343
1344
1345 static void
1346 hash_bench_one (int algo, struct bench_hash_mode *pmode)
1347 {
1348   struct bench_hash_mode mode = *pmode;
1349   struct bench_obj obj = { 0 };
1350   double result;
1351
1352   mode.algo = algo;
1353
1354   if (mode.name[0] == '\0')
1355     bench_print_algo (-14, gcry_md_algo_name (algo));
1356   else
1357     bench_print_algo (14, mode.name);
1358
1359   obj.ops = mode.ops;
1360   obj.priv = &mode;
1361
1362   result = do_slope_benchmark (&obj);
1363
1364   bench_print_result (result);
1365 }
1366
1367 static void
1368 _hash_bench (int algo)
1369 {
1370   int i;
1371
1372   for (i = 0; hash_modes[i].name; i++)
1373     hash_bench_one (algo, &hash_modes[i]);
1374 }
1375
1376 void
1377 hash_bench (char **argv, int argc)
1378 {
1379   int i, algo;
1380
1381   bench_print_section ("hash", "Hash");
1382   bench_print_header (14, "");
1383
1384   if (argv && argc)
1385     {
1386       for (i = 0; i < argc; i++)
1387         {
1388           algo = gcry_md_map_name (argv[i]);
1389           if (algo)
1390             _hash_bench (algo);
1391         }
1392     }
1393   else
1394     {
1395       for (i = 1; i < 400; i++)
1396         if (!gcry_md_test_algo (i))
1397           _hash_bench (i);
1398     }
1399
1400   bench_print_footer (14);
1401 }
1402
1403
1404 /************************************************************ MAC benchmarks. */
1405
1406 struct bench_mac_mode
1407 {
1408   const char *name;
1409   struct bench_ops *ops;
1410
1411   int algo;
1412 };
1413
1414
1415 static int
1416 bench_mac_init (struct bench_obj *obj)
1417 {
1418   struct bench_mac_mode *mode = obj->priv;
1419   gcry_mac_hd_t hd;
1420   int err;
1421   unsigned int keylen;
1422   void *key;
1423
1424   obj->min_bufsize = BUF_START_SIZE;
1425   obj->max_bufsize = BUF_END_SIZE;
1426   obj->step_size = BUF_STEP_SIZE;
1427   obj->num_measure_repetitions = num_measurement_repetitions;
1428
1429   keylen = gcry_mac_get_algo_keylen (mode->algo);
1430   if (keylen == 0)
1431     keylen = 32;
1432   key = malloc (keylen);
1433   if (!key)
1434     {
1435       fprintf (stderr, PGM ": couldn't allocate %d bytes\n", keylen);
1436       exit (1);
1437     }
1438   memset(key, 42, keylen);
1439
1440   err = gcry_mac_open (&hd, mode->algo, 0, NULL);
1441   if (err)
1442     {
1443       fprintf (stderr, PGM ": error opening mac `%s'\n",
1444                gcry_mac_algo_name (mode->algo));
1445       free (key);
1446       exit (1);
1447     }
1448
1449   err = gcry_mac_setkey (hd, key, keylen);
1450   if (err)
1451     {
1452       fprintf (stderr, PGM ": error setting key for mac `%s'\n",
1453                gcry_mac_algo_name (mode->algo));
1454       free (key);
1455       exit (1);
1456     }
1457
1458   switch (mode->algo)
1459     {
1460     default:
1461       break;
1462     case GCRY_MAC_POLY1305_AES:
1463     case GCRY_MAC_POLY1305_CAMELLIA:
1464     case GCRY_MAC_POLY1305_TWOFISH:
1465     case GCRY_MAC_POLY1305_SERPENT:
1466     case GCRY_MAC_POLY1305_SEED:
1467       gcry_mac_setiv (hd, key, 16);
1468       break;
1469     }
1470
1471   obj->priv = hd;
1472
1473   free (key);
1474   return 0;
1475 }
1476
1477 static void
1478 bench_mac_free (struct bench_obj *obj)
1479 {
1480   gcry_mac_hd_t hd = obj->priv;
1481
1482   gcry_mac_close (hd);
1483 }
1484
1485 static void
1486 bench_mac_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
1487 {
1488   gcry_mac_hd_t hd = obj->priv;
1489   size_t bs;
1490   char b;
1491
1492   gcry_mac_reset (hd);
1493   gcry_mac_write (hd, buf, buflen);
1494   bs = sizeof(b);
1495   gcry_mac_read (hd, &b, &bs);
1496 }
1497
1498 static struct bench_ops mac_ops = {
1499   &bench_mac_init,
1500   &bench_mac_free,
1501   &bench_mac_do_bench
1502 };
1503
1504
1505 static struct bench_mac_mode mac_modes[] = {
1506   {"", &mac_ops},
1507   {0},
1508 };
1509
1510
1511 static void
1512 mac_bench_one (int algo, struct bench_mac_mode *pmode)
1513 {
1514   struct bench_mac_mode mode = *pmode;
1515   struct bench_obj obj = { 0 };
1516   double result;
1517
1518   mode.algo = algo;
1519
1520   if (mode.name[0] == '\0')
1521     bench_print_algo (-18, gcry_mac_algo_name (algo));
1522   else
1523     bench_print_algo (18, mode.name);
1524
1525   obj.ops = mode.ops;
1526   obj.priv = &mode;
1527
1528   result = do_slope_benchmark (&obj);
1529
1530   bench_print_result (result);
1531 }
1532
1533 static void
1534 _mac_bench (int algo)
1535 {
1536   int i;
1537
1538   for (i = 0; mac_modes[i].name; i++)
1539     mac_bench_one (algo, &mac_modes[i]);
1540 }
1541
1542 void
1543 mac_bench (char **argv, int argc)
1544 {
1545   int i, algo;
1546
1547   bench_print_section ("mac", "MAC");
1548   bench_print_header (18, "");
1549
1550   if (argv && argc)
1551     {
1552       for (i = 0; i < argc; i++)
1553         {
1554           algo = gcry_mac_map_name (argv[i]);
1555           if (algo)
1556             _mac_bench (algo);
1557         }
1558     }
1559   else
1560     {
1561       for (i = 1; i < 600; i++)
1562         if (!gcry_mac_test_algo (i))
1563           _mac_bench (i);
1564     }
1565
1566   bench_print_footer (18);
1567 }
1568
1569
1570 /************************************************************ KDF benchmarks. */
1571
1572 struct bench_kdf_mode
1573 {
1574   struct bench_ops *ops;
1575
1576   int algo;
1577   int subalgo;
1578 };
1579
1580
1581 static int
1582 bench_kdf_init (struct bench_obj *obj)
1583 {
1584   struct bench_kdf_mode *mode = obj->priv;
1585
1586   if (mode->algo == GCRY_KDF_PBKDF2)
1587     {
1588       obj->min_bufsize = 2;
1589       obj->max_bufsize = 2 * 32;
1590       obj->step_size = 2;
1591     }
1592
1593   obj->num_measure_repetitions = num_measurement_repetitions;
1594
1595   return 0;
1596 }
1597
1598 static void
1599 bench_kdf_free (struct bench_obj *obj)
1600 {
1601   (void)obj;
1602 }
1603
1604 static void
1605 bench_kdf_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
1606 {
1607   struct bench_kdf_mode *mode = obj->priv;
1608   char keybuf[16];
1609
1610   (void)buf;
1611
1612   if (mode->algo == GCRY_KDF_PBKDF2)
1613     {
1614       gcry_kdf_derive("qwerty", 6, mode->algo, mode->subalgo, "01234567", 8,
1615                       buflen, sizeof(keybuf), keybuf);
1616     }
1617 }
1618
1619 static struct bench_ops kdf_ops = {
1620   &bench_kdf_init,
1621   &bench_kdf_free,
1622   &bench_kdf_do_bench
1623 };
1624
1625
1626 static void
1627 kdf_bench_one (int algo, int subalgo)
1628 {
1629   struct bench_kdf_mode mode = { &kdf_ops };
1630   struct bench_obj obj = { 0 };
1631   double nsecs_per_iteration;
1632   double cycles_per_iteration;
1633   char algo_name[32];
1634   char nsecpiter_buf[16];
1635   char cpiter_buf[16];
1636
1637   mode.algo = algo;
1638   mode.subalgo = subalgo;
1639
1640   switch (subalgo)
1641     {
1642     case GCRY_MD_CRC32:
1643     case GCRY_MD_CRC32_RFC1510:
1644     case GCRY_MD_CRC24_RFC2440:
1645     case GCRY_MD_MD4:
1646       /* Skip CRC32s. */
1647       return;
1648     }
1649
1650   if (gcry_md_get_algo_dlen (subalgo) == 0)
1651     {
1652       /* Skip XOFs */
1653       return;
1654     }
1655
1656   *algo_name = 0;
1657
1658   if (algo == GCRY_KDF_PBKDF2)
1659     {
1660       snprintf (algo_name, sizeof(algo_name), "PBKDF2-HMAC-%s",
1661                 gcry_md_algo_name (subalgo));
1662     }
1663
1664   bench_print_algo (-24, algo_name);
1665
1666   obj.ops = mode.ops;
1667   obj.priv = &mode;
1668
1669   nsecs_per_iteration = do_slope_benchmark (&obj);
1670
1671   strcpy(cpiter_buf, csv_mode ? "" : "-");
1672
1673   double_to_str (nsecpiter_buf, sizeof (nsecpiter_buf), nsecs_per_iteration);
1674
1675   /* If user didn't provide CPU speed, we cannot show cycles/iter results.  */
1676   if (cpu_ghz > 0.0)
1677     {
1678       cycles_per_iteration = nsecs_per_iteration * cpu_ghz;
1679       double_to_str (cpiter_buf, sizeof (cpiter_buf), cycles_per_iteration);
1680     }
1681
1682   if (csv_mode)
1683     {
1684       printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter\n",
1685               current_section_name,
1686               current_algo_name ? current_algo_name : "",
1687               current_mode_name ? current_mode_name : "",
1688               nsecpiter_buf,
1689               cpiter_buf);
1690     }
1691   else
1692     {
1693       printf ("%14s %13s\n", nsecpiter_buf, cpiter_buf);
1694     }
1695 }
1696
1697 void
1698 kdf_bench (char **argv, int argc)
1699 {
1700   char algo_name[32];
1701   int i, j;
1702
1703   bench_print_section ("kdf", "KDF");
1704
1705   if (!csv_mode)
1706     {
1707       printf (" %-*s | ", 24, "");
1708       printf ("%14s %13s\n", "nanosecs/iter", "cycles/iter");
1709     }
1710
1711   if (argv && argc)
1712     {
1713       for (i = 0; i < argc; i++)
1714         {
1715           for (j = 1; j < 400; j++)
1716             {
1717               if (gcry_md_test_algo (j))
1718                 continue;
1719
1720               snprintf (algo_name, sizeof(algo_name), "PBKDF2-HMAC-%s",
1721                         gcry_md_algo_name (j));
1722
1723               if (!strcmp(argv[i], algo_name))
1724                 kdf_bench_one (GCRY_KDF_PBKDF2, j);
1725             }
1726         }
1727     }
1728   else
1729     {
1730       for (i = 1; i < 400; i++)
1731         if (!gcry_md_test_algo (i))
1732           kdf_bench_one (GCRY_KDF_PBKDF2, i);
1733     }
1734
1735   bench_print_footer (24);
1736 }
1737
1738
1739 /************************************************************** Main program. */
1740
1741 void
1742 print_help (void)
1743 {
1744   static const char *help_lines[] = {
1745     "usage: bench-slope [options] [hash|mac|cipher|kdf [algonames]]",
1746     "",
1747     " options:",
1748     "   --cpu-mhz <mhz>           Set CPU speed for calculating cycles",
1749     "                             per bytes results.",
1750     "   --disable-hwf <features>  Disable hardware acceleration feature(s)",
1751     "                             for benchmarking.",
1752     "   --repetitions <n>         Use N repetitions (default "
1753                                      STR2(NUM_MEASUREMENT_REPETITIONS) ")",
1754     "   --unaligned               Use unaligned input buffers.",
1755     "   --csv                     Use CSV output format",
1756     NULL
1757   };
1758   const char **line;
1759
1760   for (line = help_lines; *line; line++)
1761     fprintf (stdout, "%s\n", *line);
1762 }
1763
1764
1765 /* Warm up CPU.  */
1766 static void
1767 warm_up_cpu (void)
1768 {
1769   struct nsec_time start, end;
1770
1771   get_nsec_time (&start);
1772   do
1773     {
1774       get_nsec_time (&end);
1775     }
1776   while (get_time_nsec_diff (&start, &end) < 1000.0 * 1000.0 * 1000.0);
1777 }
1778
1779
1780 int
1781 main (int argc, char **argv)
1782 {
1783   int last_argc = -1;
1784   int debug = 0;
1785
1786   if (argc)
1787     {
1788       argc--;
1789       argv++;
1790     }
1791
1792   /* We skip this test if we are running under the test suite (no args
1793      and srcdir defined) and GCRYPT_NO_BENCHMARKS is set.  */
1794   if (!argc && getenv ("srcdir") && getenv ("GCRYPT_NO_BENCHMARKS"))
1795     exit (77);
1796
1797   if (getenv ("GCRYPT_IN_REGRESSION_TEST"))
1798     {
1799       in_regression_test = 1;
1800       num_measurement_repetitions = 2;
1801     }
1802   else
1803     num_measurement_repetitions = NUM_MEASUREMENT_REPETITIONS;
1804
1805   while (argc && last_argc != argc)
1806     {
1807       last_argc = argc;
1808
1809       if (!strcmp (*argv, "--"))
1810         {
1811           argc--;
1812           argv++;
1813           break;
1814         }
1815       else if (!strcmp (*argv, "--help"))
1816         {
1817           print_help ();
1818           exit (0);
1819         }
1820       else if (!strcmp (*argv, "--verbose"))
1821         {
1822           verbose++;
1823           argc--;
1824           argv++;
1825         }
1826       else if (!strcmp (*argv, "--debug"))
1827         {
1828           verbose += 2;
1829           debug++;
1830           argc--;
1831           argv++;
1832         }
1833       else if (!strcmp (*argv, "--csv"))
1834         {
1835           csv_mode = 1;
1836           argc--;
1837           argv++;
1838         }
1839       else if (!strcmp (*argv, "--unaligned"))
1840         {
1841           unaligned_mode = 1;
1842           argc--;
1843           argv++;
1844         }
1845       else if (!strcmp (*argv, "--disable-hwf"))
1846         {
1847           argc--;
1848           argv++;
1849           if (argc)
1850             {
1851               if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
1852                 fprintf (stderr,
1853                          PGM
1854                          ": unknown hardware feature `%s' - option ignored\n",
1855                          *argv);
1856               argc--;
1857               argv++;
1858             }
1859         }
1860       else if (!strcmp (*argv, "--cpu-mhz"))
1861         {
1862           argc--;
1863           argv++;
1864           if (argc)
1865             {
1866               cpu_ghz = atof (*argv);
1867               cpu_ghz /= 1000;  /* Mhz => Ghz */
1868
1869               argc--;
1870               argv++;
1871             }
1872         }
1873       else if (!strcmp (*argv, "--repetitions"))
1874         {
1875           argc--;
1876           argv++;
1877           if (argc)
1878             {
1879               num_measurement_repetitions = atof (*argv);
1880               if (num_measurement_repetitions < 2)
1881                 {
1882                   fprintf (stderr,
1883                            PGM
1884                            ": value for --repetitions too small - using %d\n",
1885                            NUM_MEASUREMENT_REPETITIONS);
1886                   num_measurement_repetitions = NUM_MEASUREMENT_REPETITIONS;
1887                 }
1888               argc--;
1889               argv++;
1890             }
1891         }
1892     }
1893
1894   gcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
1895
1896   if (!gcry_check_version (GCRYPT_VERSION))
1897     {
1898       fprintf (stderr, PGM ": version mismatch; pgm=%s, library=%s\n",
1899                GCRYPT_VERSION, gcry_check_version (NULL));
1900       exit (1);
1901     }
1902
1903   if (debug)
1904     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
1905
1906   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
1907   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
1908   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
1909
1910   if (in_regression_test)
1911     fputs ("Note: " PGM " running in quick regression test mode.\n", stdout);
1912
1913   if (!argc)
1914     {
1915       warm_up_cpu ();
1916       hash_bench (NULL, 0);
1917       mac_bench (NULL, 0);
1918       cipher_bench (NULL, 0);
1919       kdf_bench (NULL, 0);
1920     }
1921   else if (!strcmp (*argv, "hash"))
1922     {
1923       argc--;
1924       argv++;
1925
1926       warm_up_cpu ();
1927       hash_bench ((argc == 0) ? NULL : argv, argc);
1928     }
1929   else if (!strcmp (*argv, "mac"))
1930     {
1931       argc--;
1932       argv++;
1933
1934       warm_up_cpu ();
1935       mac_bench ((argc == 0) ? NULL : argv, argc);
1936     }
1937   else if (!strcmp (*argv, "cipher"))
1938     {
1939       argc--;
1940       argv++;
1941
1942       warm_up_cpu ();
1943       cipher_bench ((argc == 0) ? NULL : argv, argc);
1944     }
1945   else if (!strcmp (*argv, "kdf"))
1946     {
1947       argc--;
1948       argv++;
1949
1950       warm_up_cpu ();
1951       kdf_bench ((argc == 0) ? NULL : argv, argc);
1952     }
1953   else
1954     {
1955       fprintf (stderr, PGM ": unknown argument: %s\n", *argv);
1956       print_help ();
1957     }
1958
1959   return 0;
1960 }
1961
1962 #endif /* !NO_GET_NSEC_TIME */