Initial implementation of GCM
[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 static void
791 bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
792 {
793   gcry_cipher_hd_t hd = obj->priv;
794   int err;
795   char tag[16];
796   char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
797                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
798
799   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
800
801   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
802   if (err)
803     {
804       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
805            gpg_strerror (err));
806       gcry_cipher_close (hd);
807       exit (1);
808     }
809
810   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
811   if (err)
812     {
813       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
814            gpg_strerror (err));
815       gcry_cipher_close (hd);
816       exit (1);
817     }
818 }
819
820 static void
821 bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
822 {
823   gcry_cipher_hd_t hd = obj->priv;
824   int err;
825   char tag[16] = { 0, };
826   char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
827                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
828
829   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
830
831   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
832   if (err)
833     {
834       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
835            gpg_strerror (err));
836       gcry_cipher_close (hd);
837       exit (1);
838     }
839
840   err = gcry_cipher_checktag (hd, tag, sizeof (tag));
841   if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
842     err = gpg_error (GPG_ERR_NO_ERROR);
843   if (err)
844     {
845       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
846            gpg_strerror (err));
847       gcry_cipher_close (hd);
848       exit (1);
849     }
850 }
851
852 static void
853 bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf,
854            size_t buflen)
855 {
856   gcry_cipher_hd_t hd = obj->priv;
857   int err;
858   char tag[16] = { 0, };
859   char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
860                      0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
861   char data = 0xff;
862
863   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
864
865   err = gcry_cipher_authenticate (hd, buf, buflen);
866   if (err)
867     {
868       fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n",
869            gpg_strerror (err));
870       gcry_cipher_close (hd);
871       exit (1);
872     }
873
874   err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
875   if (err)
876     {
877       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
878            gpg_strerror (err));
879       gcry_cipher_close (hd);
880       exit (1);
881     }
882
883   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
884   if (err)
885     {
886       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
887            gpg_strerror (err));
888       gcry_cipher_close (hd);
889       exit (1);
890     }
891 }
892
893 static struct bench_ops gcm_encrypt_ops = {
894   &bench_encrypt_init,
895   &bench_encrypt_free,
896   &bench_gcm_encrypt_do_bench
897 };
898
899 static struct bench_ops gcm_decrypt_ops = {
900   &bench_encrypt_init,
901   &bench_encrypt_free,
902   &bench_gcm_decrypt_do_bench
903 };
904
905 static struct bench_ops gcm_authenticate_ops = {
906   &bench_encrypt_init,
907   &bench_encrypt_free,
908   &bench_gcm_authenticate_do_bench
909 };
910
911
912 static struct bench_cipher_mode cipher_modes[] = {
913   {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
914   {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops},
915   {GCRY_CIPHER_MODE_CBC, "CBC enc", &encrypt_ops},
916   {GCRY_CIPHER_MODE_CBC, "CBC dec", &decrypt_ops},
917   {GCRY_CIPHER_MODE_CFB, "CFB enc", &encrypt_ops},
918   {GCRY_CIPHER_MODE_CFB, "CFB dec", &decrypt_ops},
919   {GCRY_CIPHER_MODE_OFB, "OFB enc", &encrypt_ops},
920   {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops},
921   {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops},
922   {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops},
923   {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
924   {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
925   {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
926   {GCRY_CIPHER_MODE_GCM, "GCM enc", &gcm_encrypt_ops},
927   {GCRY_CIPHER_MODE_GCM, "GCM dec", &gcm_decrypt_ops},
928   {GCRY_CIPHER_MODE_GCM, "GCM auth", &gcm_authenticate_ops},
929   {0},
930 };
931
932
933 static void
934 cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
935 {
936   struct bench_cipher_mode mode = *pmode;
937   struct bench_obj obj = { 0 };
938   double result;
939   unsigned int blklen;
940
941   mode.algo = algo;
942
943   /* Check if this mode is ok */
944   blklen = gcry_cipher_get_algo_blklen (algo);
945   if (!blklen)
946     return;
947
948   /* Stream cipher? Only test with ECB. */
949   if (blklen == 1 && mode.mode != GCRY_CIPHER_MODE_ECB)
950     return;
951   if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB)
952     {
953       mode.mode = GCRY_CIPHER_MODE_STREAM;
954       mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec";
955     }
956
957   /* CCM has restrictions for block-size */
958   if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
959     return;
960
961   /* CCM has restrictions for block-size */
962   if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN)
963     return;
964
965   printf (" %14s | ", mode.name);
966   fflush (stdout);
967
968   obj.ops = mode.ops;
969   obj.priv = &mode;
970
971   result = do_slope_benchmark (&obj);
972
973   bench_print_result (result);
974 }
975
976
977 static void
978 _cipher_bench (int algo)
979 {
980   const char *algoname;
981   int i;
982
983   algoname = gcry_cipher_algo_name (algo);
984
985   bench_print_header (14, algoname);
986
987   for (i = 0; cipher_modes[i].mode; i++)
988     cipher_bench_one (algo, &cipher_modes[i]);
989
990   bench_print_footer (14);
991 }
992
993
994 void
995 cipher_bench (char **argv, int argc)
996 {
997   int i, algo;
998
999   printf ("Cipher:\n");
1000
1001   if (argv && argc)
1002     {
1003       for (i = 0; i < argc; i++)
1004         {
1005           algo = gcry_cipher_map_name (argv[i]);
1006           if (algo)
1007             _cipher_bench (algo);
1008         }
1009     }
1010   else
1011     {
1012       for (i = 1; i < 400; i++)
1013         if (!gcry_cipher_test_algo (i))
1014           _cipher_bench (i);
1015     }
1016 }
1017
1018
1019 /*********************************************************** Hash benchmarks. */
1020
1021 struct bench_hash_mode
1022 {
1023   const char *name;
1024   struct bench_ops *ops;
1025
1026   int algo;
1027 };
1028
1029
1030 static int
1031 bench_hash_init (struct bench_obj *obj)
1032 {
1033   struct bench_hash_mode *mode = obj->priv;
1034   gcry_md_hd_t hd;
1035   int err;
1036
1037   obj->min_bufsize = BUF_START_SIZE;
1038   obj->max_bufsize = BUF_END_SIZE;
1039   obj->step_size = BUF_STEP_SIZE;
1040   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
1041
1042   err = gcry_md_open (&hd, mode->algo, 0);
1043   if (err)
1044     {
1045       fprintf (stderr, PGM ": error opening hash `%s'\n",
1046                gcry_md_algo_name (mode->algo));
1047       exit (1);
1048     }
1049
1050   obj->priv = hd;
1051
1052   return 0;
1053 }
1054
1055 static void
1056 bench_hash_free (struct bench_obj *obj)
1057 {
1058   gcry_md_hd_t hd = obj->priv;
1059
1060   gcry_md_close (hd);
1061 }
1062
1063 static void
1064 bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
1065 {
1066   gcry_md_hd_t hd = obj->priv;
1067
1068   gcry_md_reset (hd);
1069   gcry_md_write (hd, buf, buflen);
1070   gcry_md_final (hd);
1071 }
1072
1073 static struct bench_ops hash_ops = {
1074   &bench_hash_init,
1075   &bench_hash_free,
1076   &bench_hash_do_bench
1077 };
1078
1079
1080 static struct bench_hash_mode hash_modes[] = {
1081   {"", &hash_ops},
1082   {0},
1083 };
1084
1085
1086 static void
1087 hash_bench_one (int algo, struct bench_hash_mode *pmode)
1088 {
1089   struct bench_hash_mode mode = *pmode;
1090   struct bench_obj obj = { 0 };
1091   double result;
1092
1093   mode.algo = algo;
1094
1095   if (mode.name[0] == '\0')
1096     printf (" %-14s | ", gcry_md_algo_name (algo));
1097   else
1098     printf (" %14s | ", mode.name);
1099   fflush (stdout);
1100
1101   obj.ops = mode.ops;
1102   obj.priv = &mode;
1103
1104   result = do_slope_benchmark (&obj);
1105
1106   bench_print_result (result);
1107 }
1108
1109 static void
1110 _hash_bench (int algo)
1111 {
1112   int i;
1113
1114   for (i = 0; hash_modes[i].name; i++)
1115     hash_bench_one (algo, &hash_modes[i]);
1116 }
1117
1118 void
1119 hash_bench (char **argv, int argc)
1120 {
1121   int i, algo;
1122
1123   printf ("Hash:\n");
1124
1125   bench_print_header (14, "");
1126
1127   if (argv && argc)
1128     {
1129       for (i = 0; i < argc; i++)
1130         {
1131           algo = gcry_md_map_name (argv[i]);
1132           if (algo)
1133             _hash_bench (algo);
1134         }
1135     }
1136   else
1137     {
1138       for (i = 1; i < 400; i++)
1139         if (!gcry_md_test_algo (i))
1140           _hash_bench (i);
1141     }
1142
1143   bench_print_footer (14);
1144 }
1145
1146
1147 /************************************************************ MAC benchmarks. */
1148
1149 struct bench_mac_mode
1150 {
1151   const char *name;
1152   struct bench_ops *ops;
1153
1154   int algo;
1155 };
1156
1157
1158 static int
1159 bench_mac_init (struct bench_obj *obj)
1160 {
1161   struct bench_mac_mode *mode = obj->priv;
1162   gcry_mac_hd_t hd;
1163   int err;
1164   unsigned int keylen;
1165   void *key;
1166
1167   obj->min_bufsize = BUF_START_SIZE;
1168   obj->max_bufsize = BUF_END_SIZE;
1169   obj->step_size = BUF_STEP_SIZE;
1170   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
1171
1172   keylen = gcry_mac_get_algo_keylen (mode->algo);
1173   if (keylen == 0)
1174     keylen = 32;
1175   key = malloc (keylen);
1176   if (!key)
1177     {
1178       fprintf (stderr, PGM ": couldn't allocate %d bytes\n", keylen);
1179       exit (1);
1180     }
1181   memset(key, 42, keylen);
1182
1183   err = gcry_mac_open (&hd, mode->algo, 0, NULL);
1184   if (err)
1185     {
1186       fprintf (stderr, PGM ": error opening mac `%s'\n",
1187                gcry_mac_algo_name (mode->algo));
1188       free (key);
1189       exit (1);
1190     }
1191
1192   err = gcry_mac_setkey (hd, key, keylen);
1193   free (key);
1194   if (err)
1195     {
1196       fprintf (stderr, PGM ": error setting key for mac `%s'\n",
1197                gcry_mac_algo_name (mode->algo));
1198       exit (1);
1199     }
1200
1201   obj->priv = hd;
1202
1203   return 0;
1204 }
1205
1206 static void
1207 bench_mac_free (struct bench_obj *obj)
1208 {
1209   gcry_mac_hd_t hd = obj->priv;
1210
1211   gcry_mac_close (hd);
1212 }
1213
1214 static void
1215 bench_mac_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
1216 {
1217   gcry_mac_hd_t hd = obj->priv;
1218   size_t bs;
1219   char b;
1220
1221   gcry_mac_reset (hd);
1222   gcry_mac_write (hd, buf, buflen);
1223   bs = sizeof(b);
1224   gcry_mac_read (hd, &b, &bs);
1225 }
1226
1227 static struct bench_ops mac_ops = {
1228   &bench_mac_init,
1229   &bench_mac_free,
1230   &bench_mac_do_bench
1231 };
1232
1233
1234 static struct bench_mac_mode mac_modes[] = {
1235   {"", &mac_ops},
1236   {0},
1237 };
1238
1239
1240 static void
1241 mac_bench_one (int algo, struct bench_mac_mode *pmode)
1242 {
1243   struct bench_mac_mode mode = *pmode;
1244   struct bench_obj obj = { 0 };
1245   double result;
1246
1247   mode.algo = algo;
1248
1249   if (mode.name[0] == '\0')
1250     printf (" %-18s | ", gcry_mac_algo_name (algo));
1251   else
1252     printf (" %18s | ", mode.name);
1253   fflush (stdout);
1254
1255   obj.ops = mode.ops;
1256   obj.priv = &mode;
1257
1258   result = do_slope_benchmark (&obj);
1259
1260   bench_print_result (result);
1261 }
1262
1263 static void
1264 _mac_bench (int algo)
1265 {
1266   int i;
1267
1268   for (i = 0; mac_modes[i].name; i++)
1269     mac_bench_one (algo, &mac_modes[i]);
1270 }
1271
1272 void
1273 mac_bench (char **argv, int argc)
1274 {
1275   int i, algo;
1276
1277   printf ("MAC:\n");
1278
1279   bench_print_header (18, "");
1280
1281   if (argv && argc)
1282     {
1283       for (i = 0; i < argc; i++)
1284         {
1285           algo = gcry_mac_map_name (argv[i]);
1286           if (algo)
1287             _mac_bench (algo);
1288         }
1289     }
1290   else
1291     {
1292       for (i = 1; i < 400; i++)
1293         if (!gcry_mac_test_algo (i))
1294           _mac_bench (i);
1295     }
1296
1297   bench_print_footer (18);
1298 }
1299
1300
1301 /************************************************************** Main program. */
1302
1303 void
1304 print_help (void)
1305 {
1306   static const char *help_lines[] = {
1307     "usage: bench-slope [options] [hash|mac|cipher [algonames]]",
1308     "",
1309     " options:",
1310     "     --cpu-mhz <mhz>           Set CPU speed for calculating cycles per bytes",
1311     "                               results.",
1312     "     --disable-hwf <features>  Disable hardware acceleration feature(s) for",
1313     "                               benchmarking.",
1314     NULL
1315   };
1316   const char **line;
1317
1318   for (line = help_lines; *line; line++)
1319     fprintf (stdout, "%s\n", *line);
1320 }
1321
1322
1323 /* Warm up CPU.  */
1324 static void
1325 warm_up_cpu (void)
1326 {
1327   struct nsec_time start, end;
1328
1329   get_nsec_time (&start);
1330   do
1331     {
1332       get_nsec_time (&end);
1333     }
1334   while (get_time_nsec_diff (&start, &end) < 1000.0 * 1000.0 * 1000.0);
1335 }
1336
1337
1338 int
1339 main (int argc, char **argv)
1340 {
1341   int last_argc = -1;
1342   int debug = 0;
1343
1344   if (argc)
1345     {
1346       argc--;
1347       argv++;
1348     }
1349
1350   /* We skip this test if we are running under the test suite (no args
1351      and srcdir defined) and GCRYPT_NO_BENCHMARKS is set.  */
1352   if (!argc && getenv ("srcdir") && getenv ("GCRYPT_NO_BENCHMARKS"))
1353     exit (77);
1354
1355   while (argc && last_argc != argc)
1356     {
1357       last_argc = argc;
1358
1359       if (!strcmp (*argv, "--"))
1360         {
1361           argc--;
1362           argv++;
1363           break;
1364         }
1365       else if (!strcmp (*argv, "--help"))
1366         {
1367           print_help ();
1368           exit (0);
1369         }
1370       else if (!strcmp (*argv, "--verbose"))
1371         {
1372           verbose++;
1373           argc--;
1374           argv++;
1375         }
1376       else if (!strcmp (*argv, "--debug"))
1377         {
1378           verbose += 2;
1379           debug++;
1380           argc--;
1381           argv++;
1382         }
1383       else if (!strcmp (*argv, "--disable-hwf"))
1384         {
1385           argc--;
1386           argv++;
1387           if (argc)
1388             {
1389               if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
1390                 fprintf (stderr,
1391                          PGM
1392                          ": unknown hardware feature `%s' - option ignored\n",
1393                          *argv);
1394               argc--;
1395               argv++;
1396             }
1397         }
1398       else if (!strcmp (*argv, "--cpu-mhz"))
1399         {
1400           argc--;
1401           argv++;
1402           if (argc)
1403             {
1404               cpu_ghz = atof (*argv);
1405               cpu_ghz /= 1000;  /* Mhz => Ghz */
1406
1407               argc--;
1408               argv++;
1409             }
1410         }
1411     }
1412
1413   gcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
1414
1415   if (!gcry_check_version (GCRYPT_VERSION))
1416     {
1417       fprintf (stderr, PGM ": version mismatch; pgm=%s, library=%s\n",
1418                GCRYPT_VERSION, gcry_check_version (NULL));
1419       exit (1);
1420     }
1421
1422   if (debug)
1423     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
1424
1425   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
1426   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
1427   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
1428
1429   if (!argc)
1430     {
1431       warm_up_cpu ();
1432       hash_bench (NULL, 0);
1433       mac_bench (NULL, 0);
1434       cipher_bench (NULL, 0);
1435     }
1436   else if (!strcmp (*argv, "hash"))
1437     {
1438       argc--;
1439       argv++;
1440
1441       warm_up_cpu ();
1442       hash_bench ((argc == 0) ? NULL : argv, argc);
1443     }
1444   else if (!strcmp (*argv, "mac"))
1445     {
1446       argc--;
1447       argv++;
1448
1449       warm_up_cpu ();
1450       mac_bench ((argc == 0) ? NULL : argv, argc);
1451     }
1452   else if (!strcmp (*argv, "cipher"))
1453     {
1454       argc--;
1455       argv++;
1456
1457       warm_up_cpu ();
1458       cipher_bench ((argc == 0) ? NULL : argv, argc);
1459     }
1460   else
1461     {
1462       fprintf (stderr, PGM ": unknown argument: %s\n", *argv);
1463       print_help ();
1464     }
1465
1466   return 0;
1467 }
1468
1469 #endif /* !NO_GET_NSEC_TIME */