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