/* fipsdrv.c  -  A driver to help with FIPS CAVS tests.
   Copyright (C) 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 <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#ifdef HAVE_W32_SYSTEM
# include <fcntl.h> /* We need setmode().  */
#else
# include <signal.h>
#endif
#include <assert.h>
#include <unistd.h>

#ifndef _GCRYPT_IN_LIBGCRYPT
# include <gcrypt.h>
# define PACKAGE_BUGREPORT "devnull@example.org"
# define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]"
#endif
#include "../src/gcrypt-testapi.h"

#define PGM "fipsdrv"
#include "t-common.h"


/* Binary input flag.  */
static int binary_input;

/* Binary output flag.  */
static int binary_output;

/* Base64 output flag.  */
static int base64_output;

/* We need to know whether we are in loop_mode.  */
static int loop_mode;

/* If true some functions are modified to print the output in the CAVS
   response file format.  */
static int standalone_mode;


/* ASN.1 classes.  */
enum
{
  UNIVERSAL = 0,
  APPLICATION = 1,
  ASNCONTEXT = 2,
  PRIVATE = 3
};


/* ASN.1 tags.  */
enum
{
  TAG_NONE = 0,
  TAG_BOOLEAN = 1,
  TAG_INTEGER = 2,
  TAG_BIT_STRING = 3,
  TAG_OCTET_STRING = 4,
  TAG_NULL = 5,
  TAG_OBJECT_ID = 6,
  TAG_OBJECT_DESCRIPTOR = 7,
  TAG_EXTERNAL = 8,
  TAG_REAL = 9,
  TAG_ENUMERATED = 10,
  TAG_EMBEDDED_PDV = 11,
  TAG_UTF8_STRING = 12,
  TAG_REALTIVE_OID = 13,
  TAG_SEQUENCE = 16,
  TAG_SET = 17,
  TAG_NUMERIC_STRING = 18,
  TAG_PRINTABLE_STRING = 19,
  TAG_TELETEX_STRING = 20,
  TAG_VIDEOTEX_STRING = 21,
  TAG_IA5_STRING = 22,
  TAG_UTC_TIME = 23,
  TAG_GENERALIZED_TIME = 24,
  TAG_GRAPHIC_STRING = 25,
  TAG_VISIBLE_STRING = 26,
  TAG_GENERAL_STRING = 27,
  TAG_UNIVERSAL_STRING = 28,
  TAG_CHARACTER_STRING = 29,
  TAG_BMP_STRING = 30
};

/* ASN.1 Parser object.  */
struct tag_info
{
  int class;             /* Object class.  */
  unsigned long tag;     /* The tag of the object.  */
  unsigned long length;  /* Length of the values.  */
  int nhdr;              /* Length of the header (TL).  */
  unsigned int ndef:1;   /* The object has an indefinite length.  */
  unsigned int cons:1;   /* This is a constructed object.  */
};


static void
showhex (const char *prefix, const void *buffer, size_t length)
{
  const unsigned char *p = buffer;

  if (prefix)
    fprintf (stderr, PGM ": %s: ", prefix);
  while (length-- )
    fprintf (stderr, "%02X", *p++);
  if (prefix)
    putc ('\n', stderr);
}

/* static void */
/* show_sexp (const char *prefix, gcry_sexp_t a) */
/* { */
/*   char *buf; */
/*   size_t size; */

/*   if (prefix) */
/*     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); */
/* } */


/* Convert STRING consisting of hex characters into its binary
   representation and store that at BUFFER.  BUFFER needs to be of
   LENGTH bytes.  The function checks that the STRING will convert
   exactly to LENGTH bytes. The string is delimited by either end of
   string or a white space character.  The function returns -1 on
   error or the length of the parsed string.  */
static int
hex2bin (const char *string, void *buffer, size_t length)
{
  int i;
  const char *s = string;

  for (i=0; i < length; )
    {
      if (!hexdigitp (s) || !hexdigitp (s+1))
        return -1;           /* Invalid hex digits. */
      ((unsigned char*)buffer)[i++] = xtoi_2 (s);
      s += 2;
    }
  if (*s && (!my_isascii (*s) || !isspace (*s)) )
    return -1;             /* Not followed by Nul or white space.  */
  if (i != length)
    return -1;             /* Not of expected length.  */
  if (*s)
    s++; /* Skip the delimiter. */
  return s - string;
}


/* Convert STRING consisting of hex characters into its binary
   representation and return it as an allocated buffer. The valid
   length of the buffer is returned at R_LENGTH.  The string is
   delimited by end of string.  The function returns NULL on
   error.  */
static void *
hex2buffer (const char *string, size_t *r_length)
{
  const char *s;
  unsigned char *buffer;
  size_t length;

  buffer = gcry_xmalloc (strlen(string)/2+1);
  length = 0;
  for (s=string; *s; s +=2 )
    {
      if (!hexdigitp (s) || !hexdigitp (s+1))
        return NULL;           /* Invalid hex digits. */
      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
    }
  *r_length = length;
  return buffer;
}


static char *
read_textline (FILE *fp)
{
  char line[256];
  char *p;
  int any = 0;

  /* Read line but skip over initial empty lines.  */
  do
    {
      do
        {
          if (!fgets (line, sizeof line, fp))
            {
              if (feof (fp))
                return NULL;
              die ("error reading input line: %s\n", strerror (errno));
            }
          p = strchr (line, '\n');
          if (p)
            *p = 0;
          p = line + (*line? (strlen (line)-1):0);
          for ( ;p > line; p--)
            if (my_isascii (*p) && isspace (*p))
              *p = 0;
        }
      while (!any && !*line);
      any = 1;
    }
  while (*line == '#');  /* Always skip comment lines.  */
  if (verbose > 1)
    fprintf (stderr, PGM ": received line: %s\n", line);
  return gcry_xstrdup (line);
}

static char *
read_hexline (FILE *fp, size_t *retlen)
{
  char *line, *p;

  line = read_textline (fp);
  if (!line)
    return NULL;
  p = hex2buffer (line, retlen);
  if (!p)
    die ("error decoding hex string on input\n");
  gcry_free (line);
  return p;
}

static void
skip_to_empty_line (FILE *fp)
{
  char line[256];
  char *p;

  do
    {
      if (!fgets (line, sizeof line, fp))
        {
          if (feof (fp))
            return;
          die ("error reading input line: %s\n", strerror (errno));
        }
      p = strchr (line, '\n');
      if (p)
        *p =0;
    }
  while (*line);
}



/* Read a file from stream FP into a newly allocated buffer and return
   that buffer.  The valid length of the buffer is stored at R_LENGTH.
   Returns NULL on failure.  If decode is set, the file is assumed to
   be hex encoded and the decoded content is returned. */
static void *
read_file (FILE *fp, int decode, size_t *r_length)
{
  char *buffer;
  size_t buflen;
  size_t nread, bufsize = 0;

  *r_length = 0;
#define NCHUNK 8192
#ifdef HAVE_DOSISH_SYSTEM
  setmode (fileno(fp), O_BINARY);
#endif
  buffer = NULL;
  buflen = 0;
  do
    {
      bufsize += NCHUNK;
      if (!buffer)
        buffer = gcry_xmalloc (bufsize);
      else
        buffer = gcry_xrealloc (buffer, bufsize);

      nread = fread (buffer + buflen, 1, NCHUNK, fp);
      if (nread < NCHUNK && ferror (fp))
        {
          gcry_free (buffer);
          return NULL;
        }
      buflen += nread;
    }
  while (nread == NCHUNK);
#undef NCHUNK
  if (decode)
    {
      const char *s;
      char *p;

      for (s=buffer,p=buffer,nread=0; nread+1 < buflen; s += 2, nread +=2 )
        {
          if (!hexdigitp (s) || !hexdigitp (s+1))
            {
              gcry_free (buffer);
              return NULL;  /* Invalid hex digits. */
            }
          *(unsigned char*)p++ = xtoi_2 (s);
        }
      if (nread != buflen)
        {
          gcry_free (buffer);
          return NULL;  /* Odd number of hex digits. */
        }
      buflen = p - buffer;
    }

  *r_length = buflen;
  return buffer;
}

/* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Returns
   the new length of the buffer.  Dies on error.  */
static size_t
base64_decode (char *buffer, size_t length)
{
  static unsigned char const asctobin[128] =
    {
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
      0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
      0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
      0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
      0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
      0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
    };

  int idx = 0;
  unsigned char val = 0;
  int c = 0;
  char *d, *s;
  int lfseen = 1;

  /* Find BEGIN line.  */
  for (s=buffer; length; length--, s++)
    {
      if (lfseen && *s == '-' && length > 11 && !memcmp (s, "-----BEGIN ", 11))
        {
          for (; length && *s != '\n'; length--, s++)
            ;
          break;
        }
      lfseen = (*s == '\n');
    }

  /* Decode until pad character or END line.  */
  for (d=buffer; length; length--, s++)
    {
      if (lfseen && *s == '-' && length > 9 && !memcmp (s, "-----END ", 9))
        break;
      if ((lfseen = (*s == '\n')) || *s == ' ' || *s == '\r' || *s == '\t')
        continue;
      if (*s == '=')
        {
          /* Pad character: stop */
          if (idx == 1)
            *d++ = val;
          break;
        }

      if ( (*s & 0x80) || (c = asctobin[*(unsigned char *)s]) == 0xff)
        die ("invalid base64 character %02X at pos %d detected\n",
             *(unsigned char*)s, (int)(s-buffer));

      switch (idx)
        {
        case 0:
          val = c << 2;
          break;
        case 1:
          val |= (c>>4)&3;
          *d++ = val;
          val = (c<<4)&0xf0;
          break;
        case 2:
          val |= (c>>2)&15;
          *d++ = val;
          val = (c<<6)&0xc0;
          break;
        case 3:
          val |= c&0x3f;
          *d++ = val;
          break;
        }
      idx = (idx+1) % 4;
    }

  return d - buffer;
}


/* Parse the buffer at the address BUFFER which consists of the number
   of octets as stored at BUFLEN.  Return the tag and the length part
   from the TLV triplet.  Update BUFFER and BUFLEN on success.  Checks
   that the encoded length does not exhaust the length of the provided
   buffer. */
