c825f470cb
redbear-ci / check (push) Has been cancelled
kf6-kwallet's kwalletbackend hard-requires LibGcrypt 1.5.0 for PBKDF2 key derivation and the GPG-backed wallet format. Vendor both libraries under the full-fork model (committed source/, offline-reproducible) and add libgcrypt to kf6-kwallet's dependencies. Release tarballs ship a pre-generated configure and a config.sub that already recognises the *-redox triple, so no autotools regeneration is needed at cook time. libgpg-error supplies a generic pthread-backed gpgrt_lock_t for Redox via its build script; the test-only redox.patch (tms/times stub) is baked into libgcrypt's source and kept alongside for future version bumps. Promoted from the stale recipes/wip copies (now .disabled).
2866 lines
80 KiB
C
2866 lines
80 KiB
C
/* 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;
|
|
}
|