Add new MAC API, initially with HMAC
[libgcrypt.git] / tests / bench-slope.c
1 /* bench-slope.c - for libgcrypt
2  * Copyright © 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 #define PGM "bench-slope"
37
38 static int verbose;
39
40
41 /* CPU Ghz value provided by user, allows constructing cycles/byte and other
42    results.  */
43 static double cpu_ghz = -1;
44
45
46
47 /*************************************** Default parameters for measurements. */
48
49 /* Start at small buffer size, to get reasonable timer calibration for fast
50  * implementations (AES-NI etc). Sixteen selected to support the largest block
51  * size of current set cipher blocks. */
52 #define BUF_START_SIZE                  16
53
54 /* From ~0 to ~4kbytes give comparable results with results from academia
55  * (SUPERCOP). */
56 #define BUF_END_SIZE                    (BUF_START_SIZE + 4096)
57
58 /* With 128 byte steps, we get (4096)/64 = 64 data points. */
59 #define BUF_STEP_SIZE                   64
60
61 /* Number of repeated measurements at each data point. The median of these
62  * measurements is selected as data point further analysis. */
63 #define NUM_MEASUREMENT_REPETITIONS     64
64
65 /**************************************************** High-resolution timers. */
66
67 /* This benchmarking module needs needs high resolution timer.  */
68 #undef NO_GET_NSEC_TIME
69 #if defined(_WIN32)
70 struct nsec_time
71 {
72   LARGE_INTEGER perf_count;
73 };
74
75 static void
76 get_nsec_time (struct nsec_time *t)
77 {
78   BOOL ok;
79
80   ok = QueryPerformanceCounter (&t->perf_count);
81   assert (ok);
82 }
83
84 static double
85 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
86 {
87   static double nsecs_per_count = 0.0;
88   double nsecs;
89
90   if (nsecs_per_count == 0.0)
91     {
92       LARGE_INTEGER perf_freq;
93       BOOL ok;
94
95       /* Get counts per second. */
96       ok = QueryPerformanceFrequency (&perf_freq);
97       assert (ok);
98
99       nsecs_per_count = 1.0 / perf_freq.QuadPart;
100       nsecs_per_count *= 1000000.0 * 1000.0;    /* sec => nsec */
101
102       assert (nsecs_per_count > 0.0);
103     }
104
105   nsecs = end->perf_count.QuadPart - start->perf_count.QuadPart;        /* counts */
106   nsecs *= nsecs_per_count;     /* counts * (nsecs / count) => nsecs */
107
108   return nsecs;
109 }
110 #elif defined(HAVE_CLOCK_GETTIME)
111 struct nsec_time
112 {
113   struct timespec ts;
114 };
115
116 static void
117 get_nsec_time (struct nsec_time *t)
118 {
119   int err;
120
121   err = clock_gettime (CLOCK_REALTIME, &t->ts);
122   assert (err == 0);
123 }
124
125 static double
126 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
127 {
128   double nsecs;
129
130   nsecs = end->ts.tv_sec - start->ts.tv_sec;
131   nsecs *= 1000000.0 * 1000.0;  /* sec => nsec */
132
133   /* This way we don't have to care if tv_nsec unsigned or signed. */
134   if (end->ts.tv_nsec >= start->ts.tv_nsec)
135     nsecs += end->ts.tv_nsec - start->ts.tv_nsec;
136   else
137     nsecs -= start->ts.tv_nsec - end->ts.tv_nsec;
138
139   return nsecs;
140 }
141 #elif defined(HAVE_GETTIMEOFDAY)
142 struct nsec_time
143 {
144   struct timeval tv;
145 };
146
147 static void
148 get_nsec_time (struct nsec_time *t)
149 {
150   int err;
151
152   err = gettimeofday (&t->tv, NULL);
153   assert (err == 0);
154 }
155
156 static double
157 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
158 {
159   double nsecs;
160
161   nsecs = end->tv.tv_sec - start->tv.tv_sec;
162   nsecs *= 1000000;             /* sec => µsec */
163
164   /* This way we don't have to care if tv_usec unsigned or signed. */
165   if (end->tv.tv_usec >= start->tv.tv_usec)
166     nsecs += end->tv.tv_usec - start->tv.tv_usec;
167   else
168     nsecs -= start->tv.tv_usec - end->tv.tv_usec;
169
170   nsecs *= 1000;                /* µsec => nsec */
171
172   return nsecs;
173 }
174 #else
175 #define NO_GET_NSEC_TIME 1
176 #endif
177
178
179 /* If no high resolution timer found, provide dummy bench-slope.  */
180 #ifdef NO_GET_NSEC_TIME
181
182
183 int
184 main (void)
185 {
186   /* No nsec timer => SKIP test. */
187   return 77;
188 }
189
190
191 #else /* !NO_GET_NSEC_TIME */
192
193
194 /********************************************** Slope benchmarking framework. */
195
196 struct bench_obj
197 {
198   const struct bench_ops *ops;
199
200   unsigned int num_measure_repetitions;
201   unsigned int min_bufsize;
202   unsigned int max_bufsize;
203   unsigned int step_size;
204
205   void *priv;
206 };
207
208 typedef int (*const bench_initialize_t) (struct bench_obj * obj);
209 typedef void (*const bench_finalize_t) (struct bench_obj * obj);
210 typedef void (*const bench_do_run_t) (struct bench_obj * obj, void *buffer,
211                                       size_t buflen);
212
213 struct bench_ops
214 {
215   bench_initialize_t initialize;
216   bench_finalize_t finalize;
217   bench_do_run_t do_run;
218 };
219
220
221 double
222 get_slope (double (*const get_x) (unsigned int idx, void *priv),
223            void *get_x_priv, double y_points[], unsigned int npoints,
224            double *overhead)
225 {
226   double sumx, sumy, sumx2, sumy2, sumxy;
227   unsigned int i;
228   double b, a;
229
230   sumx = sumy = sumx2 = sumy2 = sumxy = 0;
231
232   for (i = 0; i < npoints; i++)
233     {
234       double x, y;
235
236       x = get_x (i, get_x_priv);        /* bytes */
237       y = y_points[i];          /* nsecs */
238
239       sumx += x;
240       sumy += y;
241       sumx2 += x * x;
242       /*sumy2 += y * y;*/
243       sumxy += x * y;
244     }
245
246   b = (npoints * sumxy - sumx * sumy) / (npoints * sumx2 - sumx * sumx);
247   a = (sumy - b * sumx) / npoints;
248
249   if (overhead)
250     *overhead = a;              /* nsecs */
251
252   return b;                     /* nsecs per byte */
253 }
254
255
256 double
257 get_bench_obj_point_x (unsigned int idx, void *priv)
258 {
259   struct bench_obj *obj = priv;
260   return (double) (obj->min_bufsize + (idx * obj->step_size));
261 }
262
263
264 unsigned int
265 get_num_measurements (struct bench_obj *obj)
266 {
267   unsigned int buf_range = obj->max_bufsize - obj->min_bufsize;
268   unsigned int num = buf_range / obj->step_size + 1;
269
270   while (obj->min_bufsize + (num * obj->step_size) > obj->max_bufsize)
271     num--;
272
273   return num + 1;
274 }
275
276
277 static int
278 double_cmp (const void *_a, const void *_b)
279 {
280   const double *a, *b;
281
282   a = _a;
283   b = _b;
284
285   if (*a > *b)
286     return 1;
287   if (*a < *b)
288     return -1;
289   return 0;
290 }
291
292
293 double
294 do_bench_obj_measurement (struct bench_obj *obj, void *buffer, size_t buflen,
295                           double *measurement_raw,
296                           unsigned int loop_iterations)
297 {
298   const unsigned int num_repetitions = obj->num_measure_repetitions;
299   const bench_do_run_t do_run = obj->ops->do_run;
300   struct nsec_time start, end;
301   unsigned int rep, loop;
302   double res;
303
304   if (num_repetitions < 1 || loop_iterations < 1)
305     return 0.0;
306
307   for (rep = 0; rep < num_repetitions; rep++)
308     {
309       get_nsec_time (&start);
310
311       for (loop = 0; loop < loop_iterations; loop++)
312         do_run (obj, buffer, buflen);
313
314       get_nsec_time (&end);
315
316       measurement_raw[rep] = get_time_nsec_diff (&start, &end);
317     }
318
319   /* Return median of repeated measurements. */
320   qsort (measurement_raw, num_repetitions, sizeof (measurement_raw[0]),
321          double_cmp);
322
323   if (num_repetitions % 2 == 1)
324     return measurement_raw[num_repetitions / 2];
325
326   res = measurement_raw[num_repetitions / 2]
327     + measurement_raw[num_repetitions / 2 - 1];
328   return res / 2;
329 }
330
331
332 unsigned int
333 adjust_loop_iterations_to_timer_accuracy (struct bench_obj *obj, void *buffer,
334                                           double *measurement_raw)
335 {
336   const double increase_thres = 3.0;
337   double tmp, nsecs;
338   unsigned int loop_iterations;
339   unsigned int test_bufsize;
340
341   test_bufsize = obj->min_bufsize;
342   if (test_bufsize == 0)
343     test_bufsize += obj->step_size;
344
345   loop_iterations = 0;
346   do
347     {
348       /* Increase loop iterations until we get other results than zero.  */
349       nsecs =
350         do_bench_obj_measurement (obj, buffer, test_bufsize,
351                                   measurement_raw, ++loop_iterations);
352     }
353   while (nsecs < 1.0 - 0.1);
354   do
355     {
356       /* Increase loop iterations until we get reasonable increase for elapsed time.  */
357       tmp =
358         do_bench_obj_measurement (obj, buffer, test_bufsize,
359                                   measurement_raw, ++loop_iterations);
360     }
361   while (tmp < nsecs * (increase_thres - 0.1));
362
363   return loop_iterations;
364 }
365
366
367 /* Benchmark and return linear regression slope in nanoseconds per byte.  */
368 double
369 do_slope_benchmark (struct bench_obj *obj)
370 {
371   unsigned int num_measurements;
372   double *measurements = NULL;
373   double *measurement_raw = NULL;
374   double slope, overhead;
375   unsigned int loop_iterations, midx, i;
376   unsigned char *real_buffer = NULL;
377   unsigned char *buffer;
378   size_t cur_bufsize;
379   int err;
380
381   err = obj->ops->initialize (obj);
382   if (err < 0)
383     return -1;
384
385   num_measurements = get_num_measurements (obj);
386   measurements = calloc (num_measurements, sizeof (*measurements));
387   if (!measurements)
388     goto err_free;
389
390   measurement_raw =
391     calloc (obj->num_measure_repetitions, sizeof (*measurement_raw));
392   if (!measurement_raw)
393     goto err_free;
394
395   if (num_measurements < 1 || obj->num_measure_repetitions < 1 ||
396       obj->max_bufsize < 1 || obj->min_bufsize > obj->max_bufsize)
397     goto err_free;
398
399   real_buffer = malloc (obj->max_bufsize + 128);
400   if (!real_buffer)
401     goto err_free;
402   /* Get aligned buffer */
403   buffer = real_buffer;
404   buffer += 128 - ((real_buffer - (unsigned char *) 0) & (128 - 1));
405
406   for (i = 0; i < obj->max_bufsize; i++)
407     buffer[i] = 0x55 ^ (-i);
408
409   /* Adjust number of loop iterations up to timer accuracy.  */
410   loop_iterations = adjust_loop_iterations_to_timer_accuracy (obj, buffer,
411                                                               measurement_raw);
412
413   /* Perform measurements */
414   for (midx = 0, cur_bufsize = obj->min_bufsize;
415        cur_bufsize <= obj->max_bufsize; cur_bufsize += obj->step_size, midx++)
416     {
417       measurements[midx] =
418         do_bench_obj_measurement (obj, buffer, cur_bufsize, measurement_raw,
419                                   loop_iterations);
420       measurements[midx] /= loop_iterations;
421     }
422
423   assert (midx == num_measurements);
424
425   slope =
426     get_slope (&get_bench_obj_point_x, obj, measurements, num_measurements,
427                &overhead);
428
429   free (measurement_raw);
430   free (real_buffer);
431   obj->ops->finalize (obj);
432
433   return slope;
434
435 err_free:
436   if (measurement_raw)
437     free (measurement_raw);
438   if (measurements)
439     free (measurements);
440   if (real_buffer)
441     free (real_buffer);
442   obj->ops->finalize (obj);
443
444   return -1;
445 }
446
447
448 /********************************************************** Printing results. */
449
450 static void
451 double_to_str (char *out, size_t outlen, double value)
452 {
453   const char *fmt;
454
455   if (value < 1.0)
456     fmt = "%.3f";
457   else if (value < 100.0)
458     fmt = "%.2f";
459   else
460     fmt = "%.1f";
461
462   snprintf (out, outlen, fmt, value);
463 }
464
465 static void
466 bench_print_result (double nsecs_per_byte)
467 {
468   double cycles_per_byte, mbytes_per_sec;
469   char nsecpbyte_buf[16];
470   char mbpsec_buf[16];
471   char cpbyte_buf[16];
472
473   strcpy (cpbyte_buf, "-");
474
475   double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte);
476
477   /* If user didn't provide CPU speed, we cannot show cycles/byte results.  */
478   if (cpu_ghz > 0.0)
479     {
480       cycles_per_byte = nsecs_per_byte * cpu_ghz;
481       double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte);
482     }
483
484   mbytes_per_sec =
485     (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024);
486   double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec);
487
488   strncat (nsecpbyte_buf, " ns/B", sizeof (nsecpbyte_buf) - 1);
489   strncat (mbpsec_buf, " MiB/s", sizeof (mbpsec_buf) - 1);
490   strncat (cpbyte_buf, " c/B", sizeof (cpbyte_buf) - 1);
491
492   printf ("%14s %15s %13s\n", nsecpbyte_buf, mbpsec_buf, cpbyte_buf);
493 }
494
495 static void
496 bench_print_header (int algo_width, const char *algo_name)
497 {
498   printf (" %-*s | ", algo_width, algo_name);
499   printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec",
500           "cycles/byte");
501 }
502
503 static void
504 bench_print_footer (int algo_width)
505 {
506   printf (" %-*s =\n", algo_width, "");
507 }
508
509
510 /********************************************************* Cipher benchmarks. */
511
512 struct bench_cipher_mode
513 {
514   int mode;
515   const char *name;
516   struct bench_ops *ops;
517
518   int algo;
519 };
520
521
522 static int
523 bench_encrypt_init (struct bench_obj *obj)
524 {
525   struct bench_cipher_mode *mode = obj->priv;
526   gcry_cipher_hd_t hd;
527   int err, keylen;
528
529   obj->min_bufsize = BUF_START_SIZE;
530   obj->max_bufsize = BUF_END_SIZE;
531   obj->step_size = BUF_STEP_SIZE;
532   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
533
534   err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0);
535   if (err)
536     {
537       fprintf (stderr, PGM ": error opening cipher `%s'\n",
538                gcry_cipher_algo_name (mode->algo));
539       exit (1);
540     }
541
542   keylen = gcry_cipher_get_algo_keylen (mode->algo);
543   if (keylen)
544     {
545       char key[keylen];
546       int i;
547
548       for (i = 0; i < keylen; i++)
549         key[i] = 0x33 ^ (11 - i);
550
551       err = gcry_cipher_setkey (hd, key, keylen);
552       if (err)
553         {
554           fprintf (stderr, PGM ": gcry_cipher_setkey failed: %s\n",
555                    gpg_strerror (err));
556           gcry_cipher_close (hd);
557           exit (1);
558         }
559     }
560   else
561     {
562       fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n",
563                gcry_cipher_algo_name (mode->algo));
564       gcry_cipher_close (hd);
565       exit (1);
566     }
567
568   obj->priv = hd;
569
570   return 0;
571 }
572
573 static void
574 bench_encrypt_free (struct bench_obj *obj)
575 {
576   gcry_cipher_hd_t hd = obj->priv;
577
578   gcry_cipher_close (hd);
579 }
580
581 static void
582 bench_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
583 {
584   gcry_cipher_hd_t hd = obj->priv;
585   int err;
586
587   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
588   if (err)
589     {
590       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
591                gpg_strerror (err));
592       gcry_cipher_close (hd);
593       exit (1);
594     }
595 }
596
597 static void
598 bench_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
599 {
600   gcry_cipher_hd_t hd = obj->priv;
601   int err;
602
603   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
604   if (err)
605     {
606       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
607                gpg_strerror (err));
608       gcry_cipher_close (hd);
609       exit (1);
610     }
611 }
612
613 static struct bench_ops encrypt_ops = {
614   &bench_encrypt_init,
615   &bench_encrypt_free,
616   &bench_encrypt_do_bench
617 };
618
619 static struct bench_ops decrypt_ops = {
620   &bench_encrypt_init,
621   &bench_encrypt_free,
622   &bench_decrypt_do_bench
623 };
624
625
626
627 static void
628 bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
629 {
630   gcry_cipher_hd_t hd = obj->priv;
631   int err;
632   char tag[8];
633   char nonce[11] = { 0x80, 0x01, };
634   size_t params[3];
635
636   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
637
638   /* Set CCM lengths */
639   params[0] = buflen;
640   params[1] = 0;                /*aadlen */
641   params[2] = sizeof (tag);
642   err =
643     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
644   if (err)
645     {
646       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
647                gpg_strerror (err));
648       gcry_cipher_close (hd);
649       exit (1);
650     }
651
652   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
653   if (err)
654     {
655       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
656                gpg_strerror (err));
657       gcry_cipher_close (hd);
658       exit (1);
659     }
660
661   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
662   if (err)
663     {
664       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
665                gpg_strerror (err));
666       gcry_cipher_close (hd);
667       exit (1);
668     }
669 }
670
671 static void
672 bench_ccm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
673 {
674   gcry_cipher_hd_t hd = obj->priv;
675   int err;
676   char tag[8] = { 0, };
677   char nonce[11] = { 0x80, 0x01, };
678   size_t params[3];
679
680   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
681
682   /* Set CCM lengths */
683   params[0] = buflen;
684   params[1] = 0;                /*aadlen */
685   params[2] = sizeof (tag);
686   err =
687     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
688   if (err)
689     {
690       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
691                gpg_strerror (err));
692       gcry_cipher_close (hd);
693       exit (1);
694     }
695
696   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
697   if (err)
698     {
699       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
700                gpg_strerror (err));
701       gcry_cipher_close (hd);
702       exit (1);
703     }
704
705   err = gcry_cipher_checktag (hd, tag, sizeof (tag));
706   if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
707     err = gpg_error (GPG_ERR_NO_ERROR);
708   if (err)
709     {
710       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
711                gpg_strerror (err));
712       gcry_cipher_close (hd);
713       exit (1);
714     }
715 }
716
717 static void
718 bench_ccm_authenticate_do_bench (struct bench_obj *obj, void *buf,
719                                  size_t buflen)
720 {
721   gcry_cipher_hd_t hd = obj->priv;
722   int err;
723   char tag[8] = { 0, };
724   char nonce[11] = { 0x80, 0x01, };
725   size_t params[3];
726   char data = 0xff;
727
728   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
729
730   /* Set CCM lengths */
731   params[0] = sizeof (data);    /*datalen */
732   params[1] = buflen;           /*aadlen */
733   params[2] = sizeof (tag);
734   err =
735     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
736   if (err)
737     {
738       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
739                gpg_strerror (err));
740       gcry_cipher_close (hd);
741       exit (1);
742     }
743
744   err = gcry_cipher_authenticate (hd, buf, buflen);
745   if (err)
746     {
747       fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n",
748                gpg_strerror (err));
749       gcry_cipher_close (hd);
750       exit (1);
751     }
752
753   err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
754   if (err)
755     {
756       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
757                gpg_strerror (err));
758       gcry_cipher_close (hd);
759       exit (1);
760     }
761
762   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
763   if (err)
764     {
765       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
766                gpg_strerror (err));
767       gcry_cipher_close (hd);
768       exit (1);
769     }
770 }
771
772 static struct bench_ops ccm_encrypt_ops = {
773   &bench_encrypt_init,
774   &bench_encrypt_free,
775   &bench_ccm_encrypt_do_bench
776 };
777
778 static struct bench_ops ccm_decrypt_ops = {
779   &bench_encrypt_init,
780   &bench_encrypt_free,
781   &bench_ccm_decrypt_do_bench
782 };
783
784 static struct bench_ops ccm_authenticate_ops = {
785   &bench_encrypt_init,
786   &bench_encrypt_free,
787   &bench_ccm_authenticate_do_bench
788 };
789
790
791 static struct bench_cipher_mode cipher_modes[] = {
792   {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
793   {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops},
794   {GCRY_CIPHER_MODE_CBC, "CBC enc", &encrypt_ops},
795   {GCRY_CIPHER_MODE_CBC, "CBC dec", &decrypt_ops},
796   {GCRY_CIPHER_MODE_CFB, "CFB enc", &encrypt_ops},
797   {GCRY_CIPHER_MODE_CFB, "CFB dec", &decrypt_ops},
798   {GCRY_CIPHER_MODE_OFB, "OFB enc", &encrypt_ops},
799   {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops},
800   {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops},
801   {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops},
802   {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
803   {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
804   {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
805   {0},
806 };
807
808
809 static void
810 cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
811 {
812   struct bench_cipher_mode mode = *pmode;
813   struct bench_obj obj = { 0 };
814   double result;
815   unsigned int blklen;
816
817   mode.algo = algo;
818
819   /* Check if this mode is ok */
820   blklen = gcry_cipher_get_algo_blklen (algo);
821   if (!blklen)
822     return;
823
824   /* Stream cipher? Only test with ECB. */
825   if (blklen == 1 && mode.mode != GCRY_CIPHER_MODE_ECB)
826     return;
827   if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB)
828     {
829       mode.mode = GCRY_CIPHER_MODE_STREAM;
830       mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec";
831     }
832
833   /* CCM has restrictions for block-size */
834   if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
835     return;
836
837   printf (" %14s | ", mode.name);
838   fflush (stdout);
839
840   obj.ops = mode.ops;
841   obj.priv = &mode;
842
843   result = do_slope_benchmark (&obj);
844
845   bench_print_result (result);
846 }
847
848
849 static void
850 _cipher_bench (int algo)
851 {
852   const char *algoname;
853   int i;
854
855   algoname = gcry_cipher_algo_name (algo);
856
857   bench_print_header (14, algoname);
858
859   for (i = 0; cipher_modes[i].mode; i++)
860     cipher_bench_one (algo, &cipher_modes[i]);
861
862   bench_print_footer (14);
863 }
864
865
866 void
867 cipher_bench (char **argv, int argc)
868 {
869   int i, algo;
870
871   printf ("Cipher:\n");
872
873   if (argv && argc)
874     {
875       for (i = 0; i < argc; i++)
876         {
877           algo = gcry_cipher_map_name (argv[i]);
878           if (algo)
879             _cipher_bench (algo);
880         }
881     }
882   else
883     {
884       for (i = 1; i < 400; i++)
885         if (!gcry_cipher_test_algo (i))
886           _cipher_bench (i);
887     }
888 }
889
890
891 /*********************************************************** Hash benchmarks. */
892
893 struct bench_hash_mode
894 {
895   const char *name;
896   struct bench_ops *ops;
897
898   int algo;
899 };
900
901
902 static int
903 bench_hash_init (struct bench_obj *obj)
904 {
905   struct bench_hash_mode *mode = obj->priv;
906   gcry_md_hd_t hd;
907   int err;
908
909   obj->min_bufsize = BUF_START_SIZE;
910   obj->max_bufsize = BUF_END_SIZE;
911   obj->step_size = BUF_STEP_SIZE;
912   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
913
914   err = gcry_md_open (&hd, mode->algo, 0);
915   if (err)
916     {
917       fprintf (stderr, PGM ": error opening hash `%s'\n",
918                gcry_md_algo_name (mode->algo));
919       exit (1);
920     }
921
922   obj->priv = hd;
923
924   return 0;
925 }
926
927 static void
928 bench_hash_free (struct bench_obj *obj)
929 {
930   gcry_md_hd_t hd = obj->priv;
931
932   gcry_md_close (hd);
933 }
934
935 static void
936 bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
937 {
938   gcry_md_hd_t hd = obj->priv;
939
940   gcry_md_reset (hd);
941   gcry_md_write (hd, buf, buflen);
942   gcry_md_final (hd);
943 }
944
945 static struct bench_ops hash_ops = {
946   &bench_hash_init,
947   &bench_hash_free,
948   &bench_hash_do_bench
949 };
950
951
952 static struct bench_hash_mode hash_modes[] = {
953   {"", &hash_ops},
954   {0},
955 };
956
957
958 static void
959 hash_bench_one (int algo, struct bench_hash_mode *pmode)
960 {
961   struct bench_hash_mode mode = *pmode;
962   struct bench_obj obj = { 0 };
963   double result;
964
965   mode.algo = algo;
966
967   if (mode.name[0] == '\0')
968     printf (" %-14s | ", gcry_md_algo_name (algo));
969   else
970     printf (" %14s | ", mode.name);
971   fflush (stdout);
972
973   obj.ops = mode.ops;
974   obj.priv = &mode;
975
976   result = do_slope_benchmark (&obj);
977
978   bench_print_result (result);
979 }
980
981 static void
982 _hash_bench (int algo)
983 {
984   int i;
985
986   for (i = 0; hash_modes[i].name; i++)
987     hash_bench_one (algo, &hash_modes[i]);
988 }
989
990 void
991 hash_bench (char **argv, int argc)
992 {
993   int i, algo;
994
995   printf ("Hash:\n");
996
997   bench_print_header (14, "");
998
999   if (argv && argc)
1000     {
1001       for (i = 0; i < argc; i++)
1002         {
1003           algo = gcry_md_map_name (argv[i]);
1004           if (algo)
1005             _hash_bench (algo);
1006         }
1007     }
1008   else
1009     {
1010       for (i = 1; i < 400; i++)
1011         if (!gcry_md_test_algo (i))
1012           _hash_bench (i);
1013     }
1014
1015   bench_print_footer (14);
1016 }
1017
1018
1019 /************************************************************ MAC benchmarks. */
1020
1021 struct bench_mac_mode
1022 {
1023   const char *name;
1024   struct bench_ops *ops;
1025
1026   int algo;
1027 };
1028
1029
1030 static int
1031 bench_mac_init (struct bench_obj *obj)
1032 {
1033   struct bench_mac_mode *mode = obj->priv;
1034   gcry_mac_hd_t hd;
1035   int err;
1036   unsigned int keylen;
1037   void *key;
1038
1039   obj->min_bufsize = BUF_START_SIZE;
1040   obj->max_bufsize = BUF_END_SIZE;
1041   obj->step_size = BUF_STEP_SIZE;
1042   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
1043
1044   keylen = gcry_mac_get_algo_keylen (mode->algo);
1045   if (keylen == 0)
1046     keylen = 32;
1047   key = malloc (keylen);
1048   if (!key)
1049     {
1050       fprintf (stderr, PGM ": couldn't allocate %d bytes\n", keylen);
1051       exit (1);
1052     }
1053   memset(key, 42, keylen);
1054
1055   err = gcry_mac_open (&hd, mode->algo, 0, NULL);
1056   if (err)
1057     {
1058       fprintf (stderr, PGM ": error opening mac `%s'\n",
1059                gcry_mac_algo_name (mode->algo));
1060       free (key);
1061       exit (1);
1062     }
1063
1064   err = gcry_mac_setkey (hd, key, keylen);
1065   free (key);
1066   if (err)
1067     {
1068       fprintf (stderr, PGM ": error setting key for mac `%s'\n",
1069                gcry_mac_algo_name (mode->algo));
1070       exit (1);
1071     }
1072
1073   obj->priv = hd;
1074
1075   return 0;
1076 }
1077
1078 static void
1079 bench_mac_free (struct bench_obj *obj)
1080 {
1081   gcry_mac_hd_t hd = obj->priv;
1082
1083   gcry_mac_close (hd);
1084 }
1085
1086 static void
1087 bench_mac_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
1088 {
1089   gcry_mac_hd_t hd = obj->priv;
1090   size_t bs;
1091   char b;
1092
1093   gcry_mac_reset (hd);
1094   gcry_mac_write (hd, buf, buflen);
1095   bs = sizeof(b);
1096   gcry_mac_read (hd, &b, &bs);
1097 }
1098
1099 static struct bench_ops mac_ops = {
1100   &bench_mac_init,
1101   &bench_mac_free,
1102   &bench_mac_do_bench
1103 };
1104
1105
1106 static struct bench_mac_mode mac_modes[] = {
1107   {"", &mac_ops},
1108   {0},
1109 };
1110
1111
1112 static void
1113 mac_bench_one (int algo, struct bench_mac_mode *pmode)
1114 {
1115   struct bench_mac_mode mode = *pmode;
1116   struct bench_obj obj = { 0 };
1117   double result;
1118
1119   mode.algo = algo;
1120
1121   if (mode.name[0] == '\0')
1122     printf (" %-18s | ", gcry_mac_algo_name (algo));
1123   else
1124     printf (" %18s | ", mode.name);
1125   fflush (stdout);
1126
1127   obj.ops = mode.ops;
1128   obj.priv = &mode;
1129
1130   result = do_slope_benchmark (&obj);
1131
1132   bench_print_result (result);
1133 }
1134
1135 static void
1136 _mac_bench (int algo)
1137 {
1138   int i;
1139
1140   for (i = 0; mac_modes[i].name; i++)
1141     mac_bench_one (algo, &mac_modes[i]);
1142 }
1143
1144 void
1145 mac_bench (char **argv, int argc)
1146 {
1147   int i, algo;
1148
1149   printf ("MAC:\n");
1150
1151   bench_print_header (18, "");
1152
1153   if (argv && argc)
1154     {
1155       for (i = 0; i < argc; i++)
1156         {
1157           algo = gcry_mac_map_name (argv[i]);
1158           if (algo)
1159             _mac_bench (algo);
1160         }
1161     }
1162   else
1163     {
1164       for (i = 1; i < 400; i++)
1165         if (!gcry_mac_test_algo (i))
1166           _mac_bench (i);
1167     }
1168
1169   bench_print_footer (18);
1170 }
1171
1172
1173 /************************************************************** Main program. */
1174
1175 void
1176 print_help (void)
1177 {
1178   static const char *help_lines[] = {
1179     "usage: bench-slope [options] [hash|mac|cipher [algonames]]",
1180     "",
1181     " options:",
1182     "     --cpu-mhz <mhz>           Set CPU speed for calculating cycles per bytes",
1183     "                               results.",
1184     "     --disable-hwf <features>  Disable hardware acceleration feature(s) for",
1185     "                               benchmarking.",
1186     NULL
1187   };
1188   const char **line;
1189
1190   for (line = help_lines; *line; line++)
1191     fprintf (stdout, "%s\n", *line);
1192 }
1193
1194
1195 /* Warm up CPU.  */
1196 static void
1197 warm_up_cpu (void)
1198 {
1199   struct nsec_time start, end;
1200
1201   get_nsec_time (&start);
1202   do
1203     {
1204       get_nsec_time (&end);
1205     }
1206   while (get_time_nsec_diff (&start, &end) < 1000.0 * 1000.0 * 1000.0);
1207 }
1208
1209
1210 int
1211 main (int argc, char **argv)
1212 {
1213   int last_argc = -1;
1214   int debug = 0;
1215
1216   if (argc)
1217     {
1218       argc--;
1219       argv++;
1220     }
1221
1222   /* We skip this test if we are running under the test suite (no args
1223      and srcdir defined) and GCRYPT_NO_BENCHMARKS is set.  */
1224   if (!argc && getenv ("srcdir") && getenv ("GCRYPT_NO_BENCHMARKS"))
1225     exit (77);
1226
1227   while (argc && last_argc != argc)
1228     {
1229       last_argc = argc;
1230
1231       if (!strcmp (*argv, "--"))
1232         {
1233           argc--;
1234           argv++;
1235           break;
1236         }
1237       else if (!strcmp (*argv, "--help"))
1238         {
1239           print_help ();
1240           exit (0);
1241         }
1242       else if (!strcmp (*argv, "--verbose"))
1243         {
1244           verbose++;
1245           argc--;
1246           argv++;
1247         }
1248       else if (!strcmp (*argv, "--debug"))
1249         {
1250           verbose += 2;
1251           debug++;
1252           argc--;
1253           argv++;
1254         }
1255       else if (!strcmp (*argv, "--disable-hwf"))
1256         {
1257           argc--;
1258           argv++;
1259           if (argc)
1260             {
1261               if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
1262                 fprintf (stderr,
1263                          PGM
1264                          ": unknown hardware feature `%s' - option ignored\n",
1265                          *argv);
1266               argc--;
1267               argv++;
1268             }
1269         }
1270       else if (!strcmp (*argv, "--cpu-mhz"))
1271         {
1272           argc--;
1273           argv++;
1274           if (argc)
1275             {
1276               cpu_ghz = atof (*argv);
1277               cpu_ghz /= 1000;  /* Mhz => Ghz */
1278
1279               argc--;
1280               argv++;
1281             }
1282         }
1283     }
1284
1285   gcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
1286
1287   if (!gcry_check_version (GCRYPT_VERSION))
1288     {
1289       fprintf (stderr, PGM ": version mismatch; pgm=%s, library=%s\n",
1290                GCRYPT_VERSION, gcry_check_version (NULL));
1291       exit (1);
1292     }
1293
1294   if (debug)
1295     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
1296
1297   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
1298   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
1299   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
1300
1301   if (!argc)
1302     {
1303       warm_up_cpu ();
1304       hash_bench (NULL, 0);
1305       mac_bench (NULL, 0);
1306       cipher_bench (NULL, 0);
1307     }
1308   else if (!strcmp (*argv, "hash"))
1309     {
1310       argc--;
1311       argv++;
1312
1313       warm_up_cpu ();
1314       hash_bench ((argc == 0) ? NULL : argv, argc);
1315     }
1316   else if (!strcmp (*argv, "mac"))
1317     {
1318       argc--;
1319       argv++;
1320
1321       warm_up_cpu ();
1322       mac_bench ((argc == 0) ? NULL : argv, argc);
1323     }
1324   else if (!strcmp (*argv, "cipher"))
1325     {
1326       argc--;
1327       argv++;
1328
1329       warm_up_cpu ();
1330       cipher_bench ((argc == 0) ? NULL : argv, argc);
1331     }
1332   else
1333     {
1334       fprintf (stderr, PGM ": unknown argument: %s\n", *argv);
1335       print_help ();
1336     }
1337
1338   return 0;
1339 }
1340
1341 #endif /* !NO_GET_NSEC_TIME */