static int
parse_tag (unsigned char const **buffer, size_t *buflen, struct tag_info *ti)
{
  int c;
  unsigned long tag;
  const unsigned char *buf = *buffer;
  size_t length = *buflen;

  ti->length = 0;
  ti->ndef = 0;
  ti->nhdr = 0;

  /* Get the tag */
  if (!length)
    return -1; /* Premature EOF.  */
  c = *buf++; length--;
  ti->nhdr++;

  ti->class = (c & 0xc0) >> 6;
  ti->cons  = !!(c & 0x20);
  tag       = (c & 0x1f);

  if (tag == 0x1f)
    {
      tag = 0;
      do
        {
          tag <<= 7;
          if (!length)
            return -1; /* Premature EOF.  */
          c = *buf++; length--;
          ti->nhdr++;
          tag |= (c & 0x7f);
        }
      while ( (c & 0x80) );
    }
  ti->tag = tag;

  /* Get the length */
  if (!length)
    return -1; /* Premature EOF. */
  c = *buf++; length--;
  ti->nhdr++;

  if ( !(c & 0x80) )
    ti->length = c;
  else if (c == 0x80)
    ti->ndef = 1;
  else if (c == 0xff)
    return -1; /* Forbidden length value.  */
  else
    {
      unsigned long len = 0;
      int count = c & 0x7f;

      for (; count; count--)
        {
          len <<= 8;
          if (!length)
            return -1; /* Premature EOF.  */
          c = *buf++; length--;
          ti->nhdr++;
          len |= (c & 0xff);
        }
      ti->length = len;
    }

  if (ti->class == UNIVERSAL && !ti->tag)
    ti->length = 0;

  if (ti->length > length)
    return -1; /* Data larger than buffer.  */

  *buffer = buf;
  *buflen = length;
  return 0;
}


/* Read the file FNAME assuming it is a PEM encoded private key file
   and return an S-expression.  With SHOW set, the key parameters are
   printed.  */
static gcry_sexp_t
read_private_key_file (const char *fname, int show)
{
  gcry_error_t err;
  FILE *fp;
  char *buffer;
  size_t buflen;
  const unsigned char *der;
  size_t derlen;
  struct tag_info ti;
  gcry_mpi_t keyparms[8];
  int n_keyparms = 8;
  int idx;
  gcry_sexp_t s_key;

  fp = fopen (fname, binary_input?"rb":"r");
  if (!fp)
    die ("can't open `%s': %s\n", fname, strerror (errno));
  buffer = read_file (fp, 0, &buflen);
  if (!buffer)
    die ("error reading `%s'\n", fname);
  fclose (fp);

  buflen = base64_decode (buffer, buflen);

  /* Parse the ASN.1 structure.  */
  der = (const unsigned char*)buffer;
  derlen = buflen;
  if ( parse_tag (&der, &derlen, &ti)
       || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef)
    goto bad_asn1;
  if ( parse_tag (&der, &derlen, &ti)
       || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef)
    goto bad_asn1;
  if (ti.length != 1 || *der)
    goto bad_asn1;  /* The value of the first integer is no 0. */
  der += ti.length; derlen -= ti.length;

  for (idx=0; idx < n_keyparms; idx++)
    {
      if ( parse_tag (&der, &derlen, &ti)
           || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef)
        goto bad_asn1;
      if (show)
        {
          char prefix[2];

          prefix[0] = idx < 8? "nedpq12u"[idx] : '?';
          prefix[1] = 0;
          showhex (prefix, der, ti.length);
        }
      err = gcry_mpi_scan (keyparms+idx, GCRYMPI_FMT_USG, der, ti.length,NULL);
      if (err)
        die ("error scanning RSA parameter %d: %s\n", idx, gpg_strerror (err));
      der += ti.length; derlen -= ti.length;
    }
  if (idx != n_keyparms)
    die ("not enough RSA key parameters\n");

  gcry_free (buffer);

  /* Convert from OpenSSL parameter ordering to the OpenPGP order. */
  /* First check that p < q; if not swap p and q and recompute u.  */
  if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0)
    {
      gcry_mpi_swap (keyparms[3], keyparms[4]);
      gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]);
    }

  /* Build the S-expression.  */
  err = gcry_sexp_build (&s_key, NULL,
                         "(private-key(rsa(n%m)(e%m)"
                         /**/            "(d%m)(p%m)(q%m)(u%m)))",
                         keyparms[0], keyparms[1], keyparms[2],
                         keyparms[3], keyparms[4], keyparms[7] );
  if (err)
    die ("error building S-expression: %s\n", gpg_strerror (err));

  for (idx=0; idx < n_keyparms; idx++)
    gcry_mpi_release (keyparms[idx]);

  return s_key;

 bad_asn1:
  die ("invalid ASN.1 structure in `%s'\n", fname);
  return NULL; /*NOTREACHED*/
}


/* Read the file FNAME assuming it is a PEM encoded public key file
   and return an S-expression.  With SHOW set, the key parameters are
   printed.  */
static gcry_sexp_t
read_public_key_file (const char *fname, int show)
{
  gcry_error_t err;
  FILE *fp;
  char *buffer;
  size_t buflen;
  const unsigned char *der;
  size_t derlen;
  struct tag_info ti;
  gcry_mpi_t keyparms[2];
  int n_keyparms = 2;
  int idx;
  gcry_sexp_t s_key;

  fp = fopen (fname, binary_input?"rb":"r");
  if (!fp)
    die ("can't open `%s': %s\n", fname, strerror (errno));
  buffer = read_file (fp, 0, &buflen);
  if (!buffer)
    die ("error reading `%s'\n", fname);
  fclose (fp);

  buflen = base64_decode (buffer, buflen);

  /* Parse the ASN.1 structure.  */
  der = (const unsigned char*)buffer;
  derlen = buflen;
  if ( parse_tag (&der, &derlen, &ti)
       || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef)
    goto bad_asn1;
  if ( parse_tag (&der, &derlen, &ti)
       || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef)
    goto bad_asn1;
  /* We skip the description of the key parameters and assume it is RSA.  */
  der += ti.length; derlen -= ti.length;

  if ( parse_tag (&der, &derlen, &ti)
       || ti.tag != TAG_BIT_STRING || ti.class || ti.cons || ti.ndef)
    goto bad_asn1;
  if (ti.length < 1 || *der)
    goto bad_asn1;  /* The number of unused bits needs to be 0. */
  der += 1; derlen -= 1;

  /* Parse the BIT string.  */
  if ( parse_tag (&der, &derlen, &ti)
       || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef)
    goto bad_asn1;

  for (idx=0; idx < n_keyparms; idx++)
    {
      if ( parse_tag (&der, &derlen, &ti)
           || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef)
        goto bad_asn1;
      if (show)
        {
          char prefix[2];

          prefix[0] = idx < 2? "ne"[idx] : '?';
          prefix[1] = 0;
          showhex (prefix, der, ti.length);
        }
      err = gcry_mpi_scan (keyparms+idx, GCRYMPI_FMT_USG, der, ti.length,NULL);
      if (err)
        die ("error scanning RSA parameter %d: %s\n", idx, gpg_strerror (err));
      der += ti.length; derlen -= ti.length;
    }
  if (idx != n_keyparms)
    die ("not enough RSA key parameters\n");

  gcry_free (buffer);

  /* Build the S-expression.  */
  err = gcry_sexp_build (&s_key, NULL,
                         "(public-key(rsa(n%m)(e%m)))",
                         keyparms[0], keyparms[1] );
  if (err)
    die ("error building S-expression: %s\n", gpg_strerror (err));

  for (idx=0; idx < n_keyparms; idx++)
    gcry_mpi_release (keyparms[idx]);

  return s_key;

 bad_asn1:
  die ("invalid ASN.1 structure in `%s'\n", fname);
  return NULL; /*NOTREACHED*/
}



/* Read the file FNAME assuming it is a binary signature result and
   return an an S-expression suitable for gcry_pk_verify.  */
static gcry_sexp_t
read_sig_file (const char *fname)
{
  gcry_error_t err;
  FILE *fp;
  char *buffer;
  size_t buflen;
  gcry_mpi_t tmpmpi;
  gcry_sexp_t s_sig;

  fp = fopen (fname, "rb");
  if (!fp)
    die ("can't open `%s': %s\n", fname, strerror (errno));
  buffer = read_file (fp, 0, &buflen);
  if (!buffer)
    die ("error reading `%s'\n", fname);
  fclose (fp);

  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, buffer, buflen, NULL);
  if (!err)
    err = gcry_sexp_build (&s_sig, NULL,
                           "(sig-val(rsa(s %m)))", tmpmpi);
  if (err)
    die ("error building S-expression: %s\n", gpg_strerror (err));
  gcry_mpi_release (tmpmpi);
  gcry_free (buffer);

  return s_sig;
}


/* Read an S-expression from FNAME.  */
static gcry_sexp_t
read_sexp_from_file (const char *fname)
{
  gcry_error_t err;
  FILE *fp;
  char *buffer;
  size_t buflen;
  gcry_sexp_t sexp;

  fp = fopen (fname, "rb");
  if (!fp)
    die ("can't open `%s': %s\n", fname, strerror (errno));
  buffer = read_file (fp, 0, &buflen);
  if (!buffer)
    die ("error reading `%s'\n", fname);
  fclose (fp);
  if (!buflen)
    die ("error: file `%s' is empty\n", fname);

  err = gcry_sexp_create (&sexp, buffer, buflen, 1, gcry_free);
  if (err)
    die ("error parsing `%s': %s\n", fname, gpg_strerror (err));

  return sexp;
}


