* Makefile.am: Add sc-copykeys program.

* sc-copykeys.c: New.
* app-openpgp.c (app_openpgp_storekey): New.
(app_openpgp_cardinfo): New.
(count_bits): New.
(store_fpr): And use it here to get the actual length in bit.
This commit is contained in:
Werner Koch 2003-07-16 13:47:14 +00:00
parent 270ba1bc96
commit fa2faef48f
5 changed files with 1031 additions and 13 deletions

View File

@ -1,3 +1,12 @@
2003-07-15 Werner Koch <wk@gnupg.org>
* Makefile.am: Add sc-copykeys program.
* sc-copykeys.c: New.
* app-openpgp.c (app_openpgp_storekey): New.
(app_openpgp_cardinfo): New.
(count_bits): New.
(store_fpr): And use it here to get the actual length in bit.
2003-07-03 Werner Koch <wk@gnupg.org>
* app-openpgp.c (do_setattr): Add setting of the URL.

View File

@ -21,7 +21,7 @@
localedir = $(datadir)/locale
INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
bin_PROGRAMS = scdaemon sc-investigate
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
@ -43,15 +43,27 @@ sc_investigate_SOURCES = \
sc-investigate.c scdaemon.h \
apdu.c apdu.h \
iso7816.c iso7816.h \
app.c app-common.h \
app-openpgp.c \
atr.c atr.h
sc_investigate_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(LIBGCRYPT_LIBS) -lgpg-error -ldl
sc_investigate_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
$(LIBGCRYPT_LIBS) -lgpg-error -ldl
sc_copykeys_SOURCES = \
sc-copykeys.c scdaemon.h \
apdu.c apdu.h \
iso7816.c iso7816.h \
app.c app-common.h \
app-openpgp.c \
atr.c atr.h
sc_copykeys_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
../common/libsimple-pwquery.a \
$(LIBGCRYPT_LIBS) -lgpg-error -ldl

View File

