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
40static int verbose;
41static int debug;
42static int error_count;
43
44
45typedef 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
54typedef int (*work_t) (context_t context, unsigned int final);
55
56
57static void
58fail (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
69static void
70die (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
82static void
83show_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
98static void *
99read_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
138static void
139benchmark (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
174static int
175work_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
201static int
202work_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
228static int
229work_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
258static int
259work_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
286static void
287process_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
306static void
307context_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
335static void
336context_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
343static void
344process_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
381static void
382generate_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
421int
422main (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}
515