static void
print_buffer (const void *buffer, size_t length)
{
  int writerr = 0;

  if (base64_output)
    {
      static const unsigned char bintoasc[64+1] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";
      const unsigned char *p;
      unsigned char inbuf[4];
      char outbuf[4];
      int idx, quads;

      idx = quads = 0;
      for (p = buffer; length; p++, length--)
        {
          inbuf[idx++] = *p;
          if (idx > 2)
            {
              outbuf[0] = bintoasc[(*inbuf>>2)&077];
              outbuf[1] = bintoasc[(((*inbuf<<4)&060)
                                    |((inbuf[1] >> 4)&017))&077];
              outbuf[2] = bintoasc[(((inbuf[1]<<2)&074)
                                    |((inbuf[2]>>6)&03))&077];
              outbuf[3] = bintoasc[inbuf[2]&077];
              if (fwrite (outbuf, 4, 1, stdout) != 1)
                writerr = 1;
              idx = 0;
              if (++quads >= (64/4))
                {
                  if (fwrite ("\n", 1, 1, stdout) != 1)
                    writerr = 1;
                  quads = 0;
                }
            }
        }
      if (idx)
        {
          outbuf[0] = bintoasc[(*inbuf>>2)&077];
          if (idx == 1)
            {
              outbuf[1] = bintoasc[((*inbuf<<4)&060)&077];
              outbuf[2] = outbuf[3] = '=';
            }
          else
            {
              outbuf[1] = bintoasc[(((*inbuf<<4)&060)
                                    |((inbuf[1]>>4)&017))&077];
              outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077];
              outbuf[3] = '=';
            }
          if (fwrite (outbuf, 4, 1, stdout) != 1)
            writerr = 1;
          quads++;
        }
      if (quads && fwrite ("\n", 1, 1, stdout) != 1)
        writerr = 1;
    }
  else if (binary_output)
    {
      if (fwrite (buffer, length, 1, stdout) != 1)
        writerr++;
    }
  else
    {
      const unsigned char *p = buffer;

      if (verbose > 1)
        showhex ("sent line", buffer, length);
      while (length-- && !ferror (stdout) )
        printf ("%02X", *p++);
      if (ferror (stdout))
        writerr++;
    }
  if (!writerr && fflush (stdout) == EOF)
    writerr++;
  if (writerr)
    {
#ifndef HAVE_W32_SYSTEM
      if (loop_mode && errno == EPIPE)
        loop_mode = 0;
      else
#endif
        die ("writing output failed: %s\n", strerror (errno));
    }
}


/* Print an MPI on a line.  */
static void
print_mpi_line (gcry_mpi_t a, int no_lz)
{
  unsigned char *buf, *p;
  gcry_error_t err;
  int writerr = 0;

  err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, a);
  if (err)
    die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err));

  p = buf;
  if (no_lz && p[0] == '0' && p[1] == '0' && p[2])
    p += 2;

  printf ("%s\n", p);
  if (ferror (stdout))
    writerr++;
  if (!writerr && fflush (stdout) == EOF)
    writerr++;
  if (writerr)
    die ("writing output failed: %s\n", strerror (errno));
  gcry_free (buf);
}


/* Print some data on hex format on a line.  */
static void
print_data_line (const void *data, size_t datalen)
{
  const unsigned char *p = data;
  int writerr = 0;

  while (data && datalen-- && !ferror (stdout) )
    printf ("%02X", *p++);
  putchar ('\n');
  if (ferror (stdout))
    writerr++;
  if (!writerr && fflush (stdout) == EOF)
    writerr++;
  if (writerr)
    die ("writing output failed: %s\n", strerror (errno));
}

/* Print the S-expression A to the stream FP.  */
static void
print_sexp (gcry_sexp_t a, FILE *fp)
{
  char *buf;
  size_t size;

  size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
  buf = gcry_xmalloc (size);
  gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
  if (fwrite (buf, size, 1, fp) != 1)
    die ("error writing to stream: %s\n", strerror (errno));
  gcry_free (buf);
}




static gcry_error_t
init_external_rng_test (void **r_context,
                    unsigned int flags,
                    const void *key, size_t keylen,
                    const void *seed, size_t seedlen,
                    const void *dt, size_t dtlen)
{
  return gcry_control (PRIV_CTL_INIT_EXTRNG_TEST,
                       r_context, flags,
                       key, keylen,
                       seed, seedlen,
                       dt, dtlen);
}

static gcry_error_t
run_external_rng_test (void *context, void *buffer, size_t buflen)
{
  return gcry_control (PRIV_CTL_RUN_EXTRNG_TEST, context, buffer, buflen);
}

static void
deinit_external_rng_test (void *context)
{
  xgcry_control ((PRIV_CTL_DEINIT_EXTRNG_TEST, context));
}


/* Given an OpenSSL cipher name NAME, return the Libgcrypt algirithm
   identified and store the libgcrypt mode at R_MODE.  Returns 0 on
   error.  */
static int
map_openssl_cipher_name (const char *name, int *r_mode)
{
  static struct {
    const char *name;
    int algo;
    int mode;
  } table[] =
    {
      { "bf-cbc",       GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC },
      { "bf",           GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC },
      { "bf-cfb",       GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB },
      { "bf-ecb",       GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB },
      { "bf-ofb",       GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB },

      { "cast-cbc",     GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC },
      { "cast",         GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC },
      { "cast5-cbc",    GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC },
      { "cast5-cfb",    GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB },
      { "cast5-ecb",    GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_ECB },
      { "cast5-ofb",    GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_OFB },

      { "des-cbc",      GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC },
      { "des",          GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC },
      { "des-cfb",      GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB },
      { "des-ofb",      GCRY_CIPHER_DES, GCRY_CIPHER_MODE_OFB },
      { "des-ecb",      GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB },

      { "des-ede3-cbc", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC },
      { "des-ede3",     GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB },
      { "des3",         GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC },
      { "des-ede3-cfb", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB },
      { "des-ede3-ofb", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_OFB },

      { "rc4",          GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM },

      { "aes-128-cbc",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC },
      { "aes-128",      GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC },
      { "aes-128-cfb",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB },
      { "aes-128-ecb",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB },
      { "aes-128-ofb",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB },

      { "aes-192-cbc",  GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC },
      { "aes-192",      GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC },
      { "aes-192-cfb",  GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB },
      { "aes-192-ecb",  GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB },
      { "aes-192-ofb",  GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB },

      { "aes-256-cbc",  GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC },
      { "aes-256",      GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC },
      { "aes-256-cfb",  GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB },
      { "aes-256-ecb",  GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB },
      { "aes-256-ofb",  GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB },

      { NULL, 0 , 0 }
    };
  int idx;

  for (idx=0; table[idx].name; idx++)
    if (!strcmp (name, table[idx].name))
      {
        *r_mode = table[idx].mode;
        return table[idx].algo;
      }
  *r_mode = 0;
  return 0;
}



/* Run an encrypt or decryption operations.  If DATA is NULL the
   function reads its input in chunks of size DATALEN from fp and
   processes it and writes it out until EOF.  */
static void
run_encrypt_decrypt (int encrypt_mode,
                     int cipher_algo, int cipher_mode,
                     const void *iv_buffer, size_t iv_buflen,
                     const void *key_buffer, size_t key_buflen,
                     const void *data, size_t datalen, FILE *fp)
{
  gpg_error_t err;
  gcry_cipher_hd_t hd;
  void *outbuf;
  size_t outbuflen;
  void *inbuf;
  size_t inbuflen;
  size_t blocklen;

  err = gcry_cipher_open (&hd, cipher_algo, cipher_mode, 0);
  if (err)
    die ("gcry_cipher_open failed for algo %d, mode %d: %s\n",
         cipher_algo, cipher_mode, gpg_strerror (err));

  blocklen = gcry_cipher_get_algo_blklen (cipher_algo);
  assert (blocklen);

  gcry_cipher_ctl (hd, PRIV_CIPHERCTL_DISABLE_WEAK_KEY, NULL, 0);

  err = gcry_cipher_setkey (hd, key_buffer, key_buflen);
  if (err)
    die ("gcry_cipher_setkey failed with keylen %u: %s\n",
         (unsigned int)key_buflen, gpg_strerror (err));

  if (iv_buffer)
    {
      err = gcry_cipher_setiv (hd, iv_buffer, iv_buflen);
      if (err)
        die ("gcry_cipher_setiv failed with ivlen %u: %s\n",
             (unsigned int)iv_buflen, gpg_strerror (err));
    }

  inbuf = data? NULL : gcry_xmalloc (datalen);
  outbuflen = datalen;
  outbuf = gcry_xmalloc (outbuflen < blocklen? blocklen:outbuflen);

  do
    {
      if (inbuf)
        {
          int nread = fread (inbuf, 1, datalen, fp);
          if (nread < (int)datalen && ferror (fp))
            die ("error reading input\n");
          data = inbuf;
          inbuflen = nread;
        }
      else
        inbuflen = datalen;

      if (encrypt_mode)
        err = gcry_cipher_encrypt (hd, outbuf, outbuflen, data, inbuflen);
      else
        err = gcry_cipher_decrypt (hd, outbuf, outbuflen, data, inbuflen);
      if (err)
        die ("gcry_cipher_%scrypt failed: %s\n",
             encrypt_mode? "en":"de", gpg_strerror (err));

      print_buffer (outbuf, outbuflen);
    }
  while (inbuf);

  gcry_cipher_close (hd);
  gcry_free (outbuf);
  gcry_free (inbuf);
}


static void
get_current_iv (gcry_cipher_hd_t hd, void *buffer, size_t buflen)
{
  unsigned char tmp[17];

  if (gcry_cipher_ctl (hd, PRIV_CIPHERCTL_GET_INPUT_VECTOR, tmp, sizeof tmp))
    die ("error getting current input vector\n");
  if (buflen > *tmp)
    die ("buffer too short to store the current input vector\n");
  memcpy (buffer, tmp+1, *tmp);
}

