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