/* pkbench.c - Pubkey menchmarking * Copyright (C) 2004, 2005, 2008 Free Software Foundation, Inc. * * This file is part of Libgcrypt. * * Libgcrypt is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Libgcrypt is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifndef HAVE_W32_SYSTEM # include #endif /*HAVE_W32_SYSTEM*/ #include #include #include #include #define PGM "pkbench" static int verbose; static int debug; static int error_count; typedef struct context { gcry_sexp_t key_secret; gcry_sexp_t key_public; gcry_sexp_t data; gcry_sexp_t data_encrypted; gcry_sexp_t data_signed; } *context_t; typedef int (*work_t) (context_t context, unsigned int final); static void fail (const char *format, ...) { va_list arg_ptr; fputs ( PGM ": ", stderr); va_start (arg_ptr, format); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); error_count++; } static void die (const char *format, ...) { va_list arg_ptr; putchar ('\n'); fputs ( PGM ": ", stderr); va_start (arg_ptr, format); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); exit (1); } static void show_sexp (const char *prefix, gcry_sexp_t a) { char *buf; size_t size; fputs (prefix, stderr); size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); buf = gcry_xmalloc (size); gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); fprintf (stderr, "%.*s", (int)size, buf); gcry_free (buf); } static void * read_file (const char *fname, size_t *r_length) { FILE *fp; struct stat st; char *buf; size_t buflen; fp = fopen (fname, "rb"); if (!fp) { fail ("can't open `%s': %s\n", fname, strerror (errno)); return NULL; } if (fstat (fileno(fp), &st)) { fail ("can't stat `%s': %s\n", fname, strerror (errno)); fclose (fp); return NULL; } buflen = st.st_size; buf = gcry_xmalloc (buflen+1); if (fread (buf, buflen, 1, fp) != 1) { fail ("error reading `%s': %s\n", fname, strerror (errno)); fclose (fp); gcry_free (buf); return NULL; } fclose (fp); if (r_length) *r_length = buflen; return buf; } static void benchmark (work_t worker, context_t context) { clock_t timer_start, timer_stop; unsigned int loop = 10; unsigned int i = 0; struct tms timer; int ret = 0; #ifdef HAVE_W32_SYSTEM timer_start = clock (); #else times (&timer); timer_start = timer.tms_utime; #endif for (i = 0; i < loop; i++) { ret = (*worker) (context, (i + 1) == loop); if (! ret) break; } #ifdef HAVE_W32_SYSTEM timer_stop = clock (); #else times (&timer); timer_stop = timer.tms_utime; #endif if (ret) printf ("%.0f ms\n", (((double) ((timer_stop - timer_start) / loop)) / CLOCKS_PER_SEC) * 10000000); else printf ("[skipped]\n"); } static int work_encrypt (context_t context, unsigned int final) { gcry_error_t err = GPG_ERR_NO_ERROR; gcry_sexp_t data_encrypted = NULL; int ret = 1; err = gcry_pk_encrypt (&data_encrypted, context->data, context->key_public); if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) { err = GPG_ERR_NO_ERROR; ret = 0; } else { assert (! err); if (final) context->data_encrypted = data_encrypted; else gcry_sexp_release (data_encrypted); } return ret; } static int work_decrypt (context_t context, unsigned int final) { gcry_error_t err = GPG_ERR_NO_ERROR; int ret = 1; if (! context->data_encrypted) ret = 0; else { gcry_sexp_t data_decrypted = NULL; err = gcry_pk_decrypt (&data_decrypted, context->data_encrypted, context->key_secret); assert (! err); if (final) { gcry_sexp_release (context->data_encrypted); context->data_encrypted = NULL; } gcry_sexp_release (data_decrypted); } return ret; } static int work_sign (context_t context, unsigned int final) { gcry_error_t err = GPG_ERR_NO_ERROR; gcry_sexp_t data_signed = NULL; int ret = 1; err = gcry_pk_sign (&data_signed, context->data, context->key_secret); if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) { err = GPG_ERR_NO_ERROR; ret = 0; } else if (err) { fail ("pk_sign failed: %s\n", gpg_strerror (err)); ret = 0; } else { if (final) context->data_signed = data_signed; else gcry_sexp_release (data_signed); } return ret; } static int work_verify (context_t context, unsigned int final) { gcry_error_t err = GPG_ERR_NO_ERROR; int ret = 1; if (!context->data_signed) return 0; err = gcry_pk_verify (context->data_signed, context->data, context->key_public); if (err) { show_sexp ("data_signed:\n", context->data_signed); show_sexp ("data:\n", context->data); fail ("pk_verify failed: %s\n", gpg_strerror (err)); ret = 0; } else if (final) { gcry_sexp_release (context->data_signed); context->data_signed = NULL; } return ret; } static void process_key_pair (context_t context) { struct { work_t worker; const char *identifier; } worker_functions[] = { { work_encrypt, "encrypt" }, { work_decrypt, "decrypt" }, { work_sign, "sign" }, { work_verify, "verify" } }; unsigned int i = 0; for (i = 0; i < (sizeof (worker_functions) / sizeof (*worker_functions)); i++) { printf ("%s: ", worker_functions[i].identifier); benchmark (worker_functions[i].worker, context); } } static void context_init (context_t context, gcry_sexp_t key_secret, gcry_sexp_t key_public) { gcry_error_t err = GPG_ERR_NO_ERROR; unsigned int key_size = 0; gcry_mpi_t data = NULL; gcry_sexp_t data_sexp = NULL; key_size = gcry_pk_get_nbits (key_secret); assert (key_size); data = gcry_mpi_new (key_size); assert (data); gcry_mpi_randomize (data, key_size, GCRY_STRONG_RANDOM); gcry_mpi_clear_bit (data, key_size - 1); err = gcry_sexp_build (&data_sexp, NULL, "(data (flags raw) (value %m))", data); assert (! err); gcry_mpi_release (data); context->key_secret = key_secret; context->key_public = key_public; context->data = data_sexp; context->data_encrypted = NULL; context->data_signed = NULL; } static void context_destroy (context_t context) { gcry_sexp_release (context->key_secret); gcry_sexp_release (context->key_public); gcry_sexp_release (context->data); } static void process_key_pair_file (const char *key_pair_file) { gcry_error_t err = GPG_ERR_NO_ERROR; void *key_pair_buffer = NULL; gcry_sexp_t key_pair_sexp = NULL; gcry_sexp_t key_secret_sexp = NULL; gcry_sexp_t key_public_sexp = NULL; struct context context = { NULL }; size_t file_length; key_pair_buffer = read_file (key_pair_file, &file_length); if (!key_pair_buffer) die ("failed to open `%s'\n", key_pair_file); err = gcry_sexp_sscan (&key_pair_sexp, NULL, key_pair_buffer, file_length); if (err) die ("gcry_sexp_sscan failed\n"); key_secret_sexp = gcry_sexp_find_token (key_pair_sexp, "private-key", 0); assert (key_secret_sexp); key_public_sexp = gcry_sexp_find_token (key_pair_sexp, "public-key", 0); assert (key_public_sexp); gcry_sexp_release (key_pair_sexp); context_init (&context, key_secret_sexp, key_public_sexp); printf ("Key file: %s\n", key_pair_file); process_key_pair (&context); printf ("\n"); context_destroy (&context); gcry_free (key_pair_buffer); } static void generate_key (const char *algorithm, const char *key_size) { gcry_error_t err = GPG_ERR_NO_ERROR; size_t key_pair_buffer_size = 0; char *key_pair_buffer = NULL; gcry_sexp_t key_spec = NULL; gcry_sexp_t key_pair = NULL; if (isdigit ((unsigned int)*key_size)) err = gcry_sexp_build (&key_spec, NULL, "(genkey (%s (nbits %s)))", algorithm, key_size); else err = gcry_sexp_build (&key_spec, NULL, "(genkey (%s (curve %s)))", algorithm, key_size); if (err) die ("sexp_build failed: %s\n", gpg_strerror (err)); err = gcry_pk_genkey (&key_pair, key_spec); if (err) { show_sexp ("request:\n", key_spec); die ("pk_genkey failed: %s\n", gpg_strerror (err)); } key_pair_buffer_size = gcry_sexp_sprint (key_pair, GCRYSEXP_FMT_ADVANCED, NULL, 0); key_pair_buffer = gcry_xmalloc (key_pair_buffer_size); gcry_sexp_sprint (key_pair, GCRYSEXP_FMT_ADVANCED, key_pair_buffer, key_pair_buffer_size); printf ("%.*s", (int)key_pair_buffer_size, key_pair_buffer); gcry_free (key_pair_buffer); } int main (int argc, char **argv) { int last_argc = -1; int genkey_mode = 0; int fips_mode = 0; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--help")) { puts ("Usage: " PGM " [OPTIONS] [FILES]\n" "Various public key tests:\n\n" " Default is to process all given key files\n\n" " --genkey ALGONAME SIZE Generate a public key\n" "\n" " --verbose enable extra informational output\n" " --debug enable additional debug output\n" " --help display this help and exit\n\n"); exit (0); } else if (!strcmp (*argv, "--verbose")) { verbose++; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose = debug = 1; argc--; argv++; } else if (!strcmp (*argv, "--genkey")) { genkey_mode = 1; argc--; argv++; } else if (!strcmp (*argv, "--fips")) { fips_mode = 1; argc--; argv++; } } gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose); if (fips_mode) gcry_control (GCRYCTL_FORCE_FIPS_MODE, 0); gcry_control (GCRYCTL_DISABLE_SECMEM); if (!gcry_check_version (GCRYPT_VERSION)) { fprintf (stderr, PGM ": version mismatch\n"); exit (1); } if (genkey_mode) { /* No valuable keys are create, so we can speed up our RNG. */ gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); } if (debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); if (genkey_mode && argc == 2) { generate_key (argv[0], argv[1]); } else if (!genkey_mode && argc) { int i; for (i = 0; i < argc; i++) process_key_pair_file (argv[i]); } else { fprintf (stderr, "usage: " PGM " [OPTIONS] [FILES] (try --help for more information)\n"); exit (1); } return error_count ? 1 : 0; }