/* Run the inner loop of the CAVS monte carlo test.  */
static void
run_cipher_mct_loop (int encrypt_mode, int cipher_algo, int cipher_mode,
                     const void *iv_buffer, size_t iv_buflen,
                     const void *key_buffer, size_t key_buflen,
                     const void *data, size_t datalen, int iterations)
{
  gpg_error_t err;
  gcry_cipher_hd_t hd;
  size_t blocklen;
  int count;
  char input[16];
  char output[16];
  char last_output[16];
  char last_last_output[16];
  char last_iv[16];


  err = gcry_cipher_open (&hd, cipher_algo, cipher_mode, 0);
  if (err)
    die ("gcry_cipher_open failed for algo %d, mode %d: %s\n",
         cipher_algo, cipher_mode, gpg_strerror (err));

  blocklen = gcry_cipher_get_algo_blklen (cipher_algo);
  if (!blocklen || blocklen > sizeof output)
    die ("invalid block length %d\n", (int)blocklen);


  gcry_cipher_ctl (hd, PRIV_CIPHERCTL_DISABLE_WEAK_KEY, NULL, 0);

  err = gcry_cipher_setkey (hd, key_buffer, key_buflen);
  if (err)
    die ("gcry_cipher_setkey failed with keylen %u: %s\n",
         (unsigned int)key_buflen, gpg_strerror (err));

  if (iv_buffer)
    {
      err = gcry_cipher_setiv (hd, iv_buffer, iv_buflen);
      if (err)
        die ("gcry_cipher_setiv failed with ivlen %u: %s\n",
             (unsigned int)iv_buflen, gpg_strerror (err));
    }

  if (datalen != blocklen)
    die ("length of input (%u) does not match block length (%u)\n",
         (unsigned int)datalen, (unsigned int)blocklen);
  memcpy (input, data, datalen);
  memset (output, 0, sizeof output);
  for (count=0; count < iterations; count++)
    {
      memcpy (last_last_output, last_output, sizeof last_output);
      memcpy (last_output, output, sizeof output);

      get_current_iv (hd, last_iv, blocklen);

      if (encrypt_mode)
        err = gcry_cipher_encrypt (hd, output, blocklen, input, blocklen);
      else
        err = gcry_cipher_decrypt (hd, output, blocklen, input, blocklen);
      if (err)
        die ("gcry_cipher_%scrypt failed: %s\n",
             encrypt_mode? "en":"de", gpg_strerror (err));


      if (encrypt_mode && (cipher_mode == GCRY_CIPHER_MODE_CFB
                           || cipher_mode == GCRY_CIPHER_MODE_CBC))
        memcpy (input, last_iv, blocklen);
      else if (cipher_mode == GCRY_CIPHER_MODE_OFB)
        memcpy (input, last_iv, blocklen);
      else if (!encrypt_mode && cipher_mode == GCRY_CIPHER_MODE_CFB)
        {
          /* Reconstruct the output vector.  */
          int i;
          for (i=0; i < blocklen; i++)
            input[i] ^= output[i];
        }
      else
        memcpy (input, output, blocklen);
    }

  print_buffer (output, blocklen);
  putchar ('\n');
  print_buffer (last_output, blocklen);
  putchar ('\n');
  print_buffer (last_last_output, blocklen);
  putchar ('\n');
  get_current_iv (hd, last_iv, blocklen);
  print_buffer (last_iv, blocklen); /* Last output vector.  */
  putchar ('\n');
  print_buffer (input, blocklen);   /* Next input text. */
  putchar ('\n');
  if (verbose > 1)
    showhex ("sent line", "", 0);
  putchar ('\n');
  fflush (stdout);

  gcry_cipher_close (hd);
}



/* Run a digest operation.  */
static void
run_digest (int digest_algo,  const void *data, size_t datalen)
{
  gpg_error_t err;
  gcry_md_hd_t hd;
  const unsigned char *digest;
  unsigned int digestlen;

  err = gcry_md_open (&hd, digest_algo, 0);
  if (err)
    die ("gcry_md_open failed for algo %d: %s\n",
         digest_algo,  gpg_strerror (err));

  gcry_md_write (hd, data, datalen);
  digest = gcry_md_read (hd, digest_algo);
  digestlen = gcry_md_get_algo_dlen (digest_algo);
  print_buffer (digest, digestlen);
  gcry_md_close (hd);
}


/* Run a HMAC operation.  */
static void
run_hmac (int digest_algo, const void *key, size_t keylen,
          const void *data, size_t datalen)
{
  gpg_error_t err;
  gcry_md_hd_t hd;
  const unsigned char *digest;
  unsigned int digestlen;

  err = gcry_md_open (&hd, digest_algo, GCRY_MD_FLAG_HMAC);
  if (err)
    die ("gcry_md_open failed for HMAC algo %d: %s\n",
         digest_algo,  gpg_strerror (err));

  gcry_md_setkey (hd, key, keylen);
  if (err)
    die ("gcry_md_setkey failed for HMAC algo %d: %s\n",
         digest_algo,  gpg_strerror (err));

  gcry_md_write (hd, data, datalen);
  digest = gcry_md_read (hd, digest_algo);
  digestlen = gcry_md_get_algo_dlen (digest_algo);
  print_buffer (digest, digestlen);
  gcry_md_close (hd);
}



/* Derive an RSA key using the S-expression in (DATA,DATALEN).  This
   S-expression is used directly as input to gcry_pk_genkey.  The
   result is printed to stdout with one parameter per line in hex
   format and in this order: p, q, n, d.  */
static void
run_rsa_derive (const void *data, size_t datalen)
{
  gpg_error_t err;
  gcry_sexp_t s_keyspec, s_key, s_top, l1;
  gcry_mpi_t mpi;
  const char *parmlist;
  int idx;

  if (!datalen)
    err = gpg_error (GPG_ERR_NO_DATA);
  else
    err = gcry_sexp_new (&s_keyspec, data, datalen, 1);
  if (err)
    die ("gcry_sexp_new failed for RSA key derive: %s\n",
         gpg_strerror (err));

  err = gcry_pk_genkey (&s_key, s_keyspec);
  if (err)
    die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err));

  gcry_sexp_release (s_keyspec);

  /* P and Q might have been swapped but we need to to return them in
     the proper order.  Build the parameter list accordingly.  */
  parmlist = "pqnd";
  s_top = gcry_sexp_find_token (s_key, "misc-key-info", 0);
  if (s_top)
    {
      l1 = gcry_sexp_find_token (s_top, "p-q-swapped", 0);
      if (l1)
        parmlist = "qpnd";
      gcry_sexp_release (l1);
      gcry_sexp_release (s_top);
    }

  /* Parse and print the parameters.  */
  l1 = gcry_sexp_find_token (s_key, "private-key", 0);
  s_top = gcry_sexp_find_token (l1, "rsa", 0);
  gcry_sexp_release (l1);
  if (!s_top)
    die ("private-key part not found in result\n");

  for (idx=0; parmlist[idx]; idx++)
    {
      l1 = gcry_sexp_find_token (s_top, parmlist+idx, 1);
      mpi = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
      gcry_sexp_release (l1);
      if (!mpi)
        die ("parameter %c missing in private-key\n", parmlist[idx]);
      print_mpi_line (mpi, 1);
      gcry_mpi_release (mpi);
    }

  gcry_sexp_release (s_top);
  gcry_sexp_release (s_key);
}


/* Generate RSA key using the S-expression in (DATA,DATALEN).  This
   S-expression is used directly as input to gcry_pk_genkey.  The
   result is printed to stdout with one parameter per line in hex
   format and in this order: e, p, q, n, d.  */
static void
run_rsa_keygen (const void *data, size_t datalen, int test)
{
  gpg_error_t err;
  gcry_sexp_t s_keyspec, s_key, s_top, l1;
  gcry_mpi_t mpi;
  const char *parmlist;
  int idx;

  if (!datalen)
    err = gpg_error (GPG_ERR_NO_DATA);
  else
    err = gcry_sexp_new (&s_keyspec, data, datalen, 1);
  if (err)
    die ("gcry_sexp_new failed for RSA key generation: %s\n",
         gpg_strerror (err));

  err = gcry_pk_genkey (&s_key, s_keyspec);

  gcry_sexp_release (s_keyspec);

  if (test) {
	if (err)
		printf("F\n");
	else {
		gcry_sexp_release (s_key);
		printf("P\n");
	}
	return;
  }

  if (err)
    die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err));

  parmlist = "epqnd";

  /* Parse and print the parameters.  */
  l1 = gcry_sexp_find_token (s_key, "private-key", 0);
  s_top = gcry_sexp_find_token (l1, "rsa", 0);
  gcry_sexp_release (l1);
  if (!s_top)
    die ("private-key part not found in result\n");

  for (idx=0; parmlist[idx]; idx++)
    {
      l1 = gcry_sexp_find_token (s_top, parmlist+idx, 1);
      mpi = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
      gcry_sexp_release (l1);
      if (!mpi)
        die ("parameter %c missing in private-key\n", parmlist[idx]);
      print_mpi_line (mpi, 1);
      gcry_mpi_release (mpi);
    }

  gcry_sexp_release (s_top);
  gcry_sexp_release (s_key);
}



static size_t
compute_tag_length (size_t n)
{
  int needed = 0;

  if (n < 128)
    needed += 2; /* Tag and one length byte.  */
  else if (n < 256)
    needed += 3; /* Tag, number of length bytes, 1 length byte.  */
  else if (n < 65536)
    needed += 4; /* Tag, number of length bytes, 2 length bytes.  */
  else
    die ("DER object too long to encode\n");

  return needed;
}

static unsigned char *
store_tag_length (unsigned char *p, int tag, size_t n)
{
  if (tag == TAG_SEQUENCE)
    tag |= 0x20; /* constructed */

  *p++ = tag;
  if (n < 128)
    *p++ = n;
  else if (n < 256)
    {
      *p++ = 0x81;
      *p++ = n;
    }
  else if (n < 65536)
    {
      *p++ = 0x82;
      *p++ = n >> 8;
      *p++ = n;
    }

  return p;
}


