./
[libgcrypt.git] / tests / pkbench.c
1 /* pkbench.c - Pubkey menchmarking
2  *      Copyright (C) 2004, 2005 Free Software Foundation, Inc.
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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <gcrypt.h>
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #ifndef HAVE_W32_SYSTEM
32 #include <sys/times.h>
33 #endif /*HAVE_W32_SYSTEM*/
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <time.h>
37
38 #define PGM "pkbench"
39
40
41 static int verbose;
42 static int debug;
43 static int error_count;
44
45
46 typedef struct context
47 {
48   gcry_sexp_t key_secret;
49   gcry_sexp_t key_public;
50   gcry_sexp_t data;
51   gcry_sexp_t data_encrypted;
52   gcry_sexp_t data_signed;
53 } *context_t;
54
55 typedef int (*work_t) (context_t context, unsigned int final);
56
57
58 static void
59 fail (const char *format, ...)
60 {
61   va_list arg_ptr;
62
63   fputs ( PGM ": ", stderr);
64   va_start (arg_ptr, format);
65   vfprintf (stderr, format, arg_ptr);
66   va_end (arg_ptr);
67   error_count++;
68 }
69
70 static void
71 die (const char *format, ...)
72 {
73   va_list arg_ptr;
74
75   putchar ('\n');
76   fputs ( PGM ": ", stderr);
77   va_start (arg_ptr, format);
78   vfprintf (stderr, format, arg_ptr);
79   va_end (arg_ptr);
80   exit (1);
81 }
82
83 static void
84 show_sexp (const char *prefix, gcry_sexp_t a)
85 {
86   char *buf;
87   size_t size;
88
89   fputs (prefix, stderr);
90   size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
91   buf = malloc (size);
92   if (!buf)
93     die ("out of core\n");
94
95   gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
96   fprintf (stderr, "%.*s", (int)size, buf);
97 }
98
99
100
101 static void
102 benchmark (work_t worker, context_t context)
103 {
104   clock_t timer_start, timer_stop;
105   unsigned int loop = 10;
106   unsigned int i = 0;
107   struct tms timer;
108   int ret = 0;
109
110 #ifdef HAVE_W32_SYSTEM
111   timer_start = clock ();
112 #else
113   times (&timer);
114   timer_start = timer.tms_utime;
115 #endif
116   for (i = 0; i < loop; i++)
117     {
118       ret = (*worker) (context, (i + 1) == loop);
119       if (! ret)
120         break;
121     }
122 #ifdef HAVE_W32_SYSTEM
123   timer_stop = clock ();
124 #else
125   times (&timer);
126   timer_stop = timer.tms_utime;
127 #endif
128
129   if (ret)
130     printf ("%.0f ms\n",
131             (((double) ((timer_stop - timer_start) / loop)) / CLOCKS_PER_SEC)
132             * 10000000);
133   else
134     printf ("[skipped]\n");
135 }
136
137 static int
138 work_encrypt (context_t context, unsigned int final)
139 {
140   gcry_error_t err = GPG_ERR_NO_ERROR;
141   gcry_sexp_t data_encrypted = NULL;
142   int ret = 1;
143
144   err = gcry_pk_encrypt (&data_encrypted,
145                          context->data, context->key_public);
146   if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
147     {
148       err = GPG_ERR_NO_ERROR;
149       ret = 0;
150     }
151   else
152     {
153       assert (! err);
154
155       if (final)
156         context->data_encrypted = data_encrypted;
157       else
158         gcry_sexp_release (data_encrypted);
159     }
160
161   return ret;
162 }
163
164 static int
165 work_decrypt (context_t context, unsigned int final)
166 {
167   gcry_error_t err = GPG_ERR_NO_ERROR;
168   int ret = 1;
169
170   if (! context->data_encrypted)
171     ret = 0;
172   else
173     {
174       gcry_sexp_t data_decrypted = NULL;
175       
176       err = gcry_pk_decrypt (&data_decrypted,
177                              context->data_encrypted,
178                              context->key_secret);
179       assert (! err);
180       if (final)
181         {
182           gcry_sexp_release (context->data_encrypted);
183           context->data_encrypted = NULL;
184         }
185       gcry_sexp_release (data_decrypted);
186     }
187
188   return ret;
189 }
190
191 static int
192 work_sign (context_t context, unsigned int final)
193 {
194   gcry_error_t err = GPG_ERR_NO_ERROR;
195   gcry_sexp_t data_signed = NULL;
196   int ret = 1;
197
198   err = gcry_pk_sign (&data_signed,
199                       context->data, context->key_secret);
200   if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
201     {
202       err = GPG_ERR_NO_ERROR;
203       ret = 0;
204     }
205   else if (err)
206     {
207       fail ("pk_sign failed: %s\n", gpg_strerror (err));
208       ret = 0;
209     }
210   else
211     {
212       if (final)
213         context->data_signed = data_signed;
214       else
215         gcry_sexp_release (data_signed);
216     }
217
218   return ret;
219 }
220
221 static int
222 work_verify (context_t context, unsigned int final)
223 {
224   gcry_error_t err = GPG_ERR_NO_ERROR;
225   int ret = 1;
226
227   if (!context->data_signed)
228     return 0;
229
230   err = gcry_pk_verify (context->data_signed,
231                         context->data,
232                         context->key_public);
233   if (err)
234     {
235       show_sexp ("data_signed:\n", context->data_signed);
236       show_sexp ("data:\n", context->data);
237       fail ("pk_verify failed: %s\n", gpg_strerror (err));
238       ret = 0;
239     }
240   else if (final)
241     {
242       gcry_sexp_release (context->data_signed);
243       context->data_signed = NULL;
244     }
245     
246   return ret;
247 }
248
249 static void
250 process_key_pair (context_t context)
251 {
252   struct
253   {
254     work_t worker;
255     const char *identifier;
256   } worker_functions[] = { { work_encrypt, "encrypt" },
257                            { work_decrypt, "decrypt" },
258                            { work_sign,    "sign"    },
259                            { work_verify,  "verify"  } };
260   unsigned int i = 0;
261
262   for (i = 0; i < (sizeof (worker_functions) / sizeof (*worker_functions)); i++)
263     {
264       printf ("%s: ", worker_functions[i].identifier);
265       benchmark (worker_functions[i].worker, context);
266     }
267 }
268
269 static void
270 context_init (context_t context, gcry_sexp_t key_secret, gcry_sexp_t key_public)
271 {
272   gcry_error_t err = GPG_ERR_NO_ERROR;
273   unsigned int key_size = 0;
274   gcry_mpi_t data = NULL;
275   gcry_sexp_t data_sexp = NULL;
276
277   key_size = gcry_pk_get_nbits (key_secret);
278   assert (key_size);
279
280   data = gcry_mpi_new (key_size);
281   assert (data);
282
283   gcry_mpi_randomize (data, key_size, GCRY_STRONG_RANDOM);
284   gcry_mpi_clear_bit (data, key_size - 1);
285   err = gcry_sexp_build (&data_sexp, NULL,
286                          "(data (flags raw) (value %m))",
287                          data);
288   assert (! err);
289   gcry_mpi_release (data);
290
291   context->key_secret = key_secret;
292   context->key_public = key_public;
293   context->data = data_sexp;
294   context->data_encrypted = NULL;
295   context->data_signed = NULL;
296 }
297
298 static void
299 context_destroy (context_t context)
300 {
301   gcry_sexp_release (context->key_secret);
302   gcry_sexp_release (context->key_public);
303   gcry_sexp_release (context->data);
304 }
305
306 static void
307 process_key_pair_file (const char *key_pair_file)
308 {
309   gcry_error_t err = GPG_ERR_NO_ERROR;
310   void *key_pair_buffer = NULL;
311   gcry_sexp_t key_pair_sexp = NULL;
312   gcry_sexp_t key_secret_sexp = NULL;
313   gcry_sexp_t key_public_sexp = NULL;
314   struct context context = { NULL };
315   struct stat statbuf;
316   int key_pair_fd = -1;
317   int ret = 0;
318
319   ret = stat (key_pair_file, &statbuf);
320   assert (! ret);
321
322   key_pair_fd = open (key_pair_file, O_RDONLY);
323   assert (key_pair_fd != -1);
324
325   key_pair_buffer = mmap (NULL, statbuf.st_size, PROT_READ,
326                           MAP_PRIVATE, key_pair_fd, 0);
327   assert (key_pair_buffer != MAP_FAILED);
328
329   err = gcry_sexp_sscan (&key_pair_sexp, NULL,
330                          key_pair_buffer, statbuf.st_size);
331   assert (! err);
332
333   key_secret_sexp = gcry_sexp_find_token (key_pair_sexp, "private-key", 0);
334   assert (key_secret_sexp);
335   key_public_sexp = gcry_sexp_find_token (key_pair_sexp, "public-key", 0);
336   assert (key_public_sexp);
337
338   gcry_sexp_release (key_pair_sexp);
339   ret = munmap (key_pair_buffer, statbuf.st_size);
340   assert (! ret);
341   ret = close (key_pair_fd);
342   assert (! ret);
343
344   context_init (&context, key_secret_sexp, key_public_sexp);
345
346   printf ("Key file: %s\n", key_pair_file);
347   process_key_pair (&context);
348   printf ("\n");
349
350   context_destroy (&context);
351 }
352
353
354 static void
355 generate_key (const char *algorithm, const char *key_size)
356 {
357   gcry_error_t err = GPG_ERR_NO_ERROR;
358   size_t key_pair_buffer_size = 0;
359   char *key_pair_buffer = NULL;
360   gcry_sexp_t key_spec = NULL;
361   gcry_sexp_t key_pair = NULL;
362
363   if (isdigit ((unsigned int)*key_size))
364     err = gcry_sexp_build (&key_spec, NULL,
365                            "(genkey (%s (nbits %s)))",
366                            algorithm, key_size);
367   else
368     err = gcry_sexp_build (&key_spec, NULL,
369                            "(genkey (%s (curve %s)))",
370                            algorithm, key_size);
371   if (err)
372     die ("sexp_build failed: %s\n", gpg_strerror (err));
373
374   err = gcry_pk_genkey (&key_pair, key_spec);
375   if (err)
376     {
377       show_sexp ("request:\n", key_spec);
378       die ("pk_genkey failed: %s\n", gpg_strerror (err));
379     }
380
381   key_pair_buffer_size = gcry_sexp_sprint (key_pair, GCRYSEXP_FMT_ADVANCED,
382                                            NULL, 0);
383   key_pair_buffer = malloc (key_pair_buffer_size);
384   assert (key_pair_buffer);
385
386   gcry_sexp_sprint (key_pair, GCRYSEXP_FMT_ADVANCED,
387                     key_pair_buffer, key_pair_buffer_size);
388
389   printf ("%.*s", (int)key_pair_buffer_size, key_pair_buffer);
390 }
391
392
393
394 int
395 main (int argc, char **argv)
396 {
397   int last_argc = -1;
398   int genkey_mode = 0;
399
400   if (argc)
401     { argc--; argv++; }
402
403   gcry_control (GCRYCTL_DISABLE_SECMEM);
404   if (!gcry_check_version (GCRYPT_VERSION))
405     {
406       fprintf (stderr, PGM ": version mismatch\n");
407       exit (1);
408     }
409
410   while (argc && last_argc != argc )
411     {
412       last_argc = argc;
413       if (!strcmp (*argv, "--"))
414         {
415           argc--; argv++;
416           break;
417         }
418       else if (!strcmp (*argv, "--help"))
419         {
420           puts ("Usage: " PGM " [OPTIONS] [FILES]\n"
421                 "Various public key tests:\n\n"
422                 "  Default is to process all given key files\n\n"
423                 "  --genkey ALGONAME SIZE  Generate a public key\n"
424                 "\n"
425                 "  --verbose    enable extra informational output\n"
426                 "  --debug      enable additional debug output\n"
427                 "  --help       display this help and exit\n\n");
428           exit (0);
429         }
430       else if (!strcmp (*argv, "--verbose"))
431         {
432           verbose = 1;
433           argc--; argv++;
434         }
435       else if (!strcmp (*argv, "--debug"))
436         {
437           verbose = debug = 1;
438           argc--; argv++;
439         }
440       else if (!strcmp (*argv, "--genkey"))
441         {
442           genkey_mode = 1;
443           argc--; argv++;
444         }
445     }          
446
447   if (genkey_mode)
448     {
449       /* No valuable keys are create, so we can speed up our RNG. */
450       gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
451     } 
452   if (debug)
453     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
454   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
455
456   
457   if (genkey_mode && argc == 2)
458     {
459       generate_key (argv[0], argv[1]);
460     }
461   else if (!genkey_mode && argc)
462     {
463       int i;
464       
465       for (i = 0; i < argc; i++)
466         process_key_pair_file (argv[i]);
467     }
468   else
469     {
470       fprintf (stderr, "usage: " PGM
471                " [OPTIONS] [FILES] (try --help for more information)\n");
472       exit (1);
473     }
474   
475   return error_count ? 1 : 0;
476 }