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