/* Generate an RSA key of size KEYSIZE using the public exponent
   PUBEXP and print it to stdout in the OpenSSL format.  The format
   is:

       SEQUENCE {
         INTEGER (0)  -- Unknown constant.
         INTEGER      -- n
         INTEGER      -- e
         INTEGER      -- d
         INTEGER      -- p
         INTEGER      -- q      (with p < q)
         INTEGER      -- dmp1 = d mod (p-1)
         INTEGER      -- dmq1 = d mod (q-1)
         INTEGER      -- u    = p^{-1} mod q
       }

*/
static void
run_rsa_gen (int keysize, int pubexp)
{
  gpg_error_t err;
  gcry_sexp_t keyspec, key, l1;
  const char keyelems[] = "nedpq..u";
  gcry_mpi_t keyparms[8];
  size_t     keyparmslen[8];
  int idx;
  size_t derlen, needed, n;
  unsigned char *derbuf, *der;

  err = gcry_sexp_build (&keyspec, NULL,
                         "(genkey (rsa (nbits %d)(rsa-use-e %d)))",
                         keysize, pubexp);
  if (err)
    die ("gcry_sexp_build failed for RSA key generation: %s\n",
         gpg_strerror (err));

  err = gcry_pk_genkey (&key, keyspec);
  if (err)
    die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err));

  gcry_sexp_release (keyspec);

  l1 = gcry_sexp_find_token (key, "private-key", 0);
  if (!l1)
    die ("private key not found in genkey result\n");
  gcry_sexp_release (key);
  key = l1;

  l1 = gcry_sexp_find_token (key, "rsa", 0);
  if (!l1)
    die ("returned private key not formed as expected\n");
  gcry_sexp_release (key);
  key = l1;

  /* Extract the parameters from the S-expression and store them in a
     well defined order in KEYPARMS.  */
  for (idx=0; idx < DIM(keyparms); idx++)
    {
      if (keyelems[idx] == '.')
        {
          keyparms[idx] = gcry_mpi_new (0);
          continue;
        }
      l1 = gcry_sexp_find_token (key, keyelems+idx, 1);
      if (!l1)
        die ("no %c parameter in returned private key\n", keyelems[idx]);
      keyparms[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
      if (!keyparms[idx])
        die ("no value for %c parameter in returned private key\n",
             keyelems[idx]);
      gcry_sexp_release (l1);
    }

  gcry_sexp_release (key);

  /* Check that p < q; if not swap p and q and recompute u.  */
  if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0)
    {
      gcry_mpi_swap (keyparms[3], keyparms[4]);
      gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]);
    }

  /* Compute the additional parameters.  */
  gcry_mpi_sub_ui (keyparms[5], keyparms[3], 1);
  gcry_mpi_mod (keyparms[5], keyparms[2], keyparms[5]);
  gcry_mpi_sub_ui (keyparms[6], keyparms[4], 1);
  gcry_mpi_mod (keyparms[6], keyparms[2], keyparms[6]);

  /* Compute the length of the DER encoding.  */
  needed = compute_tag_length (1) + 1;
  for (idx=0; idx < DIM(keyparms); idx++)
    {
      err = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, keyparms[idx]);
      if (err)
        die ("error formatting parameter: %s\n", gpg_strerror (err));
      keyparmslen[idx] = n;
      needed += compute_tag_length (n) + n;
    }

  /* Store the key parameters. */
  derlen = compute_tag_length (needed) + needed;
  der = derbuf = gcry_xmalloc (derlen);

  der = store_tag_length (der, TAG_SEQUENCE, needed);
  der = store_tag_length (der, TAG_INTEGER, 1);
  *der++ = 0;
  for (idx=0; idx < DIM(keyparms); idx++)
    {
      der = store_tag_length (der, TAG_INTEGER, keyparmslen[idx]);
      err = gcry_mpi_print (GCRYMPI_FMT_STD, der,
                           keyparmslen[idx], NULL, keyparms[idx]);
      if (err)
        die ("error formatting parameter: %s\n", gpg_strerror (err));
      der += keyparmslen[idx];
    }

  /* Print the stuff.  */
  for (idx=0; idx < DIM(keyparms); idx++)
    gcry_mpi_release (keyparms[idx]);

  assert (der - derbuf == derlen);

  if (base64_output)
    puts ("-----BEGIN RSA PRIVATE KEY-----");
  print_buffer (derbuf, derlen);
  if (base64_output)
    puts ("-----END RSA PRIVATE KEY-----");

  gcry_free (derbuf);
}



/* Sign DATA of length DATALEN using the key taken from the PEM
   encoded KEYFILE and the hash algorithm HASHALGO.  */
static void
run_rsa_sign (const void *data, size_t datalen,
              int hashalgo, int pkcs1, int pss, const char *keyfile)

{
  gpg_error_t err;
  gcry_sexp_t s_data, s_key, s_sig, s_tmp;
  gcry_mpi_t sig_mpi = NULL;
  unsigned char *outbuf;
  size_t outlen;

/*   showhex ("D", data, datalen); */
  if (pkcs1)
    {
      unsigned char hash[64];
      unsigned int hashsize;

      hashsize = gcry_md_get_algo_dlen (hashalgo);
      if (!hashsize || hashsize > sizeof hash)
        die ("digest too long for buffer or unknown hash algorithm\n");
      gcry_md_hash_buffer (hashalgo, hash, data, datalen);
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags pkcs1)(hash %s %b))",
                             gcry_md_algo_name (hashalgo),
                             (int)hashsize, hash);
    }
  else if (pss)
    {
      unsigned char hash[64];
      unsigned int hashsize;

      hashsize = gcry_md_get_algo_dlen (hashalgo);
      if (!hashsize || hashsize > sizeof hash)
        die ("digest too long for buffer or unknown hash algorithm\n");
      gcry_md_hash_buffer (hashalgo, hash, data, datalen);
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags pss)(salt-length #00#)(hash %s %b))",
                             gcry_md_algo_name (hashalgo),
                             (int)hashsize, hash);
    }
  else
    {
      gcry_mpi_t tmp;

      err = gcry_mpi_scan (&tmp, GCRYMPI_FMT_USG, data, datalen,NULL);
      if (!err)
        {
          err = gcry_sexp_build (&s_data, NULL,
                                 "(data (flags raw)(value %m))", tmp);
          gcry_mpi_release (tmp);
        }
    }
  if (err)
    die ("gcry_sexp_build failed for RSA data input: %s\n",
         gpg_strerror (err));

  s_key = read_private_key_file (keyfile, 0);

  err = gcry_pk_sign (&s_sig, s_data, s_key);
  if (err)
    {
      gcry_sexp_release (read_private_key_file (keyfile, 1));
      die ("gcry_pk_signed failed (datalen=%d,keyfile=%s): %s\n",
           (int)datalen, keyfile, gpg_strerror (err));
    }
  gcry_sexp_release (s_key);
  gcry_sexp_release (s_data);

  s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
  if (s_tmp)
    {
      gcry_sexp_release (s_sig);
      s_sig = s_tmp;
      s_tmp = gcry_sexp_find_token (s_sig, "rsa", 0);
      if (s_tmp)
        {
          gcry_sexp_release (s_sig);
          s_sig = s_tmp;
          s_tmp = gcry_sexp_find_token (s_sig, "s", 0);
          if (s_tmp)
            {
              gcry_sexp_release (s_sig);
              s_sig = s_tmp;
              sig_mpi = gcry_sexp_nth_mpi (s_sig, 1, GCRYMPI_FMT_USG);
            }
        }
    }
  gcry_sexp_release (s_sig);

  if (!sig_mpi)
    die ("no value in returned S-expression\n");
  err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &outbuf, &outlen, sig_mpi);
  if (err)
    die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err));
  gcry_mpi_release (sig_mpi);

  print_buffer (outbuf, outlen);
  gcry_free (outbuf);
}



/* Verify DATA of length DATALEN using the public key taken from the
   PEM encoded KEYFILE and the hash algorithm HASHALGO against the
   binary signature in SIGFILE.  */
static void
run_rsa_verify (const void *data, size_t datalen, int hashalgo, int pkcs1,
                int pss, const char *keyfile, const char *sigfile)

{
  gpg_error_t err;
  gcry_sexp_t s_data, s_key, s_sig;

  if (pkcs1)
    {
      unsigned char hash[64];
      unsigned int hashsize;

      hashsize = gcry_md_get_algo_dlen (hashalgo);
      if (!hashsize || hashsize > sizeof hash)
        die ("digest too long for buffer or unknown hash algorithm\n");
      gcry_md_hash_buffer (hashalgo, hash, data, datalen);
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags pkcs1)(hash %s %b))",
                             gcry_md_algo_name (hashalgo),
                             (int)hashsize, hash);
    }
  else if (pss)
    {
      unsigned char hash[64];
      unsigned int hashsize;

      hashsize = gcry_md_get_algo_dlen (hashalgo);
      if (!hashsize || hashsize > sizeof hash)
        die ("digest too long for buffer or unknown hash algorithm\n");
      gcry_md_hash_buffer (hashalgo, hash, data, datalen);
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags pss)(salt-length #00#)(hash %s %b))",
                             gcry_md_algo_name (hashalgo),
                             (int)hashsize, hash);
    }
  else
    {
      gcry_mpi_t tmp;

      err = gcry_mpi_scan (&tmp, GCRYMPI_FMT_USG, data, datalen,NULL);
      if (!err)
        {
          err = gcry_sexp_build (&s_data, NULL,
                                 "(data (flags raw)(value %m))", tmp);
          gcry_mpi_release (tmp);
        }
    }
  if (err)
    die ("gcry_sexp_build failed for RSA data input: %s\n",
         gpg_strerror (err));

  s_key = read_public_key_file (keyfile, 0);

  s_sig = read_sig_file (sigfile);

  err = gcry_pk_verify (s_sig, s_data, s_key);
  if (!err)
    puts ("GOOD signature");
  else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE)
    puts ("BAD signature");
  else
    printf ("ERROR (%s)\n", gpg_strerror (err));

  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_key);
  gcry_sexp_release (s_data);
}



/* Generate a DSA key of size KEYSIZE and return the complete
   S-expression.  */
static gcry_sexp_t
dsa_gen (int keysize)
{
  gpg_error_t err;
  gcry_sexp_t keyspec, key;

  err = gcry_sexp_build (&keyspec, NULL,
                         "(genkey (dsa (nbits %d)(use-fips186-2)))",
                         keysize);
  if (err)
    die ("gcry_sexp_build failed for DSA key generation: %s\n",
         gpg_strerror (err));

  err = gcry_pk_genkey (&key, keyspec);
  if (err)
    die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err));

  gcry_sexp_release (keyspec);

  return key;
}


/* Generate a DSA key of size KEYSIZE and return the complete
   S-expression.  */
static gcry_sexp_t
dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen)
{
  gpg_error_t err;
  gcry_sexp_t keyspec, key;

  err = gcry_sexp_build (&keyspec, NULL,
                         "(genkey"
                         "  (dsa"
                         "    (nbits %d)"
                         "    (use-fips186-2)"
                         "    (derive-parms"
                         "      (seed %b))))",
                         keysize, (int)seedlen, seed);
  if (err)
    die ("gcry_sexp_build failed for DSA key generation: %s\n",
         gpg_strerror (err));

  err = gcry_pk_genkey (&key, keyspec);
  if (err)
    die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err));

  gcry_sexp_release (keyspec);

  return key;
}


/* Generate an ECDSA key on the specified curve and return the complete
   S-expression. */
static gcry_sexp_t
ecdsa_gen_key (const char *curve)
{
  gpg_error_t err;
  gcry_sexp_t keyspec, key;

  err = gcry_sexp_build (&keyspec, NULL,
                         "(genkey"
                         "  (ecc"
                         "    (use-fips186)"
                         "    (curve %s)))",
                         curve);
  if (err)
    die ("gcry_sexp_build failed for ECDSA key generation: %s\n",
         gpg_strerror (err));
  err = gcry_pk_genkey (&key, keyspec);
  if (err)
    die ("gcry_pk_genkey failed for ECDSA: %s\n", gpg_strerror (err));

  gcry_sexp_release (keyspec);

  return key;
}


