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