@ -83,6 +83,24 @@ int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
/*-- app-openpgp.c --*/
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
int app_openpgp_cardinfo (APP app,
char **serialno,
char **disp_name,
char **pubkey_url,
unsigned char **fpr1,
unsigned char **fpr2,
unsigned char **fpr3);
int app_openpgp_storekey (APP app, int keyno,
unsigned char *template, size_t template_len,
time_t created_at,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int app_openpgp_readkey (APP app, int keyno,
unsigned char **m, size_t *mlen,
unsigned char **e, size_t *elen);
#endif /*GNUPG_SCD_APP_COMMON_H*/

View File

@ -341,6 +341,25 @@ dump_all_do (int slot)
}
}
/* Count the number of bits, assuming the A represents an unsigned big
integer of length LEN bytes. */
static unsigned int
count_bits (const unsigned char *a, size_t len)
{
unsigned int n = len * 8;
int i;
for (; len && !*a; len--, a++, n -=8)
;
if (len)
{
for (i=7; i && !(*a & (1<<i)); i--)
n--;
}
return n;
}
/* Note, that FPR must be at least 20 bytes. */
static int
store_fpr (int slot, int keynumber, u32 timestamp,
@ -348,10 +367,15 @@ store_fpr (int slot, int keynumber, u32 timestamp,
const unsigned char *e, size_t elen,
unsigned char *fpr)
{
unsigned int n;
unsigned int n, nbits;;
unsigned char *buffer, *p;
int rc;
for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
;
for (; elen && !*e; elen--, e++) /* strip leading zeroes */
;
n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n);
if (!buffer)
@ -366,13 +390,16 @@ store_fpr (int slot, int keynumber, u32 timestamp,
*p++ = timestamp >> 8;
*p++ = timestamp;
*p++ = 1; /* RSA */
*p++ = (mlen*8) >> 8; /* (we want number of bits here) */
*p++ = (mlen*8);
nbits = count_bits (m, mlen);
*p++ = nbits >> 8;
*p++ = nbits;
memcpy (p, m, mlen); p += mlen;
*p++ = (elen*8) >> 8;
*p++ = (elen*8);
nbits = count_bits (e, elen);
*p++ = nbits >> 8;
*p++ = nbits;
memcpy (p, e, elen); p += elen;
log_printhex ("fprbuf:", buffer, n+3);
gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
xfree (buffer);
@ -603,7 +630,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
}
xfree (buffer); buffer = NULL;
#if 1
#if 0
log_info ("please wait while key is being generated ...\n");
start_at = time (NULL);
rc = iso7816_generate_keypair
@ -627,6 +654,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
if (!keydata)
{
rc = gpg_error (GPG_ERR_CARD);
log_error ("response does not contain the public key data\n");
goto leave;
}
@ -634,6 +662,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
if (!m)
{
rc = gpg_error (GPG_ERR_CARD);
log_error ("response does not contain the RSA modulus\n");
goto leave;
}
@ -643,6 +672,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
if (!e)
{
rc = gpg_error (GPG_ERR_CARD);
log_error ("response does not contain the RSA public exponent\n");
goto leave;
}
@ -996,4 +1026,218 @@ leave:
/* This function is a hack to retrieve essential information about the
card to be displayed by simple tools. It mostly resembles what the
LEARN command returns. All parameters return allocated strings or
buffers or NULL if the data object is not available. All returned
values are sanitized. */
int
app_openpgp_cardinfo (APP app,
char **serialno,
char **disp_name,
char **pubkey_url,
unsigned char **fpr1,
unsigned char **fpr2,
unsigned char **fpr3)
{
int rc;
void *relptr;
unsigned char *value;
size_t valuelen;
if (serialno)
{
time_t dummy;
*serialno = NULL;
rc = app_get_serial_and_stamp (app, serialno, &dummy);
if (rc)
{
log_error ("error getting serial number: %s\n", gpg_strerror (rc));
return rc;
}
}
if (disp_name)
{
*disp_name = NULL;
relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
if (relptr)
{
*disp_name = make_printable_string (value, valuelen, 0);
xfree (relptr);
}
}
if (pubkey_url)
{
*pubkey_url = NULL;
relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen);
if (relptr)
{
*pubkey_url = make_printable_string (value, valuelen, 0);
xfree (relptr);
}
}
if (fpr1)
*fpr1 = NULL;
if (fpr2)
*fpr2 = NULL;
if (fpr3)
*fpr3 = NULL;
relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
if (relptr && valuelen >= 60)
{
if (fpr1)
{
*fpr1 = xmalloc (20);
memcpy (*fpr1, value + 0, 20);
}
if (fpr2)
{
*fpr2 = xmalloc (20);
memcpy (*fpr2, value + 20, 20);
}
if (fpr3)
{
*fpr3 = xmalloc (20);
memcpy (*fpr3, value + 40, 20);
}
}
xfree (relptr);
return 0;
}
/* This function is currently only used by the sc-copykeys program to
store a key on the smartcard. APP ist the application handle,
KEYNO is the number of the key and PINCB, PINCB_ARG are used to ask
for the SO PIN. TEMPLATE and TEMPLATE_LEN describe a buffer with
the key template to store. CREATED_AT is the timestamp used to
create the fingerprint. M, MLEN is the RSA modulus and E, ELEN the
RSA public exponent. This function silently overwrites an existing
key.*/
int
app_openpgp_storekey (APP app, int keyno,
unsigned char *template, size_t template_len,
time_t created_at,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen,
int (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc;
unsigned char fprbuf[20];
if (keyno < 1 || keyno > 3)
return gpg_error (GPG_ERR_INV_ID);
keyno--;
{
char *pinvalue;
rc = pincb (pincb_arg, "Admin PIN", &pinvalue);
if (rc)
{
log_error ("error getting PIN: %s\n", gpg_strerror (rc));
return rc;
}
rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
xfree (pinvalue);
}
if (rc)
{
log_error ("verify CHV3 failed: rc=%04X\n", rc);
goto leave;
}
rc = iso7816_put_data (app->slot, 0xE9 + keyno, template, template_len);
if (rc)
{
log_error ("failed to store the key: rc=%04X\n", rc);
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
log_printhex ("RSA n:", m, mlen);
log_printhex ("RSA e:", e, elen);
rc = store_fpr (app->slot, keyno, (u32)created_at,
m, mlen, e, elen, fprbuf);
leave:
return rc;
}
/* Utility function for external tools: Read the public RSA key at
KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */
int
app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
unsigned char **e, size_t *elen)
{
int rc;
const unsigned char *keydata, *a;
unsigned char *buffer;
size_t buflen, keydatalen, alen;
*m = NULL;
*e = NULL;
if (keyno < 1 || keyno > 3)
return gpg_error (GPG_ERR_INV_ID);
keyno--;
rc = iso7816_read_public_key(app->slot,
keyno == 0? "\xB6" :
keyno == 1? "\xB8" : "\xA4",
2,
&buffer, &buflen);
if (rc)
{
rc = gpg_error (GPG_ERR_CARD);
log_error ("reading key failed\n");
goto leave;
}
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
if (!keydata)
{
log_error ("response does not contain the public key data\n");
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0);
if (!a)
{
log_error ("response does not contain the RSA modulus\n");
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
*mlen = alen;
*m = xmalloc (alen);
memcpy (*m, a, alen);
a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0);
if (!e)
{
log_error ("response does not contain the RSA public exponent\n");
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
*elen = alen;
*e = xmalloc (alen);
memcpy (*e, a, alen);
leave:
xfree (buffer);
if (rc)
{
xfree (*m); *m = NULL;
xfree (*e); *e = NULL;
}
return rc;
}

735
scd/sc-copykeys.c Normal file
View File

@ -0,0 +1,735 @@
/* sc-copykeys.c - A tool to store keys on a smartcard.
* Copyright (C) 2003 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
#include <gcrypt.h>
#include "../common/ttyio.h"
#include "../common/simple-pwquery.h"
#include "apdu.h" /* for open_reader */
#include "atr.h"
#include "app-common.h"
#define _(a) (a)
enum cmd_and_opt_values
{ oVerbose = 'v',
oReaderPort = 500,
oDebug,
oDebugAll,
aTest };
static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, "@Options:\n " },
{ oVerbose, "verbose", 0, "verbose" },
{ oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
{ oDebug, "debug" ,4|16, "set debugging flags"},
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
{0}
};
static void copykeys (APP app, const char *fname);
static const char *
my_strusage (int level)
{
const char *p;
switch (level)
{
case 11: p = "sc-copykeys (GnuPG)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
break;
case 1:
case 40: p = _("Usage: sc-copykeys [options] (-h for help)\n");
break;
case 41: p = _("Syntax: sc-copykeys [options] "
"file-with-key\n"
"Copy keys to a smartcards\n");
break;
default: p = NULL;
}
return p;
}
/* Used by gcry for logging */
static void
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
{
/* translate the log levels */
switch (level)
{
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
default: level = JNLIB_LOG_ERROR; break;
}
log_logv (level, fmt, arg_ptr);
}
int
main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int slot, rc;
int reader_port = 32768; /* First USB reader. */
struct app_ctx_s appbuf;
memset (&appbuf, 0, sizeof appbuf);
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
log_set_prefix ("sc-copykeys", 1);
/* check that the libraries are suitable. Do it here because
the option parsing may need services of the library */
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
{
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
}
gcry_set_log_handler (my_gcry_logger, NULL);
gcry_control (GCRYCTL_DISABLE_SECMEM, 0); /* FIXME - we want to use it */
/* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
while (arg_parse (&pargs, opts) )
{
switch (pargs.r_opt)
{
case oVerbose: opt.verbose++; break;
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
default : pargs.err = 2; break;
}
}
if (log_get_errorcount(0))
exit(2);
if (argc != 1)
usage (1);
slot = apdu_open_reader (reader_port);
if (slot == -1)
exit (1);
/* FIXME: Use select_application. */
appbuf.slot = slot;
rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen);
if (rc)
{
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
exit (1);
}
appbuf.initialized = 1;
log_info ("openpgp application selected\n");
copykeys (&appbuf, *argv);
return 0;
}
void
send_status_info (CTRL ctrl, const char *keyword, ...)
{
/* DUMMY */
}
static char *
read_file (const char *fname, size_t *r_length)
{
FILE *fp;
struct stat st;
char *buf;
size_t buflen;
fp = fname? fopen (fname, "rb") : stdin;
if (!fp)
{
log_error ("can't open `%s': %s\n",
fname? fname: "[stdin]", strerror (errno));
return NULL;
}
if (fstat (fileno(fp), &st))
{
log_error ("can't stat `%s': %s\n",
fname? fname: "[stdin]", strerror (errno));
if (fname)
fclose (fp);
return NULL;
}
buflen = st.st_size;
buf = xmalloc (buflen+1);
if (fread (buf, buflen, 1, fp) != 1)
{
log_error ("error reading `%s': %s\n",
fname? fname: "[stdin]", strerror (errno));
if (fname)
fclose (fp);
xfree (buf);
return NULL;
}
if (fname)
fclose (fp);
*r_length = buflen;
return buf;
}
static gcry_sexp_t
read_key (const char *fname)
{
char *buf;
size_t buflen;
gcry_sexp_t private;
int rc;
buf = read_file (fname, &buflen);
if (!buf)
return NULL;
rc = gcry_sexp_new (&private, buf, buflen, 1);
if (rc)
{
log_error ("gcry_sexp_new failed: %s\n", gpg_strerror (rc));
return NULL;
}
xfree (buf);
return private;
}
static gcry_mpi_t *
sexp_to_kparms (gcry_sexp_t sexp, unsigned long *created)
{
gcry_sexp_t list, l2;
const char *name;
const char *s;
size_t n;
int i, idx;
const char *elems;
gcry_mpi_t *array;
*created = 0;
list = gcry_sexp_find_token (sexp, "private-key", 0 );
if(!list)
return NULL;
/* quick hack to get the creation time. */
l2 = gcry_sexp_find_token (list, "created", 0);
if (l2 && (name = gcry_sexp_nth_data (l2, 1, &n)))
{
char *tmp = xmalloc (n+1);
memcpy (tmp, name, n);
tmp[n] = 0;
*created = strtoul (tmp, NULL, 10);
xfree (tmp);
}
gcry_sexp_release (l2);
l2 = gcry_sexp_cadr (list);
gcry_sexp_release (list);
list = l2;
name = gcry_sexp_nth_data (list, 0, &n);
if(!name || n != 3 || memcmp (name, "rsa", 3))
{
gcry_sexp_release (list);
return NULL;
}
/* Parameter names used with RSA. */
elems = "nedpqu";
array = xcalloc (strlen(elems) + 1, sizeof *array);
for (idx=0, s=elems; *s; s++, idx++ )
{
l2 = gcry_sexp_find_token (list, s, 1);
if (!l2)
{
for (i=0; i<idx; i++)
gcry_mpi_release (array[i]);
xfree (array);
gcry_sexp_release (list);
return NULL; /* required parameter not found */
}
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
if (!array[idx])
{
for (i=0; i<idx; i++)
gcry_mpi_release (array[i]);
xfree (array);
gcry_sexp_release (list);
return NULL; /* required parameter is invalid */
}
}
gcry_sexp_release (list);
return array;
}
/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
static int
fpr_is_zero (const char *fpr)
{
int i;
for (i=0; i < 20 && !fpr[i]; i++)
;
return (i == 20);
}
static void
show_sha1_fpr (const unsigned char *fpr)
{
int i;
if (fpr)
{
for (i=0; i < 20 ; i+=2, fpr += 2 )
{
if (i == 10 )
tty_printf (" ");
tty_printf (" %02X%02X", *fpr, fpr[1]);
}
}
else
tty_printf (" [none]");
tty_printf ("\n");
}
/* Query the card, show a list of already stored keys and ask the user
where to store the key. Returns the key number or 0 for cancel
operation. */
static int
query_card (APP app)
{
int keyno = 0;
char *serialno, *disp_name, *pubkey_url;
unsigned char *fpr1, *fpr2, *fpr3;
if (app_openpgp_cardinfo (app,
&serialno,
&disp_name,
&pubkey_url,
&fpr1, &fpr2, &fpr3))
return 0;
for (;;)
{
char *answer;
tty_printf ("\n");
tty_printf ("Serial number ....: %s\n",
serialno? serialno : "[none]");
tty_printf ("Name of cardholder: %s\n",
disp_name && *disp_name? disp_name : "[not set]");
tty_printf ("URL of public key : %s\n",
pubkey_url && *pubkey_url? pubkey_url : "[not set]");
tty_printf ("Signature key ....:");
show_sha1_fpr (fpr1);
tty_printf ("Encryption key....:");
show_sha1_fpr (fpr2);
tty_printf ("Authentication key:");
show_sha1_fpr (fpr3);
tty_printf ("\n"
"1 - store as signature key and reset usage counter\n"
"2 - store as encryption key\n"
"3 - store as authentication key\n"
"Q - quit\n"
"\n");
answer = tty_get("Your selection? ");
tty_kill_prompt();
if (strlen (answer) != 1)
;
else if ( *answer == '1' )
{
if ( (fpr1 && !fpr_is_zero (fpr1)) )
{
tty_printf ("\n");
log_error ("WARNING: signature key does already exists!\n");
tty_printf ("\n");
if ( tty_get_answer_is_yes ("Replace existing key? ") )
{
keyno = 1;
break;
}
}
else
{
keyno = 1;
break;
}
}
else if ( *answer == '2' )
{
if ( (fpr2 && !fpr_is_zero (fpr2)) )
{
tty_printf ("\n");
log_error ("WARNING: encryption key does already exists!\n");
tty_printf ("\n");
if ( tty_get_answer_is_yes ("Replace existing key? ") )
{
keyno = 2;
break;
}
}
else
{
keyno = 2;
break;
}
}
else if ( *answer == '3' )
{
if ( (fpr3 && !fpr_is_zero (fpr3)) )
{
tty_printf ("\n");
log_error ("WARNING: authentication key does already exists!\n");
tty_printf ("\n");
if ( tty_get_answer_is_yes ("Replace existing key? ") )
{
keyno = 3;
break;
}
}
else
{
keyno = 3;
break;
}
}
else if ( *answer == 'q' || *answer == 'Q')
{
keyno = 0;
break;
}
}
xfree (serialno);
xfree (disp_name);
xfree (pubkey_url);
xfree (fpr1);
xfree (fpr2);
xfree (fpr3);
return keyno;
}
/* Callback function to ask for a PIN. */
static int
pincb (void *arg, const char *prompt, char **pinvalue)
{
char *pin = xstrdup ("12345678");
/* pin = simple_pwquery (NULL, NULL, prompt, */
/* "We need the admin's PIN to store the key on the card", */
/* NULL); */
/* if (!pin) */
/* return gpg_error (GPG_ERR_CANCELED); */
*pinvalue = pin;
return 0;
}
/* This function expects a file (or NULL for stdin) with the secret
and public key parameters. This file should consist of an
S-expression as used by gpg-agent. Only the unprotected format is
supported. Example:
(private-key
(rsa
(n #00e0ce9..[some bytes not shown]..51#)
(e #010001#)
(d #046129F..[some bytes not shown]..81#)
(p #00e861b..[some bytes not shown]..f1#)
(q #00f7a7c..[some bytes not shown]..61#)
(u #304559a..[some bytes not shown]..9b#))
(uri http://foo.bar x-foo:whatever_you_want))
*/
static void
copykeys (APP app, const char *fname)
{
int rc;
gcry_sexp_t private;
gcry_mpi_t *mpis, rsa_n, rsa_e, rsa_p, rsa_q;
unsigned int nbits;
size_t n;
unsigned char *template, *tp;
unsigned char m[128], e[4];
size_t mlen, elen;
unsigned long creation_date;
time_t created_at;
int keyno;
if (!strcmp (fname, "-"))
fname = NULL;
private = read_key (fname);
if (!private)
exit (1);
mpis = sexp_to_kparms (private, &creation_date);
if (!creation_date)
{
log_info ("no creation date found - assuming current date\n");
created_at = time (NULL);
}
else
created_at = creation_date;
gcry_sexp_release (private);
if (!mpis)
{
log_error ("invalid structure of key file or not RSA\n");
exit (1);
}
/* MPIS is now an array with the key parameters as defined by OpenPGP. */
rsa_n = mpis[0];
rsa_e = mpis[1];
gcry_mpi_release (mpis[2]);
rsa_p = mpis[3];
rsa_q = mpis[4];
gcry_mpi_release (mpis[5]);
xfree (mpis);
nbits = gcry_mpi_get_nbits (rsa_e);
if (nbits < 2 || nbits > 32)
{
log_error ("public exponent too large (more than 32 bits)\n");
goto failure;
}
nbits = gcry_mpi_get_nbits (rsa_p);
if (nbits != 512)
{
log_error ("length of first RSA prime is not 512\n");
goto failure;
}
nbits = gcry_mpi_get_nbits (rsa_q);
if (nbits != 512)
{
log_error ("length of second RSA prime is not 512\n");
goto failure;
}
nbits = gcry_mpi_get_nbits (rsa_n);
if (nbits != 1024)
{
log_error ("length of RSA modulus is not 1024\n");
goto failure;
}
keyno = query_card (app);
if (!keyno)
goto failure;
/* Build the private key template as described in section 4.3.3.6 of
the specs.
0xC0 <length> public exponent
0xC1 <length> prime p
0xC2 <length> prime q */
template = tp = xmalloc (1+2 + 1+1+4 + 1+1+64 + 1+1+64);
*tp++ = 0xC0;
*tp++ = 4;
n = 4;
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, &n, rsa_e);
if (rc)
{
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
goto failure;
}
assert (n <= 4);
memcpy (e, tp, n);
elen = n;
if (n != 4)
{
memmove (tp+4-n, tp, 4-n);
memset (tp, 0, 4-n);
}
tp += 4;
*tp++ = 0xC1;
*tp++ = 64;
n = 64;
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, &n, rsa_p);
if (rc)
{
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
goto failure;
}
assert (n == 64);
tp += 64;
*tp++ = 0xC2;
*tp++ = 64;
n = 64;
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, &n, rsa_q);
if (rc)
{
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
goto failure;
}
assert (n == 64);
tp += 64;
assert (tp - template == 138);
/* (we need the modulus to calculate the fingerprint) */
n = 128;
rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, &n, rsa_n);
if (rc)
{
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
goto failure;
}
assert (n == 128);
mlen = 128;
rc = app_openpgp_storekey (app, keyno,
template, tp - template,
created_at,
m, mlen,
e, elen,
pincb, NULL);
if (rc)
{
log_error ("error storing key: %s\n", gpg_strerror (rc));
goto failure;
}
log_info ("key successfully stored\n");
{
unsigned char *mm, *ee;
size_t mmlen, eelen;
int i;
rc = app_openpgp_readkey (app, keyno, &mm, &mmlen, &ee, &eelen);
if (rc)
{
log_error ("error reading key back: %s\n", gpg_strerror (rc));
goto failure;
}
/* Strip leading zeroes. */
for (i=0; i < mmlen && !mm[i]; i++)
;
mmlen -= i;
memmove (mm, mm+i, mmlen);
for (i=0; i < eelen && !ee[i]; i++)
;
eelen -= i;
memmove (ee, ee+i, eelen);
if (eelen != elen || mmlen != mlen)
{
log_error ("key parameter length mismatch (n=%u/%u, e=%u/%u)\n",
(unsigned int)mlen, (unsigned int)mmlen,
(unsigned int)elen, (unsigned int)eelen);
xfree (mm);
xfree (ee);
goto failure;
}
if (memcmp (m, mm, mlen))
{
log_error ("key parameter n mismatch\n");
log_printhex ("original n: ", m, mlen);
log_printhex (" copied n: ", mm, mlen);
xfree (mm);
xfree (ee);
goto failure;
}
if (memcmp (e, ee, elen))
{
log_error ("key parameter e mismatch\n");
log_printhex ("original e: ", e, elen);
log_printhex (" copied e: ", ee, elen);
xfree (mm);
xfree (ee);
goto failure;
}
xfree (mm);
xfree (ee);
}
gcry_mpi_release (rsa_e);
gcry_mpi_release (rsa_p);
gcry_mpi_release (rsa_q);
gcry_mpi_release (rsa_n);
return;
failure:
gcry_mpi_release (rsa_e);
gcry_mpi_release (rsa_p);
gcry_mpi_release (rsa_q);
gcry_mpi_release (rsa_n);
exit (1);
}