/* Print the domain parameter as well as the derive information.  KEY
   is the complete key as returned by dsa_gen.  We print to stdout
   with one parameter per line in hex format using this order: p, q,
   g, seed, counter, h. */
static void
print_dsa_domain_parameters (gcry_sexp_t key)
{
  gcry_sexp_t l1, l2;
  gcry_mpi_t mpi;
  int idx;
  const void *data;
  size_t datalen;
  char *string;

  l1 = gcry_sexp_find_token (key, "public-key", 0);
  if (!l1)
    die ("public key not found in genkey result\n");

  l2 = gcry_sexp_find_token (l1, "dsa", 0);
  if (!l2)
    die ("returned public key not formed as expected\n");
  gcry_sexp_release (l1);
  l1 = l2;

  /* Extract the parameters from the S-expression and print them to stdout.  */
  for (idx=0; "pqg"[idx]; idx++)
    {
      l2 = gcry_sexp_find_token (l1, &"pqg"[idx], 1);
      if (!l2)
        die ("no %c parameter in returned public key\n", "pqg"[idx]);
      mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
      if (!mpi)
        die ("no value for %c parameter in returned public key\n","pqg"[idx]);
      gcry_sexp_release (l2);
      if (standalone_mode)
        printf ("%c = ", "PQG"[idx]);
      print_mpi_line (mpi, 1);
      gcry_mpi_release (mpi);
    }
  gcry_sexp_release (l1);

  /* Extract the seed values.  */
  l1 = gcry_sexp_find_token (key, "misc-key-info", 0);
  if (!l1)
    die ("misc-key-info not found in genkey result\n");

  l2 = gcry_sexp_find_token (l1, "seed-values", 0);
  if (!l2)
    die ("no seed-values in returned key\n");
  gcry_sexp_release (l1);
  l1 = l2;

  l2 = gcry_sexp_find_token (l1, "seed", 0);
  if (!l2)
    die ("no seed value in returned key\n");
  data = gcry_sexp_nth_data (l2, 1, &datalen);
  if (!data)
    die ("no seed value in returned key\n");
  if (standalone_mode)
    printf ("Seed = ");
  print_data_line (data, datalen);
  gcry_sexp_release (l2);

  l2 = gcry_sexp_find_token (l1, "counter", 0);
  if (!l2)
    die ("no counter value in returned key\n");
  string = gcry_sexp_nth_string (l2, 1);
  if (!string)
    die ("no counter value in returned key\n");
  if (standalone_mode)
    printf ("c = %ld\n", strtoul (string, NULL, 10));
  else
    printf ("%lX\n", strtoul (string, NULL, 10));
  gcry_free (string);
  gcry_sexp_release (l2);

  l2 = gcry_sexp_find_token (l1, "h", 0);
  if (!l2)
    die ("no n value in returned key\n");
  mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
  if (!mpi)
    die ("no h value in returned key\n");
  if (standalone_mode)
    printf ("H = ");
  print_mpi_line (mpi, 1);
  gcry_mpi_release (mpi);
  gcry_sexp_release (l2);

  gcry_sexp_release (l1);
}


/* Print public key Q (in octet-string format) and private key d.
   KEY is the complete key as returned by ecdsa_gen_key.
   with one parameter per line in hex format using this order: d, Q. */
static void
print_ecdsa_dq (gcry_sexp_t key)
{
  gcry_sexp_t l1, l2;
  gcry_mpi_t mpi;
  int idx;

  l1 = gcry_sexp_find_token (key, "private-key", 0);
  if (!l1)
    die ("private key not found in genkey result\n");

  l2 = gcry_sexp_find_token (l1, "ecc", 0);
  if (!l2)
    die ("returned private key not formed as expected\n");
  gcry_sexp_release (l1);
  l1 = l2;

  /* Extract the parameters from the S-expression and print them to stdout.  */
  for (idx=0; "dq"[idx]; idx++)
    {
      l2 = gcry_sexp_find_token (l1, &"dq"[idx], 1);
      if (!l2)
        die ("no %c parameter in returned public key\n", "dq"[idx]);
      mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
      if (!mpi)
        die ("no value for %c parameter in returned private key\n","dq"[idx]);
      gcry_sexp_release (l2);
      if (standalone_mode)
        printf ("%c = ", "dQ"[idx]);
      print_mpi_line (mpi, 1);
      gcry_mpi_release (mpi);
    }

  gcry_sexp_release (l1);
}


/* Generate DSA domain parameters for a modulus size of KEYSIZE.  The
   result is printed to stdout with one parameter per line in hex
   format and in this order: p, q, g, seed, counter, h.  If SEED is
   not NULL this seed value will be used for the generation.  */
static void
run_dsa_pqg_gen (int keysize, const void *seed, size_t seedlen)
{
  gcry_sexp_t key;

  if (seed)
    key = dsa_gen_with_seed (keysize, seed, seedlen);
  else
    key = dsa_gen (keysize);
  print_dsa_domain_parameters (key);
  gcry_sexp_release (key);
}


/* Generate a DSA key of size of KEYSIZE and write the private key to
   FILENAME.  Also write the parameters to stdout in the same way as
   run_dsa_pqg_gen.  */
static void
run_dsa_gen (int keysize, const char *filename)
{
  gcry_sexp_t key, private_key;
  FILE *fp;

  key = dsa_gen (keysize);
  private_key = gcry_sexp_find_token (key, "private-key", 0);
  if (!private_key)
    die ("private key not found in genkey result\n");
  print_dsa_domain_parameters (key);

  fp = fopen (filename, "wb");
  if (!fp)
    die ("can't create `%s': %s\n", filename, strerror (errno));
  print_sexp (private_key, fp);
  fclose (fp);

  gcry_sexp_release (private_key);
  gcry_sexp_release (key);
}



/* Sign DATA of length DATALEN using the key taken from the S-expression
   encoded KEYFILE. */
static void
run_dsa_sign (const void *data, size_t datalen, const char *keyfile)

{
  gpg_error_t err;
  gcry_sexp_t s_data, s_key, s_sig, s_tmp, s_tmp2;
  char hash[20];
  gcry_mpi_t tmpmpi;

  gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen);
  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL);
  if (!err)
    {
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags raw)(value %m))", tmpmpi);
      gcry_mpi_release (tmpmpi);
    }
  if (err)
    die ("gcry_sexp_build failed for DSA data input: %s\n",
         gpg_strerror (err));

  s_key = read_sexp_from_file (keyfile);

  err = gcry_pk_sign (&s_sig, s_data, s_key);
  if (err)
    {
      gcry_sexp_release (read_private_key_file (keyfile, 1));
      die ("gcry_pk_signed failed (datalen=%d,keyfile=%s): %s\n",
           (int)datalen, keyfile, gpg_strerror (err));
    }
  gcry_sexp_release (s_data);

  /* We need to return the Y parameter first.  */
  s_tmp = gcry_sexp_find_token (s_key, "private-key", 0);
  if (!s_tmp)
    die ("private key part not found in provided key\n");

  s_tmp2 = gcry_sexp_find_token (s_tmp, "dsa", 0);
  if (!s_tmp2)
    die ("private key part is not a DSA key\n");
  gcry_sexp_release (s_tmp);

  s_tmp = gcry_sexp_find_token (s_tmp2, "y", 0);
  tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
  if (!tmpmpi)
    die ("no y parameter in DSA key\n");
  print_mpi_line (tmpmpi, 1);
  gcry_mpi_release (tmpmpi);
  gcry_sexp_release (s_tmp);

  gcry_sexp_release (s_key);


  /* Now return the actual signature.  */
  s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
  if (!s_tmp)
    die ("no sig-val element in returned S-expression\n");

  gcry_sexp_release (s_sig);
  s_sig = s_tmp;
  s_tmp = gcry_sexp_find_token (s_sig, "dsa", 0);
  if (!s_tmp)
    die ("no dsa element in returned S-expression\n");

  gcry_sexp_release (s_sig);
  s_sig = s_tmp;

  s_tmp = gcry_sexp_find_token (s_sig, "r", 0);
  tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
  if (!tmpmpi)
    die ("no r parameter in returned S-expression\n");
  print_mpi_line (tmpmpi, 1);
  gcry_mpi_release (tmpmpi);
  gcry_sexp_release (s_tmp);

  s_tmp = gcry_sexp_find_token (s_sig, "s", 0);
  tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
  if (!tmpmpi)
    die ("no s parameter in returned S-expression\n");
  print_mpi_line (tmpmpi, 1);
  gcry_mpi_release (tmpmpi);
  gcry_sexp_release (s_tmp);

  gcry_sexp_release (s_sig);
}



/* Verify DATA of length DATALEN using the public key taken from the
   S-expression in KEYFILE against the S-expression formatted
   signature in SIGFILE.  */
static void
run_dsa_verify (const void *data, size_t datalen,
                const char *keyfile, const char *sigfile)

{
  gpg_error_t err;
  gcry_sexp_t s_data, s_key, s_sig;
  char hash[20];
  gcry_mpi_t tmpmpi;

  gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen);
  /* Note that we can't simply use %b with HASH to build the
     S-expression, because that might yield a negative value.  */
  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL);
  if (!err)
    {
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags raw)(value %m))", tmpmpi);
      gcry_mpi_release (tmpmpi);
    }
  if (err)
    die ("gcry_sexp_build failed for DSA data input: %s\n",
         gpg_strerror (err));

  s_key = read_sexp_from_file (keyfile);
  s_sig = read_sexp_from_file (sigfile);

  err = gcry_pk_verify (s_sig, s_data, s_key);
  if (!err)
    puts ("GOOD signature");
  else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE)
    puts ("BAD signature");
  else
    printf ("ERROR (%s)\n", gpg_strerror (err));

  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_key);
  gcry_sexp_release (s_data);
}



/* Sign DATA of length DATALEN using the key taken from the S-expression
   encoded KEYFILE. */
static void
run_ecdsa_sign (const void *data, size_t datalen,
                const char *keyfile, const int algo)

