diff options
Diffstat (limited to 'libotr/libgcrypt-1.8.7/tests/fipsdrv.c')
-rw-r--r-- | libotr/libgcrypt-1.8.7/tests/fipsdrv.c | 2865 |
1 files changed, 2865 insertions, 0 deletions
diff --git a/libotr/libgcrypt-1.8.7/tests/fipsdrv.c b/libotr/libgcrypt-1.8.7/tests/fipsdrv.c new file mode 100644 index 0000000..f9d9c45 --- /dev/null +++ b/libotr/libgcrypt-1.8.7/tests/fipsdrv.c @@ -0,0 +1,2865 @@ +/* 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; +} |