blob: 67b94dc9a640d40324e2f6fffa43e713fdc2066f [file] [log] [blame]
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <gcrypt.h>
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>
#ifndef HAVE_W32_SYSTEM
# include <sys/times.h>
#endif /*HAVE_W32_SYSTEM*/
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#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;
}