{
  gpg_error_t err;
  gcry_sexp_t s_data, s_key, s_sig, s_tmp;
  char hash[128];
  gcry_mpi_t tmpmpi;

  s_key = read_sexp_from_file (keyfile);

  gcry_md_hash_buffer (algo, hash, data, datalen);
  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash,
                       gcry_md_get_algo_dlen(algo), NULL);
  if (!err)
    {
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags raw)(hash %s %M))",
                             gcry_md_algo_name(algo), tmpmpi);
      gcry_mpi_release (tmpmpi);
    }
  if (err)
    die ("gcry_sexp_build failed for ECDSA data input: %s\n",
         gpg_strerror (err));

  err = gcry_pk_sign (&s_sig, s_data, s_key);
  if (err)
    {
      die ("gcry_pk_signed failed: %s\n", gpg_strerror (err));
    }
  gcry_sexp_release (s_data);
  gcry_sexp_release (s_key);

  /* Now return the actual signature.  */
  s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
  if (!s_tmp)
    die ("no sig-val element in returned S-expression\n");

  gcry_sexp_release (s_sig);
  s_sig = s_tmp;
  s_tmp = gcry_sexp_find_token (s_sig, "ecdsa", 0);
  if (!s_tmp)
    die ("no ecdsa element in returned S-expression\n");

  gcry_sexp_release (s_sig);
  s_sig = s_tmp;

  s_tmp = gcry_sexp_find_token (s_sig, "r", 0);
  tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
  if (!tmpmpi)
    die ("no r parameter in returned S-expression\n");
  print_mpi_line (tmpmpi, 1);
  gcry_mpi_release (tmpmpi);
  gcry_sexp_release (s_tmp);

  s_tmp = gcry_sexp_find_token (s_sig, "s", 0);
  tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
  if (!tmpmpi)
    die ("no s parameter in returned S-expression\n");
  print_mpi_line (tmpmpi, 1);
  gcry_mpi_release (tmpmpi);
  gcry_sexp_release (s_tmp);

  gcry_sexp_release (s_sig);
}



/* Verify DATA of length DATALEN using the public key taken from the
   S-expression in KEYFILE against the S-expression formatted
   signature in SIGFILE.  */
static void
run_ecdsa_verify (const void *data, size_t datalen,
                const char *keyfile, const int algo, const char *sigfile)

{
  gpg_error_t err;
  gcry_sexp_t s_data, s_key, s_sig;
  char hash[128];
  gcry_mpi_t tmpmpi;

  s_key = read_sexp_from_file (keyfile);

  gcry_md_hash_buffer (algo, hash, data, datalen);
  /* Note that we can't simply use %b with HASH to build the
     S-expression, because that might yield a negative value.  */
  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash,
                       gcry_md_get_algo_dlen(algo), NULL);
  if (!err)
    {
      err = gcry_sexp_build (&s_data, NULL,
                             "(data (flags raw)(hash %s %M))",
                             gcry_md_algo_name(algo), tmpmpi);
      gcry_mpi_release (tmpmpi);
    }
  if (err)
    die ("gcry_sexp_build failed for DSA data input: %s\n",
         gpg_strerror (err));

  s_sig = read_sexp_from_file (sigfile);

  err = gcry_pk_verify (s_sig, s_data, s_key);
  if (!err)
    puts ("GOOD signature");
  else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE)
    puts ("BAD signature");
  else
    printf ("ERROR (%s)\n", gpg_strerror (err));

  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_key);
  gcry_sexp_release (s_data);
}


/* Generate an ECDSA key with specified domain parameters
   and print the d and Q values, in the standard octet-string format. */
static void
run_ecdsa_gen_key (const char *curve)
{
  gcry_sexp_t key;

  key = ecdsa_gen_key (curve);
  print_ecdsa_dq (key);

  gcry_sexp_release (key);
}



static void
usage (int show_help)
{
  if (!show_help)
    {
      fputs ("usage: " PGM
             " [OPTION] [FILE] (try --help for more information)\n", stderr);
      exit (2);
    }
  fputs
    ("Usage: " PGM " [OPTIONS] MODE [FILE]\n"
     "Run a crypto operation using hex encoded input and output.\n"
     "MODE:\n"
     "  encrypt, decrypt, digest, random, hmac-sha,\n"
     "  rsa-{derive,gen,sign,verify},\n"
     "  dsa-{pqg-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n"
     "OPTIONS:\n"
     "  --verbose        Print additional information\n"
     "  --binary         Input and output is in binary form\n"
     "  --no-fips        Do not force FIPS mode\n"
     "  --key KEY        Use the hex encoded KEY\n"
     "  --iv IV          Use the hex encoded IV\n"
     "  --dt DT          Use the hex encoded DT for the RNG\n"
     "  --algo NAME      Use algorithm NAME\n"
     "  --curve NAME     Select ECC curve spec NAME\n"
     "  --keysize N      Use a keysize of N bits\n"
     "  --signature NAME Take signature from file NAME\n"
     "  --chunk N        Read in chunks of N bytes (implies --binary)\n"
     "  --pkcs1          Use PKCS#1 encoding\n"
     "  --pss            Use PSS encoding with a zero length salt\n"
     "  --mct-server     Run a monte carlo test server\n"
     "  --loop           Enable random loop mode\n"
     "  --progress       Print pogress indicators\n"
     "  --help           Print this text\n"
     "With no FILE, or when FILE is -, read standard input.\n"
     "Report bugs to " PACKAGE_BUGREPORT ".\n" , stdout);
  exit (0);
}

int
main (int argc, char **argv)
{
  int last_argc = -1;
  gpg_error_t err;
  int no_fips = 0;
  int progress = 0;
  int use_pkcs1 = 0;
  int use_pss = 0;
  const char *mode_string;
  const char *curve_string = NULL;
  const char *key_string = NULL;
  const char *iv_string = NULL;
  const char *dt_string = NULL;
  const char *algo_string = NULL;
  const char *keysize_string = NULL;
  const char *signature_string = NULL;
  FILE *input;
  void *data;
  size_t datalen;
  size_t chunksize = 0;
  int mct_server = 0;


  if (argc)
    { argc--; argv++; }

  while (argc && last_argc != argc )
    {
      last_argc = argc;
      if (!strcmp (*argv, "--"))
        {
          argc--; argv++;
          break;
        }
      else if (!strcmp (*argv, "--help"))
        {
          usage (1);
        }
      else if (!strcmp (*argv, "--version"))
        {
          fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout);
          exit (0);
        }
      else if (!strcmp (*argv, "--verbose"))
        {
          verbose++;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--binary"))
        {
          binary_input = binary_output = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--no-fips"))
        {
          no_fips++;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--loop"))
        {
          loop_mode = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--progress"))
        {
          progress = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--key"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          key_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--iv"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          iv_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--dt"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          dt_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--algo"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          algo_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--keysize"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          keysize_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--signature"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          signature_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--chunk"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          chunksize = atoi (*argv);
          binary_input = binary_output = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--curve"))
        {
          argc--; argv++;
          if (!argc)
            usage (0);
          curve_string = *argv;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--pkcs1"))
        {
          use_pkcs1 = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--pss"))
        {
          use_pss = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--mct-server"))
        {
          mct_server = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--standalone"))
        {
          standalone_mode = 1;
          argc--; argv++;
        }
    }

  if (!argc || argc > 2)
    usage (0);

  mode_string = *argv;

  if (use_pkcs1 && use_pss)
    die ("Only one of --pkcs or --pss may be given\n");

  if (!strcmp (mode_string, "rsa-derive"))
    binary_input = 1;

  if (argc == 2 && strcmp (argv[1], "-"))
    {
      input = fopen (argv[1], binary_input? "rb":"r");
      if (!input)
        die ("can't open `%s': %s\n", argv[1], strerror (errno));
    }
  else
    input = stdin;

#ifndef HAVE_W32_SYSTEM
  if (loop_mode)
    signal (SIGPIPE, SIG_IGN);
#endif

  if (verbose)
    fprintf (stderr, PGM ": started (mode=%s)\n", mode_string);

  xgcry_control ((GCRYCTL_SET_VERBOSITY, (int)verbose));
  if (!no_fips)
    xgcry_control ((GCRYCTL_FORCE_FIPS_MODE, 0));
  if (!gcry_check_version ("1.4.3"))
    die ("Libgcrypt is not sufficient enough\n");
  if (verbose)
    fprintf (stderr, PGM ": using Libgcrypt %s\n", gcry_check_version (NULL));
  if (no_fips)
    xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
  xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));

  /* Most operations need some input data.  */
  if (!chunksize
      && !mct_server
      && strcmp (mode_string, "random")
      && strcmp (mode_string, "rsa-gen")
      && strcmp (mode_string, "rsa-keygen")
      && strcmp (mode_string, "rsa-keygen-kat")
      && strcmp (mode_string, "dsa-gen")
      && strcmp (mode_string, "ecdsa-gen-key") )
    {
      data = read_file (input, !binary_input, &datalen);
      if (!data)
        die ("error reading%s input\n", binary_input?"":" and decoding");
      if (verbose)
        fprintf (stderr, PGM ": %u bytes of input data\n",
                 (unsigned int)datalen);
    }
  else
    {
      data = NULL;
      datalen = 0;
    }


  if (!strcmp (mode_string, "encrypt") || !strcmp (mode_string, "decrypt"))
    {
      int cipher_algo, cipher_mode;
      void  *iv_buffer = NULL;
      void *key_buffer = NULL;
      size_t iv_buflen,  key_buflen;

      if (!algo_string)
        die ("option --algo is required in this mode\n");
      cipher_algo = map_openssl_cipher_name (algo_string, &cipher_mode);
      if (!cipher_algo)
        die ("cipher algorithm `%s' is not supported\n", algo_string);
      if (mct_server)
        {
          int iterations;

          for (;;)
            {
              gcry_free (key_buffer); key_buffer = NULL;
              gcry_free (iv_buffer); iv_buffer = NULL;
              gcry_free (data); data = NULL;
              if (!(key_buffer = read_textline (input)))
                {
                  if (feof (input))
                    break;
                  die ("no version info in input\n");
                }
              if (atoi (key_buffer) != 1)
                die ("unsupported input version %s\n",
                     (const char*)key_buffer);
              gcry_free (key_buffer);
              if (!(key_buffer = read_textline (input)))
                die ("no iteration count in input\n");
              iterations = atoi (key_buffer);
              gcry_free (key_buffer);
              if (!(key_buffer = read_hexline (input, &key_buflen)))
                die ("no key in input\n");
              if (!(iv_buffer = read_hexline (input, &iv_buflen)))
                die ("no IV in input\n");
              if (!(data = read_hexline (input, &datalen)))
                die ("no data in input\n");
              skip_to_empty_line (input);

              run_cipher_mct_loop ((*mode_string == 'e'),
                                   cipher_algo, cipher_mode,
                                   iv_buffer, iv_buflen,
                                   key_buffer, key_buflen,
                                   data, datalen, iterations);
            }
        }
      else
        {
          if (cipher_mode != GCRY_CIPHER_MODE_ECB)
            {
              if (!iv_string)
                die ("option --iv is required in this mode\n");
              iv_buffer = hex2buffer (iv_string, &iv_buflen);
              if (!iv_buffer)
                die ("invalid value for IV\n");
            }
          else
            {
              iv_buffer = NULL;
              iv_buflen = 0;
            }
          if (!key_string)
            die ("option --key is required in this mode\n");
          key_buffer = hex2buffer (key_string, &key_buflen);
          if (!key_buffer)
            die ("invalid value for KEY\n");

          run_encrypt_decrypt ((*mode_string == 'e'),
                               cipher_algo, cipher_mode,
                               iv_buffer, iv_buflen,
                               key_buffer, key_buflen,
                               data, data? datalen:chunksize, input);
        }
      gcry_free (key_buffer);
      gcry_free (iv_buffer);
    }
  else if (!strcmp (mode_string, "digest"))
    {
      int algo;

      if (!algo_string)
        die ("option --algo is required in this mode\n");
      algo = gcry_md_map_name (algo_string);
      if (!algo)
        die ("digest algorithm `%s' is not supported\n", algo_string);
      if (!data)
        die ("no data available (do not use --chunk)\n");

      run_digest (algo, data, datalen);
    }
  else if (!strcmp (mode_string, "random"))
    {
      void *context;
      unsigned char key[16];
      unsigned char seed[16];
      unsigned char dt[16];
      unsigned char buffer[16];
      size_t count = 0;

      if (!key_string || hex2bin (key_string, key, 16) < 0 )
        die ("value for --key are not 32 hex digits\n");
      if (!iv_string || hex2bin (iv_string, seed, 16) < 0 )
        die ("value for --iv are not 32 hex digits\n");
      if (!dt_string || hex2bin (dt_string, dt, 16) < 0 )
        die ("value for --dt are not 32 hex digits\n");

      /* The flag value 1 disables the dup check, so that the RNG
         returns all generated data.  */
      err = init_external_rng_test (&context, 1, key, 16, seed, 16, dt, 16);
      if (err)
        die ("init external RNG test failed: %s\n", gpg_strerror (err));

      do
        {
          err = run_external_rng_test (context, buffer, sizeof buffer);
          if (err)
            die ("running external RNG test failed: %s\n", gpg_strerror (err));
          print_buffer (buffer, sizeof buffer);
          if (progress)
            {
              if (!(++count % 1000))
                fprintf (stderr, PGM ": %lu random bytes so far\n",
                         (unsigned long int)(count * sizeof buffer));
            }
        }
      while (loop_mode);

      if (progress)
        fprintf (stderr, PGM ": %lu random bytes\n",
                 (unsigned long int)(count * sizeof buffer));

      deinit_external_rng_test (context);
    }
  else if (!strcmp (mode_string, "hmac-sha"))
    {
      int algo;
      void  *key_buffer;
      size_t key_buflen;

      if (!data)
        die ("no data available (do not use --chunk)\n");
      if (!algo_string)
        die ("option --algo is required in this mode\n");
      switch (atoi (algo_string))
        {
        case 1:   algo = GCRY_MD_SHA1; break;
        case 224: algo = GCRY_MD_SHA224; break;
        case 256: algo = GCRY_MD_SHA256; break;
        case 384: algo = GCRY_MD_SHA384; break;
        case 512: algo = GCRY_MD_SHA512; break;
        default:  algo = 0; break;
        }
      if (!algo)
        die ("no digest algorithm found for hmac type `%s'\n", algo_string);
      if (!key_string)
        die ("option --key is required in this mode\n");
      key_buffer = hex2buffer (key_string, &key_buflen);
      if (!key_buffer)
        die ("invalid value for KEY\n");

      run_hmac (algo, key_buffer, key_buflen, data, datalen);

      gcry_free (key_buffer);
    }
  else if (!strcmp (mode_string, "rsa-derive"))
    {
      if (!data)
        die ("no data available (do not use --chunk)\n");
      run_rsa_derive (data, datalen);
    }
  else if (!strcmp (mode_string, "rsa-keygen"))
    {
      data = read_file (input, 0, &datalen);
      if (!data)
        die ("no data available (do not use --chunk)\n");
      run_rsa_keygen (data, datalen, 0);
    }
  else if (!strcmp (mode_string, "rsa-keygen-kat"))
    {
      data = read_file (input, 0, &datalen);
      if (!data)
        die ("no data available (do not use --chunk)\n");
      run_rsa_keygen (data, datalen, 1);
    }
  else if (!strcmp (mode_string, "rsa-gen"))
    {
      int keysize;

      if (!binary_output)
        base64_output = 1;

      keysize = keysize_string? atoi (keysize_string) : 0;
      if (keysize < 128 || keysize > 16384)
        die ("invalid keysize specified; needs to be 128 .. 16384\n");
      run_rsa_gen (keysize, 65537);
    }
  else if (!strcmp (mode_string, "rsa-sign"))
    {
      int algo;

      if (!key_string)
        die ("option --key is required in this mode\n");
      if (access (key_string, R_OK))
        die ("option --key needs to specify an existing keyfile\n");
      if (!algo_string)
        die ("option --algo is required in this mode\n");
      algo = gcry_md_map_name (algo_string);
      if (!algo)
        die ("digest algorithm `%s' is not supported\n", algo_string);
      if (!data)
        die ("no data available (do not use --chunk)\n");

      run_rsa_sign (data, datalen, algo, use_pkcs1, use_pss, key_string);

    }
  else if (!strcmp (mode_string, "rsa-verify"))
    {
      int algo;

      if (!key_string)
        die ("option --key is required in this mode\n");
      if (access (key_string, R_OK))
        die ("option --key needs to specify an existing keyfile\n");
      if (!algo_string)
        die ("option --algo is required in this mode\n");
      algo = gcry_md_map_name (algo_string);
      if (!algo)
        die ("digest algorithm `%s' is not supported\n", algo_string);
      if (!data)
        die ("no data available (do not use --chunk)\n");
      if (!signature_string)
        die ("option --signature is required in this mode\n");
      if (access (signature_string, R_OK))
        die ("option --signature needs to specify an existing file\n");

      run_rsa_verify (data, datalen, algo, use_pkcs1, use_pss, key_string,
                      signature_string);

    }
  else if (!strcmp (mode_string, "dsa-pqg-gen"))
    {
      int keysize;

      keysize = keysize_string? atoi (keysize_string) : 0;
      if (keysize < 1024 || keysize > 3072)
        die ("invalid keysize specified; needs to be 1024 .. 3072\n");
      run_dsa_pqg_gen (keysize, datalen? data:NULL, datalen);
    }
  else if (!strcmp (mode_string, "dsa-gen"))
    {
      int keysize;

      keysize = keysize_string? atoi (keysize_string) : 0;
      if (keysize < 1024 || keysize > 3072)
        die ("invalid keysize specified; needs to be 1024 .. 3072\n");
      if (!key_string)
        die ("option --key is required in this mode\n");
      run_dsa_gen (keysize, key_string);
    }
  else if (!strcmp (mode_string, "dsa-sign"))
    {
      if (!key_string)
        die ("option --key is required in this mode\n");
      if (access (key_string, R_OK))
        die ("option --key needs to specify an existing keyfile\n");
      if (!data)
        die ("no data available (do not use --chunk)\n");

      run_dsa_sign (data, datalen, key_string);
    }
  else if (!strcmp (mode_string, "dsa-verify"))
    {
      if (!key_string)
        die ("option --key is required in this mode\n");
      if (access (key_string, R_OK))
        die ("option --key needs to specify an existing keyfile\n");
      if (!data)
        die ("no data available (do not use --chunk)\n");
      if (!signature_string)
        die ("option --signature is required in this mode\n");
      if (access (signature_string, R_OK))
        die ("option --signature needs to specify an existing file\n");

      run_dsa_verify (data, datalen, key_string, signature_string);
    }
  else if (!strcmp (mode_string, "ecdsa-gen-key"))
    {
      if (!curve_string)
        die ("option --curve containing name of the specified curve is required in this mode\n");
      run_ecdsa_gen_key (curve_string);
    }
  else if (!strcmp (mode_string, "ecdsa-sign"))
    {
      int algo;

      if (!key_string)
        die ("option --key is required in this mode\n");
      if (access (key_string, R_OK))
        die ("option --key needs to specify an existing keyfile\n");
      if (!algo_string)
        die ("use --algo to specify the digest algorithm\n");
      algo = gcry_md_map_name (algo_string);
      if (!algo)
        die ("digest algorithm `%s' is not supported\n", algo_string);

      if (!data)
        die ("no data available (do not use --chunk)\n");

      run_ecdsa_sign (data, datalen, key_string, algo);
    }
  else if (!strcmp (mode_string, "ecdsa-verify"))
    {
      int algo;

      if (!key_string)
        die ("option --key is required in this mode\n");
      if (access (key_string, R_OK))
        die ("option --key needs to specify an existing keyfile\n");
      if (!algo_string)
        die ("use --algo to specify the digest algorithm\n");
      algo = gcry_md_map_name (algo_string);
      if (!algo)
        die ("digest algorithm `%s' is not supported\n", algo_string);
      if (!data)
        die ("no data available (do not use --chunk)\n");
      if (!signature_string)
        die ("option --signature is required in this mode\n");
      if (access (signature_string, R_OK))
        die ("option --signature needs to specify an existing file\n");

      run_ecdsa_verify (data, datalen, key_string, algo, signature_string);
    }
  else
    usage (0);

  gcry_free (data);

  /* Because Libgcrypt does not enforce FIPS mode in all cases we let
     the process die if Libgcrypt is not anymore in FIPS mode after
     the actual operation.  */
  if (!no_fips && !gcry_fips_mode_active ())
    die ("FIPS mode is not anymore active\n");

  if (verbose)
    fputs (PGM ": ready\n", stderr);

  return 0;
}
